0

Following this problem I now have two local git repos on my windows machine that I want to merge. When I use the WinMerge tool to compare files in the repos, the differences are shown corectly. But when git tries to merge the entire file is marked as conflicted.

One important point is that there are other files which are identical and are correctly processed. Another point is that the problem has nothing to do with core.autocrlf for two reasons: 1- I tested with both true and false settings 2- WinMerge shows no difference with ignore line endings option off

How do you suggest we investigate this matter?

[edit] I upgraded git but it was no use.

Siavoshkc
  • 346
  • 2
  • 16

2 Answers2

3

When Git performs a normal merge, it considers exactly three points: the two heads you're merging, and the merge base, which is usually the nearest common ancestor. If you're merging unrelated histories, this common ancestor is the empty tree; that is, the state with no files.

A merge takes the sum of the result of applying the differences between the merge base and each head.

If, on merge, the file is identical on both sides (that is, the blob has the same object ID), then the result of the merge is simply to take that blob as the value for that file during the merge. Both sides share identical changes, so there's no point in doing any fancy analysis. This explains why identical files work just fine for you in this case.

However, in the case where the files are not identical and you have unrelated histories, Git considers this an add/add conflict: that is, one side has added some set of lines and the other side has added another set of lines. It may be the case that many of these lines are in common, but the conflict is logically the fact that two different sets of lines are added, so the entire file conflicts.

This is why merging independent histories of the same codebase tends to be a bad idea. You can either resolve the conflicts by hand, or you can perform a merge of two identical states of the repository, which will result in a conflict-free merge, and then merge your changes again in a second merge. In the latter case, your merge base will not be the empty tree, but the identical merged state, and as a result, Git will be able to resolve the changes much better without conflicts.

bk2204
  • 64,793
  • 6
  • 84
  • 100
  • I didn't understand the part "perform a merge of two identical states of the repository, which will result in a conflict-free merge, and then merge your changes again in a second merge." – Siavoshkc Apr 25 '20 at 20:58
  • 1
    You'd want to find two commits, one in your SVN version, and one in your Git version, that are identical (that is, they have exactly the same files) and merge them. Then take your new code and merge it with the result of that merge. – bk2204 Apr 25 '20 at 21:14
  • That is not possible as I see it. It is big project that I worked on for a long time on my SVN and someone else on git repo. And the git version is itself cloned from the original SVN so the history of SVN is deleted. – Siavoshkc Apr 25 '20 at 21:19
  • If you can't do that, then you'll have to resolve the conflicts by hand. There really isn't an alternative solution. – bk2204 Apr 25 '20 at 21:43
  • Not that it is not possible because the number of files and tiny differences is big, I see not logical reason to do it manually. The git exists for this purpose and should be able to do it. If WinMerge can see the real differences, git should be able to work this way. There should be an option to set one repo as common ancestor. – Siavoshkc Apr 25 '20 at 21:49
  • 2
    There is no common ancestor in this case because you have unrelated histories. This is the way Git works. I'm just telling you your options. You can also go to the Git list and request a different behavior if you want, but this is the way it works now. – bk2204 Apr 25 '20 at 22:02
0

WinMerge implements something people sometimes calls a "two-way merge". This is a misnomer as a two-way "merge" is not a merge at all: it's just a diff, followed by a guided application of that diff. See Why is a 3-way merge advantageous over a 2-way merge?

WinMerge also implements a 3-way merge. Git only implements a 3-way merge, in its merge algorithms (git merge and git merge-file). That is, there must be a common base file.

You will therefore have to use something other than Git to achieve the file-level merge, or come up with a common base file:

  • You don't need a common-ancestor commit (as in bk2204's answer), but having one makes things much easier, because Git itself will then do all the work. The common base file comes from the common merge base commit.
  • You do need a common base file, though, if you want to use Git's tools.

In other words, if you like, you can:

  • Start the merge using git merge --allow-unrelated-histories.
  • Let Git stop with its messages about CONFLICT (add/add).
  • For each merge-result that is in this conflicted state:
    • Pick out your two or three files. The two Git found are easy to get. Finding or creating a common merge base is your own problem; Git has no answer for you here.
    • Use any tool of your choice to produce the merge result. This can be git merge-file, WinMerge, or whatever you like.
    • Tell Git: this merge result is the correct result of merging. Git will accept whatever you give it as the correct merge result.
  • Now that you have resolved all conflicts, finish the merge.

It's relatively easy, using bash scripting for instance, to automate the "for each conflicted file" part. git status tells you which files are in UU (unmerged) state, so that gives you the set of file names. You can then use git ls-files --stage <name> on each file name to verify that there is indeed a slot-2 and slot-3 index entry for that name, and no slot-1 entry, i.e., that this is an "add/add" conflict. You can then use a rather user-unfriendly variant of git checkout-index to get the two conflicting-input files into temporary files.

In fact, git mergetool does all of the above for you, then runs any arbitrary command of your choice. If it's possible to run WinMerge on those files from git mergetool directly, that might be a way to deal with this.

In other words, git mergetool does the "for each conflicted merge, get the three files from Git" part of the work. Since Git didn't find a common merge base file, git mergetool provides an empty file as the merge base to the tool of your choice. You can, knowing (or checking) that this file is empty, simply throw out the file and do your "2-way merge"—really, guided diff application—to produce the correct result.

(I have no idea how to run WinMerge itself, whether as a merge tool invoked from git mergetool or any other way. So that part, you'll need to find or figure out on your own.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • I just got an idea. I create a new branch and copy-paste the other repo files on it. then commit. Then I merge it with the master branch. – Siavoshkc Apr 26 '20 at 06:23
  • When you created the new branch, what existing commit did you choose as its starting point? That commit became the merge base commit for your subsequent merge operation. – torek Apr 26 '20 at 09:14
  • Thanks. Please let me know if some other way came along. – Siavoshkc Apr 26 '20 at 17:05