Reputation: 64205
(What I described below are all happening in a local git repo for experiment. No remote repo involved.)
I create 2 local branches lb1
and lb2
. They start from the same commit node.
git branch lb1
git branch lb2
I make them upstream of each other. As below:
$ git checkout lb2
Switched to branch 'lb2'
$ git branch -u lb1
Branch lb2 set up to track local branch lb1.
$ git checkout lb1
Switched to branch 'lb1'
$ git branch -u lb2
Branch lb1 set up to track local branch lb2.
Then I make them diverge by making different changes on each. As shown below:
$ git branch -vv
* lb1 6774aa6 [lb2: ahead 1, behind 1] lb1 change
lb2 cdd1247 [lb1: ahead 1, behind 1] lb2 change
I am currently on lb1
. Then I tried to push one branch (lb1) to the other (lb2) to see what will happen:
$ git push . lb1:lb2
To .
! [rejected] lb1 -> lb2 (non-fast-forward)
error: failed to push some refs to '.'
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. Check out this branch and integrate the remote changes
hint: (e.g. 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
So if I understand it right, it says the pushed branch tip (lb1
) is behind its remote counterpart (lb2
). I have to first checkout lb1
and pull from lb2
to integrate lb2
's change.
So I do this: (note that I am on lb1
branch now)
$ git pull
From .
* branch lb2 -> FETCH_HEAD
Auto-merging file1.txt
CONFLICT (content): Merge conflict in file1.txt
Automatic merge failed; fix conflicts and then commit the result.
So git pull
gives me a chance to merge.
After I merge, I can push lb1
to lb2
:
$ git push . lb1:lb2
Total 0 (delta 0), reused 0 (delta 0)
To .
cdd1247..2dfb555 lb1 -> lb2
And revision graph is like this:
And the lb2
's change is added into lb1
's log history:
To summarize:
My question is:
Why git push
doesn't offer a chance to merge? From the perspective of English language, I think push
and pull
are symmetric. So I expect same result from them.
Upvotes: 1
Views: 703
Reputation: 70339
Merging on pull means that your local branch keeps its history in tact and additional commits are stacked on top. You have the result local and can test it before anyone else is affected. If you don’t like the result, pop the committees of the stack.
If a merge happened on a push, the remote would have a result that has never been seen before. Someone would then have to retrieve it to even know what the state was. Then it would have to be tested. All while the rest of the team are exposed it to. This is just a terrifying scenario. It only makes sense in a situation where repo is the sole concern of an individual and there is no consequential CD in place.
Upvotes: 0
Reputation: 871
I think @dunni hit the nail on the head (i.e. merged the nail into the board) in the comments when they mentioned that a git pull
is actually a convenience notation for two distinct operations. What I would challenge from your question's assertions is that git-pull
is symmetric to git-push
. It may be more accurate to state that a git-fetch
is symmetric to a git-push
. Of course, as you said, in English push and pull are very much indeed opposites to each other; however that doesn't necessarily imply symmetry in their usage in other domains.
As to why the implementors of git decided to have pull
attempt an automerge, I can only speculate. My theory is that because the working repo has such a concept as tracking branches it makes sense to automatically merge the remote tracking branch into the local branch to perform the pull operation. This is because you will have to do the merge/fast-forward necessarily to use the updates in a non-detached HEAD state. The repository has to keep this separation in order to know what commits the remote has versus what it has locally. However a remote, bare repository has no such concept as tracking branches: it just updates its refs when it runs git-receive-pack
on the backend. It doesn't care what commits are referenced; that's why force push (git push -f
) exists. In fact, force push is not even a construct in the git-pack-protocol
but just a client-side option to ignore the normal checks for keeping the history intact. When you push, you are not implying the need for a merge/fast-forward: you're only implying an update of the object database on the remote and its list of refs. On the client end, the remote tracking branch is simply updated to match what you successfully pushed (whether forced or not).
To summarize: a fetch necessitates a merge because the fetched remote tracking branch has to be merged into a corresponding local branch for you to use in your working repository. However with push you may not necessarily have a need to merge; you are just updating the remote branches.
Upvotes: 4
Reputation: 9238
You probably should better ask it in the git maiilist, but there is important difference: the merge performed by pull
is yet part of your personal history. You can test it, or update merge message, or change your mind and reset to commit before the merge. You can spend time resolving conflicts.
If git push were merging, this automatic merged commit would immediately become part of public history. It would also cause synchronisation issues, in case somebody pushes at the same moment, and merge requires resolving conflicts or writing commit message.
Actually, I have a script which tries to implement it, but I cannot say I've seen much feedback about it. These days people are used to work with mostly personal feature branches, and merge only done when server is merging pull request.
Upvotes: 1