From 2e5886601bd28c0dbfff70d1fb81e35687fb4a93 Mon Sep 17 00:00:00 2001 From: Maximiliano Korp Date: Fri, 21 Nov 2014 12:55:34 -0700 Subject: [PATCH 1/6] get a merge example up and running! --- example/merge.js | 104 +++++++++++++++++++++++++++++++ generate/descriptor.json | 23 +++++-- generate/libgit2-supplement.json | 19 ++++++ 3 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 example/merge.js diff --git a/example/merge.js b/example/merge.js new file mode 100644 index 000000000..52d8e9189 --- /dev/null +++ b/example/merge.js @@ -0,0 +1,104 @@ +var nodegit = require('../'); +var path = require('path'); +var Promise = require('nodegit-promise'); +var promisify = require('promisify-node'); +var fse = promisify(require('fs-extra')); +fse.ensureDir = promisify(fse.ensureDir); + +var normalizeOptions = require('../lib/util/normalize_options'); +var ourFileName = 'ourNewFile.txt'; +var ourFileContent = 'I like Toll Roads. I have an EZ-Pass!'; +var ourBranchName = "ours"; + +var theirFileName = 'theirNewFile.txt'; +var theirFileContent = "I'm skeptical about Toll Roads"; +var theirBranchName = "theirs"; + +var repoDir = '../../newRepo'; + +var repository; +var ourCommit; +var theirCommit; +var ourBranch; +var theirBranch; + +var ourSignature = nodegit.Signature.create("Ron Paul", "RonPaul@TollRoadsRBest.info", 123456789, 60); +var theirSignature = nodegit.Signature.create("Greg Abbott", "Gregggg@IllTollYourFace.us", 123456789, 60); + +fse.remove(path.resolve(__dirname, repoDir)) +.then(function() { + return fse.ensureDir(path.resolve(__dirname, repoDir)); +}) +.then(function() { + return nodegit.Repository.init(path.resolve(__dirname, repoDir), 0); +}) +.then(function(repo) { + repository = repo; + return fse.writeFile(path.join(repository.workdir(), ourFileName), ourFileContent); +}) +.then(function() { + return repository.openIndex(); +}) +.then(function(index) { + index.read(1); + index.addByPath(ourFileName); + index.write() + + return index.writeTree(); +}) +.then(function(oid) { + return repository.createCommit('HEAD', ourSignature, ourSignature, 'we made a commit', oid, []); +}).then(function(commitOid) { + + return repository.getCommit(commitOid).then(function(commit) { + ourCommit = commit; + }).then(function() { + return repository.createBranch(ourBranchName, commitOid).then(function(branch) { + ourBranch = branch; + return repository.createBranch(theirBranchName, commitOid); + }); + }); +}).then(function(branch) { + theirBranch = branch; + + return nodegit.Reference.lookup(repository, 'HEAD').then(function(head) { + return head.symbolicSetTarget(theirBranch.name(), theirSignature, ""); + }); +}).then(function() { + return fse.writeFile(path.join(repository.workdir(), theirFileName), theirFileContent); +}) +.then(function() { + return repository.openIndex(); +}) +.then(function(index) { + index.read(1); + index.addByPath(theirFileName); + index.write() + + return index.writeTree(); +}) +.then(function(oid) { + return repository.createCommit('HEAD', theirSignature, theirSignature, 'they made a commit', oid, [ourCommit]); +}) +.then(function(commitOid) { + return repository.getCommit(commitOid).then(function(commit) { + theirCommit = commit; + }).then(function() { + return nodegit.Reference.lookup(repository, 'HEAD').then(function(head) { + return head.symbolicSetTarget(ourBranch.name(), ourSignature, ""); + }); + }); +}) +.then(function() { + return nodegit.Merge.commits(repository, ourCommit, theirCommit, new nodegit.MergeOptions()); +}) +.then(function(index) { + index.write() + return index.writeTreeTo(repository); +}) +.then(function(oid) { + return repository.createCommit(ourBranch.name(), ourSignature, ourSignature, 'we merged their commit', oid, [ourCommit, theirCommit]); +}) +.done(function(commitId) { + console.log('New Commit: ', commitId); +}); diff --git a/generate/descriptor.json b/generate/descriptor.json index e52c66a9a..dd767bf55 100644 --- a/generate/descriptor.json +++ b/generate/descriptor.json @@ -630,9 +630,6 @@ "isOptional": true } } - }, - "git_index_write_tree_to": { - "ignore": true } } }, @@ -668,15 +665,19 @@ "git_merge_analysis": { "ignore": true }, - "git_merge_base": { - "ignore": true - }, "git_merge_base_many": { "ignore": true }, "git_merge_base_octopus": { "ignore": true }, + "git_merge_commits": { + "args": { + "opts": { + "isOptional": true + } + } + }, "git_merge_file": { "ignore": true }, @@ -1027,6 +1028,16 @@ }, "git_reference_next_name": { "ignore": true + }, + "git_reference_symbolic_set_target": { + "args": { + "signature": { + "isOptional": true + }, + "log_message": { + "isOptional": true + } + } } } }, diff --git a/generate/libgit2-supplement.json b/generate/libgit2-supplement.json index 72c36758d..693b2d865 100644 --- a/generate/libgit2-supplement.json +++ b/generate/libgit2-supplement.json @@ -261,6 +261,16 @@ "git_odb_object_size", "git_odb_object_type" ] + ], + [ + "merge_head", + [ + "git_merge_head_free", + "git_merge_head_from_fetchhead", + "git_merge_head_from_id", + "git_merge_head_from_ref", + "git_merge_head_id" + ] ] ] }, @@ -274,6 +284,15 @@ "git_odb_object_size", "git_odb_object_type" ] + }, + "merge": { + "functions": [ + "git_merge_head_free", + "git_merge_head_from_fetchhead", + "git_merge_head_from_id", + "git_merge_head_from_ref", + "git_merge_head_id" + ] } } } From 4c61589a47ad602e652aecbe6fca8fcfc33a7555 Mon Sep 17 00:00:00 2001 From: Maximiliano Korp Date: Fri, 21 Nov 2014 13:07:09 -0700 Subject: [PATCH 2/6] clean up and comment merge example --- example/merge.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/example/merge.js b/example/merge.js index 52d8e9189..f2ab808d1 100644 --- a/example/merge.js +++ b/example/merge.js @@ -5,7 +5,6 @@ var promisify = require('promisify-node'); var fse = promisify(require('fs-extra')); fse.ensureDir = promisify(fse.ensureDir); -var normalizeOptions = require('../lib/util/normalize_options'); var ourFileName = 'ourNewFile.txt'; var ourFileContent = 'I like Toll Roads. I have an EZ-Pass!'; var ourBranchName = "ours"; @@ -25,6 +24,7 @@ var theirBranch; var ourSignature = nodegit.Signature.create("Ron Paul", "RonPaul@TollRoadsRBest.info", 123456789, 60); var theirSignature = nodegit.Signature.create("Greg Abbott", "Gregggg@IllTollYourFace.us", 123456789, 60); +// Create a new repository in a clean directory, and add our first file fse.remove(path.resolve(__dirname, repoDir)) .then(function() { return fse.ensureDir(path.resolve(__dirname, repoDir)); @@ -36,6 +36,8 @@ fse.remove(path.resolve(__dirname, repoDir)) repository = repo; return fse.writeFile(path.join(repository.workdir(), ourFileName), ourFileContent); }) + +// Load up the repository index and make our initial commit to HEAD .then(function() { return repository.openIndex(); }) @@ -48,8 +50,10 @@ fse.remove(path.resolve(__dirname, repoDir)) }) .then(function(oid) { return repository.createCommit('HEAD', ourSignature, ourSignature, 'we made a commit', oid, []); -}).then(function(commitOid) { +}) +// Get commit object from the oid, and create our new branches at that position +.then(function(commitOid) { return repository.getCommit(commitOid).then(function(commit) { ourCommit = commit; }).then(function() { @@ -58,13 +62,11 @@ fse.remove(path.resolve(__dirname, repoDir)) return repository.createBranch(theirBranchName, commitOid); }); }); -}).then(function(branch) { - theirBranch = branch; +}) - return nodegit.Reference.lookup(repository, 'HEAD').then(function(head) { - return head.symbolicSetTarget(theirBranch.name(), theirSignature, ""); - }); -}).then(function() { +// Create a new file, stage it and commit it to our second branch +.then(function(branch) { + theirBranch = branch; return fse.writeFile(path.join(repository.workdir(), theirFileName), theirFileContent); }) .then(function() { @@ -78,27 +80,32 @@ fse.remove(path.resolve(__dirname, repoDir)) return index.writeTree(); }) .then(function(oid) { - return repository.createCommit('HEAD', theirSignature, theirSignature, 'they made a commit', oid, [ourCommit]); + // You don't have to change head to make a commit to a different branch. + return repository.createCommit(theirBranch.name(), theirSignature, theirSignature, 'they made a commit', oid, [ourCommit]); }) .then(function(commitOid) { return repository.getCommit(commitOid).then(function(commit) { theirCommit = commit; - }).then(function() { - return nodegit.Reference.lookup(repository, 'HEAD').then(function(head) { - return head.symbolicSetTarget(ourBranch.name(), ourSignature, ""); - }); }); }) + +// Merge the two commits .then(function() { - return nodegit.Merge.commits(repository, ourCommit, theirCommit, new nodegit.MergeOptions()); + return nodegit.Merge.commits(repository, ourCommit, theirCommit, null); }) + +// Merging returns an index that isn't backed by the repository. You have to write it to the repository, +// instead of just writing it. You have to manually check for merge conflicts, which will reject the promise .then(function(index) { index.write() return index.writeTreeTo(repository); }) + +// Create our merge commit back on our branch .then(function(oid) { return repository.createCommit(ourBranch.name(), ourSignature, ourSignature, 'we merged their commit', oid, [ourCommit, theirCommit]); }) .done(function(commitId) { + // We never changed the HEAD after the initial commit; it should still be the same as master. console.log('New Commit: ', commitId); }); From 7fd6152f22e32a80b33130cd47a29fc488813bee Mon Sep 17 00:00:00 2001 From: John Haley Date: Fri, 21 Nov 2014 15:44:36 -0700 Subject: [PATCH 3/6] Changed merge so that you don't have to pass null in for options --- example/merge.js | 2 +- lib/merge.js | 22 ++++++++++++++++++++++ lib/nodegit.js | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 lib/merge.js diff --git a/example/merge.js b/example/merge.js index f2ab808d1..a5fdea8e9 100644 --- a/example/merge.js +++ b/example/merge.js @@ -91,7 +91,7 @@ fse.remove(path.resolve(__dirname, repoDir)) // Merge the two commits .then(function() { - return nodegit.Merge.commits(repository, ourCommit, theirCommit, null); + return nodegit.Merge.commits(repository, ourCommit, theirCommit); }) // Merging returns an index that isn't backed by the repository. You have to write it to the repository, diff --git a/lib/merge.js b/lib/merge.js new file mode 100644 index 000000000..cd96c88ce --- /dev/null +++ b/lib/merge.js @@ -0,0 +1,22 @@ +var NodeGit = require("../"); +var normalizeOptions = require("./util/normalize_options"); + +var Merge = NodeGit.Merge; +var mergeCommits = Merge.commits; + +/** + * Merge 2 commits together and create an new index that can + * be used to create a merge commit. + * + * @param repo Repository that contains the given commits + * @param ourCommit The commit that reflects the destination tree + * @oaram theirCommit The commit to merge into ourCommit + * @param options The merge tree options (null for default) + */ +Merge.commits = function(repo, ourCommit, theirCommit, options) { + options = normalizeOptions(options, NodeGit.MergeOptions); + + return mergeCommits.call(this, repo, ourCommit, theirCommit, options); +}; + +module.exports = Merge; diff --git a/lib/nodegit.js b/lib/nodegit.js index 856f229e0..43760522c 100644 --- a/lib/nodegit.js +++ b/lib/nodegit.js @@ -50,6 +50,7 @@ require("./checkout"); require("./commit"); require("./diff"); require("./index"); +require("./merge"); require("./object"); require("./odb"); require("./odb_object"); From 7ae35d3efe49c192df202db47793bfb6a30c71ad Mon Sep 17 00:00:00 2001 From: Maximiliano Korp Date: Sat, 22 Nov 2014 01:03:27 -0700 Subject: [PATCH 4/6] merge with conflicts example partly done --- example/{merge.js => merge-cleanly.js} | 0 example/merge-with-conflicts.js | 177 +++++++++++++++++++++++++ 2 files changed, 177 insertions(+) rename example/{merge.js => merge-cleanly.js} (100%) create mode 100644 example/merge-with-conflicts.js diff --git a/example/merge.js b/example/merge-cleanly.js similarity index 100% rename from example/merge.js rename to example/merge-cleanly.js diff --git a/example/merge-with-conflicts.js b/example/merge-with-conflicts.js new file mode 100644 index 000000000..9b5a6060d --- /dev/null +++ b/example/merge-with-conflicts.js @@ -0,0 +1,177 @@ +var nodegit = require('../'); +var path = require('path'); +var Promise = require('nodegit-promise'); +var promisify = require('promisify-node'); +var fse = promisify(require('fs-extra')); +fse.ensureDir = promisify(fse.ensureDir); + +var repoDir = '../../newRepo'; +var fileName = 'newFile.txt'; + +var baseFileContent = 'All Bobs are created equal. ish.'; +var ourFileContent = "Big Bobs are best, IMHO."; +var theirFileContent = "Nobody expects the small Bobquisition!"; +var finalFileContent = "Big Bobs are beautiful, and the small are unexpected!"; + +var baseSignature = nodegit.Signature.create("Peaceful Bob", "justchill@bob.net", 123456789, 60); +var ourSignature = nodegit.Signature.create("Big Bob", "impressive@bob.net", 123456789, 60); +var theirSignature = nodegit.Signature.create("Small Bob", "underestimated@bob.net", 123456789, 60); + +var ourBranchName = "ours"; +var theirBranchName = "theirs"; + +var repository; +var baseCommit; +var baseCommitOid; +var ourCommit; +var theirCommit; +var ourBranch; +var theirBranch; + +// Create a new repository in a clean directory, and add our first file +fse.remove(path.resolve(__dirname, repoDir)) +.then(function() { + return fse.ensureDir(path.resolve(__dirname, repoDir)); +}) +.then(function() { + return nodegit.Repository.init(path.resolve(__dirname, repoDir), 0); +}) +.then(function(repo) { + repository = repo; + return fse.writeFile(path.join(repository.workdir(), fileName), baseFileContent); +}) + + +// Load up the repository index and make our initial commit to HEAD +.then(function() { + return repository.openIndex(); +}) +.then(function(index) { + index.read(1); + index.addByPath(fileName); + index.write() + + return index.writeTree(); +}) +.then(function(oid) { + return repository.createCommit('HEAD', baseSignature, baseSignature, 'bobs are all ok', oid, []); +}) +.then(function(commitOid) { + baseCommitOid = commitOid; + return repository.getCommit(commitOid).then(function(commit) { + baseCommit = commit; + }); +}) + + +// create our branches +.then(function() { + return repository.createBranch(ourBranchName, baseCommitOid).then(function(branch) { + ourBranch = branch; + }); +}) +.then(function() { + return repository.createBranch(theirBranchName, baseCommitOid).then(function(branch) { + theirBranch = branch; + }); +}) + + +// Write and commit our version of the file +.then(function() { + return fse.writeFile(path.join(repository.workdir(), fileName), ourFileContent); +}) +.then(function() { + return repository.openIndex().then(function(index) { + index.read(1); + index.addByPath(fileName); + index.write() + + console.log('OURS'); + for (var i = 0, l = index.entryCount(); i < l; i++) { + var entry = index.getByIndex(i); + console.log(entry.id().toString() + " " + entry.path()); + } + console.log('\n\n'); + + return index.writeTree(); + }); +}) +.then(function(oid) { + return repository.createCommit(ourBranch.name(), ourSignature, ourSignature, 'lol big bobs :yesway:', oid, [baseCommit]); +}) +.then(function(commitOid) { + return repository.getCommit(commitOid).then(function(commit) { + ourCommit = commit; + }); +}) + + +// Write and commit their version of the file +.then(function() { + return fse.writeFile(path.join(repository.workdir(), fileName), theirFileContent); +}) +.then(function() { + return repository.openIndex().then(function(index) { + index.read(1); + index.addByPath(fileName); + index.write() + console.log('Theirs'); + for (var i = 0, l = index.entryCount(); i < l; i++) { + var entry = index.getByIndex(i); + console.log(entry.id().toString() + " " + entry.path()); + } + console.log('\n\n'); + + return index.writeTree(); + }); +}) +.then(function(oid) { + return repository.createCommit(theirBranch.name(), theirSignature, theirSignature, 'lol big bobs :poop:', oid, [baseCommit]); +}) +.then(function(commitOid) { + return repository.getCommit(commitOid).then(function(commit) { + theirCommit = commit; + }); +}) + +// Merge their branch into our branch +.then(function() { + return nodegit.Merge.commits(repository, ourCommit, theirCommit, null); +}) + +// Merging returns an index that isn't backed by the repository. +// You have to write it to the repository instead of just writing it. +.then(function(index) { + return nodegit.Reference.lookup(repository, 'HEAD').then(function(head) { + return head.symbolicSetTarget(ourBranch.name(), ourSignature, ""); + }).then(function() { + if (index.hasConflicts) { + console.log('Conflict time!'); + for (var i = 0, l = index.entryCount(); i < l; i++) { + var entry = index.getByIndex(i); + console.log(entry.id().toString() + " " + entry.path()); + } + console.log('\n\n'); + //if the merge had comflicts, solve them (in this case, we simply overwrite the file) + fse.writeFileSync(path.join(repository.workdir(), fileName), finalFileContent); + // read the fs updates into the index + index.read(1); + + index.addByPath(fileName); + // mark all conflicts as resolved + index.conflictCleanup(); + // write back to the index + index.write(); + } + + return index.writeTreeTo(repository); + }); +}) +.then(function(oid) { + // create the new merge commit on our branch + return repository.createCommit(ourBranch.name(), baseSignature, baseSignature, 'Stop this bob sized fued', oid, [ourCommit, theirCommit]); +}) +.done(function(commitId) { + console.log('New Commit: ', commitId); +}); From 8d215c6e08a4d47eab4f41eaf3f4d90168112a57 Mon Sep 17 00:00:00 2001 From: Maximiliano Korp Date: Mon, 24 Nov 2014 11:31:56 -0700 Subject: [PATCH 5/6] merge examples now work! --- example/merge-cleanly.js | 15 +++++-- example/merge-with-conflicts.js | 72 +++++++++++++++------------------ 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/example/merge-cleanly.js b/example/merge-cleanly.js index a5fdea8e9..6fabc672d 100644 --- a/example/merge-cleanly.js +++ b/example/merge-cleanly.js @@ -89,18 +89,25 @@ fse.remove(path.resolve(__dirname, repoDir)) }); }) + // Merge the two commits .then(function() { return nodegit.Merge.commits(repository, ourCommit, theirCommit); }) -// Merging returns an index that isn't backed by the repository. You have to write it to the repository, -// instead of just writing it. You have to manually check for merge conflicts, which will reject the promise + +// Merging returns an index that isn't backed by the repository. +// You have to manually check for merge conflicts. If there are none +// you just have to write the index. You do have to write it to +// the repository instead of just writing it. .then(function(index) { - index.write() - return index.writeTreeTo(repository); + if (!index.hasConflicts()) { + index.write() + return index.writeTreeTo(repository); + } }) + // Create our merge commit back on our branch .then(function(oid) { return repository.createCommit(ourBranch.name(), ourSignature, ourSignature, 'we merged their commit', oid, [ourCommit, theirCommit]); diff --git a/example/merge-with-conflicts.js b/example/merge-with-conflicts.js index 9b5a6060d..652198b77 100644 --- a/example/merge-with-conflicts.js +++ b/example/merge-with-conflicts.js @@ -8,10 +8,10 @@ fse.ensureDir = promisify(fse.ensureDir); var repoDir = '../../newRepo'; var fileName = 'newFile.txt'; -var baseFileContent = 'All Bobs are created equal. ish.'; -var ourFileContent = "Big Bobs are best, IMHO."; -var theirFileContent = "Nobody expects the small Bobquisition!"; -var finalFileContent = "Big Bobs are beautiful, and the small are unexpected!"; +var baseFileContent = 'All Bobs are created equal. ish.\n'; +var ourFileContent = "Big Bobs are best, IMHO.\n"; +var theirFileContent = "Nobody expects the small Bobquisition!\n"; +var finalFileContent = "Big Bobs are beautiful, and the small are unexpected!\n"; var baseSignature = nodegit.Signature.create("Peaceful Bob", "justchill@bob.net", 123456789, 60); var ourSignature = nodegit.Signature.create("Big Bob", "impressive@bob.net", 123456789, 60); @@ -87,13 +87,6 @@ fse.remove(path.resolve(__dirname, repoDir)) index.addByPath(fileName); index.write() - console.log('OURS'); - for (var i = 0, l = index.entryCount(); i < l; i++) { - var entry = index.getByIndex(i); - console.log(entry.id().toString() + " " + entry.path()); - } - console.log('\n\n'); - return index.writeTree(); }); }) @@ -116,12 +109,6 @@ fse.remove(path.resolve(__dirname, repoDir)) index.read(1); index.addByPath(fileName); index.write() - console.log('Theirs'); - for (var i = 0, l = index.entryCount(); i < l; i++) { - var entry = index.getByIndex(i); - console.log(entry.id().toString() + " " + entry.path()); - } - console.log('\n\n'); return index.writeTree(); }); @@ -135,6 +122,15 @@ fse.remove(path.resolve(__dirname, repoDir)) }); }) + +// move the head to our branch, just to keep things tidy +.then(function() { + return nodegit.Reference.lookup(repository, 'HEAD').then(function(head) { + return head.symbolicSetTarget(ourBranch.name(), ourSignature, ""); + }) +}) + + // Merge their branch into our branch .then(function() { return nodegit.Merge.commits(repository, ourCommit, theirCommit, null); @@ -143,29 +139,25 @@ fse.remove(path.resolve(__dirname, repoDir)) // Merging returns an index that isn't backed by the repository. // You have to write it to the repository instead of just writing it. .then(function(index) { - return nodegit.Reference.lookup(repository, 'HEAD').then(function(head) { - return head.symbolicSetTarget(ourBranch.name(), ourSignature, ""); - }).then(function() { - if (index.hasConflicts) { - console.log('Conflict time!'); - for (var i = 0, l = index.entryCount(); i < l; i++) { - var entry = index.getByIndex(i); - console.log(entry.id().toString() + " " + entry.path()); - } - console.log('\n\n'); - //if the merge had comflicts, solve them (in this case, we simply overwrite the file) - fse.writeFileSync(path.join(repository.workdir(), fileName), finalFileContent); - // read the fs updates into the index - index.read(1); - - index.addByPath(fileName); - // mark all conflicts as resolved - index.conflictCleanup(); - // write back to the index - index.write(); - } - - return index.writeTreeTo(repository); + if (index.hasConflicts()) { + console.log('Conflict time!'); + + // if the merge had comflicts, solve them + // (in this case, we simply overwrite the file) + fse.writeFileSync(path.join(repository.workdir(), fileName), finalFileContent); + } +}) + +// we need to get a new index as the other one isnt backed to +// the repository in the usual fashion, and just behaves weirdly +.then(function() { + return repository.openIndex().then(function(index) { + + index.read(1); + index.addByPath(fileName); + index.write(); + + return index.writeTree(); }); }) .then(function(oid) { From dcc97ed01e66ddd12a1edb7dcbd1d65de99ea9ca Mon Sep 17 00:00:00 2001 From: Maximiliano Korp Date: Mon, 24 Nov 2014 13:56:02 -0700 Subject: [PATCH 6/6] merge tests! --- test/tests/merge.js | 287 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 test/tests/merge.js diff --git a/test/tests/merge.js b/test/tests/merge.js new file mode 100644 index 000000000..72b3d9c74 --- /dev/null +++ b/test/tests/merge.js @@ -0,0 +1,287 @@ +var assert = require("assert"); +var path = require("path"); +var promisify = require("promisify-node"); +var fse = promisify(require("fs-extra")); +fse.ensureDir = promisify(fse.ensureDir); + +describe("Merge", function() { + var nodegit = require("../../"); + + var repoDir = path.resolve("test/repos/merge"); + var ourBranchName = "ours"; + var theirBranchName = "theirs"; + + beforeEach(function() { + var test = this; + return fse.remove(path.resolve(__dirname, repoDir)) + .then(function() { + return fse.ensureDir(path.resolve(__dirname, repoDir)); + }) + .then(function() { + return nodegit.Repository.init(path.resolve(__dirname, repoDir), 0); + }) + .then(function(repo) { + test.repository = repo; + }); + }); + + it("can cleanly merge 2 files", function() { + var ourFileName = "ourNewFile.txt"; + var theirFileName = "theirNewFile.txt"; + + 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 ourCommit; + var theirCommit; + var ourBranch; + var theirBranch; + + return fse.writeFile(path.join(repository.workdir(), ourFileName), + ourFileContent) + // Load up the repository index and make our initial commit to HEAD + .then(function() { + return repository.openIndex() + .then(function(index) { + index.read(1); + index.addByPath(ourFileName); + index.write(); + + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "11ead82b1135b8e240fb5d61e703312fb9cc3d6a"); + + return repository.createCommit("HEAD", ourSignature, + ourSignature, "we made a commit", oid, []); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "91a183f87842ebb7a9b08dad8bc2473985796844"); + + return repository.getCommit(commitOid).then(function(commit) { + ourCommit = commit; + }).then(function() { + return repository.createBranch(ourBranchName, commitOid) + .then(function(branch) { + ourBranch = branch; + return repository.createBranch(theirBranchName, commitOid); + }); + }); + }) + .then(function(branch) { + theirBranch = branch; + return fse.writeFile(path.join(repository.workdir(), theirFileName), + theirFileContent); + }) + .then(function() { + return repository.openIndex() + .then(function(index) { + index.read(1); + index.addByPath(theirFileName); + index.write(); + + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "76631cb5a290dafe2959152626bb90f2a6d8ec94"); + + return repository.createCommit(theirBranch.name(), theirSignature, + theirSignature, "they made a commit", oid, [ourCommit]); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "0e9231d489b3f4303635fc4b0397830da095e7e7"); + + return repository.getCommit(commitOid).then(function(commit) { + theirCommit = commit; + }); + }) + .then(function() { + return nodegit.Merge.commits(repository, ourCommit, theirCommit); + }) + .then(function(index) { + assert(!index.hasConflicts()); + index.write(); + return index.writeTreeTo(repository); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "76631cb5a290dafe2959152626bb90f2a6d8ec94"); + + return repository.createCommit(ourBranch.name(), ourSignature, + ourSignature, "we merged their commit", oid, + [ourCommit, theirCommit]); + }) + .then(function(commitId) { + assert.equal(commitId.toString(), + "eedee554af34dd4001d8abc799cb55bb7e56a58b"); + }); + }); + + it("can merge 2 branchs with conflicts on a single file", function () { + var baseFileContent = "All Bobs are created equal. ish.\n"; + var ourFileContent = "Big Bobs are best, IMHO.\n"; + var theirFileContent = "Nobody expects the small Bobquisition!\n"; + var finalFileContent = + "Big Bobs are beautiful, and the small are unexpected!\n"; + + var baseSignature = nodegit.Signature.create + ("Peaceful Bob", "justchill@bob.net", 123456789, 60); + var ourSignature = nodegit.Signature.create + ("Big Bob", "impressive@bob.net", 123456789, 60); + var theirSignature = nodegit.Signature.create + ("Small Bob", "underestimated@bob.net", 123456789, 60); + + var repository = this.repository; + var baseCommit; + var baseCommitOid; + var ourCommit; + var theirCommit; + var ourBranch; + var theirBranch; + var fileName = "newFile.txt"; + + return fse.writeFile(path.join(repository.workdir(), fileName), + baseFileContent) + .then(function() { + return repository.openIndex() + .then(function(index) { + index.read(1); + index.addByPath(fileName); + index.write(); + + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "ea2f6521fb8097a881f43796946ac1603e1f4d75"); + + return repository.createCommit("HEAD", baseSignature, + baseSignature, "bobs are all ok", oid, []); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "a9b202f7612273fb3a68f718304298704eaeb735"); + baseCommitOid = commitOid; + + return repository.getCommit(commitOid).then(function(commit) { + baseCommit = commit; + }); + }) + .then(function() { + return repository.createBranch(ourBranchName, baseCommitOid) + .then(function(branch) { + ourBranch = branch; + }); + }) + .then(function() { + return repository.createBranch(theirBranchName, baseCommitOid) + .then(function(branch) { + theirBranch = branch; + }); + }) + .then(function() { + return fse.writeFile(path.join(repository.workdir(), fileName), + ourFileContent); + }) + .then(function() { + return repository.openIndex().then(function(index) { + index.read(1); + index.addByPath(fileName); + index.write(); + + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "c39b1e38b09085856cec7e7ff33e90f5a537d8a5"); + + return repository.createCommit(ourBranch.name(), ourSignature, + ourSignature, "lol big bobs :yesway:", oid, [baseCommit]); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "935a89c09ad757a9dde2c0257f6f1e379f71816f"); + + return repository.getCommit(commitOid).then(function(commit) { + ourCommit = commit; + }); + }) + .then(function() { + return fse.writeFile(path.join(repository.workdir(), fileName), + theirFileContent); + }) + .then(function() { + return repository.openIndex().then(function(index) { + index.read(1); + index.addByPath(fileName); + index.write(); + + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "d1a894a9a4a8c820eb66c82cdd7e6b76c8f713cb"); + + return repository.createCommit(theirBranch.name(), theirSignature, + theirSignature, "lol big bobs :poop:", oid, [baseCommit]); + }) + .then(function(commitOid) { + assert.equal(commitOid.toString(), + "bebb9ec2e0684c7cb7c1e1601c7d5a8f52b8b123"); + + return repository.getCommit(commitOid).then(function(commit) { + theirCommit = commit; + }); + }) + .then(function() { + return nodegit.Reference.lookup(repository, "HEAD") + .then(function(head) { + return head.symbolicSetTarget(ourBranch.name(), ourSignature, ""); + }); + }) + .then(function() { + return nodegit.Merge.commits(repository, ourCommit, theirCommit, null); + }) + .then(function(index) { + assert(index.hasConflicts()); + fse.writeFileSync(path.join(repository.workdir(), fileName), + finalFileContent); + }) + .then(function() { + return repository.openIndex().then(function(index) { + index.read(1); + index.addByPath(fileName); + index.write(); + + return index.writeTree(); + }); + }) + .then(function(oid) { + assert.equal(oid.toString(), + "b1cd49a27cd33b99ab6dad2fb82b3174812a8b47"); + + return repository.createCommit(ourBranch.name(), baseSignature, + baseSignature, "Stop this bob sized fued", oid, + [ourCommit, theirCommit]); + }) + .then(function(commitId) { + assert.equal(commitId.toString(), + "49014ccabf5125f9b69316acde36f891dfdb8b5c"); + }); + }); +});