Reputation: 12958
1) There's a 'master' branch with a file containing
1
2
3
4
5
2) A takes a branch from 'master' and edit like
1
2
100
3
4
5
3) B takes a branch from 'master' and edit like
1
2
3
4
200
5
4) Now A push changes into master. Then B tries to push too.
What will happen with B? Any merge-conflicts or no merge-conflicts? Reasons?
Upvotes: 1
Views: 1075
Reputation: 3300
the simplest answer is: git will assume there is a conflict if two devs have changed the same line of code in the same file (and same branch).
whoever commits and pushes first is the "winner". the second one has to deal with conflicts. he cannot push until he has pulled all changes from origin and resolved the conflicts.
Upvotes: 4
Reputation: 489908
Your question implies something that is not the case.
Specifically, git push
only pushes existing commits. It does not merge anything, and in fact, it never even tries to merge anything.
In your question, there are three entities (people and repositories) involved. Person A (let's call her Alice), person B (let's call him Bob), and person C (let's call him Central-Server and he's actually just a machine, not a person, though this is not important).
Alice and Bob both start by obtaining a copy (a clone) of some repository from Central-Server. They get exactly the same clones (which is why they are called "clones"): Alice's clone matches Bob's clone matches what's on Central-Server. They do this by running git clone <url>
where <url>
points to the central server (github or whatever it may be), and their git saves away the url under the name origin
(we'll see this name again soon).
Let's make a drawing of (part of) the git commit graph that all three entities have right now:
... - C7 - C8 <-- master
Now Alice and Bob both make changes, but they make different changes. Alice commits her change:
... - C7 - C8 - A <-- master
Then Alice runs git push origin
to push her work back to Central-Server. Central-Server looks at her request, which says "add commit A
to the end of the chain at C8
, and make master
point to A
". This operation adds new commits to the chain, and is therefore allowed, so Central-Server replies "OK" to Alice, and she is all finished. Central-Server's repository now looks the same as Alice's as well, as both of them have new commit A
after old commit C8
, with master
pointing to commit A
(and commit A
pointing back to the old C8
).
Meanwhile Bob has made his change and also added a new commit, and his commit graph now looks like this:
... - C7 - C8 - B <-- master
Bob has no idea that Alice has made commit A
, nor successfully pushed it to Central-Server. He goes to git push origin
, but this time Central-Server gets a request that says "add commit B
to the end of the chain at C8, then make master
point to B
". If Central-Server did this, the effect would be:
A
/
... - C7 - C8 - B <-- master
That is, commit A
would be left floating with nothing pointing to it. (Branch master
would point to B
and B
would point back to C8
, with nothing pointing to A
.) This is generally a bad state of affairs and git rejects it, so that Central-Server tells Bob:
rejected (non-fast-forward)
Note that there has been no merging and no rebasing.
It is now Bob's job to do the merge (or rebase). He should do so by:
Obtaining the update from someone who has it. Who has it? Alice has it, but so does Central-Server. He just asked to push to Central-Server, who told Bob "no", so he might as well get it from Central-Server.
Do the merge (or rebase), resolving any conflicts.
Retry the push.
If Bob chooses "merge" and does a proper job of merging, here is his new commit graph:
... - C7 - C8 - A - M <-- master
\ /
B
Note the new merge commit M
. Now Bob can re-try the push to Central-Server, who currently has the chain ending with A
. This time Central-Server will see a request to make master
point to M
, and since M
points to A
, that request will be allowed (it is now a "fast-forward").
Of course, if Alice (or Dave or Emily or Frank) beats Bob to the punch by adding new commits past A
and sending them back to Central-Server, Bob will have to merge (or rebase) yet again, and try again.
It's Bob's choice whether to merge or rebase. Either way, Bob will have to solve any merge conflicts—and he will get the same merge conflicts, whichever method he uses. And, in either case, he should start by running:
git fetch origin
(or just git fetch
, which will use origin
automatically).
Let's take a look at Bob's commit graph before a rebase or merge:
A <-- origin/master
/
... - C7 - C8
\
B <-- master
Note that Bob's master
points to commit B
, and Bob has this other thing—this origin/master
—pointing to Alice's commit A
. This is what git fetch
does: it brings over the latest version from Central-Server (or if Bob fetches directly from Alice, brings it over from her, since she has same commit), and then makes some label point to that commit. The label starts with origin/...
because that's the name we allowed git clone
to use: it just sticks origin/
in front of the other name (master
, in this case) so that we can tell them apart.
If Bob chooses to rebase rather than merge, he will have his git copy his commit B
to a new commit B'
:
A <-- origin/master
/ \
... - C7 - C8 B' <-- master
\
B
What happens to Bob's original B
? The answer is: it is abandoned. It remains in the repository for a while (default 30 days) in case Bob needs it back, saved in Bob's reflogs, but unless you (or Bob) explicitly ask git to look there, you do not see these commits, so they seem gone.
If Bob chooses to merge, he gets this:
A <-- origin/master
/ \
... - C7 - C8 M <-- master
\ /
B
This is the same graph we drew above, we've just lifted the A
node up so that we can have an arrow pointing to it (labeled origin/master
).
In either case, Bob can now try the push, since his new commit—either B'
or M
—points back to commit A
, so that he is only asking Central-Server to add new commits rather than forget-or-abandon commit A
.
Git will attempt to help Bob out, by comparing the changes Alice made (adding a line with 100
) to the change Bob made (adding a line with 200
). If git decides that these changes do not collide with each other, it will keep both changes. If it decides that the two changes affect the same part of the file, it will give Bob a merge conflict, marking the changed regions of the file, and make Bob decide how to combine them.
Bob can use whatever he likes to achieve the combined result. It is up to Bob to make sure that the result is correct, and then Bob should tell his git to git add
the final version of the file and git commit
to commit the changes.1 If the command he used to combine the changes was git merge
, this will make a merge commit. If it was git rebase
, this will make the new copy B'
.
1If Bob chose git rebase
, he can just use git rebase --continue
, and it will make the commit for him. It is safe for Bob to do the git commit
first and then do the git rebase --continue
, though (and back in the days of git 1.5 or so, one did have to do the commit part manually, before continuing the rebase).
git pull
I encourage new git users to start with git fetch
and then to do their own git merge
or git rebase
. Many documents tell you to start with git pull
, but I think this is a mistake. The git pull
command is meant to be a convenience: it runs git fetch
and then runs either git merge
or git rebase
, but this has several flaws. None are terribly serious any more, but these are not good for new users:
It chooses the merge vs rebase before you can even look at the changes.
The default is merge, which is usually the wrong answer for new users, who usually probably should rebase. You can change the default, but new users don't know to do that in advance. You can add --rebase
to tell it to rebase, but you can forget to include this flag.
The merge it makes is a "foxtrot merge": it has the parent commits in the wrong direction.
The arguments are confusing when compared to manual merge: git pull origin <branch>
vs git merge origin/<branch>
. (Of course, if you want to avoid foxtrot merges, you cannot use the latter either, but you probably should be rebasing anyway.)
If you supply too many arguments (git pull origin master develop
), it makes an octopus merge, which is not something new users should even think about. :-)
It used to have a number of work-destroying bug cases. I believe they are all fixed, but using git fetch
followed by a separate git merge
or git rebase
has always avoided these bugs.
Last, there is just too much magic happening here. It is supposed to be convenient (and for those who are old hands with git, it is convenient) but it just winds up being obscure.
Upvotes: 6
Reputation: 2508
Merge conflicts occur when git does not know how to handle two changes in the same part of a code, which have not been made sequentially. In your case, the lines that have been changed are different, so git will be able to merge the commits without conflicts. However, once the commit in A has been pushed, the commit in B cannot be pushed without rebasing it on top of A.
In this situation you cannot push B:
----M-----A
\----B
If you go to B and type and use:
git rebase A
your code will look like the following in your local repository:
----M-----A-----B
and you will be able to push commit B too
Upvotes: 1
Reputation: 1
When B tries to push, there will be error return, because A has been pushed changes to master. Then you should do,
git fetch
git rebase origin/master
When doing rebase step, there is no conflicts happen, because A changes line 3 of the file, B changes line 5 of the file. then you can do,
git commit -m ""
git push
Upvotes: 0