mercurial/HGWorkingCopy.st
author convert-repo
Sun, 29 Jul 2018 03:51:29 +0000
changeset 851 f54518cda3e7
parent 751 f9b0838f03a0
child 864 c854577212b8
permissions -rw-r--r--
update tags

"
stx:libscm - a new source code management library for Smalltalk/X
Copyright (C) 2012-2015 Jan Vrany

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License. 

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
"
"{ Package: 'stx:libscm/mercurial' }"

"{ NameSpace: Smalltalk }"

HGRepositoryObject subclass:#HGWorkingCopy
	instanceVariableNames:'root mergeState'
	classVariableNames:''
	poolDictionaries:''
	category:'SCM-Mercurial-Core'
!

Object subclass:#MergeState
	instanceVariableNames:'wc states'
	classVariableNames:''
	poolDictionaries:''
	privateIn:HGWorkingCopy
!

!HGWorkingCopy class methodsFor:'documentation'!

copyright
"
stx:libscm - a new source code management library for Smalltalk/X
Copyright (C) 2012-2015 Jan Vrany

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License. 

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
"
! !

!HGWorkingCopy methodsFor:'accessing'!

bookmarks
    "Return bookmarks of working copy parent"

    ^ self changeset bookmarks

    "Created: / 20-03-2014 / 08:55:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

branch
    "Return currently checked-out branch"

    | name |

    name := 'default'.
    (root asFilename / '.hg' / 'branch') exists ifTrue:[
        "File DOES contain trailing newline"    
        name := (root asFilename / '.hg' / 'branch') contents first.
    ].
    ^repository branchWithName: name createIfAbsent: true.

    "Created: / 27-11-2012 / 13:51:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 14-01-2013 / 14:23:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

branch: aStringOrHGBranch
    "Set branch for subsequent commits. Returns new branch as HGBranch"

    | name |

    name := aStringOrHGBranch asString.

    self branch name = name ifTrue:[ ^ self ].

    (root asFilename / '.hg' / 'branch') exists ifTrue:[
        (root asFilename / '.hg' / 'branch') 
            copyTo: (root asFilename / '.hg' / 'undo.branch')
    ] ifFalse:[
        "File DOES contain trailing newline"
        (root asFilename / '.hg' / 'undo.branch') writingFileDo:[:s|
            s nextPutLine: 'default'.
        ]    
    ].
    (root asFilename / '.hg' / 'branch') writingFileDo:[:s|
        s nextPutLine: name.
    ].

    "Return HGBranch here"
    ^ aStringOrHGBranch isString ifFalse:[
        aStringOrHGBranch
    ] ifTrue:[
        repository branchWithName: name createIfAbsent: true.
    ]

    "Created: / 10-12-2012 / 03:04:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

changeset
    "Return an HGChangesetId on which the working copy is based on.
     This is parent1"

    ^self parent1

    "Created: / 13-11-2012 / 21:47:45 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 01-04-2013 / 12:44:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

changesetId
    "Return an HGChangeset on which the working copy is based on.
     This is parent1"

    ^self parent1Id

    "Created: / 08-03-2013 / 19:50:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 01-04-2013 / 12:43:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

commitTask
    ^HGCommitTask new temporaryWorkingCopy: self

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

conflicts
    "Return entries with conflicts (both resolved and unresolved)"

    ^self mergeState paths collect:[:p|root / p]

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

heads
    "Return heads of currently checked-out branch"

    ^self branch heads

    "Created: / 27-11-2012 / 21:51:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parent1
    "Return an HGChangeset representing parent1 of the receiver"

    ^repository changesetWithId: self parent1Id.

    "Created: / 01-04-2013 / 12:44:13 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parent1Id
    "Return an HGChangesetId of parent1 of this working copy."

    | id dirstate |

    dirstate := root asFilename / '.hg' / 'dirstate'.
    dirstate exists ifFalse:[ 
        "No changeset yet - fresh repository"
        ^ HGChangesetId null 
    ].

    dirstate readingFileDo:[:s|
        s binary.
        id := HGChangesetId fromBytes: (s next: 20).
    ].

    ^id.

    "Created: / 01-04-2013 / 12:42:52 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parent2
    "Return an HGChangeset representing parent2 of the receiver"

    ^repository changesetWithId: self parent2Id.

    "Created: / 01-04-2013 / 12:52:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parent2Id
    "Return an HGChangesetId of parent1 of this working copy."

    | id dirstate |

    dirstate := root asFilename / '.hg' / 'dirstate'.
    dirstate exists ifFalse:[ 
        "No changeset yet - fresh repository"
        ^ HGChangesetId null 
    ].

    dirstate readingFileDo:[:s|
        s binary.
        s skip: 20.
        id := HGChangesetId fromBytes: (s next: 20).
    ].

    ^id.

    "Created: / 01-04-2013 / 12:43:09 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

path
    "Return a path to the root directory of the receiver as *Filename*. 
     Use #root to get the root working copy file"

    ^repository path

    "Created: / 17-10-2012 / 13:53:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

pathName
    "Return a path to the root directory of the receiver as *String*
     Use #root to get the root working copy file"

    ^repository pathName

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

repository
    ^repository

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

root
    ^ root
!

statusesOf: workingCopyFiles
    "Return a dictionary mapping `workingCopyFiles` to their actual
     status (unmodified, modified, added, missing. removed...)"

    | relativePathNames cmd out statuses |

    statuses := Dictionary new.
    workingCopyFiles notEmpty ifTrue:[
        relativePathNames := workingCopyFiles collect:[ :e | e pathNameRelative ].
        cmd := HGCommand status.
        cmd workingDirectory: repository pathName.
        cmd paths: relativePathNames.
        out := repository execute: cmd.
        self assert: out size == workingCopyFiles size.

        workingCopyFiles do:[:each | 
            statuses at: each put: (out detect:[:pair | pair second = each pathNameRelative]) first.
        ].
    ].
    ^ statuses

    "Created: / 23-02-2017 / 14:38:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 23-02-2017 / 20:03:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopy methodsFor:'accessing-private'!

mergeState
    mergeState isNil ifTrue:[
        mergeState := HGCachedFileData 
                        on: ((Filename named: root pathName) / '.hg' / 'merge' / 'state')
                        reader: [MergeState new setWorkingCopy: self]
    ].
    ^mergeState value

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

!HGWorkingCopy methodsFor:'actions'!

bookmarkAs: aString
    "Creates a new bookmark on working copy parent, make that bookmark active and return it. 
     Raises an HGBookmarkError if bookmark with the same name already exist"

    ^ repository bookmark: nil as: aString.

    "Created: / 20-03-2014 / 08:57:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 20-03-2014 / 18:42:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

commit: message
    "Commits all uncommited changes with given message"

    ^self commit: message files: nil

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

commit: message author: authorOrNil
    "Commits all uncommited changes with given message"

    ^self commit: message files: nil author: authorOrNil

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

commit: message author: authorOrNil date: dateSpecOrNil
    "Commits all uncommited changes with given message"

    ^self commit: message files: nil author: authorOrNil date: dateSpecOrNil

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

commit: message date: dateSpecOrNil
    "Commits all uncommited changes with given message"

    ^self commit: message files: nil author: nil date: dateSpecOrNil

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

commit:message files:files 
    "Commit given files with given message. If files
     is nil, all tracked modified files are commited"

    ^ self commit:message files:files author: nil"means - default"

    "Created: / 12-11-2012 / 22:36:44 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (format): / 17-11-2012 / 01:01:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 07-12-2012 / 11:44:14 / jv"
!

commit:message files:files author: authorOrNil
    "Commit given files with given message and author. If files
     is nil, all tracked modified files are commited"

    ^self commit:message files:files author: authorOrNil date: nil

    "Created: / 07-12-2012 / 11:41:52 / jv"
    "Modified: / 07-12-2012 / 15:45:38 / jv"
    "Modified: / 01-02-2013 / 14:28:34 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

commit:message files:files author: authorOrNil amend: amend
    "Commit given files with given message and author. 
     If `files` is nil, all tracked modified files are commited.
     If `authorOrNil` is nil, then default author (as configured in .hgrc)
     is used.
     If `amend` is true, previous changeset is amended (by means of `hg commit --amend`)
     instead creating a new changeset with current one as parent.
     "

    ^self commit:message files:files author: authorOrNil date: nil amend: amend.

    "Created: / 25-08-2015 / 16:01:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

commit:message files:files author: authorOrNil date: dateSpecOrNil
    "Commit given files with given message, author and date
     (if provided). If files is nil, all tracked modified files 
     are commited.

     Date can be a Timestamp or a String
     "
    ^ self commit:message files:files author: authorOrNil date: dateSpecOrNil amend: false

    "Created: / 01-02-2013 / 14:28:03 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 25-08-2015 / 16:02:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

commit:message files:files author: authorOrNil date: dateSpecOrNil amend: amend
    "Commit given files with given message, author and date
     (if provided). If files is nil, all tracked modified files 
     are commited.
     If `authorOrNil` is nil, then default author (as configured in .hgrc)
     is used.
     Date can be a Timestamp or a String
     If `amend` is true, previous changeset is amended (by means of `hg commit --amend`)
     instead creating a new changeset with current one as parent.                  
     "

    | author |

    author := authorOrNil.
    author isNil ifTrue:[
        author := HGAuthorQuery query.
        author isNil ifTrue:[
            author := repository config get: #(ui username) default: nil.
            author isNil ifTrue:[
                HGCommitError newException
                    parameter: { repository . message . files };
                    messageText: 'Commit author not specified!!';
                    raise
            ].
        ].
    ].

    ^self repository execute:
        (HGCommand commit
            workingDirectory:root pathName;
            message:message;
            files:files;
            author: author;
            date: dateSpecOrNil;
            amend: amend;
            yourself).

    "Created: / 25-08-2015 / 16:02:03 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

commit:message files:files date: dateSpecOrNil
    "Commit given files with given message and author. If files
     is nil, all tracked modified files are commited"

    ^self commit:message files:files author: nil date: dateSpecOrNil

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

merge: aChangesetOrChangesetId
    "Merge given changeset into workinf copy."

    ^self repository execute:
        (HGCommand merge
            workingDirectory: self path;
            revision: aChangesetOrChangesetId asHGChangesetId asString;
            tool: 'internal:dump';
            yourself)

    "Created: / 14-01-2013 / 15:14:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-03-2013 / 22:59:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

track: workingCopyFiles
    "Make sure that all `workingCopyFiles` are tracked by the working copy
     so subsequent #commit: would commit them to the repository."
        
    | statuses workingCopyFilesToAdd |

    statuses := self statusesOf: workingCopyFiles.
    workingCopyFilesToAdd := workingCopyFiles select:[:e | (statuses at: e) isNotTracked ].
    workingCopyFilesToAdd notEmpty ifTrue:[
        repository execute:
                (HGCommand add
                    workingDirectory: repository pathName;
                    paths: (workingCopyFilesToAdd collect:[ :e| e pathNameRelative ]);
                    yourself)
    ].

    "Created: / 23-02-2017 / 15:11:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (comment): / 23-02-2017 / 16:29:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

update
    "Update the working copy to the latest rev in current branch"

    ^self update: self branch name

    "Created: / 10-12-2012 / 11:25:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

update: revisionOrBranch

    self changeset id = revisionOrBranch ifTrue:[ ^ self ].

    ^self repository execute:
        (HGCommand update
            workingDirectory: self path;
            revision: revisionOrBranch asString;
            yourself)

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

!HGWorkingCopy methodsFor:'initialization'!

setRepository: aHGRepository
    super setRepository: aHGRepository.
    root :=HGWorkingCopyFile wc: self path: repository path.

    "Created: / 19-09-2012 / 09:43:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 19-10-2012 / 15:44:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopy methodsFor:'inspecting'!

browse
    "Opens a file browser on the working copy"

    root browse

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

!HGWorkingCopy methodsFor:'instance creation'!

/ aString
    ^root construct: aString

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

construct: aString

    ^root construct: aString

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

!HGWorkingCopy::MergeState methodsFor:'accessing'!

at: path
    ^self states at: path ifAbsent:[$C"like clean"].

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

includesKey: path
    ^self states includesKey: path

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

paths
    ^self states keys

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

!HGWorkingCopy::MergeState methodsFor:'initialization'!

setWorkingCopy: anHGWorkingCopy
    wc := anHGWorkingCopy.

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

!HGWorkingCopy::MergeState methodsFor:'private'!

states
    states isNil ifTrue:[
        states := wc repository execute:
                    (HGCommand resolve__list
                        workingDirectory: wc pathName;
                        yourself) .
    ].
    ^states

    "Created: / 14-01-2013 / 16:47:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-03-2013 / 23:00:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGWorkingCopy class methodsFor:'documentation'!

version_HG

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

version_SVN
    ^ '§Id::                                                                                                                        §'
! !