0

I have to clone this repo : https://github.com/glo2003/H21-tp1-git and integrate both branches which are :

refactor/structure and fix/ingredients to the main branch

Main currently looks like this :

Framboise
Banane
Champignon

refactor/structure has a new folder called listes with the file liste.txt in it ( so the file structure is listes\listes.txt) :

- Framboise
- Banane
- Champignon

fix/ingredients :

Framboise
Banane
Pommes

The end result wanted is this one (so champignon is replaced by Pommes and the list has a new format with the ' - ' also the folder structure will be listes\listes.txt

- Framboise
- Banane
- Pommes

What i tried :

git clone https://github.com/glo2003/H21-tp1-git.git

git merge fix/ingredients

git merge refactor/structure

And i did git status to check to situation :

enter image description here

It looks like a listes.txt got deleted but not sure.

From there i wanted to fix the conflict, with vi listes.txt or vi listes\listes.txt but i don't see any Head tag for the conflict so i am a bit confused ? What am i doing wrong. Thanks a lot.

matt
  • 515,959
  • 87
  • 875
  • 1,141
codetime
  • 209
  • 2
  • 9
  • Correct, you get a conflict `liste.txt deleted in refactor/structure and modified in HEAD`. Now you must decide what you want Git to do. Did you want to accept the deletion or the modification? – matt Jan 28 '21 at 23:00
  • In other words, yes, on main you have _liste.txt_ and it is Framboise, Banane, Pommes. But in the branch `refactor/structure` you have _listes/liste.txt_ and it is -Framboise, -Banane, -Champignon. What do you want to do about this? – matt Jan 28 '21 at 23:06
  • @matt Well in refactor/structure everything is good, but the only change needed is to change Champignons to Pommes, but when i do vi listes\listes.txt to change it, there's no Head tag where the conflict is so im not sure – codetime Jan 28 '21 at 23:06
  • This is not a textual conflict, it is a file level conflict. You cannot fix it by editing a file. You fix it by adding / removing from the index. – matt Jan 28 '21 at 23:07
  • @matt well Champignons needs to be changed to Pommes and the list as a ' - ' now ( see end result) So i guess it's textual in this case ? But yea theres a new folder since liste is now in listes/liste.txt how can i fix this ? Thanks :) – codetime Jan 28 '21 at 23:11
  • You can change manually whichever file you are planning on keeping, before doing the add or rm or both. – matt Jan 28 '21 at 23:12
  • Ok, I see how to turn this into a text-level conflict you can resolve with `vi`. – matt Jan 29 '21 at 01:22

2 Answers2

2

This is a file level conflict, not a textual conflict. To fix it, you manipulate the index: you say git add to preserve a file and git rm to remove a file. Then say git commit to make it so.

For example, if you like what the restructure branch did, you can say

$ git rm liste.txt
$ git add listes/liste.txt
$ git commit -m"finish merge"

But then you have also accepted the contents of that file. If you wanted the contents to match main, you'd need to copy what's in the top level liste.txt into what's in listes/liste.txt before doing those file level things.


In the comments, you've basically said you wish this were a text-level conflict so you could resolve it at text level. To make that so, you need to:

  1. Abort the merge.

  2. Rearrange the file structure on main to match the file structure on the branch, and add and commit.

  3. Now perform the merge, specifying it this way:

     git merge -Srecursive -Xno-renames refactor/structure
    

This will give you the moved liste.txt as a text conflict that you can proceed to resolve in vi or whatever.

So, here's how I solved it, from the start:

$ git clone git@github.com:glo2003/H21-tp1-git.git
$ cd H21-tp1-git/
# create local branches
$ git checkout fix/ingredients
$ git checkout refactor/structure
$ git checkout main
$ git merge fix/ingredients
$ git merge refactor/structure
# file-level conflict, abort
$ git merge --abort
# mimic "theirs" file structure, and merge
$ mkdir listes
$ git mv liste.txt listes/liste.txt
$ git add .
$ git merge -Srecursive -Xno-renames refactor/structure
# now we have the text level conflict you wanted, as we can see:
$ cat listes/liste.txt
<<<<<<< HEAD
Framboise
Banane
Pommes
=======
- Framboise
- Banane
- Champignon
>>>>>>> refactor/structure
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • it confuses me a bit when you say it's not textual, i still need to change Champignon by Pommes ? i just go on and edit it ? with vi listes/liste.txt ? I really don't see how it's only a file problem when both files doesn't have the same text and the combination of both gives the good text. Thanks – codetime Jan 28 '21 at 23:28
  • Yeah, that's a complicated problem, see https://stackoverflow.com/questions/3491270/git-merge-apply-changes-to-code-that-moved-to-a-different-file for some discussion. – matt Jan 28 '21 at 23:50
  • OK I see how to do it. – matt Jan 29 '21 at 01:16
2

When you run git merge, git:

  1. Finds some common / shared starting-point. This is another commit, somewhere in history, from which both "you" (the person running git merge) and "they" (whoever produced the commit you're merging) started.

  2. Compares the entire contents of the merge base to your current commit. These are "your changes". In your case, you changed an existing file, liste.txt.

  3. Compares the entire contents of the merge base to their commit. These are "their changes". In their case, they removed an existing file, liste.txt, and created an entirely different file, listes/listes.txt.

  4. Combines—or attempts to combine—these two sets of changes.

Conflicts occur when the changes cannot be combined automatically using the simple rules that Git has built in. Here, the two changes that cannot be combined are:

  • you modified some file;
  • they removed that file entirely.

Git calls this a "modify/delete conflict".

Now, you claim that they renamed liste.txt to listes/listes.txt. On what basis do you make this claim? If the file contents of listes/listes.txt were exactly the same as the file contents of the former, now-deleted liste.txt, I might agree with you—and so might Git. If your basis for this claim is that liste.txt and listes.txt are sufficiently similar, well, I don't see that off-hand. Git doesn't see it either. Once you point out that if we replace line 3 and make medium-small changes to lines 1 and 2, this degree of changing would alter the contents of liste.txt to match those in the new file listes/listes.txt, then I might agree—and so might Git.

In the end, what Git does with this, to decide whether some file was renamed, is to compute what Git calls a similarity index. If the similarity index of the old file and the new file is high enough—meets or exceeds a 50%-similar threshold—Git will declare that the file was renamed, rather than deleting the left-side file (liste.txt) and creating an entirely new and different right-side file (listes/listes.txt).

You can control this rename similarity threshold, using -X find-renames=number. You can also, as in matt's answer, disable rename detection entirely, using -X no-renames. That's useful if Git is mis-classifying something as a rename when it shouldn't—but if there are some renames that Git should find, you'll have to deal with them yourself.

In general, Git's similarity detection works pretty well on large files. The reason it works so poorly on your sample file is that your sample file is tiny, and too many lines are too different.

torek
  • 448,244
  • 59
  • 642
  • 775
  • The `no-renames` was the hard part for me. I initially just aborted the merge, rearranged the structure of `main`, and asked for the merge. But instead of the text conflict, I still got a file conflict, even though the structures matched! And you know why: in `main` this was a rename, but in the branch they had moved and edited in one move and it was _not_ seen as a rename but as a deletion. Ouch! It was the `no-renames` that prevented that. – matt Jan 29 '21 at 01:34
  • I would have liked this to work the opposite way; I would have liked, in `main` to say `git mv --no-follow` or something similar, meaning, rename this file but please lose the history just as if I had done a move plus a heavy edit like in the other branch. But I couldn't find a way to do that. – matt Jan 29 '21 at 01:37
  • @matt: in this case I'd also be concerned about getting an add/add conflict, if I fixed up the directory structure in a commit before merging. But that did actually work, with the `-X no-renames`, so that's worth noting. :-) As for `git mv`, all it does is update Git's index just as `git rm` and `git add` would: there's no flag to record moves. (Well, OK, it updates *and* also renames in the work-tree, which is convenient.) – torek Jan 29 '21 at 01:38