Here's a demonstration of what I came up with after 15 minutes of hacking. It's not a complete solution to the posed problem, but it should cut down on the work involved.
The goal is to use git bisect
to find the earliest conflict-free merge point for a future commit. The solution takes advantage of the binary search capability inherent in git bisect
in order to cut down on the steps.
Unfortunately, this does not preclude later commits from conflicting, so an interactive rebase is required to vet the results (but that's the point, anyway).
The one disadvantage/caveat is that you have to reverse the sense of good
and bad
in your head when you instruct git about whether the step failed or succeeded when testing the patch.
If any of the steps below are unclear, let me know and I'll try to elaborate.
First create the following file in a series of commits. Each commit should add a series of four identical lines (a's, then b's, then c's, then d's).
a
a
a
a
b
b
b
b
c
c
c
c
d
d
d
d
At this point, git log
should output something like:
commit 6f2b809863632a86cc0523df3a4bcca22cf5ab17
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:45:44 2011 -0500
Added d.
commit 91ba7e6f19db74adb6ce79e7b85ea965788f6b88
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:44:26 2011 -0500
Added c.
commit f83beee55d6e060536584852ebb55c5ac3b850b2
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:44:00 2011 -0500
Added b.
commit d6d924b0a30a9720f6e01dcc79dc49097832a587
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:43:38 2011 -0500
Added a.
commit 74d41121470108642b1a5df087bc837fdf77d31c
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:43:11 2011 -0500
Initial commit.
Now edit the file, so that it contains the following, and commit this:
a
a
a
a
b
x
x
b
c
x
x
c
d
d
d
d
The log should now include one more commit:
commit 09f247902a9939cb228b580d39ed2622c3211ca6
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:46:36 2011 -0500
Replaced a few lines with x.
Now generate a patch for the X
commit.
git diff -p master~ > x.patch
Crank up bisect
-- remember to use git bisect good
when the patch fails and git bisect bad
when the patch succeeds:
$ git bisect start
$ git bisect good 74d41121470108642b1a5df087bc837fdf77d31c
$ git bisect bad master
Bisecting: 2 revisions left to test after this (roughly 1 step)
[f83beee55d6e060536584852ebb55c5ac3b850b2] Added b.
$ patch --dry-run -p1 < x.patch
patching file file.txt
Hunk #1 FAILED at 3.
1 out of 1 hunk FAILED -- saving rejects to file file.txt.rej
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[6f2b809863632a86cc0523df3a4bcca22cf5ab17] Added d.
$ patch --dry-run -p1 < x.patch
patching file file.txt
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[91ba7e6f19db74adb6ce79e7b85ea965788f6b88] Added c.
$ patch --dry-run -p1 < x.patch
patching file file.txt
Hunk #1 succeeded at 3 with fuzz 2.
$ git bisect bad
91ba7e6f19db74adb6ce79e7b85ea965788f6b88 is the first bad commit
commit 91ba7e6f19db74adb6ce79e7b85ea965788f6b88
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:44:26 2011 -0500
Added c.
$ git bisect reset
As expected, the edits in commit X
can be moved immediately after commit C
. An interactive rebase confirms this:
91e92489 * Added d.
6c082b1f * Replaced a few lines with x.
a60ae2a9 * Added c.
4d5e78f2 * Added b.
7d2ff759 * Added a.
74d41121 * Initial commit.