git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: "bubbling up" patches in a commit sequence.

From
Johannes Schindelin <johannes.schindelin@gmx.de>
Date
Aug 15, 2022, 12:25 UTC
Message-ID
<7o9n6751-2083-155s-02op-o5635o4qr278@tzk.qr>
In-Reply-To
<CANgJU+VYSuEkU+V0WRpsTPv9iPYeDo52MeMHuD7-Yp4JnA60NA@mail.gmail.com>
Hi Yves,
On Sat, 13 Aug 2022, demerphq wrote:
> I keep finding myself using interactive rebase to try to find the
> earliest place in a change sequence that a given commit can be placed
> without conflicting with any other patch.

I find myself doing that a lot, too. So much so that I wrote shell code to do that for me. The essential idea is to use the diff hunk header of the hunk that I want to stage and transmogrify it into the `-L <start>,<end>:<path>` parameter of `git log` (and yes, the `sed` call to transmogrify that is a bit hard to read).

The relevant part of the code looks like this:
-- snip --
sh_quote () {
	for arg
	do
		echo "'$(echo "$arg" | sed "s/'/'\\''/g")'"
	done
}
staged_log () { # [--upstream=<ref> | -u <ref>]
	upstream=not\ set
	while case "$1" in
	--upstream) shift; upstream="$1";;
	--upstream=*) upstream="${1#*=}";;
	-u) shift; upstream="$1";;
	-*) die "Unknown option: $1";;
	*) break;;
	esac; do shift; done
	test not\ set != "$upstream" ||
	upstream="$(git rev-parse @{upstream} 2>/dev/null)"
	# look beyond upstream if identical to HEAD
	test -z "$upstream" || test 0 != $(git rev-list --count $upstream..) || upstream=
	diff="$(git diff --cached -U1)"
	cached_diff="$diff"
	test -n "$diff" ||
	diff="$(git diff -U1)"
	test -n "$diff" ||
	die "No changes"
	args="$(echo "$diff" |
		sed -ne '/^--- a\//{s/^-* a\/\(.*\)/'\''\1'\''/;x}' -e \
			'/^@@ -/{s/^@@ -\([^, ]*\),\([^ ]*\).*/-L \1,+\2/;s/^@@ -\([^,]*\) .*/-L \1,+1/;G;s/\n/:/g;p}' |
			tr '\n' ' ') ${upstream:+$upstream..} $(sh_quote "$@")"
	eval "git log $args"
	revs="$(eval "git log --pretty=%H --no-patch $args")"
	case "$revs" in
	*[!0-9a-z]*) ;; # multiple revs
	'')
		# not a single rev
		test -z "$upstream" ||
		staged_log -u ''
		;;
	?*)
		printf "Commit (yes/no/edit)? "
		read line
		case "$line" in
		[Yy]*) git commit --fixup "$revs" $(test -n "$cached_diff" || echo "-a");;
		[Ee]*) git commit --fixup "$revs" $(test -n "$cached_diff" || echo "-a") -se;;
		esac
		;;
	esac
}
-- snap --

Unfortunately, the `-L <...>` code currently works reliably only for a single hunk, if I use multiple hunks, I sometimes run into assertions.

To help with that, the shell code looks at the staged hunk(s), if any. Only if no changes are staged, it falls back to the unstaged diff.

Ciao, Dscho

Previous: Jeff King
Message 3 of 3 in “"bubbling up" patches in a commit sequence.”
  1. demerphqAug 13, 2022
  2. Jeff KingAug 14, 2022
  3. Johannes SchindelinAug 15, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.