diff --git a/spec/remote.spec.js b/spec/remote.spec.js index f1819e77..35eac439 100644 --- a/spec/remote.spec.js +++ b/spec/remote.spec.js @@ -213,12 +213,18 @@ describe('Git Remotes', function() { }); it('will not fetch if ref does not exist on remote', function() { -expectTreeAsync( - 'git clone; git fakeTeamwork; git fetch foo:master', - '{"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":"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"}}}' - ); + expectTreeAsync( + 'git clone; git fakeTeamwork; git fetch foo:master', + '{"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":"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('does not fetch if ref does not exist on remote with one arg', function() { + expectTreeAsync( + 'git clone; git fakeTeamwork; git fetch 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":"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"}}}' + ); + }); }); diff --git a/src/js/git/commands.js b/src/js/git/commands.js index 9212fb64..36f1efa1 100644 --- a/src/js/git/commands.js +++ b/src/js/git/commands.js @@ -253,14 +253,14 @@ var commandConfig = { fetch: { regex: /^git +fetch($|\s)/, execute: function(engine, command) { - var options = {}; - if (!engine.hasOrigin()) { throw new GitError({ msg: intl.str('git-error-origin-required') }); } + var source; + var destination; var generalArgs = command.getGeneralArgs(); command.twoArgsImpliedOrigin(generalArgs); assertOriginSpecified(generalArgs); @@ -268,21 +268,28 @@ var commandConfig = { var firstArg = generalArgs[1]; if (firstArg && isColonRefspec(firstArg)) { var refspecParts = firstArg.split(':'); - options.source = refspecParts[0]; - options.destination = refspecParts[1]; + source = refspecParts[0]; + destination = validateBranchName(engine, refspecParts[1]); // destination will be created by fetch, but check source - assertIsRef(engine.origin, options.source); } else if (firstArg) { // here is the deal -- its JUST like git push. the first arg // is used as both the destination and the source, so we need // to make sure it exists as the source on REMOTE and then // the destination will be created locally - var tracking = assertBranchIsRemoteTracking(engine, firstArg); - options.branches = [engine.refs[tracking]]; + source = firstArg; + destination = firstArg; + //var tracking = assertBranchIsRemoteTracking(engine, firstArg); + //options.branches = [engine.refs[tracking]]; + } + if (source) { // empty string fails this check + assertIsRef(engine.origin, source); } - engine.fetch(options); + engine.fetch({ + source: source, + destination: destination + }); } }, diff --git a/src/js/git/index.js b/src/js/git/index.js index 6c31f130..3874eac6 100644 --- a/src/js/git/index.js +++ b/src/js/git/index.js @@ -357,13 +357,9 @@ GitEngine.prototype.makeOrigin = function(treeString) { return; } - var originTarget = branchJSON.target; - // 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 - while (!this.refs[originTarget]) { - var parents = originTree.commits[originTarget].parents; - originTarget = parents[0]; - } + var originTarget = this.findCommonAncestorWithRemote( + branchJSON.target + ); // now we have something in common, lets make the tracking branch var remoteBranch = this.makeBranch( @@ -375,6 +371,27 @@ GitEngine.prototype.makeOrigin = function(treeString) { }, this); }; +GitEngine.prototype.makeRemoteBranchForRemote = function(branchName) { + var target = this.origin.refs[branchName].get('target'); + var originTarget = this.findCommonAncestorWithRemote( + target.get('id'); + ); + return this.makeBranch( + ORIGIN_PREFIX + branchName, + this.getCommitFromRef(originTarget) + ); +}; + +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 + while (!this.refs[originTarget]) { + var parents = this.origin.refs[originTarget].get('parents'); + originTarget = parents[0].get('id'); + } + return originTarget; +}; + GitEngine.prototype.makeBranchOnOriginAndTrack = function(branchName, target) { var remoteBranch = this.makeBranch( ORIGIN_PREFIX + branchName, @@ -1018,11 +1035,30 @@ GitEngine.prototype.pushDeleteRemoteBranch = function( GitEngine.prototype.fetch = function(options) { options = options || {}; - // get all remotes - var allRemotes = this.branchCollection.filter(function(branch) { - return branch.getIsRemote(); - }); - var branchesToFetch = options.branches || allRemotes; + // ok this is just like git push -- if the destination does not exist, + // we need to make it + if (options.destination) { + // its just like creating a branch, we will merge (if pulling) later + this.validateAndMakeBranch( + options.destination, + this.getCommitFromRef('HEAD') + ); + } + + // now we need to know which remotes to fetch. lets do this by either checking our source + // option or just getting all of them + var branchesToFetch; + if (options.source) { + // gah -- first we have to check that we even have a remote branch + // for this source (we know its on the remote based on validation) + if (!this.refs[ORIGIN_PREFX + options.source]) { + this.makeRemoteBranchForRemote(options.source); + } + } else { + branchesToFetch = this.branchCollection.filter(function(branch) { + return branch.getIsRemote(); + }); + } // first check if our local remote branch is upstream of the origin branch set. // this check essentially pretends the local remote branch is in origin and