Merge branch 'pb/subtree-split-and-merge-after-squashing-tag-fix'
A bugfix to "git subtree" in its split and merge features. * pb/subtree-split-and-merge-after-squashing-tag-fix: subtree: fix split after annotated tag was squashed merged subtree: fix squash merging after annotated tag was squashed merged subtree: process 'git-subtree-split' trailer in separate function subtree: use named variables instead of "$@" in cmd_pull subtree: define a variable before its first use in 'find_latest_squash' subtree: prefix die messages with 'fatal' subtree: add 'die_incompatible_opt' function to reduce duplication subtree: use 'git rev-parse --verify [--quiet]' for better error messages test-lib-functions: mark 'test_commit' variables as 'local'
This commit is contained in:
commit
a23e0b69e2
@ -98,10 +98,18 @@ progress () {
|
|||||||
assert () {
|
assert () {
|
||||||
if ! "$@"
|
if ! "$@"
|
||||||
then
|
then
|
||||||
die "assertion failed: $*"
|
die "fatal: assertion failed: $*"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Usage: die_incompatible_opt OPTION COMMAND
|
||||||
|
die_incompatible_opt () {
|
||||||
|
assert test "$#" = 2
|
||||||
|
opt="$1"
|
||||||
|
arg_command="$2"
|
||||||
|
die "fatal: the '$opt' flag does not make sense with 'git subtree $arg_command'."
|
||||||
|
}
|
||||||
|
|
||||||
main () {
|
main () {
|
||||||
if test $# -eq 0
|
if test $# -eq 0
|
||||||
then
|
then
|
||||||
@ -147,7 +155,7 @@ main () {
|
|||||||
allow_addmerge=$arg_split_rejoin
|
allow_addmerge=$arg_split_rejoin
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
die "Unknown command '$arg_command'"
|
die "fatal: unknown command '$arg_command'"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
# Reset the arguments array for "real" flag parsing.
|
# Reset the arguments array for "real" flag parsing.
|
||||||
@ -176,16 +184,16 @@ main () {
|
|||||||
arg_debug=1
|
arg_debug=1
|
||||||
;;
|
;;
|
||||||
--annotate)
|
--annotate)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_split_annotate="$1"
|
arg_split_annotate="$1"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--no-annotate)
|
--no-annotate)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_split_annotate=
|
arg_split_annotate=
|
||||||
;;
|
;;
|
||||||
-b)
|
-b)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_split_branch="$1"
|
arg_split_branch="$1"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
@ -194,7 +202,7 @@ main () {
|
|||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-m)
|
-m)
|
||||||
test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_addmerge_message="$1"
|
arg_addmerge_message="$1"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
@ -202,41 +210,41 @@ main () {
|
|||||||
arg_prefix=
|
arg_prefix=
|
||||||
;;
|
;;
|
||||||
--onto)
|
--onto)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_split_onto="$1"
|
arg_split_onto="$1"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--no-onto)
|
--no-onto)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_split_onto=
|
arg_split_onto=
|
||||||
;;
|
;;
|
||||||
--rejoin)
|
--rejoin)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
;;
|
;;
|
||||||
--no-rejoin)
|
--no-rejoin)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
;;
|
;;
|
||||||
--ignore-joins)
|
--ignore-joins)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_split_ignore_joins=1
|
arg_split_ignore_joins=1
|
||||||
;;
|
;;
|
||||||
--no-ignore-joins)
|
--no-ignore-joins)
|
||||||
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_split_ignore_joins=
|
arg_split_ignore_joins=
|
||||||
;;
|
;;
|
||||||
--squash)
|
--squash)
|
||||||
test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_addmerge_squash=1
|
arg_addmerge_squash=1
|
||||||
;;
|
;;
|
||||||
--no-squash)
|
--no-squash)
|
||||||
test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
|
test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command"
|
||||||
arg_addmerge_squash=
|
arg_addmerge_squash=
|
||||||
;;
|
;;
|
||||||
--)
|
--)
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
die "Unexpected option: $opt"
|
die "fatal: unexpected option: $opt"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
@ -244,17 +252,17 @@ main () {
|
|||||||
|
|
||||||
if test -z "$arg_prefix"
|
if test -z "$arg_prefix"
|
||||||
then
|
then
|
||||||
die "You must provide the --prefix option."
|
die "fatal: you must provide the --prefix option."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$arg_command" in
|
case "$arg_command" in
|
||||||
add)
|
add)
|
||||||
test -e "$arg_prefix" &&
|
test -e "$arg_prefix" &&
|
||||||
die "prefix '$arg_prefix' already exists."
|
die "fatal: prefix '$arg_prefix' already exists."
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
test -e "$arg_prefix" ||
|
test -e "$arg_prefix" ||
|
||||||
die "'$arg_prefix' does not exist; use 'git subtree add'"
|
die "fatal: '$arg_prefix' does not exist; use 'git subtree add'"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@ -274,11 +282,11 @@ cache_setup () {
|
|||||||
assert test $# = 0
|
assert test $# = 0
|
||||||
cachedir="$GIT_DIR/subtree-cache/$$"
|
cachedir="$GIT_DIR/subtree-cache/$$"
|
||||||
rm -rf "$cachedir" ||
|
rm -rf "$cachedir" ||
|
||||||
die "Can't delete old cachedir: $cachedir"
|
die "fatal: can't delete old cachedir: $cachedir"
|
||||||
mkdir -p "$cachedir" ||
|
mkdir -p "$cachedir" ||
|
||||||
die "Can't create new cachedir: $cachedir"
|
die "fatal: can't create new cachedir: $cachedir"
|
||||||
mkdir -p "$cachedir/notree" ||
|
mkdir -p "$cachedir/notree" ||
|
||||||
die "Can't create new cachedir: $cachedir/notree"
|
die "fatal: can't create new cachedir: $cachedir/notree"
|
||||||
debug "Using cachedir: $cachedir" >&2
|
debug "Using cachedir: $cachedir" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +342,7 @@ cache_set () {
|
|||||||
test "$oldrev" != "latest_new" &&
|
test "$oldrev" != "latest_new" &&
|
||||||
test -e "$cachedir/$oldrev"
|
test -e "$cachedir/$oldrev"
|
||||||
then
|
then
|
||||||
die "cache for $oldrev already exists!"
|
die "fatal: cache for $oldrev already exists!"
|
||||||
fi
|
fi
|
||||||
echo "$newrev" >"$cachedir/$oldrev"
|
echo "$newrev" >"$cachedir/$oldrev"
|
||||||
}
|
}
|
||||||
@ -363,13 +371,47 @@ try_remove_previous () {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: find_latest_squash DIR
|
# Usage: process_subtree_split_trailer SPLIT_HASH MAIN_HASH [REPOSITORY]
|
||||||
|
process_subtree_split_trailer () {
|
||||||
|
assert test $# = 2 -o $# = 3
|
||||||
|
b="$1"
|
||||||
|
sq="$2"
|
||||||
|
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 [REPOSITORY]
|
||||||
find_latest_squash () {
|
find_latest_squash () {
|
||||||
assert test $# = 1
|
assert test $# = 1 -o $# = 2
|
||||||
debug "Looking for latest squash ($dir)..."
|
dir="$1"
|
||||||
|
repository=""
|
||||||
|
if test "$#" = 2
|
||||||
|
then
|
||||||
|
repository="$2"
|
||||||
|
fi
|
||||||
|
debug "Looking for latest squash (dir=$dir, repository=$repository)..."
|
||||||
local indent=$(($indent + 1))
|
local indent=$(($indent + 1))
|
||||||
|
|
||||||
dir="$1"
|
|
||||||
sq=
|
sq=
|
||||||
main=
|
main=
|
||||||
sub=
|
sub=
|
||||||
@ -387,8 +429,7 @@ find_latest_squash () {
|
|||||||
main="$b"
|
main="$b"
|
||||||
;;
|
;;
|
||||||
git-subtree-split:)
|
git-subtree-split:)
|
||||||
sub="$(git rev-parse "$b^{commit}")" ||
|
process_subtree_split_trailer "$b" "$sq" "$repository"
|
||||||
die "could not rev-parse split hash $b from commit $sq"
|
|
||||||
;;
|
;;
|
||||||
END)
|
END)
|
||||||
if test -n "$sub"
|
if test -n "$sub"
|
||||||
@ -412,14 +453,19 @@ find_latest_squash () {
|
|||||||
done || exit $?
|
done || exit $?
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: find_existing_splits DIR REV
|
# Usage: find_existing_splits DIR REV [REPOSITORY]
|
||||||
find_existing_splits () {
|
find_existing_splits () {
|
||||||
assert test $# = 2
|
assert test $# = 2 -o $# = 3
|
||||||
debug "Looking for prior splits..."
|
debug "Looking for prior splits..."
|
||||||
local indent=$(($indent + 1))
|
local indent=$(($indent + 1))
|
||||||
|
|
||||||
dir="$1"
|
dir="$1"
|
||||||
rev="$2"
|
rev="$2"
|
||||||
|
repository=""
|
||||||
|
if test "$#" = 3
|
||||||
|
then
|
||||||
|
repository="$3"
|
||||||
|
fi
|
||||||
main=
|
main=
|
||||||
sub=
|
sub=
|
||||||
local grep_format="^git-subtree-dir: $dir/*\$"
|
local grep_format="^git-subtree-dir: $dir/*\$"
|
||||||
@ -439,8 +485,7 @@ find_existing_splits () {
|
|||||||
main="$b"
|
main="$b"
|
||||||
;;
|
;;
|
||||||
git-subtree-split:)
|
git-subtree-split:)
|
||||||
sub="$(git rev-parse "$b^{commit}")" ||
|
process_subtree_split_trailer "$b" "$sq" "$repository"
|
||||||
die "could not rev-parse split hash $b from commit $sq"
|
|
||||||
;;
|
;;
|
||||||
END)
|
END)
|
||||||
debug "Main is: '$main'"
|
debug "Main is: '$main'"
|
||||||
@ -490,7 +535,7 @@ copy_commit () {
|
|||||||
cat
|
cat
|
||||||
) |
|
) |
|
||||||
git commit-tree "$2" $3 # reads the rest of stdin
|
git commit-tree "$2" $3 # reads the rest of stdin
|
||||||
) || die "Can't copy commit $1"
|
) || die "fatal: can't copy commit $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: add_msg DIR LATEST_OLD LATEST_NEW
|
# Usage: add_msg DIR LATEST_OLD LATEST_NEW
|
||||||
@ -718,11 +763,11 @@ ensure_clean () {
|
|||||||
assert test $# = 0
|
assert test $# = 0
|
||||||
if ! git diff-index HEAD --exit-code --quiet 2>&1
|
if ! git diff-index HEAD --exit-code --quiet 2>&1
|
||||||
then
|
then
|
||||||
die "Working tree has modifications. Cannot add."
|
die "fatal: working tree has modifications. Cannot add."
|
||||||
fi
|
fi
|
||||||
if ! git diff-index --cached HEAD --exit-code --quiet 2>&1
|
if ! git diff-index --cached HEAD --exit-code --quiet 2>&1
|
||||||
then
|
then
|
||||||
die "Index has modifications. Cannot add."
|
die "fatal: index has modifications. Cannot add."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -730,7 +775,7 @@ ensure_clean () {
|
|||||||
ensure_valid_ref_format () {
|
ensure_valid_ref_format () {
|
||||||
assert test $# = 1
|
assert test $# = 1
|
||||||
git check-ref-format "refs/heads/$1" ||
|
git check-ref-format "refs/heads/$1" ||
|
||||||
die "'$1' does not look like a ref"
|
die "fatal: '$1' does not look like a ref"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: process_split_commit REV PARENTS
|
# Usage: process_split_commit REV PARENTS
|
||||||
@ -796,7 +841,7 @@ cmd_add () {
|
|||||||
if test $# -eq 1
|
if test $# -eq 1
|
||||||
then
|
then
|
||||||
git rev-parse -q --verify "$1^{commit}" >/dev/null ||
|
git rev-parse -q --verify "$1^{commit}" >/dev/null ||
|
||||||
die "'$1' does not refer to a commit"
|
die "fatal: '$1' does not refer to a commit"
|
||||||
|
|
||||||
cmd_add_commit "$@"
|
cmd_add_commit "$@"
|
||||||
|
|
||||||
@ -811,7 +856,7 @@ cmd_add () {
|
|||||||
|
|
||||||
cmd_add_repository "$@"
|
cmd_add_repository "$@"
|
||||||
else
|
else
|
||||||
say >&2 "error: parameters were '$*'"
|
say >&2 "fatal: parameters were '$*'"
|
||||||
die "Provide either a commit or a repository and commit."
|
die "Provide either a commit or a repository and commit."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@ -843,7 +888,7 @@ cmd_add_commit () {
|
|||||||
git checkout -- "$dir" || exit $?
|
git checkout -- "$dir" || exit $?
|
||||||
tree=$(git write-tree) || exit $?
|
tree=$(git write-tree) || exit $?
|
||||||
|
|
||||||
headrev=$(git rev-parse HEAD) || exit $?
|
headrev=$(git rev-parse --verify HEAD) || exit $?
|
||||||
if test -n "$headrev" && test "$headrev" != "$rev"
|
if test -n "$headrev" && test "$headrev" != "$rev"
|
||||||
then
|
then
|
||||||
headp="-p $headrev"
|
headp="-p $headrev"
|
||||||
@ -866,17 +911,22 @@ cmd_add_commit () {
|
|||||||
say >&2 "Added dir '$dir'"
|
say >&2 "Added dir '$dir'"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: cmd_split [REV]
|
# Usage: cmd_split [REV] [REPOSITORY]
|
||||||
cmd_split () {
|
cmd_split () {
|
||||||
if test $# -eq 0
|
if test $# -eq 0
|
||||||
then
|
then
|
||||||
rev=$(git rev-parse HEAD)
|
rev=$(git rev-parse HEAD)
|
||||||
elif test $# -eq 1
|
elif test $# -eq 1 -o $# -eq 2
|
||||||
then
|
then
|
||||||
rev=$(git rev-parse -q --verify "$1^{commit}") ||
|
rev=$(git rev-parse -q --verify "$1^{commit}") ||
|
||||||
die "'$1' does not refer to a commit"
|
die "fatal: '$1' does not refer to a commit"
|
||||||
else
|
else
|
||||||
die "You must provide exactly one revision. Got: '$*'"
|
die "fatal: you must provide exactly one revision, and optionnally a repository. Got: '$*'"
|
||||||
|
fi
|
||||||
|
repository=""
|
||||||
|
if test "$#" = 2
|
||||||
|
then
|
||||||
|
repository="$2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -n "$arg_split_rejoin"
|
if test -n "$arg_split_rejoin"
|
||||||
@ -900,7 +950,7 @@ cmd_split () {
|
|||||||
done || exit $?
|
done || exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
unrevs="$(find_existing_splits "$dir" "$rev")" || exit $?
|
unrevs="$(find_existing_splits "$dir" "$rev" "$repository")" || exit $?
|
||||||
|
|
||||||
# We can't restrict rev-list to only $dir here, because some of our
|
# We can't restrict rev-list to only $dir here, because some of our
|
||||||
# parents have the $dir contents the root, and those won't match.
|
# parents have the $dir contents the root, and those won't match.
|
||||||
@ -919,7 +969,7 @@ cmd_split () {
|
|||||||
latest_new=$(cache_get latest_new) || exit $?
|
latest_new=$(cache_get latest_new) || exit $?
|
||||||
if test -z "$latest_new"
|
if test -z "$latest_new"
|
||||||
then
|
then
|
||||||
die "No new revisions were found"
|
die "fatal: no new revisions were found"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -n "$arg_split_rejoin"
|
if test -n "$arg_split_rejoin"
|
||||||
@ -940,7 +990,7 @@ cmd_split () {
|
|||||||
then
|
then
|
||||||
if ! git merge-base --is-ancestor "$arg_split_branch" "$latest_new"
|
if ! git merge-base --is-ancestor "$arg_split_branch" "$latest_new"
|
||||||
then
|
then
|
||||||
die "Branch '$arg_split_branch' is not an ancestor of commit '$latest_new'."
|
die "fatal: branch '$arg_split_branch' is not an ancestor of commit '$latest_new'."
|
||||||
fi
|
fi
|
||||||
action='Updated'
|
action='Updated'
|
||||||
else
|
else
|
||||||
@ -954,20 +1004,25 @@ cmd_split () {
|
|||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: cmd_merge REV
|
# Usage: cmd_merge REV [REPOSITORY]
|
||||||
cmd_merge () {
|
cmd_merge () {
|
||||||
test $# -eq 1 ||
|
test $# -eq 1 -o $# -eq 2 ||
|
||||||
die "You must provide exactly one revision. Got: '$*'"
|
die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'"
|
||||||
rev=$(git rev-parse -q --verify "$1^{commit}") ||
|
rev=$(git rev-parse -q --verify "$1^{commit}") ||
|
||||||
die "'$1' does not refer to a commit"
|
die "fatal: '$1' does not refer to a commit"
|
||||||
|
repository=""
|
||||||
|
if test "$#" = 2
|
||||||
|
then
|
||||||
|
repository="$2"
|
||||||
|
fi
|
||||||
ensure_clean
|
ensure_clean
|
||||||
|
|
||||||
if test -n "$arg_addmerge_squash"
|
if test -n "$arg_addmerge_squash"
|
||||||
then
|
then
|
||||||
first_split="$(find_latest_squash "$dir")" || exit $?
|
first_split="$(find_latest_squash "$dir" "$repository")" || exit $?
|
||||||
if test -z "$first_split"
|
if test -z "$first_split"
|
||||||
then
|
then
|
||||||
die "Can't squash-merge: '$dir' was never added."
|
die "fatal: can't squash-merge: '$dir' was never added."
|
||||||
fi
|
fi
|
||||||
set $first_split
|
set $first_split
|
||||||
old=$1
|
old=$1
|
||||||
@ -995,19 +1050,21 @@ cmd_merge () {
|
|||||||
cmd_pull () {
|
cmd_pull () {
|
||||||
if test $# -ne 2
|
if test $# -ne 2
|
||||||
then
|
then
|
||||||
die "You must provide <repository> <ref>"
|
die "fatal: you must provide <repository> <ref>"
|
||||||
fi
|
fi
|
||||||
|
repository="$1"
|
||||||
|
ref="$2"
|
||||||
ensure_clean
|
ensure_clean
|
||||||
ensure_valid_ref_format "$2"
|
ensure_valid_ref_format "$ref"
|
||||||
git fetch "$@" || exit $?
|
git fetch "$repository" "$ref" || exit $?
|
||||||
cmd_merge FETCH_HEAD
|
cmd_merge FETCH_HEAD "$repository"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: cmd_push REPOSITORY [+][LOCALREV:]REMOTEREF
|
# Usage: cmd_push REPOSITORY [+][LOCALREV:]REMOTEREF
|
||||||
cmd_push () {
|
cmd_push () {
|
||||||
if test $# -ne 2
|
if test $# -ne 2
|
||||||
then
|
then
|
||||||
die "You must provide <repository> <refspec>"
|
die "fatal: you must provide <repository> <refspec>"
|
||||||
fi
|
fi
|
||||||
if test -e "$dir"
|
if test -e "$dir"
|
||||||
then
|
then
|
||||||
@ -1022,13 +1079,13 @@ cmd_push () {
|
|||||||
fi
|
fi
|
||||||
ensure_valid_ref_format "$remoteref"
|
ensure_valid_ref_format "$remoteref"
|
||||||
localrev_presplit=$(git rev-parse -q --verify "$localrevname_presplit^{commit}") ||
|
localrev_presplit=$(git rev-parse -q --verify "$localrevname_presplit^{commit}") ||
|
||||||
die "'$localrevname_presplit' does not refer to a commit"
|
die "fatal: '$localrevname_presplit' does not refer to a commit"
|
||||||
|
|
||||||
echo "git push using: " "$repository" "$refspec"
|
echo "git push using: " "$repository" "$refspec"
|
||||||
localrev=$(cmd_split "$localrev_presplit") || die
|
localrev=$(cmd_split "$localrev_presplit" "$repository") || die
|
||||||
git push "$repository" "$localrev":"refs/heads/$remoteref"
|
git push "$repository" "$localrev":"refs/heads/$remoteref"
|
||||||
else
|
else
|
||||||
die "'$dir' must already exist. Try 'git subtree add'."
|
die "fatal: '$dir' must already exist. Try 'git subtree add'."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ SYNOPSIS
|
|||||||
[verse]
|
[verse]
|
||||||
'git subtree' [<options>] -P <prefix> add <local-commit>
|
'git subtree' [<options>] -P <prefix> add <local-commit>
|
||||||
'git subtree' [<options>] -P <prefix> add <repository> <remote-ref>
|
'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>]
|
'git subtree' [<options>] -P <prefix> split [<local-commit>]
|
||||||
|
|
||||||
[verse]
|
[verse]
|
||||||
@ -76,7 +76,7 @@ add <repository> <remote-ref>::
|
|||||||
only a single commit from the subproject, rather than its
|
only a single commit from the subproject, rather than its
|
||||||
entire history.
|
entire history.
|
||||||
|
|
||||||
merge <local-commit>::
|
merge <local-commit> [<repository>]::
|
||||||
Merge recent changes up to <local-commit> into the <prefix>
|
Merge recent changes up to <local-commit> into the <prefix>
|
||||||
subtree. As with normal 'git merge', this doesn't
|
subtree. As with normal 'git merge', this doesn't
|
||||||
remove your own local changes; it just merges those
|
remove your own local changes; it just merges those
|
||||||
@ -88,8 +88,13 @@ 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,
|
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
|
for example. If your merge introduces a conflict, you can resolve it in
|
||||||
the usual ways.
|
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>]::
|
split [<local-commit>] [<repository>]::
|
||||||
Extract a new, synthetic project history from the
|
Extract a new, synthetic project history from the
|
||||||
history of the <prefix> subtree of <local-commit>, or of
|
history of the <prefix> subtree of <local-commit>, or of
|
||||||
HEAD if no <local-commit> is given. The new history
|
HEAD if no <local-commit> is given. The new history
|
||||||
@ -109,6 +114,11 @@ settings passed to 'split' (such as '--annotate') are the same.
|
|||||||
Because of this, if you add new commits and then re-split, the new
|
Because of this, if you add new commits and then re-split, the new
|
||||||
commits will be attached as commits on top of the history you
|
commits will be attached as commits on top of the history you
|
||||||
generated last time, so 'git merge' and friends will work as expected.
|
generated last time, so 'git merge' and friends will work as expected.
|
||||||
|
+
|
||||||
|
When a 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.
|
||||||
|
|
||||||
pull <repository> <remote-ref>::
|
pull <repository> <remote-ref>::
|
||||||
Exactly like 'merge', but parallels 'git pull' in that
|
Exactly like 'merge', but parallels 'git pull' in that
|
||||||
|
@ -43,6 +43,30 @@ last_commit_subject () {
|
|||||||
git log --pretty=format:%s -1
|
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_success 'shows short help text for -h' '
|
||||||
test_expect_code 129 git subtree -h >out 2>err &&
|
test_expect_code 129 git subtree -h >out 2>err &&
|
||||||
test_must_be_empty 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'
|
# Tests for 'git subtree split'
|
||||||
#
|
#
|
||||||
@ -277,7 +308,7 @@ test_expect_success 'split requires option --prefix' '
|
|||||||
cd "$test_count" &&
|
cd "$test_count" &&
|
||||||
git fetch ./"sub proj" HEAD &&
|
git fetch ./"sub proj" HEAD &&
|
||||||
git subtree add --prefix="sub dir" FETCH_HEAD &&
|
git subtree add --prefix="sub dir" FETCH_HEAD &&
|
||||||
echo "You must provide the --prefix option." >expected &&
|
echo "fatal: you must provide the --prefix option." >expected &&
|
||||||
test_must_fail git subtree split >actual 2>&1 &&
|
test_must_fail git subtree split >actual 2>&1 &&
|
||||||
test_debug "printf '"expected: "'" &&
|
test_debug "printf '"expected: "'" &&
|
||||||
test_debug "cat expected" &&
|
test_debug "cat expected" &&
|
||||||
@ -296,7 +327,7 @@ test_expect_success 'split requires path given by option --prefix must exist' '
|
|||||||
cd "$test_count" &&
|
cd "$test_count" &&
|
||||||
git fetch ./"sub proj" HEAD &&
|
git fetch ./"sub proj" HEAD &&
|
||||||
git subtree add --prefix="sub dir" FETCH_HEAD &&
|
git subtree add --prefix="sub dir" FETCH_HEAD &&
|
||||||
echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" >expected &&
|
echo "fatal: '\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" >expected &&
|
||||||
test_must_fail git subtree split --prefix=non-existent-directory >actual 2>&1 &&
|
test_must_fail git subtree split --prefix=non-existent-directory >actual 2>&1 &&
|
||||||
test_debug "printf '"expected: "'" &&
|
test_debug "printf '"expected: "'" &&
|
||||||
test_debug "cat expected" &&
|
test_debug "cat expected" &&
|
||||||
@ -551,6 +582,12 @@ test_expect_success 'split "sub dir"/ with --branch for an incompatible branch'
|
|||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'split after annotated tag was added/merged with --squash pre-v2.32.0' '
|
||||||
|
test_create_pre2_32_repo "$test_count" &&
|
||||||
|
test_must_fail git -C "$test_count-clone" subtree split --prefix="sub" HEAD &&
|
||||||
|
git -C "$test_count-clone" subtree split --prefix="sub" HEAD "../$test_count-sub"
|
||||||
|
'
|
||||||
|
|
||||||
#
|
#
|
||||||
# Tests for 'git subtree pull'
|
# Tests for 'git subtree pull'
|
||||||
#
|
#
|
||||||
@ -570,7 +607,7 @@ test_expect_success 'pull requires option --prefix' '
|
|||||||
cd "$test_count" &&
|
cd "$test_count" &&
|
||||||
test_must_fail git subtree pull ./"sub proj" HEAD >out 2>err &&
|
test_must_fail git subtree pull ./"sub proj" HEAD >out 2>err &&
|
||||||
|
|
||||||
echo "You must provide the --prefix option." >expected &&
|
echo "fatal: you must provide the --prefix option." >expected &&
|
||||||
test_must_be_empty out &&
|
test_must_be_empty out &&
|
||||||
test_cmp expected err
|
test_cmp expected err
|
||||||
)
|
)
|
||||||
@ -584,7 +621,7 @@ test_expect_success 'pull requires path given by option --prefix must exist' '
|
|||||||
(
|
(
|
||||||
test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" HEAD >out 2>err &&
|
test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" HEAD >out 2>err &&
|
||||||
|
|
||||||
echo "'\''sub dir'\'' does not exist; use '\''git subtree add'\''" >expected &&
|
echo "fatal: '\''sub dir'\'' does not exist; use '\''git subtree add'\''" >expected &&
|
||||||
test_must_be_empty out &&
|
test_must_be_empty out &&
|
||||||
test_cmp expected err
|
test_cmp expected err
|
||||||
)
|
)
|
||||||
@ -630,6 +667,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'
|
# Tests for 'git subtree push'
|
||||||
#
|
#
|
||||||
@ -643,7 +685,7 @@ test_expect_success 'push requires option --prefix' '
|
|||||||
cd "$test_count" &&
|
cd "$test_count" &&
|
||||||
git fetch ./"sub proj" HEAD &&
|
git fetch ./"sub proj" HEAD &&
|
||||||
git subtree add --prefix="sub dir" FETCH_HEAD &&
|
git subtree add --prefix="sub dir" FETCH_HEAD &&
|
||||||
echo "You must provide the --prefix option." >expected &&
|
echo "fatal: you must provide the --prefix option." >expected &&
|
||||||
test_must_fail git subtree push "./sub proj" from-mainline >actual 2>&1 &&
|
test_must_fail git subtree push "./sub proj" from-mainline >actual 2>&1 &&
|
||||||
test_debug "printf '"expected: "'" &&
|
test_debug "printf '"expected: "'" &&
|
||||||
test_debug "cat expected" &&
|
test_debug "cat expected" &&
|
||||||
@ -662,7 +704,7 @@ test_expect_success 'push requires path given by option --prefix must exist' '
|
|||||||
cd "$test_count" &&
|
cd "$test_count" &&
|
||||||
git fetch ./"sub proj" HEAD &&
|
git fetch ./"sub proj" HEAD &&
|
||||||
git subtree add --prefix="sub dir" FETCH_HEAD &&
|
git subtree add --prefix="sub dir" FETCH_HEAD &&
|
||||||
echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" >expected &&
|
echo "fatal: '\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" >expected &&
|
||||||
test_must_fail git subtree push --prefix=non-existent-directory "./sub proj" from-mainline >actual 2>&1 &&
|
test_must_fail git subtree push --prefix=non-existent-directory "./sub proj" from-mainline >actual 2>&1 &&
|
||||||
test_debug "printf '"expected: "'" &&
|
test_debug "printf '"expected: "'" &&
|
||||||
test_debug "cat expected" &&
|
test_debug "cat expected" &&
|
||||||
@ -953,6 +995,12 @@ test_expect_success 'push "sub dir"/ with a local rev' '
|
|||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'push after annotated tag was added/merged with --squash pre-v2.32.0' '
|
||||||
|
test_create_pre2_32_repo "$test_count" &&
|
||||||
|
test_create_commit "$test_count-clone" sub/main-sub1 &&
|
||||||
|
git -C "$test_count-clone" subtree push --prefix="sub" "../$test_count-sub" from-mainline
|
||||||
|
'
|
||||||
|
|
||||||
#
|
#
|
||||||
# Validity checking
|
# Validity checking
|
||||||
#
|
#
|
||||||
|
@ -273,13 +273,13 @@ debug () {
|
|||||||
# <file>, <contents>, and <tag> all default to <message>.
|
# <file>, <contents>, and <tag> all default to <message>.
|
||||||
|
|
||||||
test_commit () {
|
test_commit () {
|
||||||
notick= &&
|
local notick= &&
|
||||||
echo=echo &&
|
local echo=echo &&
|
||||||
append= &&
|
local append= &&
|
||||||
author= &&
|
local author= &&
|
||||||
signoff= &&
|
local signoff= &&
|
||||||
indir= &&
|
local indir= &&
|
||||||
tag=light &&
|
local tag=light &&
|
||||||
while test $# != 0
|
while test $# != 0
|
||||||
do
|
do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -322,7 +322,7 @@ test_commit () {
|
|||||||
shift
|
shift
|
||||||
done &&
|
done &&
|
||||||
indir=${indir:+"$indir"/} &&
|
indir=${indir:+"$indir"/} &&
|
||||||
file=${2:-"$1.t"} &&
|
local file=${2:-"$1.t"} &&
|
||||||
if test -n "$append"
|
if test -n "$append"
|
||||||
then
|
then
|
||||||
$echo "${3-$1}" >>"$indir$file"
|
$echo "${3-$1}" >>"$indir$file"
|
||||||
|
Loading…
Reference in New Issue
Block a user