GitSourceCodeManager.st
author Claus Gittinger <cg@exept.de>
Mon, 20 Aug 2018 10:11:25 +0200
changeset 4346 6604af2f1554
parent 4286 3286be6412f3
permissions -rw-r--r--
#OTHER by cg class: FileBasedSourceCodeManager class removed: #version_FileRepository

"
 COPYRIGHT (c) 2012 by Claus Gittinger
              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:libbasic3' }"

"{ NameSpace: Smalltalk }"

AbstractSourceCodeManager subclass:#GitSourceCodeManager
	instanceVariableNames:''
	classVariableNames:'Verbose DefaultRepository PerModuleRepositories DisabledModules
		RecentlyCheckedModulesAndPackages GitCommandTimeout GitTempDir
		GitCommandSemaphore GitExecutable GitCommitOptions
		GitUpdateOptions WorkDirectory'
	poolDictionaries:''
	category:'System-SourceCodeManagement'
!

!GitSourceCodeManager class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2012 by Claus Gittinger
              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
"
    Attention: this will be removed soon - it has been obsoleted by the
    new mercurial support found in libscm/mercurial.

    SourceCodeManager which accesses the sourcecode through Git.
    ongoing work - unfinished and unusable

    Comment:
        The code here is a copy-paste mess; it definitely needs some cleanup...

    [author:]
        Claus Gittinger

    [see also:]
"
! !

!GitSourceCodeManager class methodsFor:'initialization'!

forgetDisabledModules
    DisabledModules := nil.
!

gitCommandSemaphore
    GitCommandSemaphore isNil ifTrue:[
        GitCommandSemaphore := (Semaphore new:10) name:'Concurrent GIT Commands'.    "/ at most 10 git activities concurrently
    ].
    ^ GitCommandSemaphore
!

initialize
    | s |

    "/ GitCommandSemaphore := (Semaphore new:10) name:'Concurrent GIT Commands'.    "/ at most 10 git activities concurrently
    GitCommitOptions := ''.
    GitUpdateOptions := ''.
    DisabledModules := nil.
    PerModuleRepositories isNil ifTrue:[
        PerModuleRepositories := Dictionary new.
    ].

    "/ will do that lazy later, to avoid making startup slower and slower...
"/    ok := OperatingSystem canExecuteCommand:(self gitExecutable).
"/    ok ifFalse:[
"/        'GitSourceCodeManager [warning]: disabled because no >>git<< command was found' infoPrintCR.
"/        ^ self
"/    ].

    "/
    "/ optionally set the WorkTreeDirectoryName from $STX_WORKTREE;
    "/ if non-nil, a working tree is kept there
    "/ and updated/commited files are not removed.
    "/ If you use a regular (make-) tree, 
    "/ set WorkTreeDirectoryName (or the environment variable) to that.
    "/
    "/ this is not yet finished.
    "/
    s := OperatingSystem getEnvironment:'STX_WORKTREE'.
    s notNil ifTrue:[
        WorkTreeDirectoryName := s.
        UseWorkTree := true.
    ]

    "
     AbstractSourceCodeManager initialize
     GitSourceCodeManager initialize
    "

    "Created: / 04-11-1995 / 19:14:38 / cg"
    "Modified: / 19-12-1995 / 14:25:46 / stefan"
    "Modified (comment): / 20-03-2012 / 19:05:00 / cg"
!

initializeForRepository:aDirectoryName
    "reinitialize. 
     Can be used from the launcher to change/configure the repository."

    |dir|

    self repositoryName:aDirectoryName.
    AbstractSourceCodeManager initialize.
    GitSourceCodeManager initialize.

    (dir := aDirectoryName asFilename) exists ifFalse:[
        dir recursiveMakeDirectory.
    ].
    (dir construct:'.git') exists ifFalse:[
        self executeGitCommand:'init' inDirectory:dir
    ].

    "Created: / 13-08-1997 / 17:20:57 / cg"
    "Modified: / 25-09-1997 / 12:28:05 / stefan"
    "Modified: / 23-07-2012 / 16:16:57 / cg"
!

initializeRepository
    self initializeForRepository:(self repositoryName)

    "Created: / 02-03-2012 / 16:56:21 / cg"
! !

!GitSourceCodeManager class methodsFor:'accessing'!

executeGitCommand:cmd inDirectory:dir
    |retCode out err|

    retCode := self 
        executeGitCommand:cmd 
        outputTo:(out := WriteStream on:(String new:100)) 
        errorTo:(err := WriteStream on:(String new:100))     
        inDirectory:dir.
    retCode ifFalse:[
        Transcript showCR:'-----------------------------------------------'.
        Transcript showCR:'out:'.
        Transcript showCR:out contents.
        Transcript showCR:'err:'.
        Transcript showCR:err contents.
    ].
    ^ retCode

    "Created: / 23-07-2012 / 16:17:06 / cg"
!

executeGitCommand:cmd outputTo:outStreamOrNil errorTo:errStreamOrNil inDirectory:dir
    ^ OperatingSystem 
        executeCommand:'git ',cmd 
        outputTo:outStreamOrNil 
        errorTo:errStreamOrNil     
        inDirectory:dir asFilename pathName.

    "Created: / 24-07-2012 / 09:33:29 / cg"
!

gitBinDirectory:ignoredString 
    "ignored - for backward compatibility (to read old settings files)"
    "Created: / 14-01-2012 / 20:49:46 / cg"
!

gitCommandTimeout
    ^ GitCommandTimeout ? ("360" 120 seconds)

    "Modified (comment): / 08-01-2012 / 19:02:44 / cg"
    "Created: / 02-03-2012 / 15:23:40 / cg"
!

gitCommandTimeout:aTimeDuration
    GitCommandTimeout := aTimeDuration asTimeDuration.

    "Created: / 02-03-2012 / 15:23:46 / cg"
!

gitCommitOptions
    ^ GitCommitOptions ? ''

    "Created: / 02-03-2012 / 15:23:51 / cg"
!

gitCommitOptions:aString 
    GitCommitOptions := aString.

    "Created: / 02-03-2012 / 15:23:57 / cg"
!

gitExecutable
    "return the name of the git executable."

    ^ GitExecutable ? 'git'

    "Created: / 02-03-2012 / 15:24:02 / cg"
!

gitExecutable:aString
    "set the name of the git executable."

    GitExecutable := aString isEmptyOrNil ifTrue:[
        nil
    ] ifFalse:[
        aString
    ].

    "Modified: / 21-09-2006 / 16:41:33 / cg"
    "Created: / 02-03-2012 / 15:24:07 / cg"
    "Modified: / 03-08-2017 / 22:44:40 / mawalch"
!

gitTmpDirectory
    "return the name of the tmp repository.
     That's the directory, where temporary files are created for checkin/checkout.
     If nil, the system's default tempDirectory is used."

    ^ (GitTempDir ? Filename tempDirectory pathName)

    "
     CVSTempDir := nil   
    "

    "Created: / 02-03-2012 / 15:24:12 / cg"
!

gitTmpDirectory:aPathNameString
    "set the name of the tmp repository.
     That's the directory, where temporary files are created for checkin/checkout.
     If nil, the system's default tempDirectory is used."

    GitTempDir := aPathNameString

    "Created: / 02-03-2012 / 15:24:18 / cg"
!

gitUpdateOptions
    ^ GitUpdateOptions ? ''

    "Created: / 02-03-2012 / 15:24:24 / cg"
!

gitUpdateOptions:aString 
    GitUpdateOptions := aString.

    "Created: / 02-03-2012 / 15:24:28 / cg"
!

knownModules
    "return the modules, we currently know"

    PerModuleRepositories isEmptyOrNil ifTrue:[^ #() ].
    ^ PerModuleRepositories keys

    "Modified: / 14-01-2012 / 21:23:37 / cg"
!

knownRepositories
    "return the modules, we currently know"

    ^ PerModuleRepositories values copyWith:DefaultRepository

    "Modified: / 14-01-2012 / 21:23:59 / cg"
!

repositoryForPackage:packageId
    "superclass AbstractSourceCodeManager class says that I am responsible to implement this method"

    ^self getGitRepositoryForModule: ( packageId upTo:$:)

    "Modified: / 10-10-2011 / 19:38:47 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 02-03-2012 / 15:25:19 / cg"
!

repositoryInfoPerModule
    "return the dictionary, which associates gitRepositories to module names.
     If no entry is contained in this dictionary for some module,
     the default crepository will be used."

    ^ PerModuleRepositories ? #()

    "Modified (comment): / 02-03-2012 / 15:25:28 / cg"
!

repositoryInfoPerModule:aDictionary
    "set the dictionary, which associates repositories to module names.
     If no entry is contained in this dictionary for some module,
     the default repository will be used."

    PerModuleRepositories := aDictionary

    "Modified: / 14-01-2012 / 21:25:21 / cg"
!

repositoryName
    "return the name of the global repository.
     This is used, if no per-module repository is defined."

    ^ DefaultRepository

    "Created: / 14-09-1996 / 13:22:05 / cg"
    "Modified: / 14-01-2012 / 21:25:42 / cg"
!

repositoryName:aDirectoryName
    "set the name of the repository;
     that's the name of the global repository, which is used 
     if no specific repository was defined for a module."

    DisabledModules := nil.
    DefaultRepository := aDirectoryName.

    "Created: / 14-09-1996 / 13:22:24 / cg"
    "Modified: / 14-01-2012 / 21:26:17 / cg"
!

repositoryName:aRepositoryName forModule:aModuleName
    "set the repository which provides the sources for all 
     classes in a particular module.
     This can be used from an rc-script, to specify a repository
     for a particular module.
     If left unspecified, the global (i.e. fallBack) repository is used."

    DisabledModules := nil.
    PerModuleRepositories at:aModuleName put:aRepositoryName

    "Modified (comment): / 14-01-2012 / 21:27:08 / cg"
!

repositoryNameForModule:aModuleName
    "return the repository which provides the sources for all 
     classes in a particular module.
     Nil is returned for unspecified moduleRoots; in this case, 
     the global (i.e. fallBack) repository will be used for source access."

    ^ PerModuleRepositories at:aModuleName ifAbsent:nil.

    "Created: / 19-09-1997 / 06:13:06 / cg"
!

repositoryNameForPackage:packageId     
    ^ self repositoryNameForModule:(packageId upTo:$:)

    "Created: / 10-10-2011 / 19:44:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (format): / 25-07-2012 / 18:23:06 / cg"
!

workDirectory
    ^ WorkDirectory

    "Created: / 03-03-2012 / 11:08:29 / cg"
!

workDirectory:aPath
    WorkDirectory := aPath

    "Created: / 03-03-2012 / 11:08:37 / cg"
! !

!GitSourceCodeManager class methodsFor:'basic access'!

checkinClass:aClassOrNil fileName:classFileName directory:packageDir module:moduleDir source:sourceFileOrNil logMessage:logMessage force:force
    "checkin of a class into the source repository.
     Return true if ok, false if not."

    |path relPath logArg out err|

    relPath := (moduleDir asFilename construct:packageDir) construct:classFileName.
    path := self repositoryName asFilename construct:relPath.
    sourceFileOrNil notNil ifTrue:[
        sourceFileOrNil asFilename moveTo: path.
    ].

    (self executeGitCommand:'add ',relPath name inDirectory:self repositoryName) ifFalse:[
        self halt:'git command failed'
    ].

    out := WriteStream on:(String new:100).
    err := WriteStream on:(String new:100).
    (self executeGitCommand:'status --porcelain' outputTo:out errorTo:err inDirectory:self repositoryName) ifFalse:[
        self halt:'git command failed'
    ].
    (out contents withoutSeparators isEmptyOrNil) ifTrue:[
        (err contents withoutSeparators isEmptyOrNil) ifTrue:[
            "/ nothing to commit
            ^ true
        ].
    ].
    logArg := logMessage copyReplaceAll:$" withAll:''''''.
    (self executeGitCommand:'commit -m "',logArg,'"' inDirectory:self repositoryName) ifFalse:[
        self halt:'git command failed'
    ].
    ^ true

    "Created: / 23-07-2012 / 20:05:14 / cg"
! !

!GitSourceCodeManager class methodsFor:'basic administration'!

addFile:fileName inDirectory:dirPath
    self executeGitCommand:('add "',fileName,'"') inDirectory:dirPath

    "Created: / 25-07-2012 / 23:07:57 / cg"
!

checkForExistingContainer:fileName inModule:moduleName directory:packageDirName
    "check for a container to exist. Return a boolean result."

    |path subDir|

    path := (self repositoryName asFilename construct:moduleName) construct:packageDirName.
    path exists ifFalse:[
        subDir := moduleName asFilename construct:packageDirName.
        self executeGitCommand:'checkout ',subDir name inDirectory:self repositoryName.
    ].
    ^ (path construct:fileName) exists

    "Created: / 23-07-2012 / 16:21:02 / cg"
    "Modified (format): / 24-02-2017 / 11:32:22 / cg"
!

checkForExistingModule:moduleName
    "check for a module to be present"

    |path subDir|

    path := self repositoryName asFilename construct:moduleName.
    path exists ifFalse:[
        subDir := moduleName asFilename.
        self executeGitCommand:'checkout ',subDir name inDirectory:self repositoryName.
        self breakPoint:#cg.
    ].
    ^ path exists

    "Created: / 23-07-2012 / 18:44:27 / cg"
!

checkForExistingModule:moduleName directory:packageDir
    "check for a package directory to be present"

    |path subDir|

    path := (self repositoryName asFilename construct:moduleName) construct:packageDir.
    path exists ifFalse:[
        subDir := moduleName asFilename construct:packageDir.
        self executeGitCommand:'checkout ',subDir name inDirectory:self repositoryName.
    ].
    ^ path exists

    "Created: / 23-07-2012 / 19:07:34 / cg"
!

checkin:containerFilename text:someText directory:packageDir module:moduleDir logMessage:logMessage force:force onBranch:branchNameOrNil
    |path relPath|

    branchNameOrNil notNil ifTrue:[self error:'branches are not yet supported'].
    
    relPath := (moduleDir asFilename construct:packageDir) construct:containerFilename.
    path := self repositoryName asFilename construct:relPath.
    path contents: someText.

    ^ self checkinClass:nil 
        fileName:containerFilename 
        directory:packageDir 
        module:moduleDir
        source:nil
        logMessage:logMessage 
        force:force

    "Created: / 05-12-2017 / 23:28:56 / cg"
!

commitRepository:repositoryDirectory logMessage:logMessage
    |logArg|

    self 
        executeGitCommand:('status') 
        outputTo:Transcript 
        errorTo:Transcript     
        inDirectory:repositoryDirectory.

    logArg := logMessage copyReplaceAll:$" withAll:''''''.
    self 
        executeGitCommand:('commit -m "',logArg,'"') 
        outputTo:Transcript 
        errorTo:Transcript     
        inDirectory:repositoryDirectory.

    "Created: / 25-07-2012 / 22:47:57 / cg"
!

createContainerFor:aClass inModule:moduleName directory:dirName container:fileName
    "create a new container & check into it an initial version of aClass"

    ^ self shouldImplement
!

createModule:moduleName
    "create a new module directory"

    |dir|

    dir := self repositoryName asFilename construct:moduleName.
    dir exists ifTrue:[^ true].
    dir recursiveMakeDirectory.
    ^ dir exists.

    "Created: / 23-07-2012 / 19:04:51 / cg"
!

createModule:moduleName directory:directory
    "create a new package directory"

    |dir|

    dir := (self repositoryName asFilename construct:moduleName) construct:directory.
    dir exists ifTrue:[^ true].
    dir recursiveMakeDirectory.
    ^ dir exists.

    "Created: / 23-07-2012 / 19:08:22 / cg"
!

initialRevisionStringFor:aClass inModule:moduleDir directory:packageDir container:fileName
    "return a string usable as initial revision string"

    ^ self 
        revisionStringFor:aClass 
        inModule:moduleDir 
        directory:packageDir 
        container:fileName 
        revision:'1'

    "Created: / 23-07-2012 / 18:40:25 / cg"
!

updateRepository:repositoryDirectory
    self executeGitCommand:'update' inDirectory:repositoryDirectory

    "Created: / 25-07-2012 / 18:55:56 / cg"
! !

!GitSourceCodeManager class methodsFor:'debugging'!

verboseSourceCodeAccess
    ^ Verbose ? false
!

verboseSourceCodeAccess:aBoolean
    Verbose := aBoolean
! !

!GitSourceCodeManager class methodsFor:'queries'!

isContainerBased
    ^ false

    "Created: / 24-07-2012 / 18:21:32 / cg"
!

isExperimental
    ^ true

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

isResponsibleForPackage:aString
    ^true.

    "Created: / 09-07-2011 / 14:32:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (comment): / 02-03-2012 / 15:05:14 / cg"
!

managerTypeName
    ^ 'Git'

    "Created: / 16-08-2006 / 11:05:56 / cg"
!

nameOfVersionMethodForExtensions
    ^ #'extensionsVersion_Git'

    "Modified: / 02-03-2012 / 15:05:02 / cg"
!

nameOfVersionMethodInClasses
    ^ #'version_Git'

    "Modified: / 02-03-2012 / 15:04:56 / cg"
!

settingsApplicationClass
    "link to my settings application (needed for the settings dialog"

    ^ GitSourceCodeManagementSettingsAppl

    "Created: / 19-04-2011 / 12:43:29 / cg"
    "Modified: / 02-03-2012 / 15:04:50 / cg"
! !

!GitSourceCodeManager class methodsFor:'saving'!

savePreferencesOn:aStream
    aStream nextPutLine:'GitSourceCodeManager notNil ifTrue:['.
    self repositoryInfoPerModule notEmptyOrNil ifTrue:[
        aStream nextPutLine:'    GitSourceCodeManager repositoryInfoPerModule:' , self repositoryInfoPerModule storeString , '.'.
    ].
    GitExecutable notNil ifTrue:[
        aStream nextPutLine:'    GitSourceCodeManager gitExecutable:' , GitExecutable storeString , '.'.
    ].
    (Smalltalk at:#SourceCodeManager) == self ifTrue:[
        aStream nextPutLine:'    Smalltalk at:#SourceCodeManager put: GitSourceCodeManager.'.
        aStream nextPutLine:'    GitSourceCodeManager initializeForRepository:' , self repositoryName storeString , '.'.
    ] ifFalse:[
        aStream nextPutLine:'    GitSourceCodeManager repositoryName:' , self repositoryName storeString , '.'.
    ].
    aStream nextPutLine:'].'.

    "Created: / 09-11-2006 / 15:09:25 / cg"
    "Modified: / 02-03-2012 / 15:17:42 / cg"
! !

!GitSourceCodeManager class methodsFor:'source code administration'!

revisionInfoFromString:aString 
    "{ Pragma: +optSpace }"

    "return a VersionInfo object filled with revision info.
     This extracts the relevant info from aString."

    ^ self revisionInfoFromStandardVersionString:aString

    "
     self revisionInfoFromString:'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826' 
    "

    "Created: / 23-07-2012 / 19:02:56 / cg"
! !

!GitSourceCodeManager class methodsFor:'testing'!

isGit
    ^ true

    "Created: / 02-03-2012 / 15:17:19 / cg"
! !

!GitSourceCodeManager class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !


GitSourceCodeManager initialize!