diff --git a/build/bundle.js b/build/bundle.js index 4d0b9489..dcd12f3d 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -5387,7 +5387,7 @@ GitEngine.prototype.exportTree = function() { GitEngine.prototype.printTree = function(tree) { tree = tree || this.exportTree(); - TreeCompare.prototype.stripTreeFields([tree]); + TreeCompare.prototype.reduceTreeFields([tree]); var str = JSON.stringify(tree); if (/'/.test(str)) { @@ -7333,7 +7333,7 @@ TreeCompare.prototype.compareBranchWithinTrees = function(treeA, treeB, branchNa treeA = this.convertTreeSafe(treeA); treeB = this.convertTreeSafe(treeB); - this.stripTreeFields([treeA, treeB]); + this.reduceTreeFields([treeA, treeB]); // we need a recursive comparison function to bubble up the branch var recurseCompare = function(commitA, commitB) { @@ -7371,39 +7371,49 @@ TreeCompare.prototype.convertTreeSafe = function(tree) { return tree; }; -TreeCompare.prototype.stripTreeFields = function(trees) { - var commitStripFields = [ - 'createTime', - 'author', - 'commitMessage', - 'gitVisuals', - 'children', - 'visNode', - 'type' - ]; - var branchStripFields = [ - 'type', - 'visBranch' +TreeCompare.prototype.reduceTreeFields = function(trees) { + var commitSaveFields = [ + 'parents', + 'id', + 'rootCommit' ]; var commitSortFields = ['children', 'parents']; + var branchSaveFields = [ + 'target', + 'id' + ]; - var strip = function(objects, stripFields, sortFields) { - _.each(objects, function(obj) { - _.each(stripFields, function(field) { - delete obj[field]; - }); - _.each(sortFields, function(field) { - if (obj[field]) { - obj[field].sort(); + // this function saves only the specified fields of a tree + var saveOnly = function(tree, treeKey, saveFields, sortFields) { + var objects = tree[treeKey]; + _.each(objects, function(obj, objKey) { + // our blank slate to copy over + var blank = {}; + _.each(saveFields, function(field) { + if (obj[field] !== undefined) { + blank[field] = obj[field]; } }); + + _.each(sortFields, function(field) { + // also sort some fields + if (obj[field]) { + obj[field].sort(); + blank[field] = obj[field]; + } + }); + tree[treeKey][objKey] = blank; }); }; _.each(trees, function(tree) { - strip(tree.commits, commitStripFields, commitSortFields); - strip(tree.branches, branchStripFields); - strip([tree.HEAD], branchStripFields); + saveOnly(tree, 'commits', commitSaveFields, commitSortFields); + saveOnly(tree, 'branches', branchSaveFields); + + tree.HEAD = { + target: tree.HEAD.target, + id: tree.HEAD.id + }; }); }; @@ -7413,7 +7423,7 @@ TreeCompare.prototype.compareTrees = function(treeA, treeB) { // now we need to strip out the fields we don't care about, aka things // like createTime, message, author - this.stripTreeFields([treeA, treeB]); + this.reduceTreeFields([treeA, treeB]); console.log('comparing tree A', treeA, 'to', treeB); @@ -9767,7 +9777,7 @@ GitEngine.prototype.exportTree = function() { GitEngine.prototype.printTree = function(tree) { tree = tree || this.exportTree(); - TreeCompare.prototype.stripTreeFields([tree]); + TreeCompare.prototype.reduceTreeFields([tree]); var str = JSON.stringify(tree); if (/'/.test(str)) { @@ -11346,7 +11356,7 @@ TreeCompare.prototype.compareBranchWithinTrees = function(treeA, treeB, branchNa treeA = this.convertTreeSafe(treeA); treeB = this.convertTreeSafe(treeB); - this.stripTreeFields([treeA, treeB]); + this.reduceTreeFields([treeA, treeB]); // we need a recursive comparison function to bubble up the branch var recurseCompare = function(commitA, commitB) { @@ -11384,39 +11394,49 @@ TreeCompare.prototype.convertTreeSafe = function(tree) { return tree; }; -TreeCompare.prototype.stripTreeFields = function(trees) { - var commitStripFields = [ - 'createTime', - 'author', - 'commitMessage', - 'gitVisuals', - 'children', - 'visNode', - 'type' - ]; - var branchStripFields = [ - 'type', - 'visBranch' +TreeCompare.prototype.reduceTreeFields = function(trees) { + var commitSaveFields = [ + 'parents', + 'id', + 'rootCommit' ]; var commitSortFields = ['children', 'parents']; + var branchSaveFields = [ + 'target', + 'id' + ]; - var strip = function(objects, stripFields, sortFields) { - _.each(objects, function(obj) { - _.each(stripFields, function(field) { - delete obj[field]; - }); - _.each(sortFields, function(field) { - if (obj[field]) { - obj[field].sort(); + // this function saves only the specified fields of a tree + var saveOnly = function(tree, treeKey, saveFields, sortFields) { + var objects = tree[treeKey]; + _.each(objects, function(obj, objKey) { + // our blank slate to copy over + var blank = {}; + _.each(saveFields, function(field) { + if (obj[field] !== undefined) { + blank[field] = obj[field]; } }); + + _.each(sortFields, function(field) { + // also sort some fields + if (obj[field]) { + obj[field].sort(); + blank[field] = obj[field]; + } + }); + tree[treeKey][objKey] = blank; }); }; _.each(trees, function(tree) { - strip(tree.commits, commitStripFields, commitSortFields); - strip(tree.branches, branchStripFields); - strip([tree.HEAD], branchStripFields); + saveOnly(tree, 'commits', commitSaveFields, commitSortFields); + saveOnly(tree, 'branches', branchSaveFields); + + tree.HEAD = { + target: tree.HEAD.target, + id: tree.HEAD.id + }; }); }; @@ -11426,7 +11446,7 @@ TreeCompare.prototype.compareTrees = function(treeA, treeB) { // now we need to strip out the fields we don't care about, aka things // like createTime, message, author - this.stripTreeFields([treeA, treeB]); + this.reduceTreeFields([treeA, treeB]); console.log('comparing tree A', treeA, 'to', treeB); diff --git a/spec/git.spec.js b/spec/git.spec.js index 380f8e25..31890f2c 100644 --- a/spec/git.spec.js +++ b/spec/git.spec.js @@ -14,18 +14,96 @@ var compareAnswer = function(headless, expectedJSON) { expect(equal).toBe(true); }; -var expectCommandToProduceTree = function(command, expectedJSON) { +var expectTree = function(command, expectedJSON) { var headless = new HeadlessGit(); headless.sendCommand(command); compareAnswer(headless, expectedJSON); }; describe('GitEngine', function() { - it('Should commit off of head', function() { - - expectCommandToProduceTree( + it('Commits', function() { + expectTree( 'git commit', '{"branches":{"master":{"target":"C2","id":"master","type":"branch"}},"commits":{"C0":{"type":"commit","parents":[],"id":"C0","rootCommit":true},"C1":{"type":"commit","parents":["C0"],"id":"C1"},"C2":{"type":"commit","parents":["C1"],"id":"C2"}},"HEAD":{"id":"HEAD","target":"master","type":"general ref"}}' ); }); + + it('Checkouts', function() { + expectTree( + 'git checkout -b side', + '{"branches":{"master":{"target":"C1","id":"master"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}' + ); + }); + + it('Rebases', function() { + expectTree( + 'gc; git checkout -b side C1; gc; git rebase master', + '%7B%22branches%22%3A%7B%22master%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22master%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22side%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22side%22%2C%22id%22%3A%22HEAD%22%7D%7D' + ); + }); + + it('Reverts', function() { + expectTree( + 'git revert HEAD', + '%7B%22branches%22%3A%7B%22master%22%3A%7B%22target%22%3A%22C1%27%22%2C%22id%22%3A%22master%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C1%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22master%22%2C%22id%22%3A%22HEAD%22%7D%7D' + ); + }); + + it('Merges', function() { + expectTree( + 'gc; git checkout -b side C1; gc; git merge master', + '{"branches":{"master":{"target":"C2","id":"master"},"side":{"target":"C4","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"}},"HEAD":{"target":"side","id":"HEAD"}}' + ); + }); + + it('Resets', function() { + expectTree( + 'git commit; git reset HEAD~1', + '{"branches":{"master":{"target":"C1","id":"master"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"master","id":"HEAD"}}' + ); + }); + + it('Branches', function() { + expectTree( + 'git branch side C0', + '{"branches":{"master":{"target":"C1","id":"master"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"}}' + ); + }); + + it('Deletes branches', function() { + expectTree( + 'git branch side; git branch -d side', + '{"branches":{"master":{"target":"C1","id":"master"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"}}' + ); + }); + + it('Ammends commits', function() { + expectTree( + 'git commit --amend', + '%7B%22branches%22%3A%7B%22master%22%3A%7B%22target%22%3A%22C1%27%22%2C%22id%22%3A%22master%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22master%22%2C%22id%22%3A%22HEAD%22%7D%7D' + ); + }); + + it('Cherry picks', function() { + expectTree( + 'git checkout -b side C0; gc; git cherry-pick C1', + '%7B%22branches%22%3A%7B%22master%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22master%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C1%27%22%2C%22id%22%3A%22side%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C1%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22side%22%2C%22id%22%3A%22HEAD%22%7D%7D' + ); + }); + + it('Forces branches', function() { + expectTree( + 'git checkout -b side; git branch -f side C0', + '{"branches":{"master":{"target":"C1","id":"master"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}' + ); + }); + + it('Rebases only new commits to destination', function() { + expectTree( + 'git checkout -b side C0; gc; gc;git cherry-pick C1;git rebase master', + '%7B%22branches%22%3A%7B%22master%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22master%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22side%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C1%27%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%27%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%27%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22side%22%2C%22id%22%3A%22HEAD%22%7D%7D' + ); + }); + }); + diff --git a/src/js/git/headless.js b/src/js/git/headless.js index 7252592c..3d133703 100644 --- a/src/js/git/headless.js +++ b/src/js/git/headless.js @@ -49,7 +49,7 @@ HeadlessGit.prototype.sendCommand = function(value) { var commandObj = new Command({ rawStr: commandStr }); - console.log('dispatching command', value); + console.log('dispatching command "', commandStr, '"'); var done = function() {}; this.gitEngine.dispatch(commandObj, done); }, this); diff --git a/src/js/git/index.js b/src/js/git/index.js index 65d0d473..289fad09 100644 --- a/src/js/git/index.js +++ b/src/js/git/index.js @@ -17,14 +17,6 @@ var Errors = require('../util/errors'); var GitError = Errors.GitError; var CommandResult = Errors.CommandResult; -// backbone or something uses _.uniqueId, so we make our own here -var uniqueId = (function() { - var n = 0; - return function(prepend) { - return prepend? prepend + n++ : n++; - }; -})(); - function GitEngine(options) { this.rootCommit = null; this.refs = {}; @@ -43,6 +35,14 @@ function GitEngine(options) { this.generalArgs = []; this.events.on('processCommand', _.bind(this.dispatch, this)); + + // backbone or something uses _.uniqueId, so we make our own here + this.uniqueId = (function() { + var n = 0; + return function(prepend) { + return prepend? prepend + n++ : n++; + }; + })(); } GitEngine.prototype.defaultInit = function() { @@ -110,7 +110,7 @@ GitEngine.prototype.exportTree = function() { GitEngine.prototype.printTree = function(tree) { tree = tree || this.exportTree(); - TreeCompare.prototype.stripTreeFields([tree]); + TreeCompare.prototype.reduceTreeFields([tree]); var str = JSON.stringify(tree); if (/'/.test(str)) { @@ -357,9 +357,9 @@ GitEngine.prototype.makeCommit = function(parents, id, options) { // people like nikita (thanks for finding this!) could // make branches named C2 before creating the commit C2 if (!id) { - id = uniqueId('C'); + id = this.uniqueId('C'); while (this.refs[id]) { - id = uniqueId('C'); + id = this.uniqueId('C'); } }