{"thread":{"id":"65001","subject":"[PATCH 1/3] contrib/subtree: capture additional test-cases","startedAt":"2026-02-15T20:19:34Z","lastAt":"2026-02-18T02:25:51Z","messageCount":6,"participants":["Colin Stagner","D. Ben Knoble"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"536066","messageId":"20260215201906.889951-2-ask+git@howdoi.land","threadId":"65001","inReplyTo":"20260215201906.889951-1-ask+git@howdoi.land","subject":"[PATCH 1/3] contrib/subtree: capture additional test-cases","fromName":"Colin Stagner","fromEmail":"ask+git@howdoi.land","sentAt":"2026-02-15T20:18:45Z","receivedAt":"2026-02-15T20:19:34Z","isPatch":true,"sender":{"key":"ask+git@howdoi.land","avatar":null},"body":"Patch series e7b07376e5 (Merge branch 'rs/subtree-fixes',\n2018-10-26) corrects several defects in `git subtree split`.\nThe defects affect `split --rejoin` and merge commit processing.\n\nThere is no test coverage for this, and e7b07376e5 did not\nintroduce any.\n\nConvert the minimum working example [1] from the original patch\nsubmission [2] into test cases.\n\n[1]: https://gist.github.com/FoxFireX/1b794384612b7fd5e7cd157cff96269e\n\n[2]: <20180928183540.48968-1-roger.strain@swri.org>\n\nSigned-off-by: Colin Stagner <ask+git@howdoi.land>\n---\n contrib/subtree/t/t7900-subtree.sh | 110 +++++++++++++++++++++++++++++\n 1 file changed, 110 insertions(+)\n\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex e7040718f2..3ee2f95d86 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -1575,6 +1575,116 @@ test_expect_success 'push split to subproj' '\n \t)\n '\n \n+# --ignore-joins must ignore mainline content outside of the\n+# subtree. This test verifies that the logic in\n+# `find_existing_splits()` correctly handles a `git subtree add`\n+# In this test, the split history must not contain a commit titled\n+#\n+#     Add 'sub/' from commit ...\n+#\n+# see: dd21d43b58 (subtree: make --ignore-joins pay\n+#      attention to adds, 2018-09-28)\n+test_expect_success 'split --ignore-joins respects subtree add' '\n+\tsubtree_test_create_repo \"$test_count\" &&\n+\t(\n+\t\tcd \"$test_count\" &&\n+\t\ttest_commit main_must_not_be_in_subtree &&\n+\t\ttest_create_subtree_add . mksubtree sub sub1 &&\n+\t\ttest_commit sub/sub2 &&\n+\t\ttest_commit main_must_not_be_in_subtree2 &&\n+\t\tgit subtree split --prefix sub -b first_split --rejoin &&\n+\t\ttest_commit sub/sub3 &&\n+\t\tno_ignore_joins=\"$(git subtree split --prefix sub -b no_ignore_joins)\" &&\n+\t\tignore_joins=\"$(git subtree split --prefix sub --ignore-joins -b ignore_joins)\" &&\n+\t\tgit checkout ignore_joins &&\n+\t\ttest_path_is_file sub1.t &&\n+\t\ttest_path_is_file sub2.t &&\n+\t\ttest_path_is_file sub3.t &&\n+\t\t! test_path_is_file main_must_not_be_in_subtree.t &&\n+\t\t! test_path_is_file main_must_not_be_in_subtree2.t &&\n+\t\ttest -z \"$(git log -1 --grep \"Add '''sub/''' from commit\" ignore_joins)\" &&\n+\t\ttest \"$no_ignore_joins\" = \"$ignore_joins\" &&\n+\t\ttest \"$(git rev-list --count ignore_joins)\" -eq 3;\n+\t)\n+'\n+\n+# split excludes commits reachable from any previous --rejoin.\n+# These ignored commits can still be the basis for new work\n+# after the --rejoin. These commits must be processed, even\n+# if they are excluded. Otherwise, the split history will be\n+# incorrect.\n+#\n+# here, the merge\n+#\n+#     git merge --no-ff new_work_based_on_prejoin\n+#\n+# doesn't contain any subtree changes and so should not end\n+# up in the split history. this subtree should be flat,\n+# with no merges.\n+#\n+# see: 315a84f9aa (subtree: use commits before rejoins for\n+#      splits, 2018-09-28)\n+test_expect_success 'split links out-of-tree pre --rejoin commits with post --rejoin commits' '\n+\tsubtree_test_create_repo \"$test_count\" &&\n+\t(\n+\t\tcd \"$test_count\" &&\n+\t\ttest_commit main_must_not_be_in_subtree &&\n+\t\tmkdir sub &&\n+\t\ttest_commit sub/sub1 &&\n+\t\ttest_commit sub/sub2 &&\n+\t\tgit subtree split --prefix sub --rejoin &&\n+\t\ttest \"$(git rev-list --count HEAD)\" -eq 6 &&\n+\t\tgit checkout sub/sub1 &&\n+\t\tgit checkout -b new_work_based_on_prejoin &&\n+\t\ttest_commit main_must_not_be_in_subtree2 &&\n+\t\tgit checkout main &&\n+\t\tgit merge --no-ff new_work_based_on_prejoin &&\n+\t\ttest_commit sub/sub3 &&\n+\t\tgit subtree split -d --prefix sub -b second_split &&\n+\t\tgit checkout second_split &&\n+\t\ttest_path_is_file sub1.t &&\n+\t\ttest_path_is_file sub2.t &&\n+\t\ttest_path_is_file sub3.t &&\n+\t\t! test_path_is_file main_must_not_be_in_subtree.t &&\n+\t\t! test_path_is_file main_must_not_be_in_subtree2.t &&\n+\t\ttest \"$(git rev-list --count --merges second_split)\" -eq 0 &&\n+\t\ttest \"$(git rev-list --count second_split)\" -eq 3;\n+\t)\n+'\n+\n+# split must keep merge commits with unrelated histories, even\n+# if both parents are treesame. When deciding whether or not\n+# to eliminate a parent, copy_or_skip compares the merge-base\n+# of each parent.\n+#\n+# in the split_of_merges branch:\n+#\n+#   * expect 4 commits\n+#   * HEAD~ must be a merge\n+#\n+# see: 68f8ff8151 (subtree: improve decision on merges kept\n+#      in split, 2018-09-28)\n+test_expect_success 'split preserves merges with unrelated history' '\n+\tsubtree_test_create_repo \"$test_count\" &&\n+\t(\n+\t\tcd \"$test_count\" &&\n+\t\ttest_commit main_must_not_be_in_subtree &&\n+\t\tmkdir sub &&\n+\t\ttest_commit sub/sub1 &&\n+\t\tgit checkout --orphan new_history &&\n+\t\tgit checkout sub/sub1 -- . &&\n+\t\tgit add . &&\n+\t\tgit commit -m \"treesame history but not a merge-base\" &&\n+\t\tgit checkout main &&\n+\t\tgit merge --allow-unrelated-histories --no-ff new_history &&\n+\t\ttest \"$(git rev-parse \"HEAD^1^{tree}\")\" = \"$(git rev-parse \"HEAD^2^{tree}\")\" &&\n+\t\ttest_commit sub/sub2 &&\n+\t\tgit subtree split -d --prefix sub -b split_of_merges &&\n+\t\ttest \"$(git rev-list --count split_of_merges)\" -eq 4 &&\n+\t\ttest -n \"$(git rev-list --merges HEAD~)\";\n+\t)\n+'\n+\n #\n # This test covers 2 cases in subtree split copy_or_skip code\n # 1) Merges where one parent is a superset of the changes of the other\n-- \n2.43.0\n\n"},{"id":"536067","messageId":"20260215201906.889951-1-ask+git@howdoi.land","threadId":"65001","inReplyTo":null,"subject":"[PATCH 0/3] contrib/subtree: process out-of-prefix subtrees","fromName":"Colin Stagner","fromEmail":"ask+git@howdoi.land","sentAt":"2026-02-15T20:18:44Z","receivedAt":"2026-02-15T20:19:34Z","isPatch":true,"sender":{"key":"ask+git@howdoi.land","avatar":null},"body":"* cs/subtree-remove-optimization:\n  Remove an optimization that can exclude too much\n  history during a \"subtree split.\"\n\nRemove an optimization introduced in 98ba49ccc2 (subtree: fix split\nprocessing with multiple subtrees present, 2023-12-01). The\noptimization incorrectly excludes commits from the split history\nthat must be included.\n\nThe above commit, and my later attempt to fix it in 83f9dad7d6\n(contrib/subtree: fix split with squashed subtrees, 2025-09-09),\nboth introduce known regressions.\n\nImprove test coverage for `copy_or_skip`, which will help detect\nfuture regressions.\n\nColin Stagner (3):\n  contrib/subtree: capture additional test-cases\n  contrib/subtree: test history depth\n  contrib/subtree: process out-of-prefix subtrees\n\n contrib/subtree/git-subtree.sh     |  50 +-------\n contrib/subtree/t/t7900-subtree.sh | 198 +++++++++++++++++++++++++++--\n 2 files changed, 190 insertions(+), 58 deletions(-)\n\n\nbase-commit: 852829b3dd2fe4e7c7fc4d8badde644cf1b66c74\n-- \n2.43.0\n\n"},{"id":"536068","messageId":"20260215201906.889951-4-ask+git@howdoi.land","threadId":"65001","inReplyTo":"20260215201906.889951-1-ask+git@howdoi.land","subject":"[PATCH 3/3] contrib/subtree: process out-of-prefix subtrees","fromName":"Colin Stagner","fromEmail":"ask+git@howdoi.land","sentAt":"2026-02-15T20:18:47Z","receivedAt":"2026-02-15T20:19:53Z","isPatch":true,"sender":{"key":"ask+git@howdoi.land","avatar":null},"body":"`should_ignore_subtree_split_commit` detects subtrees which are\noutside of the current path --prefix and ignores them. This can\nspeed up splits of repositories that have many subtrees.\n\nSince its inception [1], every iteration of this logic [2], [3]\nincorrectly excludes commits. This alters the split history. The\nsplit history and its commit hashes are API contract, so this is\nnot permissible.\n\nWhile a commit from a different subtree may look like it doesn't\ncontribute anything to a split, sometimes it does. Merge commits\nare a particular hot spot. For these, the pruning logic in\n`copy_or_skip` performs:\n\n1. a check for \"treesame\" parents\n2. two different common ancestry checks\n\nThese checks operate on the **split history**, not the input\nhistory. The split history omits commits that do not affect the\n--prefix. This can significantly alter the ancestry of a merge.\nIn order to determine if `copy_or_skip` will skip a merge, it\nis likely necessary to compute all the split history... which\nis what `should_ignore_subtree_split_commit` tries to avoid.\n\nTo make this logic API-preserving, we could gate it behind a\nnew CLI argument. The present implementation is actually a\nspeed penalty in many cases, however, so this is not done here.\n\nRemove the `should_ignore_subtree_split_commit` logic. This\nfixes the regression reported in [4].\n\n[1]: 98ba49ccc2 (subtree: fix split processing with multiple subtrees present, 2023-12-01)\n\n[2]: 83f9dad7d6 (contrib/subtree: fix split with squashed subtrees, 2025-09-09)\n\n[3]: 28a7e27cff (contrib/subtree: detect rewritten subtree commits, 2026-01-09)\n\n[4]: <20251230170719.845029-1-george@mail.dietrich.pub>\n\nReported-by: George <george@mail.dietrich.pub>\n\nReported-by: Christian Heusel <christian@heusel.eu>\n\nSigned-off-by: Colin Stagner <ask+git@howdoi.land>\n---\n contrib/subtree/git-subtree.sh     | 50 +---------------------\n contrib/subtree/t/t7900-subtree.sh | 68 ++++++++++++++++++++++++++++--\n 2 files changed, 65 insertions(+), 53 deletions(-)\n\ndiff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh\nindex 17106d1a72..ba9fb2ee5d 100755\n--- a/contrib/subtree/git-subtree.sh\n+++ b/contrib/subtree/git-subtree.sh\n@@ -785,42 +785,6 @@ ensure_valid_ref_format () {\n \t\tdie \"fatal: '$1' does not look like a ref\"\n }\n \n-# Usage: should_ignore_subtree_split_commit REV\n-#\n-# Check if REV is a commit from another subtree and should be\n-# ignored from processing for splits\n-should_ignore_subtree_split_commit () {\n-\tassert test $# = 1\n-\n-\tgit show \\\n-\t\t--no-patch \\\n-\t\t--no-show-signature \\\n-\t\t--format='%(trailers:key=git-subtree-dir,key=git-subtree-mainline)' \\\n-\t\t\"$1\" |\n-\t(\n-\thave_mainline=\n-\tsubtree_dir=\n-\n-\twhile read -r trailer val\n-\tdo\n-\t\tcase \"$trailer\" in\n-\t\tgit-subtree-dir:)\n-\t\t\tsubtree_dir=\"${val%/}\" ;;\n-\t\tgit-subtree-mainline:)\n-\t\t\thave_mainline=y ;;\n-\t\tesac\n-\tdone\n-\n-\tif test -n \"${subtree_dir}\" &&\n-\t\ttest -z \"${have_mainline}\" &&\n-\t\ttest \"${subtree_dir}\" != \"$arg_prefix\"\n-\tthen\n-\t\treturn 0\n-\tfi\n-\treturn 1\n-\t)\n-}\n-\n # Usage: process_split_commit REV PARENTS\n process_split_commit () {\n \tassert test $# = 2\n@@ -1006,19 +970,7 @@ cmd_split () {\n \teval \"$grl\" |\n \twhile read rev parents\n \tdo\n-\t\tif should_ignore_subtree_split_commit \"$rev\"\n-\t\tthen\n-\t\t\tcontinue\n-\t\tfi\n-\t\tparsedparents=''\n-\t\tfor parent in $parents\n-\t\tdo\n-\t\t\tif ! should_ignore_subtree_split_commit \"$parent\"\n-\t\t\tthen\n-\t\t\t\tparsedparents=\"$parsedparents$parent \"\n-\t\t\tfi\n-\t\tdone\n-\t\tprocess_split_commit \"$rev\" \"$parsedparents\"\n+\t\tprocess_split_commit \"$rev\" \"$parents\"\n \tdone || exit $?\n \n \tlatest_new=$(cache_get latest_new) || exit $?\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex dad8dea63a..05a774ad47 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -428,8 +428,7 @@ test_expect_success 'split sub dir/ with --rejoin' '\n # \t- Perform 'split' on subtree B\n # \t- Create new commits with changes to subtree A and B\n # \t- Perform split on subtree A\n-# \t- Check that the commits in subtree B are not processed\n-#\t\t\tas part of the subtree A split\n+# \t- Check for expected history\n test_expect_success 'split with multiple subtrees' '\n \tsubtree_test_create_repo \"$test_count\" &&\n \tsubtree_test_create_repo \"$test_count/subA\" &&\n@@ -458,8 +457,8 @@ test_expect_success 'split with multiple subtrees' '\n \t\t--squash --rejoin -m \"Sub A Split 2\" -b a2 &&\n \ttest \"$(git -C \"$test_count\" rev-list --count main..a2)\" -eq 2 &&\n \ttest \"$(git -C \"$test_count\" rev-list --count a1..a2)\" -eq 1 &&\n-\ttest \"$(git -C \"$test_count\" subtree split --prefix=subBDir \\\n-\t\t--squash --rejoin -d -m \"Sub B Split 1\" -b b2 2>&1 | grep -w \"\\[1\\]\")\" = \"\" &&\n+\tgit -C \"$test_count\" subtree split --prefix=subBDir \\\n+\t\t--squash --rejoin -d -m \"Sub B Split 1\" -b b2 &&\n \ttest \"$(git -C \"$test_count\" rev-list --count main..b2)\" -eq 2 &&\n \ttest \"$(git -C \"$test_count\" rev-list --count b1..b2)\" -eq 1\n '\n@@ -507,6 +506,67 @@ do\n \t'\n done\n \n+# Usually,\n+#\n+#    git subtree merge -P subA --squash f00...\n+#\n+# makes two commits, in this order:\n+#\n+# 1. Squashed 'subA/' content from commit f00...\n+# 2. Merge commit (1) as 'subA'\n+#\n+# Commit 1 updates the subtree but does *not* rewrite paths.\n+# Commit 2 rewrites all trees to start with `subA/`\n+#\n+# Commit 1 either has no parents or depends only on other\n+# \"Squashed 'subA/' content\" commits.\n+#\n+# For merge without --squash, subtree produces just one commit:\n+# a merge commit with git-subtree trailers.\n+#\n+# In either case, if the user rebases these commits, they will\n+# still have the git-subtree-* trailers… but will NOT have\n+# the layout described above.\n+#\n+# Test that subsequent `git subtree split` are not confused by this.\n+test_expect_success 'split with rebased subtree commit' '\n+\tsubtree_test_create_repo \"$test_count\" &&\n+\t(\n+\t\tcd \"$test_count\" &&\n+\t\ttest_commit file0 &&\n+\t\ttest_create_subtree_add \\\n+\t\t\t. mksubtree subA file1 --squash &&\n+\t\ttest_path_is_file subA/file1.t &&\n+\t\tmkdir subB &&\n+\t\ttest_commit subB/bfile &&\n+\t\tgit commit --amend -F - <<'EOF' &&\n+Squashed '\\''subB/'\\'' content from commit '\\''badf00da911bbe895347b4b236f5461d55dc9877'\\''\n+\n+Simulate a cherry-picked or rebased subtree commit.\n+\n+git-subtree-dir: subB\n+git-subtree-split: badf00da911bbe895347b4b236f5461d55dc9877\n+EOF\n+\t\ttest_commit subA/file2 &&\n+\t\ttest_commit subB/bfile2 &&\n+\t\tgit commit --amend -F - <<'EOF' &&\n+Split '\\''subB/'\\'' into commit '\\''badf00da911bbe895347b4b236f5461d55dc9877'\\''\n+\n+Simulate a cherry-picked or rebased subtree commit.\n+\n+git-subtree-dir: subB\n+git-subtree-mainline: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n+git-subtree-split: badf00da911bbe895347b4b236f5461d55dc9877\n+EOF\n+\t\tgit subtree split --prefix=subA --branch=bsplit &&\n+\t\tgit checkout bsplit &&\n+\t\ttest_path_is_file file1.t &&\n+\t\ttest_path_is_file file2.t &&\n+\t\ttest \"$(last_commit_subject)\" = \"subA/file2\" &&\n+\t\ttest \"$(git rev-list --count bsplit)\" -eq 2\n+\t)\n+'\n+\n test_expect_success 'split sub dir/ with --rejoin from scratch' '\n \tsubtree_test_create_repo \"$test_count\" &&\n \ttest_create_commit \"$test_count\" main1 &&\n-- \n2.43.0\n\n"},{"id":"536069","messageId":"20260215201906.889951-3-ask+git@howdoi.land","threadId":"65001","inReplyTo":"20260215201906.889951-1-ask+git@howdoi.land","subject":"[PATCH 2/3] contrib/subtree: test history depth","fromName":"Colin Stagner","fromEmail":"ask+git@howdoi.land","sentAt":"2026-02-15T20:18:46Z","receivedAt":"2026-02-15T20:19:54Z","isPatch":true,"sender":{"key":"ask+git@howdoi.land","avatar":null},"body":"Add history depth checks to some of the subtree unit tests.\n\nThese checks were previously introduced as part of 28a7e27cff\n(contrib/subtree: detect rewritten subtree commits, 2026-01-09),\nwhich has since been reverted.\n\nSigned-off-by: Colin Stagner <ask+git@howdoi.land>\n---\n contrib/subtree/t/t7900-subtree.sh | 22 ++++++++++++++++------\n 1 file changed, 16 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex 3ee2f95d86..dad8dea63a 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -411,8 +411,9 @@ test_expect_success 'split sub dir/ with --rejoin' '\n \t\tgit fetch ./\"sub proj\" HEAD &&\n \t\tgit subtree merge --prefix=\"sub dir\" FETCH_HEAD &&\n \t\tsplit_hash=$(git subtree split --prefix=\"sub dir\" --annotate=\"*\") &&\n-\t\tgit subtree split --prefix=\"sub dir\" --annotate=\"*\" --rejoin &&\n-\t\ttest \"$(last_commit_subject)\" = \"Split '\\''sub dir/'\\'' into commit '\\''$split_hash'\\''\"\n+\t\tgit subtree split --prefix=\"sub dir\" --annotate=\"*\" -b spl --rejoin &&\n+\t\ttest \"$(last_commit_subject)\" = \"Split '\\''sub dir/'\\'' into commit '\\''$split_hash'\\''\" &&\n+\t\ttest \"$(git rev-list --count spl)\" -eq 5\n \t)\n '\n \n@@ -442,18 +443,25 @@ test_expect_success 'split with multiple subtrees' '\n \tgit -C \"$test_count\" subtree add --prefix=subADir FETCH_HEAD &&\n \tgit -C \"$test_count\" fetch ./subB HEAD &&\n \tgit -C \"$test_count\" subtree add --prefix=subBDir FETCH_HEAD &&\n+\ttest \"$(git -C \"$test_count\" rev-list --count main)\" -eq 7 &&\n \ttest_create_commit \"$test_count\" subADir/main-subA1 &&\n \ttest_create_commit \"$test_count\" subBDir/main-subB1 &&\n \tgit -C \"$test_count\" subtree split --prefix=subADir \\\n-\t\t--squash --rejoin -m \"Sub A Split 1\" &&\n+\t\t--squash --rejoin -m \"Sub A Split 1\" -b a1 &&\n+\ttest \"$(git -C \"$test_count\" rev-list --count main..a1)\" -eq 1 &&\n \tgit -C \"$test_count\" subtree split --prefix=subBDir \\\n-\t\t--squash --rejoin -m \"Sub B Split 1\" &&\n+\t\t--squash --rejoin -m \"Sub B Split 1\" -b b1 &&\n+\ttest \"$(git -C \"$test_count\" rev-list --count main..b1)\" -eq 1 &&\n \ttest_create_commit \"$test_count\" subADir/main-subA2 &&\n \ttest_create_commit \"$test_count\" subBDir/main-subB2 &&\n \tgit -C \"$test_count\" subtree split --prefix=subADir \\\n-\t\t--squash --rejoin -m \"Sub A Split 2\" &&\n+\t\t--squash --rejoin -m \"Sub A Split 2\" -b a2 &&\n+\ttest \"$(git -C \"$test_count\" rev-list --count main..a2)\" -eq 2 &&\n+\ttest \"$(git -C \"$test_count\" rev-list --count a1..a2)\" -eq 1 &&\n \ttest \"$(git -C \"$test_count\" subtree split --prefix=subBDir \\\n-\t\t--squash --rejoin -d -m \"Sub B Split 1\" 2>&1 | grep -w \"\\[1\\]\")\" = \"\"\n+\t\t--squash --rejoin -d -m \"Sub B Split 1\" -b b2 2>&1 | grep -w \"\\[1\\]\")\" = \"\" &&\n+\ttest \"$(git -C \"$test_count\" rev-list --count main..b2)\" -eq 2 &&\n+\ttest \"$(git -C \"$test_count\" rev-list --count b1..b2)\" -eq 1\n '\n \n # When subtree split-ing a directory that has other subtree\n@@ -477,6 +485,7 @@ do\n \t\t\ttest_path_is_file subA/file1.t &&\n \t\t\ttest_path_is_file subA/subB/file2.t &&\n \t\t\tgit subtree split --prefix=subA --branch=bsplit &&\n+\t\t\ttest \"$(git rev-list --count bsplit)\" -eq 2 &&\n \t\t\tgit checkout bsplit &&\n \t\t\ttest_path_is_file file1.t &&\n \t\t\ttest_path_is_file subB/file2.t &&\n@@ -489,6 +498,7 @@ do\n \t\t\t\t--prefix=subA/subB mksubtree &&\n \t\t\ttest_path_is_file subA/subB/file3.t &&\n \t\t\tgit subtree split --prefix=subA --branch=bsplit &&\n+\t\t\ttest \"$(git rev-list --count bsplit)\" -eq 3 &&\n \t\t\tgit checkout bsplit &&\n \t\t\ttest_path_is_file file1.t &&\n \t\t\ttest_path_is_file subB/file2.t &&\n-- \n2.43.0\n\n"},{"id":"536135","messageId":"CALnO6CAK1-+MpvjjPFUCeOEbDQn+R8EG7E8bmHo1XYt_ZMQpCg@mail.gmail.com","threadId":"65001","inReplyTo":"20260215201906.889951-4-ask+git@howdoi.land","subject":"Re: [PATCH 3/3] contrib/subtree: process out-of-prefix subtrees","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-02-16T21:33:35Z","receivedAt":"2026-02-16T21:33:47Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Mon, Feb 16, 2026 at 4:16 PM Colin Stagner <ask+git@howdoi.land> wrote:\n>\n> `should_ignore_subtree_split_commit` detects subtrees which are\n> outside of the current path --prefix and ignores them. This can\n> speed up splits of repositories that have many subtrees.\n>\n> Since its inception [1], every iteration of this logic [2], [3]\n> incorrectly excludes commits. This alters the split history. The\n> split history and its commit hashes are API contract, so this is\n> not permissible.\n>\n> While a commit from a different subtree may look like it doesn't\n> contribute anything to a split, sometimes it does. Merge commits\n> are a particular hot spot. For these, the pruning logic in\n> `copy_or_skip` performs:\n>\n> 1. a check for \"treesame\" parents\n> 2. two different common ancestry checks\n>\n> These checks operate on the **split history**, not the input\n> history. The split history omits commits that do not affect the\n> --prefix. This can significantly alter the ancestry of a merge.\n> In order to determine if `copy_or_skip` will skip a merge, it\n> is likely necessary to compute all the split history... which\n> is what `should_ignore_subtree_split_commit` tries to avoid.\n>\n> To make this logic API-preserving, we could gate it behind a\n> new CLI argument. The present implementation is actually a\n> speed penalty in many cases, however, so this is not done here.\n>\n> Remove the `should_ignore_subtree_split_commit` logic. This\n> fixes the regression reported in [4].\n>\n> [1]: 98ba49ccc2 (subtree: fix split processing with multiple subtrees present, 2023-12-01)\n>\n> [2]: 83f9dad7d6 (contrib/subtree: fix split with squashed subtrees, 2025-09-09)\n>\n> [3]: 28a7e27cff (contrib/subtree: detect rewritten subtree commits, 2026-01-09)\n>\n> [4]: <20251230170719.845029-1-george@mail.dietrich.pub>\n>\n> Reported-by: George <george@mail.dietrich.pub>\n>\n> Reported-by: Christian Heusel <christian@heusel.eu>\n>\n> Signed-off-by: Colin Stagner <ask+git@howdoi.land>\n\nnit: \"git interpret-trailers --parse\" doesn't understand\nline-separated trailers, so only finds the SoB line. See\ngit-interpret-trailers(1).\n\n(A useful test locally was something like\n\n    b4 am https://lore.kernel.org/git/20260215201906.889951-4-ask+git@howdoi.land/\n    git mailsplit -op\n20260215_ask_git_contrib_subtree_process_out_of_prefix_subtrees.mbx\n    git interpret-trailers --parse p/0003\n\nwhich output only\n\n    Signed-off-by: Colin Stagner <ask+git@howdoi.land>\n\nwhich I don't think you intended.)\n\n-- \nD. Ben Knoble\n"},{"id":"536238","messageId":"7f30cd09-30ea-4173-9a7c-38f2c46f9ea9@howdoi.land","threadId":"65001","inReplyTo":"CALnO6CAK1-+MpvjjPFUCeOEbDQn+R8EG7E8bmHo1XYt_ZMQpCg@mail.gmail.com","subject":"Re: [PATCH 3/3] contrib/subtree: process out-of-prefix subtrees","fromName":"Colin Stagner","fromEmail":"ask+git@howdoi.land","sentAt":"2026-02-18T02:25:31Z","receivedAt":"2026-02-18T02:25:51Z","isPatch":true,"sender":{"key":"ask+git@howdoi.land","avatar":null},"body":"On 2/16/26 15:33, D. Ben Knoble wrote:\n> On Mon, Feb 16, 2026 at 4:16 PM Colin Stagner <ask+git@howdoi.land> wrote:\n>>\n>> Reported-by: George <george@mail.dietrich.pub>\n>>\n>> Reported-by: Christian Heusel <christian@heusel.eu>\n>>\n>> Signed-off-by: Colin Stagner <ask+git@howdoi.land>\n> \n> nit: \"git interpret-trailers --parse\" doesn't understand\n> line-separated trailers\n\nWill fix in v2.\n\nAnd I think I'll give b4 a shot, while I'm at it.\n\n"}]}