c9-core/plugins/c9.ide.scm/scm.branches.js

1236 wiersze
46 KiB
JavaScript

define(function(require, exports, module) {
main.consumes = [
"Panel", "Menu", "MenuItem", "Divider", "settings", "ui", "c9",
"watcher", "panels", "util", "save", "preferences", "commands", "Tree",
"tabManager", "layout", "preferences.experimental", "scm", "util",
"dialog.alert", "dialog.confirm", "dialog.localchanges", "console"
];
main.provides = ["scm.branches"];
return main;
function main(options, imports, register) {
var Panel = imports.Panel;
var Tree = imports.Tree;
var Menu = imports.Menu;
var MenuItem = imports.MenuItem;
var Divider = imports.Divider;
var settings = imports.settings;
var ui = imports.ui;
var c9 = imports.c9;
var tabManager = imports.tabManager;
var cnsl = imports.console;
// var watcher = imports.watcher;
var util = imports.util;
// var panels = imports.panels;
// var util = imports.util;
// var save = imports.save;
// var layout = imports.layout;
var scmProvider = imports.scm;
// var prefs = imports.preferences;
// var commands = imports.commands;
var experimental = imports["preferences.experimental"];
var alert = imports["dialog.alert"].show;
var confirm = imports["dialog.confirm"].show;
var showLocalChanges = imports["dialog.localchanges"].show;
var async = require("async");
var timeago = require("timeago");
var escapeHTML = require("ace/lib/lang").escapeHTML;
/*
TODO:
- Add loading state when doing actions on branches
- Sort current branch to the top of the list
- Sort current user to the top of the list
- Do not rename remote branches (single click)
- Check out remote branches should show locally
-
*/
/***** Initialization *****/
var ENABLED = experimental.addExperiment("git", !c9.hosted, "Panels/Source Control Management");
if (!ENABLED)
return register(null, { "scm.branches": {}});
var plugin = new Panel("Ajax.org", main.consumes, {
index: options.index || 350,
caption: "Branches",
minWidth: 130,
where: options.where || "left"
});
var emit = plugin.getEmitter();
var RECENT_THRESHOLD = 14 * 24 * 60 * 60 * 1000; // 2 weeks ago
var ITEM_THRESHOLD_LOCAL = 5;
var ITEM_THRESHOLD_REMOTE = 10;
var ICON_PERSON = require("text!./icons/person.svg");
var ICON_BRANCH = require("text!./icons/git-branch.svg");
var ICON_PULLREQUEST = require("text!./icons/git-pull-request.svg");
var ICON_TAG = require("text!./icons/git-tag.svg");
var REMOTES = {};
var CURBRANCH;
var branchesTree, lastData;
var displayMode = "branches";
var mnuSettings, btnSettings;
// var workspaceDir = c9.workspaceDir; // + "/plugins/c9.ide.scm/mock/git";
var ready, scm;
var loaded = false;
function load() {
if (loaded) return false;
loaded = true;
plugin.setCommand({
name: "branches",
hint: "Version Branches",
bindKey: { mac: "", win: "" },
extra: function(editor, args, e) {
}
});
settings.on("read", function() {
settings.setDefaults("project/scm", [["primary", '["origin/master"]']]);
settings.setDefaults("user/scm", [["showauthor", [false]]]); // TODO this doesn't actually work
settings.setDefaults("state/scm", [["branches-display-mode", "branches"]]);
displayMode = settings.get("state/scm/@branches-display-mode");
});
settings.on("user/scm/@showauthor", function() {
plugin.on("draw", function() {
var showAuthor = settings.getBool("user/scm/@showauthor");
branchesTree.container.className =
branchesTree.container.className.replace(/ showAuthorName/, "");
if (showAuthor)
branchesTree.container.className += " showAuthorName";
});
});
scmProvider.on("scm", function(implementation) {
scm = implementation;
if (plugin.active)
refresh();
}, plugin);
plugin.once("show", function() {
if (!ready) refresh();
});
}
var drawn = false;
function draw(opts) {
if (drawn) return;
drawn = true;
var mnuFilter = Menu({ items: [
new MenuItem({ type: "radio", caption: "Branches", value: "branches", selected: displayMode == "branches" }),
new MenuItem({ type: "radio", caption: "Committer", value: "committer", selected: displayMode == "committer" })
]}, plugin);
mnuFilter.on("itemclick", function(e) {
settings.set("state/scm/@branches-display-mode", e.value);
button.$caption.innerHTML = (e.value == "branches"
? ICON_BRANCH
: ICON_PERSON) + e.item.caption + "<span> </span>";
displayMode = e.item.value;
if (displayMode == "branches")
showBranches();
else
showCommitters();
});
var codebox = new ui.codebox({
realtime: "true",
skin: "codebox",
class: "branch-filter",
"initial-message": "Filter Branches",
clearbutton: "true",
focusselect: "true",
singleline: "true",
style: "flex:1"
});
var container = new ui.bar({
style: "position:absolute;left:0;right:0;bottom:0;top:47px;"
});
var button = new ui.button({
caption: "Branches",
skin: "btn-switcher",
submenu: mnuFilter.aml
});
var hbox = new ui.hbox({
height: 27,
left: 10,
top: 10,
right: 10,
childNodes: [codebox, button]
});
opts.aml.appendChild(hbox);
opts.aml.appendChild(container);
button.$caption = button.oCaption.parentNode;
button.$caption.innerHTML = (displayMode == "branches"
? ICON_BRANCH
: ICON_PERSON) + displayMode.uCaseFirst() + "<span> </span>";
var mnuContext = new Menu({ items: [
new MenuItem({ caption: "Checkout...", onclick: function() {
checkout(branchesTree.selectedNode);
}, isAvailable: function() {
return branchesTree.selectedNodes.length == 1
&& branchesTree.selectedNode.hash;
} }),
new MenuItem({ caption: "Delete", onclick: function() {
var nodes = branchesTree.selectedNodes;
removeBranches(nodes);
}, isAvailable: function() {
var node = branchesTree.selectedNode;
return node
&& (node.hash && node.path !== CURBRANCH
|| node.parent.isRemote ? true : false);
} }),
new MenuItem({ caption: "Rename", onclick: function() {
branchesTree.startRename(branchesTree.selectedNode);
}, isAvailable: function() {
var node = branchesTree.selectedNode;
return branchesTree.selectedNodes.length == 1
&& node.path && node.path.match(/^refs\/heads/);
} }),
new Divider(),
new MenuItem({ caption: "Create Branch", onclick: function() {
newBranch(branchesTree.selectedNode);
}, isAvailable: function() {
return branchesTree.selectedNodes.length == 1
&& branchesTree.selectedNode.hash;
} }),
// new MenuItem({ caption: "Create Pull Request" }),
new MenuItem({ caption: "Create Workspace", onclick: function() {
}, isAvailable: function() {
return branchesTree.selectedNodes.length == 1
&& branchesTree.selectedNode.hash;
} }),
new Divider(),
new MenuItem({ caption: "Show In Version Log", onclick: function() {
showBranchInLog(branchesTree.selectedNode);
}, isAvailable: function() {
return branchesTree.selectedNodes.length == 1
&& branchesTree.selectedNode.hash;
} }),
new MenuItem({ caption: "Compare", onclick: function() {
showCompareView(branchesTree.selectedNode.path);
}, isAvailable: function() {
return branchesTree.selectedNodes.length == 1
&& branchesTree.selectedNode.hash;
} })
// new Divider(),
// new MenuItem({ caption: "Merge Into Current Branch" })
]}, plugin);
container.setAttribute("contextmenu", mnuContext.aml);
branchesTree = new Tree({
container: container.$int,
scrollMargin: [0, 10],
theme: "filetree branches"
+ (settings.getBool("user/scm/@showauthor") ? " showAuthorName" : ""),
enableRename: true,
enableVariableHeight: true,
isLoading: function() {},
getIconHTML: function(node) {
if (node.isFolder || !node.path || !node.subject) return "";
if (node.status == "loading")
return "<span class='filetree-icon'></span>";
var icon;
if (node.path.indexOf("refs/tags/") === 0)
icon = ICON_TAG;
else if (node.parent.parent == pullRequests)
icon = ICON_PULLREQUEST;
else
icon = ICON_BRANCH; // todo diff between local, remote, stash
return "<span class='filetree-icon'>" + icon + "</span>";
},
getCaptionHTML: function(node) {
var name;
if (branchesTree.filterKeyword && node.path && !node.parent.parent
|| node.path && displayMode == "committer")
name = node.path.replace(/^refs\//, "");
else
name = node.label || node.name;
if (node.type == "user")
return "<img src='"
+ util.getGravatarUrl(node.email.replace(/[<>]/g, ""), 32, "")
+ "' width='16' height='16' />"
+ escapeHTML(node.label)
+ " (" + node.children.length + ")";
if (node.isRemote) {
return "remotes <span class='remote-button'>Add Remote</span>";
}
if (node.parent.isRemote) {
return escapeHTML(name) + " ["
+ (REMOTES[name] || "") + "]";
}
if (node.authorname) {
return escapeHTML(name)
+ "<span class='author'><img src='"
+ util.getGravatarUrl(node.authoremail.replace(/[<>]/g, ""), 32, "")
+ "' width='16' height='16' />"
+ escapeHTML(node.authorname) + "</span>"
+ "<span class='extrainfo'> - "
+ (node.date ? timeago(node.date) : "") + "</span>";
}
return escapeHTML(name);
},
getTooltipText: function(node) {
return node.authorname
? "[" + node.hash + "] " + node.authorname + " - " + node.subject
: "";
},
getRowIndent: function(node) {
return displayMode == "committer" //branchesTree.filterKeyword ||
? node.$depth
: node.$depth ? node.$depth - 1 : 0;
},
getItemHeight: function(node, index) {
if (node.className == "heading first") return 27;
if (node.className == "heading") return 35;
return this.rowHeight;
},
getEmptyMessage: function() {
return branchesTree.filterKeyword
? "No branches found for '" + branchesTree.filterKeyword + "'"
: (branchesTree.emptyMessage || "Loading...");
},
getClassName: function(node) {
return (node.className || "")
+ (node.path == CURBRANCH ? " current" : "")
+ (node.status == "loading" ? " loading" : "");
},
sort: function(children) {
if (!children.length)
return;
var compare = branchesTree.model.alphanumCompare;
if (children[0].type == "user")
return children.sort(function(a, b) {
if (a.label == "[None]") return 1;
if (b.label == "[None]") return -1;
return compare(a.label + "", b.label + "");
});
return children.sort(function(a, b) {
if (a.isFolder) return 0;
if (a.path == CURBRANCH) return -1;
if (b.path == CURBRANCH) return 1;
if (a.authorname && !b.authorname)
return -1;
if (b.authorname && !a.authorname)
return 1;
return a.date - b.date;
});
}
}, plugin);
branchesTree.renderer.scrollBarV.$minWidth = 10;
branchesTree.on("afterChoose", function(e) {
var node = branchesTree.selectedNode;
if (!node) return;
if (node.showall) {
expand(node.parent);
}
else if (node.showall === false) {
collapse(node.parent);
}
else if (node.path) {
showBranchInLog(node);
// showCompareView(node.path);
}
});
branchesTree.on("beforeRename", function(e) {
if (!e.node.path || !e.node.path.match(/^refs\/(?:heads|remotes)/)
|| e.node.path == CURBRANCH)
return e.preventDefault();
});
branchesTree.on("afterRename", function(e) {
// TODO Test if branch exists. If it does, warn user and do nothing
// TODO Check for illegal characters
var base = e.node.path.match(/^refs\/(?:remotes\/[^\/]+|heads)/)[0];
var newPath = base + "/" + e.value;
scm.renameBranch(e.node.path, newPath, function(err) {
if (err) return;
e.node.name =
e.node.label = e.value;
e.node.path = newPath;
branchesTree.refresh();
});
});
var remoteName, remoteURI;
var remoteMenu = new ui.menu({
width: 400,
height: 86,
style: "padding:0",
childNodes: [
new ui.hsplitbox({
height: 20,
edge: 10,
padding: 10,
childNodes: [
remoteName = new ui.textbox({ width: "100", "initial-message": "Name" }),
remoteURI = new ui.textbox({ "initial-message": "URL" })
]
}),
new ui.button({
caption: "Add Remote",
skin: "btn-default-css3",
class: "btn-green",
right: 10,
bottom: 10,
onclick: function() {
if (!remoteName.getValue() || !remoteURI.getValue() || REMOTES[name])
return;
remoteMenu.disable();
var name = remoteName.getValue();
var url = remoteURI.getValue();
scm.addRemote(name, url, function(err) {
remoteMenu.enable();
if (err) {
return alert("Could Not Add Remote",
"Received Error While Adding Remote",
err.message || err);
}
REMOTES[name] = url;
remoteName.clear();
remoteURI.clear();
remoteMenu.hide();
var node = nodeRemote.map[name] = {
label: name,
path: "remotes/" + name
};
nodeRemote.children.push(node);
branchesTree.refresh();
refresh();
});
}
})
]
});
container.$int.addEventListener("click", function(e) {
if (e.target.className == "remote-button") {
var b = e.target.getBoundingClientRect();
remoteMenu.display(b.left, b.top + b.height);
}
});
function forwardToTree() {
branchesTree.execCommand(this.name);
}
codebox.ace.on("input", function() {
branchesTree.filterKeyword = codebox.ace.getValue();
});
codebox.ace.commands.addCommands([
"centerselection",
"goToStart",
"goToEnd",
"pageup",
"gotopageup",
"pagedown",
"gotopageDown",
"scrollup",
"scrolldown",
"goUp",
"goDown",
"selectUp",
"selectDown",
"selectMoreUp",
"selectMoreDown"
].map(function(name) {
var command = branchesTree.commands.byName[name];
return {
name: command.name,
bindKey: command.editorKey || command.bindKey,
exec: forwardToTree
};
}));
refresh();
mnuSettings = new Menu({ items: [
new MenuItem({ caption: "Refresh", onclick: refresh }, plugin),
new Divider(),
new MenuItem({ caption: "Remove Local Merged Branches", onclick: function() {
scm.removeAllLocalMerged(function() {
refresh();
});
} }, plugin),
new Divider(),
new MenuItem({ caption: "Show Author Name", type: "check", checked: "user/scm/@showauthor" }, plugin)
]}, plugin);
btnSettings = opts.aml.appendChild(new ui.button({
skin: "header-btn",
class: "panel-settings changes",
submenu: mnuSettings.aml
}));
// Mark Dirty
// plugin.on("show", function() {
// save.on("afterSave", markDirty);
// watcher.on("change", markDirty);
// });
// plugin.on("hide", function() {
// clearTimeout(timer);
// save.off("afterSave", markDirty);
// watcher.off("change", markDirty);
// });
// watcher.watch(util.normalizePath(workspaceDir) + "/.git");
// var timer = null;
// function markDirty(e) {
// clearTimeout(timer);
// timer = setTimeout(function() {
// if (tree && tree.meta.options && !tree.meta.options.hash) {
// tree.meta.options.force = true;
// emit("reload", tree.meta.options);
// }
// }, 800);
// }
}
/***** Methods *****/
var recentLocal = {
label: "recent local branches",
className: "heading first",
children: [],
isOpen: true,
isFolder: true,
noSelect: true,
$sorted: true
};
var primaryRemote = {
label: "primary remote branches",
className: "heading",
children: [],
isOpen: true,
isFolder: true,
noSelect: true,
$sorted: true
};
var pullRequests = {
label: "pull requests",
className: "heading",
isPR: true,
children: [
{
label: "Open",
children: [],
isOpen: true,
isFolder: true
},
{
label: "Closed",
children: [],
isOpen: false,
isFolder: true
}
],
isOpen: true,
isFolder: true,
map: {},
noSelect: true,
$sorted: true
};
var recentActive = {
label: "recently active",
className: "heading",
children: [],
isOpen: true,
isFolder: true,
map: {},
noSelect: true,
$sorted: true
};
var all = {
label: "all",
className: "heading",
children: [],
isOpen: true,
isFolder: true,
noSelect: true,
$sorted: true
};
var branchesRoot = {
path: "",
children: [recentLocal, primaryRemote, pullRequests, recentActive, all]
};
var committersRoot = {
path: "",
children: []
};
var nodeRemote;
function loadBranches(data) {
if (!data || data.length == 1 && !data[0].hash)
return purgeTree();
var root = branchesRoot;
root.children.forEach(function(n) {
if (n.isPR) {
n.children[0].children.length = 0;
n.children[1].children.length = 0;
n.children[0].map = {};
n.children[1].map = {};
}
else {
n.children.length = 0;
n.map = {};
}
});
root.map = {};
// Store all branches in all
data.forEach(function(x) {
var parts = parseRawBranch(x);
addToAll(parts, x);
});
// Check for empty remotes
if (!nodeRemote) {
nodeRemote = { label: "remotes", isOpen: true, map: {}, children: []};
all.children.push(nodeRemote);
all.map["remotes"] = nodeRemote;
}
for (var name in REMOTES) {
if (!nodeRemote.map[name]) {
var node = nodeRemote.map[name] = {
label: name,
path: "remotes/" + name
};
nodeRemote.children.push(node);
}
}
// Sort by date
data.sort(function(a, b) { return b.date - a.date; });
var local = [], remote = [], threshold = Date.now() - RECENT_THRESHOLD;
for (var i = 0, l = data.length; i < l; i++) {
var x = data[i];
if (x.date < threshold) continue;
if (x.path.indexOf("refs/remotes") === 0 && !isPrimary(x.path))
remote.push(copyNode(x));
else if (x.path.indexOf("refs/heads") === 0)
local.push(copyNode(x));
}
// TODO add current branch to top of recent local and make bold
// TODO in committers view move current user to the top and auto expand, show current branch in bold
recentLocal.limit = ITEM_THRESHOLD_LOCAL;
recentLocal.cache = local;
local.sort(function(a, b) { return b.date - a.date; });
recentActive.limit = ITEM_THRESHOLD_REMOTE;
recentActive.cache = remote;
remote.sort(function(a, b) { return b.date - a.date; });
updateTreeState();
}
function isPrimary(path) {
var primary = ["origin/master"]; //TODO settings.getJson("project/scm/@primary");
return ~primary.indexOf(path.replace(/^refs\/remotes\//, ""));
}
function copyNode(x) {
var y = util.extend({ className: "root-branch" }, x);
y.name = x.path.replace(/^refs\/(?:(?:remotes|tags|heads)\/)?/, "");
return y;
}
function parseRawBranch(x) {
x.date = parseInt(x.committerdate) * 1000;
x.path = x.name;
var parts = x.path.replace(/^refs\//, "").split("/");
x.name = parts.pop(); // disregard the name
if (parts[0] == "remotes") {
if (isPrimary(x.path))
primaryRemote.children.push(copyNode(x));
}
return parts;
}
function addToAll(parts, x) {
var node = all;
parts.forEach(function(p) {
var items = node.children || (node.children = []);
var map = node.map || (node.map = {});
if (map[p]) node = map[p];
else {
node = map[p] = {
label: p,
path: (node.path || "") + p + "/"
};
if (p == "remotes") {
node.isOpen = true;
node.isRemote = true;
nodeRemote = node;
}
items.push(node);
}
});
var items = node.children || (node.children = []);
var map = node.map || (node.map = {});
map[x.name] = x;
items.push(x);
}
function updateTreeState() {
var n, cur, isOpen, isOverflow, local, remote;
local = recentLocal.cache;
cur = recentLocal.children;
isOpen = cur.length && cur[cur.length - 1].showall === false;
isOverflow = local.length > ITEM_THRESHOLD_LOCAL;
if (!isOverflow)
recentLocal.children = local.slice();
else if (isOpen) {
n = { showall: false, label: "Show Less..." };
recentLocal.children = local.slice();
recentLocal.children.push(n);
}
else {
n = { showall: true, label: "Show All (" + local.length + ")..." };
recentLocal.children = local.slice(0, ITEM_THRESHOLD_LOCAL);
recentLocal.children.push(n);
}
remote = recentActive.cache;
cur = recentActive.children;
isOpen = cur.length && cur[cur.length - 1].showall === false;
isOverflow = remote.length > ITEM_THRESHOLD_REMOTE;
if (!isOverflow)
recentActive.children = remote.slice();
else if (isOpen) {
n = { showall: false, label: "Show Less..." };
recentActive.children = remote.slice();
recentActive.children.push(n);
}
else {
n = { showall: true, label: "Show All (" + remote.length + ")..." };
recentActive.children = remote.slice(0, ITEM_THRESHOLD_REMOTE);
recentActive.children.push(n);
}
// Remove empty blocks
purgeTree();
// Reset committers root
committersRoot.children.length = 0;
}
function purgeTree() {
branchesRoot.children = branchesRoot.children.filter(function(n) {
if (n == all) return true;
if (n.isPR) return n.children[0].length + n.children[1].length;
return n.children.length;
});
}
function showBranches() {
branchesTree.filterProperty = "path";
branchesTree.filterRoot = lastData;
branchesTree.setRoot(branchesRoot.children);
}
function showCommitters() {
if (!ready) return plugin.once("ready", showCommitters);
if (!committersRoot.children.length) {
var data = lastData;
var users = {}, emails = {};
data.forEach(function(x) {
var user = x.authorname || "[None]";
if (!emails[user]) emails[user] = x.authoremail;
(users[user] || (users[user] = [])).push(x);
});
for (var user in users) {
committersRoot.children.push({
label: user,
authorname: user,
email: emails[user],
type: "user",
children: users[user],
clone: function() {
var x = function() {};
x.prototype = this;
var y = new x();
y.keepChildren = true;
y.isOpen = true;
return y;
}
});
}
}
branchesTree.filterProperty = "authorname";
branchesTree.filterRoot = committersRoot.children;
branchesTree.setRoot(committersRoot.children);
}
function refresh() {
if (!scm) {
branchesTree.setRoot(null);
branchesTree.emptyMessage = "No repository detected";
return;
}
async.parallel([
function (next) {
scm.listAllRefs(function(err, data) {
lastData = data;
next(err);
});
},
function (next) {
scm.getRemotes(function(err, remotes) {
if (!err) REMOTES = remotes;
next();
});
},
function (next) {
scm.getCurrentBranch(function(err, branch) {
if (!err) CURBRANCH = "refs/heads/" + branch;
next();
});
}
], function(err) {
// if (!REMOTES["test"]) debugger;
if (err) {
branchesTree.emptyMessage = "Error while loading\n" + escapeHTML(err.message);
branchesTree.setRoot(null);
return console.error(err);
}
ready = true;
loadBranches(lastData);
if (displayMode == "branches")
showBranches();
else
showCommitters();
emit("ready");
});
}
function resolveLocalChanges(callback, onCancel) {
showLocalChanges(null,
// Stash
function() {
scm.stash(function(err) {
if (err) {
onCancel();
return alert("Could Not Stash Branch",
"Received Error While Stashing Changes",
err.message || err);
}
callback();
});
},
// Discard
function() {
scm.resetHard(function(err) {
if (err) {
onCancel();
return alert("Could Not Discard Changes",
"Received Error While Discarding Changes",
err.message || err);
}
callback();
});
},
// Cancel
function() {
// Do Nothing
onCancel();
});
}
function checkout(node) {
setLoading(node);
scm.checkout(node.path, function cb(err) {
if (err && err.code == scm.errors.LOCALCHANGES) {
resolveLocalChanges(function() {
scm.checkout(node.path, cb);
}, function() {
clearLoading(node);
});
return;
}
clearLoading(node);
if (err) {
return alert("Could Not Checkout Branch",
"Received Error While Checking out Branch",
err.message || err);
}
CURBRANCH = node.path;
branchesTree.refresh();
});
}
function removeBranches(nodes) {
nodes.forEach(function(node) {
if (node.path == CURBRANCH) return;
if (node.parent.isRemote)
return removeRemote(node);
confirm("Delete Branch",
"Are you sure you want to delete '" + node.name + "'",
"Click OK to delete this branch or click Cancel to cancel this action.",
function() {
setLoading(node);
scm.removeBranch(node.path, function(err) {
clearLoading(node);
if (err) {
return alert("Could Not Remove Branch",
"Received Error While Removing Branch",
err.message || err);
}
if (node.parent.map)
delete node.parent.map[node.label];
if (node.parent.cache)
node.parent.cache.remove(node);
node.parent.children.remove(node);
updateTreeState();
branchesTree.refresh();
});
},
function() {});
});
}
function findNewName(c) {
var name = "refs/heads/newbranche" + (c || "");
if (all.map.heads.children.some(function(n) { return n.path == name; }))
return findNewName(2);
return name;
}
function newBranch(node) {
var name = findNewName();
setLoading(node);
scm.addBranch(name, node.path, function(err) {
if (err) {
clearLoading(node);
return alert("Could Not Add Branch",
"Received Error While Adding Branch",
err.message || err);
}
updateBranch(name, null, function(err, newNode) {
clearLoading(node);
if (err) console.error(err); // TODO
if (recentLocal.children.indexOf(newNode) == -1)
expand(recentLocal);
// Select New Branch
branchesTree.select(newNode);
// Start renaming New Branch
branchesTree.startRename();
});
});
}
function updateBranch(name, node, callback) {
scm.listRef(name, function(err, data) {
if (err) return callback(err);
var parts = parseRawBranch(data);
// Existing branch
if (node) {
for (var prop in data) {
node[prop] = data[prop];
}
}
// New branch
else {
// Add to all
addToAll(parts, data);
if (data.date > Date.now() - RECENT_THRESHOLD) {
if (data.path.indexOf("refs/remotes") === 0 && !isPrimary(data.path)) {
recentActive.cache.push(node = copyNode(data));
recentActive.cache.sort(function(a, b) { return b.date - a.date; });
}
else if (data.path.indexOf("refs/heads") === 0) {
recentLocal.cache.push(node = copyNode(data));
recentLocal.cache.sort(function(a, b) { return b.date - a.date; });
}
}
updateTreeState();
}
branchesTree.refresh();
callback(null, node || data);
});
}
function removeRemote(node) {
confirm("Delete Remote",
"Are you sure you want to delete '" + node.label + "'",
"Click OK to delete this remote or click Cancel to cancel this action.",
function() {
setLoading(node);
scm.removeRemote(node.label, function(err) {
clearLoading(node);
if (err) {
return alert("Could Not Remove Remote",
"Received Error While Removing Remote",
err.message || err);
}
delete REMOTES[name];
if (node.parent.map)
delete node.parent.map[node.label];
node.parent.children.remove(node);
branchesTree.refresh();
});
},
function() {});
}
function expand(node) {
var more = node.children[node.children.length - 1];
if (!more || !more.hasOwnProperty("showall")) return;
node.children = node.cache.slice();
node.children.push(more);
more.showall = false;
more.label = "Show Less...";
branchesTree.refresh();
}
function collapse(node) {
var more = node.children[node.children.length - 1];
if (!more || !more.hasOwnProperty("showall")) return;
node.children = node.cache.slice(0, node.limit);
node.children.push(more);
more.showall = true;
more.label = "Show All (" + node.cache.length + ")...";
branchesTree.refresh();
}
function openLog(callback) {
var tabs = tabManager.getTabs();
var tab;
if (tabs.some(function(t) { return (tab = t).editorType == "scmlog"; }))
return callback(null, tabManager.focusTab(tab));
cnsl.show();
tabManager.open({
editorType: "scmlog",
focus: true,
pane: cnsl.getPanes()[0]
}, function(err, tab) {
callback(err, tab);
});
}
function showBranchInLog(node) {
setLoading(node);
openLog(function(err, tab) {
if (err) return clearLoading(node);
var editor = tab.editor;
editor.on("ready", function() {
clearLoading(node);
editor.showBranch(node.hash);
});
});
}
// function openSelection(opts) {
// if (!c9.has(c9.STORAGE))
// return;
// var node = tree.selectedNode;
// if (!node || node.isFolder)
// return;
// if (node.parent == conflicts)
// return openConflictView(node);
// var options = tree.meta.options;
// var oldPath = node.path;
// var newPath = node.originalPath || node.path;
// var hash = options.hash
// ? options.hash + ":"
// : (node.parent == staged ? "STAGED:" : "MODIFIED:");
// var base = options.base
// ? options.base + ":"
// : (node.parent == staged ? "HEAD:" : "PREVIOUS:");
// var diffview = {
// oldPath: base + oldPath,
// newPath: hash + newPath
// };
// var tab = findOpenDiffview(diffview);
// if (tab && !(opts && opts.preview)) {
// if (tab.document.meta.preview)
// tabManager.preview({ cancel: true, keep: true });
// else {
// opts && opts.preview
// ? tabManager.activateTab(tab)
// : tabManager.focusTab(tab);
// }
// return;
// }
// tabManager[opts && opts.preview ? "preview" : "open"]({
// editorType: "diffview",
// focus: true,
// document: {
// diffview: diffview
// }
// }, function(){});
// }
function showCompareView(path) {
scmProvider.openDiff({
branch: path,
compareBranch: "refs/remotes/origin/master"
});
}
function setLoading(node) {
node.status = "loading";
branchesTree.refresh();
}
function clearLoading(node) {
node.status = "loaded";
branchesTree.refresh();
}
/***** Lifecycle *****/
plugin.on("load", function() {
load();
});
plugin.on("draw", function(e) {
draw(e);
});
plugin.on("enable", function() {
});
plugin.on("disable", function() {
});
plugin.on("resize", function() {
branchesTree && branchesTree.resize();
});
plugin.on("show", function onShow(e) {
branchesTree && setTimeout(branchesTree.resize);
});
plugin.on("hide", function(e) {
});
plugin.on("unload", function() {
loaded = false;
drawn = false;
ready = false;
nodeRemote = null;
branchesTree = null;
lastData = null;
displayMode = null;
mnuSettings = null;
btnSettings = null;
// workspaceDir = c9.workspaceDir;
});
/***** Register and define API *****/
plugin.freezePublicAPI({
get tree() { return branchesTree; }
});
register(null, {
"scm.branches": plugin
});
}
});