@readme/nodegit
Version:
Node.js libgit2 asynchronous native bindings
438 lines (392 loc) • 10.4 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);
/**
* @async
* @param {Number} n
* @return {Commit}
*/
Commit.prototype.parent = function(n) {
var repo = this.repo;
return _parent.call(this, n).then(p => {
p.repo = repo;
return p;
});
};
/**
* 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
* @return {Oid}
*/
Commit.prototype.amend = function (
updateRef, author, committer, message_encoding, message, tree) {
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
) {
let repo = this.repo;
let parentOids = this.parents();
let _this = this;
let 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));
});
let treeObject;
let parents;
let commitContent;
let commit;
let skippedSigning;
let resolvedAuthor;
let resolvedCommitter;
let resolvedMessageEncoding;
let resolvedMessage;
let resolvedTree;
let createCommitPromise = Promise.all(promises)
.then(function(results) {
treeObject = fp.head(results);
parents = fp.tail(results);
return _this.getTree();
})
.then(function(commitTreeResult) {
let commitTree = commitTreeResult;
let truthyArgs = fp.omitBy(
fp.isNil,
{
author,
committer,
messageEncoding,
message,
tree: treeObject
}
);
let commitFields = {
author: _this.author(),
committer: _this.committer(),
messageEncoding: _this.messageEncoding(),
message: _this.message(),
tree: commitTree
};
({
author: resolvedAuthor,
committer: resolvedCommitter,
messageEncoding: resolvedMessageEncoding,
message: resolvedMessage,
tree: resolvedTree
} = fp.assign(
commitFields,
truthyArgs
));
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({ code, field, 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: {
const 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
* @return {Array<Diff>} an array of diffs
*/
Commit.prototype.getDiff = function() {
return this.getDiffWithOptions(null);
};
/**
* Generate an array of diff trees showing changes between this commit
* and its parent(s).
*
* @async
* @param {Object} options
* @return {Array<Diff>} an array of diffs
*/
Commit.prototype.getDiffWithOptions = function(options) {
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);
});
});
};
/**
* 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) {
return this.getTree().then(function(tree) {
return tree.getEntry(path);
});
};
/**
* Retrieve the commit's parents as commit objects.
*
* @async
* @param {number} limit Optional amount of parents to return.
* @return {Array<Commit>} array of commits
*/
Commit.prototype.getParents = function(limit) {
var parents = [];
// 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);
};
/**
* @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() {
return this.repo.getTree(this.treeId());
};
/**
* 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();
};