You cannot fetch or push to the remote branch if you made some commits on the tracking branch ("broke the model") and remote branch has other commits. You can force the issue, but then either remote (for push) or local (for fetch) commits would be lost. Sidenote: for some branches you have to skip fast-forward check, because But one of those branches can be temporary "branch" FETCH_HEAD (if you Because usually you work with one branch with tracking (remote) + local branch workflow; I think with multiple branches you usually use temporary feature branches... -- Jakub Narebski Warsaw, Poland ShadeHawk on #git -
Ok, I'm trying to come up with an experiment that verifies this, so I can give a concrete example to our developers. I don't seem to be able to get it to fail, but I sure remember having severe problems with this in practice. Here is my attempt, again with git 1.4.4.1: # Create my repo and add a file A % mkdir my_repo % cd my_repo % git init-db defaulting to local storage area % echo A > A % git add A && git commit -a -m "Add A" Committing initial tree f53c91092dbda83f3565e78c285f3e2ab0cfd968 # Create a peer repo and add a file B % cd .. % mkdir peer_repo % cd peer_repo/ % git init-db defaulting to local storage area % echo B > B % git add B && git commit -a -m "Add B" Committing initial tree bfcfa4ca04d80d4b092e022ad163e82ca0f4a34f # Create a topic branch in peer repo and add file C % git checkout -b topic % echo C > C % git add C && git commit -a -m "Add C" # Go back to my repo and fetch peer's topic branch % cd ../my_repo/ % git fetch ../peer_repo topic:topic warning: no common commits remote: Generating pack... remote: Done counting 6 objects. remote: Deltifying 6 objects. remote: /6) done/6) done Unpacking 6 objects remote: Total 6, written 6 (delta 0), reused 0 (delta 0) 100% (6/6) done * refs/heads/topic: storing branch 'topic' of ../peer_repo commit: af3ab53 # In my repo, checkout topic and change B % git checkout topic % echo "Change B" >> B % git commit -a -m "Change B" # Go back to peer repo and change C % cd ../peer_repo/ % echo "Change C" >> C % git commit -a -m "Change C" # Go back to my repo and pull peer's topic branch % cd ../my_repo % git pull ../peer_repo topic:topic remote: Generating pack... remote: Done counting 5 objects. remote: Result has 3 objects. remote: Deltifying 3 objects. remote: 100% (3/3) done remote: Total 3, written 3 (delta 0), reused 0 (delta 0) Unpacking 3 objects 100% (3/3) done * refs/heads/topic: not updating to non-fast forward branch 'topic' of ../peer_repo old...new: ...
Because your pull command really means "merge in the topic branch from peer_repo, and while you're at it, store it in my local tracking branch topic". Remember that pull is really a fetch+merge. But the fetch is actually doing _two_ things: putting the fetched branch into FETCH_HEAD, and putting it in into refs/heads/topic. The latter fails (because of a non-fastforward), but pull actually uses the FETCH_HEAD results to do the merge. Yes, this seems overly complex for what you're doing, but the reason for FETCH_HEAD is to support pulls when you _don't_ have a tracking branch at all (i.e., 'git pull ../peer_repo topic'). -Peff -
Ok, fair enough, but then I guess I'm back to my original question: how can I give a concrete demonstration to our developers that this is a bad thing? This is not 100% required, so if you are tired of answering my incessant questions, feel free to decline. I will be able to get our group to move forward, simply because we need to try to stay current, and there are lots of improvements in git besides this issue. Bill -
I think it will always work with the example you gave, because you are simultaneously fetching into the tracking branch (which fails) and merging from FETCH_HEAD (which succeeds) into that same tracking branch. At best, though, the tracking branch you have is pointless (since you immediately overwrite it anyway). The point of a tracking branch generally is to allow you to do operations against your peer's idea of the branch (e.g., diffing against upstream's version of "topic"). But you can't do that, because "topic" always contains your topic, not upstream's. In effect, your pull becomes one without a tracking branch at all. This will also get you on a push, where there is no merging at all, just a fast-forward (or failure). IIRC, you ran into problems before because you were trying to push into your public repo from your private, but the two had divergent branches. So I think to illustrate the problem you had before, you actually need an intermediate repo which has you fetch from and push to. -Peff -
It no longer works with recent git, as of v1.4.4.1-37-gd25c26e. Now git-fetch exit with a non-zero status when fast-forward check fails, so the merge does not happen. Santi -
Try this: # create first repository mkdir foo cd foo git init-db echo FOO > biz git add biz git commit -a -m "commit 1 in foo" # clone this repository elsewhere cd .. git clone foo bar cd bar # modify the tracking branch (origin in this case) git checkout origin echo BAR > biz git commit -a -m "modify tracking branch in bar" # go back to original repo and modify it cd ../foo echo BAZ > biz git commit -a -m "modify master branch in foo" # now back to the cloned repo cd ../bar git fetch # it fails git push # itfails git pull # it almost performs the merge but... Because you now have a conflict to resolve, you might wish to inspect what the remote state actually is. But because you modified your origin branch you don't have any pristine version of what the remote has, except maybe the MERGE_HEAD. But if you work in a complex project, MERGE_HEAD will not stay there for long if you move around. Imagine that you want to move to another branch, say master, because right now you don't feel like resolving this ultra complex merge: git checkout master # it fails (unresolved merge) git checkout -f # succeeds, discarding the merge But at this point you still don't have any way to look at the remote's content. If instead the origin branch was untouched and the work had occurred in the master branch, then you could git-log the origin branch, you could git-diff your master branch with the origin branch, you could even checkout the origin content to test it, etc. But since you added commits on top of the origin branch then you cannot do any of that because it is impossible to update the origin branch with the remote stuff. Well you can force it of course: git fetch -f But by doing so you lose all the work you committed on top of origin. BTW I checked out v1.4.4.1 to verify the above operations..... and this GIT version really feels odd compared to v1.5.0 Nicolas -
Ok, that's basically what I was looking for --- incontrovertible evidence from "them" (a git expert --- not me). Thanks very much Nicolas. Bill -
