Reputation: 410
I couldn´t get my head around the following problem.
We are 2 coders working on the same project.
There is 1 master branch on which we both push.
In the merge there are not the 5 commits you see in the picture below:
In the past the merge considered all files of the commits in the violet branch.
Now the difference is we had our first merge clonflict with a file we both touched.
After the merge Git seems to have not considered any file of the violet branch like in the picture below:
My teammate decided to let his AppModel.php overwrite to get the conflict solved.
Now we don´t have a clue why Git didn´t work as usual or even how to redo/retry the merge.
Thanks for your time and help :)
Upvotes: 1
Views: 3400
Reputation: 489628
This bit:
My teammate decided to let his AppModel.php overwrite to get the conflict solved.
is almost certainly the entire problem.
I've written up an explanation of what git merge
does below, explaining why this is (almost certainly) the entire problem and how you're supposed to do merges instead, but for now the thing you need to do, since these commits are published,1 is simply to re-insert your own changes as new commits, which you can do via git cherry-pick
for instance. You will get one or more merge conflicts while cherry-picking, which you can solve the right way (instead of just taking one or the other version of the entire file).
I don't use most of the GUI versions of these tools, so the following assumes you are using the command line.
To cherry-pick your previous commits (so as to "replay" them), you need to name each commit. One way to do this is with the raw SHA-1 IDs, which are not shown above—I can see one, fa434c6e39
, but not the rest—but if you have them in front of you (in a window, written down, whatever) and if they were just 1
, 2,
3,
4, and
5` (obviously they're not, they're long 40-character strings):
$ git cherry-pick 1
$ git cherry-pick 2
...
$ git cherry-pick 5
would bring them in. Or you can combine this into one command:
$ git cherry-pick 1 2 3 4 5
There's another way to name them more symbolically, though, using gitrevisions
syntax:
$ git cherry-pick master^2~5..master^2
which does not even require finding the raw SHA-1 IDs. The one drawback to this method is that these names are relative to where master
points, so this assumes that master
currently points to the bad merge. Here master^2
is the second-parent of the bad merge, which is your most recent pre-merge commit. From this commit, we walk back 5 steps, then use the ..
syntax, then select that particular commit again. The range of the graph thus selected is the 5 commits you made.
(Note that we are depending, here, on the fact that you made exactly 5 commits, but we can see them—two IMRPOVEMENTs and three BUGFIXes—in the graphical display above, so we know this is correct.)
Each cherry-pick directs git to find what you changed in that commit, and simply do it again now: take a diff between a specific commit and its parent commit, and re-apply that diff now.
1There is an alternative: you can reset-away ("remove") the bad merge, replace it with a good merge, and then "force-push" the result. If you do this, though, then everyone else who has already picked up the bad merge must pick up your changes from the public repository, and move any subsequent changes they have made onto your new corrected merge. If "everyone else" is simply the one other guy who made the bad merge, this may actually be a better way to handle the problem. But if you do, make sure he knows you're doing it, otherwise he may just restore the bad merge. He needs to remove his copy of the bad merge too!
In the case of an unpublished commit—for instance, if you do a merge, and then while testing the result, you find that the merge is bad—you can do this "remove the bad merge and retry" thing (or use git commit --amend
) safely. Removing published commits is bad precisely because it makes more work for other people who have picked up the published commits. If the commit is not published, no one else has it, so you are not making more work for other people.
When you ask git to merge two lines of development, such as this:
* Neue Funktion: Mediathe...
| * IMPROVEMENT: Pins der K...
| * IMPROVEMENT: Mailsedung...
| * BUGFIX: Bewerten und An...
| * BUGFIX: Anfrage-Buttons...
| * BUGFIX: Anfrage-Buttons...
|/
* BUGFIX: Anfrage löschen...
it starts by identifying the "merge common base", the point at which the two lines diverged. In this case, that's the BUGFIX: Anfrage löschen geht nicht
commit.
Next, git does two git diff
s. One is from the common base to the tip of the current branch: in this case, from Anfrage löschen...
to Neue Funktion: Mediathe...
. Those are "your" changes since the base. (Remember that in this case, "you" is the other fellow, the one who did the bad merge.) The second diff is from the common base to the tip of the to-be-merged branch: in this case, from Anfrage löschen...
to Pins der Karte...
. Those are "his" changes.
(Try it yourself: find the commit ID of the merge base, and run git diff <commit-ID-of-base> <other-commit-ID>
. Do this for each of the two commits that are to be merged.)
Finally, git looks at these two diffs. Wherever you made a change that they did not, git takes your change. Wherever they made a change that you did not, git takes their change. If you and they changed the same line(s) of the same file(s), git declares a "conflict".
In the case of a conflict, git writes the file with both sets of changes in it (and, optionally, the lines that both took as common from the base: to see these lines, set the merge.conflictstyle = diff3
option in your git configuration.) It then requires that you, the user, edit the file and select the correct change(s), whichever those may be.
If you simply take your (or their) version of the file, git assumes that you looked and decided that their (or, respectively, your) changes were no longer desired.
Note also that when git thinks there is no conflict, that does not mean that the resulting merge is correct. It just means that what you did was to change line(s) in file(s) or location(s) that they did not touch. For instance, suppose that your software has a bug that can be fixed in one (but not both) of two ways, by adding a line to function f1
, or by removing a line from function f2
. Suppose further that you did the remove-from-f2
and they did the add-to-f1
. Git will compare your two changes, see that they changed something you left alone and you changed something they left alone, and take both changes. This produces a new and different bug—but as far as git can tell, it's the correct way to merge.
Upvotes: 4