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

Re: [PATCH v2] contrib/subtree: fix split with squashed subtrees

From
PWPhillip Wood <phillip.wood123@gmail.com>
Date
Sep 8, 2025, 15:21 UTC
Message-ID
<b78639ee-021d-49fc-8b8d-0140ed8fc010@gmail.com>
In-Reply-To
<20250905022728.940664-1-ask+git@howdoi.land>
Hi Colin

This is looking good. I've left a few comments below, especially on the new test.

On 05/09/2025 03:27, Colin Stagner wrote:
Show 28 quoted lines
>   
> -# Usage: check if a commit from another subtree should be
> +# Usage: should_ignore_subtree_split_commit REV
> +#
> +# Check if REV is a commit from another subtree and should be
>   # ignored from processing for splits
>   should_ignore_subtree_split_commit () {
>   	assert test $# = 1
> -	local rev="$1"
> +
> +	git show \
> +		--no-patch \
> +		--no-show-signature \
> +		--format='%(trailers:key=git-subtree-dir,key=git-subtree-mainline)' \
> +		"$1" |
> +	(
> +	have_mainline=
> +	subtree_dir=
> +
> +	while read -r trailer val
> +	do
> +		case "$trailer" in
> +		(git-subtree-dir:)
> +			subtree_dir="${val%/}" ;;
> +		(git-subtree-mainline:)
> +			have_mainline=y ;;
> +		esac
> +	done

This looks good, we run git log once and then parse the trailers. We do not use the optional '(' in case statements in our code though.

> -	if test -n "$(git log -1 --grep="git-subtree-dir:" $rev)"
> +	if test -n "${subtree_dir:-}" &&
> +		test -z "${have_mainline:-}" &&
> +		test "${subtree_dir}" != "$arg_prefix"

If we have a git-subtree-dir: trailer whose value does not match the subtree we're interested in and there is no git-subtree-mainline: trailer then we skip this commit - good. What's the idea behind using "${var:-}" rather than "{var}"?

Show 43 quoted lines
>   	then
> -		if test -z "$(git log -1 --grep="git-subtree-mainline:" $rev)" &&
> -			test -z "$(git log -1 --grep="git-subtree-dir: $arg_prefix$" $rev)"
> -		then
> -			return 0
> -		fi
> +		return 0
>   	fi
>   	return 1
> +	)
>   }
>   
>   # Usage: process_split_commit REV PARENTS
> diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
> index ca4df5be83..8bd45e7be7 100755
> --- a/contrib/subtree/t/t7900-subtree.sh
> +++ b/contrib/subtree/t/t7900-subtree.sh
> @@ -67,6 +67,34 @@ test_create_pre2_32_repo () {
>   	git -C "$1-clone" replace HEAD^2 $new_commit
>   }
>   
> +# test_create_subtree_add REPO ORPHAN PREFIX FILENAME ...
> +#
> +# Create a simple subtree on a new branch named ORPHAN in REPO.
> +# The subtree is then merged into the current branch of REPO,
> +# under PREFIX. The generated subtree has has one commit
> +# with subject and tag FILENAME with a single file "FILENAME.t"
> +#
> +# When this method returns:
> +# - the current branch of REPO will have file PREFIX/FILENAME.t
> +# - REPO will have a branch named ORPHAN with subtree history
> +#
> +# additional arguments are forwarded to "subtree add"
> +test_create_subtree_add () {
> +	(
> +		cd "$1" &&
> +		orphan="$2" &&
> +		prefix="$3" &&
> +		filename="$4" &&
> +		shift 4 &&
> +		last="$(git branch --show-current)" &&
> +		git checkout --orphan "$orphan" &&
> +		git rm -rf . &&
If you use "git switch --orphan" that clears the worktree for you
> +		test_commit "$filename" &&
> +		git checkout "$last" &&

I think this could be "git checkout @{-1}" and then we'd avoid having to run "git branch" above

Show 20 quoted lines
> +		git subtree add --prefix="$prefix" "$@" "$orphan"
> +	)
> +}
> +
>   test_expect_success 'shows short help text for -h' '
>   	test_expect_code 129 git subtree -h >out 2>err &&
>   	test_must_be_empty err &&
> @@ -425,6 +453,48 @@ test_expect_success 'split with multiple subtrees' '
>   		--squash --rejoin -d -m "Sub B Split 1" 2>&1 | grep -w "\[1\]")" = ""
>   '
>   
> +# When subtree split-ing a directory that has other subtree
> +# *merges* underneath it, the split must include those subtrees.
> +# This test creates a nested subtree, `subA/subB`, and tests
> +# that the tree is correct after a subtree split of `subA/`.
> +# The test covers:
> +# - An initial `subtree add`; and
> +# - A follow-up `subtree merge`
> +# both with and without `--squashed`.
> +for is_squashed in '' 'y';
no need for ';' at the end of the line
Show 8 quoted lines
> +do
> +	test_expect_success "split keeps nested ${is_squashed:+--squash }subtrees that are part of the split" '
> +		subtree_test_create_repo "$test_count" &&
> +		(
> +			cd "$test_count" &&
> +			mkdir subA &&
> +			test_commit subA/file1 &&
> +			git branch -m main &&
For tests that depend on the default branch name you can add
	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
	export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME

to the start of the file before it sources test-lib.sh. Or we could change subtree_create_repo() to pass all its arguments along to test_create_repo() and use "subtree_create_repo --initial-branch=main $test_count"

> +			test_create_subtree_add \
> +				. mksubtree subA/subB file2 ${is_squashed:+--squash} &&
> +			test -e subA/file1.t &&

We have test_path_is_file() for this which prints a useful diagnostic message it it fails

Thanks
Phillip
Show 27 quoted lines
> +			test -e subA/subB/file2.t &&
> +			git subtree split --prefix=subA --branch=bsplit &&
> +			git checkout bsplit &&
> +			test -e file1.t &&
> +			test -e subB/file2.t &&
> +			git checkout mksubtree &&
> +			git branch -D bsplit &&
> +			test_commit file3 &&
> +			git checkout main &&
> +			git subtree merge \
> +				${is_squashed:+--squash} \
> +				--prefix=subA/subB mksubtree &&
> +			test -e subA/subB/file3.t &&
> +			git subtree split --prefix=subA --branch=bsplit &&
> +			git checkout bsplit &&
> +			test -e file1.t &&
> +			test -e subB/file2.t &&
> +			test -e subB/file3.t
> +		)
> +	'
> +done
> +
>   test_expect_success 'split sub dir/ with --rejoin from scratch' '
>   	subtree_test_create_repo "$test_count" &&
>   	test_create_commit "$test_count" main1 &&
> 
> base-commit: 09669c729af92144fde84e97d358759b5b42b555
Previous: Colin StagnerNext: Colin Stagner
Message 8 of 15 in “contrib/subtree: fix split with squashed subtrees”
  1. contrib/subtree: fix split with squashed subtreesColin Stagner, Aug 24, 2025
  2. Phillip WoodSep 1, 2025
  3. Colin StagnerSep 1, 2025
  4. Phillip WoodSep 2, 2025
  5. Phillip WoodSep 2, 2025
  6. Colin StagnerSep 4, 2025
  7. contrib/subtree: fix split with squashed subtreesColin Stagner, Sep 5, 2025
  8. Phillip WoodSep 8, 2025
  9. Colin StagnerSep 10, 2025
  10. Junio C HamanoSep 10, 2025
  11. Colin StagnerSep 10, 2025
  12. Junio C HamanoSep 10, 2025
  13. contrib/subtree: fix split with squashed subtreesColin Stagner, Sep 10, 2025
  14. Phillip WoodSep 10, 2025
  15. Junio C HamanoSep 11, 2025

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.