diff -r 6cac06c54187 -r ef306e387021 VersionDiffBrowser.st --- a/VersionDiffBrowser.st Fri Dec 17 21:48:58 1999 +0100 +++ b/VersionDiffBrowser.st Sat Dec 18 11:57:21 1999 +0100 @@ -1,12 +1,18 @@ ApplicationModel subclass:#VersionDiffBrowser - instanceVariableNames:'classBeingCompared versionA versionB changeSetA changeSetB - filteredChangeSetA filteredChangeSetB changedMethodsChangeSet - indexFromChangedToA indexFromChangedToB diffTextView' + instanceVariableNames:'classChangeSet diffTextView' classVariableNames:'' poolDictionaries:'' category:'Interface-Browsers' ! +HierarchicalItem subclass:#ClassChangeSet + instanceVariableNames:'classBeingCompared labelA labelB changeSet onlyInA onlyInB + changedMethods' + classVariableNames:'' + poolDictionaries:'' + privateIn:VersionDiffBrowser +! + VersionDiffBrowser comment:'some comment '! @@ -35,15 +41,10 @@ #(#WindowSpec #label: 'Version DiffBrowser' #name: 'Version DiffBrowser' - #layout: #(#LayoutFrame 220 0 200 0 1084 0 703 0) - #level: 0 #min: #(#Point 10 10) #max: #(#Point nil nil) - #bounds: #(#Rectangle 220 200 1085 704) + #bounds: #(#Rectangle 110 151 975 655) #menu: #mainMenu - #usePreferredExtent: false - #returnIsOKInDialog: true - #escapeIsCancelInDialog: true ) #component: #(#SpecCollection @@ -51,7 +52,6 @@ #(#VariableVerticalPanelSpec #name: 'VariableVerticalPanel1' #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0) - #handles: #(#Any 0.319444 1.0) #component: #(#SpecCollection #collection: #( @@ -92,7 +92,7 @@ ) ) - #extent: #(#Point 286 160) + #extent: #(#Point 286 252) ) #(#ViewSpec #name: 'BoxM' @@ -121,7 +121,7 @@ ) ) - #extent: #(#Point 286 160) + #extent: #(#Point 286 252) ) #(#ViewSpec #name: 'BoxB' @@ -150,7 +150,7 @@ ) ) - #extent: #(#Point 287 160) + #extent: #(#Point 287 252) ) ) @@ -207,6 +207,7 @@ ) ) + #handles: #(#Any 0.5 1.0) ) ) @@ -401,39 +402,32 @@ !VersionDiffBrowser class methodsFor:'startup'! -openOnClass:aClass labelA:lblA sourceA:srcA labelB:lblB sourceB:srcB - |browser| - - browser := self new. - browser allButOpen. +openOnClass:aClass labelA:aLabelA sourceA:aSourceA labelB:aLabelB sourceB:aSourceB + |theBrowser| - browser classBeingCompared:aClass. - browser - labelA:lblA sourceA:srcA - labelB:lblB sourceB:srcB. - - browser openWindow. - - ^ browser. + theBrowser := self new. + theBrowser allButOpen. + theBrowser classChangeSet:(ClassChangeSet newForClass:aClass labelA:aLabelA sourceA:aSourceA labelB:aLabelB sourceB:aSourceB). + theBrowser updateLabels. + theBrowser updateLists. + theBrowser openWindow. + ^ theBrowser. " self openOnClass:Array versionA:'1.116' versionB:'1.113' " ! -openOnClass:aClass versionA:vsnA versionB:vsnB - |browser| - - browser := self new. - browser allButOpen. +openOnClass:aClass versionA:aVersionA versionB:aVersionB + |theBrowser| - browser classBeingCompared:aClass. - browser versionA:vsnA. - browser versionB:vsnB. - - browser openWindow. - - ^ browser. + theBrowser := self new. + theBrowser allButOpen. + theBrowser classChangeSet:(ClassChangeSet newForClass:aClass versionA:aVersionA versionB:aVersionB). + theBrowser updateLabels. + theBrowser updateLists. + theBrowser openWindow. + ^ theBrowser. " self openOnClass:Array versionA:'1.116' versionB:'1.113' @@ -442,55 +436,54 @@ !VersionDiffBrowser methodsFor:'accessing'! -classBeingCompared - "return the value of the instance variable 'classBeingCompared' (automatically generated)" - - ^ classBeingCompared! +changeSetA +" +gets the change set which contains only the new methods +in versionA of the class -classBeingCompared:something - "set the value of the instance variable 'classBeingCompared' (automatically generated)" + +" - classBeingCompared := something. - self updateViewsIfPossible. + ^ self classChangeSet methodsOnlyInA ! -labelA:lblA sourceA:srcA labelB:lblB sourceB:srcB - |s| +changeSetB +" +gets the change set which contains only the new methods +in versionB of the class + + +" + + ^ self classChangeSet methodsOnlyInB +! + +classBeingCompared - changeSetA := ChangeSet fromStream:(s := srcA readStream). - s close. - changeSetB := ChangeSet fromStream:(s := srcB readStream). - s close. - versionA := lblA. - versionB := lblB. - self updateLabels. - self updateLists. + ^ self classChangeSet classBeingCompared +! + +classChangeSet + "return the value of the instance variable 'classChangeSet' (automatically generated)" -! + ^ classChangeSet! + +classChangeSet:something + "set the value of the instance variable 'classChangeSet' (automatically generated)" + + classChangeSet := something.! versionA "return the value of the instance variable 'versionA' (automatically generated)" - ^ versionA! - -versionA:something - "set the value of the instance variable 'versionA' (automatically generated)" - - versionA := something. - self updateViewsIfPossible. + ^ self classChangeSet labelA ! versionB "return the value of the instance variable 'versionB' (automatically generated)" - ^ versionB! - -versionB:something - "set the value of the instance variable 'versionB' (automatically generated)" - - versionB := something. - self updateViewsIfPossible. + ^ self classChangeSet labelB ! ! @@ -504,11 +497,8 @@ self methodsOnlyInASelection value:nil. self methodsOnlyInBSelection value:nil. - idxA := indexFromChangedToA at:sel ifAbsent:nil. - idxB := indexFromChangedToB at:sel ifAbsent:nil. - - changeA := changeSetA at:idxA. - changeB := changeSetB at:idxB. + changeA := (self classChangeSet methodsChanged at:sel) first. + changeB := (self classChangeSet methodsChanged at:sel) second. self withWaitCursorDo:[ self diffTextView text1:changeA source text2:changeB source. @@ -530,7 +520,7 @@ self methodsOnlyInBSelection value:nil. self methodsChangedSelection value:nil. - change := filteredChangeSetA at:sel. + change := self changeSetA at:sel. self methodText value:(change source). self showSingleTextView. ] @@ -544,7 +534,7 @@ self methodsOnlyInASelection value:nil. self methodsChangedSelection value:nil. - change := filteredChangeSetB at:sel. + change := self changeSetB at:sel. self methodText value:(change source). self showSingleTextView. ] @@ -597,6 +587,20 @@ ^ holder. ! +diffTextView + "automatically generated by UIPainter ..." + + diffTextView isNil ifTrue:[ + diffTextView := HVScrollableView + for:DiffTextView + miniScrollerH:true miniScrollerV:false. + diffTextView addNextPreviousButtons. + + self addAcceptToTextViewMenus. + ]. + ^ diffTextView +! + methodText "automatically generated by UIPainter ..." @@ -734,33 +738,6 @@ !VersionDiffBrowser methodsFor:'initialization & release'! -closeDownViews - "This is a hook method generated by the Browser. - It will be invoked when your app/dialog-window is really closed." - - "/ change the code below as required ... - "/ This should cleanup any leftover resources - "/ (for example, temporary files) - "/ super closeRequest will initiate the closeDown - - "/ add your code here - - "/ do not remove the one below ... - ^ super closeDownViews -! - -closeRequest - "This is a hook method generated by the Browser. - It will be invoked when your app/dialog-window is about to be - closed (and has a chance to suppress the close)." - - "/ change the code below as required ... - "/ Closing can be suppressed, by returning. - "/ super closeRequest will initiate the closeDown - - ^ super closeRequest -! - postBuildWith:aBuilder "This is a hook method generated by the Browser. It will be invoked during the initialization of your app/dialog, @@ -772,7 +749,6 @@ "/ add any code here ... (aBuilder componentAt:#TopHorizontalPanel) ignoreInvisibleComponents:true. - ^ super postBuildWith:aBuilder ! ! @@ -802,14 +778,12 @@ |oldVersion newVersion vsnMthdA vsnMthdB vsnA vsnB info| - ^ self warn:'This function is not yet implemented'. - - vsnMthdA := changeSetA + vsnMthdA := self changeSetA detect:[:ch | ch isMethodChange and:[ch changeClass isMeta and:[ch changeSelector = #version]]] ifNone:nil. - vsnMthdB := changeSetA + vsnMthdB := self changeSetA detect:[:ch | ch isMethodChange and:[ch changeClass isMeta and:[ch changeSelector = #version]]] @@ -848,7 +822,6 @@ self halt. - "Modified: / 14.12.1999 / 15:34:28 / cg" ! inspectSelectedChangeInA @@ -873,7 +846,7 @@ sel := self methodsOnlyInASelection value. sel notNil ifTrue:[ - change := (changeSetA at:sel). + change := (self changeSetA at:sel). ]. ^ change ! @@ -883,7 +856,7 @@ sel := self methodsOnlyInBSelection value. sel notNil ifTrue:[ - change := (changeSetB at:sel). + change := (self changeSetB at:sel). ]. ^ change ! @@ -893,17 +866,55 @@ sel := self methodsChangedSelection value. sel notNil ifTrue:[ - idxA := indexFromChangedToA at:sel ifAbsent:nil. - idxB := indexFromChangedToB at:sel ifAbsent:nil. + idxA := self indexFromChangedToA at:sel ifAbsent:nil. + idxB := self indexFromChangedToB at:sel ifAbsent:nil. - ^ Array with:(changeSetA at:idxA) - with:(changeSetB at:idxB) + ^ Array with:(self changeSetA at:idxA) + with:(self changeSetB at:idxB) ]. ^ nil ! ! !VersionDiffBrowser methodsFor:'private'! +generator + + ^[:c | + |result mPkg cPkg cls m| + + c isClassDefinitionChange ifTrue:[ + result := c printStringWithoutClass + ] ifFalse:[ + c isDoIt ifTrue:[ + result := c source , ' (doIt)' + ] ifFalse:[ + c className = self classBeingCompared name ifTrue:[ + result := c printStringWithoutClass. + c isMethodChange ifTrue:[ + cls := c changeClass. + cls notNil ifTrue:[ + m := cls compiledMethodAt:c selector. + (m notNil + and:[m package ~= cls package]) ifTrue:[ + result := c printStringWithoutClass , ' [' , m package , ']'. + ]. + ]. + ] + ] ifFalse:[ + "/ include name in private class changes + (c className startsWith:(self classBeingCompared name , '::')) ifTrue:[ + result := (c className copyFrom:(self classBeingCompared name size + 3)) , ' ' , c printStringWithoutClass + ] ifFalse:[ + result := c printString + ] + ] + ] + ]. + result + ]. + +! + showDiffTextView (self builder componentAt:#diffTextViewBox) raise; beVisible. (self builder componentAt:#singleTextView) beInvisible. @@ -918,32 +929,19 @@ ! -updateFromVersions - |mgr versionAStream versionBStream| +updateLabels - mgr := classBeingCompared sourceCodeManager. - - versionAStream := mgr getSourceStreamFor:classBeingCompared revision:versionA. - changeSetA := ChangeSet fromStream:versionAStream. - versionAStream close. - versionBStream := mgr getSourceStreamFor:classBeingCompared revision:versionB. - changeSetB := ChangeSet fromStream:versionBStream. - versionBStream close. - - self updateLists + |theVersionA theVersionB| -! - -updateLabels - versionA notNil ifTrue:[ - self diffTextLabelA value:versionA. - self onlyInALabelHolder value:('Only in ' , versionA). + (theVersionA := self versionA) notNil ifTrue:[ + self diffTextLabelA value:theVersionA. + self onlyInALabelHolder value:('Only in ' , theVersionA). (self builder componentAt:#OnlyInALabel) backgroundColor:Color green darkened. ]. - versionB notNil ifTrue:[ - self diffTextLabelB value:versionB. - self onlyInBLabelHolder value:('Only in ' , versionB). + (theVersionB :=self versionB) notNil ifTrue:[ + self diffTextLabelB value:theVersionB. + self onlyInBLabelHolder value:('Only in ' , theVersionB). (self builder componentAt:#OnlyInBLabel) backgroundColor:Color red darkened. ]. ! @@ -951,188 +949,32 @@ updateLists "walk over the two changeSets and add change to one of the tree lists" - |mgr nmListA nmListB nmListM generator| - - filteredChangeSetA := ChangeSet new. - filteredChangeSetB := ChangeSet new. - changedMethodsChangeSet := ChangeSet new. - - indexFromChangedToA := OrderedCollection new. - indexFromChangedToB := OrderedCollection new. - - changeSetA keysAndValuesDo:[:idxA :aChangeInA | - |anyFound ch| - - anyFound := false. - - changeSetB keysAndValuesDo:[:idxB :aChangeInB | - (aChangeInA isForSameAs:aChangeInB) ifTrue:[ - anyFound := true. - - "/ also in B - is it different ? - (aChangeInA sameAs:aChangeInB) ifFalse:[ - changedMethodsChangeSet add:aChangeInA. - indexFromChangedToA add:idxA. - indexFromChangedToB add:idxB. - ] ifTrue:[ - aChangeInA isMethodChange ifTrue:[ - aChangeInA methodCategory ~= aChangeInB methodCategory ifTrue:[ - "/ only the category is different; - "/ make it a MethodCategory changes. - - ch := MethodCategoryChange new - className:aChangeInA className - selector:aChangeInA selector - category:aChangeInA methodCategory. - changeSetA at:idxA put:ch. - - ch := MethodCategoryChange new - className:aChangeInB className - selector:aChangeInB selector - category:aChangeInB methodCategory. - changeSetB at:idxB put:ch. - - changedMethodsChangeSet add:aChangeInA. - indexFromChangedToA add:idxA. - indexFromChangedToB add:idxB. - ] - ]. - ] - ] ifFalse:[ - (aChangeInA sameAs:aChangeInB) ifTrue:[ - anyFound := true. - ] ifFalse:[ - ] - ] - ]. - - anyFound ifFalse:[ - filteredChangeSetA add:aChangeInA. - ] - ]. - - changeSetB keysAndValuesDo:[:idxB :aChangeInB | - |anyFound| - - anyFound := false. - - changeSetA do:[:aChangeInA | - |idxM| - - (aChangeInA isForSameAs:aChangeInB) ifTrue:[ - anyFound := true. + self methodsOnlyInA removeAll. + self methodsOnlyInA addAll: (self classChangeSet methodsOnlyInA collect:self generator). + self methodsOnlyInB removeAll. + self methodsOnlyInB addAll: (self classChangeSet methodsOnlyInB collect:self generator). + self methodsChanged removeAll. + self methodsChanged addAll: (self classChangeSet methodsChanged collect:[:arr| self generator value:(arr first)]). - "/ also in B - is it different ? - (aChangeInA sameAs:aChangeInB) ifFalse:[ - "/ already there ? - idxM := changedMethodsChangeSet findFirst:[:c | c isForSameAs:aChangeInB]. - idxM == 0 ifTrue:[ - changedMethodsChangeSet add:aChangeInB. - indexFromChangedToB add:idxB. - ] ifFalse:[ - indexFromChangedToB at:idxM put:idxB - ] - ] - ] ifFalse:[ - (aChangeInA sameAs:aChangeInB) ifTrue:[ - anyFound := true. - ] ifFalse:[ - ] - ] - ]. - anyFound ifFalse:[ - filteredChangeSetB add:aChangeInB. - ] - ]. - - generator := [:c | - |result mPkg cPkg cls m| - - c isClassDefinitionChange ifTrue:[ - result := c printStringWithoutClass - ] ifFalse:[ - c isDoIt ifTrue:[ - result := c source , ' (doIt)' - ] ifFalse:[ - c className = classBeingCompared name ifTrue:[ - result := c printStringWithoutClass. - c isMethodChange ifTrue:[ - cls := c changeClass. - cls notNil ifTrue:[ - m := cls compiledMethodAt:c selector. - (m notNil - and:[m package ~= cls package]) ifTrue:[ - result := c printStringWithoutClass , ' [' , m package , ']'. - ]. - ]. - ] - ] ifFalse:[ - "/ include name in private class changes - (c className startsWith:(classBeingCompared name , '::')) ifTrue:[ - result := (c className copyFrom:(classBeingCompared name size + 3)) , ' ' , c printStringWithoutClass - ] ifFalse:[ - result := c printString - ] - ] - ] - ]. - result - ]. - - nmListA := self methodsOnlyInA. - nmListA removeAll. - nmListA addAll:(filteredChangeSetA collect:generator). - - nmListB := self methodsOnlyInB. - nmListB removeAll. - nmListB addAll:(filteredChangeSetB collect:generator). - - nmListM := self methodsChanged. - nmListM removeAll. - nmListM addAll:(changedMethodsChangeSet collect:generator). - - nmListA isEmpty ifTrue:[ + self methodsOnlyInA value isEmpty ifTrue:[ (self builder componentAt:#BoxA) beInvisible. ] ifFalse:[ (self builder componentAt:#BoxA) beVisible. ]. - nmListB isEmpty ifTrue:[ + self methodsOnlyInB value isEmpty ifTrue:[ (self builder componentAt:#BoxB) beInvisible. ] ifFalse:[ (self builder componentAt:#BoxB) beVisible. ]. - nmListM isEmpty ifTrue:[ - (nmListA notEmpty or:[nmListB notEmpty]) ifTrue:[ + self methodsChanged value isEmpty ifTrue:[ + (self methodsOnlyInA value notEmpty or:[self methodsOnlyInB value notEmpty]) ifTrue:[ (self builder componentAt:#BoxM) beInvisible. ] ] ifFalse:[ (self builder componentAt:#BoxM) beVisible. ] -! - -updateViewsIfPossible - classBeingCompared notNil ifTrue:[ - self window label:'Version Diff Browser on ' , classBeingCompared name. - - self updateLabels. - - changeSetA notNil ifTrue:[ - changeSetB notNil ifTrue:[ - self updateLists. - ^ self - ] - ]. - - versionA notNil ifTrue:[ - versionB notNil ifTrue:[ - self updateFromVersions - ] - ] - ] - - ! ! !VersionDiffBrowser methodsFor:'values'! @@ -1141,8 +983,8 @@ |m v| (Array - with:diffTextView leftTextView - with:diffTextView rightTextView) + with:self diffTextView leftTextView + with:self diffTextView rightTextView) do:[:v | m := v editMenu. m addLabels:#('-' 'accept') @@ -1154,12 +996,12 @@ idx := self methodsChangedSelection value. - v == diffTextView leftTextView ifTrue:[ - idx2 := indexFromChangedToA at:idx. - change := changeSetA at:idx2. + v == self diffTextView leftTextView ifTrue:[ + idx2 := self indexFromChangedToA at:idx. + change := self changeSetA at:idx2. ] ifFalse:[ - idx2 := indexFromChangedToB at:idx. - change := changeSetB at:idx2. + idx2 := self indexFromChangedToB at:idx. + change := self changeSetB at:idx2. ]. change apply ]. @@ -1168,24 +1010,199 @@ v menuMessage:#value. ] +! ! + +!VersionDiffBrowser::ClassChangeSet class methodsFor:'instance creation'! + +changeSetForClass:aClass andRevision:aVersion +"return a ChangeSet for the class aClass and version aVersion. +The version from the class source stream is checked out from the repositiory +into a source stream. Then the change set is generated from the source stream. + + +" + |theSourceCodeManager theSourceStream theChangeSet| + + theSourceCodeManager := aClass sourceCodeManager. + [theSourceStream := theSourceCodeManager getSourceStreamFor:aClass revision:aVersion. + theSourceStream notNil + ifTrue:[theChangeSet:=ChangeSet fromStream:theSourceStream]] + valueNowOrOnUnwindDo: + [theSourceStream notNil ifTrue:[theSourceStream close]]. + ^theChangeSet +! + +changeSetForClass:aClass andSource:aSource +"return a ChangeSet for the class aClass and source aSource. +The source is converted to a stream and then the change set is generated +from the source stream. + + +" + |theChangeSet theSourceStream| + + [(theSourceStream := aSource readStream) notNil + ifTrue:[theChangeSet:=ChangeSet fromStream:(theSourceStream := aSource readStream)]] + valueNowOrOnUnwindDo: + [theSourceStream notNil + ifTrue:[theSourceStream close]]. + ^theChangeSet +! + +newForClass:aClass labelA:aLabelA sourceA:aSourceA labelB:aLabelB sourceB:aSourceB +"return a ClassChangeSet for the class aClass and the two sources. +The two classes are compared via the source files. A new instance of +ClassChangeSet is generated containing the correspondenting changes. + + +" + |theChangeSetA theChangeSetB theClassChangeSet| + + theClassChangeSet := self new. + theClassChangeSet classBeingCompared:aClass. + theClassChangeSet labelA:aLabelA. + theClassChangeSet labelB:aLabelB. + theChangeSetA:=self changeSetForClass:aClass andSource:aSourceA. + theChangeSetB:=self changeSetForClass:aClass andSource:aSourceB. + theChangeSetA isNil + ifTrue: [theChangeSetA := ChangeSet new]. + theChangeSetB isNil + ifTrue: [theChangeSetB := ChangeSet new]. + ^theClassChangeSet changeSet:(theChangeSetA diffSetsAgainst:theChangeSetB) ! -diffTextView - "automatically generated by UIPainter ..." +newForClass:aClass versionA:aVersionA versionB:aVersionB +"return a ClassChangeSet for the class aClass and the two versions. +The two class version are checked out from the repository and then being +compared. A new instance of ClassChangeSet is generated containing +the correspondenting changes. + + +" + |theChangeSetA theChangeSetB theClassChangeSet| + + theClassChangeSet := self new. + theClassChangeSet classBeingCompared:aClass. + theClassChangeSet labelA:aVersionA. + theClassChangeSet labelB:aVersionB. + theChangeSetA := self changeSetForClass: aClass andRevision: aVersionA. + theChangeSetB := self changeSetForClass: aClass andRevision: aVersionB. + theChangeSetA isNil + ifTrue: [theChangeSetA := ChangeSet new]. + theChangeSetB isNil + ifTrue: [theChangeSetB := ChangeSet new]. + ^theClassChangeSet changeSet:(theChangeSetA diffSetsAgainst:theChangeSetB) + +! ! + +!VersionDiffBrowser::ClassChangeSet methodsFor:'accessing'! + +changeSet +"returns a identity dictionary containing the different changes. +key: value: +#changed OrderedCollection of arrays containing ChangeSets for methods + which are changed and exists in the class versionA and versionB +#onlyInArg ChangeSet for the methods which exists only in the class versionB +#onlyInReceiver ChangeSet for the methods which exists only in the class versionA + + +" + ^ changeSet +! + +changeSet:something +"sets a identity dictionary containing the different changes. +key: value: +#changed OrderedCollection of arrays containing ChangeSets for methods + which are changed and exists in the class versionA and versionB +#onlyInArg ChangeSet for the methods which exists only in the class versionB +#onlyInReceiver ChangeSet for the methods which exists only in the class versionA + + +" + + changeSet := something. +! + +classBeingCompared +"returns the value of the class which is compared + + +" + + ^ classBeingCompared +! - diffTextView isNil ifTrue:[ - diffTextView := HVScrollableView - for:DiffTextView - miniScrollerH:true miniScrollerV:false. - diffTextView addNextPreviousButtons. +classBeingCompared:something +"set the value of the class which is compared + +" + + classBeingCompared := something. +! + +labelA +"returns the label for the class versionA + + +" + ^ labelA +! + +labelA:something +"sets the label for the class versionA + + +" + labelA := something. +! + +labelB +"returns the label for the class versionB + + +" + ^ labelB +! - self addAcceptToTextViewMenus. - ]. - ^ diffTextView +labelB:something +"sets the label for the class versionB + + +" + + labelB := something. +! + +methodsChanged +"returns a OrderedCollection of arrays containing ChangeSets for methods +which are changed and exists in the class versionA and versionB + + +" + ^ self changeSet at:#changed +! + +methodsOnlyInA +"returns a ChangeSet for the methods which exists only in the class versionA + + +" + + ^ self changeSet at:#onlyInReceiver +! + +methodsOnlyInB +"returns a ChangeSet for the methods which exists only in the class versionB + + +" + + ^ self changeSet at:#onlyInArg ! ! !VersionDiffBrowser class methodsFor:'documentation'! version - ^ '$Header: /cvs/stx/stx/libtool/VersionDiffBrowser.st,v 1.13 1999-12-14 14:33:52 cg Exp $' + ^ '$Header: /cvs/stx/stx/libtool/VersionDiffBrowser.st,v 1.14 1999-12-18 10:57:21 ps Exp $' ! !