subtree: fix split after annotated tag was squashed merged

The previous commit fixed a failure in 'git subtree merge --squash' when the previous squash-merge merged an annotated tag of the subtree repository which is missing locally. The same failure happens in 'git subtree split', either directly or when called by 'git subtree push', under the same circumstances: 'cmd_split' invokes 'find_existing_splits', which loops through previous commits and invokes 'git rev-parse' (via 'process_subtree_split_trailer') on the value of any 'git subtree-split' trailer it finds. This fails if this value is the hash of an annotated tag which is missing locally. Add a new optional argument 'repository' to 'cmd_split' and 'find_existing_splits', and invoke 'cmd_split' with that argument from 'cmd_push'. This allows 'process_subtree_split_trailer' to try to fetch the missing tag from the 'repository' if it's not available locally, mirroring the new behaviour of 'git subtree pull' and 'git subtree merge'. Signed-off-by: Philippe Blain <levraiphilippeblain@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Philippe Blain committed Oct 21, 2022 at 15:13 UTC 1762382ab19dd6d5d84dd32e35e25c2b55b651f0
3 files changed +36 -9
contrib/subtree/git-subtree.sh
+18 -8
@@ -453,14 +453,19 @@ find_latest_squash () {
453 done || exit $?
454 }
455
456 -# Usage: find_existing_splits DIR REV
456 +# Usage: find_existing_splits DIR REV [REPOSITORY]
457 find_existing_splits () {
458 - assert test $# = 2
458 + assert test $# = 2 -o $# = 3
459 debug "Looking for prior splits..."
460 local indent=$(($indent + 1))
461
462 dir="$1"
463 rev="$2"
464 + repository=""
465 + if test "$#" = 3
466 + then
467 + repository="$3"
468 + fi
469 main=
470 sub=
471 local grep_format="^git-subtree-dir: $dir/*\$"
@@ -480,7 +485,7 @@ find_existing_splits () {
485 main="$b"
486 ;;
487 git-subtree-split:)
483 - process_subtree_split_trailer "$b" "$sq"
488 + process_subtree_split_trailer "$b" "$sq" "$repository"
489 ;;
490 END)
491 debug "Main is: '$main'"
@@ -906,17 +911,22 @@ cmd_add_commit () {
911 say >&2 "Added dir '$dir'"
912 }
913
909 -# Usage: cmd_split [REV]
914 +# Usage: cmd_split [REV] [REPOSITORY]
915 cmd_split () {
916 if test $# -eq 0
917 then
918 rev=$(git rev-parse HEAD)
914 - elif test $# -eq 1
919 + elif test $# -eq 1 -o $# -eq 2
920 then
921 rev=$(git rev-parse -q --verify "$1^{commit}") ||
922 die "fatal: '$1' does not refer to a commit"
923 else
919 - die "fatal: you must provide exactly one revision. Got: '$*'"
924 + die "fatal: you must provide exactly one revision, and optionnally a repository. Got: '$*'"
925 + fi
926 + repository=""
927 + if test "$#" = 2
928 + then
929 + repository="$2"
930 fi
931
932 if test -n "$arg_split_rejoin"
@@ -940,7 +950,7 @@ cmd_split () {
950 done || exit $?
951 fi
952
943 - unrevs="$(find_existing_splits "$dir" "$rev")" || exit $?
953 + unrevs="$(find_existing_splits "$dir" "$rev" "$repository")" || exit $?
954
955 # We can't restrict rev-list to only $dir here, because some of our
956 # parents have the $dir contents the root, and those won't match.
@@ -1072,7 +1082,7 @@ cmd_push () {
1082 die "fatal: '$localrevname_presplit' does not refer to a commit"
1083
1084 echo "git push using: " "$repository" "$refspec"
1075 - localrev=$(cmd_split "$localrev_presplit") || die
1085 + localrev=$(cmd_split "$localrev_presplit" "$repository") || die
1086 git push "$repository" "$localrev":"refs/heads/$remoteref"
1087 else
1088 die "fatal: '$dir' must already exist. Try 'git subtree add'."
contrib/subtree/git-subtree.txt
+6 -1
@@ -94,7 +94,7 @@ annotated tag of the subtree repository, that tag needs to be available locally.
94 If <repository> is given, a missing tag will automatically be fetched from that
95 repository.
96
97 -split [<local-commit>]::
97 +split [<local-commit>] [<repository>]::
98 Extract a new, synthetic project history from the
99 history of the <prefix> subtree of <local-commit>, or of
100 HEAD if no <local-commit> is given. The new history
@@ -114,6 +114,11 @@ settings passed to 'split' (such as '--annotate') are the same.
114 Because of this, if you add new commits and then re-split, the new
115 commits will be attached as commits on top of the history you
116 generated last time, so 'git merge' and friends will work as expected.
117 ++
118 +When a previous merge with '--squash' merged an annotated tag of the
119 +subtree repository, that tag needs to be available locally.
120 +If <repository> is given, a missing tag will automatically be fetched from that
121 +repository.
122
123 pull <repository> <remote-ref>::
124 Exactly like 'merge', but parallels 'git pull' in that
contrib/subtree/t/t7900-subtree.sh
+12
@@ -582,6 +582,12 @@ test_expect_success 'split "sub dir"/ with --branch for an incompatible branch'
582 )
583 '
584
585 +test_expect_success 'split after annotated tag was added/merged with --squash pre-v2.32.0' '
586 + test_create_pre2_32_repo "$test_count" &&
587 + test_must_fail git -C "$test_count-clone" subtree split --prefix="sub" HEAD &&
588 + git -C "$test_count-clone" subtree split --prefix="sub" HEAD "../$test_count-sub"
589 +'
590 +
591 #
592 # Tests for 'git subtree pull'
593 #
@@ -989,6 +995,12 @@ test_expect_success 'push "sub dir"/ with a local rev' '
995 )
996 '
997
998 +test_expect_success 'push after annotated tag was added/merged with --squash pre-v2.32.0' '
999 + test_create_pre2_32_repo "$test_count" &&
1000 + test_create_commit "$test_count-clone" sub/main-sub1 &&
1001 + git -C "$test_count-clone" subtree push --prefix="sub" "../$test_count-sub" from-mainline
1002 +'
1003 +
1004 #
1005 # Validity checking
1006 #