diff --git a/lib/commit.js b/lib/commit.js index 232d771b0..734cfc781 100644 --- a/lib/commit.js +++ b/lib/commit.js @@ -1,4 +1,5 @@ var events = require("events"); +var fp = require("lodash/fp"); var NodeGit = require("../"); var Commit = NodeGit.Commit; var LookupWrapper = NodeGit.Utils.lookupWrapper; @@ -50,6 +51,130 @@ Commit.prototype.amend = function ( }); }; +/** + * 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 {String} signatureField + * @param {Function} onSignature + * @return {Oid} +*/ +Commit.prototype.amendWithSignature = function( + updateRef, + author, + committer, + messageEncoding, + message, + tree, + signatureField, + 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; + var parents; + var commitContent; + var commitOid; + var commit; + + 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, + committer, + messageEncoding, + message, + tree: treeObject + } + ); + + var commitFields = { + author: _this.author(), + committer: _this.committer(), + messageEncoding: _this.messageEncoding(), + message: _this.message(), + tree: commitTree + }; + + var { + author: resolvedAuthor, + committer: resolvedCommitter, + messageEncoding: resolvedMessageEncoding, + message: resolvedMessage, + tree: resolvedTree + } = fp.assign( + truthyArgs, + commitFields + ); + + return Commit.createBuffer( + repo, + resolvedAuthor, + resolvedCommitter, + resolvedMessageEncoding, + resolvedMessage, + resolvedTree, + parents.length, + parents + ); + }) + .then(function(commitContentResult) { + commitContent = commitContentResult + "\n"; + return onSignature(commitContent); + }) + .then(function(signature) { + return Commit.createWithSignature( + repo, + commitContent, + signature, + signatureField + ); + }); + + if (!updateRef) { + return createCommitPromise; + } + + return createCommitPromise.then(function(commitOidResult) { + commitOid = commitOidResult; + 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} diff --git a/test/tests/commit.js b/test/tests/commit.js index 395b0eebe..9bb0e4b2d 100644 --- a/test/tests/commit.js +++ b/test/tests/commit.js @@ -450,6 +450,73 @@ describe("Commit", function() { }); }); + + it("can amend commit with signature", function() { + const signature = "-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "iQJHBAEBCAAxFiEEKdxGpJ93wnkLaBKfURjJKedOfEMFAlxPKUYTHHN0ZXZla0Bh\n" + + "eG9zb2Z0LmNvbQAKCRBRGMkp5058Q3vcD/0Uf6P68g98Kbvsgjg/aidM1ujruXaw\n" + + "X5WSsCAw+wWGICOj0n+KBnmQruI4HSFz3zykEshuOpcBv1X/+huwDeB/hBqonCU8\n" + + "QdexCdWR70YbT1bufesUwV9v1qwE4WOmFxWXgwh55K0wDRkc0u2aLcwrJkIEEVfs\n" + + "HqZyFzU4kwbGekY/m7d1DsBhWyKEGW9/25WMYmjWOWOiaFjeBaHLlxiEM8KGnMLH\n" + + "wx37NuFuaABgi23AAcBGdeWy04TEuU4S51+bHM3RotrZ2cryW2lEbkkXodhIJcq0\n" + + "RgrStCbvR0ehnOPdYSiRbxK8JNLZuNjHlK2g7wVi+C83vwMQuhU4H6OlYHGVr664\n" + + "4YzL83FdIo7wiMOFd2OOMLlCfHgTun60FvjCs4WHjrwH1fQl287FRPLa/4olBSQP\n" + + "yUXJaZdxm4cB4L/1pmbb/J/XUiOio3MpaN3GFm2hZloUlag1uPDBtCxTl5odvj4a\n" + + "GOmTBWznXxF/zrKnQVSvv+EccNxYFc0VVjAxGgNqPzIxDAKtw1lE5pbBkFpFpNHz\n" + + "StmwZkP9QIJY4hJYQfM+pzHLe8xjexL+Kh/TrYXgY1m/4vJe0HJSsnRnaR8Yfqhh\n" + + "LReqo94VHRYXR0rZQv4py0D9TrWaI8xHLve6ewhLPNRzyaI9fNrinbcPYZZOWnRi\n" + + "ekgUBx+BX6nJOw==\n" + + "=4Hy5\n" + + "-----END PGP SIGNATURE-----"; + + function onSignature(dataToSign) { + return new Promise(function (resolve) { + return resolve(signature); + }); + } + + var repo; + var oid; + var commit; + var message; + var parents; + + return NodeGit.Repository.open(reposPath) + .then(function(repoResult) { + repo = repoResult; + return repo.getHeadCommit(); + }) + .then(function(headCommit) { + message = headCommit.message().trim(); + parents = headCommit.parents(); + + return headCommit.amendWithSignature( + null, + null, + null, + null, + null, + null, + "gpgsig", + onSignature + ); + }) + .then(function(oidResult) { + oid = oidResult; + return NodeGit.Commit.lookup(repo, oid); + }) + .then(function(commitResult) { + commit = commitResult; + return commit.getSignature("gpgsig"); + }) + .then(function(signatureInfo) { + assert.equal(signatureInfo.signature, signature); + assert.equal(commit.message().trim(), message); + assert.deepEqual(commit.parents(), parents); + }); + }); + it("has an owner", function() { var owner = this.commit.owner(); assert.ok(owner instanceof Repository);