diff --git a/spec/remote.spec.js b/spec/remote.spec.js index a0cbbd83..942db6ee 100644 --- a/spec/remote.spec.js +++ b/spec/remote.spec.js @@ -137,14 +137,14 @@ describe('Git Remotes', function() { it('can push with colon refspec', function() { expectTreeAsync( - 'git clone; gc; git checkout -b foo HEAD~1; git push master:master', + 'git clone; gc; git checkout -b foo HEAD~1; git push origin master:master', '{"branches":{"master":{"target":"C2","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C2","id":"o/master","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"foo","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C2","id":"master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"master","id":"HEAD"}}}' ); }); it('can delete branches with colon refspec', function() { expectTreeAsync( - 'git branch foo; git clone; git push :foo', + 'git branch foo; git clone; git push origin :foo', '{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"}}}' ); }); @@ -165,21 +165,21 @@ describe('Git Remotes', function() { it('does push for HEAD as a source though to a new branch', function() { expectTreeAsync( - 'git clone; git commit; git checkout C2; git push HEAD:foo', + 'git clone; git commit; git checkout C2; git push origin HEAD:foo', '{"branches":{"master":{"target":"C2","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null},"o/foo":{"target":"C2","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"C2","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"master","id":"HEAD"}}}' ); }); it('but it cant delete master on remote', function() { expectTreeAsync( - 'git branch foo; git clone; git push :master', + 'git branch foo; git clone; git push origin :master', '{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":"o/foo"},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"}}}' ); }); it('will prune the origin tree when deleting branches', function() { expectTreeAsync( - 'git checkout -b foo; git commit; git clone; git push :foo', + 'git checkout -b foo; git commit; git clone; git push origin :foo', '{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"foo","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"}}}' ); }); @@ -270,7 +270,7 @@ describe('Git Remotes', function() { it('pulls to the right branch and destination', function() { expectTreeAsync( - 'git clone; git checkout -b side o/master;git fakeTeamwork;git pull master:o/master', + 'git clone; git checkout -b side o/master;git fakeTeamwork;git pull origin master:o/master', '{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C2","id":"o/master","remoteTrackingBranchID":null},"side":{"target":"C2","id":"side","remoteTrackingBranchID":"o/master"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"side","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C2","id":"master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"master","id":"HEAD"}}}' ); }); @@ -285,35 +285,35 @@ describe('Git Remotes', function() { it('correctly resolves source during git fetch with params', function() { expectTreeAsync( - 'git clone; git push master:foo; git fakeTeamwork foo 2; git fetch origin foo^:blah', + 'git clone; git push origin master:foo; git fakeTeamwork foo 2; git fetch origin foo^:blah', '{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null},"blah":{"target":"C2","id":"blah","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"master","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null},"foo":{"target":"C3","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"foo","id":"HEAD"}}}' ); }); it('correctly makes a new branch during fetch despite nothing to download', function() { expectTreeAsync( - 'git clone; git push master:foo', + 'git clone; git push origin master:foo', '{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"}}}' ); }); it('correctly resolves existing commits and upates', function() { expectTreeAsync( - 'git clone; git push master:foo; git fakeTeamwork foo 2; git fetch origin foo^:blah;go C0; git fetch origin foo^:master', + 'git clone; git push origin master:foo; git fakeTeamwork foo 2; git fetch origin foo^:blah;go C0; git fetch origin foo^:master', '{"branches":{"master":{"target":"C2","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null},"blah":{"target":"C2","id":"blah","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"C0","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null},"foo":{"target":"C3","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"foo","id":"HEAD"}}}' ); }); it('doesnt let you fetch to master if you are checked out there', function() { expectTreeAsync( - 'git clone; git push master:foo; git fakeTeamwork foo 2; git fetch origin foo^:blah; git fetch foo:master', + 'git clone; git push origin master:foo; git fakeTeamwork foo 2; git fetch origin foo^:blah; git fetch foo:master', '{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null},"blah":{"target":"C2","id":"blah","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"master","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null},"foo":{"target":"C3","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"foo","id":"HEAD"}}}' ); }); it('doesnt let you delete branches that dont exist', function() { expectTreeAsync( - 'git clone; git push :foo', + 'git clone; git push origin :foo', '{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"master","id":"HEAD"}}}' ); }); diff --git a/src/js/git/commands.js b/src/js/git/commands.js index 013f86bf..57a66043 100644 --- a/src/js/git/commands.js +++ b/src/js/git/commands.js @@ -72,6 +72,9 @@ var assertIsRemoteBranch = function(engine, ref) { }; var assertOriginSpecified = function(generalArgs) { + if (!generalArgs.length) { + return; + } if (generalArgs[0] !== 'origin') { throw new GitError({ msg: intl.todo( @@ -208,7 +211,7 @@ var commandConfig = { var commandOptions = command.getOptionsMap(); var generalArgs = command.getGeneralArgs(); - command.twoArgsImpliedOrigin(generalArgs); + command.twoArgsForOrigin(generalArgs); assertOriginSpecified(generalArgs); // here is the deal -- git pull is pretty complex with // the arguments it wants. You can @@ -331,7 +334,7 @@ var commandConfig = { var source; var destination; var generalArgs = command.getGeneralArgs(); - command.twoArgsImpliedOrigin(generalArgs); + command.twoArgsForOrigin(generalArgs); assertOriginSpecified(generalArgs); var firstArg = generalArgs[1]; @@ -677,7 +680,7 @@ var commandConfig = { // git push is pretty complex in terms of // the arguments it wants as well... get ready! var generalArgs = command.getGeneralArgs(); - command.twoArgsImpliedOrigin(generalArgs); + command.twoArgsForOrigin(generalArgs); assertOriginSpecified(generalArgs); var firstArg = generalArgs[1]; diff --git a/src/js/models/commandModel.js b/src/js/models/commandModel.js index 2e93f6ca..d565d69e 100644 --- a/src/js/models/commandModel.js +++ b/src/js/models/commandModel.js @@ -140,11 +140,8 @@ var Command = Backbone.Model.extend({ } }, - twoArgsImpliedOrigin: function(args) { + twoArgsForOrigin: function(args) { this.validateArgBounds(args, 0, 2); - if (args.length < 2) { - args.unshift('origin'); - } }, // this is a little utility class to help arg validation that happens over and over again diff --git a/src/levels/index.js b/src/levels/index.js index a168d41c..727945f7 100644 --- a/src/levels/index.js +++ b/src/levels/index.js @@ -42,7 +42,8 @@ exports.levelSequences = { require('./remote/tracking').level, require('./remote/pushArgs').level, require('./remote/pushArgs2').level, - require('./remote/fetchArgs').level + require('./remote/fetchArgs').level, + require('./remote/sourceNothing').level ] }; diff --git a/src/levels/remote/sourceNothing.js b/src/levels/remote/sourceNothing.js new file mode 100644 index 00000000..12161090 --- /dev/null +++ b/src/levels/remote/sourceNothing.js @@ -0,0 +1,66 @@ +exports.level = { + "goalTreeString": "{\"branches\":{\"master\":{\"target\":\"C1\",\"id\":\"master\",\"remoteTrackingBranchID\":\"o/master\"},\"o/master\":{\"target\":\"C1\",\"id\":\"o/master\",\"remoteTrackingBranchID\":null},\"bar\":{\"target\":\"C1\",\"id\":\"bar\",\"remoteTrackingBranchID\":null}},\"commits\":{\"C0\":{\"parents\":[],\"id\":\"C0\",\"rootCommit\":true},\"C1\":{\"parents\":[\"C0\"],\"id\":\"C1\"}},\"HEAD\":{\"target\":\"master\",\"id\":\"HEAD\"},\"originTree\":{\"branches\":{\"master\":{\"target\":\"C1\",\"id\":\"master\",\"remoteTrackingBranchID\":null}},\"commits\":{\"C0\":{\"parents\":[],\"id\":\"C0\",\"rootCommit\":true},\"C1\":{\"parents\":[\"C0\"],\"id\":\"C1\"}},\"HEAD\":{\"target\":\"master\",\"id\":\"HEAD\"}}}", + "solutionCommand": "git push origin :foo;git fetch origin :bar", + "startTree": "{\"branches\":{\"master\":{\"target\":\"C1\",\"id\":\"master\",\"remoteTrackingBranchID\":\"o/master\"},\"o/master\":{\"target\":\"C1\",\"id\":\"o/master\",\"remoteTrackingBranchID\":null},\"o/foo\":{\"target\":\"C1\",\"id\":\"o/foo\",\"remoteTrackingBranchID\":null}},\"commits\":{\"C0\":{\"parents\":[],\"id\":\"C0\",\"rootCommit\":true},\"C1\":{\"parents\":[\"C0\"],\"id\":\"C1\"}},\"HEAD\":{\"target\":\"master\",\"id\":\"HEAD\"},\"originTree\":{\"branches\":{\"master\":{\"target\":\"C1\",\"id\":\"master\",\"remoteTrackingBranchID\":null},\"foo\":{\"target\":\"C1\",\"id\":\"foo\",\"remoteTrackingBranchID\":null}},\"commits\":{\"C0\":{\"parents\":[],\"id\":\"C0\",\"rootCommit\":true},\"C1\":{\"parents\":[\"C0\"],\"id\":\"C1\"}},\"HEAD\":{\"target\":\"master\",\"id\":\"HEAD\"}}}", + "name": { + "en_US": "Source of nothing" + }, + "hint": { + "en_US": "The branch command is disabled for this level so you'll have to use fetch!" + }, + "startDialog": { + "en_US": { + "childViews": [ + { + "type": "ModalAlert", + "options": { + "markdowns": [ + "### Oddities of ``", + "", + "Git abuses the `` parameter in two weird ways. These two abuses come from the fact that you can technically specify \"nothing\" as a valid `source` for both git push and git fetch. The way you specify nothing is via an empty argument:", + "", + "* `git push origin :side`", + "* `git fetch origin :bugFix`", + "", + "Let's see what these do..." + ] + } + }, + { + "type": "GitDemonstrationView", + "options": { + "beforeMarkdowns": [ + "What does pushing \"nothing\" to a remote branch do? It deletes it!" + ], + "afterMarkdowns": [ + "There, we successfully deleted the `foo` branch on remote by pushing the concept of \"nothing\" to it. That kinda makes sense..." + ], + "command": "git push origin :foo", + "beforeCommand": "git clone; git push origin master:foo" + } + }, + { + "type": "GitDemonstrationView", + "options": { + "beforeMarkdowns": [ + "Finally, fetching \"nothing\" to a place locally actually makes a new branch" + ], + "afterMarkdowns": [ + "Very odd / bizarre, but whatever. That's git for you!" + ], + "command": "git fetch origin :bar", + "beforeCommand": "git clone" + } + }, + { + "type": "ModalAlert", + "options": { + "markdowns": [ + "This is a quick level -- just delete one remote branch and create a new branch with `git fetch` to finish!" + ] + } + } + ] + } + } +};