From: Phillip Wood <phillip.wood123@gmail.com>
To: Colin Stagner <ask+git@howdoi.land>,
git@vger.kernel.org, phillip.wood@dunelm.org.uk
Cc: Zach FettersMoore <zach.fetters@apollographql.com>,
Christian Couder <chriscool@tuxfamily.org>,
Patrik Weiskircher <patrik@pspdfkit.com>
Subject: Re: [PATCH v2] contrib/subtree: fix split with squashed subtrees
Date: Mon, 8 Sep 2025 16:21:38 +0100 [thread overview]
Message-ID: <b78639ee-021d-49fc-8b8d-0140ed8fc010@gmail.com> (raw)
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:
>
> -# 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}"?
> 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
> + 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
> +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
> + 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
next prev parent reply other threads:[~2025-09-08 15:21 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-24 19:10 [PATCH] contrib/subtree: fix split with squashed subtrees Colin Stagner
2025-09-01 13:54 ` Phillip Wood
2025-09-01 20:43 ` Colin Stagner
2025-09-02 13:22 ` Phillip Wood
2025-09-02 14:57 ` Phillip Wood
2025-09-04 1:34 ` Colin Stagner
2025-09-05 2:27 ` [PATCH v2] " Colin Stagner
2025-09-08 15:21 ` Phillip Wood [this message]
2025-09-10 1:56 ` Colin Stagner
2025-09-10 2:02 ` Junio C Hamano
2025-09-10 3:00 ` Colin Stagner
2025-09-10 15:10 ` Junio C Hamano
2025-09-10 3:11 ` [PATCH v3] " Colin Stagner
2025-09-10 9:39 ` Phillip Wood
2025-09-11 16:01 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=b78639ee-021d-49fc-8b8d-0140ed8fc010@gmail.com \
--to=phillip.wood123@gmail.com \
--cc=ask+git@howdoi.land \
--cc=chriscool@tuxfamily.org \
--cc=git@vger.kernel.org \
--cc=patrik@pspdfkit.com \
--cc=phillip.wood@dunelm.org.uk \
--cc=zach.fetters@apollographql.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).