mercurial/HGWorkingCopyFile.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Sun, 03 Mar 2013 23:58:51 +0000
branchhg-command-server-support
changeset 235 3d8ef499d7d9
parent 234 a9ef61b902ae
child 261 14855fd7632d
permissions -rw-r--r--
Command server is now used by default. All HGTests exept 2 passes. Further invesitgation on those two is required.

"
 COPYRIGHT (c) 2012-2013 by Jan Vrany
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libscm/mercurial' }"

Object subclass:#HGWorkingCopyFile
	instanceVariableNames:'wc children filename revisions'
	classVariableNames:''
	poolDictionaries:''
	category:'SCM-Mercurial-Core'
!

Object subclass:#LazyRevision
	instanceVariableNames:'collection index changeset wc path'
	classVariableNames:''
	poolDictionaries:''
	privateIn:HGWorkingCopyFile
!

!HGWorkingCopyFile class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2012-2013 by Jan Vrany
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    A representation on a file in working copy. It behaves just like 
    ordinary filename but also provides methods for quering it's
    state (added/removed/modified...), access to previous versions
    and so on.

    [author:]
        Jan Vrany <jan.vrany@fit.cvut.cz>

    [instance variables:]

    [class variables:]

    [see also:]

"
! !

!HGWorkingCopyFile class methodsFor:'instance creation'!

wc: aHGWorkingCopy path: aStringOrFilename
    ^self new setWorkingCopy: aHGWorkingCopy path: aStringOrFilename

    "Created: / 24-09-2012 / 13:52:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'accessing'!

changeset
    ^wc changeset

    "Created: / 05-12-2012 / 19:23:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

pathName
    ^filename pathName

    "Created: / 12-11-2012 / 22:43:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

pathNameRelative
    ^self == wc root
        ifTrue:['']
        ifFalse:[filename pathName copyFrom: (wc root pathName size + 2)]

    "Created: / 25-09-2012 / 00:28:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 16-11-2012 / 11:23:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

pathNameRelativeSlashed
    | p |

    p := self pathNameRelative.
    (p includes:$\) ifTrue:[
        p := p copyReplaceAll:$\ with:$/.
    ].
    ^p

    "Created: / 06-12-2012 / 17:11:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

repository
    ^wc repository

    "Created: / 03-03-2013 / 20:40:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

revisions
    | path pathS |

    path := self pathNameRelative.
    pathS := OperatingSystem isMSWINDOWSlike 
                ifTrue:[path copyReplaceAll:$\ with: $/]
                ifFalse:[path].


    revisions isNil ifTrue:[
        | old oldIds |
        oldIds := self repository execute:
                    (HGCommand log
                        workingDirectory: wc pathName;
                        path: path;
                        yourself).
        pathS.
        old := OrderedCollection new.
        oldIds withIndexDo: [:id :index|
            old add: (
                LazyRevision new
                    setCollection: old index: index changesetId: id workingCopy: wc path: pathS)
"/            | cs f |
"/
"/            f := (cs := wc repository changesetWithId: id) / p.
"/
"/            cs changes do:[:chg|
"/                "/Catch renames...
"/                (chg isCopied and:[chg path = p]) ifTrue:[
"/                    p := chg source.
"/                ]
"/            ].
"/            f.
        ].

        revisions := old.
    ].
    "/older revisions are cached, newer not since they may change...

    ^((wc changeset / pathS) newer:true) , revisions

    "Created: / 05-12-2012 / 19:09:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 06-12-2012 / 03:50:58 / jv"
    "Modified: / 03-03-2013 / 23:03:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

status
    | cmd statuses  |

    cmd := HGCommand status.
    cmd workingDirectory: filename directory.
    cmd path: filename pathName.
    statuses := self repository execute: cmd.
    self assert: statuses size == 1.
    self assert: statuses first second = filename baseName.
    ^statuses first first.

    "Created: / 24-09-2012 / 22:27:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-03-2013 / 23:04:07 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'delegating'!

doesNotUnderstand: aMessage

    ^(filename respondsTo: aMessage selector) ifTrue:[
        aMessage sendTo: filename
    ] ifFalse:[
        super doesNotUnderstand: aMessage
    ].

    "Created: / 24-09-2012 / 13:46:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'enumerating-contents'!

directoryContentsAsFilenamesDo:aBlock
    "evaluate aBlock for each file in the directory represented by the receiver.
     The block is invoked with a filename-argument.
     The enumerations order is undefined - i.e. usually NOT sorted by
     filenames (but by creation time - on some systems).
     This excludes entries for '.' or '..'.
     NoOp for non-existing directories; however, this behavior
     may be changed in the near future, to raise an exception instead.
     So users of this method better test for existing directory before.
     Notice: this enumerates fileName objects; see also
     #directoryContentsDo:, which enumerates strings."

    self directoryContentsDo:[:entry |
        aBlock value:(self construct:entry).
    ]

    "
     '.' asFilename directoryContentsAsFilenamesDo:[:fn | Transcript showCR:fn pathName].
    "

    "Modified: / 18.9.1997 / 18:42:23 / stefan"
    "Modified: / 23.12.1999 / 20:56:35 / cg"
! !

!HGWorkingCopyFile methodsFor:'initialization'!

setWorkingCopy: aHGWorkingCopy path: aStringOrFilename

    wc := aHGWorkingCopy.
    filename := aStringOrFilename asFilename.

    "Created: / 24-09-2012 / 13:53:05 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'inspecting'!

browse
    "Opens a file browser on the working copy"

    HGSourceCodeManager workingCopyBrowserClass openOnDirectory: filename

    "Created: / 04-02-2012 / 17:14:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 14-12-2012 / 15:48:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'instance creation'!

/ aString
    ^self construct: aString

    "Created: / 24-09-2012 / 13:49:45 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

construct: aString
    ^(aString includes: Filename separator) ifTrue:[
        self components: (aString tokensBasedOn: Filename separator)
    ] ifFalse:[
        self component: aString
    ]

    "Created: / 24-09-2012 / 13:50:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'instance creation-private'!

component: aString

    aString = '.' ifTrue:[ ^ self ].
    aString = '..' ifTrue:[ ^ self error:'Not yet supported' ].

    children isNil ifTrue: [ children := Dictionary new ].
    ^children 
        at: aString 
        ifAbsentPut:[HGWorkingCopyFile wc: wc path: (filename construct: aString)]

    "Created: / 24-09-2012 / 23:26:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 01-12-2012 / 02:09:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

components: anArray"OfStrings"
    ^anArray inject: self into:[:entry :name | entry component: name ]

    "Created: / 24-09-2012 / 23:25:07 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'operations'!

markResolved
    self repository execute:
        (HGCommand resolve
            workingDirectory: filename directory;
            mark: true;
            files: (Array with: filename baseName);
            yourself)

    "Created: / 15-01-2013 / 10:22:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-03-2013 / 23:01:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

markUnresolved
    self repository execute:
        (HGCommand resolve
            workingDirectory: filename directory;
            unmark: true;
            files: (Array with: filename baseName);
            yourself).

    "Created: / 15-01-2013 / 10:22:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-03-2013 / 23:02:13 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

moveTo: destination
    "Make sure that this entry is tracked by Mercurial"

    self isTracked ifTrue:[
        self repository execute:
            (HGCommand mv
                workingDirectory: filename directory;
                source: filename pathName;
                destination: destination pathName;
                yourself)
    ].
    filename exists ifTrue:[
        filename moveTo: destination pathName
    ].

    "Created: / 15-11-2012 / 00:23:06 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-03-2013 / 23:02:59 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

remove
    "Make sure that this entry is tracked by Mercurial"

    self isTracked ifTrue:[
        self repository execute:
            (HGCommand remove
               workingDirectory: filename directory;
               paths: { filename baseName };
               yourself)
    ].
    filename remove

    "Created: / 15-11-2012 / 00:08:59 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-03-2013 / 23:03:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

track
    "Make sure that this entry is tracked by Mercurial"

    self isUntracked ifTrue:[
        self repository execute:
            (HGCommand add
                workingDirectory: filename directory;
                paths: { filename baseName };
                yourself)
    ]

    "Created: / 15-11-2012 / 00:08:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-03-2013 / 23:04:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'printing & storing'!

printOn:aStream
    "append a printed representation if the receiver to the argument, aStream"

    | path |

    aStream nextPut:$[.
    path := filename pathName.
    path := path copyFrom: wc path pathName size + 1.
    aStream nextPutAll: path.
    aStream nextPut:$].

    "Modified: / 17-10-2012 / 13:51:44 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'reading-directories'!

directoryContentsAsFilenames
    "return the contents of the directory as a collection of filenames.
     This excludes any entries for '.' or '..'.
     Returns nil for non-existing directories; however, this behavior
     may be changed in the near future, to raise an exception instead.
     So users of this method better test for existing directory before.
     Notice: 
        this returns the file-names as fileName instances; 
        see also #directoryContents, which returns strings."

    |names|

    names := filename directoryContents.
    names isNil ifTrue:[^ nil].
    ^ names collect:[:entry | self construct:entry].

    "
     '.' asFilename directoryContentsAsFilenames   
     '/XXXdoesNotExist' asFilename directoryContentsAsFilenames
    "

    "Modified: / 15-11-2012 / 01:13:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

directoryContentsAsFilenamesMatching: patternOrCollectionOfThose

    "
        Same as directoryContentsAsFilenames, but returns only files
        that matches given patterns. This uses String>>matches:
        for pattern matching
    "

    |names|

    names := filename directoryContentsMatching: patternOrCollectionOfThose .
    names isNil ifTrue:[^ nil].
    ^ names asOrderedCollection collect:[:entry | self construct:entry].

    "
    '/etc' asFilename
        directoryContentsAsFilenamesMatching: 'pass*'

    '/etc' asFilename
            directoryContentsAsFilenamesMatching: #('pass*' 'nsswitch.conf')
    "

    "Created: / 03-06-2009 / 09:57:45 / Jan Vrany <vranyj1@fel.cvut.cz>"
    "Modified: / 15-11-2012 / 01:13:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

recursiveDirectoryContentsAsFilenames
    "return the contents of the directory and all subdirectories
     as a collection of filenames.
     This excludes any entries for '.' or '..'.
     Returns nil for non-existing directories; however, this behavior
     may be changed in the near future, to raise an exception instead.
     So users of this method better test for existing directory before.
     Notice: 
        this returns the file-names as fileName instances; 
        see also #recursiveDirectoryContents, which returns strings.

     Warning: this may take a long time to execute."

    |names|

    names := filename recursiveDirectoryContents.
    names isNil ifTrue:[^ nil].
    ^ names collect:[:entry | self construct:entry].

    "Created: / 15-11-2012 / 01:11:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile methodsFor:'testing'!

isAdded
    ^ self status isAdded

    "Modified: / 23-10-2012 / 11:13:03 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isClean
    ^ self status isClean

    "Modified: / 23-10-2012 / 11:13:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isCleanOrIgnored
    | s |

    s := self status.
    ^s isClean or:[s isIgnored]

    "Created: / 15-11-2012 / 01:25:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isConflict
    "Return true, if the file had a conflict during merge or update.

     Note, that this return true even if the file was later merged
     and conflicts resolved. To check whether conflicts are resolved or
     not, use #isResolved or isUnresolved"

    ^wc mergeState includesKey: self pathNameRelative.

    "Created: / 14-01-2013 / 16:54:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isIgnored
    ^ self status isIgnored

    "Modified: / 23-10-2012 / 11:13:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isMissing
    ^ self status isMissing

    "Modified: / 23-10-2012 / 11:12:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isModified
    ^ self status isModified

    "Modified: / 23-10-2012 / 11:13:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isNotTracked
    ^ self status isNotTracked

    "Modified: / 23-10-2012 / 11:13:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isRemoved
    ^ self status isRemoved

    "Modified: / 23-10-2012 / 11:13:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isResolved
    "Return true, if the file had a conflict during merge or update
     and is marked as resolved."

    ^(wc mergeState at: self pathNameRelative) == $R

    "Created: / 14-01-2013 / 16:56:01 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isTracked
    | s |

    s := self status.
    ^s isNotTracked not and:[s isIgnored not and:[s isRemoved not]]

    "Created: / 15-11-2012 / 00:11:50 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isUnresolved
    "Return true, if the file had a conflict during merge or update
     and is marked as not yet resolved."

    ^(wc mergeState at: self pathNameRelative) == $U

    "Created: / 14-01-2013 / 16:56:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isUntracked
    "An alias for not-tracked"
    ^ self isNotTracked

    "Created: / 14-11-2012 / 23:56:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isUntrackedOrIgnored
    | s |

    s := self status.
    ^s isNotTracked or:[s isIgnored]

    "Created: / 15-11-2012 / 01:23:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isUnversioned
    "An alias for not-tracked"
    ^ self isNotTracked

    "Created: / 14-11-2012 / 23:56:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile::LazyRevision methodsFor:'accessing'!

changeset
    ^changeset isHGChangeset 
        ifTrue:[changeset]
        ifFalse:[changeset := wc repository changesetWithId: changeset]

    "Created: / 22-01-2013 / 13:38:53 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

changesetId
    ^changeset isHGChangesetId
        ifTrue:[changeset]
        ifFalse:[changeset id]

    "Created: / 22-01-2013 / 13:38:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile::LazyRevision methodsFor:'error handling'!

doesNotUnderstand: aMessage
    (HGChangesetFile canUnderstand: aMessage selector) ifFalse:[
        ^ super doesNotUnderstand: aMessage
    ].
    self ensureNotLazy.
    ^aMessage sendTo: (collection at: index).

    "Created: / 22-01-2013 / 13:41:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile::LazyRevision methodsFor:'initialization'!

setCollection: coll index: idx changesetId: csId workingCopy: workCopy path: p
    collection := coll.
    index := idx.
    changeset := csId.
    wc := workCopy.
    path := p.

    "Created: / 22-01-2013 / 13:32:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

setPath: p
    path := p.

    "Created: / 22-01-2013 / 13:48:45 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile::LazyRevision methodsFor:'private'!

ensureNotLazy
    | cs file renamed |

    index ~~ 1 ifTrue:[
        (collection at: index - 1) ensureNotLazy.
    ].

    cs := self changeset.
    file := cs / path.
    collection at: index put: file.
    index ~~ collection size ifTrue:[
        cs changes do:[:chg|
            "/Catch renames...
            (chg isCopied and:[chg path = path]) ifTrue:[
                renamed := chg source.
                index + 1 to: collection size do:[:i|
                    (collection at: i) setPath: renamed.
                ].
                ^self
            ]
        ].
    ].

    "Created: / 22-01-2013 / 13:48:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopyFile class methodsFor:'documentation'!

version_HG

    ^ '$Changeset: <not expanded> $'
!

version_SVN
    ^ '§Id::                                                                                                                        §'
! !