From ba13a10c01b31b5a8483dcd19156f7d956600844 Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Wed, 26 Jun 2024 16:12:02 -0400
Subject: [PATCH 01/15] wip: smarter behavior on rebase conflicts on production
---
cls/SourceControl/Git/Extension.cls | 6 +-
.../Git/Util/ProductionConflictResolver.cls | 134 ++++++++++++++++++
cls/SourceControl/Git/Utils.cls | 78 +++++++---
3 files changed, 199 insertions(+), 19 deletions(-)
create mode 100644 cls/SourceControl/Git/Util/ProductionConflictResolver.cls
diff --git a/cls/SourceControl/Git/Extension.cls b/cls/SourceControl/Git/Extension.cls
index 1e466710..d9ca0382 100644
--- a/cls/SourceControl/Git/Extension.cls
+++ b/cls/SourceControl/Git/Extension.cls
@@ -159,7 +159,10 @@ Method OnSourceMenuItem(name As %String, ByRef Enabled As %String, ByRef Display
} else {
set Enabled = -1
}
- if (name '= "") {
+
+ if (name = "Status") {
+ set DisplayName = ..LocalizeName(name)_" (branch: "_##class(SourceControl.Git.Utils).GetCurrentBranch()_")"
+ } if (name '= "") {
set DisplayName = ..LocalizeName(name)
}
quit $$$OK
@@ -395,4 +398,3 @@ Method AddToSourceControl(InternalName As %String, Description As %String = "")
}
}
-
diff --git a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
new file mode 100644
index 00000000..07327e9c
--- /dev/null
+++ b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
@@ -0,0 +1,134 @@
+Include (%occInclude, %occErrors, %occKeyword, %occReference, %occSAX)
+
+Class SourceControl.Git.Util.ProductionConflictResolver Extends %RegisteredObject
+{
+
+Property logStream As %Stream.Object [ Private ];
+
+Property productionFile As %String [ Private ];
+
+Property productionClassname As %Dictionary.CacheClassname [ Private ];
+
+Property errorStatus As %Status [ InitialExpression = 1, Private ];
+
+/// API property: whether or not the conflict was resolved
+Property resolved As %Boolean [ InitialExpression = 0 ];
+
+/// API property: error message if resolved is false
+Property errorMessage As %String [ Calculated ];
+
+Method errorMessageGet() As %String
+{
+ If $$$ISERR(..errorStatus) {
+ Do $System.Status.DecomposeStatus(..errorStatus,.components)
+ If $Get(components(1,"code")) = $$$GeneralError {
+ Quit $Get(components(1,"param",1))
+ } Else {
+ Set ex = ##class(%Exception.StatusException).CreateFromStatus(..errorStatus)
+ Do ex.Log()
+ Quit "an internal error occurred and has been logged."
+ }
+ } Else {
+ Quit ""
+ }
+}
+
+ClassMethod FromLog(pOutStream As %Stream.Object) As SourceControl.Git.Util.ProductionConflictResolver
+{
+ Set inst = ..%New()
+ Try {
+ Set inst.logStream = pOutStream
+ Do inst.ConsumeStream()
+ Do inst.Resolve()
+ } Catch e {
+ Set inst.resolved = 0
+ Set inst.errorStatus = e.AsStatus()
+ }
+ Do inst.logStream.Rewind() // Finally
+ Quit inst
+}
+
+Method ConsumeStream() [ Private ]
+{
+ Do ..logStream.Rewind()
+ Do ..logStream.ReadLine()
+ Set productionLine = ..logStream.ReadLine()
+ Set ..productionFile = $Piece(productionLine,"Merge conflict in ",2)
+ If ..productionFile = "" {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"Message did not reflect merge conflict on a single file."))
+ }
+ If '..logStream.AtEnd {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"Multiple files had merge conflicts; cannot resolve intelligently."))
+ }
+ Set internalName = ##class(SourceControl.Git.Utils).NameToInternalName(..productionFile)
+ If ($Piece(internalName,".",*) '= "CLS") {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"File with conflict is not a class."))
+ }
+ Set ..productionClassname = $Piece(internalName,".",1,*-1)
+ If '$$$comClassDefined(..productionClassname) && '$ClassMethod(..productionClassname,"%Extends","Ens.Production") {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"File with conflict is not an interoperability production."))
+ }
+}
+
+Method Resolve() [ Private ]
+{
+ // If we got this far, ..productionClassname is a subclass of Ens.Production and ..productionFile is its file within the git root.
+ Set code = 0
+ Set code = code + ##class(SourceControl.Git.Utils).RunGitCommand("show",,.base,":1:"_..productionFile)
+ Set code = code + ##class(SourceControl.Git.Utils).RunGitCommand("show",,.ours,":2:"_..productionFile)
+ Set code = code + ##class(SourceControl.Git.Utils).RunGitCommand("show",,.theirs,":3:"_..productionFile)
+
+ If code > 0 {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"Unable to load base/ours/theirs for "_..productionFile))
+ }
+
+ Do ..ClassToTree(base,.baseTree)
+ Do ..ClassToTree(ours,.oursTree)
+ Do ..ClassToTree(theirs,.theirsTree)
+
+ zw baseTree,oursTree,theirsTree
+
+ Do ..ThreeWayMerge(.baseTree,.theirsTree,.oursTree,.resolved)
+
+ Do ..TreeToClass(.resolved)
+}
+
+Method ClassToTree(source As %Stream.Object, Output tree)
+{
+ Set sc = $$$OK
+ Kill ^||%oddDEF
+ Set ^||%oddDEF(..productionClassname)=""
+ Try {
+ Do source.Rewind()
+ While 'source.AtEnd {
+ Set tTextArray($Increment(tTextArray(0))) = source.ReadLine(,.sc)
+ $$$ThrowOnError(sc)
+ }
+ $$$ThrowOnError(##class(%Atelier.v1.Utils.TextServices).SetTextFromArray(.tTextArray,,..productionClassname,"CLS"))
+ Merge classDef = ^||%oddDEF(..productionClassname)
+
+ Set xmlStream = ##class(%Stream.TmpCharacter).%New()
+ For i=1:1:$Get(classDef($$$cCLASSxdata,"ProductionDefinition",$$$cXDATAdata)) {
+ Do xmlStream.WriteLine($Get(classDef($$$cCLASSxdata,"ProductionDefinition",$$$cXDATAdata,i)))
+ }
+ Set handler=##class(%XML.ImportHandler).%New("IRIS.Temp",$$$IntHandler)
+ $$$ThrowOnError(##Class(%XML.SAX.Parser).ParseStream(xmlStream,handler))
+
+ Merge tree = @handler.DOMName@(handler.Tree)
+ } Catch e {
+ Set sc = e.AsStatus()
+ }
+ Do source.Rewind()
+ Kill ^||%oddDEF(..productionClassname)
+ $$$ThrowOnError(sc)
+}
+
+Method ThreeWayMerge(ByRef base, ByRef theirs, ByRef ours, Output resolved)
+{
+}
+
+Method TreeToClass(ByRef tree)
+{
+}
+
+}
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index b209ba6c..398b6f44 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -303,6 +303,9 @@ ClassMethod AfterUserAction(Type As %Integer, Name As %String, InternalName As %
do ..Sync(Msg)
set Reload = 1
}
+ } elseif (menuItemName = "GitWebUI") {
+ // Always force reload as many things could have possibly changed.
+ set Reload = 1
}
quit $$$OK
}
@@ -395,14 +398,49 @@ ClassMethod StageAddedFiles()
}
/// Merges the files from the configured branch as part of the Sync operation
-ClassMethod MergeDefaultRemoteBranch()
+/// Returns true if this resulted in durable changes to the local git repo
+ClassMethod MergeDefaultRemoteBranch(Output alert As %String = "") As %Boolean
{
+ set rebased = 0
set settings = ##class(SourceControl.Git.Settings).%New()
set defaultMergeBranch = settings.defaultMergeBranch
if defaultMergeBranch '= "" {
- do ..RunGitWithArgs(.errStream, .outStream, "rebase", defaultMergeBranch)
+ do ..RunGitWithArgs(.errStream, .outStream, "fetch", "origin", defaultMergeBranch_":"_defaultMergeBranch)
do ..PrintStreams(errStream, outStream)
+
+ // Start a transaction so code changes can be rolled back
+ set initTLevel = $TLevel
+ try {
+ TSTART
+ set code = ..RunGitWithArgs(.errStream, .outStream, "rebase", defaultMergeBranch)
+ if (code '= 0) {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"git rebase reported failure"))
+ }
+ set rebased = 1
+ TCOMMIT
+ } catch e {
+ // "rebase" may throw an exception due to errors syncing to IRIS. In that case, roll back and keep going to abort the rebase.
+ write !,"Attempting to resolve differences in production definition..."
+ set resolver = ##class(SourceControl.Git.Util.ProductionConflictResolver).FromLog(outStream)
+ write !! zw resolver write !!
+ if resolver.resolved {
+ set rebased = 1
+ write " success!"
+ } else {
+ write " unable to resolve - "_resolver.errorMessage
+ while $TLevel > initTLevel {
+ TROLLBACK 1
+ }
+ }
+ }
+ if 'rebased {
+ do ..RunGitCommand("rebase",.errStream, .outStream,"--abort")
+ do ..PrintStreams(errStream, outStream)
+ set alert = "WARNING: Remote branch '"_defaultMergeBranch_"' could not be merged due to conflicts. Changes have been pushed to '"_..GetCurrentBranch()_"' and must be resolved in your git remote."
+ write !,alert,!
+ }
}
+ quit rebased
}
/// Converts the DynamicArray into a list and calls the SourceControl.Git.Change RemoveUncommitted method on the newly created list
@@ -419,7 +457,7 @@ ClassMethod ClearUncommitted(filesWithActions) As %Status
quit $$$OK
}
-ClassMethod Sync(Msg As %String) As %Status
+ClassMethod Sync(Msg As %String, Output alert As %String) As %Status
{
write !, "Syncing local repository...", !
do ..StageAddedFiles()
@@ -430,20 +468,24 @@ ClassMethod Sync(Msg As %String) As %Status
do ..Fetch()
do ..Pull()
do ..SyncCommit(Msg)
- do ..Push()
- do ..MergeDefaultRemoteBranch()
- do ..Push()
-
+ do ..Push(,1)
+ if ..MergeDefaultRemoteBranch(.alert) {
+ do ..Push(,1)
+ }
}
-
quit $$$OK
}
-ClassMethod Push(remote As %String = "origin") As %Status
+ClassMethod Push(remote As %String = "origin", force As %Boolean = 0) As %Status
{
do ##class(SourceControl.Git.Utils).RunGitCommandWithInput("branch",,.errStream,.outstream,"--show-current")
set branchName = outstream.ReadLine(outstream.Size)
- do ..RunGitWithArgs(.errStream, .outStream, "push", remote, branchName)
+ if (force) {
+ set args($i(args)) = "--force"
+ }
+ set args($i(args)) = remote
+ set args($i(args)) = branchName
+ do ..RunGitWithArgs(.errStream, .outStream, "push", args...)
do ..PrintStreams(errStream, outStream)
quit $$$OK
}
@@ -475,7 +517,7 @@ ClassMethod Pull(remote As %String = "origin") As %Status
set branchName = outStream.ReadLine(outStream.Size)
write !, "Pulling from branch: ", branchName
kill errStream, outStream
- set returnCode = ..RunGitWithArgs(.errStream, .outStream, "pull", remote _ "/" _ branchName)
+ set returnCode = ..RunGitWithArgs(.errStream, .outStream, "pull", remote, branchName)
w !, "Pull ran with return code: " _ returnCode
quit $$$OK
@@ -1226,6 +1268,7 @@ ClassMethod ImportCSPFile(InternalName As %String) As %Status
ClassMethod ListItemsInFiles(ByRef itemList, ByRef err) As %Status
{
#define DoNotLoad 1
+ set res = $$$OK
set mappingFileType = $order($$$SourceMapping(""))
while (mappingFileType '= "") {
@@ -1536,7 +1579,7 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O
{
set pullArg = ""
if command = "pull" {
- set pullArg = args(1)
+ set pullArg = $Get(args(1))
}
// Special case: git --version is used internally even when the settings incorporated here may be invalid/unspecified.
if (command '= "--version") {
@@ -1570,12 +1613,13 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O
set diffBase = ""
set diffCompare = ""
set pullOriginIndex = ""
- if (command = "checkout") || (command = "merge") || (command = "rebase") || (command = "pull"){
+ if (command = "checkout") || (command = "merge") || (command = "rebase") || (command = "pull") {
set syncIris = 1
- set diffCompare = args(args)
+ if $data(args) && $data(args(args),diffCompare) {
+ // no-op
+ }
}
-
for i=1:1:$get(args) {
if ($data(args(i))) {
set newArgs($increment(newArgs)) = args(i)
@@ -1607,7 +1651,7 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O
}
do ..RunGitCommand("fetch", .errorStream, .outputStream)
kill errorStream, outputStream
- do ##class(SourceControl.Git.Utils).RunGitCommandWithInput("diff",,.errorStream,.outputStream, diffBase_".."_diffCompare, "--name-status")
+ do ##class(SourceControl.Git.Utils).RunGitCommandWithInput("diff",,.errorStream,.outputStream, diffBase_$Case(diffCompare,"":"",:"..")_diffCompare, "--name-status")
while (outputStream.AtEnd = 0) {
set file = outputStream.ReadLine()
set modification = ##class(SourceControl.Git.Modification).%New()
@@ -1656,8 +1700,8 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O
for stream=errStream,outStream {
set stream.RemoveOnClose = 1
}
- do ..PrintStreams(errStream, outStream)
if syncIris {
+ do ..PrintStreams(errStream, outStream)
$$$ThrowOnError(..SyncIrisWithRepo(.files))
}
quit returnCode
From 724d1183c4634ceb193b527349950dd6608f297e Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Fri, 28 Jun 2024 06:06:10 -0400
Subject: [PATCH 02/15] wip: more work toward smart XML three-way merge
Going to abandon the general solution to focus on the things that commonly happen - rather than trying to reimplement three-way merge, just going to do the right thing with the git conflict file
---
cls/SourceControl/Git/Extension.cls | 1 +
cls/SourceControl/Git/Util/Buffer.cls | 1 +
.../Git/Util/ProductionConflictResolver.cls | 56 ++++++++---
cls/SourceControl/Git/Utils.cls | 1 +
cls/SourceControl/Git/WebUIDriver.cls | 1 +
cls/_zpkg/isc/sc/git/Socket.cls | 1 +
.../SourceControl/Git/XMLThreeWayMerge.cls | 93 +++++++++++++++++++
7 files changed, 139 insertions(+), 15 deletions(-)
create mode 100644 test/UnitTest/SourceControl/Git/XMLThreeWayMerge.cls
diff --git a/cls/SourceControl/Git/Extension.cls b/cls/SourceControl/Git/Extension.cls
index d9ca0382..6cfb2305 100644
--- a/cls/SourceControl/Git/Extension.cls
+++ b/cls/SourceControl/Git/Extension.cls
@@ -398,3 +398,4 @@ Method AddToSourceControl(InternalName As %String, Description As %String = "")
}
}
+
diff --git a/cls/SourceControl/Git/Util/Buffer.cls b/cls/SourceControl/Git/Util/Buffer.cls
index 8134e516..425a62cd 100644
--- a/cls/SourceControl/Git/Util/Buffer.cls
+++ b/cls/SourceControl/Git/Util/Buffer.cls
@@ -281,3 +281,4 @@ Method UsePreviousDeviceAndSettings() [ Internal, Private ]
}
}
+
diff --git a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
index 07327e9c..e0f668f6 100644
--- a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
+++ b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
@@ -82,18 +82,16 @@ Method Resolve() [ Private ]
$$$ThrowStatus($$$ERROR($$$GeneralError,"Unable to load base/ours/theirs for "_..productionFile))
}
- Do ..ClassToTree(base,.baseTree)
- Do ..ClassToTree(ours,.oursTree)
- Do ..ClassToTree(theirs,.theirsTree)
+ Do ..ClassToXMLDoc(base,.baseDoc)
+ Do ..ClassToXMLDoc(ours,.oursDoc)
+ Do ..ClassToXMLDoc(theirs,.theirsDoc)
- zw baseTree,oursTree,theirsTree
+ Do ..ThreeWayMerge(baseDoc,oursDoc,theirsDoc,.resolved)
- Do ..ThreeWayMerge(.baseTree,.theirsTree,.oursTree,.resolved)
-
- Do ..TreeToClass(.resolved)
+ Do ..XMLDocToClass(resolved)
}
-Method ClassToTree(source As %Stream.Object, Output tree)
+Method ClassToXMLDoc(source As %Stream.Object, Output doc As %XML.Document)
{
Set sc = $$$OK
Kill ^||%oddDEF
@@ -111,24 +109,52 @@ Method ClassToTree(source As %Stream.Object, Output tree)
For i=1:1:$Get(classDef($$$cCLASSxdata,"ProductionDefinition",$$$cXDATAdata)) {
Do xmlStream.WriteLine($Get(classDef($$$cCLASSxdata,"ProductionDefinition",$$$cXDATAdata,i)))
}
- Set handler=##class(%XML.ImportHandler).%New("IRIS.Temp",$$$IntHandler)
- $$$ThrowOnError(##Class(%XML.SAX.Parser).ParseStream(xmlStream,handler))
-
- Merge tree = @handler.DOMName@(handler.Tree)
+ Set reader = ##class(%XML.Reader).%New()
+ $$$ThrowOnError(reader.OpenStream(xmlStream))
+ Set doc = reader.Document
} Catch e {
Set sc = e.AsStatus()
}
Do source.Rewind()
- Kill ^||%oddDEF(..productionClassname)
+ Kill ^||%oddDEF
$$$ThrowOnError(sc)
}
-Method ThreeWayMerge(ByRef base, ByRef theirs, ByRef ours, Output resolved)
+ClassMethod ThreeWayMerge(base As %XML.Document, theirs As %XML.Document, ours As %XML.Document, Output resolved)
+{
+ // TODO: actually do three-way merge
+ Merge resolved = ours
+}
+
+ClassMethod BuildElementPathMap(doc As %XML.Document, Output map)
{
}
-Method TreeToClass(ByRef tree)
+ClassMethod XMLDocToStream(document As %XML.Document, ByRef outStream)
{
+ Set writer = ##class(%XML.Writer).%New()
+ Set export = ##class(%Stream.TmpCharacter).%New()
+ $$$ThrowOnError(writer.OutputToStream(export))
+ $$$ThrowOnError(writer.Document(document))
+
+ Set reader = ##class(%XML.Reader).%New()
+ $$$ThrowOnError(reader.OpenStream(export))
+ Do reader.CorrelateRoot("Ens.Config.Production")
+ If 'reader.Next(.production,.sc) {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"Could not reimport production from resolved XML."))
+ }
+ $$$ThrowOnError(sc)
+ $$$ThrowOnError(production.XMLExportToStream(.outStream,,"literal,indent"))
+}
+
+Method XMLDocToClass(resolved As %XML.Document)
+{
+ Set xData = ##class(%Dictionary.XDataDefinition).IDKEYOpen(..productionClassname,"ProductionDefinition",,.sc)
+ $$$ThrowOnError(sc)
+ Do xData.Implementation.Clear()
+ Do ..XMLDocToStream(resolved,xData.Implementation)
+ Do xData.Implementation.Rewind()
+ Write !!,$ZConvert(xData.Implementation.Read(100000),"O","HTML"),!!
}
}
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index 398b6f44..df6e7dee 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -2392,3 +2392,4 @@ ClassMethod BaselineExport(pCommitMessage = "", pPushToRemote = "") As %Status
}
}
+
diff --git a/cls/SourceControl/Git/WebUIDriver.cls b/cls/SourceControl/Git/WebUIDriver.cls
index 0bf43026..e19827d6 100644
--- a/cls/SourceControl/Git/WebUIDriver.cls
+++ b/cls/SourceControl/Git/WebUIDriver.cls
@@ -243,3 +243,4 @@ ClassMethod GetPackageVersion() As %Library.DynamicObject
}
}
+
diff --git a/cls/_zpkg/isc/sc/git/Socket.cls b/cls/_zpkg/isc/sc/git/Socket.cls
index 130972fd..7f211d15 100644
--- a/cls/_zpkg/isc/sc/git/Socket.cls
+++ b/cls/_zpkg/isc/sc/git/Socket.cls
@@ -194,3 +194,4 @@ Method SendJSON(pObject As %DynamicAbstractObject)
}
}
+
diff --git a/test/UnitTest/SourceControl/Git/XMLThreeWayMerge.cls b/test/UnitTest/SourceControl/Git/XMLThreeWayMerge.cls
new file mode 100644
index 00000000..23e07f49
--- /dev/null
+++ b/test/UnitTest/SourceControl/Git/XMLThreeWayMerge.cls
@@ -0,0 +1,93 @@
+Class UnitTest.SourceControl.Git.XMLThreeWayMerge Extends %UnitTest.TestCase
+{
+
+Method TestMerge()
+{
+ Set base = ..UtilXDataToDocument("Base")
+ Set ours = ..UtilXDataToDocument("Ours")
+ Set theirs = ..UtilXDataToDocument("Theirs")
+ Set expectedResolve = ..UtilXDataToDocument("Resolved")
+
+ Do ##class(SourceControl.Git.Util.ProductionConflictResolver).
+}
+
+Method UtilXDataToDocument(xDataName As %String) As %XML.Document
+{
+ Set xdata = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(),xDataName,,.sc)
+ $$$ThrowOnError(sc)
+ Set reader = ##class(%XML.Reader).%New()
+ $$$ThrowOnError(reader.OpenStream(xdata.Implementation))
+ Quit reader.Document
+}
+
+XData Base
+{
+
+ Health Connect Cloud Base Production
+ 1
+ -
+ 12345
+ localhost
+
+ -
+
+ -
+
+
+}
+
+XData Ours
+{
+
+ Health Connect Cloud Base Production Ours
+ 2
+ -
+ 12346
+ localhost
+
+ -
+
+ -
+
+
+}
+
+XData Theirs
+{
+
+ Health Connect Cloud Base Production
+ 2
+ -
+ 12345
+ example.com
+
+ -
+
+ -
+
+ -
+
+
+}
+
+XData Resolved
+{
+
+ Health Connect Cloud Base Production Ours
+ 2
+ -
+ 12346
+ example.com
+
+ -
+
+ -
+
+ -
+
+ -
+
+
+}
+
+}
From 327369a96a2dac3e7dd5723651ab9b4c776e8b25 Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Fri, 28 Jun 2024 08:41:03 -0400
Subject: [PATCH 03/15] feat: automatically resolve common production conflict
case
---
.../Git/Util/ProductionConflictResolver.cls | 139 +++++++++--------
cls/SourceControl/Git/Utils.cls | 9 +-
.../Git/ProductionConflictResolve.cls | 141 ++++++++++++++++++
.../SourceControl/Git/XMLThreeWayMerge.cls | 93 ------------
4 files changed, 212 insertions(+), 170 deletions(-)
create mode 100644 test/UnitTest/SourceControl/Git/ProductionConflictResolve.cls
delete mode 100644 test/UnitTest/SourceControl/Git/XMLThreeWayMerge.cls
diff --git a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
index e0f668f6..0a3d07a0 100644
--- a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
+++ b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
@@ -72,89 +72,84 @@ Method ConsumeStream() [ Private ]
Method Resolve() [ Private ]
{
- // If we got this far, ..productionClassname is a subclass of Ens.Production and ..productionFile is its file within the git root.
- Set code = 0
- Set code = code + ##class(SourceControl.Git.Utils).RunGitCommand("show",,.base,":1:"_..productionFile)
- Set code = code + ##class(SourceControl.Git.Utils).RunGitCommand("show",,.ours,":2:"_..productionFile)
- Set code = code + ##class(SourceControl.Git.Utils).RunGitCommand("show",,.theirs,":3:"_..productionFile)
-
- If code > 0 {
- $$$ThrowStatus($$$ERROR($$$GeneralError,"Unable to load base/ours/theirs for "_..productionFile))
- }
+ Set filePath = ##class(SourceControl.Git.Utils).TempFolder()_..productionFile
+ Set file = ##class(%Stream.FileCharacter).%OpenId(filePath,,.sc)
+ $$$ThrowOnError(sc)
- Do ..ClassToXMLDoc(base,.baseDoc)
- Do ..ClassToXMLDoc(ours,.oursDoc)
- Do ..ClassToXMLDoc(theirs,.theirsDoc)
+ Do ..ResolveStream(file) // Throws exception on failure
- Do ..ThreeWayMerge(baseDoc,oursDoc,theirsDoc,.resolved)
+ $$$ThrowOnError(##class(SourceControl.Git.Utils).ImportItem(..productionClassname_".CLS",1))
+ $$$ThrowOnError($System.OBJ.Compile(..productionClassname,"ck"))
- Do ..XMLDocToClass(resolved)
+ // TODO: if we add multiple resolvers, move this to the end.
+ set code = ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "add", ..productionFile)
+ if (code '= 0) {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"git add reported failure"))
+ }
+ set code = ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "commit", "-m", "Auto-resolved conflict on production class")
+ if (code '= 0) {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"git commit reported failure"))
+ }
+ set code = ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "rebase", "--continue")
+ if (code '= 0) {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"git rebase --continue reported failure"))
+ }
+
+ Set ..resolved = 1
}
-Method ClassToXMLDoc(source As %Stream.Object, Output doc As %XML.Document)
+/// Non-private to support unit testing
+ClassMethod ResolveStream(stream As %Stream.Object)
{
- Set sc = $$$OK
- Kill ^||%oddDEF
- Set ^||%oddDEF(..productionClassname)=""
- Try {
- Do source.Rewind()
- While 'source.AtEnd {
- Set tTextArray($Increment(tTextArray(0))) = source.ReadLine(,.sc)
- $$$ThrowOnError(sc)
+ // File may have:
+ /*
+ <<<<<<< HEAD
+ -
+ =======
+
-
+ >>>>>>> 607d1f6 (modified src/HCC/Connect/Production.cls add Demo5)
+
+ */
+
+ // If:
+ // * We have one such marker (<<<<<<< / ======= / >>>>>>>)
+ // * The line after >>>>>> is " "
+ // Then:
+ // * We can replace ======= with ""
+
+ Set copy = ##class(%Stream.TmpCharacter).%New()
+ Set markerCount = 0
+ Set postCloseMarker = 0
+ While 'stream.AtEnd {
+ Set line = stream.ReadLine()
+ Set start = $Extract(line,1,7)
+ If start = "<<<<<<<" {
+ Set markerCount = markerCount + 1
+ Continue
+ } ElseIf (start = ">>>>>>>") {
+ Set postCloseMarker = 1
+ Continue
+ } ElseIf (start = "=======") {
+ Do copy.WriteLine(" ")
+ Continue
+ } ElseIf postCloseMarker {
+ If $ZStrip(line,"<>W") '= "" {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"The type of conflict encountered is not handled; user must resolve manually."))
+ }
+ Set postCloseMarker = 0
}
- $$$ThrowOnError(##class(%Atelier.v1.Utils.TextServices).SetTextFromArray(.tTextArray,,..productionClassname,"CLS"))
- Merge classDef = ^||%oddDEF(..productionClassname)
-
- Set xmlStream = ##class(%Stream.TmpCharacter).%New()
- For i=1:1:$Get(classDef($$$cCLASSxdata,"ProductionDefinition",$$$cXDATAdata)) {
- Do xmlStream.WriteLine($Get(classDef($$$cCLASSxdata,"ProductionDefinition",$$$cXDATAdata,i)))
- }
- Set reader = ##class(%XML.Reader).%New()
- $$$ThrowOnError(reader.OpenStream(xmlStream))
- Set doc = reader.Document
- } Catch e {
- Set sc = e.AsStatus()
+ Do copy.WriteLine(line)
}
- Do source.Rewind()
- Kill ^||%oddDEF
- $$$ThrowOnError(sc)
-}
-ClassMethod ThreeWayMerge(base As %XML.Document, theirs As %XML.Document, ours As %XML.Document, Output resolved)
-{
- // TODO: actually do three-way merge
- Merge resolved = ours
-}
-
-ClassMethod BuildElementPathMap(doc As %XML.Document, Output map)
-{
-}
-
-ClassMethod XMLDocToStream(document As %XML.Document, ByRef outStream)
-{
- Set writer = ##class(%XML.Writer).%New()
- Set export = ##class(%Stream.TmpCharacter).%New()
- $$$ThrowOnError(writer.OutputToStream(export))
- $$$ThrowOnError(writer.Document(document))
-
- Set reader = ##class(%XML.Reader).%New()
- $$$ThrowOnError(reader.OpenStream(export))
- Do reader.CorrelateRoot("Ens.Config.Production")
- If 'reader.Next(.production,.sc) {
- $$$ThrowStatus($$$ERROR($$$GeneralError,"Could not reimport production from resolved XML."))
+ If markerCount > 1 {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"Multiple conflicts found, cannot resolve automatically."))
+ } ElseIf markerCount = 0 {
+ $$$ThrowStatus($$$ERROR($$$GeneralError,"No conflict markers found in file"))
}
- $$$ThrowOnError(sc)
- $$$ThrowOnError(production.XMLExportToStream(.outStream,,"literal,indent"))
-}
-Method XMLDocToClass(resolved As %XML.Document)
-{
- Set xData = ##class(%Dictionary.XDataDefinition).IDKEYOpen(..productionClassname,"ProductionDefinition",,.sc)
- $$$ThrowOnError(sc)
- Do xData.Implementation.Clear()
- Do ..XMLDocToStream(resolved,xData.Implementation)
- Do xData.Implementation.Rewind()
- Write !!,$ZConvert(xData.Implementation.Read(100000),"O","HTML"),!!
+ $$$ThrowOnError(stream.CopyFromAndSave(copy))
+
+ Quit 1
}
}
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index df6e7dee..4684aa7d 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -422,17 +422,17 @@ ClassMethod MergeDefaultRemoteBranch(Output alert As %String = "") As %Boolean
// "rebase" may throw an exception due to errors syncing to IRIS. In that case, roll back and keep going to abort the rebase.
write !,"Attempting to resolve differences in production definition..."
set resolver = ##class(SourceControl.Git.Util.ProductionConflictResolver).FromLog(outStream)
- write !! zw resolver write !!
if resolver.resolved {
set rebased = 1
+ TCOMMIT
write " success!"
} else {
write " unable to resolve - "_resolver.errorMessage
- while $TLevel > initTLevel {
- TROLLBACK 1
- }
}
}
+ while $TLevel > initTLevel {
+ TROLLBACK 1
+ }
if 'rebased {
do ..RunGitCommand("rebase",.errStream, .outStream,"--abort")
do ..PrintStreams(errStream, outStream)
@@ -2392,4 +2392,3 @@ ClassMethod BaselineExport(pCommitMessage = "", pPushToRemote = "") As %Status
}
}
-
diff --git a/test/UnitTest/SourceControl/Git/ProductionConflictResolve.cls b/test/UnitTest/SourceControl/Git/ProductionConflictResolve.cls
new file mode 100644
index 00000000..5e34ae4c
--- /dev/null
+++ b/test/UnitTest/SourceControl/Git/ProductionConflictResolve.cls
@@ -0,0 +1,141 @@
+Class UnitTest.SourceControl.Git.ProductionConflictResolve Extends %UnitTest.TestCase
+{
+
+Method TestResolve()
+{
+ Set file = ##class(%Stream.FileCharacter).%New()
+ Set file.RemoveOnClose = 1
+ Set xdata = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(),"SampleFile1",,.sc)
+ While 'xdata.Data.AtEnd {
+ Do file.WriteLine(xdata.Data.ReadLine())
+ }
+ $$$ThrowOnError(file.%Save())
+
+ Set resolved = ##class(%Stream.FileCharacter).%New()
+ Set resolved.RemoveOnClose = 1
+ Set xdata = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(),"ResolvedFile1",,.sc)
+ While 'xdata.Data.AtEnd {
+ Do resolved.WriteLine(xdata.Data.ReadLine())
+ }
+ $$$ThrowOnError(resolved.%Save())
+
+ Do ##class(SourceControl.Git.Util.ProductionConflictResolver).ResolveStream(file)
+
+ Do $$$AssertFilesSame(file.Filename,resolved.Filename)
+}
+
+XData SampleFile1 [ MimeType = text/plain ]
+{
+Class HCC.Connect.Production Extends Ens.Production
+{
+
+XData ProductionDefinition
+{
+
+ Health Connect Cloud Base Production
+ 1
+ -
+
+ -
+ bank
+
+ -
+
+ -
+ HCC.Connect.FeatureAProcessRoutingRule
+
+ -
+
+ -
+
+ -
+ HCC.Connect.FeatureBProcessRoutingRule
+
+ -
+
+ -
+ 127.0.0.1
+ 8080
+
+ -
+
+ -
+
+ -
+ 1.4.3.5
+
+ -
+ 12345
+ localhost
+
+ -
+
+<<<<<<< HEAD
+ -
+=======
+
-
+>>>>>>> 607d1f6 (modified src/HCC/Connect/Production.cls add Demo5)
+
+
+}
+
+}
+}
+
+XData ResolvedFile1 [ MimeType = text/plain ]
+{
+Class HCC.Connect.Production Extends Ens.Production
+{
+
+XData ProductionDefinition
+{
+
+ Health Connect Cloud Base Production
+ 1
+ -
+
+ -
+ bank
+
+ -
+
+ -
+ HCC.Connect.FeatureAProcessRoutingRule
+
+ -
+
+ -
+
+ -
+ HCC.Connect.FeatureBProcessRoutingRule
+
+ -
+
+ -
+ 127.0.0.1
+ 8080
+
+ -
+
+ -
+
+ -
+ 1.4.3.5
+
+ -
+ 12345
+ localhost
+
+ -
+
+ -
+
+ -
+
+
+}
+
+}
+}
+
+}
diff --git a/test/UnitTest/SourceControl/Git/XMLThreeWayMerge.cls b/test/UnitTest/SourceControl/Git/XMLThreeWayMerge.cls
deleted file mode 100644
index 23e07f49..00000000
--- a/test/UnitTest/SourceControl/Git/XMLThreeWayMerge.cls
+++ /dev/null
@@ -1,93 +0,0 @@
-Class UnitTest.SourceControl.Git.XMLThreeWayMerge Extends %UnitTest.TestCase
-{
-
-Method TestMerge()
-{
- Set base = ..UtilXDataToDocument("Base")
- Set ours = ..UtilXDataToDocument("Ours")
- Set theirs = ..UtilXDataToDocument("Theirs")
- Set expectedResolve = ..UtilXDataToDocument("Resolved")
-
- Do ##class(SourceControl.Git.Util.ProductionConflictResolver).
-}
-
-Method UtilXDataToDocument(xDataName As %String) As %XML.Document
-{
- Set xdata = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(),xDataName,,.sc)
- $$$ThrowOnError(sc)
- Set reader = ##class(%XML.Reader).%New()
- $$$ThrowOnError(reader.OpenStream(xdata.Implementation))
- Quit reader.Document
-}
-
-XData Base
-{
-
- Health Connect Cloud Base Production
- 1
- -
- 12345
- localhost
-
- -
-
- -
-
-
-}
-
-XData Ours
-{
-
- Health Connect Cloud Base Production Ours
- 2
- -
- 12346
- localhost
-
- -
-
- -
-
-
-}
-
-XData Theirs
-{
-
- Health Connect Cloud Base Production
- 2
- -
- 12345
- example.com
-
- -
-
- -
-
- -
-
-
-}
-
-XData Resolved
-{
-
- Health Connect Cloud Base Production Ours
- 2
- -
- 12346
- example.com
-
- -
-
- -
-
- -
-
- -
-
-
-}
-
-}
From 5cbf1b662a39596d899eb64e558966c81ebf5e5e Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Fri, 28 Jun 2024 08:56:04 -0400
Subject: [PATCH 04/15] Update CHANGELOG.md
---
CHANGELOG.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1dbba46d..d9656079 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Items mapped from database other than namespace's default routine database are now ignored by default when exporting or adding files
- New setting to configure whether mapped items should be should be treated as read-only
- Added a basic mode to automatically perform functionality expected in basic use cases (#349)
-- New sync operation for basic mode that fetches, pulls, commits and then pushes (#349)
+- New sync operation for basic mode that fetches, pulls, commits, pushes, rebases, and pushes again (#349)
+- "Sync" operation in basic mode automatically resolves the class of merge conflict common in production classes where multiple independent items are added in different feature branches
- Now skips files belonging to other git enabled packages in `##class(SourceControl.Git.Change).RefreshUncommitted()` (#347)
- Added a new "Branch" parameter to `##class(SourceControl.Git.PullEventHandler)` (#351)
- Command-line utility to do a baseline export of items in a namespace
From be5a51d43da41b5ffad99a768dbcb8ac14c3ed91 Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Fri, 28 Jun 2024 09:39:14 -0400
Subject: [PATCH 05/15] fix: various things for demo
IncrementalLoad needs to compile with ck, not u - production xdata changes aren't picked up with "u"
Protection against argumentless operations in RunGitCommandWithInput
---
.../Git/PullEventHandler/IncrementalLoad.cls | 3 +--
cls/SourceControl/Git/Utils.cls | 15 ++++++++++-----
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls b/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls
index 5f3a8b11..cb5ce93a 100644
--- a/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls
+++ b/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls
@@ -35,7 +35,7 @@ Method OnPull() As %Status
write !, "Nothing to compile."
quit $$$OK
}
- quit $system.OBJ.CompileList(.compilelist, "cukb")
+ quit $system.OBJ.CompileList(.compilelist, "ck")
}
Method DeleteFile(item As %String) As %Status
@@ -71,4 +71,3 @@ Method DeleteFile(item As %String) As %Status
}
}
-
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index 015269c1..5223ab0d 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -1608,10 +1608,14 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O
if (command = "checkout"){
set syncIrisWithDiff = 1
- set diffCompare = args(args)
+ if $data(args) && $data(args(args),diffCompare) {
+ // no-op
+ }
} elseif (command = "merge") || (command = "rebase") || (command = "pull"){
set syncIrisWithCommand = 1
- set diffCompare = args(args)
+ if $data(args) && $data(args(args),diffCompare) {
+ // no-op
+ }
}
for i=1:1:$get(args) {
@@ -1688,10 +1692,12 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O
for stream=errStream,outStream {
set stream.RemoveOnClose = 1
}
- do ..PrintStreams(errStream, outStream)
+
if syncIrisWithDiff {
+ do ..PrintStreams(errStream, outStream)
$$$ThrowOnError(..SyncIrisWithRepoThroughDiff(.files))
} elseif syncIrisWithCommand {
+ do ..PrintStreams(errStream, outStream)
$$$ThrowOnError(..SyncIrisWithRepoThroughCommand(.outStream))
}
quit returnCode
@@ -1746,7 +1752,7 @@ ClassMethod SyncIrisWithRepoThroughDiff(ByRef files) As %Status
set key = $order(files(""))
set deletedFiles = ""
set addedFiles = ""
- while(key '= "") {
+ while (key '= "") {
set modification = files(key)
if (modification.changeType = "D"){
if (modification.internalName '= "") {
@@ -1757,7 +1763,6 @@ ClassMethod SyncIrisWithRepoThroughDiff(ByRef files) As %Status
if (modification.internalName '= "") {
set addedFiles = addedFiles_","_modification.internalName
set files(key) = modification
-
}
}
set key = $order(files(key))
From 59859feed5d4dbb453980b35bbef45fa985a6eb1 Mon Sep 17 00:00:00 2001
From: isc-hwojnick
Date: Fri, 28 Jun 2024 15:59:23 -0400
Subject: [PATCH 06/15] now doesn't prompt for commit msg if no uncomitted
files
---
.../Git/PullEventHandler/IncrementalLoad.cls | 1 +
cls/SourceControl/Git/Settings.cls | 1 +
cls/SourceControl/Git/Utils.cls | 43 ++++++++++++++-----
3 files changed, 34 insertions(+), 11 deletions(-)
diff --git a/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls b/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls
index da34b62d..5f3a8b11 100644
--- a/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls
+++ b/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls
@@ -71,3 +71,4 @@ Method DeleteFile(item As %String) As %Status
}
}
+
diff --git a/cls/SourceControl/Git/Settings.cls b/cls/SourceControl/Git/Settings.cls
index dc73319f..aa5357ea 100644
--- a/cls/SourceControl/Git/Settings.cls
+++ b/cls/SourceControl/Git/Settings.cls
@@ -210,3 +210,4 @@ Method OnAfterConfigure() As %Boolean
}
}
+
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index 02eceb85..6f3c66a1 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -253,9 +253,15 @@ ClassMethod UserAction(InternalName As %String, MenuName As %String, ByRef Targe
set Action = 7
quit $$$OK
} elseif (menuItemName = "Sync") {
- set Target = "Enter a commit message for the sync operation"
- set Action = 7
- set Msg = ..PreSync()
+ if ..CheckForUncommittedFiles() {
+ set Target = "Enter a commit message for the sync operation"
+ set Action = 7
+ set Msg = ..PreSync()
+ } else {
+ do ..Sync("")
+ }
+
+
quit $$$OK
} elseif (menuItemName = "Push") {
quit ..Push()
@@ -376,17 +382,32 @@ ClassMethod PreSync() As %String
/// Commits all the files as needed by the Sync operation
ClassMethod SyncCommit(Msg As %String) As %Status
{
- set uncommittedFilesWithAction = ##class(SourceControl.Git.Utils).UncommittedWithAction().%Get("user")
- set username = ..GitUserName()
- set email = ..GitUserEmail()
- set author = username_" <"_email_">"
- do ..RunGitWithArgs(.errStream, .outStream, "commit", "--author", author, "-m", Msg)
- do ..PrintStreams(errStream, outStream)
- $$$QuitOnError(..ClearUncommitted(uncommittedFilesWithAction))
- $$$QuitOnError(##class(SourceControl.Git.Change).RefreshUncommitted(,,,1))
+
+ if ..CheckForUncommittedFiles() {
+ set uncommittedFilesWithAction = ##class(SourceControl.Git.Utils).UncommittedWithAction().%Get("user")
+ set username = ..GitUserName()
+ set email = ..GitUserEmail()
+ set author = username_" <"_email_">"
+ do ..RunGitWithArgs(.errStream, .outStream, "commit", "--author", author, "-m", Msg)
+ do ..PrintStreams(errStream, outStream)
+ $$$QuitOnError(..ClearUncommitted(uncommittedFilesWithAction))
+ $$$QuitOnError(##class(SourceControl.Git.Change).RefreshUncommitted(,,,1))
+ }
+
quit $$$OK
}
+ClassMethod CheckForUncommittedFiles() As %Boolean
+{
+ set uncommittedFilesWithAction = ##class(SourceControl.Git.Utils).UncommittedWithAction().%Get("user")
+ set valInArr = uncommittedFilesWithAction.%Pop()
+ if valInArr = "" {
+ return 0
+ } else {
+ quit 1
+ }
+}
+
/// Goes through all the added files and stages them
ClassMethod StageAddedFiles()
{
From ae774d0c5a8861304b571dede945624d64480616 Mon Sep 17 00:00:00 2001
From: isc-hwojnick
Date: Fri, 28 Jun 2024 16:01:26 -0400
Subject: [PATCH 07/15] changelog updated
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40dc0d5e..1730bfd5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Modifications to local repo files are now synced with IRIS (#153)
- Menu items names are properly translated from internal name in VSCode, Management Portal (#372)
+- Syncing only prompts users for a commit message if there are uncommitted files (#390)
## [2.3.1] - 2024-04-30
From 9a35f64346d7217d1e7600419eca5e6a0270b8c5 Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Mon, 1 Jul 2024 15:06:31 -0400
Subject: [PATCH 08/15] feat: alert with warning if sync fails without commit
---
cls/SourceControl/Git/Utils.cls | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index 201bc134..40f74f43 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -258,10 +258,13 @@ ClassMethod UserAction(InternalName As %String, MenuName As %String, ByRef Targe
set Action = 7
set Msg = ..PreSync()
} else {
- do ..Sync("")
+ set Target = ""
+ do ..Sync("",.Target)
+ if (Target '= "") {
+ set Action = 6
+ }
}
-
quit $$$OK
} elseif (menuItemName = "Push") {
quit ..Push()
@@ -462,7 +465,7 @@ ClassMethod MergeDefaultRemoteBranch(Output alert As %String = "") As %Boolean
if 'rebased {
do ..RunGitCommand("rebase",.errStream, .outStream,"--abort")
do ..PrintStreams(errStream, outStream)
- set alert = "WARNING: Remote branch '"_defaultMergeBranch_"' could not be merged due to conflicts. Changes have been pushed to '"_..GetCurrentBranch()_"' and must be resolved in your git remote."
+ set alert = "WARNING: Remote branch '"_defaultMergeBranch_"' could not be merged due to conflicts. Changes have been pushed to '"_..GetCurrentBranch()_"' and must be resolved in your git remote. See log for more details."
write !,alert,!
}
}
From feff521dba8dfd11622615bdb93698cf4910c326 Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Mon, 1 Jul 2024 16:14:16 -0400
Subject: [PATCH 09/15] fix: diff handles renames on checkout
---
cls/SourceControl/Git/Utils.cls | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index 40f74f43..5b6bf4c0 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -1677,11 +1677,18 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O
set modification = ##class(SourceControl.Git.Modification).%New()
set modification.changeType = $piece(file, $c(9), 1)
- set modification.externalName = $zstrip($piece(file, $c(9),2),"
Date: Tue, 2 Jul 2024 10:52:50 -0400
Subject: [PATCH 10/15] Update ProductionConflictResolver.cls
---
cls/SourceControl/Git/Util/ProductionConflictResolver.cls | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
index 0a3d07a0..79298646 100644
--- a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
+++ b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
@@ -86,7 +86,7 @@ Method Resolve() [ Private ]
if (code '= 0) {
$$$ThrowStatus($$$ERROR($$$GeneralError,"git add reported failure"))
}
- set code = ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "commit", "-m", "Auto-resolved conflict on production class")
+ set code = ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "commit", "--no-edit")
if (code '= 0) {
$$$ThrowStatus($$$ERROR($$$GeneralError,"git commit reported failure"))
}
From c82de157b8d43af0c0d636304cc31a6e939b3b5c Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Tue, 2 Jul 2024 11:30:24 -0400
Subject: [PATCH 11/15] fix: menu discard calls PullEventHandler
refactor: add and use utility methods in PullEventHandler
---
cls/SourceControl/Git/PullEventHandler.cls | 23 ++++++++++++++++++++++
cls/SourceControl/Git/Utils.cls | 18 ++++-------------
2 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/cls/SourceControl/Git/PullEventHandler.cls b/cls/SourceControl/Git/PullEventHandler.cls
index 3dfb3a4c..66728728 100644
--- a/cls/SourceControl/Git/PullEventHandler.cls
+++ b/cls/SourceControl/Git/PullEventHandler.cls
@@ -20,5 +20,28 @@ Method OnPull() As %Status [ Abstract ]
{
}
+/// files is an integer-subscripted array of SourceControl.Git.Modification objects.
+ClassMethod ForModifications(ByRef files) As %Status
+{
+ set event = $classmethod(##class(SourceControl.Git.Utils).PullEventClass(),"%New")
+ set event.LocalRoot = ##class(SourceControl.Git.Utils).TempFolder()
+ merge event.ModifiedFiles = files
+ quit event.OnPull()
}
+/// InternalName may be a comma-delimited string or $ListBuild list
+ClassMethod ForInternalNames(InternalName As %String) As %Status
+{
+ set list = $select($listvalid(InternalName):InternalName,1:$ListFromString(InternalName))
+ set pointer = 0
+ while $listnext(list,pointer,InternalName) {
+ set mod = ##class(SourceControl.Git.Modification).%New()
+ set mod.internalName = InternalName
+ set mod.externalName = ##class(SourceControl.Git.Utils).FullExternalName(InternalName)
+ set mod.changeType = "M"
+ set files($i(files)) = mod
+ }
+ quit ..ForModifications(.files)
+}
+
+}
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index 5b6bf4c0..a86181a1 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -338,8 +338,7 @@ ClassMethod Revert(InternalName As %String) As %Status
do ..RunGitCommand("checkout", .errStream, .outStream, "--", filename)
$$$QuitOnError(##class(SourceControl.Git.Change).RemoveUncommitted(filename,0,1))
$$$QuitOnError(##class(SourceControl.Git.Change).RefreshUncommitted(0,1,,1))
- $$$QuitOnError(..ImportItem(InternalName,1))
- quit $$$OK
+ quit ##class(SourceControl.Git.PullEventHandler).ForInternalNames(InternalName)
}
ClassMethod Commit(InternalName As %String, Message As %String = "example commit message") As %Status
@@ -1415,10 +1414,7 @@ ClassMethod ImportRoutines(force As %Boolean = 0) As %Status
}
}
- set eventHandler = $classmethod(..PullEventClass(),"%New")
- set eventHandler.LocalRoot = ..TempFolder()
- merge eventHandler.ModifiedFiles = files
- set sc = eventHandler.OnPull()
+ set sc = ##class(SourceControl.Git.PullEventHandler).ForModifications(.files)
if $$$ISERR(sc) {
set ec = $$$ADDSC(ec,sc)
}
@@ -1772,10 +1768,7 @@ ClassMethod SyncIrisWithRepoThroughCommand(ByRef outStream) As %Status
}
do outStream.Rewind()
- set event = $classmethod(..PullEventClass(),"%New")
- set event.LocalRoot = ..TempFolder()
- merge event.ModifiedFiles = files
- quit event.OnPull()
+ quit ##class(SourceControl.Git.PullEventHandler).ForModifications(.files)
}
ClassMethod SyncIrisWithRepoThroughDiff(ByRef files) As %Status
@@ -1809,10 +1802,7 @@ ClassMethod SyncIrisWithRepoThroughDiff(ByRef files) As %Status
set sc = ##class(SourceControl.Git.Utils).AddToServerSideSourceControl(addedFiles)
}
- set event = $classmethod(..PullEventClass(),"%New")
- set event.LocalRoot = ..TempFolder()
- merge event.ModifiedFiles = files
- quit event.OnPull()
+ quit ##class(SourceControl.Git.PullEventHandler).ForModifications(.files)
}
ClassMethod GenerateCommitMessageFromFiles(filesWithActions) As %String
From c6752f87afbbe1564c9a6739258e7ad6c8a2b26a Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Tue, 2 Jul 2024 13:09:01 -0400
Subject: [PATCH 12/15] fix: sync rebase properly with diff
---
.../Git/Util/ProductionConflictResolver.cls | 3 +-
cls/SourceControl/Git/Utils.cls | 65 +++++++++++--------
2 files changed, 41 insertions(+), 27 deletions(-)
diff --git a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
index 79298646..68ca4a50 100644
--- a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
+++ b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
@@ -90,12 +90,13 @@ Method Resolve() [ Private ]
if (code '= 0) {
$$$ThrowStatus($$$ERROR($$$GeneralError,"git commit reported failure"))
}
+
set code = ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "rebase", "--continue")
if (code '= 0) {
$$$ThrowStatus($$$ERROR($$$GeneralError,"git rebase --continue reported failure"))
}
- Set ..resolved = 1
+ set ..resolved = 1
}
/// Non-private to support unit testing
diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls
index a86181a1..e7977155 100644
--- a/cls/SourceControl/Git/Utils.cls
+++ b/cls/SourceControl/Git/Utils.cls
@@ -436,6 +436,9 @@ ClassMethod MergeDefaultRemoteBranch(Output alert As %String = "") As %Boolean
do ..RunGitWithArgs(.errStream, .outStream, "fetch", "origin", defaultMergeBranch_":"_defaultMergeBranch)
do ..PrintStreams(errStream, outStream)
+ do ..RunGitWithArgs(,.outStream, "rev-parse", defaultMergeBranch)
+ set startSha = outStream.ReadLine()
+
// Start a transaction so code changes can be rolled back
set initTLevel = $TLevel
try {
@@ -461,7 +464,11 @@ ClassMethod MergeDefaultRemoteBranch(Output alert As %String = "") As %Boolean
while $TLevel > initTLevel {
TROLLBACK 1
}
- if 'rebased {
+ if rebased {
+ do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "diff", startSha, "HEAD", "--name-status")
+ do ##class(SourceControl.Git.Utils).ParseDiffStream(outStream,,.finalFileSet)
+ do ##class(SourceControl.Git.Utils).SyncIrisWithRepoThroughDiff(.finalFileSet)
+ } else {
do ..RunGitCommand("rebase",.errStream, .outStream,"--abort")
do ..PrintStreams(errStream, outStream)
set alert = "WARNING: Remote branch '"_defaultMergeBranch_"' could not be merged due to conflicts. Changes have been pushed to '"_..GetCurrentBranch()_"' and must be resolved in your git remote. See log for more details."
@@ -1668,29 +1675,7 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O
do ..RunGitCommand("fetch", .errorStream, .outputStream)
kill errorStream, outputStream
do ##class(SourceControl.Git.Utils).RunGitCommandWithInput("diff",,.errorStream,.outputStream, diffBase_$Case(diffCompare,"":"",:"..")_diffCompare, "--name-status")
- while (outputStream.AtEnd = 0) {
- set file = outputStream.ReadLine()
- set modification = ##class(SourceControl.Git.Modification).%New()
- set modification.changeType = $piece(file, $c(9), 1)
-
- set modification.externalName = $zstrip($piece(file, $c(9), 2),"
Date: Tue, 2 Jul 2024 13:12:43 -0400
Subject: [PATCH 13/15] Update CHANGELOG.md
---
CHANGELOG.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66dace33..ac4f26bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 'New Branch' menu option in basic now will create new branches from the configured default merge branch (#366)
- Merging back with the default merge branch is now a part of the basic mode's Sync flow (#366)
- Added a new option "compileOnImport". If true, Import options will compile files using the pull event handler. (#362)
+- If merge conflicts occur on the production during Sync due to multiple branches all adding business hosts, they will be resolved automatically.
### Fixed
- Modifications to local repo files are now synced with IRIS (#153)
@@ -29,6 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Git operations from the WebUI now don't unlock the session if they aren't read-only
- Syncing only prompts users for a commit message if there are uncommitted files (#390)
- WebUI works properly for users with %Developer without needing to add further SQL privileges (#365)
+- Fixed `` error running Import All (#380)
+- Discarding changes now recompiles - critical for productions and some other cases (#387)
## [2.3.1] - 2024-04-30
From b2dceed4b1036792ba87b752812843fa65d98258 Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Tue, 2 Jul 2024 13:13:13 -0400
Subject: [PATCH 14/15] Update CHANGELOG.md
remove duplicate entry
---
CHANGELOG.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac4f26bb..689bf6bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,7 +21,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 'New Branch' menu option in basic now will create new branches from the configured default merge branch (#366)
- Merging back with the default merge branch is now a part of the basic mode's Sync flow (#366)
- Added a new option "compileOnImport". If true, Import options will compile files using the pull event handler. (#362)
-- If merge conflicts occur on the production during Sync due to multiple branches all adding business hosts, they will be resolved automatically.
### Fixed
- Modifications to local repo files are now synced with IRIS (#153)
From d4923f0fb82b4a449c326a1bc399088aac4ba766 Mon Sep 17 00:00:00 2001
From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com>
Date: Wed, 3 Jul 2024 12:51:58 -0400
Subject: [PATCH 15/15] fix: only call %Extends if class defined
---
cls/SourceControl/Git/Util/ProductionConflictResolver.cls | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
index 68ca4a50..ec29a1a9 100644
--- a/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
+++ b/cls/SourceControl/Git/Util/ProductionConflictResolver.cls
@@ -65,7 +65,7 @@ Method ConsumeStream() [ Private ]
$$$ThrowStatus($$$ERROR($$$GeneralError,"File with conflict is not a class."))
}
Set ..productionClassname = $Piece(internalName,".",1,*-1)
- If '$$$comClassDefined(..productionClassname) && '$ClassMethod(..productionClassname,"%Extends","Ens.Production") {
+ If '($$$comClassDefined(..productionClassname) && $ClassMethod(..productionClassname,"%Extends","Ens.Production")) {
$$$ThrowStatus($$$ERROR($$$GeneralError,"File with conflict is not an interoperability production."))
}
}