A helper for interactive rebases with conflicts

From: Johannes Schindelin <hidden>
Date: 2019-11-21 08:54:18

Team,

I frequently find my interactive rebases being interrupted with conflicts
due to already-upstreamed patches. Typically, these patches should be left
out from the todo list (because it uses the `right_only` flag of the
revision walker), but that logic can fail when passing an `--onto`
parameter: the upstreamed patch might not be part of the symmetric range
used to generate the todo list.

For these cases, I wrote this little script, which looks at the oneline of
the commit which could not be cherry-picked, tries to find it in the
commits reachable from HEAD (but not from the cherry-picked commit), and
once found, runs a range-diff:

-- snip --
#!/bin/sh

string2regex () {
	echo "$1" | sed 's/[]\\\/[*?]/\\&/g'
}

compare_to_upstream_commit () { # [<tip-commit>] [<upstream-branch>] [<commit-count>]
	tip=HEAD
	upstream=upstream/master
	count=1

	test $# -gt 0 ||
	test ! -f "$(git rev-parse --git-path rebase-merge/stopped-sha)" ||
	set -- stopped-sha

	case "$1" in
	stopped|stopped-sha)
		tip="$(cat "$(git rev-parse --git-path rebase-merge/stopped-sha)")" &&
		git rev-parse -q --verify "$tip" ||
		die "Could not get stopped-sha; Not in a rebase?\n"
		upstream=HEAD
		shift
		;;
	*[^0-9a-f]*) ;; # not a tip commit
	?*) tip="$(git rev-parse --verify "$1"^0)" || die "Could not parse $1"; shift;;
	esac

	case "$1" in
	*[^0-9]*) upstream="$1"; shift;;
	esac

	case "$1" in
	''|*[^0-9]*) ;; # not a count
	*) count=$1; shift;;
	esac

	test 0 = $# ||
	die "Unhandled argument(s): $*"

	oneline="$(git show -s --format=%s $tip)" &&
	regex="$(string2regex "$oneline")" &&
	upstream_commit="$(git rev-list --no-merges -1 --grep="$regex" $tip.."$upstream")" &&
	{ test -n "$upstream_commit" ||
		upstream_commit="$(git rev-list --no-merges -1 -i --grep="$regex" $tip.."$upstream")"; } &&
	{ test -n "$upstream_commit" ||
		upstream_commit="$(git range-diff -s $tip^..$tip $tip.."$upstream" |
			sed -n 's/^[^:]*:[^:]*=[^:]*: *\([^ ]*\).*/\1/p')"; } &&
	{ test -n "$upstream_commit" ||
		upstream_commit="$(git range-diff --creation-factor=95 -s $tip^..$tip $tip.."$upstream" |
			sed -n 's/^[^:]*:[^:]*=[^:]*: *\([^ ]*\).*/\1/p')"; } &&
	test -n "$upstream_commit" ||
	die "Could not find upstream commit for '$oneline'"

	git range-diff --creation-factor=95 "$tip~$count..$tip" "$upstream_commit~$count..$upstream_commit"
}

compare_to_upstream_commit "$@"
-- snap --

As you can see, this script does more than just handle interrupted
rebases: it also allows you to specify a tip commit, an upstream branch
and a commit count to conveniently find the tip commit in the upstream
branch and then do a range-diff of <commit-count> patches.

Obviously, the described logic fails badly when the oneline has been
changed, so the script falls back to running a full range-diff and seeing
whether it can identify the upstream commit that way. If that fails, too,
it runs the range-diff again with a looser net.

Maybe this script will prove useful to somebody else than me, too?

Ciao,
Johannes
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help