Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to redo parts of an iterative merge? #188

Open
BowiFromStackOverflow opened this issue Aug 25, 2021 · 4 comments
Open

Is it possible to redo parts of an iterative merge? #188

BowiFromStackOverflow opened this issue Aug 25, 2021 · 4 comments

Comments

@BowiFromStackOverflow
Copy link

This is more a question than an "issue". I've posted the very same question on StackOverflow already, hoping for it to just be me being unable to find the information in git-imerge documentation and got a "I do not think there is an easy way to achieve it!" comment.

The internet tells me about git-imerge:

Lets you test every intermediate state. If there is a problem, you can use "git bisect" to find the exact pairwise merge that was faulty. You can redo that merge and continue the incremental merge from there (retaining earlier pairwise merges).

But how can I do that?

To start the operation of eg. merging branch newFeature to master, I would use

git checkout master
git-imerge start --name=mergeMyNewFeatureToMaster --first-parent --goal=merge newFeature

And then resolve all the conflicts that imerge finds by itself. After finishing, I see that somewhere a bug has been inserted. I search for it using

git bisect start
git bisect good ...
git bisect bad ...
...

But when I have found the merge commit that did introduce the problem – how can I redo that merge and continue the incremental merge from there?

@mhagger
Copy link
Owner

mhagger commented Aug 25, 2021

There's no automation around that. Part of what would be required is to remove some of git-imerge's special references that it uses to keep track of progress: refs/imerge/$NAME/manual/$I-$J and refs/imerge/$NAME/auto/$I-$J. Specifically, if merge $I-$J was incorrect, you'd have to remove any references with higher $I or $J values. You might also have to modify the blob pointed at by refs/imerge/$NAME/state. I'm mostly looking at its blockers value, which might refer to later merges. I haven't tried this before, so you might have to fiddle with things.

To be clear, if this works, it would put you back in the state when the erroneous merge was done. Any merges done after that would have to be re-done. It might be possible to salvage some of the later merges, but that would be a significantly more complicated project.

If you don't want to muddle around with git-imerge's internal state, another approach would be to restart the imerge with the same arguments, and when you are asked to do a merge manually that was done right the first time, you could read the tree for the completed merge into your working copy (it would be the tree under refs/imerge/$NAME/manual/$I-$J for the $I-$J value you are working on).

Let us know if this works out!

@BowiFromStackOverflow
Copy link
Author

First of all, thank you very much for your reply! I am currently not having the problem – in my case, I was just trying git-imerge with a very small set of commits and ended up just doing a normal merge with editing undetectably conflicting lines (it was only one...) manually at the end. But I am curious on how to solve such a problem. =)

If you don't want to muddle around with git-imerge's internal state, another approach would be to restart the imerge with the same arguments, and when you are asked to do a merge manually that was done right the first time, you could read the tree for the completed merge into your working copy (it would be the tree under refs/imerge/$NAME/manual/$I-$J for the $I-$J value you are working on).

Let us know if this works out!

I have to admit that I do not understand what you are suggesting – so when I have found the "bad" merge commit by bisecting, I just rerun the git-imerge start ... command? Wouldn't this be in the completely wrong spot? And why should it ask me for a manual merge when there is no detectable conflict? I guess I am deeply misunderstanding your suggestion. ;-)

(This is a minimal undetectable conflict example I wrote for testing right now – a few people at a party; after the merge of the log lines we see that Adam is drinking wine despite Caesar having drunk all of it earlier: https://gist.github.com/BowiFromStackOverflow/5575fd41cf8944b72557083f359914ae)

@mhagger
Copy link
Owner

mhagger commented Aug 26, 2021

I have to admit that I do not understand what you are suggesting – so when I have found the "bad" merge commit by bisecting, I just rerun the git-imerge start ... command? Wouldn't this be in the completely wrong spot? And why should it ask me for a manual merge when there is no detectable conflict? I guess I am deeply misunderstanding your suggestion. ;-)

For concreteness, let's suppose that you are merging branch into main. So you'd start by doing something like

git checkout main
git imerge merge branch

At some point during this imerge, or even after you've completed the imerge but before you run git imerge finish, you notice that there is a failure, which you assume to have happened in one of the manual sub-merges.

At this point you can run bisect between main and the commit that you noticed to be broken. This should show you which $I-$J submerge introduced the failure, assuming that it's one of the merges that was done explicitly.

You could now start a new incremental merge with the same initial arguments:

git checkout main
git imerge merge --name=take2 branch

It should present the exact same manual merges to you, in the same order. So for example if it asks you to do merge $I-$J, instead of doing the merge manually again, you could copy in the tree from the first imerge. The default name of that imerge is the name of the branch, branch, so run

git checkout refs/imerge/branch/manual/$I-$J -- .

(I think that's the right command). Then run your tests to see if the merge results in your problem. If so, fix it before running git imerge continue, and from this point on you'll probably have to redo the rest of the merges manually.

This will always work if you used --goal=full or --manual for the first merge, because then all submerges will have been filled in. But those options are very expensive, so in most cases you won't be guaranteed to find the very first broken sub-merge.

This could work better if git-imerge were taught to check not only whether a sub-merge completes without conflicts, but also runs a user-specified test against it before considering it a success. This would allow git-imerge to (usually) find the exact erroneous sub-merge by itself and hand control over to the user when that happens. It was implemented in #64 but that never got merged (probably my fault).

@BowiFromStackOverflow
Copy link
Author

I have to admit that I do not understand what you are suggesting – so when I have found the "bad" merge commit by bisecting, I just rerun the git-imerge start ... command? Wouldn't this be in the completely wrong spot? And why should it ask me for a manual merge when there is no detectable conflict? I guess I am deeply misunderstanding your suggestion. ;-)

For concreteness, let's suppose that you are merging branch into main. So you'd start by doing something like

git checkout main
git imerge merge branch

That's right so far (even if I used git-imerge start --name=includingBranchIntoMain --first-parent --goal=merge branch instead)

At some point during this imerge, or even after you've completed the imerge but before you run git imerge finish, you notice that there is a failure, which you assume to have happened in one of the manual sub-merges.

Here we're diverging: It happened in one of the automatic sub-merges (like in my example in the gist, there is no detectable conflict at all).

At this point you can run bisect between main and the commit that you noticed to be broken. This should show you which $I-$J submerge introduced the failure, assuming that it's one of the merges that was done explicitly.

You could now start a new incremental merge with the same initial arguments:

git checkout main
git imerge merge --name=take2 branch

It should present the exact same manual merges to you, in the same order. So for example if it asks you to do merge $I-$J, instead of doing the merge manually again, you could copy in the tree from the first imerge. The default name of that imerge is the name of the branch, branch, so run

git checkout refs/imerge/branch/manual/$I-$J -- .

(I think that's the right command). Then run your tests to see if the merge results in your problem. If so, fix it before running git imerge continue, and from this point on you'll probably have to redo the rest of the merges manually.

Oh, now I think I understand it! Unfortunately, it is a conflict that does not cause git-imerge to stop, so I cannot do that while running the incremental merge.

This will always work if you used --goal=full or --manual for the first merge, because then all submerges will have been filled in. But those options are very expensive, so in most cases you won't be guaranteed to find the very first broken sub-merge.

What is --manual?

This could work better if git-imerge were taught to check not only whether a sub-merge completes without conflicts, but also runs a user-specified test against it before considering it a success. This would allow git-imerge to (usually) find the exact erroneous sub-merge by itself and hand control over to the user when that happens. It was implemented in #64 but that never got merged (probably my fault).

That indeed would be great! Even if there was no way of detecting the error automatically, one could do it like this:

  • Determine the bad commit with bisect
  • Write down its two parent commits
  • Write a script that checks the commit IDs of the two parent commits of the current state and returns a failure if they are the two written-down ones
  • Redo the incremental merge – the manual merges have to get redone, but the commits of the first take are there for reference. Since the script checks for the merge that would come out bad, it detects it as conflicting and the user can solve the problem.

Or do you have another opinion on that? Of course, on the long hand, it would be even greater to have a feature that "rolls back" to a specific incremental merge commit like it was in conflict and discards all commits affected by that – but I think, it would be very much effort.

It was implemented in #64 but that never got merged (probably my fault).

I hope it didn't get lost in a merge conflict. ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants