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 &&