MCWorkingCopy.st
author Claus Gittinger <cg@exept.de>
Tue, 08 May 2018 19:58:20 +0200
changeset 1043 aeecdb5610e4
parent 917 e92da349ec56
child 1156 b6ca954ebfc9
permissions -rw-r--r--
#FEATURE by cg class: MCPostscriptDefinition added: #asChange

"{ Package: 'stx:goodies/monticello' }"

MCPackageManager subclass:#MCWorkingCopy
	instanceVariableNames:'versionInfo ancestry counter repositoryGroup requiredPackages'
	classVariableNames:''
	poolDictionaries:''
	category:'SCM-Monticello-Versioning'
!


!MCWorkingCopy class methodsFor:'as yet unclassified'!

adoptVersionInfoFrom: anInstaller
	|viCache|
	viCache := Dictionary new.
	anInstaller versionInfo keysAndValuesDo: [:packageName :info |
		(self forPackage: (MCPackage named: packageName))
			versionInfo: (self infoFromDictionary:  info cache: viCache)].
	[anInstaller clearVersionInfo] on: Error do: ["backwards compat"].
!

ancestorsFromArray: anArray cache: aDictionary
	^ anArray ifNotNil: [anArray collect: [:dict | self infoFromDictionary: dict cache: aDictionary]]
!

infoFromDictionary: aDictionary cache: cache
	| id |
	id _ aDictionary at: #id.
	^ cache at: id ifAbsentPut:
		[MCVersionInfo
			name: (aDictionary at: #name)
			id: (aDictionary at: #id)
			message: (aDictionary at: #message)
			date: (aDictionary at: #date)
			time: (aDictionary at: #time)
			author: (aDictionary at: #author)
			ancestors: (self ancestorsFromArray: (aDictionary at: #ancestors) cache: cache)]
!

initialize
	Smalltalk 
		at: #MczInstaller
		ifPresent: [:installer | self adoptVersionInfoFrom: installer].
	self updateInstVars.
	"Temporary conversion code -- remove later"
	registry ifNotNil:[registry rehash]. "changed #="
	self allInstancesDo:[:each| "moved notifications"
		Smalltalk at: #SystemChangeNotifier ifPresent:[:cls|
			cls uniqueInstance noMoreNotificationsFor: each.
		].
	].
	self registerForNotifications.
!

updateInstVars
	self allInstances do: [:ea | ea updateInstVars]
! !


!MCWorkingCopy methodsFor:'accessing'!

ancestors
	^ ancestry ancestors
!

ancestry
	^ ancestry
!

clearRequiredPackages
	requiredPackages _ nil
!

currentVersionInfo
	^ (self needsSaving or: [ancestry ancestors isEmpty])
		ifTrue: [self newVersion info]
		ifFalse: [ancestry ancestors first]
!

description
	^ self packageNameWithStar, ' (', ancestry ancestorString, ')'
!

needsSaving
	^ self modified or: [self requiredPackages anySatisfy: [:ea | ea workingCopy needsSaving]]
!

requirePackage: aPackage
	(self requiredPackages includes: aPackage) ifFalse: [requiredPackages add: aPackage]
!

requiredPackages
	^ requiredPackages ifNil: [requiredPackages _ OrderedCollection new]
!

versionInfo: aVersionInfo
	ancestry _ MCWorkingAncestry new addAncestor: aVersionInfo
! !

!MCWorkingCopy methodsFor:'migration'!

updateInstVars
	ancestry ifNil:
		[ancestry _ MCWorkingAncestry new.
		versionInfo ifNotNil:
			[versionInfo ancestors do: [:ea | ancestry addAncestor: ea].
			versionInfo _ nil]]
! !

!MCWorkingCopy methodsFor:'operations'!

adopt: aVersion
	ancestry addAncestor: aVersion info.
	self changed.
!

backportChangesTo: aVersionInfo
        | baseVersion fullPatch currentVersionInfo currentVersion newSnapshot newAncestry |
        currentVersionInfo := self currentVersionInfo.
        baseVersion := self repositoryGroup versionWithInfo: aVersionInfo.
        currentVersion := self repositoryGroup versionWithInfo: currentVersionInfo.
        fullPatch := currentVersion snapshot patchRelativeToBase: baseVersion snapshot.
        (MCChangeSelectionRequest new
                patch: fullPatch;
                label: 'Changes to Backport';
                raiseSignal "signal" ) ifNotNilDo:
                [:partialPatch |
                newSnapshot := MCPatcher apply: partialPatch to: baseVersion snapshot.
                newAncestry := MCWorkingAncestry new
                                                        addAncestor: aVersionInfo;
                                                        addStepChild: currentVersionInfo;
                                                        yourself.
                MCPackageLoader updatePackage: package withSnapshot: newSnapshot.
                ancestry := newAncestry.
                self modified: false; modified: true]
!

changesRelativeToRepository: aRepository
	| ancestorVersion ancestorSnapshot |
	ancestorVersion _ aRepository closestAncestorVersionFor: ancestry ifNone: [].
	ancestorSnapshot _ ancestorVersion ifNil: [MCSnapshot empty] ifNotNil: [ancestorVersion snapshot].
	^ package snapshot patchRelativeToBase: ancestorSnapshot
!

loaded: aVersion
	ancestry _ MCWorkingAncestry new addAncestor: aVersion info.
	requiredPackages _ OrderedCollection withAll: (aVersion dependencies collect: [:ea | ea package]).
	self modified: false.
	self changed
!

merge: targetVersion
	| ancestorInfo merger ancestorSnapshot packageSnapshot |
	targetVersion dependencies do: [:ea | ea resolve merge].
	ancestorInfo _ targetVersion info commonAncestorWith: ancestry.
	
	ancestorInfo = targetVersion info ifTrue: [^ MCNoChangesException signal].
	
	packageSnapshot _ package snapshot.
	ancestorSnapshot _ ancestorInfo
							ifNotNil: [(self findSnapshotWithVersionInfo: ancestorInfo)]
							ifNil: [self notifyNoCommonAncestorWith: targetVersion.  MCSnapshot empty].
	
	(ancestry ancestors size = 1
		and: [ancestry ancestors first = ancestorInfo]
		and: [(packageSnapshot patchRelativeToBase: ancestorSnapshot) isEmpty])
				ifTrue: [^ targetVersion load].
	
	merger _ MCThreeWayMerger 
				base: packageSnapshot
				target: targetVersion snapshot
				ancestor: ancestorSnapshot.
	((MCMergeResolutionRequest new merger: merger)
		signal: 'Merging ', targetVersion info name) = true ifTrue:
			[merger loadWithNameLike: targetVersion info name.
			ancestry addAncestor: targetVersion info].
	self changed
!

merged: aVersion
	ancestry addAncestor: aVersion info.
	self changed
!

newVersion
	^ (self requestVersionNameAndMessageWithSuggestion: self uniqueVersionName) ifNotNilDo:
		[:pair |
		self newVersionWithName: pair first message: pair last].
!

newVersionWithName: nameString message: messageString
	| info deps |
	info _ ancestry infoWithName: nameString message: messageString.
	ancestry _ MCWorkingAncestry new addAncestor: info.
	self modified: true; modified: false.
	
	deps _ self requiredPackages collect:
		[:ea | 
		MCVersionDependency
			package: ea
			info: ea workingCopy currentVersionInfo].

	^ MCVersion
		package: package
		info: info
		snapshot: package snapshot
		dependencies: deps
!

notifyNoCommonAncestorWith: aVersion
	self notify:
'Could not find a common ancestor between (',
aVersion info name,
') and (',
ancestry ancestorString, ').
Proceeding with this merge may cause spurious conflicts.'
!

unload
	MCPackageLoader unloadPackage: self package.
	self unregister.
! !

!MCWorkingCopy methodsFor:'private'!

findSnapshotWithVersionInfo: aVersionInfo
	^ aVersionInfo
		ifNil: [MCSnapshot empty]
		ifNotNil: [(self repositoryGroup versionWithInfo: aVersionInfo) snapshot]
!

initialize
	super initialize.
	ancestry _ MCWorkingAncestry new
!

nextVersionName
    ^self nextVersionNameWithTemplate: '%(PACKAGE)-%(AUTHOR).%(REVISION_NUMBER)'

    "Modified: / 06-06-2013 / 20:02:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

nextVersionNameWithTemplate: template
    "Return next version name based on given template.
     Following parameters are expanded:
        REVISION_NUMBER ... sequential version number (the higher, the newer, basically)
        PACKAGE ........... package name
        AUTHOR ............ author of the version
    "

        | branch oldName |
        ancestry ancestors isEmpty
                ifTrue: [counter ifNil: [counter := 0]. branch := package monticelloName asString copy replaceAll: $/ with: $_; replaceAll: $: with: $_]
                ifFalse:
                        [oldName := ancestry ancestors first name.
                        oldName last isDigit
                                ifFalse: [branch := oldName]
                                ifTrue: [branch := oldName copyUpToLast: $-].
                        counter ifNil: [
                                counter := (ancestry ancestors collect: [:each |
                                        each name last isDigit
                                                ifFalse: [0]
                                                ifTrue: [(each name copyAfterLast: $-) extractNumber]]) max]].

        counter := counter + 1.
        ^template expandPlaceholdersWith:
            (Dictionary new
                at: 'REVISION_NUMBER' put: counter;
                at: 'PACKAGE' put: branch;
                at: 'AUTHOR' put: ((OperatingSystem getFullUserNameFromID: OperatingSystem getUserID) reject:[:c|c isSeparator]);
                yourself)

    "Created: / 06-06-2013 / 20:02:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

possiblyNewerVersions

	^Array streamContents: [:strm |
		self repositoryGroup repositories do: [:repo |
			strm nextPutAll: (self possiblyNewerVersionsIn: repo)]]
!

possiblyNewerVersionsIn: aRepository

	^aRepository possiblyNewerVersionsOfAnyOf: self ancestors
!

requestVersionNameAndMessageWithSuggestion: aString
        ^ (MCVersionNameAndMessageRequest new suggestedName: aString) raiseRequest

    "Modified: / 23-08-2011 / 07:42:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

uniqueVersionName
	|versionName|
	counter _ nil.
	[versionName _ self nextVersionName.
	self repositoryGroup includesVersionNamed: versionName] whileTrue.
	^ versionName
!

versionSeparator
	^ $_
! !

!MCWorkingCopy methodsFor:'repositories'!

repositoryGroup
	^ repositoryGroup ifNil: [repositoryGroup _ MCRepositoryGroup new]
!

repositoryGroup: aRepositoryGroup
	repositoryGroup _ aRepositoryGroup
! !

!MCWorkingCopy class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/goodies/monticello/MCWorkingCopy.st,v 1.7 2014-02-25 13:39:03 cg Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/goodies/monticello/MCWorkingCopy.st,v 1.7 2014-02-25 13:39:03 cg Exp $'
! !


MCWorkingCopy initialize!