1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-04-23 17:15:11 +02:00

Merge branch 'ls/subtree' into next

"git subtree" updates.

* ls/subtree: (30 commits)
  subtree: be stricter about validating flags
  subtree: push: allow specifying a local rev other than HEAD
  subtree: allow 'split' flags to be passed to 'push'
  subtree: allow --squash to be used with --rejoin
  subtree: give the docs a once-over
  subtree: have $indent actually affect indentation
  subtree: don't let debug and progress output clash
  subtree: add comments and sanity checks
  subtree: remove duplicate check
  subtree: parse revs in individual cmd_ functions
  subtree: use "^{commit}" instead of "^0"
  subtree: don't fuss with PATH
  subtree: use "$*" instead of "$@" as appropriate
  subtree: use more explicit variable names for cmdline args
  subtree: use git-sh-setup's `say`
  subtree: use `git merge-base --is-ancestor`
  subtree: drop support for git < 1.7
  subtree: more consistent error propagation
  subtree: don't have loose code outside of a function
  subtree: t7900: add porcelain tests for 'pull' and 'push'
  ...
This commit is contained in:
Junio C Hamano 2021-05-03 15:02:46 +09:00
commit 12c5fe8677
5 changed files with 1425 additions and 866 deletions

1
.gitignore vendored
View File

@ -163,6 +163,7 @@
/git-stripspace
/git-submodule
/git-submodule--helper
/git-subtree
/git-svn
/git-switch
/git-symbolic-ref

View File

@ -4,208 +4,261 @@
#
# Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
#
if test $# -eq 0
if test -z "$GIT_EXEC_PATH" || test "${PATH#"${GIT_EXEC_PATH}:"}" = "$PATH" || ! test -f "$GIT_EXEC_PATH/git-sh-setup"
then
set -- -h
echo >&2 'It looks like either your git installation or your'
echo >&2 'git-subtree installation is broken.'
echo >&2
echo >&2 "Tips:"
echo >&2 " - If \`git --exec-path\` does not print the correct path to"
echo >&2 " your git install directory, then set the GIT_EXEC_PATH"
echo >&2 " environment variable to the correct directory."
echo >&2 " - Make sure that your \`${0##*/}\` file is either in your"
echo >&2 " PATH or in your git exec path (\`$(git --exec-path)\`)."
echo >&2 " - You should run git-subtree as \`git ${0##*/git-}\`,"
echo >&2 " not as \`${0##*/}\`." >&2
exit 126
fi
OPTS_SPEC="\
git subtree add --prefix=<prefix> <commit>
git subtree add --prefix=<prefix> <repository> <ref>
git subtree merge --prefix=<prefix> <commit>
git subtree split --prefix=<prefix> [<commit>]
git subtree pull --prefix=<prefix> <repository> <ref>
git subtree push --prefix=<prefix> <repository> <ref>
git subtree split --prefix=<prefix> <commit>
git subtree push --prefix=<prefix> <repository> <refspec>
--
h,help show the help
q quiet
d show debug messages
P,prefix= the name of the subdir to split out
m,message= use the given message as the commit message for the merge commit
options for 'split'
options for 'split' (also: 'push')
annotate= add a prefix to commit message of new commits
b,branch= create a new branch from the split subtree
ignore-joins ignore prior --rejoin commits
onto= try connecting new tree to an existing one
rejoin merge the new branch back into HEAD
options for 'add', 'merge', and 'pull'
options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin')
squash merge subtree changes as a single commit
m,message= use the given message as the commit message for the merge commit
"
eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
PATH=$PATH:$(git --exec-path)
. git-sh-setup
require_work_tree
quiet=
branch=
debug=
command=
onto=
rejoin=
ignore_joins=
annotate=
squash=
message=
prefix=
indent=0
# Usage: debug [MSG...]
debug () {
if test -n "$debug"
if test -n "$arg_debug"
then
printf "%s\n" "$*" >&2
fi
}
say () {
if test -z "$quiet"
then
printf "%s\n" "$*" >&2
printf "%$(($indent * 2))s%s\n" '' "$*" >&2
fi
}
# Usage: progress [MSG...]
progress () {
if test -z "$quiet"
if test -z "$GIT_QUIET"
then
printf "%s\r" "$*" >&2
if test -z "$arg_debug"
then
# Debug mode is off.
#
# Print one progress line that we keep updating (use
# "\r" to return to the beginning of the line, rather
# than "\n" to start a new line). This only really
# works when stderr is a terminal.
printf "%s\r" "$*" >&2
else
# Debug mode is on. The `debug` function is regularly
# printing to stderr.
#
# Don't do the one-line-with-"\r" thing, because on a
# terminal the debug output would overwrite and hide the
# progress output. Add a "progress:" prefix to make the
# progress output and the debug output easy to
# distinguish. This ensures maximum readability whether
# stderr is a terminal or a file.
printf "progress: %s\n" "$*" >&2
fi
fi
}
# Usage: assert CMD...
assert () {
if ! "$@"
then
die "assertion failed: " "$@"
die "assertion failed: $*"
fi
}
ensure_single_rev () {
if test $# -ne 1
main () {
if test $# -eq 0
then
die "You must provide exactly one revision. Got: '$@'"
set -- -h
fi
}
set_args="$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
eval "$set_args"
. git-sh-setup
require_work_tree
while test $# -gt 0
do
opt="$1"
shift
case "$opt" in
-q)
quiet=1
;;
-d)
debug=1
;;
--annotate)
annotate="$1"
# First figure out the command and whether we use --rejoin, so
# that we can provide more helpful validation when we do the
# "real" flag parsing.
arg_split_rejoin=
allow_split=
allow_addmerge=
while test $# -gt 0
do
opt="$1"
shift
case "$opt" in
--annotate|-b|-P|-m|--onto)
shift
;;
--rejoin)
arg_split_rejoin=1
;;
--no-rejoin)
arg_split_rejoin=
;;
--)
break
;;
esac
done
arg_command=$1
case "$arg_command" in
add|merge|pull)
allow_addmerge=1
;;
--no-annotate)
annotate=
;;
-b)
branch="$1"
shift
;;
-P)
prefix="${1%/}"
shift
;;
-m)
message="$1"
shift
;;
--no-prefix)
prefix=
;;
--onto)
onto="$1"
shift
;;
--no-onto)
onto=
;;
--rejoin)
rejoin=1
;;
--no-rejoin)
rejoin=
;;
--ignore-joins)
ignore_joins=1
;;
--no-ignore-joins)
ignore_joins=
;;
--squash)
squash=1
;;
--no-squash)
squash=
;;
--)
break
split|push)
allow_split=1
allow_addmerge=$arg_split_rejoin
;;
*)
die "Unexpected option: $opt"
die "Unknown command '$arg_command'"
;;
esac
done
# Reset the arguments array for "real" flag parsing.
eval "$set_args"
command="$1"
shift
# Begin "real" flag parsing.
arg_debug=
arg_prefix=
arg_split_branch=
arg_split_onto=
arg_split_ignore_joins=
arg_split_annotate=
arg_addmerge_squash=
arg_addmerge_message=
while test $# -gt 0
do
opt="$1"
shift
case "$command" in
add|merge|pull)
default=
;;
split|push)
default="--default HEAD"
;;
*)
die "Unknown command '$command'"
;;
esac
case "$opt" in
-q)
GIT_QUIET=1
;;
-d)
arg_debug=1
;;
--annotate)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_split_annotate="$1"
shift
;;
--no-annotate)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_split_annotate=
;;
-b)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_split_branch="$1"
shift
;;
-P)
arg_prefix="${1%/}"
shift
;;
-m)
test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_addmerge_message="$1"
shift
;;
--no-prefix)
arg_prefix=
;;
--onto)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_split_onto="$1"
shift
;;
--no-onto)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_split_onto=
;;
--rejoin)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
;;
--no-rejoin)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
;;
--ignore-joins)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_split_ignore_joins=1
;;
--no-ignore-joins)
test -n "$allow_split" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_split_ignore_joins=
;;
--squash)
test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_addmerge_squash=1
;;
--no-squash)
test -n "$allow_addmerge" || die "The '$opt' flag does not make sense with 'git subtree $arg_command'."
arg_addmerge_squash=
;;
--)
break
;;
*)
die "Unexpected option: $opt"
;;
esac
done
shift
if test -z "$prefix"
then
die "You must provide the --prefix option."
fi
case "$command" in
add)
test -e "$prefix" &&
die "prefix '$prefix' already exists."
;;
*)
test -e "$prefix" ||
die "'$prefix' does not exist; use 'git subtree add'"
;;
esac
dir="$(dirname "$prefix/.")"
if test "$command" != "pull" &&
test "$command" != "add" &&
test "$command" != "push"
then
revs=$(git rev-parse $default --revs-only "$@") || exit $?
dirs=$(git rev-parse --no-revs --no-flags "$@") || exit $?
ensure_single_rev $revs
if test -n "$dirs"
if test -z "$arg_prefix"
then
die "Error: Use --prefix instead of bare filenames."
die "You must provide the --prefix option."
fi
fi
debug "command: {$command}"
debug "quiet: {$quiet}"
debug "revs: {$revs}"
debug "dir: {$dir}"
debug "opts: {$*}"
debug
case "$arg_command" in
add)
test -e "$arg_prefix" &&
die "prefix '$arg_prefix' already exists."
;;
*)
test -e "$arg_prefix" ||
die "'$arg_prefix' does not exist; use 'git subtree add'"
;;
esac
dir="$(dirname "$arg_prefix/.")"
debug "command: {$arg_command}"
debug "quiet: {$GIT_QUIET}"
debug "dir: {$dir}"
debug "opts: {$*}"
debug
"cmd_$arg_command" "$@"
}
# Usage: cache_setup
cache_setup () {
assert test $# = 0
cachedir="$GIT_DIR/subtree-cache/$$"
rm -rf "$cachedir" ||
die "Can't delete old cachedir: $cachedir"
@ -216,6 +269,7 @@ cache_setup () {
debug "Using cachedir: $cachedir" >&2
}
# Usage: cache_get [REVS...]
cache_get () {
for oldrev in "$@"
do
@ -227,6 +281,7 @@ cache_get () {
done
}
# Usage: cache_miss [REVS...]
cache_miss () {
for oldrev in "$@"
do
@ -237,24 +292,30 @@ cache_miss () {
done
}
# Usage: check_parents PARENTS_EXPR
check_parents () {
missed=$(cache_miss "$1")
local indent=$(($2 + 1))
assert test $# = 1
missed=$(cache_miss "$1") || exit $?
local indent=$(($indent + 1))
for miss in $missed
do
if ! test -r "$cachedir/notree/$miss"
then
debug " incorrect order: $miss"
process_split_commit "$miss" "" "$indent"
debug "incorrect order: $miss"
process_split_commit "$miss" ""
fi
done
}
# Usage: set_notree REV
set_notree () {
assert test $# = 1
echo "1" > "$cachedir/notree/$1"
}
# Usage: cache_set OLDREV NEWREV
cache_set () {
assert test $# = 2
oldrev="$1"
newrev="$2"
if test "$oldrev" != "latest_old" &&
@ -266,7 +327,9 @@ cache_set () {
echo "$newrev" >"$cachedir/$oldrev"
}
# Usage: rev_exists REV
rev_exists () {
assert test $# = 1
if git rev-parse "$1" >/dev/null 2>&1
then
return 0
@ -275,32 +338,25 @@ rev_exists () {
fi
}
rev_is_descendant_of_branch () {
newrev="$1"
branch="$2"
branch_hash=$(git rev-parse "$branch")
match=$(git rev-list -1 "$branch_hash" "^$newrev")
if test -z "$match"
then
return 0
else
return 1
fi
}
# if a commit doesn't have a parent, this might not work. But we only want
# Usage: try_remove_previous REV
#
# If a commit doesn't have a parent, this might not work. But we only want
# to remove the parent from the rev-list, and since it doesn't exist, it won't
# be there anyway, so do nothing in that case.
try_remove_previous () {
assert test $# = 1
if rev_exists "$1^"
then
echo "^$1^"
fi
}
# Usage: find_latest_squash DIR
find_latest_squash () {
assert test $# = 1
debug "Looking for latest squash ($dir)..."
local indent=$(($indent + 1))
dir="$1"
sq=
main=
@ -319,7 +375,7 @@ find_latest_squash () {
main="$b"
;;
git-subtree-split:)
sub="$(git rev-parse "$b^0")" ||
sub="$(git rev-parse "$b^{commit}")" ||
die "could not rev-parse split hash $b from commit $sq"
;;
END)
@ -329,7 +385,8 @@ find_latest_squash () {
then
# a rejoin commit?
# Pretend its sub was a squash.
sq="$sub"
sq=$(git rev-parse --verify "$sq^2") ||
die
fi
debug "Squash found: $sq $sub"
echo "$sq" "$sub"
@ -340,22 +397,26 @@ find_latest_squash () {
sub=
;;
esac
done
done || exit $?
}
# Usage: find_existing_splits DIR REV
find_existing_splits () {
assert test $# = 2
debug "Looking for prior splits..."
local indent=$(($indent + 1))
dir="$1"
revs="$2"
rev="$2"
main=
sub=
local grep_format="^git-subtree-dir: $dir/*\$"
if test -n "$ignore_joins"
if test -n "$arg_split_ignore_joins"
then
grep_format="^Add '$dir/' from commit '"
fi
git log --grep="$grep_format" \
--no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
--no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' "$rev" |
while read a b junk
do
case "$a" in
@ -366,11 +427,11 @@ find_existing_splits () {
main="$b"
;;
git-subtree-split:)
sub="$(git rev-parse "$b^0")" ||
sub="$(git rev-parse "$b^{commit}")" ||
die "could not rev-parse split hash $b from commit $sq"
;;
END)
debug " Main is: '$main'"
debug "Main is: '$main'"
if test -z "$main" -a -n "$sub"
then
# squash commits refer to a subtree
@ -389,10 +450,12 @@ find_existing_splits () {
sub=
;;
esac
done
done || exit $?
}
# Usage: copy_commit REV TREE FLAGS_STR
copy_commit () {
assert test $# = 3
# We're going to set some environment vars here, so
# do it in a subshell to get rid of them safely later
debug copy_commit "{$1}" "{$2}" "{$3}"
@ -411,23 +474,32 @@ copy_commit () {
GIT_COMMITTER_EMAIL \
GIT_COMMITTER_DATE
(
printf "%s" "$annotate"
printf "%s" "$arg_split_annotate"
cat
) |
git commit-tree "$2" $3 # reads the rest of stdin
) || die "Can't copy commit $1"
}
# Usage: add_msg DIR LATEST_OLD LATEST_NEW
add_msg () {
assert test $# = 3
dir="$1"
latest_old="$2"
latest_new="$3"
if test -n "$message"
if test -n "$arg_addmerge_message"
then
commit_message="$message"
commit_message="$arg_addmerge_message"
else
commit_message="Add '$dir/' from commit '$latest_new'"
fi
if test -n "$arg_split_rejoin"
then
# If this is from a --rejoin, then rejoin_msg has
# already inserted the `git-subtree-xxx:` tags
echo "$commit_message"
return
fi
cat <<-EOF
$commit_message
@ -437,22 +509,26 @@ add_msg () {
EOF
}
# Usage: add_squashed_msg REV DIR
add_squashed_msg () {
if test -n "$message"
assert test $# = 2
if test -n "$arg_addmerge_message"
then
echo "$message"
echo "$arg_addmerge_message"
else
echo "Merge commit '$1' as '$2'"
fi
}
# Usage: rejoin_msg DIR LATEST_OLD LATEST_NEW
rejoin_msg () {
assert test $# = 3
dir="$1"
latest_old="$2"
latest_new="$3"
if test -n "$message"
if test -n "$arg_addmerge_message"
then
commit_message="$message"
commit_message="$arg_addmerge_message"
else
commit_message="Split '$dir/' into commit '$latest_new'"
fi
@ -465,7 +541,9 @@ rejoin_msg () {
EOF
}
# Usage: squash_msg DIR OLD_SUBTREE_COMMIT NEW_SUBTREE_COMMIT
squash_msg () {
assert test $# = 3
dir="$1"
oldsub="$2"
newsub="$3"
@ -487,12 +565,16 @@ squash_msg () {
echo "git-subtree-split: $newsub"
}
# Usage: toptree_for_commit COMMIT
toptree_for_commit () {
assert test $# = 1
commit="$1"
git rev-parse --verify "$commit^{tree}" || exit $?
}
# Usage: subtree_for_commit COMMIT DIR
subtree_for_commit () {
assert test $# = 2
commit="$1"
dir="$2"
git ls-tree "$commit" -- "$dir" |
@ -503,17 +585,19 @@ subtree_for_commit () {
test "$type" = "commit" && continue # ignore submodules
echo $tree
break
done
done || exit $?
}
# Usage: tree_changed TREE [PARENTS...]
tree_changed () {
assert test $# -gt 0
tree=$1
shift
if test $# -ne 1
then
return 0 # weird parents, consider it changed
else
ptree=$(toptree_for_commit $1)
ptree=$(toptree_for_commit $1) || exit $?
if test "$ptree" != "$tree"
then
return 0 # changed
@ -523,7 +607,9 @@ tree_changed () {
fi
}
# Usage: new_squash_commit OLD_SQUASHED_COMMIT OLD_NONSQUASHED_COMMIT NEW_NONSQUASHED_COMMIT
new_squash_commit () {
assert test $# = 3
old="$1"
oldsub="$2"
newsub="$3"
@ -538,7 +624,9 @@ new_squash_commit () {
fi
}
# Usage: copy_or_skip REV TREE NEWPARENTS
copy_or_skip () {
assert test $# = 3
rev="$1"
tree="$2"
newparents="$3"
@ -613,7 +701,9 @@ copy_or_skip () {
fi
}
# Usage: ensure_clean
ensure_clean () {
assert test $# = 0
if ! git diff-index HEAD --exit-code --quiet 2>&1
then
die "Working tree has modifications. Cannot add."
@ -624,15 +714,18 @@ ensure_clean () {
fi
}
# Usage: ensure_valid_ref_format REF
ensure_valid_ref_format () {
assert test $# = 1
git check-ref-format "refs/heads/$1" ||
die "'$1' does not look like a ref"
}
# Usage: process_split_commit REV PARENTS
process_split_commit () {
assert test $# = 2
local rev="$1"
local parents="$2"
local indent=$3
if test $indent -eq 0
then
@ -647,20 +740,21 @@ process_split_commit () {
progress "$revcount/$revmax ($createcount) [$extracount]"
debug "Processing commit: $rev"
exists=$(cache_get "$rev")
local indent=$(($indent + 1))
exists=$(cache_get "$rev") || exit $?
if test -n "$exists"
then
debug " prior: $exists"
debug "prior: $exists"
return
fi
createcount=$(($createcount + 1))
debug " parents: $parents"
check_parents "$parents" "$indent"
newparents=$(cache_get $parents)
debug " newparents: $newparents"
debug "parents: $parents"
check_parents "$parents"
newparents=$(cache_get $parents) || exit $?
debug "newparents: $newparents"
tree=$(subtree_for_commit "$rev" "$dir")
debug " tree is: $tree"
tree=$(subtree_for_commit "$rev" "$dir") || exit $?
debug "tree is: $tree"
# ugly. is there no better way to tell if this is a subtree
# vs. a mainline commit? Does it matter?
@ -675,17 +769,15 @@ process_split_commit () {
fi
newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
debug " newrev is: $newrev"
debug "newrev is: $newrev"
cache_set "$rev" "$newrev"
cache_set latest_new "$newrev"
cache_set latest_old "$rev"
}
# Usage: cmd_add REV
# Or: cmd_add REPOSITORY REF
cmd_add () {
if test -e "$dir"
then
die "'$dir' already exists. Cannot add."
fi
ensure_clean
@ -707,27 +799,35 @@ cmd_add () {
cmd_add_repository "$@"
else
say "error: parameters were '$@'"
say >&2 "error: parameters were '$*'"
die "Provide either a commit or a repository and commit."
fi
}
# Usage: cmd_add_repository REPOSITORY REFSPEC
cmd_add_repository () {
assert test $# = 2
echo "git fetch" "$@"
repository=$1
refspec=$2
git fetch "$@" || exit $?
revs=FETCH_HEAD
set -- $revs
cmd_add_commit "$@"
cmd_add_commit FETCH_HEAD
}
# Usage: cmd_add_commit REV
cmd_add_commit () {
rev=$(git rev-parse $default --revs-only "$@") || exit $?
ensure_single_rev $rev
# The rev has already been validated by cmd_add(), we just
# need to normalize it.
assert test $# = 1
rev=$(git rev-parse --verify "$1^{commit}") || exit $?
debug "Adding $dir as '$rev'..."
git read-tree --prefix="$dir" $rev || exit $?
if test -z "$arg_split_rejoin"
then
# Only bother doing this if this is a genuine 'add',
# not a synthetic 'add' from '--rejoin'.
git read-tree --prefix="$dir" $rev || exit $?
fi
git checkout -- "$dir" || exit $?
tree=$(git write-tree) || exit $?
@ -739,44 +839,61 @@ cmd_add_commit () {
headp=
fi
if test -n "$squash"
if test -n "$arg_addmerge_squash"
then
rev=$(new_squash_commit "" "" "$rev") || exit $?
commit=$(add_squashed_msg "$rev" "$dir" |
git commit-tree "$tree" $headp -p "$rev") || exit $?
else
revp=$(peel_committish "$rev") &&
revp=$(peel_committish "$rev") || exit $?
commit=$(add_msg "$dir" $headrev "$rev" |
git commit-tree "$tree" $headp -p "$revp") || exit $?
fi
git reset "$commit" || exit $?
say "Added dir '$dir'"
say >&2 "Added dir '$dir'"
}
# Usage: cmd_split [REV]
cmd_split () {
if test $# -eq 0
then
rev=$(git rev-parse HEAD)
elif test $# -eq 1
then
rev=$(git rev-parse -q --verify "$1^{commit}") ||
die "'$1' does not refer to a commit"
else
die "You must provide exactly one revision. Got: '$*'"
fi
if test -n "$arg_split_rejoin"
then
ensure_clean
fi
debug "Splitting $dir..."
cache_setup || exit $?
if test -n "$onto"
if test -n "$arg_split_onto"
then
debug "Reading history for --onto=$onto..."
git rev-list $onto |
debug "Reading history for --onto=$arg_split_onto..."
git rev-list $arg_split_onto |
while read rev
do
# the 'onto' history is already just the subdir, so
# any parent we find there can be used verbatim
debug " cache: $rev"
debug "cache: $rev"
cache_set "$rev" "$rev"
done
done || exit $?
fi
unrevs="$(find_existing_splits "$dir" "$revs")"
unrevs="$(find_existing_splits "$dir" "$rev")" || exit $?
# 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.
# (and rev-list --follow doesn't seem to solve this)
grl='git rev-list --topo-order --reverse --parents $revs $unrevs'
grl='git rev-list --topo-order --reverse --parents $rev $unrevs'
revmax=$(eval "$grl" | wc -l)
revcount=0
createcount=0
@ -784,52 +901,58 @@ cmd_split () {
eval "$grl" |
while read rev parents
do
process_split_commit "$rev" "$parents" 0
process_split_commit "$rev" "$parents"
done || exit $?
latest_new=$(cache_get latest_new)
latest_new=$(cache_get latest_new) || exit $?
if test -z "$latest_new"
then
die "No new revisions were found"
fi
if test -n "$rejoin"
if test -n "$arg_split_rejoin"
then
debug "Merging split branch into HEAD..."
latest_old=$(cache_get latest_old)
git merge -s ours \
--allow-unrelated-histories \
-m "$(rejoin_msg "$dir" "$latest_old" "$latest_new")" \
"$latest_new" >&2 || exit $?
fi
if test -n "$branch"
then
if rev_exists "refs/heads/$branch"
latest_old=$(cache_get latest_old) || exit $?
arg_addmerge_message="$(rejoin_msg "$dir" "$latest_old" "$latest_new")" || exit $?
if test -z "$(find_latest_squash "$dir")"
then
if ! rev_is_descendant_of_branch "$latest_new" "$branch"
cmd_add "$latest_new" >&2 || exit $?
else
cmd_merge "$latest_new" >&2 || exit $?
fi
fi
if test -n "$arg_split_branch"
then
if rev_exists "refs/heads/$arg_split_branch"
then
if ! git merge-base --is-ancestor "$arg_split_branch" "$latest_new"
then
die "Branch '$branch' is not an ancestor of commit '$latest_new'."
die "Branch '$arg_split_branch' is not an ancestor of commit '$latest_new'."
fi
action='Updated'
else
action='Created'
fi
git update-ref -m 'subtree split' \
"refs/heads/$branch" "$latest_new" || exit $?
say "$action branch '$branch'"
"refs/heads/$arg_split_branch" "$latest_new" || exit $?
say >&2 "$action branch '$arg_split_branch'"
fi
echo "$latest_new"
exit 0
}
# Usage: cmd_merge REV
cmd_merge () {
rev=$(git rev-parse $default --revs-only "$@") || exit $?
ensure_single_rev $rev
test $# -eq 1 ||
die "You must provide exactly one revision. Got: '$*'"
rev=$(git rev-parse -q --verify "$1^{commit}") ||
die "'$1' does not refer to a commit"
ensure_clean
if test -n "$squash"
if test -n "$arg_addmerge_squash"
then
first_split="$(find_latest_squash "$dir")"
first_split="$(find_latest_squash "$dir")" || exit $?
if test -z "$first_split"
then
die "Can't squash-merge: '$dir' was never added."
@ -839,7 +962,7 @@ cmd_merge () {
sub=$2
if test "$sub" = "$rev"
then
say "Subtree is already at commit $rev."
say >&2 "Subtree is already at commit $rev."
exit 0
fi
new=$(new_squash_commit "$old" "$sub" "$rev") || exit $?
@ -847,26 +970,16 @@ cmd_merge () {
rev="$new"
fi
version=$(git version)
if test "$version" \< "git version 1.7"
if test -n "$arg_addmerge_message"
then
if test -n "$message"
then
git merge -s subtree --message="$message" "$rev"
else
git merge -s subtree "$rev"
fi
git merge -Xsubtree="$arg_prefix" \
--message="$arg_addmerge_message" "$rev"
else
if test -n "$message"
then
git merge -Xsubtree="$prefix" \
--message="$message" "$rev"
else
git merge -Xsubtree="$prefix" $rev
fi
git merge -Xsubtree="$arg_prefix" $rev
fi
}
# Usage: cmd_pull REPOSITORY REMOTEREF
cmd_pull () {
if test $# -ne 2
then
@ -875,27 +988,36 @@ cmd_pull () {
ensure_clean
ensure_valid_ref_format "$2"
git fetch "$@" || exit $?
revs=FETCH_HEAD
set -- $revs
cmd_merge "$@"
cmd_merge FETCH_HEAD
}
# Usage: cmd_push REPOSITORY [+][LOCALREV:]REMOTEREF
cmd_push () {
if test $# -ne 2
then
die "You must provide <repository> <ref>"
die "You must provide <repository> <refspec>"
fi
ensure_valid_ref_format "$2"
if test -e "$dir"
then
repository=$1
refspec=$2
refspec=${2#+}
remoteref=${refspec#*:}
if test "$remoteref" = "$refspec"
then
localrevname_presplit=HEAD
else
localrevname_presplit=${refspec%%:*}
fi
ensure_valid_ref_format "$remoteref"
localrev_presplit=$(git rev-parse -q --verify "$localrevname_presplit^{commit}") ||
die "'$localrevname_presplit' does not refer to a commit"
echo "git push using: " "$repository" "$refspec"
localrev=$(git subtree split --prefix="$prefix") || die
git push "$repository" "$localrev":"refs/heads/$refspec"
localrev=$(cmd_split "$localrev_presplit") || die
git push "$repository" "$localrev":"refs/heads/$remoteref"
else
die "'$dir' must already exist. Try 'git subtree add'."
fi
}
"cmd_$command" "$@"
main "$@"

View File

@ -9,13 +9,14 @@ git-subtree - Merge subtrees together and split repository into subtrees
SYNOPSIS
--------
[verse]
'git subtree' add -P <prefix> <commit>
'git subtree' add -P <prefix> <repository> <ref>
'git subtree' pull -P <prefix> <repository> <ref>
'git subtree' push -P <prefix> <repository> <ref>
'git subtree' merge -P <prefix> <commit>
'git subtree' split -P <prefix> [OPTIONS] [<commit>]
'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> split [<local-commit>]
[verse]
'git subtree' [<options>] -P <prefix> pull <repository> <remote-ref>
'git subtree' [<options>] -P <prefix> push <repository> <refspec>
DESCRIPTION
-----------
@ -28,7 +29,7 @@ as a subdirectory of your application.
Subtrees are not to be confused with submodules, which are meant for
the same task. Unlike submodules, subtrees do not need any special
constructions (like .gitmodules files or gitlinks) be present in
constructions (like '.gitmodules' files or gitlinks) be present in
your repository, and do not force end-users of your
repository to do anything special or to understand how subtrees
work. A subtree is just a subdirectory that can be
@ -59,70 +60,71 @@ project as much as possible. That is, if you make a change that
affects both the library and the main application, commit it in
two pieces. That way, when you split the library commits out
later, their descriptions will still make sense. But if this
isn't important to you, it's not *necessary*. git subtree will
isn't important to you, it's not *necessary*. 'git subtree' will
simply leave out the non-library-related parts of the commit
when it splits it out into the subproject later.
COMMANDS
--------
add::
add <local-commit>::
add <repository> <remote-ref>::
Create the <prefix> subtree by importing its contents
from the given <commit> or <repository> and remote <ref>.
from the given <local-commit> or <repository> and <remote-ref>.
A new commit is created automatically, joining the imported
project's history with your own. With '--squash', imports
project's history with your own. With '--squash', import
only a single commit from the subproject, rather than its
entire history.
merge::
Merge recent changes up to <commit> into the <prefix>
merge <local-commit>::
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
changes into the latest <commit>. With '--squash',
creates only one commit that contains all the changes,
changes into the latest <local-commit>. With '--squash',
create only one commit that contains all the changes,
rather than merging in the entire history.
+
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.
pull::
Exactly like 'merge', but parallels 'git pull' in that
it fetches the given ref from the specified remote
repository.
push::
Does a 'split' (see below) using the <prefix> supplied
and then does a 'git push' to push the result to the
repository and ref. This can be used to push your
subtree to different branches of the remote repository.
split::
split [<local-commit>]::
Extract a new, synthetic project history from the
history of the <prefix> subtree. The new history
history of the <prefix> subtree of <local-commit>, or of
HEAD if no <local-commit> is given. The new history
includes only the commits (including merges) that
affected <prefix>, and each of those commits now has the
contents of <prefix> at the root of the project instead
of in a subdirectory. Thus, the newly created history
is suitable for export as a separate git repository.
+
After splitting successfully, a single commit id is printed to stdout.
After splitting successfully, a single commit ID is printed to stdout.
This corresponds to the HEAD of the newly created tree, which you can
manipulate however you want.
+
Repeated splits of exactly the same history are guaranteed to be
identical (i.e. to produce the same commit ids). 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 generated last time, so 'git merge'
and friends will work as expected.
+
Note that if you use '--squash' when you merge, you should usually not
just '--rejoin' when you split.
identical (i.e. to produce the same commit IDs) as long as the
settings passed to 'split' (such as '--annotate') are the same.
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
generated last time, so 'git merge' and friends will work as expected.
pull <repository> <remote-ref>::
Exactly like 'merge', but parallels 'git pull' in that
it fetches the given ref from the specified remote
repository.
OPTIONS
-------
push <repository> [+][<local-commit>:]<remote-ref>::
Does a 'split' using the <prefix> subtree of <local-commit>
and then does a 'git push' to push the result to the
<repository> and <remote-ref>. This can be used to push your
subtree to different branches of the remote repository. Just
as with 'split', if no <local-commit> is given, then HEAD is
used. The optional leading '+' is ignored.
OPTIONS FOR ALL COMMANDS
------------------------
-q::
--quiet::
Suppress unnecessary output messages on stderr.
@ -137,21 +139,16 @@ OPTIONS
want to manipulate. This option is mandatory
for all commands.
-m <message>::
--message=<message>::
This option is only valid for add, merge, pull, and split --rejoin.
Specify <message> as the commit message for the merge commit.
OPTIONS FOR 'add' AND 'merge' (ALSO: 'pull', 'split --rejoin', AND 'push --rejoin')
-----------------------------------------------------------------------------------
These options for 'add' and 'merge' may also be given to 'pull' (which
wraps 'merge'), 'split --rejoin' (which wraps either 'add' or 'merge'
as appropriate), and 'push --rejoin' (which wraps 'split --rejoin').
OPTIONS FOR add, merge, and pull
--------------------------------
--squash::
This option is only valid for add, merge, and pull
commands.
+
Instead of merging the entire history from the subtree project, produce
only a single commit that contains all the differences you want to
merge, and then merge that new commit into your project.
Instead of merging the entire history from the subtree project, produce
only a single commit that contains all the differences you want to
merge, and then merge that new commit into your project.
+
Using this option helps to reduce log clutter. People rarely want to see
every change that happened between v1.0 and v1.1 of the library they're
@ -174,57 +171,53 @@ Whether or not you use '--squash', changes made in your local repository
remain intact and can be later split and send upstream to the
subproject.
-m <message>::
--message=<message>::
Specify <message> as the commit message for the merge commit.
OPTIONS FOR 'split' (ALSO: 'push')
----------------------------------
These options for 'split' may also be given to 'push' (which wraps
'split').
OPTIONS FOR split
-----------------
--annotate=<annotation>::
This option is only valid for the split command.
+
When generating synthetic history, add <annotation> as a prefix to each
commit message. Since we're creating new commits with the same commit
message, but possibly different content, from the original commits, this
can help to differentiate them and avoid confusion.
When generating synthetic history, add <annotation> as a prefix to each
commit message. Since we're creating new commits with the same commit
message, but possibly different content, from the original commits, this
can help to differentiate them and avoid confusion.
+
Whenever you split, you need to use the same <annotation>, or else you
don't have a guarantee that the new re-created history will be identical
to the old one. That will prevent merging from working correctly. git
subtree tries to make it work anyway, particularly if you use --rejoin,
subtree tries to make it work anyway, particularly if you use '--rejoin',
but it may not always be effective.
-b <branch>::
--branch=<branch>::
This option is only valid for the split command.
+
After generating the synthetic history, create a new branch called
<branch> that contains the new history. This is suitable for immediate
pushing upstream. <branch> must not already exist.
After generating the synthetic history, create a new branch called
<branch> that contains the new history. This is suitable for immediate
pushing upstream. <branch> must not already exist.
--ignore-joins::
This option is only valid for the split command.
+
If you use '--rejoin', git subtree attempts to optimize its history
reconstruction to generate only the new commits since the last
'--rejoin'. '--ignore-join' disables this behaviour, forcing it to
regenerate the entire history. In a large project, this can take a long
time.
If you use '--rejoin', git subtree attempts to optimize its history
reconstruction to generate only the new commits since the last
'--rejoin'. '--ignore-joins' disables this behavior, forcing it to
regenerate the entire history. In a large project, this can take a long
time.
--onto=<onto>::
This option is only valid for the split command.
+
If your subtree was originally imported using something other than git
subtree, its history may not match what git subtree is expecting. In
that case, you can specify the commit id <onto> that corresponds to the
first revision of the subproject's history that was imported into your
project, and git subtree will attempt to build its history from there.
If your subtree was originally imported using something other than git
subtree, its history may not match what git subtree is expecting. In
that case, you can specify the commit ID <onto> that corresponds to the
first revision of the subproject's history that was imported into your
project, and git subtree will attempt to build its history from there.
+
If you used 'git subtree add', you should never need this option.
--rejoin::
This option is only valid for the split command.
+
After splitting, merge the newly created synthetic history back into
your main project. That way, future splits can search only the part of
history that has been added since the most recent --rejoin.
After splitting, merge the newly created synthetic history back into
your main project. That way, future splits can search only the part of
history that has been added since the most recent '--rejoin'.
+
If your split commits end up merged into the upstream subproject, and
then you want to get the latest upstream version, this will allow git's
@ -235,13 +228,12 @@ Unfortunately, using this option results in 'git log' showing an extra
copy of every new commit that was created (the original, and the
synthetic one).
+
If you do all your merges with '--squash', don't use '--rejoin' when you
split, because you don't want the subproject's history to be part of
your project anyway.
If you do all your merges with '--squash', make sure you also use
'--squash' when you 'split --rejoin'.
EXAMPLE 1. Add command
----------------------
EXAMPLE 1. 'add' command
------------------------
Let's assume that you have a local repository that you would like
to add an external vendor library to. In this case we will add the
git-subtree repository as a subdirectory of your already existing
@ -253,15 +245,15 @@ git-extensions repository in ~/git-extensions/:
'master' needs to be a valid remote ref and can be a different branch
name
You can omit the --squash flag, but doing so will increase the number
You can omit the '--squash' flag, but doing so will increase the number
of commits that are included in your local repository.
We now have a ~/git-extensions/git-subtree directory containing code
from the master branch of git://github.com/apenwarr/git-subtree.git
in our git-extensions repository.
EXAMPLE 2. Extract a subtree using commit, merge and pull
---------------------------------------------------------
EXAMPLE 2. Extract a subtree using 'commit', 'merge' and 'pull'
---------------------------------------------------------------
Let's use the repository for the git source code as an example.
First, get your own copy of the git.git repository:
@ -269,7 +261,7 @@ First, get your own copy of the git.git repository:
$ cd test-git
gitweb (commit 1130ef3) was merged into git as of commit
0a8f4f0, after which it was no longer maintained separately.
0a8f4f0, after which it was no longer maintained separately.
But imagine it had been maintained separately, and we wanted to
extract git's changes to gitweb since that time, to share with
the upstream. You could do this:
@ -279,14 +271,14 @@ the upstream. You could do this:
--branch gitweb-latest
$ gitk gitweb-latest
$ git push git@github.com:whatever/gitweb.git gitweb-latest:master
(We use '0a8f4f0^..' because that means "all the changes from
0a8f4f0 to the current version, including 0a8f4f0 itself.")
If gitweb had originally been merged using 'git subtree add' (or
a previous split had already been done with --rejoin specified)
a previous split had already been done with '--rejoin' specified)
then you can do all your splits without having to remember any
weird commit ids:
weird commit IDs:
$ git subtree split --prefix=gitweb --annotate='(split) ' --rejoin \
--branch gitweb-latest2
@ -313,7 +305,7 @@ And fast forward again:
$ git subtree merge --prefix=gitweb --squash gitweb-latest
And notice that your change is still intact:
$ ls -l gitweb/myfile
And you can split it out and look at your changes versus
@ -321,8 +313,8 @@ the standard gitweb:
git log gitweb-latest..$(git subtree split --prefix=gitweb)
EXAMPLE 3. Extract a subtree using branch
-----------------------------------------
EXAMPLE 3. Extract a subtree using a branch
-------------------------------------------
Suppose you have a source directory with many files and
subdirectories, and you want to extract the lib directory to its own
git project. Here's a short way to do it:

View File

@ -5,72 +5,24 @@
#
test_description='Basic porcelain support for subtrees
This test verifies the basic operation of the add, pull, merge
and split subcommands of git subtree.
This test verifies the basic operation of the add, merge, split, pull,
and push subcommands of git subtree.
'
TEST_DIRECTORY=$(pwd)/../../../t
export TEST_DIRECTORY
. "$TEST_DIRECTORY"/test-lib.sh
. ../../../t/test-lib.sh
subtree_test_create_repo()
{
# Use our own wrapper around test-lib.sh's test_create_repo, in order
# to set log.date=relative. `git subtree` parses the output of `git
# log`, and so it must be careful to not be affected by settings that
# change the `git log` output. We test this by setting
# log.date=relative for every repo in the tests.
subtree_test_create_repo () {
test_create_repo "$1" &&
(
cd "$1" &&
git config log.date relative
)
git -C "$1" config log.date relative
}
create()
{
echo "$1" >"$1" &&
git add "$1"
}
check_equal()
{
test_debug 'echo'
test_debug "echo \"check a:\" \"{$1}\""
test_debug "echo \" b:\" \"{$2}\""
if [ "$1" = "$2" ]; then
return 0
else
return 1
fi
}
undo()
{
git reset --hard HEAD~
}
# Make sure no patch changes more than one file.
# The original set of commits changed only one file each.
# A multi-file change would imply that we pruned commits
# too aggressively.
join_commits()
{
commit=
all=
while read x y; do
if [ -z "$x" ]; then
continue
elif [ "$x" = "commit:" ]; then
if [ -n "$commit" ]; then
echo "$commit $all"
all=
fi
commit="$y"
else
all="$all $y"
fi
done
echo "$commit $all"
}
test_create_commit() (
test_create_commit () (
repo=$1 &&
commit=$2 &&
cd "$repo" &&
@ -81,98 +33,116 @@ test_create_commit() (
git commit -m "$commit" || error "Could not commit"
)
last_commit_message()
{
test_wrong_flag() {
test_must_fail "$@" >out 2>err &&
test_must_be_empty out &&
grep "flag does not make sense with" err
}
last_commit_subject () {
git log --pretty=format:%s -1
}
subtree_test_count=0
next_test() {
subtree_test_count=$(($subtree_test_count+1))
}
test_expect_success 'shows short help text for -h' '
test_expect_code 129 git subtree -h >out 2>err &&
test_must_be_empty err &&
grep -e "^ *or: git subtree pull" out &&
grep -e --annotate out
'
#
# Tests for 'git subtree add'
#
next_test
test_expect_success 'no merge from non-existent subtree' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
test_must_fail git subtree merge --prefix="sub dir" FETCH_HEAD
)
'
next_test
test_expect_success 'no pull from non-existent subtree' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" master
)'
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" HEAD
)
'
test_expect_success 'add rejects flags for split' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
test_wrong_flag git subtree add --prefix="sub dir" --annotate=foo FETCH_HEAD &&
test_wrong_flag git subtree add --prefix="sub dir" --branch=foo FETCH_HEAD &&
test_wrong_flag git subtree add --prefix="sub dir" --ignore-joins FETCH_HEAD &&
test_wrong_flag git subtree add --prefix="sub dir" --onto=foo FETCH_HEAD &&
test_wrong_flag git subtree add --prefix="sub dir" --rejoin FETCH_HEAD
)
'
next_test
test_expect_success 'add subproj as subtree into sub dir/ with --prefix' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD &&
check_equal "$(last_commit_message)" "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''"
test "$(last_commit_subject)" = "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''"
)
'
next_test
test_expect_success 'add subproj as subtree into sub dir/ with --prefix and --message' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" --message="Added subproject" FETCH_HEAD &&
check_equal "$(last_commit_message)" "Added subproject"
test "$(last_commit_subject)" = "Added subproject"
)
'
next_test
test_expect_success 'add subproj as subtree into sub dir/ with --prefix as -P and --message as -m' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add -P "sub dir" -m "Added subproject" FETCH_HEAD &&
check_equal "$(last_commit_message)" "Added subproject"
test "$(last_commit_subject)" = "Added subproject"
)
'
next_test
test_expect_success 'add subproj as subtree into sub dir/ with --squash and --prefix and --message' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" --message="Added subproject with squash" --squash FETCH_HEAD &&
check_equal "$(last_commit_message)" "Added subproject with squash"
test "$(last_commit_subject)" = "Added subproject with squash"
)
'
@ -180,100 +150,117 @@ test_expect_success 'add subproj as subtree into sub dir/ with --squash and --pr
# Tests for 'git subtree merge'
#
next_test
test_expect_success 'merge rejects flags for split' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
test_wrong_flag git subtree merge --prefix="sub dir" --annotate=foo FETCH_HEAD &&
test_wrong_flag git subtree merge --prefix="sub dir" --branch=foo FETCH_HEAD &&
test_wrong_flag git subtree merge --prefix="sub dir" --ignore-joins FETCH_HEAD &&
test_wrong_flag git subtree merge --prefix="sub dir" --onto=foo FETCH_HEAD &&
test_wrong_flag git subtree merge --prefix="sub dir" --rejoin FETCH_HEAD
)
'
test_expect_success 'merge new subproj history into sub dir/ with --prefix' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
check_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
test "$(last_commit_subject)" = "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
)
'
next_test
test_expect_success 'merge new subproj history into sub dir/ with --prefix and --message' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" --message="Merged changes from subproject" FETCH_HEAD &&
check_equal "$(last_commit_message)" "Merged changes from subproject"
test "$(last_commit_subject)" = "Merged changes from subproject"
)
'
next_test
test_expect_success 'merge new subproj history into sub dir/ with --squash and --prefix and --message' '
subtree_test_create_repo "$subtree_test_count/sub proj" &&
subtree_test_create_repo "$subtree_test_count" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count/sub proj" &&
subtree_test_create_repo "$test_count" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" --message="Merged changes from subproject using squash" --squash FETCH_HEAD &&
check_equal "$(last_commit_message)" "Merged changes from subproject using squash"
test "$(last_commit_subject)" = "Merged changes from subproject using squash"
)
'
next_test
test_expect_success 'merge the added subproj again, should do nothing' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD &&
# this shouldn not actually do anything, since FETCH_HEAD
# is already a parent
result=$(git merge -s ours -m "merge -s -ours" FETCH_HEAD) &&
check_equal "${result}" "Already up to date."
test "${result}" = "Already up to date."
)
'
next_test
test_expect_success 'merge new subproj history into subdir/ with a slash appended to the argument of --prefix' '
test_create_repo "$test_count" &&
test_create_repo "$test_count/subproj" &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/subproj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/subproj" sub1 &&
(
cd "$test_count" &&
git fetch ./subproj master &&
git fetch ./subproj HEAD &&
git subtree add --prefix=subdir/ FETCH_HEAD
) &&
test_create_commit "$test_count/subproj" sub2 &&
(
cd "$test_count" &&
git fetch ./subproj master &&
git fetch ./subproj HEAD &&
git subtree merge --prefix=subdir/ FETCH_HEAD &&
check_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
test "$(last_commit_subject)" = "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
)
'
@ -281,18 +268,17 @@ test_expect_success 'merge new subproj history into subdir/ with a slash appende
# Tests for 'git subtree split'
#
next_test
test_expect_success 'split requires option --prefix' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD &&
echo "You must provide the --prefix option." > expected &&
test_must_fail git subtree split > actual 2>&1 &&
echo "You must provide the --prefix option." >expected &&
test_must_fail git subtree split >actual 2>&1 &&
test_debug "printf '"expected: "'" &&
test_debug "cat expected" &&
test_debug "printf '"actual: "'" &&
@ -301,18 +287,17 @@ test_expect_success 'split requires option --prefix' '
)
'
next_test
test_expect_success 'split requires path given by option --prefix must exist' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD &&
echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" > expected &&
test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
echo "'\''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_debug "printf '"expected: "'" &&
test_debug "cat expected" &&
test_debug "printf '"actual: "'" &&
@ -321,222 +306,696 @@ test_expect_success 'split requires path given by option --prefix must exist' '
)
'
next_test
test_expect_success 'split sub dir/ with --rejoin' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
test_expect_success 'split rejects flags for add' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
test_wrong_flag git subtree split --prefix="sub dir" --squash &&
test_wrong_flag git subtree split --prefix="sub dir" --message=foo
)
'
test_expect_success 'split sub dir/ with --rejoin' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree split --prefix="sub dir" --annotate="*" --rejoin &&
check_equal "$(last_commit_message)" "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
)
'
'
next_test
test_expect_success 'split sub dir/ with --rejoin from scratch' '
subtree_test_create_repo "$subtree_test_count" &&
test_create_commit "$subtree_test_count" main1 &&
subtree_test_create_repo "$test_count" &&
test_create_commit "$test_count" main1 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
mkdir "sub dir" &&
echo file >"sub dir"/file &&
git add "sub dir/file" &&
git commit -m"sub dir file" &&
split_hash=$(git subtree split --prefix="sub dir" --rejoin) &&
git subtree split --prefix="sub dir" --rejoin &&
check_equal "$(last_commit_message)" "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
)
'
'
next_test
test_expect_success 'split sub dir/ with --rejoin and --message' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
git subtree split --prefix="sub dir" --message="Split & rejoin" --annotate="*" --rejoin &&
check_equal "$(last_commit_message)" "Split & rejoin"
test "$(last_commit_subject)" = "Split & rejoin"
)
'
next_test
test_expect_success 'split "sub dir"/ with --rejoin and --squash' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" --squash FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git subtree pull --prefix="sub dir" --squash ./"sub proj" HEAD &&
MAIN=$(git rev-parse --verify HEAD) &&
SUB=$(git -C "sub proj" rev-parse --verify HEAD) &&
SPLIT=$(git subtree split --prefix="sub dir" --annotate="*" --rejoin --squash) &&
test_must_fail git merge-base --is-ancestor $SUB HEAD &&
test_must_fail git merge-base --is-ancestor $SPLIT HEAD &&
git rev-list HEAD ^$MAIN >commit-list &&
test_line_count = 2 commit-list &&
test "$(git rev-parse --verify HEAD:)" = "$(git rev-parse --verify $MAIN:)" &&
test "$(git rev-parse --verify HEAD:"sub dir")" = "$(git rev-parse --verify $SPLIT:)" &&
test "$(git rev-parse --verify HEAD^1)" = $MAIN &&
test "$(git rev-parse --verify HEAD^2)" != $SPLIT &&
test "$(git rev-parse --verify HEAD^2:)" = "$(git rev-parse --verify $SPLIT:)" &&
test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$SPLIT'\''"
)
'
test_expect_success 'split then pull "sub dir"/ with --rejoin and --squash' '
# 1. "add"
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
git -C "$test_count" subtree --prefix="sub dir" add --squash ./"sub proj" HEAD &&
# 2. commit from parent
test_create_commit "$test_count" "sub dir"/main-sub1 &&
# 3. "split --rejoin --squash"
git -C "$test_count" subtree --prefix="sub dir" split --rejoin --squash &&
# 4. "pull --squash"
test_create_commit "$test_count/sub proj" sub2 &&
git -C "$test_count" subtree -d --prefix="sub dir" pull --squash ./"sub proj" HEAD &&
test_must_fail git merge-base HEAD FETCH_HEAD
'
test_expect_success 'split "sub dir"/ with --branch' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br &&
check_equal "$(git rev-parse subproj-br)" "$split_hash"
test "$(git rev-parse subproj-br)" = "$split_hash"
)
'
next_test
test_expect_success 'check hash of split' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br &&
check_equal "$(git rev-parse subproj-br)" "$split_hash" &&
test "$(git rev-parse subproj-br)" = "$split_hash" &&
# Check hash of split
new_hash=$(git rev-parse subproj-br^2) &&
(
cd ./"sub proj" &&
subdir_hash=$(git rev-parse HEAD) &&
check_equal ''"$new_hash"'' "$subdir_hash"
test "$new_hash" = "$subdir_hash"
)
)
'
next_test
test_expect_success 'split "sub dir"/ with --branch for an existing branch' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git branch subproj-br FETCH_HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br &&
check_equal "$(git rev-parse subproj-br)" "$split_hash"
test "$(git rev-parse subproj-br)" = "$split_hash"
)
'
next_test
test_expect_success 'split "sub dir"/ with --branch for an incompatible branch' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git branch init HEAD &&
git fetch ./"sub proj" master &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
test_must_fail git subtree split --prefix="sub dir" --branch init
)
'
#
# Tests for 'git subtree pull'
#
test_expect_success 'pull requires option --prefix' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$test_count" &&
test_must_fail git subtree pull ./"sub proj" HEAD >out 2>err &&
echo "You must provide the --prefix option." >expected &&
test_must_be_empty out &&
test_cmp expected err
)
'
test_expect_success 'pull requires path given by option --prefix must exist' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
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 &&
test_must_be_empty out &&
test_cmp expected err
)
'
test_expect_success 'pull basic operation' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$test_count" &&
exp=$(git -C "sub proj" rev-parse --verify HEAD:) &&
git subtree pull --prefix="sub dir" ./"sub proj" HEAD &&
act=$(git rev-parse --verify HEAD:"sub dir") &&
test "$act" = "$exp"
)
'
test_expect_success 'pull rejects flags for split' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count/sub proj" sub2 &&
(
test_must_fail git subtree pull --prefix="sub dir" --annotate=foo ./"sub proj" HEAD &&
test_must_fail git subtree pull --prefix="sub dir" --branch=foo ./"sub proj" HEAD &&
test_must_fail git subtree pull --prefix="sub dir" --ignore-joins ./"sub proj" HEAD &&
test_must_fail git subtree pull --prefix="sub dir" --onto=foo ./"sub proj" HEAD &&
test_must_fail git subtree pull --prefix="sub dir" --rejoin ./"sub proj" HEAD
)
'
#
# Tests for 'git subtree push'
#
test_expect_success 'push requires option --prefix' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD &&
echo "You must provide the --prefix option." >expected &&
test_must_fail git subtree push "./sub proj" from-mainline >actual 2>&1 &&
test_debug "printf '"expected: "'" &&
test_debug "cat expected" &&
test_debug "printf '"actual: "'" &&
test_debug "cat actual" &&
test_cmp expected actual
)
'
test_expect_success 'push requires path given by option --prefix must exist' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD &&
echo "'\''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_debug "printf '"expected: "'" &&
test_debug "cat expected" &&
test_debug "printf '"actual: "'" &&
test_debug "cat actual" &&
test_cmp expected actual
)
'
test_expect_success 'push rejects flags for add' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
test_wrong_flag git subtree split --prefix="sub dir" --squash ./"sub proj" from-mainline &&
test_wrong_flag git subtree split --prefix="sub dir" --message=foo ./"sub proj" from-mainline
)
'
test_expect_success 'push basic operation' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
before=$(git rev-parse --verify HEAD) &&
split_hash=$(git subtree split --prefix="sub dir") &&
git subtree push --prefix="sub dir" ./"sub proj" from-mainline &&
test "$before" = "$(git rev-parse --verify HEAD)" &&
test "$split_hash" = "$(git -C "sub proj" rev-parse --verify refs/heads/from-mainline)"
)
'
test_expect_success 'push sub dir/ with --rejoin' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree push --prefix="sub dir" --annotate="*" --rejoin ./"sub proj" from-mainline &&
test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$split_hash'\''" &&
test "$split_hash" = "$(git -C "sub proj" rev-parse --verify refs/heads/from-mainline)"
)
'
test_expect_success 'push sub dir/ with --rejoin from scratch' '
subtree_test_create_repo "$test_count" &&
test_create_commit "$test_count" main1 &&
(
cd "$test_count" &&
mkdir "sub dir" &&
echo file >"sub dir"/file &&
git add "sub dir/file" &&
git commit -m"sub dir file" &&
split_hash=$(git subtree split --prefix="sub dir" --rejoin) &&
git init --bare "sub proj.git" &&
git subtree push --prefix="sub dir" --rejoin ./"sub proj.git" from-mainline &&
test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$split_hash'\''" &&
test "$split_hash" = "$(git -C "sub proj.git" rev-parse --verify refs/heads/from-mainline)"
)
'
test_expect_success 'push sub dir/ with --rejoin and --message' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
git subtree push --prefix="sub dir" --message="Split & rejoin" --annotate="*" --rejoin ./"sub proj" from-mainline &&
test "$(last_commit_subject)" = "Split & rejoin" &&
split_hash="$(git rev-parse --verify HEAD^2)" &&
test "$split_hash" = "$(git -C "sub proj" rev-parse --verify refs/heads/from-mainline)"
)
'
test_expect_success 'push "sub dir"/ with --rejoin and --squash' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" --squash FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git subtree pull --prefix="sub dir" --squash ./"sub proj" HEAD &&
MAIN=$(git rev-parse --verify HEAD) &&
SUB=$(git -C "sub proj" rev-parse --verify HEAD) &&
SPLIT=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree push --prefix="sub dir" --annotate="*" --rejoin --squash ./"sub proj" from-mainline &&
test_must_fail git merge-base --is-ancestor $SUB HEAD &&
test_must_fail git merge-base --is-ancestor $SPLIT HEAD &&
git rev-list HEAD ^$MAIN >commit-list &&
test_line_count = 2 commit-list &&
test "$(git rev-parse --verify HEAD:)" = "$(git rev-parse --verify $MAIN:)" &&
test "$(git rev-parse --verify HEAD:"sub dir")" = "$(git rev-parse --verify $SPLIT:)" &&
test "$(git rev-parse --verify HEAD^1)" = $MAIN &&
test "$(git rev-parse --verify HEAD^2)" != $SPLIT &&
test "$(git rev-parse --verify HEAD^2:)" = "$(git rev-parse --verify $SPLIT:)" &&
test "$(last_commit_subject)" = "Split '\''sub dir/'\'' into commit '\''$SPLIT'\''" &&
test "$SPLIT" = "$(git -C "sub proj" rev-parse --verify refs/heads/from-mainline)"
)
'
test_expect_success 'push "sub dir"/ with --branch' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree push --prefix="sub dir" --annotate="*" --branch subproj-br ./"sub proj" from-mainline &&
test "$(git rev-parse subproj-br)" = "$split_hash" &&
test "$split_hash" = "$(git -C "sub proj" rev-parse --verify refs/heads/from-mainline)"
)
'
test_expect_success 'check hash of push' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree push --prefix="sub dir" --annotate="*" --branch subproj-br ./"sub proj" from-mainline &&
test "$(git rev-parse subproj-br)" = "$split_hash" &&
# Check hash of split
new_hash=$(git rev-parse subproj-br^2) &&
(
cd ./"sub proj" &&
subdir_hash=$(git rev-parse HEAD) &&
test "$new_hash" = "$subdir_hash"
) &&
test "$split_hash" = "$(git -C "sub proj" rev-parse --verify refs/heads/from-mainline)"
)
'
test_expect_success 'push "sub dir"/ with --branch for an existing branch' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git branch subproj-br FETCH_HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
git subtree push --prefix="sub dir" --annotate="*" --branch subproj-br ./"sub proj" from-mainline &&
test "$(git rev-parse subproj-br)" = "$split_hash" &&
test "$split_hash" = "$(git -C "sub proj" rev-parse --verify refs/heads/from-mainline)"
)
'
test_expect_success 'push "sub dir"/ with --branch for an incompatible branch' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git branch init HEAD &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
test_must_fail git subtree push --prefix="sub dir" --branch init "./sub proj" from-mainline
)
'
test_expect_success 'push "sub dir"/ with a local rev' '
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$test_count" &&
bad_tree=$(git rev-parse --verify HEAD:"sub dir") &&
good_tree=$(git rev-parse --verify HEAD^:"sub dir") &&
git subtree push --prefix="sub dir" --annotate="*" ./"sub proj" HEAD^:from-mainline &&
split_tree=$(git -C "sub proj" rev-parse --verify refs/heads/from-mainline:) &&
test "$split_tree" = "$good_tree"
)
'
#
# Validity checking
#
next_test
test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count/sub proj" sub3 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
test_create_commit "$test_count/sub proj" sub3 &&
test_create_commit "$test_count" "sub dir"/main-sub3 &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub4 &&
test_create_commit "$test_count/sub proj" sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
test_create_commit "$test_count" "sub dir"/main-sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD &&
@ -547,46 +1006,45 @@ test_expect_success 'make sure exactly the right set of files ends up in the sub
)
'
next_test
test_expect_success 'make sure the subproj *only* contains commits that affect the "sub dir"' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count/sub proj" sub3 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
test_create_commit "$test_count/sub proj" sub3 &&
test_create_commit "$test_count" "sub dir"/main-sub3 &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub4 &&
test_create_commit "$test_count/sub proj" sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
test_create_commit "$test_count" "sub dir"/main-sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD &&
@ -598,52 +1056,51 @@ test_expect_success 'make sure the subproj *only* contains commits that affect t
)
'
next_test
test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count/sub proj" sub3 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
test_create_commit "$test_count/sub proj" sub3 &&
test_create_commit "$test_count" "sub dir"/main-sub3 &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub4 &&
test_create_commit "$test_count/sub proj" sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
test_create_commit "$test_count" "sub dir"/main-sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
(
cd "$subtree_test_count" &&
git subtree pull --prefix="sub dir" ./"sub proj" master &&
cd "$test_count" &&
git subtree pull --prefix="sub dir" ./"sub proj" HEAD &&
test_write_lines main1 main2 >chkm &&
test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 >chkms &&
@ -657,53 +1114,52 @@ test_expect_success 'make sure exactly the right set of files ends up in the mai
)
'
next_test
test_expect_success 'make sure each filename changed exactly once in the entire history' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git config log.date relative &&
git fetch ./"sub proj" master &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count/sub proj" sub3 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
test_create_commit "$test_count/sub proj" sub3 &&
test_create_commit "$test_count" "sub dir"/main-sub3 &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub4 &&
test_create_commit "$test_count/sub proj" sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
test_create_commit "$test_count" "sub dir"/main-sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
(
cd "$subtree_test_count" &&
git subtree pull --prefix="sub dir" ./"sub proj" master &&
cd "$test_count" &&
git subtree pull --prefix="sub dir" ./"sub proj" HEAD &&
test_write_lines main1 main2 >chkm &&
test_write_lines sub1 sub2 sub3 sub4 >chks &&
@ -723,105 +1179,103 @@ test_expect_success 'make sure each filename changed exactly once in the entire
)
'
next_test
test_expect_success 'make sure the --rejoin commits never make it into subproj' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count/sub proj" sub3 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
test_create_commit "$test_count/sub proj" sub3 &&
test_create_commit "$test_count" "sub dir"/main-sub3 &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub4 &&
test_create_commit "$test_count/sub proj" sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
test_create_commit "$test_count" "sub dir"/main-sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
(
cd "$subtree_test_count" &&
git subtree pull --prefix="sub dir" ./"sub proj" master &&
check_equal "$(git log --pretty=format:"%s" HEAD^2 | grep -i split)" ""
cd "$test_count" &&
git subtree pull --prefix="sub dir" ./"sub proj" HEAD &&
test "$(git log --pretty=format:"%s" HEAD^2 | grep -i split)" = ""
)
'
next_test
test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count/sub proj" sub3 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
test_create_commit "$test_count/sub proj" sub3 &&
test_create_commit "$test_count" "sub dir"/main-sub3 &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub4 &&
test_create_commit "$test_count/sub proj" sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
test_create_commit "$test_count" "sub dir"/main-sub4 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
) &&
(
cd "$subtree_test_count/sub proj" &&
cd "$test_count/sub proj" &&
git fetch .. subproj-br &&
git merge FETCH_HEAD
) &&
(
cd "$subtree_test_count" &&
git subtree pull --prefix="sub dir" ./"sub proj" master &&
cd "$test_count" &&
git subtree pull --prefix="sub dir" ./"sub proj" HEAD &&
# They are meaningless to subproj since one side of the merge refers to the mainline
check_equal "$(git log --pretty=format:"%s%n%b" HEAD^2 | grep "git-subtree.*:")" ""
test "$(git log --pretty=format:"%s%n%b" HEAD^2 | grep "git-subtree.*:")" = ""
)
'
@ -829,145 +1283,135 @@ test_expect_success 'make sure no "git subtree" tagged commits make it into subp
# A new set of tests
#
next_test
test_expect_success 'make sure "git subtree split" find the correct parent' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git branch subproj-ref FETCH_HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --branch subproj-br &&
# at this point, the new commit parent should be subproj-ref, if it is
# not, something went wrong (the "newparent" of "master~" commit should
# not, something went wrong (the "newparent" of "HEAD~" commit should
# have been sub2, but it was not, because its cache was not set to
# itself)
check_equal "$(git log --pretty=format:%P -1 subproj-br)" "$(git rev-parse subproj-ref)"
test "$(git log --pretty=format:%P -1 subproj-br)" = "$(git rev-parse subproj-ref)"
)
'
next_test
test_expect_success 'split a new subtree without --onto option' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --branch subproj-br
) &&
mkdir "$subtree_test_count"/"sub dir2" &&
test_create_commit "$subtree_test_count" "sub dir2"/main-sub2 &&
mkdir "$test_count"/"sub dir2" &&
test_create_commit "$test_count" "sub dir2"/main-sub2 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
# also test that we still can split out an entirely new subtree
# if the parent of the first commit in the tree is not empty,
# then the new subtree has accidentally been attached to something
git subtree split --prefix="sub dir2" --branch subproj2-br &&
check_equal "$(git log --pretty=format:%P -1 subproj2-br)" ""
test "$(git log --pretty=format:%P -1 subproj2-br)" = ""
)
'
next_test
test_expect_success 'verify one file change per commit' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git branch sub1 FETCH_HEAD &&
git subtree add --prefix="sub dir" sub1
) &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$test_count/sub proj" sub2 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir" --branch subproj-br
) &&
mkdir "$subtree_test_count"/"sub dir2" &&
test_create_commit "$subtree_test_count" "sub dir2"/main-sub2 &&
mkdir "$test_count"/"sub dir2" &&
test_create_commit "$test_count" "sub dir2"/main-sub2 &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git subtree split --prefix="sub dir2" --branch subproj2-br &&
x= &&
git log --pretty=format:"commit: %H" | join_commits |
(
while read commit a b; do
test_debug "echo Verifying commit $commit"
test_debug "echo a: $a"
test_debug "echo b: $b"
check_equal "$b" ""
x=1
done
check_equal "$x" 1
)
git log --format="%H" >commit-list &&
while read commit
do
git log -n1 --format="" --name-only "$commit" >file-list &&
test_line_count -le 1 file-list || return 1
done <commit-list
)
'
next_test
test_expect_success 'push split to subproj' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
test_create_commit "$subtree_test_count" main1 &&
test_create_commit "$subtree_test_count/sub proj" sub1 &&
subtree_test_create_repo "$test_count" &&
subtree_test_create_repo "$test_count/sub proj" &&
test_create_commit "$test_count" main1 &&
test_create_commit "$test_count/sub proj" sub1 &&
(
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
cd "$test_count" &&
git fetch ./"sub proj" HEAD &&
git subtree add --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
test_create_commit "$subtree_test_count" main2 &&
test_create_commit "$subtree_test_count/sub proj" sub2 &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub1 &&
test_create_commit "$test_count" main2 &&
test_create_commit "$test_count/sub proj" sub2 &&
test_create_commit "$test_count" "sub dir"/main-sub2 &&
(
cd $subtree_test_count/"sub proj" &&
git branch sub-branch-1 &&
cd .. &&
git fetch ./"sub proj" master &&
cd $test_count/"sub proj" &&
git branch sub-branch-1 &&
cd .. &&
git fetch ./"sub proj" HEAD &&
git subtree merge --prefix="sub dir" FETCH_HEAD
) &&
test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
(
cd "$subtree_test_count" &&
git subtree push ./"sub proj" --prefix "sub dir" sub-branch-1 &&
cd ./"sub proj" &&
git checkout sub-branch-1 &&
check_equal "$(last_commit_message)" "sub dir/main-sub3"
test_create_commit "$test_count" "sub dir"/main-sub3 &&
(
cd "$test_count" &&
git subtree push ./"sub proj" --prefix "sub dir" sub-branch-1 &&
cd ./"sub proj" &&
git checkout sub-branch-1 &&
test "$(last_commit_subject)" = "sub dir/main-sub3"
)
'
@ -991,43 +1435,43 @@ test_expect_success 'push split to subproj' '
# set of commits.
#
next_test
test_expect_success 'subtree descendant check' '
subtree_test_create_repo "$subtree_test_count" &&
test_create_commit "$subtree_test_count" folder_subtree/a &&
subtree_test_create_repo "$test_count" &&
defaultBranch=$(sed "s,ref: refs/heads/,," "$test_count/.git/HEAD") &&
test_create_commit "$test_count" folder_subtree/a &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git branch branch
) &&
test_create_commit "$subtree_test_count" folder_subtree/0 &&
test_create_commit "$subtree_test_count" folder_subtree/b &&
cherry=$(cd "$subtree_test_count"; git rev-parse HEAD) &&
test_create_commit "$test_count" folder_subtree/0 &&
test_create_commit "$test_count" folder_subtree/b &&
cherry=$(cd "$test_count"; git rev-parse HEAD) &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git checkout branch
) &&
test_create_commit "$subtree_test_count" commit_on_branch &&
test_create_commit "$test_count" commit_on_branch &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git cherry-pick $cherry &&
git checkout master &&
git checkout $defaultBranch &&
git merge -m "merge should be kept on subtree" branch &&
git branch no_subtree_work_branch
) &&
test_create_commit "$subtree_test_count" folder_subtree/d &&
test_create_commit "$test_count" folder_subtree/d &&
(
cd "$subtree_test_count" &&
cd "$test_count" &&
git checkout no_subtree_work_branch
) &&
test_create_commit "$subtree_test_count" not_a_subtree_change &&
test_create_commit "$test_count" not_a_subtree_change &&
(
cd "$subtree_test_count" &&
git checkout master &&
cd "$test_count" &&
git checkout $defaultBranch &&
git merge -m "merge should be skipped on subtree" no_subtree_work_branch &&
git subtree split --prefix folder_subtree/ --branch subtree_tip master &&
git subtree split --prefix folder_subtree/ --branch subtree_tip $defaultBranch &&
git subtree split --prefix folder_subtree/ --branch subtree_branch branch &&
check_equal $(git rev-list --count subtree_tip..subtree_branch) 0
test $(git rev-list --count subtree_tip..subtree_branch) = 0
)
'

View File

@ -23,9 +23,9 @@
"pull" and "merge" commands should fail if you've never merged
that --prefix before
docs should provide an example of "add"
note that the initial split doesn't *have* to have a commitid
specified... that's just an optimization
@ -33,7 +33,7 @@
get a misleading "prefix must end with /" message from
one of the other git tools that git-subtree calls. Should
detect this situation and print the *real* problem.
"pull --squash" should do fetch-synthesize-merge, but instead just
does "pull" directly, which doesn't work at all.