Skip to content
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed errors on production page when item settings need to be XML escaped (#667)
- Fixed push button not appearing after commit (#654)
- Fixed merge conflict resolution on stash popping (#531)

- Fix "Max $ZF String" error when committing lots of files (#617)

## [2.8.0] - 2024-12-06

### Added
Expand Down
99 changes: 97 additions & 2 deletions cls/SourceControl/Git/Utils.cls
Original file line number Diff line number Diff line change
Expand Up @@ -3033,8 +3033,7 @@ ClassMethod SetConfiguredRemote(url) As %String
quit output
}

// Returns true if the current branch is the default merge branch and we are in basic mode

/// Returns true if the current branch is the default merge branch and we are in basic mode
ClassMethod InDefaultBranchBasicMode() As %Boolean
{
set basicMode = ..BasicMode()
Expand Down Expand Up @@ -3077,4 +3076,100 @@ ClassMethod RunGitAndHandleMerge(command As %String, inFile As %String, Output r
return $$$ERROR($$$GeneralError,"git reported failure")
}

/// Runs the commit on the specified files (unstaging and restaging currently staged files)
ClassMethod RunGitCommandReStage(Output outStream, Output errStream, command As %String, ByRef fileList As %Library.DynamicArray, args...) As %Integer
{
set sc = $$$OK
set tInitialTLevel = $TLevel
try {
LOCK +^GitCommit(42):5
if '$Test {
$$$ThrowStatus($$$ERROR($$$GeneralError, "Couldn't grab a lock. Try again later"))
}
set tGitCommitLocked = 1


TSTART

// Unstage and save all currently staged files
set sc = ..GitUnstage(.unstaged)

// Stage all files
set iterator = fileList.%GetIterator()
while iterator.%GetNext(.key, .value) {
do ..RunGitCommand("add",.err, .out, value)
}

// Run the git command
set returnCode = ..RunGitWithArgs(.errStream, .outStream, command, args...)

// Restage files
do ..GitStage(.unstaged)

TCOMMIT
} catch e {
set sc = e.AsStatus()
}

If tGitCommitLocked {
Lock -^GitCommit(42)
}

While $TLevel > tInitialTLevel {
TROLLBACK 1
}

return returnCode
}

ClassMethod GitStage(ByRef files As %Library.DynamicObject) As %Status
{
set staged = files.%Get("staged")
set tracked = files.%Get("tracked")

set iterator = staged.%GetIterator()
while iterator.%GetNext(.key, .value) {
do ..RunGitCommand("add",.errStream,.outStream, value)
}

set iterator = tracked.%GetIterator()
while iterator.%GetNext(.key, .value) {
do ..RunGitCommand("add",.errStream, .outStream, value, "--intent-to-add")
}
return $$$OK
}

/// Unstages all currently staged files and returns them in the array
ClassMethod GitUnstage(Output output As %Library.DynamicObject) As %Status
{
set stagedList = []
set trackedList = []

do ..RunGitCommandWithInput("status",, .errStream, .outStream, "--porcelain")
set line = outStream.ReadLine()
// Read through output
while (line'="") {
set staged = 0
set tracked = 0
set filename = $piece(line, " ", *-0)
if ($extract(line,1,1) '= " ") && ($extract(line,1,1) '= "?") {
set staged = 1
}
elseif ($extract(line,2,2) = "A") {
set tracked = 1
}
if staged {
do stagedList.%Push(filename)
do ..RunGitCommand("reset", .errStream, .out, filename)
} elseif tracked {
do trackedList.%Push(filename)
do ..RunGitCommand("reset",.errStream,.out, filename)
}
set line = outStream.ReadLine()
}

set output = {"staged": (stagedList), "tracked": (trackedList)}
return $$$OK
}

}
113 changes: 73 additions & 40 deletions cls/SourceControl/Git/WebUIDriver.cls
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out
do %request.Set("EXPIRES",0)
do ##class(%CSP.StreamServer).OnPreHTTP() // Need to call this to set headers properly
set %stream = 1 // Leak this to webuidriver.csp
} elseif $match(pathStart,"git-command|git|dirname|hostname|viewonly|discarded-states|restore-discarded|contexts|create-branch") {
} elseif $match(pathStart,"git-command|git|dirname|hostname|viewonly|discarded-states|restore-discarded|contexts|create-branch|git-command-unstage") {
if (%request.Method = "GET") {
set %data = ##class(%Stream.TmpCharacter).%New()
// Things not handled from Python backend:
Expand Down Expand Up @@ -220,36 +220,15 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out
}
// Want to invoke merge conflict autoresolver in case of issues
set returnCode = ##class(SourceControl.Git.Utils).RunGitCommandWithInput("-c",inFile,.errStream,.outStream,gitArgs...)

set %data = ##class(%Stream.TmpCharacter).%New()
set changeTerminators = (%data.LineTerminator '= $char(13,10))
set %data.LineTerminator = $char(13,10) // For the CSPGateway.
do outStream.Rewind()
while 'outStream.AtEnd {
do %data.WriteLine(outStream.ReadLine())
}

set nLines = 0
do errStream.Rewind()
while 'errStream.AtEnd {
do %data.WriteLine(errStream.ReadLine())
set:changeTerminators nLines = nLines + 1
}
do ..ConvertGitOutput(.outStream,.errStream,returnCode,.%data)

do %data.WriteLine("Git-Stderr-Length: " _ (errStream.Size + nLines))
do %data.Write("Git-Return-Code: " _ returnCode) // No ending newline expected
do %data.Rewind()
if '$listfind(readOnlyCommands,baseCommand) {
do ##class(SourceControl.Git.Change).RefreshUncommitted(,,,1)
}
set handled = 1
} elseif (pathStart = "git-command") {
set stringBody = ""
while '%request.Content.AtEnd {
set stringBody = stringBody _ %request.Content.Read()
}
set stringBody = $zconvert(stringBody,"I","UTF8")
set requestBody = ##class(%Library.DynamicObject).%FromJSON(stringBody)
do ..UnpackRequest(.%request, .requestBody)
set command = requestBody.command

set gitCmd = command.%Get(0)
Expand Down Expand Up @@ -309,22 +288,7 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out
do %data.Rewind()
} else {
set returnCode = ##class(SourceControl.Git.Utils).RunGitCommandWithInput("-c", inFile, .errStream, .outStream, argsArr...)
set %data = ##class(%Stream.TmpCharacter).%New()
set changeTerminators = (%data.LineTerminator '= $char(13,10))
set %data.LineTerminator = $char(13,10) // For the CSPGateway.
while 'outStream.AtEnd {
do %data.WriteLine(outStream.ReadLine())
}

set nLines = 0
while 'errStream.AtEnd {
do %data.WriteLine(errStream.ReadLine())
set:changeTerminators nLines = nLines + 1
}

do %data.WriteLine("Git-Stderr-Length: " _ (errStream.Size + nLines))
do %data.Write("Git-Return-Code: " _ returnCode) // No ending newline expected
do %data.Rewind()
do ..ConvertGitOutput(.outStream,.errStream,returnCode,.%data)
}
set handled = 1

Expand Down Expand Up @@ -368,6 +332,41 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out
}
set handled = 1
}
} elseif (pathStart = "git-command-unstage") {
do ..UnpackRequest(.%request, .requestBody)

set flags = requestBody.flags
if (flags '= "") {
set flaglist = $listfromstring(flags,",")
for i=1:1:$listlength(flaglist) {
set flag = $listget(flaglist,i)
set args($increment(args)) = flag
}
}
set message = requestBody.message
if (message '= "") {
set args($increment(args)) = "-m"
set args($increment(args)) = message
}
set details = requestBody.details
if (details '= "") {
set args($increment(args)) = "-m"
set args($increment(args)) = details
}
set command = requestBody.command

// Extract Files
set fileList = []
set files = requestBody.files
set iter = files.%GetIterator()
while iter.%GetNext(,.value) {
do fileList.%Push(value)
}

set returnCode = ##class(SourceControl.Git.Utils).RunGitCommandReStage(.outStream,.errStream,command,.fileList,args...)

do ..ConvertGitOutput(.outStream,.errStream,returnCode,.%data)
set handled = 1
}
}
}
Expand Down Expand Up @@ -465,4 +464,38 @@ ClassMethod GetRemote() As %Library.DynamicObject
quit {"remote": (remote)}
}

ClassMethod UnpackRequest(ByRef %request As %CSP.Request, Output obj As %Library.DynamicObject) As %Status
{
set stringBody = ""
while '%request.Content.AtEnd {
set stringBody = stringBody _ %request.Content.Read()
}
set stringBody = $zconvert(stringBody,"I","UTF8")
set obj = ##class(%Library.DynamicObject).%FromJSON(stringBody)
return $$$OK
}

ClassMethod ConvertGitOutput(ByRef outStream, ByRef errStream, returnCode As %String, Output %data) As %Status
{
set %data = ##class(%Stream.TmpCharacter).%New()
set changeTerminators = (%data.LineTerminator '= $char(13,10))
set %data.LineTerminator = $char(13,10) // For the CSPGateway.
do outStream.Rewind()
while 'outStream.AtEnd {
do %data.WriteLine(outStream.ReadLine())
}

set nLines = 0
do errStream.Rewind()
while 'errStream.AtEnd {
do %data.WriteLine(errStream.ReadLine())
set:changeTerminators nLines = nLines + 1
}

do %data.WriteLine("Git-Stderr-Length: " _ (errStream.Size + nLines))
do %data.Write("Git-Return-Code: " _ returnCode) // No ending newline expected
do %data.Rewind()
return $$$OK
}

}
Loading
Loading