Skip to content

Commit bb59cd5

Browse files
committed
Allow unstaged files to be reverted
1 parent 604e306 commit bb59cd5

8 files changed

Lines changed: 181 additions & 82 deletions

File tree

editor/js/main.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@
7272
"abort-merge":"Git merge aborted",
7373
"loaded":"Project '"+msg.project+"' loaded",
7474
"updated":"Project '"+msg.project+"' updated",
75-
"pull":"Project '"+msg.project+"' reloaded"
76-
}[msg.action]
75+
"pull":"Project '"+msg.project+"' reloaded",
76+
"revert": "Project '"+msg.project+"' reloaded"
77+
}[msg.action];
7778
RED.notify(message);
7879
RED.sidebar.info.refresh()
7980
});

editor/js/ui/projectSettings.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ RED.projects.settings = (function() {
134134
},
135135
200: function(data) {
136136
done(null,data);
137+
RED.sidebar.versionControl.refresh(true);
137138
},
138139
400: {
139140
'unexpected_error': function(error) {
@@ -197,6 +198,7 @@ RED.projects.settings = (function() {
197198
done(error,null);
198199
},
199200
200: function(data) {
201+
RED.sidebar.versionControl.refresh(true);
200202
done(null,data);
201203
},
202204
400: {
@@ -326,6 +328,7 @@ RED.projects.settings = (function() {
326328
done(error,null);
327329
},
328330
200: function(data) {
331+
RED.sidebar.versionControl.refresh(true);
329332
done(null,data);
330333
},
331334
400: {
@@ -900,6 +903,7 @@ RED.projects.settings = (function() {
900903
},
901904
200: function(data) {
902905
activeProject = data;
906+
RED.sidebar.versionControl.refresh(true);
903907
updateForm();
904908
done();
905909
},

editor/js/ui/tab-versionControl.js

Lines changed: 130 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,82 @@ RED.sidebar.versionControl = (function() {
3939

4040
var isMerging;
4141

42-
// TODO: DRY projectSummary.js
42+
function viewFileDiff(entry,state) {
43+
var activeProject = RED.projects.getActiveProject();
44+
var diffTarget = (state === 'staged')?"index":"tree";
45+
utils.sendRequest({
46+
url: "projects/"+activeProject.name+"/diff/"+diffTarget+"/"+encodeURIComponent(entry.file),
47+
type: "GET",
48+
responses: {
49+
0: function(error) {
50+
console.log(error);
51+
// done(error,null);
52+
},
53+
200: function(data) {
54+
if (mergeConflictNotification) {
55+
mergeConflictNotification.close();
56+
mergeConflictNotification = null;
57+
}
58+
var title;
59+
if (state === 'unstaged') {
60+
title = 'Unstaged changes : '+entry.file
61+
} else if (state === 'staged') {
62+
title = 'Staged changes : '+entry.file
63+
} else {
64+
title = 'Resolve conflicts : '+entry.file
65+
}
66+
var options = {
67+
diff: data.diff,
68+
title: title,
69+
unmerged: state === 'unmerged',
70+
project: activeProject
71+
}
72+
if (state == 'unstaged') {
73+
options.oldRevTitle = entry.indexStatus === " "?"HEAD":"Staged";
74+
options.newRevTitle = "Unstaged";
75+
options.oldRev = entry.indexStatus === " "?"@":":0";
76+
options.newRev = "_";
77+
} else if (state === 'staged') {
78+
options.oldRevTitle = "HEAD";
79+
options.newRevTitle = "Staged";
80+
options.oldRev = "@";
81+
options.newRev = ":0";
82+
} else {
83+
options.onresolve = function(resolution) {
84+
utils.sendRequest({
85+
url: "projects/"+activeProject.name+"/resolve/"+encodeURIComponent(entry.file),
86+
type: "POST",
87+
responses: {
88+
0: function(error) {
89+
console.log(error);
90+
// done(error,null);
91+
},
92+
200: function(data) {
93+
refresh(true);
94+
},
95+
400: {
96+
'unexpected_error': function(error) {
97+
console.log(error);
98+
// done(error,null);
99+
}
100+
},
101+
}
102+
},{resolutions:resolution.resolutions[entry.file]});
103+
}
104+
}
105+
options.oncancel = showMergeConflictNotification;
106+
RED.diff.showUnifiedDiff(options);
107+
// console.log(data.diff);
108+
},
109+
400: {
110+
'unexpected_error': function(error) {
111+
console.log(error);
112+
// done(error,null);
113+
}
114+
}
115+
}
116+
})
117+
}
43118

44119
function createChangeEntry(row, entry, status, state) {
45120
row.addClass("sidebar-version-control-change-entry");
@@ -52,88 +127,67 @@ RED.sidebar.versionControl = (function() {
52127

53128

54129
var icon = $('<i class=""></i>').appendTo(container);
55-
var label = $('<span>').appendTo(container);
56-
57-
var bg = $('<div class="button-group"></div>').appendTo(row);
58-
var viewDiffButton = $('<button class="editor-button editor-button-small"><i class="fa fa-'+(state==='unmerged'?'columns':'eye')+'"></i></button>')
59-
.appendTo(bg)
60-
.click(function(evt) {
61-
evt.preventDefault();
62-
var activeProject = RED.projects.getActiveProject();
63-
var diffTarget = (state === 'staged')?"index":"tree";
64-
utils.sendRequest({
65-
url: "projects/"+activeProject.name+"/diff/"+diffTarget+"/"+encodeURIComponent(entry.file),
66-
type: "GET",
67-
responses: {
68-
0: function(error) {
69-
console.log(error);
70-
// done(error,null);
71-
},
72-
200: function(data) {
73-
if (mergeConflictNotification) {
74-
mergeConflictNotification.close();
75-
mergeConflictNotification = null;
76-
}
77-
var title;
78-
if (state === 'unstaged') {
79-
title = 'Unstaged changes : '+entry.file
80-
} else if (state === 'staged') {
81-
title = 'Staged changes : '+entry.file
82-
} else {
83-
title = 'Resolve conflicts : '+entry.file
84-
}
85-
var options = {
86-
diff: data.diff,
87-
title: title,
88-
unmerged: state === 'unmerged',
89-
project: activeProject
90-
}
91-
if (state == 'unstaged') {
92-
options.oldRevTitle = entry.indexStatus === " "?"HEAD":"Staged";
93-
options.newRevTitle = "Unstaged";
94-
options.oldRev = entry.indexStatus === " "?"@":":0";
95-
options.newRev = "_";
96-
} else if (state === 'staged') {
97-
options.oldRevTitle = "HEAD";
98-
options.newRevTitle = "Staged";
99-
options.oldRev = "@";
100-
options.newRev = ":0";
101-
} else {
102-
options.onresolve = function(resolution) {
103-
utils.sendRequest({
104-
url: "projects/"+activeProject.name+"/resolve/"+encodeURIComponent(entry.file),
105-
type: "POST",
130+
var entryLink = $('<a href="#">')
131+
.appendTo(container)
132+
.click(function(e) {
133+
e.preventDefault();
134+
viewFileDiff(entry,state);
135+
});
136+
var label = $('<span>').appendTo(entryLink);
137+
138+
var entryTools = $('<div class="sidebar-version-control-change-entry-tools">').appendTo(row);
139+
var bg;
140+
var revertButton;
141+
if (state === 'unstaged') {
142+
bg = $('<span class="button-group" style="margin-right: 5px;"></span>').appendTo(entryTools);
143+
revertButton = $('<button class="editor-button editor-button-small"><i class="fa fa-reply"></i></button>')
144+
.appendTo(bg)
145+
.click(function(evt) {
146+
evt.preventDefault();
147+
var spinner = utils.addSpinnerOverlay(container).addClass('projects-dialog-spinner-contain');
148+
var notification = RED.notify("Are you sure you want to revert the changes to '"+entry.file+"'? This cannot be undone.", {
149+
type: "warning",
150+
modal: true,
151+
fixed: true,
152+
buttons: [
153+
{
154+
text: RED._("common.label.cancel"),
155+
click: function() {
156+
spinner.remove();
157+
notification.close();
158+
}
159+
},{
160+
text: 'Revert changes',
161+
click: function() {
162+
notification.close();
163+
var activeProject = RED.projects.getActiveProject();
164+
var url = "projects/"+activeProject.name+"/files/_/"+entry.file;
165+
var options = {
166+
url: url,
167+
type: "DELETE",
106168
responses: {
107-
0: function(error) {
108-
console.log(error);
109-
// done(error,null);
110-
},
111169
200: function(data) {
112-
refresh(true);
170+
spinner.remove();
113171
},
114172
400: {
115173
'unexpected_error': function(error) {
174+
spinner.remove();
116175
console.log(error);
117176
// done(error,null);
118177
}
119-
},
178+
}
120179
}
121-
},{resolutions:resolution.resolutions[entry.file]});
180+
}
181+
utils.sendRequest(options);
122182
}
123183
}
124-
options.oncancel = showMergeConflictNotification;
125-
RED.diff.showUnifiedDiff(options);
126-
// console.log(data.diff);
127-
},
128-
400: {
129-
'unexpected_error': function(error) {
130-
console.log(error);
131-
// done(error,null);
132-
}
133-
}
134-
}
135-
})
136-
})
184+
185+
]
186+
})
187+
188+
});
189+
}
190+
bg = $('<span class="button-group"></span>').appendTo(entryTools);
137191
if (state !== 'unmerged') {
138192
$('<button class="editor-button editor-button-small"><i class="fa fa-'+((state==='unstaged')?"plus":"minus")+'"></i></button>')
139193
.appendTo(bg)
@@ -203,11 +257,10 @@ RED.sidebar.versionControl = (function() {
203257
delete entry.spinner;
204258
}
205259

206-
viewDiffButton.attr("disabled",(status === 'D' || status === '?'));
207-
viewDiffButton.find("i")
208-
.toggleClass('fa-eye',!(status === 'D' || status === '?'))
209-
.toggleClass('fa-eye-slash',(status === 'D' || status === '?'))
210-
260+
if (revertButton) {
261+
revertButton.toggle(status !== '?');
262+
}
263+
entryLink.toggleClass("disabled",(status === 'D' || status === '?'));
211264
}
212265
entry["update"+((state==='unstaged')?"Unstaged":"Staged")](entry, status);
213266
}

editor/sass/projects.scss

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,13 @@
444444
span {
445445
margin: 0 6px;
446446
}
447-
.button-group {
447+
a {
448+
color: currentColor;
449+
&.disabled {
450+
pointer-events: none;
451+
}
452+
}
453+
.sidebar-version-control-change-entry-tools {
448454
position: absolute;
449455
top: 4px;
450456
right: 4px;
@@ -455,7 +461,7 @@
455461
}
456462

457463
&:hover {
458-
.button-group {
464+
.sidebar-version-control-change-entry-tools {
459465
display: block;
460466
}
461467
}

red/api/editor/projects/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,22 @@ module.exports = {
170170
})
171171
});
172172

173+
// Revert a file
174+
app.delete("/:id/files/_/*", needsPermission("projects.write"), function(req,res) {
175+
var projectId = req.params.id;
176+
var filePath = req.params[0];
177+
178+
runtime.storage.projects.revertFile(req.user, projectId,filePath).then(function() {
179+
res.status(204).end();
180+
})
181+
.catch(function(err) {
182+
console.log(err.stack);
183+
res.status(400).json({error:"unexpected_error", message:err.toString()});
184+
})
185+
});
186+
187+
188+
173189
// Stage a file
174190
app.post("/:id/stage/*", needsPermission("projects.write"), function(req,res) {
175191
var projectName = req.params.id;

red/runtime/storage/localfilesystem/projects/Project.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@ Project.prototype.getFile = function (filePath,treeish) {
329329
return fs.readFile(fspath.join(this.path,filePath),"utf8");
330330
}
331331
};
332+
Project.prototype.revertFile = function (filePath) {
333+
var self = this;
334+
return gitTools.revertFile(this.path, filePath).then(function() {
335+
return self.load();
336+
});
337+
};
338+
339+
332340

333341
Project.prototype.status = function(user) {
334342
var self = this;

red/runtime/storage/localfilesystem/projects/git/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,10 @@ module.exports = {
421421
return status.files;
422422
})
423423
},
424+
revertFile: function(cwd, filePath) {
425+
var args = ["checkout",filePath];
426+
return runGitCommand(args,cwd);
427+
},
424428
stageFile: function(cwd,file) {
425429
var args = ["add"];
426430
if (Array.isArray(file)) {

red/runtime/storage/localfilesystem/projects/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ function getFileDiff(user, project,file,type) {
173173
}
174174
function getCommits(user, project,options) {
175175
checkActiveProject(project);
176-
return activeProject.getCommits(options);
176+
return activeProject.getCommits(options);
177177
}
178178
function getCommit(user, project,sha) {
179179
checkActiveProject(project);
@@ -184,6 +184,12 @@ function getFile(user, project,filePath,sha) {
184184
checkActiveProject(project);
185185
return activeProject.getFile(filePath,sha);
186186
}
187+
function revertFile(user, project,filePath) {
188+
checkActiveProject(project);
189+
return activeProject.revertFile(filePath).then(function() {
190+
return reloadActiveProject("revert");
191+
})
192+
}
187193
function push(user, project,remoteBranchName,setRemote) {
188194
checkActiveProject(project);
189195
return activeProject.push(user,remoteBranchName,setRemote);
@@ -413,6 +419,7 @@ module.exports = {
413419
updateProject: updateProject,
414420
getFiles: getFiles,
415421
getFile: getFile,
422+
revertFile: revertFile,
416423
stageFile: stageFile,
417424
unstageFile: unstageFile,
418425
commit: commit,

0 commit comments

Comments
 (0)