diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f1b755e..3550acfa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Don't automatically stage files added to source control (#303)
- Performance improvements (#269, #315)
- Checkout of branches whose names contain slashes via Web UI no longer fails (#295)
+- Display other developer's username in Web UI's Workspace when hovering over the name of a file they changed (#304)
## [2.3.0] - 2023-12-06
diff --git a/cls/SourceControl/Git/Change.cls b/cls/SourceControl/Git/Change.cls
index 0caf80d3..e6b1e2e6 100644
--- a/cls/SourceControl/Git/Change.cls
+++ b/cls/SourceControl/Git/Change.cls
@@ -44,7 +44,9 @@ ClassMethod RemoveUncommitted(FileList, Display = 1, Revert = 0, ActiveCommit =
set changeSourceClass=##class(%Studio.SourceControl.Interface).SourceControlClassGet()
}
if ('$get(^SYS("SourceControl","ChangeConfig","KeepHistory")))||(Revert) {
- set sc=..%DeleteId(obj.%Id())
+ if (obj.ChangedBy = $username) {
+ set sc=..%DeleteId(obj.%Id())
+ }
} else {
if $get(CommitCCR)'="" set obj.CCR=CommitCCR
set obj.P4Issued=$zdatetime($h,3)
@@ -77,6 +79,29 @@ ClassMethod IsUncommitted(Filename, ByRef ID) As %Boolean
}
}
+ClassMethod GetOtherDeveloperChanges() As %Boolean
+{
+ set numEntries = 0
+ set fileToOtherDevelopers = {}
+ set query = "Select ItemFile, ChangedBy FROM SourceControl_Git.Change WHERE CHARINDEX('SourceControl.Git', Name)>0 AND Committed = '0' AND ChangedBy <> ?"
+ set statement = ##class(%SQL.Statement).%New()
+ set status = statement.%Prepare(query)
+ $$$ThrowOnError(status)
+ set rset = statement.%Execute($username)
+ if (rset.%SQLCODE < 0) {
+ throw ##class(%Exception.SQL).CreateFromSQLCODE(rset.%SQLCODE,rset.%Message)
+ }
+ while rset.%Next(.sc) {
+ $$$ThrowOnError(sc)
+ set filePath = "cls\"_$EXTRACT(rset.ItemFile, $FIND(rset.ItemFile,"cls\"),$LENGTH(rset.ItemFile))
+ set otherDevelopers = fileToOtherDevelopers.%Get(filePath, [])
+ do otherDevelopers.%Push(rset.ChangedBy)
+ do fileToOtherDevelopers.%Set(filePath, otherDevelopers)
+ }
+ $$$ThrowOnError(sc)
+ return fileToOtherDevelopers
+}
+
/// Goes through Uncommitted queue and removes any items of action 'edit' or 'add' which are ReadOnly or non-existent on the filesystem
ClassMethod RefreshUncommitted(Display = 0, IncludeRevert = 0, Output gitFiles, Force As %Boolean = 0) As %Status
{
@@ -192,4 +217,3 @@ Storage Default
}
}
-
diff --git a/cls/SourceControl/Git/WebUIDriver.cls b/cls/SourceControl/Git/WebUIDriver.cls
index acb05ff6..5a70b400 100644
--- a/cls/SourceControl/Git/WebUIDriver.cls
+++ b/cls/SourceControl/Git/WebUIDriver.cls
@@ -183,24 +183,26 @@ ClassMethod Uncommitted() As %SystemBase
set output = ""
set key = ""
- set array = []
- set key = ""
+ set editedByCurrentUser = []
+ set fileToOtherDevelopers = {}
for {
set key = $order(files(key),1,fileData)
quit:key=""
// Check that current user has files(key) uncommitted and only %Push if they do
set filename = ##class(Utils).FullExternalName(key)
if (($ISVALIDNUM(key)) && (files(key) '= "")){
- do array.%Push($listget(fileData,2))
+ do editedByCurrentUser.%Push($listget(fileData,2))
}
else{
set sc=##class(SourceControl.Git.Change).GetUncommitted(filename,.tAction,.tInternalName,.UncommittedUser,.tSource,.UncommittedLastUpdated)
if ($$$ISOK(sc)) && ($data(tAction)&&(UncommittedUser=$username)) {
- do array.%Push($listget(fileData,2))
+ do editedByCurrentUser.%Push($listget(fileData,2))
}
}
}
- quit array
+ do fileToOtherDevelopers.%Set("current user's changes", editedByCurrentUser)
+ do fileToOtherDevelopers.%Set("other users' changes", ##class(SourceControl.Git.Change).GetOtherDeveloperChanges())
+ quit fileToOtherDevelopers
}
ClassMethod GetURLPrefix(%request As %CSP.Request, URL As %String) As %String
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 3dbc6ad8..1010fbaa 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
@@ -414,7 +414,7 @@ webui.SideBarView = function(mainView, noEventHandlers) {
var flag = 0;
webui.git("status -u --porcelain", function(data) {
$.get("api/uncommitted", function (uncommitted) {
- var uncommittedItems = JSON.parse(uncommitted);
+ var uncommittedItems = JSON.parse(uncommitted)["current user's changes"];
var col = 1
webui.splitLines(data).forEach(function(line) {
var status = line[col];
@@ -488,7 +488,7 @@ webui.SideBarView = function(mainView, noEventHandlers) {
var flag = 0;
webui.git("status -u --porcelain", function(data) {
$.get("api/uncommitted", function (uncommitted) {
- var uncommittedItems = JSON.parse(uncommitted);
+ var uncommittedItems = JSON.parse(uncommitted)["current user's changes"];
var col = 1
webui.splitLines(data).forEach(function(line) {
var status = line[col];
@@ -2143,13 +2143,30 @@ webui.ChangedFilesView = function(workspaceView, type, label) {
var col = type == "working-copy" ? 1 : 0;
webui.git("status -u --porcelain", function(data) {
$.get("api/uncommitted", function (uncommitted) {
- var uncommittedItems = JSON.parse(uncommitted);
+ var uncommittedItems = JSON.parse(uncommitted)["current user's changes"];
+ var otherDeveloperUncommittedItems = JSON.parse(uncommitted)["other users' changes"];
self.filesCount = 0;
var filePaths = [];
+ function addItemToFileList(fileList, otherDeveloperUsername, model) {
+ var isForCurrentUser = otherDeveloperUsername === ""? true : false;
+ var cssClass = isForCurrentUser ? 'list-group-item available' : 'list-group-item unavailable';
+ if(isForCurrentUser){
+ var item = $('').prependTo(fileList)[0];
+ }
+ else{
+ var item = $(''+
+ webui.peopleIcon).appendTo(fileList)[0];
+ }
+ item.model = model;
+ item.appendChild(document.createTextNode(model));
+ if (isForCurrentUser) {
+ $(item).click(self.select);
+ $(item).dblclick(self.process);
+ }
+ }
webui.splitLines(data).forEach(function(line) {
var indexStatus = line[0];
var workingTreeStatus = line[1];
- var status = line[col];
line = line.substring(3);
var splitted = line.split(" -> ");
var model;
@@ -2159,10 +2176,15 @@ webui.ChangedFilesView = function(workspaceView, type, label) {
model = line;
}
filePaths.push(model);
+
if (workingTreeStatus === "D") {
localStorage.removeItem(model);
}
- if (col == 0 && indexStatus != " " && indexStatus != "?" && localStorage.getItem(model) !== null || col == 1 && workingTreeStatus != " " && workingTreeStatus != "D" && localStorage.getItem(model) === null) {
+
+ var isNotStaged= workingTreeStatus != "D" && workingTreeStatus != " " && localStorage.getItem(model) === null;
+ var addUnstagedFile = col == 1 && isNotStaged;
+ var addStagedFile = col == 0 && indexStatus != " " && indexStatus != "?" && localStorage.getItem(model) !== null;
+ if (addUnstagedFile || addStagedFile) {
++self.filesCount;
var isForCurrentUser;
if(model.indexOf(" ") > -1){
@@ -2170,29 +2192,31 @@ webui.ChangedFilesView = function(workspaceView, type, label) {
} else {
isForCurrentUser = (uncommittedItems.indexOf(model) > -1);
}
- var cssClass = isForCurrentUser ? 'list-group-item available' : 'list-group-item unavailable';
-
- if(isForCurrentUser){
- var item = $('').prependTo(fileList)[0];
- }
- else{
- var item = $(''+
- webui.peopleIcon).appendTo(fileList)[0];
- }
- item.model = model;
- item.status = status;
- item.appendChild(document.createTextNode(line));
- $(item).click(self.select);
+
if (isForCurrentUser) {
- $(item).dblclick(self.process);
+ addItemToFileList(fileList, "", model);
}
}
});
+
+ for (const otherDeveloperItem of Object.keys(otherDeveloperUncommittedItems)) {
+ for (const otherDeveloperUsername of otherDeveloperUncommittedItems[otherDeveloperItem]) {
+ if (col==1) {
+ addItemToFileList(fileList, otherDeveloperUsername, otherDeveloperItem);
+ }
+ }
+ }
+
+ $(function () {
+ $('[data-toggle="tooltip"]').tooltip()
+ });
+
Object.keys(localStorage).filter(function (key) {
return !filePaths.includes(key);
}).map(function (key) {
localStorage.removeItem(key);
});
+
if (selectedIndex !== null && selectedIndex >= fileList.childElementCount) {
selectedIndex = fileList.childElementCount - 1;
if (selectedIndex == -1) {
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 bfe80019..1010fbaa 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
@@ -414,7 +414,7 @@ webui.SideBarView = function(mainView, noEventHandlers) {
var flag = 0;
webui.git("status -u --porcelain", function(data) {
$.get("api/uncommitted", function (uncommitted) {
- var uncommittedItems = JSON.parse(uncommitted);
+ var uncommittedItems = JSON.parse(uncommitted)["current user's changes"];
var col = 1
webui.splitLines(data).forEach(function(line) {
var status = line[col];
@@ -488,7 +488,7 @@ webui.SideBarView = function(mainView, noEventHandlers) {
var flag = 0;
webui.git("status -u --porcelain", function(data) {
$.get("api/uncommitted", function (uncommitted) {
- var uncommittedItems = JSON.parse(uncommitted);
+ var uncommittedItems = JSON.parse(uncommitted)["current user's changes"];
var col = 1
webui.splitLines(data).forEach(function(line) {
var status = line[col];
@@ -2143,13 +2143,30 @@ webui.ChangedFilesView = function(workspaceView, type, label) {
var col = type == "working-copy" ? 1 : 0;
webui.git("status -u --porcelain", function(data) {
$.get("api/uncommitted", function (uncommitted) {
- var uncommittedItems = JSON.parse(uncommitted);
+ var uncommittedItems = JSON.parse(uncommitted)["current user's changes"];
+ var otherDeveloperUncommittedItems = JSON.parse(uncommitted)["other users' changes"];
self.filesCount = 0;
var filePaths = [];
+ function addItemToFileList(fileList, otherDeveloperUsername, model) {
+ var isForCurrentUser = otherDeveloperUsername === ""? true : false;
+ var cssClass = isForCurrentUser ? 'list-group-item available' : 'list-group-item unavailable';
+ if(isForCurrentUser){
+ var item = $('').prependTo(fileList)[0];
+ }
+ else{
+ var item = $(''+
+ webui.peopleIcon).appendTo(fileList)[0];
+ }
+ item.model = model;
+ item.appendChild(document.createTextNode(model));
+ if (isForCurrentUser) {
+ $(item).click(self.select);
+ $(item).dblclick(self.process);
+ }
+ }
webui.splitLines(data).forEach(function(line) {
var indexStatus = line[0];
var workingTreeStatus = line[1];
- var status = line[col];
line = line.substring(3);
var splitted = line.split(" -> ");
var model;
@@ -2164,7 +2181,10 @@ webui.ChangedFilesView = function(workspaceView, type, label) {
localStorage.removeItem(model);
}
- if (col == 0 && indexStatus != " " && indexStatus != "?" && localStorage.getItem(model) !== null || col == 1 && workingTreeStatus != " " && workingTreeStatus != "D" && localStorage.getItem(model) === null) {
+ var isNotStaged= workingTreeStatus != "D" && workingTreeStatus != " " && localStorage.getItem(model) === null;
+ var addUnstagedFile = col == 1 && isNotStaged;
+ var addStagedFile = col == 0 && indexStatus != " " && indexStatus != "?" && localStorage.getItem(model) !== null;
+ if (addUnstagedFile || addStagedFile) {
++self.filesCount;
var isForCurrentUser;
if(model.indexOf(" ") > -1){
@@ -2172,25 +2192,25 @@ webui.ChangedFilesView = function(workspaceView, type, label) {
} else {
isForCurrentUser = (uncommittedItems.indexOf(model) > -1);
}
- var cssClass = isForCurrentUser ? 'list-group-item available' : 'list-group-item unavailable';
-
- if(isForCurrentUser){
- var item = $('').prependTo(fileList)[0];
- }
- else{
- var item = $(''+
- webui.peopleIcon).appendTo(fileList)[0];
- }
- item.model = model;
- item.status = status;
- item.appendChild(document.createTextNode(line));
- $(item).click(self.select);
+
if (isForCurrentUser) {
- $(item).dblclick(self.process);
+ addItemToFileList(fileList, "", model);
}
}
});
+ for (const otherDeveloperItem of Object.keys(otherDeveloperUncommittedItems)) {
+ for (const otherDeveloperUsername of otherDeveloperUncommittedItems[otherDeveloperItem]) {
+ if (col==1) {
+ addItemToFileList(fileList, otherDeveloperUsername, otherDeveloperItem);
+ }
+ }
+ }
+
+ $(function () {
+ $('[data-toggle="tooltip"]').tooltip()
+ });
+
Object.keys(localStorage).filter(function (key) {
return !filePaths.includes(key);
}).map(function (key) {