mhagger / git-imerge

Incremental merge for git
GNU General Public License v2.0
2.68k stars 126 forks source link

imerge sometimes reports a conflict, leaving unmerged files with zero conflicts #161

Open dabrahams opened 3 years ago

dabrahams commented 3 years ago

The unmerged files have changes in them that just need to be staged, but no conflict markers

It's not terrible, but it's confusing, and it wastes time and energy in a process that is already, often, laborious… which is the whole reason we need git imerge!

mhagger commented 3 years ago

If you could reproduce this problem in the form of a failing unit test, that would be a helpful step towards solving it.

dabrahams commented 3 years ago

Yeah, I'll try to find it. I actually think this is something that happens with Git even with no imerge involved.

On Tue, Sep 22, 2020 at 5:11 AM Michael Haggerty notifications@github.com wrote:

If you could reproduce this problem in the form of a failing unit test, that would be a helpful step towards solving it.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mhagger/git-imerge/issues/161#issuecomment-696680746, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAKYIPOHXSURGOQX5YGVJTSHCIARANCNFSM4RD2OIQA .

-- -Dave

jmah commented 3 years ago

@dabrahams Are you aware of git rerere? That facility can sometimes produce the situation you describe — if you've already resolved conflicts in a 'similar' merge situation, git rerere will re-apply the recorded resolution to the merged result, but leave the result unstaged (expecting manual verification).

mhagger commented 3 years ago

Are you aware of git rerere?

This is a possibility for merges done outside of git-imerge, but git-imerge should always temporarily disable rerere whenever it initiates a merge itself. (Of course it's possible that I missed a spot, but that's the intention anyway.)

dabrahams commented 3 years ago

Also, I pretty much always keep rerere disabled, because I often have to go back over a complicated merge and rerere just perpetuates my mistakes.

CmdQ commented 2 years ago

The same for me even multiple times for that specific merge, number 4 or 5 then has a real conflict. Unfortunately the commits are too big and too proprietary to give out.

But I can show you some anonymized…

Output

> git imerge rebase master
Attempting automerge of 6-7...failure.
Attempting automerge of 1-1...success.
Attempting automerge of 1-5...failure.
Attempting automerge of 1-3...failure.
Attempting automerge of 1-2...failure.
Attempting automerge of 6-1...success.
Autofilling 1-1...success.
Autofilling 2-1...success.
Autofilling 3-1...success.
Autofilling 4-1...success.
Autofilling 5-1...success.
Autofilling 6-1...success.
Recording autofilled block MergeState('gerrit-topic', tip1='master', tip2='gerrit-topic', goal='rebase')[0:7,0:2].
Attempting automerge of 6-7...failure.
Attempting automerge of 1-2...failure.
Switched to branch 'imerge/gerrit-topic'
Auto-merging path/to/some/file.py
Auto-merging path/to/another/file.py
CONFLICT (content): Merge conflict in path/to/conflict.py
Removing setup.py
Removing setup.cfg
Removing requirements.txt
Staged 'path/to/conflict.py' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.

Original first commit:
commit 31d79731505113fc03c73601a660d30bc94ddb37 (refs/imerge/gerrit-topic/manual/1-0)
Author: Me <email@me.com>
Date:   Mon Aug 23 16:01:59 2021 +0200

    some commit message

Original second commit:
commit 5471e7539dbef11ce7eb854f98847d1e586266a3 (refs/imerge/gerrit-topic/manual/0-2)
Author: Me <email@me.com>
Date:   Mon Jun 21 10:19:31 2021 +0200

    another commit message

There was a conflict merging commit 1-2, shown above.
Please resolve the conflict, commit the result, then type

    git-imerge continue

> git status
On branch imerge/gerrit-topic
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
        modified:   .gitignore
        new file:   .python-version
        new file:   .readthedocs.yaml
        modified:   Jenkinsfile
        new file:   scripts/build_docs.sh
        deleted:    setup.cfg
        deleted:    setup.py
        modified:   path/to/some/file.py
        modified:   path/to/another/file.py
        modified:   path/to/conflict.py

Config

> git --version
git version 2.25.1
> git config --get rerere.enabled
true
> git config --get rerere.autoupdate
true
mhagger commented 2 years ago

@CmdQ:

Staged 'path/to/conflict.py' using previous resolution.

:point_up: I believe that this message comes from rerere. git-imerge must not be turning it off correctly.

git-imerge does set rerere.enabled to false when running automerges. But it could be that the rerere.autoupdate setting that you have turned on is enough to make rerere do its thing, even if rerere.enabled is false.

You could test this theory by trying the same merge, with and without the following diff applied to git-imerge:

diff --git a/gitimerge.py b/gitimerge.py
index 90997ba..f481800 100644
--- a/gitimerge.py
+++ b/gitimerge.py
@@ -653,7 +653,7 @@ class GitRepository(object):
         worktree."""

         call_silently(['git', 'checkout', '-f', commit1])
-        cmd = ['git', '-c', 'rerere.enabled=false', 'merge']
+        cmd = ['git', '-c', 'rerere.enabled=false', '-c', 'rerere.autoupdate=false', 'merge']
         if msg is not None:
             cmd += ['-m', msg]
         cmd += [commit2]

If it succeeds with this patch but fails without it, then we should merge that patch in.

But when testing, please remember that rerere stores "hidden" information, so it would be best to test repeatedly (or reset the rerere cache between tests) to be sure of getting a reproducible result.

Thanks!

CmdQ commented 2 years ago

Sorry for the late reply, I was on vacation.

Just to see if I applied to the correct change:

> which git-imerge
/home/linuxbrew/.linuxbrew/bin/git-imerge
> readlink -f /home/linuxbrew/.linuxbrew/bin/git-imerge
/home/linuxbrew/.linuxbrew/Cellar/git-imerge/1.2.0/libexec/bin/git-imerge
> find /home/linuxbrew/.linuxbrew/Cellar/git-imerge/1.2.0 -name gitimerge.py
/home/linuxbrew/.linuxbrew/Cellar/git-imerge/1.2.0/libexec/lib/python3.9/site-packages/gitimerge.py

So that's what I edited. Added the second -c option with parameter to the list but it didn't seem to solve the issue:

> git imerge rebase master
Attempting automerge of 6-7...failure.
Attempting automerge of 1-1...success.
Attempting automerge of 1-5...failure.
Attempting automerge of 1-3...failure.
Attempting automerge of 1-2...failure.
Attempting automerge of 6-1...success.
Autofilling 1-1...success.
Autofilling 2-1...success.
Autofilling 3-1...success.
Autofilling 4-1...success.
Autofilling 5-1...success.
Autofilling 6-1...success.
Recording autofilled block MergeState('gerrit-topic', tip1='master', tip2='gerrit-topic', goal='rebase')[0:7,0:2].
Attempting automerge of 6-7...failure.
Attempting automerge of 1-2...failure.
Switched to branch 'imerge/gerrit-topic'
Auto-merging path/to/some/file.py
Auto-merging path/to/another/file.py
CONFLICT (content): Merge conflict in path/to/conflict.py
Removing setup.py
Removing setup.cfg
Removing requirements.txt
Staged 'path/to/conflict.py' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.

Original first commit:
…

Original second commit:
…

There was a conflict merging commit 1-2, shown above.
Please resolve the conflict, commit the result, then type

    git-imerge continue

So it seems pretty much the same to me.

Maybe it's because rerere still remembers stuff from before I had installed git-imerge and uses that even if it's now allowed to record new stuff?

CmdQ commented 2 years ago

After I deleted everything that rerere had recorded I arrive this behavior

> rm -rf .git/rr-cache
> git imerge rebase master
Attempting automerge of 6-7...failure.
Attempting automerge of 1-1...success.
Attempting automerge of 1-5...failure.
Attempting automerge of 1-3...failure.
Attempting automerge of 1-2...failure.
Attempting automerge of 6-1...success.
Autofilling 1-1...success.
Autofilling 2-1...success.
Autofilling 3-1...success.
Autofilling 4-1...success.
Autofilling 5-1...success.
Autofilling 6-1...success.
Recording autofilled block MergeState('gerrit-topic', tip1='master', tip2='gerrit-topic', goal='rebase')[0:7,0:2].
Attempting automerge of 6-7...failure.
Attempting automerge of 1-2...failure.
Switched to branch 'imerge/gerrit-topic'
Auto-merging path/to/some/file.py
Auto-merging path/to/conflict.py
CONFLICT (content): Merge conflict in path/to/conflict.py
Removing setup.py
Removing setup.cfg
Removing requirements.txt
Recorded preimage for 'path/to/conflict.py'
Automatic merge failed; fix conflicts and then commit the result.

Original first commit:
…

Original second commit:
…

There was a conflict merging commit 1-2, shown above.
Please resolve the conflict, commit the result, then type

    git-imerge continue

It tells me there's a conflict in one file and git status finally says the same.

Note the line Automatic merge failed; fix conflicts and then commit the result. though, which happened both with the diff and without.

CmdQ commented 2 years ago

I find at least one other git merge call where it possibly should also be set, namely in

def manualmerge(self, commit, msg):
    """Initiate a merge of commit into the current HEAD."""

Maybe extract a git-calling function that simply always passes the -c flags. It probably doesn't hurt for ones that don't touch rerere.


Another idea I had: Before starting rename the current .git/rr-cache to something special, do all the work (still with the -c) and afterwards restore the directory (deleting what may have come to exist in its place).

Maybe it's a workaround, but if git rerere doesn't respect that config…