Revert "Merge branch 'cs/rebased-subtree-split'"

This reverts commit 79e3055baba32e2952e6e8994cdcd4fc145ba7f0, reversing changes made to 9813aace1e52765e01e688672cdcdcbe25336ec7. Regresison report https://lore.kernel.org/git/755578cb-07e0-4b40-aa90-aacf4d45ccaa@heusel.eu/

Junio C Hamano committed Jan 25, 2026 at 09:43 UTC ab689ea7f91ab0858e85776f31102203d3ea7b83
2 files changed +55 -169
contrib/subtree/git-subtree.sh
+49 -92
@@ -325,12 +325,6 @@ check_parents () {
325 done
326 }
327
328 -# Usage: get_notree REV
329 -get_notree () {
330 - assert test $# = 1
331 - test -r "$cachedir/notree/$1"
332 -}
333 -
328 # Usage: set_notree REV
329 set_notree () {
330 assert test $# = 1
@@ -517,71 +511,6 @@ find_existing_splits () {
511 done || exit $?
512 }
513
520 -# Usage: find_other_splits DIR REV UNREVS...
521 -#
522 -# Scan history in REV UNREVS for other `git subtree split --rejoin`
523 -# merge commits belonging to prefixes outside of DIR. These
524 -# "other splits" don't contribute to DIR and can be ignored.
525 -#
526 -# If any such rejoins are found,
527 -#
528 -# * emit their second-parent as an UNREV, avoiding a
529 -# potentially costly history traversal
530 -#
531 -# * mark the merge commit as "notree" to ignore it
532 -find_other_splits () {
533 - assert test $# -ge 2
534 - dir="${1%/}"
535 - rev="$2"
536 - shift 2
537 - debug "Looking for other splits with dir != $dir..."
538 -
539 - git log \
540 - --grep '^git-subtree-mainline:' \
541 - --no-patch \
542 - --no-show-signature \
543 - --format='hash: %H%nparents: %P%n%(trailers:key=git-subtree-dir,key=git-subtree-mainline,key=git-subtree-split)%nEND' \
544 - "$rev" ${@:+"$@"} |
545 - while read -r key val
546 - do
547 - case "$key" in
548 - hash:)
549 - commit_hash="${val}"
550 - commit_parents=
551 - subtree_dir=
552 - subtree_mainline=
553 - subtree_split=
554 - ;;
555 - parents:)
556 - commit_parents="${val}" ;;
557 - git-subtree-dir:)
558 - subtree_dir="${val%/}/" ;;
559 - git-subtree-mainline:)
560 - subtree_mainline="${val}" ;;
561 - git-subtree-split:)
562 - subtree_split="${val}" ;;
563 - END)
564 - # verify:
565 - # * all git-subtree-* trailers are present
566 - # * this subtree is outside of $dir
567 - # * the first parent is the git-subtree-mainline:
568 - # * the commit has at least two parents
569 - if test -n "${subtree_dir}" &&
570 - test -n "${subtree_split}" &&
571 - test -n "${subtree_mainline}" &&
572 - test "${subtree_dir}" = "${subtree_dir#"${dir}/"}" &&
573 - test "${commit_parents}" != "${commit_parents#"$subtree_mainline "}" &&
574 - rev_exists "${commit_hash}^2"
575 - then
576 - debug "find_other_splits excluding dir=$subtree_dir merged in ${commit_hash}"
577 - echo "^${commit_hash}^2"
578 - set_notree "${commit_hash}"
579 - fi
580 - ;;
581 - esac
582 - done
583 -}
584 -
514 # Usage: copy_commit REV TREE FLAGS_STR
515 copy_commit () {
516 assert test $# = 3
@@ -856,6 +785,42 @@ ensure_valid_ref_format () {
785 die "fatal: '$1' does not look like a ref"
786 }
787
788 +# Usage: should_ignore_subtree_split_commit REV
789 +#
790 +# Check if REV is a commit from another subtree and should be
791 +# ignored from processing for splits
792 +should_ignore_subtree_split_commit () {
793 + assert test $# = 1
794 +
795 + git show \
796 + --no-patch \
797 + --no-show-signature \
798 + --format='%(trailers:key=git-subtree-dir,key=git-subtree-mainline)' \
799 + "$1" |
800 + (
801 + have_mainline=
802 + subtree_dir=
803 +
804 + while read -r trailer val
805 + do
806 + case "$trailer" in
807 + git-subtree-dir:)
808 + subtree_dir="${val%/}" ;;
809 + git-subtree-mainline:)
810 + have_mainline=y ;;
811 + esac
812 + done
813 +
814 + if test -n "${subtree_dir}" &&
815 + test -z "${have_mainline}" &&
816 + test "${subtree_dir}" != "$arg_prefix"
817 + then
818 + return 0
819 + fi
820 + return 1
821 + )
822 +}
823 +
824 # Usage: process_split_commit REV PARENTS
825 process_split_commit () {
826 assert test $# = 2
@@ -1029,39 +994,31 @@ cmd_split () {
994 fi
995
996 unrevs="$(find_existing_splits "$dir" "$rev" "$repository")" || exit $?
1032 - (find_other_splits >"$cachedir/prune" "$dir" "$rev" $unrevs) || exit $?
997
998 # We can't restrict rev-list to only $dir here, because some of our
999 # parents have the $dir contents the root, and those won't match.
1000 # (and rev-list --follow doesn't seem to solve this)
1037 - revmax="$(git rev-list \
1038 - <"$cachedir/prune" \
1039 - --topo-order \
1040 - --reverse \
1041 - --parents \
1042 - --stdin \
1043 - --count \
1044 - "$rev" \
1045 - $unrevs
1046 - )"
1001 + grl='git rev-list --topo-order --reverse --parents $rev $unrevs'
1002 + revmax=$(eval "$grl" | wc -l)
1003 revcount=0
1004 createcount=0
1005 extracount=0
1050 - git rev-list \
1051 - <"$cachedir/prune" \
1052 - --topo-order \
1053 - --reverse \
1054 - --parents \
1055 - --stdin \
1056 - "$rev" \
1057 - $unrevs |
1006 + eval "$grl" |
1007 while read rev parents
1008 do
1060 - if get_notree "$rev"
1009 + if should_ignore_subtree_split_commit "$rev"
1010 then
1011 continue
1012 fi
1064 - process_split_commit "$rev" "$parents"
1013 + parsedparents=''
1014 + for parent in $parents
1015 + do
1016 + if ! should_ignore_subtree_split_commit "$parent"
1017 + then
1018 + parsedparents="$parsedparents$parent "
1019 + fi
1020 + done
1021 + process_split_commit "$rev" "$parsedparents"
1022 done || exit $?
1023
1024 latest_new=$(cache_get latest_new) || exit $?
contrib/subtree/t/t7900-subtree.sh
+6 -77
@@ -411,9 +411,8 @@ test_expect_success 'split sub dir/ with --rejoin' '
411 git fetch ./"sub proj" HEAD &&
412 git subtree merge --prefix="sub dir" FETCH_HEAD &&
413 split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
414 - git subtree split --prefix="sub dir" --annotate="*" -b spl --rejoin &&
415 - test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$split_hash'\''" &&
416 - test "$(git rev-list --count spl)" -eq 5
414 + git subtree split --prefix="sub dir" --annotate="*" --rejoin &&
415 + test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
416 )
417 '
418
@@ -443,25 +442,18 @@ test_expect_success 'split with multiple subtrees' '
442 git -C "$test_count" subtree add --prefix=subADir FETCH_HEAD &&
443 git -C "$test_count" fetch ./subB HEAD &&
444 git -C "$test_count" subtree add --prefix=subBDir FETCH_HEAD &&
446 - test "$(git -C "$test_count" rev-list --count main)" -eq 7 &&
445 test_create_commit "$test_count" subADir/main-subA1 &&
446 test_create_commit "$test_count" subBDir/main-subB1 &&
447 git -C "$test_count" subtree split --prefix=subADir \
450 - --squash --rejoin -m "Sub A Split 1" -b a1 &&
451 - test "$(git -C "$test_count" rev-list --count main..a1)" -eq 1 &&
448 + --squash --rejoin -m "Sub A Split 1" &&
449 git -C "$test_count" subtree split --prefix=subBDir \
453 - --squash --rejoin -m "Sub B Split 1" -b b1 &&
454 - test "$(git -C "$test_count" rev-list --count main..b1)" -eq 1 &&
450 + --squash --rejoin -m "Sub B Split 1" &&
451 test_create_commit "$test_count" subADir/main-subA2 &&
452 test_create_commit "$test_count" subBDir/main-subB2 &&
453 git -C "$test_count" subtree split --prefix=subADir \
458 - --squash --rejoin -m "Sub A Split 2" -b a2 &&
459 - test "$(git -C "$test_count" rev-list --count main..a2)" -eq 2 &&
460 - test "$(git -C "$test_count" rev-list --count a1..a2)" -eq 1 &&
454 + --squash --rejoin -m "Sub A Split 2" &&
455 test "$(git -C "$test_count" subtree split --prefix=subBDir \
462 - --squash --rejoin -d -m "Sub B Split 1" -b b2 2>&1 | grep -w "\[1\]")" = "" &&
463 - test "$(git -C "$test_count" rev-list --count main..b2)" -eq 2 &&
464 - test "$(git -C "$test_count" rev-list --count b1..b2)" -eq 1
456 + --squash --rejoin -d -m "Sub B Split 1" 2>&1 | grep -w "\[1\]")" = ""
457 '
458
459 # When subtree split-ing a directory that has other subtree
@@ -485,7 +477,6 @@ do
477 test_path_is_file subA/file1.t &&
478 test_path_is_file subA/subB/file2.t &&
479 git subtree split --prefix=subA --branch=bsplit &&
488 - test "$(git rev-list --count bsplit)" -eq 2 &&
480 git checkout bsplit &&
481 test_path_is_file file1.t &&
482 test_path_is_file subB/file2.t &&
@@ -498,7 +489,6 @@ do
489 --prefix=subA/subB mksubtree &&
490 test_path_is_file subA/subB/file3.t &&
491 git subtree split --prefix=subA --branch=bsplit &&
501 - test "$(git rev-list --count bsplit)" -eq 3 &&
492 git checkout bsplit &&
493 test_path_is_file file1.t &&
494 test_path_is_file subB/file2.t &&
@@ -507,67 +497,6 @@ do
497 '
498 done
499
510 -# Usually,
511 -#
512 -# git subtree merge -P subA --squash f00...
513 -#
514 -# makes two commits, in this order:
515 -#
516 -# 1. Squashed 'subA/' content from commit f00...
517 -# 2. Merge commit (1) as 'subA'
518 -#
519 -# Commit 1 updates the subtree but does *not* rewrite paths.
520 -# Commit 2 rewrites all trees to start with `subA/`
521 -#
522 -# Commit 1 either has no parents or depends only on other
523 -# "Squashed 'subA/' content" commits.
524 -#
525 -# For merge without --squash, subtree produces just one commit:
526 -# a merge commit with git-subtree trailers.
527 -#
528 -# In either case, if the user rebases these commits, they will
529 -# still have the git-subtree-* trailers… but will NOT have
530 -# the layout described above.
531 -#
532 -# Test that subsequent `git subtree split` are not confused by this.
533 -test_expect_success 'split with rebased subtree commit' '
534 - subtree_test_create_repo "$test_count" &&
535 - (
536 - cd "$test_count" &&
537 - test_commit file0 &&
538 - test_create_subtree_add \
539 - . mksubtree subA file1 --squash &&
540 - test_path_is_file subA/file1.t &&
541 - mkdir subB &&
542 - test_commit subB/bfile &&
543 - git commit --amend -F - <<'EOF' &&
544 -Squashed '\''subB/'\'' content from commit '\''badf00da911bbe895347b4b236f5461d55dc9877'\''
545 -
546 -Simulate a cherry-picked or rebased subtree commit.
547 -
548 -git-subtree-dir: subB
549 -git-subtree-split: badf00da911bbe895347b4b236f5461d55dc9877
550 -EOF
551 - test_commit subA/file2 &&
552 - test_commit subB/bfile2 &&
553 - git commit --amend -F - <<'EOF' &&
554 -Split '\''subB/'\'' into commit '\''badf00da911bbe895347b4b236f5461d55dc9877'\''
555 -
556 -Simulate a cherry-picked or rebased subtree commit.
557 -
558 -git-subtree-dir: subB
559 -git-subtree-mainline: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
560 -git-subtree-split: badf00da911bbe895347b4b236f5461d55dc9877
561 -EOF
562 - git subtree split --prefix=subA --branch=bsplit &&
563 - git checkout bsplit &&
564 - test_path_is_file file1.t &&
565 - test_path_is_file file2.t &&
566 - test "$(last_commit_subject)" = "subA/file2" &&
567 - test "$(git rev-list --count bsplit)" -eq 2
568 - )
569 -'
570 -
500 test_expect_success 'split sub dir/ with --rejoin from scratch' '
501 subtree_test_create_repo "$test_count" &&
502 test_create_commit "$test_count" main1 &&