mirror of
https://github.com/pcottle/learnGitBranching.git
synced 2025-07-03 11:14:27 +02:00
big progress on fetch
This commit is contained in:
parent
c7ba5c3afe
commit
99bf9bacd3
5 changed files with 88 additions and 40 deletions
|
@ -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"}}}'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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': {
|
||||||
|
|
8
todo.txt
8
todo.txt
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue