nodegit
Version:
Node.js libgit2 asynchronous native bindings
400 lines (348 loc) • 10.3 kB
JavaScript
;
var events = require("events");
var fp = require("lodash/fp");
var NodeGit = require("../");
var Commit = NodeGit.Commit;
var LookupWrapper = NodeGit.Utils.lookupWrapper;
var _amend = Commit.prototype.amend;
var _parent = Commit.prototype.parent;
/**
* Retrieves the commit pointed to by the oid
* @async
* @param {Repository} repo The repo that the commit lives in
* @param {String|Oid|Commit} id The commit to lookup
* @return {Commit}
*/
Commit.lookup = LookupWrapper(Commit);
/**
* Amend a commit
* @async
* @param {String} update_ref
* @param {Signature} author
* @param {Signature} committer
* @param {String} message_encoding
* @param {String} message
* @param {Tree|Oid} tree
* @param {Oid} callback
*/
Commit.prototype.amend = function (updateRef, author, committer, message_encoding, message, tree, callback) {
var repo = this.repo;
var _this = this;
var treePromise;
if (tree instanceof NodeGit.Oid) {
treePromise = repo.getTree(tree);
} else {
treePromise = Promise.resolve(tree);
}
return treePromise.then(function (treeObject) {
return _amend.call(_this, updateRef, author, committer, message_encoding, message, treeObject);
});
};
/**
* Amend a commit with the given signature
* @async
* @param {String} updateRef
* @param {Signature} author
* @param {Signature} committer
* @param {String} messageEncoding
* @param {String} message
* @param {Tree|Oid} tree
* @param {Function} onSignature Callback to be called with string to be signed
* @return {Oid}
*/
Commit.prototype.amendWithSignature = function (updateRef, author, committer, messageEncoding, message, tree, onSignature) {
var repo = this.repo;
var parentOids = this.parents();
var _this = this;
var promises = [];
if (tree instanceof NodeGit.Oid) {
promises.push(repo.getTree(tree));
} else {
promises.push(Promise.resolve(tree));
}
parentOids.forEach(function (parentOid) {
promises.push(repo.getCommit(parentOid));
});
var treeObject = void 0;
var parents = void 0;
var commitContent = void 0;
var commit = void 0;
var skippedSigning = void 0;
var resolvedAuthor = void 0;
var resolvedCommitter = void 0;
var resolvedMessageEncoding = void 0;
var resolvedMessage = void 0;
var resolvedTree = void 0;
var createCommitPromise = Promise.all(promises).then(function (results) {
treeObject = fp.head(results);
parents = fp.tail(results);
return _this.getTree();
}).then(function (commitTreeResult) {
var commitTree = commitTreeResult;
var truthyArgs = fp.omitBy(fp.isNil, {
author: author,
committer: committer,
messageEncoding: messageEncoding,
message: message,
tree: treeObject
});
var commitFields = {
author: _this.author(),
committer: _this.committer(),
messageEncoding: _this.messageEncoding(),
message: _this.message(),
tree: commitTree
};
var _fp$assign = fp.assign(commitFields, truthyArgs);
resolvedAuthor = _fp$assign.author;
resolvedCommitter = _fp$assign.committer;
resolvedMessageEncoding = _fp$assign.messageEncoding;
resolvedMessage = _fp$assign.message;
resolvedTree = _fp$assign.tree;
return Commit.createBuffer(repo, resolvedAuthor, resolvedCommitter, resolvedMessageEncoding, resolvedMessage, resolvedTree, parents.length, parents);
}).then(function (commitContentResult) {
commitContent = commitContentResult;
if (!commitContent.endsWith("\n")) {
commitContent += "\n";
}
return onSignature(commitContent);
}).then(function (_ref) {
var code = _ref.code,
field = _ref.field,
signedData = _ref.signedData;
switch (code) {
case NodeGit.Error.CODE.OK:
return Commit.createWithSignature(repo, commitContent, signedData, field);
case NodeGit.Error.CODE.PASSTHROUGH:
skippedSigning = true;
return Commit.create(repo, updateRef, resolvedAuthor, resolvedCommitter, resolvedMessageEncoding, resolvedMessage, resolvedTree, parents.length, parents);
default:
{
var error = new Error("Commit.amendWithSignature threw with error code " + code);
error.errno = code;
throw error;
}
}
});
if (!updateRef) {
return createCommitPromise;
}
return createCommitPromise.then(function (commitOid) {
if (skippedSigning) {
return commitOid;
}
return repo.getCommit(commitOid).then(function (commitResult) {
commit = commitResult;
return repo.getReference(updateRef);
}).then(function (ref) {
return ref.setTarget(commitOid, "commit (amend): " + commit.summary());
}).then(function () {
return commitOid;
});
});
};
/**
* Retrieve the commit time as a Date object.
* @return {Date}
*/
Commit.prototype.date = function () {
return new Date(this.timeMs());
};
/**
* Generate an array of diff trees showing changes between this commit
* and its parent(s).
*
* @async
* @param {Function} callback
* @return {Array<Diff>} an array of diffs
*/
Commit.prototype.getDiff = function (callback) {
return this.getDiffWithOptions(null, callback);
};
/**
* Generate an array of diff trees showing changes between this commit
* and its parent(s).
*
* @async
* @param {Object} options
* @param {Function} callback
* @return {Array<Diff>} an array of diffs
*/
Commit.prototype.getDiffWithOptions = function (options, callback) {
var commit = this;
return commit.getTree().then(function (thisTree) {
return commit.getParents().then(function (parents) {
var diffs;
if (parents.length) {
diffs = parents.map(function (parent) {
return parent.getTree().then(function (parentTree) {
return thisTree.diffWithOptions(parentTree, options);
});
});
} else {
diffs = [thisTree.diffWithOptions(null, options)];
}
return Promise.all(diffs);
});
}).then(function (diffs) {
if (typeof callback === "function") {
callback(null, diffs);
}
return diffs;
}, callback);
};
/**
* Retrieve the entry represented by path for this commit.
* Path must be relative to repository root.
*
* @async
* @param {String} path
* @return {TreeEntry}
*/
Commit.prototype.getEntry = function (path, callback) {
return this.getTree().then(function (tree) {
return tree.getEntry(path).then(function (entry) {
if (typeof callback === "function") {
callback(null, entry);
}
return entry;
});
}, callback);
};
/**
* Retrieve the commit's parents as commit objects.
*
* @async
* @param {number} limit Optional amount of parents to return.
* @param {Function} callback
* @return {Array<Commit>} array of commits
*/
Commit.prototype.getParents = function (limit, callback) {
var parents = [];
// Shift arguments.
if (typeof limit === "function") {
callback = limit;
}
// If no limit was set, default to the maximum parents.
limit = typeof limit === "number" ? limit : this.parentcount();
limit = Math.min(limit, this.parentcount());
for (var i = 0; i < limit; i++) {
var oid = this.parentId(i);
var parent = this.repo.getCommit(oid);
parents.push(parent);
}
// Wait for all parents to complete, before returning.
return Promise.all(parents).then(function (parents) {
if (typeof callback === "function") {
callback(null, parents);
}
return parents;
}, callback);
};
/**
* @typedef extractedSignature
* @type {Object}
* @property {String} signature the signature of the commit
* @property {String} signedData the extracted signed data
*/
/**
* Retrieve the signature and signed data for a commit.
* @param {String} field Optional field to get from the signature,
* defaults to gpgsig
* @return {extractedSignature}
*/
Commit.prototype.getSignature = function (field) {
return Commit.extractSignature(this.repo, this.id(), field);
};
/**
* Get the tree associated with this commit.
*
* @async
* @return {Tree}
*/
Commit.prototype.getTree = function (callback) {
return this.repo.getTree(this.treeId(), callback);
};
/**
* Walk the history from this commit backwards.
*
* An EventEmitter is returned that will emit a "commit" event for each
* commit in the history, and one "end" event when the walk is completed.
* Don't forget to call `start()` on the returned event.
*
* @fires EventEmitter#commit Commit
* @fires EventEmitter#end Array<Commit>
* @fires EventEmitter#error Error
*
* @return {EventEmitter}
* @start start()
*/
Commit.prototype.history = function () {
var event = new events.EventEmitter();
var oid = this.id();
var revwalk = this.repo.createRevWalk();
revwalk.sorting.apply(revwalk, arguments);
var commits = [];
event.start = function () {
revwalk.walk(oid, function commitRevWalk(error, commit) {
if (error) {
if (error.errno === NodeGit.Error.CODE.ITEROVER) {
event.emit("end", commits);
return;
} else {
return event.emit("error", error);
}
}
event.emit("commit", commit);
commits.push(commit);
});
};
return event;
};
/**
* Get the specified parent of the commit.
*
* @param {number} the position of the parent, starting from 0
* @async
* @return {Commit} the parent commit at the specified position
*/
Commit.prototype.parent = function (id) {
var repository = this.repo;
return _parent.call(this, id).then(function (parent) {
parent.repo = repository;
return parent;
});
};
/**
* Retrieve the commit's parent shas.
*
* @return {Array<Oid>} array of oids
*/
Commit.prototype.parents = function () {
var result = [];
for (var i = 0; i < this.parentcount(); i++) {
result.push(this.parentId(i));
}
return result;
};
/**
* Retrieve the SHA.
* @return {String}
*/
Commit.prototype.sha = function () {
return this.id().toString();
};
/**
* Retrieve the commit time as a unix timestamp.
* @return {Number}
*/
Commit.prototype.timeMs = function () {
return this.time() * 1000;
};
/**
* The sha of this commit
* @return {String}
*/
Commit.prototype.toString = function () {
return this.sha();
};