git/GitRepository.st
author vranyj1@bd9d3459-6c23-4dd9-91de-98eeebb81177
Sun, 30 Sep 2012 22:13:12 +0000
changeset 23 5cbdd3cb7ce4
parent 20 24ae01b36807
child 24 bce2a03d1070
permissions -rw-r--r--
- GitPrimitives changed: #prim_git_remote_list:repo: #prim_git_strarray_copy:src: #prim_git_strarray_free: - GitLibraryObject added: #documentation comment/format in: #getHandleClass #setHandleFromRef: - GitCommand added:42 methods category of: - GitRepository class definition added:6 methods category of: - stx_libscm_git changed: #classNamesAndAttributes #extensionMethodNames #preRequisites - GitStringArray class definition added:7 methods changed: #count #strings category of: #count #count: #strings #strings: - GitWorkingCopy class definition added: #repository changed: #setRepository: - GitObject class definition - GitRemote added:7 methods - GitRepositoryObject added:6 methods - GitTests added: #test_02a - GitStructure added: #new - GitWorkingCopyEntry added: #isChanged - extensions ...

"{ Package: 'stx:libscm/git' }"

GitLibraryObject subclass:#GitRepository
	instanceVariableNames:'path workdir index remotes'
	classVariableNames:''
	poolDictionaries:'GitObjectType GitStatusCodes'
	category:'SCM-Git-Model'
!


!GitRepository class methodsFor:'instance creation'!

clone: url to: stringOrDirectory
    | dir ref fetchStats checkoutStats options |

    (url asString startsWith: 'file://') ifTrue:[
        "Arghhh...local transport not yet supported by libgit2, use command line..."
        GitCommand clone: url to: stringOrDirectory pathName.
        ^self open: stringOrDirectory pathName
    ].


    dir := stringOrDirectory asFilename.
    dir exists ifTrue:[
        GitError raiseErrorString:'Destination directory already exists'.
        ^self
    ].
    dir directory isDirectory ifFalse:[
        GitError raiseErrorString:'Parent directory for destination does not exists'.
        ^self
    ].

    ref := ByteArray new: ExternalBytes sizeofPointer.
    fetchStats := GitIndexerStatsStructure new.
    checkoutStats := GitIndexerStatsStructure new.
    options := GitCheckoutOptions new.
    options strategyCreateMissing.
    GitError raiseIfError: 
        (GitPrimitives prim_git_clone: ref origin_url: url asString workdir_path: dir pathName fetch_stats: fetchStats checkout_stats: checkoutStats checkout_opts: options).
    ^self new 
        setHandleFromRef:ref;
        setPath: dir pathName;
        yourself

    "Created: / 01-10-2012 / 00:09:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

open: aString
    "Opens an existing git repository on given path"

    | ref |

    ref := ByteArray new: ExternalBytes sizeofPointer.
    GitError raiseIfError: (GitPrimitives prim_git_repository_open: ref path: aString).
    ^self new 
        setHandleFromRef:ref;
        setPath: aString;
        yourself

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

!GitRepository class methodsFor:'accessing'!

structSize
    "Returns size of undelaying structure in bytes"

    ^0
! !

!GitRepository methodsFor:'accessing'!

head
    | ref err |
    ref := ByteArray new: ExternalBytes sizeofPointer.
    err := GitPrimitives prim_git_repository_head: ref repo: handle.
    GitError raiseIfError: err.

    ^GitReference new setHandleFromRef: ref

    "Created: / 25-09-2012 / 10:48:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

path
    ^ path
!

remotes

    remotes isNil ifTrue:[
        | list  |

        list := GitStringArray new.
        GitError raiseIfError:(GitPrimitives prim_git_remote_list: list repo: handle).
        remotes := Dictionary new.
        list do:[:name|
            remotes at: name put: (self remoteNamed: name)
        ].
    ].
    ^remotes.

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

workingCopy
    self isBare ifTrue:[
        GitError raiseErrorString: 'Repository is bare'.
    ].
    ^GitWorkingCopy new setRepository: self

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

workingCopyOn: aStringOrFilename
    | repo directory |

    directory := aStringOrFilename asFilename.
    directory exists ifFalse:[
        [ directory recursiveMakeDirectory ] on: Error do:[:cause|
            GitError new
                parameter: cause;
                messageText: 'Cannot create directory for working copy';
                raise.
        ]
    ] ifTrue:[
        directory isDirectory ifFalse:[
            GitError raiseErrorString: 'Given directory is not a directory'.
        ]
    ].

    repo := self workdir = aStringOrFilename asFilename 
                ifTrue:[self]
                ifFalse:[self copy workdir: aStringOrFilename asString update: false].
    ^GitWorkingCopy new setRepository: repo

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

!GitRepository methodsFor:'actions'!

commit: message tree: tree parents: parents update: refOrNil commiter: commiterOrNil author: authorOrNil
    | oid err committer author parentsA |

    committer := commiterOrNil notNil 
                    ifTrue:[committer copyNow]
                    ifFalse:[GitCommitterQuery query copyNow].
    author := commiterOrNil notNil
                    ifTrue:[author]
                    ifFalse:[committer copy].
    parentsA := ByteArray new: (ExternalBytes sizeofPointer * parents size).
    parents withIndexDo:[:each :idx|
        parentsA pointerAt: (1 + ((idx - 1) * ExternalBytes sizeofPointer)) put: each getHandle.
    ].
    oid := GitOid new.

    err := GitPrimitives prim_git_commit_create: oid 
                                           repo: handle 
                                     update_ref: (refOrNil notNil ifTrue:[refOrNil name] ifFalse:[nil]) 
                                         author: author getHandle
                                      committer: committer getHandle
                               message_encoding: 'utf-8'
                                        message: message utf8Encoded
                                           tree: tree getHandle
                                   parent_count: parents size
                                        parents: parentsA.
    GitError raiseIfError: err.

    ^self lookup: oid type: OBJ_COMMIT.

    "Created: / 25-09-2012 / 14:51:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

push: aGitRemote
    "pushes all changes to given remote repository"

    GitCommand push
        remote: aGitRemote name;
        execute

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

!GitRepository methodsFor:'copying'!

cloneTo: aStringOrFilename"^<GitRepository>"

    "Clones the receiver to given directory. And equivalent to

        git clone <path> <clonePath>

    "

    ^GitRepository 
        clone: ('file://', self path ) 
           to: (aStringOrFilename)

    "Created: / 30-09-2012 / 19:07:03 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

shallowCopy

    ^self class open: path

    "Created: / 10-09-2012 / 19:04:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GitRepository methodsFor:'initialization'!

setPath: aString
    path := aString

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

!GitRepository methodsFor:'initialization & release'!

free
    handle notNil ifTrue:[
        GitPrimitives prim_git_object_free: handle. 
        handle := nil.
    ].

    "Created: / 17-09-2012 / 21:16:44 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GitRepository methodsFor:'lookup'!

lookup: oid
    "Lookup an object with given OID"
    ^self lookup: oid type: OBJ_ANY

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

lookup: oid type: typeId
    "Lookup an object with given OID"

    | ref err type obj |

    oid class == GitOid ifFalse:[
        self error: 'Passed oid is not a GitOid'.
        ^nil.
    ].
    ref := ByteArray new: ExternalBytes sizeofPointer.
    err := GitPrimitives prim_git_object_lookup: ref repo: handle id: oid type: typeId.
    GitError raiseIfError: err.

    typeId == OBJ_ANY ifTrue:[
        obj := ExternalAddress new setAddressFromBytes:ref.
        type := GitPrimitives prim_git_object_type: obj.
    ] ifFalse:[
        type := typeId.
    ].
    obj := GitObject newForType: type.
    ^obj
        setHandleFromRef: ref;
        setOid: oid;
        setRepository: self;
        yourself

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

!GitRepository methodsFor:'private'!

checkout: object
    | err stats options |
    object isGitTreeish ifFalse:[
        GitError raiseErrorString:'Invalid argument: ', object printString.
        ^self.
    ].

    stats := GitIndexerStatsStructure new.
    options := GitCheckoutOptions new.
    options strategyCreateMissing.
    err := GitPrimitives prim_git_checkout_tree: handle treeish: object getHandle opts: options stats: stats. 
    GitError raiseIfError: err.

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

!GitRepository methodsFor:'private-accessing'!

getHandleClass
    ^GitRepositoryHandle

    "Created: / 17-09-2012 / 21:20:59 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

remoteNamed: name
    | ref err |

    ref := ByteArray new: ExternalBytes sizeofPointer.
    err := GitPrimitives prim_git_remote_load: ref repo: handle  name: name.
    GitError raiseIfError: err.
    ^GitRemote new
        setHandleFromRef: ref;
        setRepository: self;
        setName: name

    "Created: / 30-09-2012 / 20:16:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

statusOf: aFilename
    | ref err |

    (aFilename pathName startsWith: self workdir pathName ) ifFalse:[
        GitError raiseErrorString: 'Given path is within working copy'.
        ^0.
    ].

    ref := ByteArray new: ExternalBytes sizeofInt.
    err := GitPrimitives prim_git_status_file: ref repo: handle path: (aFilename pathName copyFrom: (workdir pathName size + 2)).
    GitError raiseIfError: err.

    ^ref longAt:1

    "Created: / 24-09-2012 / 22:27:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

workdir
    "Get the path of the working directory for this repository or nil, if
     repository is bare"

    self isBare ifTrue: [ ^ nil ].
    workdir isNil ifTrue:[
        workdir := GitPrimitives prim_git_repository_workdir: handle.
        workdir := workdir asFilename.
    ].
    ^workdir

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

workdir: aStringOrFilename update: aBoolean
    "Set the path of the working directory for this repository.
     If update is true, then create/update gitlink in workdir and set 
     config 'core.worktree' (if workdir is not the parent of the 
     .git directory)"

    GitError raiseIfError: 
        (GitPrimitives prim_git_repository_set_workdir: handle 
                                               workdir: aStringOrFilename asString
                                        update_gitlink: (aBoolean ifTrue:[1] ifFalse:[0])).
    workdir := aStringOrFilename asFilename.

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

!GitRepository methodsFor:'testing'!

headIsDetached
    ^(GitPrimitives prim_git_repository_head_detached: handle) == 1

    "Created: / 25-09-2012 / 11:06:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

headIsOrphan
    ^(GitPrimitives prim_git_repository_head_orphan: handle) == 1

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

isBare
    "Check if a repository is bare"

    ^(GitPrimitives prim_git_repository_is_bare: handle) == 1

    "Created: / 10-09-2012 / 19:11:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isEmpty
    "An empty repository has just been initialized and contains
     no commits."

    ^(GitPrimitives prim_git_repository_is_empty: self) == 1

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

!GitRepository class methodsFor:'documentation'!

version_SVN
    ^ '$Id::                                                                                                                        $'
! !