mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add commit history view in sidebar
This commit is contained in:
parent
eae390acf5
commit
19c84eb694
@ -1509,10 +1509,10 @@ RED.diff = (function() {
|
||||
label.text("@@ -"+(diffLines[end-1].a.i+1)+" +"+(diffLines[end-1].b.i+1));
|
||||
}
|
||||
diffRow.click(function(evt) {
|
||||
console.log(start,end,diffLines.length);
|
||||
// console.log(start,end,diffLines.length);
|
||||
if (end - start > 20) {
|
||||
var startPos = $(this).offset();
|
||||
console.log(startPos);
|
||||
// console.log(startPos);
|
||||
if (start > 0) {
|
||||
for (var i=start;i<start+10;i++) {
|
||||
createDiffLine(diffLines[i]).addClass("unchanged").insertBefore($(this));
|
||||
@ -1646,8 +1646,63 @@ RED.diff = (function() {
|
||||
return string1 === string2 ? 0 : 1;
|
||||
}
|
||||
|
||||
function showUnifiedDiff(diff,title) {
|
||||
var hunks = parseUnifiedDiff(diff);
|
||||
function createUnifiedDiffTable(files) {
|
||||
var diffPanel = $('<div></div>');
|
||||
files.forEach(function(file) {
|
||||
var hunks = file.hunks;
|
||||
|
||||
var codeTable = $("<table>").appendTo(diffPanel);
|
||||
$('<colgroup><col width="50"><col width="50"><col width="100%"></colgroup>').appendTo(codeTable);
|
||||
var codeBody = $('<tbody>').appendTo(codeTable);
|
||||
|
||||
var diffRow = $('<tr class="node-text-diff-file-header">').appendTo(codeBody);
|
||||
var content = $('<td colspan="3"></td>').appendTo(diffRow);
|
||||
var label = $('<span></span>').text(file.file).appendTo(content);
|
||||
|
||||
for (var i=0;i<hunks.length;i++) {
|
||||
var diffRow = $('<tr class="node-text-diff-header">').appendTo(codeBody);
|
||||
var content = $('<td colspan="3"></td>').appendTo(diffRow);
|
||||
var label = $('<span></span>').text(hunks[i].header).appendTo(content);
|
||||
|
||||
var localLine = hunks[i].localStartLine;
|
||||
var remoteLine = hunks[i].remoteStartLine;
|
||||
|
||||
|
||||
for (var j=0;j<hunks[i].lines.length;j++) {
|
||||
var lineText = hunks[i].lines[j];
|
||||
if (lineText[0] === '\\' || lineText === "") {
|
||||
// Comment line - bail out of this hunk
|
||||
break;
|
||||
}
|
||||
diffRow = $('<tr>').appendTo(codeBody);
|
||||
var localLineNo = $('<td class="lineno">').appendTo(diffRow);
|
||||
var remoteLineNo = $('<td class="lineno">').appendTo(diffRow);
|
||||
var line = $('<td class="linetext">').appendTo(diffRow);
|
||||
$('<span class="prefix">').text(lineText[0]).appendTo(line);
|
||||
$('<span>').text(lineText.substring(1)).appendTo(line);
|
||||
if (lineText[0] === '+') {
|
||||
line.addClass("added");
|
||||
remoteLineNo.text(remoteLine++);
|
||||
} else if (lineText[0] === '-') {
|
||||
line.addClass("removed");
|
||||
localLineNo.text(localLine++);
|
||||
} else {
|
||||
line.addClass("unchanged");
|
||||
if (localLine > 0) {
|
||||
localLineNo.text(localLine++);
|
||||
}
|
||||
if (remoteLine > 0) {
|
||||
remoteLineNo.text(remoteLine++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return diffPanel;
|
||||
}
|
||||
|
||||
function showCommitDiff(diff,title) {
|
||||
var commit = parseCommitDiff(diff);
|
||||
var trayOptions = {
|
||||
title: title||"Compare Changes", //TODO: nls
|
||||
width: Infinity,
|
||||
@ -1671,83 +1726,125 @@ RED.diff = (function() {
|
||||
$('<colgroup><col width="50"><col width="50"><col width="100%"></colgroup>').appendTo(codeTable);
|
||||
var codeBody = $('<tbody>').appendTo(codeTable);
|
||||
|
||||
for (var i=0;i<hunks.length;i++) {
|
||||
|
||||
var diffRow = $('<tr class="node-text-diff-header">').appendTo(codeBody);
|
||||
var diffRow = $('<tr class="node-text-diff-file-header">').appendTo(codeBody);
|
||||
var content = $('<td colspan="3"></td>').appendTo(diffRow);
|
||||
var label = $('<span></span>').text(hunks[i].header).appendTo(content);
|
||||
|
||||
var localLine = hunks[i].localStartLine;
|
||||
var remoteLine = hunks[i].remoteStartLine;
|
||||
var label = $('<pre></pre>').text(commit.preamble).appendTo(content);
|
||||
|
||||
|
||||
createUnifiedDiffTable(commit.files).appendTo(diffPanel);
|
||||
|
||||
|
||||
for (var j=0;j<hunks[i].lines.length;j++) {
|
||||
var lineText = hunks[i].lines[j];
|
||||
diffRow = $('<tr>').appendTo(codeBody);
|
||||
var localLineNo = $('<td class="lineno">').appendTo(diffRow);
|
||||
var remoteLineNo = $('<td class="lineno">').appendTo(diffRow);
|
||||
var line = $('<td class="linetext">').appendTo(diffRow);
|
||||
$('<span class="prefix">').text(lineText[0]).appendTo(line);
|
||||
$('<span>').text(lineText.substring(1)).appendTo(line);
|
||||
if (lineText[0] === '+') {
|
||||
line.addClass("added");
|
||||
remoteLineNo.text(remoteLine++);
|
||||
} else if (lineText[0] === '-') {
|
||||
line.addClass("removed");
|
||||
localLineNo.text(localLine++);
|
||||
} else {
|
||||
line.addClass("unchanged");
|
||||
localLineNo.text(localLine++);
|
||||
remoteLineNo.text(remoteLine++);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
diffVisible = false;
|
||||
|
||||
},
|
||||
show: function() {
|
||||
|
||||
}
|
||||
}
|
||||
RED.tray.show(trayOptions);
|
||||
}
|
||||
function showUnifiedDiff(diff,title) {
|
||||
var files = parseUnifiedDiff(diff);
|
||||
var trayOptions = {
|
||||
title: title||"Compare Changes", //TODO: nls
|
||||
width: Infinity,
|
||||
overlay: true,
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("common.label.done"),
|
||||
click: function() {
|
||||
RED.tray.close();
|
||||
}
|
||||
}
|
||||
],
|
||||
resize: function(dimensions) {
|
||||
// trayWidth = dimensions.width;
|
||||
},
|
||||
open: function(tray) {
|
||||
var trayBody = tray.find('.editor-tray-body');
|
||||
var diffPanel = $('<div class="node-text-diff"></div>').appendTo(trayBody);
|
||||
createUnifiedDiffTable(files).appendTo(diffPanel);
|
||||
|
||||
|
||||
},
|
||||
close: function() {
|
||||
diffVisible = false;
|
||||
},
|
||||
show: function() {
|
||||
|
||||
}
|
||||
}
|
||||
RED.tray.show(trayOptions);
|
||||
}
|
||||
|
||||
function parseCommitDiff(diff) {
|
||||
var result = {
|
||||
};
|
||||
var lines = diff.split("\n");
|
||||
for (var i=0;i<lines.length;i++) {
|
||||
if (/^diff /.test(lines[i])) {
|
||||
result.files = parseUnifiedDiff(lines.slice(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.preamble = lines.slice(0,i).join("\n");
|
||||
return result;
|
||||
}
|
||||
function parseUnifiedDiff(diff) {
|
||||
var lines = diff.split("\n");
|
||||
var hunks = [];
|
||||
var inHunk = false;
|
||||
var currentHunk;
|
||||
var lines;
|
||||
if (Array.isArray(diff)) {
|
||||
lines = diff;
|
||||
} else {
|
||||
lines = diff.split("\n");
|
||||
}
|
||||
var fileHeader = /^\+\+\+ b\/(.*)\t?/;
|
||||
var hunkHeader = /^@@ -((\d+)(,(\d+))?) \+((\d+)(,(\d+))?) @@ ?(.*)$/;
|
||||
var comment = /^\\/;
|
||||
var localChange = /^-/;
|
||||
var remoteChange = /^\+/;
|
||||
|
||||
var files = [];
|
||||
var currentFile;
|
||||
var hunks = [];
|
||||
var currentHunk;
|
||||
for (var i=0;i<lines.length;i++) {
|
||||
var hunkLine = hunkHeader.exec(lines[i]);
|
||||
var line = lines[i];
|
||||
if (/^diff/.test(line)) {
|
||||
if (currentHunk) {
|
||||
currentFile.hunks.push(currentHunk);
|
||||
files.push(currentFile);
|
||||
}
|
||||
currentHunk = null;
|
||||
currentFile = {
|
||||
file: null,
|
||||
hunks: []
|
||||
}
|
||||
} else {
|
||||
var fileLine = fileHeader.exec(line);
|
||||
if (fileLine) {
|
||||
currentFile.file = fileLine[1];
|
||||
} else {
|
||||
var hunkLine = hunkHeader.exec(line);
|
||||
if (hunkLine) {
|
||||
if (inHunk) {
|
||||
hunks.push(currentHunk);
|
||||
if (currentHunk) {
|
||||
currentFile.hunks.push(currentHunk);
|
||||
}
|
||||
currentHunk = {
|
||||
header: lines[i],
|
||||
header: line,
|
||||
localStartLine: hunkLine[2],
|
||||
localLength: hunkLine[4]||1,
|
||||
remoteStartLine: hunkLine[6],
|
||||
remoteLength: hunkLine[8]||1,
|
||||
lines: []
|
||||
}
|
||||
inHunk = true;
|
||||
} else if (inHunk) {
|
||||
currentHunk.lines.push(lines[i]);
|
||||
} else if (currentHunk) {
|
||||
currentHunk.lines.push(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentHunk) {
|
||||
hunks.push(currentHunk);
|
||||
currentFile.hunks.push(currentHunk);
|
||||
files.push(currentFile);
|
||||
}
|
||||
return hunks;
|
||||
return files;
|
||||
}
|
||||
|
||||
return {
|
||||
@ -1755,6 +1852,7 @@ RED.diff = (function() {
|
||||
getRemoteDiff: getRemoteDiff,
|
||||
showRemoteDiff: showRemoteDiff,
|
||||
showUnifiedDiff: showUnifiedDiff,
|
||||
showCommitDiff: showCommitDiff,
|
||||
mergeDiff: mergeDiff
|
||||
}
|
||||
})();
|
||||
|
@ -29,6 +29,9 @@ RED.sidebar.versionControl = (function() {
|
||||
var bulkChangeSpinner;
|
||||
var commitButton;
|
||||
|
||||
var localCommitList;
|
||||
|
||||
|
||||
// TODO: DRY projectSummary.js
|
||||
function addSpinnerOverlay(container) {
|
||||
var spinner = $('<div class="projects-dialog-spinner"><img src="red/images/spin.svg"/></div>').appendTo(container);
|
||||
@ -315,6 +318,37 @@ RED.sidebar.versionControl = (function() {
|
||||
collapsible: true
|
||||
});
|
||||
|
||||
var bg = $('<div style="float: right"></div>').appendTo(localHistory.header);
|
||||
$('<button class="editor-button editor-button-small"><i class="fa fa-refresh"></i></button>')
|
||||
.appendTo(bg)
|
||||
.click(function(evt) {
|
||||
evt.preventDefault();
|
||||
refreshLocalCommits();
|
||||
})
|
||||
|
||||
localCommitList = $("<ol>",{style:"position: absolute; top: 0px; bottom: 0; right:0; left:0;"}).appendTo(localHistory.content);
|
||||
localCommitList.editableList({
|
||||
addButton: false,
|
||||
scrollOnAdd: false,
|
||||
addItem: function(row,index,entry) {
|
||||
row.addClass('sidebar-version-control-commit-entry');
|
||||
row.click(function(e) {
|
||||
var activeProject = RED.projects.getActiveProject();
|
||||
if (activeProject) {
|
||||
$.getJSON("/projects/"+activeProject.name+"/commits/"+entry.sha,function(result) {
|
||||
RED.diff.showCommitDiff(result.commit);
|
||||
});
|
||||
}
|
||||
});
|
||||
var container = $('<div>').appendTo(row);
|
||||
$('<div class="sidebar-version-control-commit-sha">').text(entry.sha.substring(0,7)).appendTo(container);
|
||||
$('<div class="sidebar-version-control-commit-subject">').text(entry.subject).appendTo(container);
|
||||
$('<div class="sidebar-version-control-commit-user">').text(entry.author).appendTo(container);
|
||||
$('<div class="sidebar-version-control-commit-date">').text(humanizeSinceDate(parseInt(entry.date))).appendTo(container);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var remoteHistory = sections.add({
|
||||
title: "Remote History",
|
||||
collapsible: true
|
||||
@ -335,6 +369,26 @@ RED.sidebar.versionControl = (function() {
|
||||
|
||||
}
|
||||
|
||||
function humanizeSinceDate(date) {
|
||||
var delta = (Date.now()/1000) - date;
|
||||
|
||||
var daysDelta = Math.floor(delta / (60*60*24));
|
||||
if (daysDelta > 30) {
|
||||
return (new Date(date*1000)).toLocaleDateString();
|
||||
} else if (daysDelta > 0) {
|
||||
return daysDelta+" day"+(daysDelta>1?"s":"")+" ago";
|
||||
}
|
||||
var hoursDelta = Math.floor(delta / (60*60));
|
||||
if (hoursDelta > 0) {
|
||||
return hoursDelta+" hour"+(hoursDelta>1?"s":"")+" ago";
|
||||
}
|
||||
var minutesDelta = Math.floor(delta / 60);
|
||||
if (minutesDelta > 0) {
|
||||
return minutesDelta+" minute"+(minutesDelta>1?"s":"")+" ago";
|
||||
}
|
||||
return "Seconds ago";
|
||||
}
|
||||
|
||||
function updateBulk(files,unstaged) {
|
||||
var activeProject = RED.projects.getActiveProject();
|
||||
if (unstaged) {
|
||||
@ -369,6 +423,21 @@ RED.sidebar.versionControl = (function() {
|
||||
|
||||
var emptyStagedItem = { label:"None" };
|
||||
|
||||
function refreshLocalCommits() {
|
||||
localCommitList.editableList('empty');
|
||||
var spinner = addSpinnerOverlay(localCommitList);
|
||||
var activeProject = RED.projects.getActiveProject();
|
||||
if (activeProject) {
|
||||
$.getJSON("/projects/"+activeProject.name+"/commits",function(result) {
|
||||
result.commits.forEach(function(c) {
|
||||
localCommitList.editableList('addItem',c);
|
||||
})
|
||||
spinner.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function refreshFiles(result) {
|
||||
if (bulkChangeSpinner) {
|
||||
bulkChangeSpinner.remove();
|
||||
@ -464,6 +533,7 @@ RED.sidebar.versionControl = (function() {
|
||||
unstagedChangesList.editableList('empty');
|
||||
stagedChangesList.editableList('empty');
|
||||
}
|
||||
|
||||
refreshInProgress = true;
|
||||
|
||||
var activeProject = RED.projects.getActiveProject();
|
||||
|
@ -601,12 +601,21 @@
|
||||
tr.end-block {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
tr.node-text-diff-file-header td {
|
||||
background: #f3f3f3;
|
||||
padding: 5px 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
tr.node-text-diff-header td {
|
||||
padding: 5px 10px;
|
||||
text-align: left;
|
||||
color: #999;
|
||||
color: #666;
|
||||
background: #ffd;
|
||||
height: 30px;
|
||||
vertical-align: middle;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
tr.node-text-diff-expand td {
|
||||
cursor: pointer;
|
||||
|
@ -247,16 +247,7 @@
|
||||
overflow-y: auto;
|
||||
padding: 8px 20px 20px;
|
||||
}
|
||||
|
||||
.sidebar-version-control-change-container {
|
||||
position: relative;
|
||||
height: 50%;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid $secondary-border-color;
|
||||
transition: height 0.2s ease-in-out;
|
||||
&:first-child {
|
||||
// border-bottom: 1px solid $primary-border-color;
|
||||
}
|
||||
.sidebar-version-control {
|
||||
.red-ui-editableList-container {
|
||||
background: #f9f9f9;
|
||||
padding: 0;
|
||||
@ -270,6 +261,17 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
.sidebar-version-control-change-container {
|
||||
position: relative;
|
||||
height: 50%;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid $secondary-border-color;
|
||||
transition: height 0.2s ease-in-out;
|
||||
&:first-child {
|
||||
// border-bottom: 1px solid $primary-border-color;
|
||||
}
|
||||
|
||||
}
|
||||
.sidebar-version-control-change-commit-box {
|
||||
position:absolute;
|
||||
bottom: 0;
|
||||
@ -323,6 +325,38 @@
|
||||
background: #fefefe;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-version-control-commit-entry {
|
||||
min-height: 20px;
|
||||
padding: 5px 10px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
.sidebar-version-control-commit-sha {
|
||||
float: right;
|
||||
font-family: monospace;
|
||||
color: #c38888;
|
||||
display: inline-block;
|
||||
font-size: 0.85em;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.sidebar-version-control-commit-subject {
|
||||
color: #666;
|
||||
}
|
||||
.sidebar-version-control-commit-date {
|
||||
color: #999;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.sidebar-version-control-commit-user {
|
||||
float: right;
|
||||
color: #999;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.sidebar-version-control-change-header {
|
||||
color: #666;
|
||||
background: #f6f6f6;
|
||||
|
@ -203,6 +203,31 @@ module.exports = {
|
||||
})
|
||||
});
|
||||
|
||||
app.get("/:id/commits", function(req, res) {
|
||||
var projectName = req.params.id;
|
||||
var options = {};
|
||||
runtime.storage.projects.getCommits(projectName,options).then(function(data) {
|
||||
res.json(data);
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.log(err.stack);
|
||||
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||
})
|
||||
});
|
||||
|
||||
app.get("/:id/commits/:sha", function(req, res) {
|
||||
var projectName = req.params.id;
|
||||
var sha = req.params.sha;
|
||||
|
||||
runtime.storage.projects.getCommit(projectName,sha).then(function(data) {
|
||||
res.json({commit:data});
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.log(err.stack);
|
||||
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||
})
|
||||
});
|
||||
|
||||
app.get(new RegExp("/([^\/]+)\/files\/(.*)"), function(req,res) {
|
||||
// Get project file
|
||||
});
|
||||
|
@ -173,6 +173,28 @@ function getFiles(localRepo) {
|
||||
})
|
||||
}
|
||||
|
||||
function parseLog(log) {
|
||||
var lines = log.split("\n");
|
||||
var currentCommit = null;
|
||||
var commits = [];
|
||||
lines.forEach(function(l) {
|
||||
if (/^sha: /.test(l)) {
|
||||
if (currentCommit) {
|
||||
commits.push(currentCommit);
|
||||
}
|
||||
currentCommit = {}
|
||||
}
|
||||
var m = /^(.*): (.*)$/.exec(l);
|
||||
if (m) {
|
||||
currentCommit[m[1]] = m[2];
|
||||
}
|
||||
});
|
||||
if (currentCommit) {
|
||||
commits.push(currentCommit);
|
||||
}
|
||||
return {commits: commits};
|
||||
}
|
||||
|
||||
var gitCommand = "git";
|
||||
module.exports = {
|
||||
initRepo: function(cwd) {
|
||||
@ -219,5 +241,13 @@ module.exports = {
|
||||
}
|
||||
args.push(file);
|
||||
return runCommand(gitCommand,args,cwd);
|
||||
},
|
||||
getCommits: function(cwd,options) {
|
||||
var args = ["log", "--format=sha: %H%nauthor: %an%ndate: %ct%nsubject: %s","-n 10"];
|
||||
return runCommand(gitCommand,args,cwd).then(parseLog);
|
||||
},
|
||||
getCommit: function(cwd,sha) {
|
||||
var args = ["show",sha];
|
||||
return runCommand(gitCommand,args,cwd);
|
||||
}
|
||||
}
|
||||
|
@ -355,6 +355,15 @@ function getFileDiff(project,file,type) {
|
||||
var projectPath = fspath.join(projectsDir,project);
|
||||
return gitTools.getFileDiff(projectPath,file,type);
|
||||
}
|
||||
function getCommits(project,options) {
|
||||
var projectPath = fspath.join(projectsDir,project);
|
||||
return gitTools.getCommits(projectPath,options);
|
||||
}
|
||||
function getCommit(project,sha) {
|
||||
var projectPath = fspath.join(projectsDir,project);
|
||||
return gitTools.getCommit(projectPath,sha);
|
||||
}
|
||||
|
||||
function getFile(project,path) {
|
||||
|
||||
}
|
||||
@ -512,6 +521,8 @@ module.exports = {
|
||||
unstageFile: unstageFile,
|
||||
commit: commit,
|
||||
getFileDiff: getFileDiff,
|
||||
getCommits: getCommits,
|
||||
getCommit: getCommit,
|
||||
|
||||
getFlows: getFlows,
|
||||
saveFlows: saveFlows,
|
||||
|
Loading…
Reference in New Issue
Block a user