From 92833d60518b0538a14c6a1eb604bd8e6af10739 Mon Sep 17 00:00:00 2001 From: tyler wanek Date: Wed, 8 Feb 2017 15:53:27 -0700 Subject: [PATCH 1/2] Update merge branches with processMergeMessageCallback When merging branches, we should provide a callback that allows users to inspect and modify the message that will be used for merging. The generated merge message itself was also incorrect when compared to git core, and so has been updated to match git core. --- lib/repository.js | 40 +++++- test/tests/merge.js | 320 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 350 insertions(+), 10 deletions(-) diff --git a/lib/repository.js b/lib/repository.js index 1a4e6eaa8..995beba94 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -1325,11 +1325,19 @@ Repository.prototype.refreshIndex = function(callback) { * @return {Oid|Index} A commit id for a succesful merge or an index for a * merge with conflicts */ -Repository.prototype.mergeBranches = - function(to, from, signature, mergePreference, mergeOptions) { +Repository.prototype.mergeBranches = function( + to, + from, + signature, + mergePreference, + mergeOptions, + processMergeMessageCallback +) { var repo = this; var fromBranch; var toBranch; + processMergeMessageCallback = processMergeMessageCallback || + function (message) { return message; }; mergePreference = mergePreference || NodeGit.Merge.PREFERENCE.NONE; mergeOptions = normalizeOptions(mergeOptions, NodeGit.MergeOptions); @@ -1414,19 +1422,37 @@ Repository.prototype.mergeBranches = return index.writeTreeTo(repo); }) .then(function(oid) { + var mergeDecorator; + if (fromBranch.isTag()) { + mergeDecorator = "tag"; + } else if (fromBranch.isRemote()) { + mergeDecorator = "remote-tracking branch"; + } else { + mergeDecorator = "branch"; + } + var message = - "Merged " + - fromBranch.shorthand() + - " into " + - toBranch.shorthand(); + "Merge " + + mergeDecorator + + " '" + + fromBranch.shorthand(); + + // https://github.com/git/git/blob/master/builtin/fmt-merge-msg.c#L456-L459 + if (toBranch.shorthand() !== "master") { + message += "' into " + toBranch.shorthand(); + } + return Promise.all([oid, processMergeMessageCallback(message)]); + }) + .then(function([oid, message]) { return repo.createCommit( toBranch.name(), signature, signature, message, oid, - [toCommitOid, fromCommitOid]); + [toCommitOid, fromCommitOid] + ); }) .then(function(commit) { // we've updated the checked out branch, so make sure we update diff --git a/test/tests/merge.js b/test/tests/merge.js index 3a63b0ced..5bf3cb81c 100644 --- a/test/tests/merge.js +++ b/test/tests/merge.js @@ -348,7 +348,7 @@ describe("Merge", function() { }) .then(function(oid) { assert.equal(oid.toString(), - "6806d22d2b6c0095b29dc5ec51829caeb67861f1"); + "65516eb7b20f51d275096cd28f132ff606a09e07"); return repository.getBranchCommit(ourBranchName) .then(function(branchCommit) { @@ -501,7 +501,7 @@ describe("Merge", function() { }) .then(function(commitId) { assert.equal(commitId.toString(), - "5384feb481d9c29081b3a0c1478fcc24a3953efa"); + "96d6f1d0704eb3ef9121a13348d17c1d672c28aa"); }) .then(function() { return repository.getStatus(); @@ -512,6 +512,320 @@ describe("Merge", function() { }); }); + it( + "can merge --no-ff a non-fast-forward using the convenience method " + + "with custom merge message via sync callback", + function() { + var initialFileName = "initialFile.txt"; + var ourFileName = "ourNewFile.txt"; + var theirFileName = "theirNewFile.txt"; + + var initialFileContent = "I'd like to drive somewhere"; + var ourFileContent = "I like Toll Roads. I have an EZ-Pass!"; + var theirFileContent = "I'm skeptical about Toll Roads"; + + var ourSignature = NodeGit.Signature.create + ("Ron Paul", "RonPaul@TollRoadsRBest.info", 123456789, 60); + var theirSignature = NodeGit.Signature.create + ("Greg Abbott", "Gregggg@IllTollYourFace.us", 123456789, 60); + + var repository = this.repository; + var initialCommit; + var ourCommit; + var theirCommit; + var ourBranch; + var theirBranch; + + return fse.writeFile( + path.join(repository.workdir(), initialFileName), + initialFileContent) + // Load up the repository index and make our initial commit to HEAD + .then(function() { + return repository.refreshIndex(); + }) + .then(function(index) { + return index.addByPath(initialFileName) + .then(function() { + return index.write(); + }) + .then(function() { + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "21a553813e2f670815b649eef51eeadb253a5d0c"); + + return repository.createCommit("HEAD", ourSignature, + ourSignature, "initial commit", oid, []); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "af66a9c799a10a23319ee4318c8bb2021521f539"); + + return repository.getCommit(commitOid).then(function(commit) { + initialCommit = commit; + }).then(function() { + return repository.createBranch(ourBranchName, commitOid) + .then(function(branch) { + ourBranch = branch; + return repository.createBranch(theirBranchName, commitOid); + }); + }); + }) + .then(function(branch) { + theirBranch = branch; + }) + .then(function() { + return fse.writeFile(path.join(repository.workdir(), ourFileName), + ourFileContent); + }) + .then(function() { + return repository.refreshIndex(); + }) + .then(function(index) { + return index.addByPath(ourFileName) + .then(function() { + return index.write(); + }) + .then(function() { + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "af60aa06b3537f75b427f6268a130c842c84a137"); + + return repository.createCommit(ourBranch.name(), ourSignature, + ourSignature, "we made a commit", oid, [initialCommit]); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "7ce31c05427659986d50abfb90c8f7db88ef4fa1"); + + return repository.getCommit(commitOid).then(function(commit) { + ourCommit = commit; + }); + }) + .then(function() { + return fse.writeFile(path.join(repository.workdir(), theirFileName), + theirFileContent); + }) + .then(function() { + return repository.refreshIndex(); + }) + .then(function(index) { + return index.addByPath(theirFileName) + .then(function() { + return index.write(); + }) + .then(function() { + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "f007361737a2ca00a0e80fc2daf55064463173b4"); + + return repository.createCommit(theirBranch.name(), theirSignature, + theirSignature, "they made a commit", oid, [initialCommit]); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "b588f0eef1809226f8f7db542940749da15ae1de"); + + return repository.getCommit(commitOid).then(function(commit) { + theirCommit = commit; + }); + }) + .then(function() { + var opts = {checkoutStrategy: NodeGit.Checkout.STRATEGY.FORCE}; + return repository.checkoutBranch(ourBranchName, opts); + }) + .then(function() { + return repository.mergeBranches( + ourBranchName, + theirBranchName, + ourSignature, + NodeGit.Merge.PREFERENCE.NO_FASTFORWARD, + null, + function(message) { + assert(message === "Merge branch 'theirs' into ours"); + return "We manipulated the message, HAH."; + } + ); + }) + .then(function(commitId) { + assert.equal(commitId.toString(), + "5b49a43be0ba95e7767dd9a2880bab4795c6db70"); + }) + .then(function() { + return repository.getStatus(); + }) + .then(function(statuses) { + // make sure we didn't change the index + assert.equal(statuses.length, 0); + }); + } + ); + + it( + "can merge --no-ff a non-fast-forward using the convenience method " + + "with custom merge message via async callback", + function() { + var initialFileName = "initialFile.txt"; + var ourFileName = "ourNewFile.txt"; + var theirFileName = "theirNewFile.txt"; + + var initialFileContent = "I'd like to drive somewhere"; + var ourFileContent = "I like Toll Roads. I have an EZ-Pass!"; + var theirFileContent = "I'm skeptical about Toll Roads"; + + var ourSignature = NodeGit.Signature.create + ("Ron Paul", "RonPaul@TollRoadsRBest.info", 123456789, 60); + var theirSignature = NodeGit.Signature.create + ("Greg Abbott", "Gregggg@IllTollYourFace.us", 123456789, 60); + + var repository = this.repository; + var initialCommit; + var ourCommit; + var theirCommit; + var ourBranch; + var theirBranch; + + return fse.writeFile( + path.join(repository.workdir(), initialFileName), + initialFileContent) + // Load up the repository index and make our initial commit to HEAD + .then(function() { + return repository.refreshIndex(); + }) + .then(function(index) { + return index.addByPath(initialFileName) + .then(function() { + return index.write(); + }) + .then(function() { + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "21a553813e2f670815b649eef51eeadb253a5d0c"); + + return repository.createCommit("HEAD", ourSignature, + ourSignature, "initial commit", oid, []); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "af66a9c799a10a23319ee4318c8bb2021521f539"); + + return repository.getCommit(commitOid).then(function(commit) { + initialCommit = commit; + }).then(function() { + return repository.createBranch(ourBranchName, commitOid) + .then(function(branch) { + ourBranch = branch; + return repository.createBranch(theirBranchName, commitOid); + }); + }); + }) + .then(function(branch) { + theirBranch = branch; + }) + .then(function() { + return fse.writeFile(path.join(repository.workdir(), ourFileName), + ourFileContent); + }) + .then(function() { + return repository.refreshIndex(); + }) + .then(function(index) { + return index.addByPath(ourFileName) + .then(function() { + return index.write(); + }) + .then(function() { + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "af60aa06b3537f75b427f6268a130c842c84a137"); + + return repository.createCommit(ourBranch.name(), ourSignature, + ourSignature, "we made a commit", oid, [initialCommit]); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "7ce31c05427659986d50abfb90c8f7db88ef4fa1"); + + return repository.getCommit(commitOid).then(function(commit) { + ourCommit = commit; + }); + }) + .then(function() { + return fse.writeFile(path.join(repository.workdir(), theirFileName), + theirFileContent); + }) + .then(function() { + return repository.refreshIndex(); + }) + .then(function(index) { + return index.addByPath(theirFileName) + .then(function() { + return index.write(); + }) + .then(function() { + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "f007361737a2ca00a0e80fc2daf55064463173b4"); + + return repository.createCommit(theirBranch.name(), theirSignature, + theirSignature, "they made a commit", oid, [initialCommit]); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "b588f0eef1809226f8f7db542940749da15ae1de"); + + return repository.getCommit(commitOid).then(function(commit) { + theirCommit = commit; + }); + }) + .then(function() { + var opts = {checkoutStrategy: NodeGit.Checkout.STRATEGY.FORCE}; + return repository.checkoutBranch(ourBranchName, opts); + }) + .then(function() { + return repository.mergeBranches( + ourBranchName, + theirBranchName, + ourSignature, + NodeGit.Merge.PREFERENCE.NO_FASTFORWARD, + null, + function(message) { + assert(message === "Merge branch 'theirs' into ours"); + return Promise.resolve("We manipulated the message, HAH."); + } + ); + }) + .then(function(commitId) { + assert.equal(commitId.toString(), + "5b49a43be0ba95e7767dd9a2880bab4795c6db70"); + }) + .then(function() { + return repository.getStatus(); + }) + .then(function(statuses) { + // make sure we didn't change the index + assert.equal(statuses.length, 0); + }); + } + ); + it("can merge --ff-only a fast-forward using the convenience method", function() { var ourFileName = "ourNewFile.txt"; @@ -906,7 +1220,7 @@ describe("Merge", function() { }) .then(function(commitId) { assert.equal(commitId.toString(), - "5384feb481d9c29081b3a0c1478fcc24a3953efa"); + "96d6f1d0704eb3ef9121a13348d17c1d672c28aa"); }); }); From 96e03dc887c6763c3bacc22364a67e9bbec387c5 Mon Sep 17 00:00:00 2001 From: tyler wanek Date: Fri, 10 Feb 2017 10:40:35 -0700 Subject: [PATCH 2/2] end quote comes after the shorthand --- lib/repository.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/repository.js b/lib/repository.js index 995beba94..0c4ac3c7a 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -1435,11 +1435,12 @@ Repository.prototype.mergeBranches = function( "Merge " + mergeDecorator + " '" + - fromBranch.shorthand(); + fromBranch.shorthand() + + "'"; // https://github.com/git/git/blob/master/builtin/fmt-merge-msg.c#L456-L459 if (toBranch.shorthand() !== "master") { - message += "' into " + toBranch.shorthand(); + message += " into " + toBranch.shorthand(); } return Promise.all([oid, processMergeMessageCallback(message)]);