h,help show the help
q quiet
d show debug messages
-prefix= the name of the subdir to split out
+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'
annotate= add a prefix to commit message of new commits
b,branch= create a new branch from the split subtree
ignore_joins=
annotate=
squash=
+message=
debug()
{
--annotate) annotate="$1"; shift ;;
--no-annotate) annotate= ;;
-b) branch="$1"; shift ;;
- --prefix) prefix="$1"; shift ;;
+ -P) prefix="$1"; shift ;;
+ -m) message="$1"; shift ;;
--no-prefix) prefix= ;;
--onto) onto="$1"; shift ;;
--no-onto) onto= ;;
if [ -z "$prefix" ]; then
die "You must provide the --prefix option."
fi
-dir="$prefix"
+dir="$(dirname "$prefix/.")"
if [ "$command" != "pull" ]; then
revs=$(git rev-parse $default --revs-only "$@") || exit $?
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 [ -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
# 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.
sq=
main=
sub=
- git log --grep="^git-subtree-dir: $dir\$" \
+ git log --grep="^git-subtree-dir: $dir/*\$" \
--pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
while read a b junk; do
debug "$a $b $junk"
revs="$2"
main=
sub=
- git log --grep="^git-subtree-dir: $dir\$" \
+ git log --grep="^git-subtree-dir: $dir/*\$" \
--pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
while read a b junk; do
case "$a" in
- START) main="$b"; sq="$b" ;;
+ START) sq="$b" ;;
git-subtree-mainline:) main="$b" ;;
git-subtree-split:) sub="$b" ;;
END)
+ debug " Main is: '$main'"
if [ -z "$main" -a -n "$sub" ]; then
# squash commits refer to a subtree
+ debug " Squash: $sq from $sub"
cache_set "$sq" "$sub"
fi
if [ -n "$main" -a -n "$sub" ]; then
dir="$1"
latest_old="$2"
latest_new="$3"
+ if [ -n "$message" ]; then
+ commit_message="$message"
+ else
+ commit_message="Add '$dir/' from commit '$latest_new'"
+ fi
cat <<-EOF
- Add '$dir/' from commit '$latest_new'
+ $commit_message
git-subtree-dir: $dir
git-subtree-mainline: $latest_old
EOF
}
+add_squashed_msg()
+{
+ if [ -n "$message" ]; then
+ echo "$message"
+ else
+ echo "Merge commit '$1' as '$2'"
+ fi
+}
+
rejoin_msg()
{
dir="$1"
latest_old="$2"
latest_new="$3"
+ if [ -n "$message" ]; then
+ commit_message="$message"
+ else
+ commit_message="Split '$dir/' into commit '$latest_new'"
+ fi
cat <<-EOF
- Split '$dir/' into commit '$latest_new'
+ $commit_message
git-subtree-dir: $dir
git-subtree-mainline: $latest_old
if [ -n "$squash" ]; then
rev=$(new_squash_commit "" "" "$rev") || exit $?
- commit=$(echo "Merge commit '$rev' as '$dir'" |
+ commit=$(add_squashed_msg "$rev" "$dir" |
git commit-tree $tree $headp -p "$rev") || exit $?
else
commit=$(add_msg "$dir" "$headrev" "$rev" |
cmd_split()
{
- if [ -n "$branch" ] && rev_exists "refs/heads/$branch"; then
- die "Branch '$branch' already exists."
- fi
-
debug "Splitting $dir..."
cache_setup || exit $?
# ugly. is there no better way to tell if this is a subtree
# vs. a mainline commit? Does it matter?
- [ -z $tree ] && continue
+ if [ -z $tree ]; then
+ cache_set $rev $rev
+ continue
+ fi
newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
debug " newrev is: $newrev"
$latest_new >&2 || exit $?
fi
if [ -n "$branch" ]; then
- git update-ref -m 'subtree split' "refs/heads/$branch" \
- $latest_new "" || exit $?
- say "Created branch '$branch'"
+ if rev_exists "refs/heads/$branch"; then
+ if ! rev_is_descendant_of_branch $latest_new $branch; then
+ die "Branch '$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'"
fi
echo $latest_new
exit 0
rev="$new"
fi
- git merge -s subtree $rev
+ git merge -s subtree --message="$message" $rev
}
cmd_pull()
{
ensure_clean
- set -x
- git pull -s subtree "$@"
+ git fetch "$@" || exit $?
+ revs=FETCH_HEAD
+ cmd_merge
}
"cmd_$command" "$@"