c9-core/plugins/c9.ide.scm/v1/git.js

473 wiersze
15 KiB
JavaScript

define(function(require, exports, module) {
main.consumes = [
"Plugin", "scm", "proc", "c9"
];
main.provides = ["scm.git"];
return main;
function main(options, imports, register) {
var Plugin = imports.Plugin;
var scm = imports.scm;
var proc = imports.proc;
var c9 = imports.c9;
var basename = require("path").basename;
var dirname = require("path").dirname;
/***** Initialization *****/
var plugin = new Plugin("Ajax.org", main.consumes);
// var emit = plugin.getEmitter();
var workspaceDir = c9.workspaceDir;
scm.on && scm.on("workspaceDir", function(options) {
workspaceDir = options.workspaceDir || c9.workspaceDir;
}, plugin);
/***** Methods *****/
/**
* Detect whether the path has a git repository
*/
function detect(path, callback) {
}
function addAll(callback) {
git("add -u", callback);
}
function addFileToStaging(paths, callback) {
git(["add", "-f", "--ignore-errors", "--"].concat(paths), callback);
}
function unstageAll(callback) {
git("reset --mixed", callback);
}
function unstage(paths, callback) {
git(["reset", "--mixed", "--"].concat(paths), callback);
}
function fetch(options, callback) {
var args = ["fetch"];
if (options.prune) args.push("--prune");
if (options.branch) args.push(options.branch);
git(args, callback);
}
function pull(options, callback) {
var args = ["pull"];
if (options.prune) args.push("--prune");
if (options.branch) args.push(options.branch);
git(args, callback);
}
function push(options, callback) {
var args = ["push"];
if (options.force) args.push("--force");
if (options.branch) args.push(options.branch);
git(args, callback);
}
function git(args, cb) {
if (typeof args == "string")
args = args.split(/\s+/);
proc.spawn("git", {
args: args,
cwd: workspaceDir
}, function(e, p) {
// if (e) console.error(e);
buffer(p, function(stdout, stderr) {
// console.log(e, stdout);
cb && cb(e, stdout, stderr);
});
});
}
function buffer(process, callback) {
var stdout = "", stderr = "";
process.stdout.on("data", function(c) {
stdout += c;
});
process.stderr.on("data", function(c) {
stderr += c;
});
process.on("exit", function(c) {
callback(stdout, stderr);
});
}
function getStatus(options, cb) {
var t = Date.now();
var args = [];
var hash = options.hash;
var base = options.base;
if ((hash || base) && !options.twoWay) {
args.push("diff", "--name-status", "-b", "-z",
"--no-textconv", "--no-ext-diff", "--no-color",
"--find-renames"
);
if (hash == "staging")
hash = "--cached";
if (base == "staging")
base = "--cached";
if (hash === 0 || base === 0) {
args.push(base || hash);
} else {
args.push(base || hash + "^1", hash);
}
} else {
options.twoWay = true;
args.push("status", "--porcelain", "-b", "-z");
// if (!ignored.isOpen)
args.push("--untracked-files=no");
if (options.untracked == "all")
args.push("--untracked-files=all");
if (options.ignored)
args.push("--ignored");
}
args.push("--");
if (options.path)
args.push(options.path);
proc.execFile("git", {
args: args,
cwd: workspaceDir
}, function(err, stdout, stderr) {
if (err) {
if (/fatal: bad revision/.test(err.message)) {
var EMPTY = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
if (options.base != EMPTY) {
options.base = EMPTY;
return getStatus(options, cb);
}
}
return cb(err);
}
// console.log(err, stdout);
// console.log(t-Date.now(), stdout.length);
cb(err, stdout);
});
}
function getLog(options, cb) {
var t = Date.now();
git(["rev-list", "HEAD", "--count"], function(err, stdout, stderr) {
if (err) return cb(err);
console.log(err, stdout);
console.log(t - Date.now(), stdout.length);
var args = ["log", "--topo-order", "--date=raw"];
if (options.boundary !== false) args.push("--boundary");
if (options.logOptions) args.push.apply(args, options.logOptions);
// not using %D since it is missing on git 1.9
args.push('--pretty=format:' + (options.format || "%h %p %d %B %an %ct %ae ").replace(/ /g, "%x00"));
args.push("--all");
args.push("HEAD");
args.push("-n", options.count || 1000);
if (options.from)
args.push("--skip=" + options.from);
args.push("--");
if (options.path)
args.push(options.path);
git(args, function(err, stdout, stderr) {
if (err) return cb(err);
var data = stdout.trim().split("\x00\n");
// handle empty git history
if (data.length == 1 && !data[0]) {
data = [];
}
var root = [];
var head;
for (var i = 0; i < data.length; i++) {
var line = data[i].split("\x00");
var branches = line[2];
if (branches) {
// remove braces
branches = branches
.replace(/^\s*\(\s*/g, "")
.replace(/\s*\)\s*$/g, "");
if (/(^|, )HEAD[, ]/.test(branches))
head = line[0];
}
root.push({
hash: line[0],
parents: line[1],
message: line[3],
label: line[3].substring(0, line[3].indexOf("\n") + 1 || undefined),
branches: branches || undefined, // set to undefined to not keep in JSON.stringify
authorname: line[4],
authoremail: line[5],
date: line[6],
});
}
// console.log(err, x);
console.log(t - Date.now(), stdout.length);
root.unshift({
label: "// WIP",
hash: 0,
parents: head
});
cb(null, root);
});
});
}
function getFileAtHash(hash, path, cb) {
var id = hash;
if (path) {
if (!hash) {
if (path[0] != "/")
path = "/" + path;
return proc.readfile(path, {}, cb);
}
if (hash == "staging")
hash = "";
if (path[0] == "/")
path = path.substr(1);
id = hash + ":" + path;
}
proc.execFile("git", {
args: ["show", id],
maxBuffer: 1000 * 1024,
cwd: workspaceDir
}, function(err, stdout, stderr) {
cb(err, stdout, stderr);
});
}
function loadDiff(options, callback) {
var req = {};
var args = ["diff", "-U20000", options.oldPath, options.newPath];
proc.execFile("git", {
args: args,
cwd: workspaceDir
}, function(err, stdout, stderr) {
if (err || !stdout) {
return getFileAtHash(options.oldPath, "", function(err, orig) {
getFileAtHash(options.newPath, "", function(err, edit) {
callback(err, {
request: req,
orig: orig || "",
edit: edit || ""
});
});
});
}
callback(null, {
request: req,
patch: stdout
});
});
return req;
}
function addLinesToStaging(patch, cb) {
proc.spawn("git", {
args: ["apply", "--cached", "--unidiff-zero", "--whitespace=nowarn", "-"], // "--recount",
cwd: workspaceDir
}, function(err, p) {
if (err) return cb(err);
process = p.process;
var stderr = "";
var stdout = "";
process.stdout.on("data", function(e) {
stdout += e;
});
process.stderr.on("data", function(e) {
stderr += e;
});
process.on("exit", function(e) {
cb(e, stdout, stderr);
});
process.stdin.write(patch);
process.stdin.end();
});
}
// TODO reload
function updateCommitStatus(ammend, callback) {
var args = ["commit", "--dry-run", "--porcelain", "--branch", "-z"];
if (ammend)
args.push("--amend");
git(args, callback);
}
function commit(options, callback) {
if (!options.message) return;
var args = ["commit", options.ammend && "--amend", "-m", options.message].filter(Boolean);
git(args, callback);
}
function listAllRefs(cb) {
var args = ["for-each-ref", "--count=3000", "--sort=*objecttype", "--sort=-committerdate"];
args.push(
'--format=%(objectname:short) %(refname) %(upstream:trackshort) %(objecttype) %(subject) %(authorname) %(authoremail) %(committerdate:raw)'.replace(/ /g, "%00")
);
git(args, function(err, stdout) {
if (err) return cb(err);
var data = stdout.trim().split("\n").map(function(x) {
var parts = x.split("\x00");
return {
hash: parts[0],
name: parts[1],
upstream: parts[2],
type: parts[3],
subject: parts[4],
authorname: parts[5],
authoremail: parts[6],
committerdate: parts[7],
};
});
cb && cb(null, data);
});
}
function getBlame(path, callback) {
proc.spawn("git", {
args: ["blame", "-wp", "--", basename(path)],
cwd: workspaceDir + "/" + dirname(path)
}, function(err, process) {
if (err) return callback(err);
buffer(process, function(stdout, stderr) {
callback(stderr, stdout);
});
});
}
/***** Lifecycle *****/
plugin.on("load", function() {
if (scm.register)
scm.register("git", plugin);
});
plugin.on("unload", function() {
if (scm.register)
scm.unregister("git", plugin);
});
/***** Register and define API *****/
/**
*/
plugin.freezePublicAPI({
/**
*
*/
detect: detect,
/**
*
*/
addAll: addAll,
/**
*
*/
addFileToStaging: addFileToStaging,
/**
*
*/
unstageAll: unstageAll,
/**
*
*/
unstage: unstage,
/**
*
*/
fetch: fetch,
/**
*
*/
pull: pull,
/**
*
*/
git: git,
/**
*
*/
push: push,
/**
*
*/
buffer: buffer,
/**
*
*/
getStatus: getStatus,
/**
*
*/
getLog: getLog,
/**
*
*/
getFileAtHash: getFileAtHash,
/**
*
*/
loadDiff: loadDiff,
/**
*
*/
addLinesToStaging: addLinesToStaging,
/**
*
*/
updateCommitStatus: updateCommitStatus,
/**
*
*/
commit: commit,
/**
*
*/
listAllRefs: listAllRefs,
/**
*
*/
getBlame: getBlame,
});
register(null, {
"scm.git": plugin
});
}
});