diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ae1e72..1b1f2ac2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix creating new branch from Git Web UI (#591) - Fix wording for Git Repo Root Directory (#601) - Fix Diff View options not applying immediately (#590) +- Cleaned up parsing of command output in Git Web UI (#609) - Fix TempFolder misspecification (#611) ## [2.6.0] - 2024-10-07 diff --git a/cls/SourceControl/Git/WebUIDriver.cls b/cls/SourceControl/Git/WebUIDriver.cls index a8172be3..38388f16 100644 --- a/cls/SourceControl/Git/WebUIDriver.cls +++ b/cls/SourceControl/Git/WebUIDriver.cls @@ -235,9 +235,7 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out do %data.WriteLine(errStream.ReadLine()) set:changeTerminators nLines = nLines + 1 } - - // Need to write out an extra newline - do %data.WriteLine() + do %data.WriteLine("Git-Stderr-Length: " _ (errStream.Size + nLines)) do %data.Write("Git-Return-Code: " _ returnCode) // No ending newline expected do %data.Rewind() @@ -295,10 +293,7 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out do %data.WriteLine(errStream.ReadLine()) set:changeTerminators nLines = nLines + 1 } - - // Need to write out two lines or we get an infinite loop in JavaScript... - do %data.WriteLine() - do %data.WriteLine() + do %data.WriteLine("Git-Stderr-Length: " _ (errStream.Size + nLines)) do %data.Write("Git-Return-Code: " _ returnCode) // No ending newline expected do %data.Rewind() diff --git a/git-webui/release/share/git-webui/webui/js/git-webui.js b/git-webui/release/share/git-webui/webui/js/git-webui.js index 6825f72c..6a7972f5 100644 --- a/git-webui/release/share/git-webui/webui/js/git-webui.js +++ b/git-webui/release/share/git-webui/webui/js/git-webui.js @@ -125,6 +125,27 @@ webui.gitVersion = function() { }) } +webui.parseGitResponse = function(data) { + var matches = data.match(/([\S\s]*)Git-Stderr-Length:\s(\d*)[\r\n]{1,2}Git-Return-Code:\s(\d*)/); + if (!matches || (matches.length < 4)) { + return { + output: '', + message: '', + rcode: 1 + } + } + var messageData = matches[1].replace(/(\r\n)/gm, "\n"); + var errorLength = parseInt(matches[2],10); + var boundary = messageData.length - errorLength; + var stdout = messageData.substring(0, boundary) + var stderr = messageData.substring(boundary); + return { + output: stdout, + message: stderr, + rcode: matches[3] + }; +} + webui.git_command = function(command, callback) { $.ajax({ url: "git-command", @@ -134,32 +155,10 @@ webui.git_command = function(command, callback) { command: command }), success: function(data) { - // Convention : last lines are footer meta data like headers. An empty line marks the start if the footers - var footers = {}; - var fIndex = data.length; - while (true) { - var oldFIndex = fIndex; - fIndex = data.lastIndexOf("\r\n", fIndex - 1); - var line = data.substring(fIndex + 2, oldFIndex); - if (line.length > 0) { - var footer = line.split(": "); - footers[footer[0]] = footer[1]; - } else { - break; - } - } - // Trims the the data variable to remove the footers extracted in the loop. - // Windows adds \r\n for every line break but the Git-Stderr-Length variable, - // counts it as only one character, throwing off the message length. - var trimmedData = data.substring(0, fIndex).replace(/(\r\n)/gm, "\n"); - var fIndex = trimmedData.length - - var messageLength = parseInt(footers["Git-Stderr-Length"]); - var messageStartIndex = fIndex-messageLength; - var message = trimmedData.substring(messageStartIndex, fIndex); - - var output = trimmedData.substring(0, messageStartIndex); - var rcode = parseInt(footers["Git-Return-Code"]); + var result = webui.parseGitResponse(data); + var rcode = result.rcode; + var output = result.output; + var message = result.message; if (rcode == 0) { if (callback) { @@ -197,13 +196,8 @@ webui.git_command = function(command, callback) { } }, error: function(data) { - var trimmedData = data.substring(0, fIndex).replace(/(\r\n)/gm, "\n"); - var fIndex = trimmedData.length - - var messageLength = parseInt(footers["Git-Stderr-Length"]); - var messageStartIndex = fIndex-messageLength; - var message = trimmedData.substring(messageStartIndex, fIndex); - webui.showError(message); + var trimmedData = data.replace(/(\r\n)/gm, "\n"); + webui.showError(trimmedData); }, }); } @@ -233,32 +227,10 @@ webui.git = function(cmd, arg1, arg2, arg3, arg4) { $.post("git", {command: cmd}, function(data, status, xhr) { if (xhr.status == 200) { - // Convention : last lines are footer meta data like headers. An empty line marks the start if the footers - var footers = {}; - var fIndex = data.length; - while (true) { - var oldFIndex = fIndex; - fIndex = data.lastIndexOf("\r\n", fIndex - 1); - var line = data.substring(fIndex + 2, oldFIndex); - if (line.length > 0) { - var footer = line.split(": "); - footers[footer[0]] = footer[1]; - } else { - break; - } - } - // Trims the the data variable to remove the footers extracted in the loop. - // Windows adds \r\n for every line break but the Git-Stderr-Length variable, - // counts it as only one character, throwing off the message length. - var trimmedData = data.substring(0, fIndex).replace(/(\r\n)/gm, "\n"); - var fIndex = trimmedData.length - - var messageLength = parseInt(footers["Git-Stderr-Length"]); - var messageStartIndex = fIndex-messageLength; - var message = trimmedData.substring(messageStartIndex, fIndex); - - var output = trimmedData.substring(0, messageStartIndex); - var rcode = parseInt(footers["Git-Return-Code"]); + var result = webui.parseGitResponse(data); + var rcode = result.rcode; + var output = result.output; + var message = result.message; if (rcode == 0) { if (callback) { @@ -295,15 +267,15 @@ webui.git = function(cmd, arg1, arg2, arg3, arg4) { } } } else { - if(errorCallback) { - errorCallback(message); + if (errorCallback) { + errorCallback(data); } else{ - webui.showError(message); + webui.showError(data); } } }, "text") .fail(function(xhr, status, error) { - webui.showError("Git webui server not running"); + webui.showError("An internal error occurred and has been logged."); }); }; @@ -1401,21 +1373,17 @@ webui.StashListView = function(stashView) { webui.git("stash list --format='%gd::%ch::%cL::%cN::%gs'", function(data) { var start = 0; var count = 0; - while (true) { - var end = data.indexOf("\n", start); - if (end != -1) { - var len = end - start; - } else { - break; + var lines = data.split("\n"); + for (var i = 0; i < lines.length; i++) { + if (lines[i].length == 0) { + continue; } - var entry = new Entry(self, data.substring(start, start+len)); + var entry = new Entry(self, lines[i]); if(start == 0){ entry.select(); } content.appendChild(entry.element); - - start = end + 1; - ++count; + count++; } if(count == 0){ var emptyStash = $('

You have no stashed changes.

'); @@ -1476,6 +1444,9 @@ webui.StashListView = function(stashView) { self.message = "" var pieces = data.split(/::|:\s/gm); + if (pieces.length < 5) { + return; + } self.stashIndex = pieces[0].substring(pieces[0].indexOf('{')+1, pieces[0].indexOf('}')); self.date = pieces[1]; self.authorEmail = pieces[2]; @@ -1583,6 +1554,10 @@ webui.DiffView = function(sideBySide, hunkSelectionAllowed, parent, stashedCommi } }; + self.clear = function() { + self.refresh(""); + } + self.reRun = function() { self.update(this.cmd, this.diffOpts, this.file, this.mode) } @@ -1911,6 +1886,7 @@ webui.DiffView = function(sideBySide, hunkSelectionAllowed, parent, stashedCommi webui.git("stash pop stash@{"+stashIndex+"}", function(output){ webui.showSuccess(output); parent.stashView.update(0); + self.clear(); }); } @@ -1922,6 +1898,7 @@ webui.DiffView = function(sideBySide, hunkSelectionAllowed, parent, stashedCommi webui.git("stash drop stash@{"+stashIndex+"}", function(output){ webui.showSuccess(output.substring(output.indexOf("Dropped"))); parent.stashView.update(0); + self.clear(); }); } @@ -2484,7 +2461,7 @@ webui.WorkspaceView = function(mainView) { self.update = function(mode) { self.newChangedFilesView.update(); if (self.newChangedFilesView.getSelectedItemsCount() == 0) { - self.diffView.update(undefined, undefined, undefined, mode); + self.diffView.clear(); } }; diff --git a/git-webui/src/share/git-webui/webui/js/git-webui.js b/git-webui/src/share/git-webui/webui/js/git-webui.js index 6825f72c..6a7972f5 100644 --- a/git-webui/src/share/git-webui/webui/js/git-webui.js +++ b/git-webui/src/share/git-webui/webui/js/git-webui.js @@ -125,6 +125,27 @@ webui.gitVersion = function() { }) } +webui.parseGitResponse = function(data) { + var matches = data.match(/([\S\s]*)Git-Stderr-Length:\s(\d*)[\r\n]{1,2}Git-Return-Code:\s(\d*)/); + if (!matches || (matches.length < 4)) { + return { + output: '', + message: '', + rcode: 1 + } + } + var messageData = matches[1].replace(/(\r\n)/gm, "\n"); + var errorLength = parseInt(matches[2],10); + var boundary = messageData.length - errorLength; + var stdout = messageData.substring(0, boundary) + var stderr = messageData.substring(boundary); + return { + output: stdout, + message: stderr, + rcode: matches[3] + }; +} + webui.git_command = function(command, callback) { $.ajax({ url: "git-command", @@ -134,32 +155,10 @@ webui.git_command = function(command, callback) { command: command }), success: function(data) { - // Convention : last lines are footer meta data like headers. An empty line marks the start if the footers - var footers = {}; - var fIndex = data.length; - while (true) { - var oldFIndex = fIndex; - fIndex = data.lastIndexOf("\r\n", fIndex - 1); - var line = data.substring(fIndex + 2, oldFIndex); - if (line.length > 0) { - var footer = line.split(": "); - footers[footer[0]] = footer[1]; - } else { - break; - } - } - // Trims the the data variable to remove the footers extracted in the loop. - // Windows adds \r\n for every line break but the Git-Stderr-Length variable, - // counts it as only one character, throwing off the message length. - var trimmedData = data.substring(0, fIndex).replace(/(\r\n)/gm, "\n"); - var fIndex = trimmedData.length - - var messageLength = parseInt(footers["Git-Stderr-Length"]); - var messageStartIndex = fIndex-messageLength; - var message = trimmedData.substring(messageStartIndex, fIndex); - - var output = trimmedData.substring(0, messageStartIndex); - var rcode = parseInt(footers["Git-Return-Code"]); + var result = webui.parseGitResponse(data); + var rcode = result.rcode; + var output = result.output; + var message = result.message; if (rcode == 0) { if (callback) { @@ -197,13 +196,8 @@ webui.git_command = function(command, callback) { } }, error: function(data) { - var trimmedData = data.substring(0, fIndex).replace(/(\r\n)/gm, "\n"); - var fIndex = trimmedData.length - - var messageLength = parseInt(footers["Git-Stderr-Length"]); - var messageStartIndex = fIndex-messageLength; - var message = trimmedData.substring(messageStartIndex, fIndex); - webui.showError(message); + var trimmedData = data.replace(/(\r\n)/gm, "\n"); + webui.showError(trimmedData); }, }); } @@ -233,32 +227,10 @@ webui.git = function(cmd, arg1, arg2, arg3, arg4) { $.post("git", {command: cmd}, function(data, status, xhr) { if (xhr.status == 200) { - // Convention : last lines are footer meta data like headers. An empty line marks the start if the footers - var footers = {}; - var fIndex = data.length; - while (true) { - var oldFIndex = fIndex; - fIndex = data.lastIndexOf("\r\n", fIndex - 1); - var line = data.substring(fIndex + 2, oldFIndex); - if (line.length > 0) { - var footer = line.split(": "); - footers[footer[0]] = footer[1]; - } else { - break; - } - } - // Trims the the data variable to remove the footers extracted in the loop. - // Windows adds \r\n for every line break but the Git-Stderr-Length variable, - // counts it as only one character, throwing off the message length. - var trimmedData = data.substring(0, fIndex).replace(/(\r\n)/gm, "\n"); - var fIndex = trimmedData.length - - var messageLength = parseInt(footers["Git-Stderr-Length"]); - var messageStartIndex = fIndex-messageLength; - var message = trimmedData.substring(messageStartIndex, fIndex); - - var output = trimmedData.substring(0, messageStartIndex); - var rcode = parseInt(footers["Git-Return-Code"]); + var result = webui.parseGitResponse(data); + var rcode = result.rcode; + var output = result.output; + var message = result.message; if (rcode == 0) { if (callback) { @@ -295,15 +267,15 @@ webui.git = function(cmd, arg1, arg2, arg3, arg4) { } } } else { - if(errorCallback) { - errorCallback(message); + if (errorCallback) { + errorCallback(data); } else{ - webui.showError(message); + webui.showError(data); } } }, "text") .fail(function(xhr, status, error) { - webui.showError("Git webui server not running"); + webui.showError("An internal error occurred and has been logged."); }); }; @@ -1401,21 +1373,17 @@ webui.StashListView = function(stashView) { webui.git("stash list --format='%gd::%ch::%cL::%cN::%gs'", function(data) { var start = 0; var count = 0; - while (true) { - var end = data.indexOf("\n", start); - if (end != -1) { - var len = end - start; - } else { - break; + var lines = data.split("\n"); + for (var i = 0; i < lines.length; i++) { + if (lines[i].length == 0) { + continue; } - var entry = new Entry(self, data.substring(start, start+len)); + var entry = new Entry(self, lines[i]); if(start == 0){ entry.select(); } content.appendChild(entry.element); - - start = end + 1; - ++count; + count++; } if(count == 0){ var emptyStash = $('

You have no stashed changes.

'); @@ -1476,6 +1444,9 @@ webui.StashListView = function(stashView) { self.message = "" var pieces = data.split(/::|:\s/gm); + if (pieces.length < 5) { + return; + } self.stashIndex = pieces[0].substring(pieces[0].indexOf('{')+1, pieces[0].indexOf('}')); self.date = pieces[1]; self.authorEmail = pieces[2]; @@ -1583,6 +1554,10 @@ webui.DiffView = function(sideBySide, hunkSelectionAllowed, parent, stashedCommi } }; + self.clear = function() { + self.refresh(""); + } + self.reRun = function() { self.update(this.cmd, this.diffOpts, this.file, this.mode) } @@ -1911,6 +1886,7 @@ webui.DiffView = function(sideBySide, hunkSelectionAllowed, parent, stashedCommi webui.git("stash pop stash@{"+stashIndex+"}", function(output){ webui.showSuccess(output); parent.stashView.update(0); + self.clear(); }); } @@ -1922,6 +1898,7 @@ webui.DiffView = function(sideBySide, hunkSelectionAllowed, parent, stashedCommi webui.git("stash drop stash@{"+stashIndex+"}", function(output){ webui.showSuccess(output.substring(output.indexOf("Dropped"))); parent.stashView.update(0); + self.clear(); }); } @@ -2484,7 +2461,7 @@ webui.WorkspaceView = function(mainView) { self.update = function(mode) { self.newChangedFilesView.update(); if (self.newChangedFilesView.getSelectedItemsCount() == 0) { - self.diffView.update(undefined, undefined, undefined, mode); + self.diffView.clear(); } };