edit: To retroactively enable rerere, see here.. The script in this answer doesn't turn rerere on and will behave differently than rerere would in the presence of further changes, in E
's history (see below), to n...D
-conflicted hunks resolved in o
. rerere
does an n...E
merge and applies any n...D
resolutions that are still valid, while this script does an standard o...E
merge (but replaces parent o
with o
)
Copying the graph for reference:
before desired after
Master A\--B--C\--D\....E \ Master A\--B--C\-D...E\
Branch l--m---n---o--p--q / Branch l--m---n------o'--p'--q'
rerere will thus bypass E
-reverted n...D
conflicts, while the script will treat the reversion as a further change. Both scripts produce the "desired after" graph above, but they get there by different routes.
An hour's trying hasn't produced any more comprehensible further textual description of the differences than simply looking at the graphs and deducing.
It's not clear to me that either rerere's or the script's behavior is always better.
When I do
go back to o, merge E
with
git checkout o
git merge E
I get
Master A\--B--C\--D\---------E
Branch l--m---n---o\-p--q \
HEAD `--------o'
with o
for o'
's parent. You do want the o'
content (to preserve any conflict resolutions you'd already applied in o
), but you want it to have n
and E
as parents, not o
and E
. That's actually fairly easy.
So the complete procedure is,
#!/bin/sh
# 1. identify the most recent merge on $1
# 2. verify that it's from $2
# 3. update the merge to $2's current state,
# 4. preserving any conflict resolutions that were already done
# 5. and cherry-pick everything since.
# most recent merge in $1
update=`git rev-list -1 --min-parents=2 $1`
# verify most recent merge is from correct merge base
test "`git merge-base $1 $2`" = "`git rev-parse $update^2`" \
|| { echo "most recent merge isn't of $1 from $2"; exit 1; }
set -e # exit immediately on error
git checkout $update
git merge $2
git checkout $(
# this makes a new commit with HEAD's commit message and tree
# but with $update^ and $2 as parents
git cat-file -p HEAD \
| sed 1,/^$/d \
| git commit-tree -p $update^ -p $2 HEAD^{tree}
)
test $update = `git rev-parse $1` || git cherry-pick $update..$1
git checkout -B $1
If you're willing to lose any conflict resolutions done in o
you can instead do (from the set -e
)
set -e
git checkout $update^
git merge $2
test $update = `git rev-parse $1` || git cherry-pick $update..$1
git checkout -B $1
If doing work in a subshell and exiting from that to continue isn't any trouble you can make this much less fragile by doing
git merge $2 \
|| { echo "Resolve conflicts and \`exit\`, or \`exit 1\` to not continue"; $SHELL; }
edit: update to handle updating the merge when there's nothing to cherry-pick.