1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Add delete local branch option

This commit is contained in:
Nick O'Leary 2017-12-08 16:31:42 +00:00
parent d007623347
commit 27f1d3b704
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
6 changed files with 226 additions and 19 deletions

View File

@ -947,8 +947,132 @@ RED.projects.settings = (function() {
updateForm(); updateForm();
} }
function createLocalBranchListSection(activeProject,pane) {
var localBranchContainer = $('<div class="user-settings-section"></div>').appendTo(pane);
$('<h4></h4>').text("Branches").appendTo(localBranchContainer);
var row = $('<div class="user-settings-row projects-dialog-branch-list"></div>').appendTo(localBranchContainer);
var branchList = $('<ol>').appendTo(row).editableList({
addButton: false,
scrollOnAdd: false,
addItem: function(row,index,entry) {
var container = $('<div class="projects-dialog-branch-list-entry">').appendTo(row);
$('<span><i class="fa fa-code-fork"></i></span>').appendTo(container);
$('<span class="branch-name">').text(entry.name).appendTo(container);
// if (entry.commit) {
// $('<span class="commit">').text(entry.commit.sha).appendTo(container);
// }
if (entry.remote) {
$('<span class="branch-remote-name">').text(entry.remote||"").appendTo(container);
if (entry.status.ahead+entry.status.behind > 0) {
$('<span class="branch-remote-status">'+
'<i class="fa fa-long-arrow-up"></i> <span>'+entry.status.ahead+'</span> '+
'<i class="fa fa-long-arrow-down"></i> <span>'+entry.status.behind+'</span>'+
'</span>').appendTo(container);
}
}
var tools = $('<span class="projects-dialog-branch-list-entry-tools">').appendTo(container);
if (entry.current) {
tools.text('current');
} else {
$('<button class="editor-button editor-button-small">delete</button>')
.appendTo(tools)
.click(function(e) {
e.preventDefault();
var spinner = utils.addSpinnerOverlay(row).addClass('projects-dialog-spinner-contain');
var notification = RED.notify("Are you sure you want to delete the local branch '"+entry.name+"'? This cannot be undone.", {
type: "warning",
modal: true,
fixed: true,
buttons: [
{
text: RED._("common.label.cancel"),
click: function() {
spinner.remove();
notification.close();
}
},{
text: 'Delete branch',
click: function() {
notification.close();
var url = "projects/"+activeProject.name+"/branches/"+entry.name;
var options = {
url: url,
type: "DELETE",
responses: {
200: function(data) {
row.fadeOut(200,function() {
branchList.editableList('removeItem',entry);
spinner.remove();
});
},
400: {
'git_delete_branch_unmerged': function(error) {
notification = RED.notify("The local branch '"+entry.name+"' has unmerged changes that will be lost. Are you sure you want to delete it?", {
type: "warning",
modal: true,
fixed: true,
buttons: [
{
text: RED._("common.label.cancel"),
click: function() {
spinner.remove();
notification.close();
}
},{
text: 'Delete unmerged branch',
click: function() {
options.url += "?force=true";
notification.close();
utils.sendRequest(options);
}
}
]
});
},
'unexpected_error': function(error) {
console.log(error);
spinner.remove();
}
},
}
}
utils.sendRequest(options);
}
}
]
})
})
}
}
});
$.getJSON("projects/"+activeProject.name+"/branches",function(result) {
if (result.branches) {
result.branches.sort(function(A,B) {
if (A.current) { return -1 }
if (B.current) { return 1 }
return A.name.localeCompare(B.name);
});
result.branches.forEach(function(branch) {
branchList.editableList('addItem',branch);
})
}
})
}
function createRemoteRepositorySection(activeProject,pane) { function createRemoteRepositorySection(activeProject,pane) {
var title = $('<h3></h3>').text("Version Control").appendTo(pane); $('<h3></h3>').text("Version Control").appendTo(pane);
createLocalBranchListSection(activeProject,pane);
var repoContainer = $('<div class="user-settings-section"></div>').appendTo(pane);
var title = $('<h4></h4>').text("Git remotes").appendTo(repoContainer);
var editRepoButton = $('<button class="editor-button editor-button-small" style="float: right;">edit</button>') var editRepoButton = $('<button class="editor-button editor-button-small" style="float: right;">edit</button>')
.appendTo(title) .appendTo(title)
.click(function(evt) { .click(function(evt) {
@ -958,9 +1082,9 @@ RED.projects.settings = (function() {
$('.projects-dialog-remote-list-entry-delete').show(); $('.projects-dialog-remote-list-entry-delete').show();
remoteListAddButton.show(); remoteListAddButton.show();
}); });
var repoContainer = $('<div class="user-settings-section"></div>').appendTo(pane);
var grTitle = $('<h4></h4>').text("Git remotes").appendTo(repoContainer);
row = $('<div class="user-settings-row projects-dialog-remote-list"></div>').appendTo(repoContainer); row = $('<div class="user-settings-row projects-dialog-remote-list"></div>').appendTo(repoContainer);
var remotesList = $('<ol>').appendTo(row); var remotesList = $('<ol>').appendTo(row);

View File

@ -676,3 +676,44 @@
} }
} }
*/ */
.projects-dialog-branch-list {
.red-ui-editableList-container li:last-child {
border-bottom: none;
}
}
.projects-dialog-branch-list-entry {
span {
display: inline-block;
}
span:first-child {
text-align: center;
min-width: 30px;
}
.branch-name {
min-width: 150px;
}
.branch-remote-name {
color: #aaa;
font-size: 0.9em;
min-width: 150px;
}
.branch-remote-status {
color: #aaa;
font-size: 0.9em;
}
.commit {
color: #aaa;
font-size: 0.9em;
padding: 2px 5px;
}
.projects-dialog-branch-list-entry-tools {
float: right;
margin-right: 20px;
font-size: 0.9em;
color: #999;
}
}

View File

@ -115,7 +115,7 @@ module.exports = {
}) })
}); });
// Delete project - tbd // Delete project
app.delete("/:id", needsPermission("projects.write"), function(req,res) { app.delete("/:id", needsPermission("projects.write"), function(req,res) {
runtime.storage.projects.deleteProject(req.user, req.params.id).then(function() { runtime.storage.projects.deleteProject(req.user, req.params.id).then(function() {
res.status(204).end(); res.status(204).end();
@ -373,6 +373,23 @@ module.exports = {
}) })
}); });
// Delete a local branch - ?force=true
app.delete("/:id/branches/:branchName", needsPermission("projects.write"), function(req, res) {
var projectName = req.params.id;
var branchName = req.params.branchName;
var force = !!req.query.force;
runtime.storage.projects.deleteBranch(req.user, projectName, branchName, false, force).then(function(data) {
res.status(204).end();
})
.catch(function(err) {
if (err.code) {
res.status(400).json({error:err.code, message: err.message});
} else {
res.status(400).json({error:"unexpected_error", message:err.toString()});
}
});
});
// Get a list of remote branches // Get a list of remote branches
app.get("/:id/branches/remote", needsPermission("projects.read"), function(req, res) { app.get("/:id/branches/remote", needsPermission("projects.read"), function(req, res) {
var projectName = req.params.id; var projectName = req.params.id;
@ -380,7 +397,6 @@ module.exports = {
res.json(data); res.json(data);
}) })
.catch(function(err) { .catch(function(err) {
console.log(err.stack);
if (err.code) { if (err.code) {
res.status(400).json({error:err.code, message: err.message}); res.status(400).json({error:err.code, message: err.message});
} else { } else {

View File

@ -234,7 +234,7 @@ Project.prototype.update = function (user, data) {
if (data.git.hasOwnProperty('remotes')) { if (data.git.hasOwnProperty('remotes')) {
var remoteNames = Object.keys(data.git.remotes); var remoteNames = Object.keys(data.git.remotes);
var remotesChanged = false; var remotesChanged = false;
var modifyRemotesPromise = when.resolve(); var modifyRemotesPromise = Promise.resolve();
remoteNames.forEach(function(name) { remoteNames.forEach(function(name) {
if (data.git.remotes[name].removed) { if (data.git.remotes[name].removed) {
remotesChanged = true; remotesChanged = true;
@ -348,7 +348,7 @@ Project.prototype.status = function(user) {
} }
}); });
} else { } else {
fetchPromise = when.resolve(); fetchPromise = Promise.resolve();
} }
var completeStatus = function(fetchError) { var completeStatus = function(fetchError) {
@ -461,18 +461,23 @@ Project.prototype.getBranches = function (user, isRemote) {
if (isRemote) { if (isRemote) {
fetchPromise = self.fetch(user); fetchPromise = self.fetch(user);
} else { } else {
fetchPromise = when.resolve(); fetchPromise = Promise.resolve();
} }
return fetchPromise.then(function() { return fetchPromise.then(function() {
return gitTools.getBranches(self.path,isRemote); return gitTools.getBranches(self.path,isRemote);
}); });
}; };
Project.prototype.deleteBranch = function (user, branch, isRemote, force) {
// TODO: isRemote==true support
// TODO: make sure we don't try to delete active branch
return gitTools.deleteBranch(this.path,branch,isRemote, force);
};
Project.prototype.fetch = function(user,remoteName) { Project.prototype.fetch = function(user,remoteName) {
var username; var username;
if (!user) { if (!user) {
username = "_"; username = "_";
console.log(new Error().stack);
} else { } else {
username = user.username; username = user.username;
} }
@ -484,7 +489,7 @@ Project.prototype.fetch = function(user,remoteName) {
}) })
} else { } else {
var remotes = Object.keys(this.remotes); var remotes = Object.keys(this.remotes);
var promise = when.resolve(); var promise = Promise.resolve();
remotes.forEach(function(remote) { remotes.forEach(function(remote) {
promise = promise.then(function() { promise = promise.then(function() {
return gitTools.fetch(project.path,remote,authCache.get(project.name,project.remotes[remote].fetch,username)) return gitTools.fetch(project.path,remote,authCache.get(project.name,project.remotes[remote].fetch,username))
@ -661,7 +666,7 @@ function createProject(user, metadata) {
} }
var project = metadata.name; var project = metadata.name;
return when.promise(function(resolve,reject) { return new Promise(function(resolve,reject) {
var projectPath = fspath.join(projectsDir,project); var projectPath = fspath.join(projectsDir,project);
fs.stat(projectPath, function(err,stat) { fs.stat(projectPath, function(err,stat) {
if (!err) { if (!err) {

View File

@ -55,10 +55,9 @@ function runGitCommand(args,cwd,env) {
err.code = "git_local_overwrite"; err.code = "git_local_overwrite";
} else if (/CONFLICT/.test(err.stdout)) { } else if (/CONFLICT/.test(err.stdout)) {
err.code = "git_pull_merge_conflict"; err.code = "git_pull_merge_conflict";
} else if (/not fully merged/.test(stderr)) {
err.code = "git_delete_branch_unmerged";
} }
return reject(err); return reject(err);
} }
resolve(stdout); resolve(stdout);
@ -276,8 +275,7 @@ function getBranches(cwd, remote) {
if (remote) { if (remote) {
args.push('-r'); args.push('-r');
} }
//TODO: parse out ahead/behind status (currently m[5] vvv ) var branchRE = /^([ \*] )(\S+) +(\S+)(?: \[(\S+?)(?:: (?:ahead (\d+)(?:, )?)?(?:behind (\d+))?)?\])? (.*)$/;
var branchRE = /^([ \*] )(\S+) +(\S+)(?: \[(\S+?)(?:: (.*))?\])? (.*)$/;
return runGitCommand(args,cwd).then(function(output) { return runGitCommand(args,cwd).then(function(output) {
var branches = []; var branches = [];
var lines = output.split("\n"); var lines = output.split("\n");
@ -288,10 +286,13 @@ function getBranches(cwd, remote) {
branch = { branch = {
name: m[2], name: m[2],
remote: m[4], remote: m[4],
status: m[5], status: {
ahead: m[5]||0,
behind: m[6]||0,
},
commit: { commit: {
sha: m[3], sha: m[3],
subject: m[6] subject: m[7]
} }
} }
if (m[1] === '* ') { if (m[1] === '* ') {
@ -516,6 +517,19 @@ module.exports = {
args.push(branchName); args.push(branchName);
return runGitCommand(args,cwd); return runGitCommand(args,cwd);
}, },
deleteBranch: function(cwd, branchName, isRemote, force) {
if (isRemote) {
throw new Error("Deleting remote branches not supported");
}
var args = ['branch'];
if (force) {
args.push('-D');
} else {
args.push('-d');
}
args.push(branchName);
return runGitCommand(args, cwd);
},
getBranchStatus: getBranchStatus, getBranchStatus: getBranchStatus,
addRemote: addRemote, addRemote: addRemote,
removeRemote: removeRemote removeRemote: removeRemote

View File

@ -212,6 +212,12 @@ function getBranches(user, project,isRemote) {
checkActiveProject(project); checkActiveProject(project);
return activeProject.getBranches(user, isRemote); return activeProject.getBranches(user, isRemote);
} }
function deleteBranch(user, project, branch, isRemote, force) {
checkActiveProject(project);
return activeProject.deleteBranch(user, branch, isRemote, force);
}
function setBranch(user, project,branchName,isCreate) { function setBranch(user, project,branchName,isCreate) {
checkActiveProject(project); checkActiveProject(project);
return activeProject.setBranch(branchName,isCreate).then(function() { return activeProject.setBranch(branchName,isCreate).then(function() {
@ -419,6 +425,7 @@ module.exports = {
resolveMerge: resolveMerge, resolveMerge: resolveMerge,
abortMerge: abortMerge, abortMerge: abortMerge,
getBranches: getBranches, getBranches: getBranches,
deleteBranch: deleteBranch,
setBranch: setBranch, setBranch: setBranch,
getBranchStatus:getBranchStatus, getBranchStatus:getBranchStatus,