0

Imagine the following git flow:

      R1               refactoring #1
     /  \
    /    \
---A------M1---M2---   master
    \         /
     \       /
      R2-----          refactoring #2

The code in the A-snapshot looks like

x1++;
y1++;
count++;
x2++;
y2++;

Both R1 and R2 commits are refactorings trying to make the code look a bit nicer.

R1 converts the A-snapshot code to:

count++;
x1++;
y1++;
x2++;
y2++;

R2 converts the A-snapshot code to:

x1++;
y1++;
x2++;
y2++;
count++;

You probably expect the second merge (M2) to let you resolve the merge conflict.
Surprise: git does not see a conflict here, merge M2 is silent, its result is:

count++;
x1++;
y1++;
x2++;
y2++;
count++;

Both R1 and R2 are correct, but their merge result is obviously incorrect, and we have not received any warning from git.
It would be natural to receive a conflict in M2 to have a possibility to solve the problem early.

My question:
How to convince git that moving a row (or moving a block of rows) is an atomic change (instead of independent delete + insert)?

P.S.:
Steps to reproduce the problem:

git init test
cd test
echo "x1++;" > file
echo "y1++;" >> file
echo "count++;" >> file
echo "x2++;" >> file
echo "y2++;" >> file
git add .
git commit -m"A"
git checkout -b R1
echo "count++;" > file
echo "x1++;" >> file
echo "y1++;" >> file
echo "x2++;" >> file
echo "y2++;" >> file
git commit -am"R1"
git checkout -b R2 master
echo "x1++;" > file
echo "y1++;" >> file
echo "x2++;" >> file
echo "y2++;" >> file
echo "count++;" >> file
git commit -am"R2"
git checkout master
git merge --no-ff R1
git merge --no-ff R2
ESkri
  • 1,461
  • 1
  • 1
  • 8
  • 2
    From a pure _text_ POV, git resolved it correctly. What you are facing there is a semantic conflict. I have no idea if there are easy workarounds to make git not be fooled by this (I am sure an exhaustive set of tests would definitely catch it!). – eftshift0 Jun 30 '23 at 08:41
  • 2
    Could you give the steps with commands? I tried commands according to the graph, and it raised conflicts when merging R2 to master. – ElpieKay Jun 30 '23 at 09:05
  • @eftshift0 - `git` is working with **changes** of a text. It should properly split whole textual diff into set of "atomic changes". Even pure text file may imply uniqueness of its lines (a simple shopping list, for example). – ESkri Jun 30 '23 at 09:07
  • 2
    @ElpieKay Same, tested here on a mini repo, it conflicts indeed. – Romain Valeri Jun 30 '23 at 09:09
  • ElpieKay and RomainValeri, can you try having those lines not as _the whole_ file but giving it a few empty lines after/before (IOW, having those lines in the middle of the file)? – eftshift0 Jun 30 '23 at 09:14
  • 1
    git does not go look that a line was moved.... it just sees that a line was added here, a line was removed there. When merging it will only see that the line that was originally in the middle was removed exactly the same way in both branches and so that line is removed in the end result.... also each one of the 2 branches merged added a line to the file (one branch added before, another after).... no conflict. It's all ok – eftshift0 Jun 30 '23 at 09:25
  • @eftshift0 I tried again with the 3 lines in the middle of the file, and still got the same conflicts above `x1++;`, with `count++;` vs empty. – ElpieKay Jun 30 '23 at 09:26
  • Interesting, indeed. – eftshift0 Jun 30 '23 at 09:27
  • @ElpieKay - Sorry, the example was oversimplified - more lines are needed to reproduce the problem. Now my question is updated: the example is corrected and verified. – ESkri Jun 30 '23 at 09:29
  • @RomainValeri - Question updated. Can you reproduce the problem now? – ESkri Jun 30 '23 at 09:38
  • @eftshift0 - `git does not go look that a line was moved.... it just sees that a line was added here, a line was removed there` - Yes, that's exactly the problem. git works with lines while it must work with changes. – ESkri Jun 30 '23 at 09:42
  • Proving that a line has moved would be a pretty costly operation. You would have to look at every removal and addition in the commit, then check for exactness of every line to every other. To git one line blinks out of existence and another that happenns to have the exact content is created. Content is not compared. It is just easy to see in your example. A commit can be a whole mess of those additions and deletions. – Peter Krebs Jun 30 '23 at 09:51
  • @PeterKrebs - `You would have to look at every removal and addition in the commit, then check for exactness of every line to every other.` - What about using a hash table for removed/inserted lines content? – ESkri Jun 30 '23 at 09:57
  • That would optimize the execution time, sure. But you would still compare every deletion hash to every addition hash which is costly compared to just not doing it. It would also be nice if git understood programming languages to compare function by function or something like that. But more people are doing projects using git than git can keep up with specific improvement ideas. – Peter Krebs Jun 30 '23 at 11:42
  • "Moving a line" is not something specific to a language or a project. It is common. – ESkri Jun 30 '23 at 14:21

0 Answers0