diff options
| -rwxr-xr-x | contrib/subtree/git-subtree.sh | 56 | ||||
| -rw-r--r-- | contrib/subtree/git-subtree.txt | 9 | ||||
| -rwxr-xr-x | contrib/subtree/t/t7900-subtree.sh | 36 |
3 files changed, 86 insertions, 15 deletions
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index b90ca0036f..2c67989fe8 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -371,20 +371,45 @@ try_remove_previous () { fi } -# Usage: process_subtree_split_trailer SPLIT_HASH MAIN_HASH +# Usage: process_subtree_split_trailer SPLIT_HASH MAIN_HASH [REPOSITORY] process_subtree_split_trailer () { - assert test $# = 2 + assert test $# = 2 -o $# = 3 b="$1" sq="$2" - sub="$(git rev-parse --verify --quiet "$b^{commit}")" || - die "fatal: could not rev-parse split hash $b from commit $sq" + repository="" + if test "$#" = 3 + then + repository="$3" + fi + fail_msg="fatal: could not rev-parse split hash $b from commit $sq" + if ! sub="$(git rev-parse --verify --quiet "$b^{commit}")" + then + # if 'repository' was given, try to fetch the 'git-subtree-split' hash + # before 'rev-parse'-ing it again, as it might be a tag that we do not have locally + if test -n "${repository}" + then + git fetch "$repository" "$b" + sub="$(git rev-parse --verify --quiet "$b^{commit}")" || + die "$fail_msg" + else + hint1=$(printf "hint: hash might be a tag, try fetching it from the subtree repository:") + hint2=$(printf "hint: git fetch <subtree-repository> $b") + fail_msg=$(printf "$fail_msg\n$hint1\n$hint2") + die "$fail_msg" + fi + fi } -# Usage: find_latest_squash DIR +# Usage: find_latest_squash DIR [REPOSITORY] find_latest_squash () { - assert test $# = 1 + assert test $# = 1 -o $# = 2 dir="$1" - debug "Looking for latest squash ($dir)..." + repository="" + if test "$#" = 2 + then + repository="$2" + fi + debug "Looking for latest squash (dir=$dir, repository=$repository)..." local indent=$(($indent + 1)) sq= @@ -404,7 +429,7 @@ find_latest_squash () { main="$b" ;; git-subtree-split:) - process_subtree_split_trailer "$b" "$sq" + process_subtree_split_trailer "$b" "$sq" "$repository" ;; END) if test -n "$sub" @@ -969,17 +994,22 @@ cmd_split () { exit 0 } -# Usage: cmd_merge REV +# Usage: cmd_merge REV [REPOSITORY] cmd_merge () { - test $# -eq 1 || - die "fatal: you must provide exactly one revision. Got: '$*'" + test $# -eq 1 -o $# -eq 2 || + die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'" rev=$(git rev-parse -q --verify "$1^{commit}") || die "fatal: '$1' does not refer to a commit" + repository="" + if test "$#" = 2 + then + repository="$2" + fi ensure_clean if test -n "$arg_addmerge_squash" then - first_split="$(find_latest_squash "$dir")" || exit $? + first_split="$(find_latest_squash "$dir" "$repository")" || exit $? if test -z "$first_split" then die "fatal: can't squash-merge: '$dir' was never added." @@ -1017,7 +1047,7 @@ cmd_pull () { ensure_clean ensure_valid_ref_format "$ref" git fetch "$repository" "$ref" || exit $? - cmd_merge FETCH_HEAD + cmd_merge FETCH_HEAD "$repository" } # Usage: cmd_push REPOSITORY [+][LOCALREV:]REMOTEREF diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt index 9cddfa2654..0e7524d786 100644 --- a/contrib/subtree/git-subtree.txt +++ b/contrib/subtree/git-subtree.txt @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git subtree' [<options>] -P <prefix> add <local-commit> 'git subtree' [<options>] -P <prefix> add <repository> <remote-ref> -'git subtree' [<options>] -P <prefix> merge <local-commit> +'git subtree' [<options>] -P <prefix> merge <local-commit> [<repository>] 'git subtree' [<options>] -P <prefix> split [<local-commit>] [verse] @@ -76,7 +76,7 @@ add <repository> <remote-ref>:: only a single commit from the subproject, rather than its entire history. -merge <local-commit>:: +merge <local-commit> [<repository>]:: Merge recent changes up to <local-commit> into the <prefix> subtree. As with normal 'git merge', this doesn't remove your own local changes; it just merges those @@ -88,6 +88,11 @@ If you use '--squash', the merge direction doesn't always have to be forward; you can use this command to go back in time from v2.5 to v2.4, for example. If your merge introduces a conflict, you can resolve it in the usual ways. ++ +When using '--squash', and the previous merge with '--squash' merged an +annotated tag of the subtree repository, that tag needs to be available locally. +If <repository> is given, a missing tag will automatically be fetched from that +repository. split [<local-commit>]:: Extract a new, synthetic project history from the diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index 249743ab9a..d0671676c7 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -43,6 +43,30 @@ last_commit_subject () { git log --pretty=format:%s -1 } +# Upon 'git subtree add|merge --squash' of an annotated tag, +# pre-2.32.0 versions of 'git subtree' would write the hash of the tag +# (sub1 below), instead of the commit (sub1^{commit}) in the +# "git-subtree-split" trailer. +# We immitate this behaviour below using a replace ref. +# This function creates 3 repositories: +# - $1 +# - $1-sub (added as subtree "sub" in $1) +# - $1-clone (clone of $1) +test_create_pre2_32_repo () { + subtree_test_create_repo "$1" && + subtree_test_create_repo "$1-sub" && + test_commit -C "$1" main1 && + test_commit -C "$1-sub" --annotate sub1 && + git -C "$1" subtree add --prefix="sub" --squash "../$1-sub" sub1 && + tag=$(git -C "$1" rev-parse FETCH_HEAD) && + commit=$(git -C "$1" rev-parse FETCH_HEAD^{commit}) && + git -C "$1" log -1 --format=%B HEAD^2 >msg && + test_commit -C "$1-sub" --annotate sub2 && + git clone --no-local "$1" "$1-clone" && + new_commit=$(cat msg | sed -e "s/$commit/$tag/" | git -C "$1-clone" commit-tree HEAD^2^{tree}) && + git -C "$1-clone" replace HEAD^2 $new_commit +} + test_expect_success 'shows short help text for -h' ' test_expect_code 129 git subtree -h >out 2>err && test_must_be_empty err && @@ -264,6 +288,13 @@ test_expect_success 'merge new subproj history into subdir/ with a slash appende ) ' +test_expect_success 'merge with --squash after annotated tag was added/merged with --squash pre-v2.32.0 ' ' + test_create_pre2_32_repo "$test_count" && + git -C "$test_count-clone" fetch "../$test_count-sub" sub2 && + test_must_fail git -C "$test_count-clone" subtree merge --prefix="sub" --squash FETCH_HEAD && + git -C "$test_count-clone" subtree merge --prefix="sub" --squash FETCH_HEAD "../$test_count-sub" +' + # # Tests for 'git subtree split' # @@ -630,6 +661,11 @@ test_expect_success 'pull rejects flags for split' ' ) ' +test_expect_success 'pull with --squash after annotated tag was added/merged with --squash pre-v2.32.0 ' ' + test_create_pre2_32_repo "$test_count" && + git -C "$test_count-clone" subtree -d pull --prefix="sub" --squash "../$test_count-sub" sub2 +' + # # Tests for 'git subtree push' # |
