threads / discuss / 58301

"bubbling up" patches in a commit sequence.

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

## tl;dr

3 messages between Aug 13, 2022 and Aug 15, 2022.

replies: 2people: 3as markdown or json

demerphq· Aug 13, 2022, 08:45 UTC · lore
Hi all,

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. When I am tired I do this by repeatedly moving the given commit up by one or two lines manually and then letting rebase interactive apply the new ordering, when it conflicts I abort and stop and either leave the patch in its new position or more likely use the "fixup" option to merge it with the patch it conflicts with. Sometimes when I am more awake I try to do a binary search pattern :-), but regardless the process is tedius. I call this "bubbling up a patch".

In general I do this when I want to find a "fixup" pair that should be merged together before the PR is pushed, but there are other reasons, sometimes a PR contains a number of sub topics which are evolving and using this technique can help the patches related to different sub topics be grouped together for easier review.

Anybody created tooling to do something like this? Or suggestions on how to approach it efficiently?

For instance i would love to have tool that could give me a list of the patches in my topic branch with information about which commits they have to be after to not conflict (or some other way to understand the "conflict properties" of the commit graph), and a way to move a commit to the earliest position in the patch sequence where it would not commit.

Please forgive me for not using the correct specialist git jargon for these concepts, if there is any.

cheers, Yves

-- 
perl -Mre=debug -e "/just|another|perl|hacker/"
Jeff King· Aug 14, 2022, 06:43 UTC · re: demerphq · lore

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

On Sat, Aug 13, 2022 at 10:45:53AM +0200, demerphq wrote:
Show 8 quoted lines
> In general I do this when I want to find a "fixup" pair that should be
> merged together before the PR is pushed, but there are other reasons,
> sometimes a PR contains a number of sub topics which are evolving and
> using this technique  can help the patches related to different sub
> topics be grouped together for easier review.
> 
> Anybody created tooling to do something like this? Or suggestions on
> how to approach it efficiently?

I haven't used these myself, but there are some projects that might do what you want:

   - https://github.com/tummychow/git-absorb
   - https://github.com/torbiak/git-autofixup
> Please forgive me for not using the correct specialist git jargon for
> these concepts, if there is any.

I think the key term is "absorb", but don't feel bad. I didn't remember that either, but only had a vague feeling I had seen something recently. I skimmed the "tools" section of the last couple issues of Git Rev News (thanks, Rev News editors!) which led me to autofixup, which mentioned "absorb". :)

-Peff
Johannes Schindelin· Aug 15, 2022, 12:25 UTC · re: demerphq · lore

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

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

← back to recent threads