big progress on fetch

This commit is contained in:
Peter Cottle 2013-10-22 09:42:18 -07:00
parent c7ba5c3afe
commit 99bf9bacd3
5 changed files with 88 additions and 40 deletions

View file

@ -254,5 +254,19 @@ describe('Git Remotes', function() {
); );
}); });
it('fetches with no args, explicit dest args, and with just one arg', function() {
expectTreeAsync(
'git clone; git fakeTeamwork; git fetch origin master:o/master;git fakeTeamwork;git fetch;git fakeTeamwork;git fetch origin master',
'{"branches":{"master":{"target":"C1","id":"master","remoteTrackingBranchID":"o/master"},"o/master":{"target":"C4","id":"o/master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C3"],"id":"C4"}},"HEAD":{"target":"master","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C4","id":"master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C3"],"id":"C4"}},"HEAD":{"target":"master","id":"HEAD"}}}'
);
});
it('doesnt fetch if out of sync, but will update explicit dest if specified', function() {
expectTreeAsync(
'git clone; git fakeTeamwork; git fetch origin master:master;gc;git fakeTeamwork;git fetch origin master:master',
'{"branches":{"master":{"target":"C3","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"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"master","id":"HEAD"},"originTree":{"branches":{"master":{"target":"C4","id":"master","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C4":{"parents":["C2"],"id":"C4"}},"HEAD":{"target":"master","id":"HEAD"}}}'
);
});
}); });

View file

@ -23,6 +23,13 @@ var validateBranchName = function(engine, name) {
return engine.validateBranchName(name); return engine.validateBranchName(name);
}; };
var validateBranchNameIfNeeded = function(engine, name) {
if (engine.refs[name]) {
return name;
}
return validateBranchName(engine, name);
};
var assertIsBranch = function(engine, ref) { var assertIsBranch = function(engine, ref) {
assertIsRef(engine, ref); assertIsRef(engine, ref);
var obj = engine.refs[ref]; var obj = engine.refs[ref];
@ -297,7 +304,10 @@ var commandConfig = {
if (firstArg && isColonRefspec(firstArg)) { if (firstArg && isColonRefspec(firstArg)) {
var refspecParts = firstArg.split(':'); var refspecParts = firstArg.split(':');
source = refspecParts[0]; source = refspecParts[0];
destination = validateBranchName(engine, refspecParts[1]); destination = validateBranchNameIfNeeded(
engine,
crappyUnescape(refspecParts[1])
);
} else if (firstArg) { } else if (firstArg) {
// here is the deal -- its JUST like git push. the first arg // here is the deal -- its JUST like git push. the first arg
// is used as both the destination and the source, so we need // is used as both the destination and the source, so we need

View file

@ -26,6 +26,12 @@ function catchShortCircuit(err) {
} }
} }
function invariant(truthy, reason) {
if (!truthy) {
throw new Error(reason);
}
}
function GitEngine(options) { function GitEngine(options) {
this.rootCommit = null; this.rootCommit = null;
this.refs = {}; this.refs = {};
@ -371,6 +377,20 @@ GitEngine.prototype.makeOrigin = function(treeString) {
}, this); }, this);
}; };
GitEngine.prototype.makeRemoteBranchIfNeeded = function(branchName) {
if (this.refs[ORIGIN_PREFIX + branchName]) {
return;
}
return this.makeRemoteBranchForRemote(branchName);
};
GitEngine.prototype.makeBranchIfNeeded = function(branchName) {
if (this.refs[branchName]) {
return;
}
return this.validateAndMakeBranch(branchName, this.getCommitFromRef('HEAD'));
};
GitEngine.prototype.makeRemoteBranchForRemote = function(branchName) { GitEngine.prototype.makeRemoteBranchForRemote = function(branchName) {
var target = this.origin.refs[branchName].get('target'); var target = this.origin.refs[branchName].get('target');
var originTarget = this.findCommonAncestorWithRemote( var originTarget = this.findCommonAncestorWithRemote(
@ -1035,53 +1055,60 @@ GitEngine.prototype.pushDeleteRemoteBranch = function(
GitEngine.prototype.fetch = function(options) { GitEngine.prototype.fetch = function(options) {
options = options || {}; options = options || {};
// ok this is just like git push -- if the destination does not exist, // first check for super stupid case where we are just making
// we need to make it // a branch with fetch...
if (options.destination && !this.refs[options.destination]) { if (options.destination && options.source === '') {
// its just like creating a branch, we will merge (if pulling) later
this.validateAndMakeBranch( this.validateAndMakeBranch(
options.destination, options.destination,
this.getCommitFromRef('HEAD') this.getCommitFromRef('HEAD')
); );
} return;
} else if (options.destination && options.source) {
this.makeRemoteBranchIfNeeded(options.source);
this.makeBranchIfNeeded(options.destination);
// now we need to know which remotes to fetch. lets do this by either checking our source return this.fetchCore([{
// option or just getting all of them destination: options.destination,
var branchesToFetch; source: options.source
if (options.source) { }],
// gah -- first we have to check that we even have a remote branch options
// for this source (we know its on the remote based on validation) );
if (!this.refs[ORIGIN_PREFIX + options.source]) {
this.makeRemoteBranchForRemote(options.source);
}
// now just specify branches to fetch based on this source
branchesToFetch = [this.refs[ORIGIN_PREFIX + options.source]];
} else {
branchesToFetch = this.branchCollection.filter(function(branch) {
return branch.getIsRemote();
});
} }
// get all remote branches and specify the dest / source pairs
var allBranchesOnRemote = this.origin.branchCollection.toArray();
var sourceDestPairs = _.map(allBranchesOnRemote, function(branch) {
var branchName = branch.get('id');
this.makeRemoteBranchIfNeeded(branchName);
return {
destination: branch.getPrefixedID(),
source: branchName
};
}, this);
return this.fetchCore(sourceDestPairs, options);
};
GitEngine.prototype.fetchCore = function(sourceDestPairs, options) {
// first check if our local remote branch is upstream of the origin branch set. // 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 // this check essentially pretends the local remote branch is in origin and
// could be fast forwarded (basic sanity check) // could be fast forwarded (basic sanity check)
_.each(branchesToFetch, function(localRemoteBranch) { _.each(sourceDestPairs, function(pair) {
this.checkUpstreamOfSource( this.checkUpstreamOfSource(
this, this,
this.origin, this.origin,
localRemoteBranch, pair.destination,
this.origin.refs[localRemoteBranch.getBaseID()] pair.source
); );
}, this); }, this);
// then we get the difference in commits between these two graphs // then we get the difference in commits between these two graphs
var commitsToMake = []; var commitsToMake = [];
_.each(branchesToFetch, function(localRemoteBranch) { _.each(sourceDestPairs, function(pair) {
commitsToMake = commitsToMake.concat(this.getTargetGraphDifference( commitsToMake = commitsToMake.concat(this.getTargetGraphDifference(
this, this,
this.origin, this.origin,
localRemoteBranch, pair.destination,
this.origin.refs[localRemoteBranch.getBaseID()], pair.source,
_.extend( _.extend(
{}, {},
options, options,
@ -1159,14 +1186,15 @@ GitEngine.prototype.fetch = function(options) {
}, this); }, this);
chain = chain.then(_.bind(function() { chain = chain.then(_.bind(function() {
// update all the remote branches // update all the destinations
_.each(branchesToFetch, function(localRemoteBranch) { _.each(sourceDestPairs, function(pair) {
var remoteBranch = this.origin.refs[localRemoteBranch.getBaseID()]; var ours = this.refs[pair.destination];
var remoteLocationID = remoteBranch.get('target').get('id'); var theirs = this.origin.refs[pair.source];
var theirCommitID = theirs.get('target').get('id');
// by definition we just made the commit with this id, // by definition we just made the commit with this id,
// so we can grab it now // so we can grab it now
var localCommit = this.refs[remoteLocationID]; var localCommit = this.refs[theirCommitID];
this.setTargetLocation(localRemoteBranch, localCommit); this.setTargetLocation(ours, localCommit);
}, this); }, this);
// unhighlight origin by refreshing // unhighlight origin by refreshing

View file

@ -92,7 +92,7 @@ exports.strings = {
}, },
'git-error-origin-fetch-no-ff': { 'git-error-origin-fetch-no-ff': {
'__desc__': 'One of the error messages for git', '__desc__': 'One of the error messages for git',
'en_US': 'Your origin branch is out of sync with the remote branch and fetch cannot be performed. try using --force', 'en_US': 'Your origin branch is out of sync with the remote branch and fetch cannot be performed',
'fr_FR': 'Votre branche origin n\'est plus synchronisée avec la branche distante et fetch ne peut pas être appliqué. Essayez avec l\'option --force' 'fr_FR': 'Votre branche origin n\'est plus synchronisée avec la branche distante et fetch ne peut pas être appliqué. Essayez avec l\'option --force'
}, },
'git-error-origin-push-no-ff': { 'git-error-origin-push-no-ff': {

View file

@ -22,12 +22,6 @@ aka fetch + merge, just like expected. ill probably still update o/master just f
just pass options into fetch, then merge the source. just pass options into fetch, then merge the source.
2) oh boy heres anothere data point. git fetch banana:banana will actually fast-forward banana to what remote has, but not update o/banana. weirdly it doesnt let you do this if you are checked out on banana
3) furthermore, if banana has commits and its not a FF, it will reject the command. wth??
3.5) but of course git fetch banana:oBanana will make a new branch
4) and then "git pull origin banana:origin/banana" works because it goes into the remote branch and then merges the fetch HEAD with current location 4) and then "git pull origin banana:origin/banana" works because it goes into the remote branch and then merges the fetch HEAD with current location
[ ] FIX the level you came up with [ ] FIX the level you came up with
@ -50,6 +44,8 @@ Ideas for cleaning
Done things: Done things:
(I only started this on Dec 17th 2012 to get a better sense of what was done) (I only started this on Dec 17th 2012 to get a better sense of what was done)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[x] oh boy heres anothere data point. git fetch banana:banana will actually fast-forward banana to what remote has, but not update o/banana. weirdly it doesnt let you do this if you are checked out on banana
[x] furthermore, if banana has commits and its not a FF, it will reject the command. wth??
[x] test is failing because we create banana when we should only really be creating o/banana [x] test is failing because we create banana when we should only really be creating o/banana
[x] work on TABBED levels layout [x] work on TABBED levels layout
[x] EASY -- make colors the same between remote branches and their remote counterparts [x] EASY -- make colors the same between remote branches and their remote counterparts