diff --git a/spec/remote.spec.js b/spec/remote.spec.js index 942db6ee..ce670389 100644 --- a/spec/remote.spec.js +++ b/spec/remote.spec.js @@ -318,5 +318,26 @@ describe('Git Remotes', function() { ); }); + it('pulls to a new branch and then merges in that branch', function() { + expectTreeAsync( + 'git clone; git fakeTeamwork; git commit; git pull origin master:bar', + '{"branches":{"master":{"target":"C4","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C1","id":"o/master","remoteTrackingBranchID":null},"bar":{"target":"C2","id":"bar","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C3":{"parents":["C1"],"id":"C3"},"C2":{"parents":["C1"],"id":"C2"},"C4":{"parents":["C2","C3"],"id":"C4"}},"HEAD":{"target":"master","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('makes a new branch from pull and doesnt bork', function() { + expectTreeAsync( + 'git clone; git pull origin :bar', + '{"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"}}}' + ); + }); + + it('makes the new branch on push in the right place', function() { + expectTreeAsync( + 'git clone; git fakeTeamwork; 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":"C2","id":"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":"master","id":"HEAD"}}}' + ); + }); + }); diff --git a/src/js/git/commands.js b/src/js/git/commands.js index 57a66043..6406f747 100644 --- a/src/js/git/commands.js +++ b/src/js/git/commands.js @@ -78,7 +78,7 @@ var assertOriginSpecified = function(generalArgs) { if (generalArgs[0] !== 'origin') { throw new GitError({ msg: intl.todo( - generalArgs[0] + ' is not a remote in your repository! try origin' + generalArgs[0] + ' is not a remote in your repository! try adding origin that argument' ) }); } diff --git a/src/js/git/index.js b/src/js/git/index.js index 4c7cfb8f..e58c2383 100644 --- a/src/js/git/index.js +++ b/src/js/git/index.js @@ -394,8 +394,11 @@ GitEngine.prototype.makeBranchIfNeeded = function(branchName) { if (this.refs[branchName]) { return; } + var where = this.findCommonAncestorForRemote( + this.getCommitFromRef('HEAD').get('id') + ); - return this.validateAndMakeBranch(branchName, this.getCommitFromRef('HEAD')); + return this.validateAndMakeBranch(branchName, this.getCommitFromRef(where)); }; GitEngine.prototype.makeRemoteBranchForRemote = function(branchName) { @@ -409,6 +412,15 @@ GitEngine.prototype.makeRemoteBranchForRemote = function(branchName) { ); }; +GitEngine.prototype.findCommonAncestorForRemote = function(myTarget) { + // like the method below but opposite + while (!this.origin.refs[myTarget]) { + var parents = this.refs[myTarget].get('parents'); + myTarget = parents[0].get('id'); + } + return myTarget; +}; + GitEngine.prototype.findCommonAncestorWithRemote = function(originTarget) { // now this is tricky -- our remote could have commits that we do // not have. so lets go upwards until we find one that we have @@ -429,10 +441,12 @@ GitEngine.prototype.makeBranchOnOriginAndTrack = function(branchName, target) { this.setLocalToTrackRemote(this.refs[branchName], remoteBranch); } - var originTarget = this.origin.refs['master'].get('target'); + var originTarget = this.findCommonAncestorForRemote( + this.getCommitFromRef(target).get('id') + ); this.origin.makeBranch( branchName, - originTarget + this.origin.getCommitFromRef(originTarget) ); }; @@ -934,7 +948,7 @@ GitEngine.prototype.push = function(options) { if (!this.origin.refs[options.destination]) { this.makeBranchOnOriginAndTrack( options.destination, - 'HEAD' + this.getCommitFromRef(sourceBranch) ); // play an animation now since we might not have to fast forward // anything... this is weird because we are punting an animation @@ -1229,6 +1243,11 @@ GitEngine.prototype.pull = function(options) { destination: options.destination }); + if (!pendingFetch) { + // short circuited for some reason + return; + } + var destBranch = this.refs[options.destination]; // then either rebase or merge if (options.isRebase) { diff --git a/src/levels/remote/pullArgs.js b/src/levels/remote/pullArgs.js new file mode 100644 index 00000000..babe0b02 --- /dev/null +++ b/src/levels/remote/pullArgs.js @@ -0,0 +1,64 @@ +exports.level = { + "goalTreeString": "{\"branches\":{\"master\":{\"target\":\"C1\",\"id\":\"master\",\"remoteTrackingBranchID\":\"o/master\"},\"o/master\":{\"target\":\"C1\",\"id\":\"o/master\",\"remoteTrackingBranchID\":null},\"o/bar\":{\"target\":\"C1\",\"id\":\"o/bar\",\"remoteTrackingBranchID\":null}},\"commits\":{\"C0\":{\"parents\":[],\"id\":\"C0\",\"rootCommit\":true},\"C1\":{\"parents\":[\"C0\"],\"id\":\"C1\"}},\"HEAD\":{\"target\":\"master\",\"id\":\"HEAD\"},\"originTree\":{\"branches\":{\"master\":{\"target\":\"C3\",\"id\":\"master\",\"remoteTrackingBranchID\":null},\"bar\":{\"target\":\"C3\",\"id\":\"bar\",\"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\":\"master\",\"id\":\"HEAD\"}}}", + "solutionCommand": "git clone;git fakeTeamwork 2", + "name": { + "en_US": "Push arguments" + }, + "hint": { + "en_US": "Remember that you can create new local branches with fetch/pull arguments" + }, + "startDialog": { + "en_US": { + "childViews": [ + { + "type": "ModalAlert", + "options": { + "markdowns": [ + "## Git pull arguments", + "", + "Now that you know pretty much *everything* there is to know about arguments for `git fetch` and `git push`, there's almost really nothing left to cover for git pull :)", + "", + "That's because git pull at the end of the day is *really* just shorthand for a fetch followed by merging in whatever was just fetched.", + "", + "This applies when you use crazy-complicated arguments as well. Let's see some examples:" + ] + } + }, + { + "type": "GitDemonstrationView", + "options": { + "beforeMarkdowns": [ + "If we specify the place to fetch, everything happens as before but we merge in whatever was just fetched" + ], + "afterMarkdowns": [ + "See! by specifying `master` we downloaded commits onto `o/master` just as normal. Then we merged `o/master` to where we are, *regardless* of what was currently checked out." + ], + "command": "git pull origin master", + "beforeCommand": "git clone; go -b bar; git commit; git fakeTeamwork" + } + }, + { + "type": "GitDemonstrationView", + "options": { + "beforeMarkdowns": [ + "Does it work with source and destination too? You bet! Let's see that:" + ], + "afterMarkdowns": [ + "Wow, that's a TON in one command. We created a new branch locally named `foo`, downloaded commits from remote's master onto that branch `foo`, and then merged that branch into our currently checked out branch `bar`. It's over 9000!!!" + ], + "command": "git pull origin master:foo", + "beforeCommand": "git clone; git fakeTeamwork; go -b bar; git commit" + } + }, + { + "type": "ModalAlert", + "options": { + "markdowns": [ + "Ok to finish up, attain the state of the goal visualization. You'll need to download some commits, make some new branches, and merge those branches into other branches, but it shouldn't take many commands :P" + ] + } + } + ] + } + } +};