summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rwxr-xr-xgit-subtree.sh73
-rwxr-xr-xtest.sh106
3 files changed, 164 insertions, 17 deletions
diff --git a/.gitignore b/.gitignore
index e358b18b78..7e77c9d022 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
*~
git-subtree.xml
git-subtree.1
+mainline
+subproj
diff --git a/git-subtree.sh b/git-subtree.sh
index 8baa376fe5..66ce251eaa 100755
--- a/git-subtree.sh
+++ b/git-subtree.sh
@@ -16,7 +16,8 @@ git subtree split --prefix=<prefix> <commit...>
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
@@ -40,6 +41,7 @@ rejoin=
ignore_joins=
annotate=
squash=
+message=
debug()
{
@@ -76,7 +78,8 @@ while [ $# -gt 0 ]; do
--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= ;;
@@ -158,6 +161,20 @@ 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 [ -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.
@@ -266,8 +283,13 @@ add_msg()
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
@@ -275,13 +297,27 @@ add_msg()
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
@@ -441,7 +477,7 @@ cmd_add()
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" |
@@ -454,10 +490,6 @@ cmd_add()
cmd_split()
{
- if [ -n "$branch" ] && rev_exists "refs/heads/$branch"; then
- die "Branch '$branch' already exists."
- fi
-
debug "Splitting $dir..."
cache_setup || exit $?
@@ -488,7 +520,8 @@ cmd_split()
eval "$grl" |
while read rev parents; do
revcount=$(($revcount + 1))
- say -n "$revcount/$revmax ($createcount) "
+ say -n "$revcount/$revmax ($createcount)
+"
debug "Processing commit: $rev"
exists=$(cache_get $rev)
if [ -n "$exists" ]; then
@@ -505,7 +538,10 @@ cmd_split()
# 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"
@@ -526,9 +562,16 @@ cmd_split()
$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
@@ -561,7 +604,7 @@ cmd_merge()
rev="$new"
fi
- git merge -s subtree $rev
+ git merge -s subtree --message="$message" $rev
}
cmd_pull()
diff --git a/test.sh b/test.sh
index 12b0456574..cfe3a3c258 100755
--- a/test.sh
+++ b/test.sh
@@ -53,6 +53,16 @@ multiline()
done
}
+undo()
+{
+ git reset --hard HEAD~
+}
+
+last_commit_message()
+{
+ git log --format=%s -1
+}
+
rm -rf mainline subproj
mkdir mainline subproj
@@ -82,7 +92,24 @@ git branch subdir
git fetch ../subproj sub1
git branch sub1 FETCH_HEAD
+
+# check if --message works for add
+git subtree add --prefix=subdir --message="Added subproject" sub1
+check_equal "$(last_commit_message)" "Added subproject"
+undo
+
+# check if --message works as -m and --prefix as -P
+git subtree add -P subdir -m "Added subproject using git subtree" sub1
+check_equal "$(last_commit_message)" "Added subproject using git subtree"
+undo
+
+# check if --message works with squash too
+git subtree add -P subdir -m "Added subproject with squash" --squash sub1
+check_equal "$(last_commit_message)" "Added subproject with squash"
+undo
+
git subtree add --prefix=subdir/ FETCH_HEAD
+check_equal "$(last_commit_message)" "Add 'subdir/' from commit '$(git rev-parse sub1)'"
# this shouldn't actually do anything, since FETCH_HEAD is already a parent
git merge -m 'merge -s -ours' -s ours FETCH_HEAD
@@ -98,13 +125,44 @@ git commit -m 'main-sub7'
git fetch ../subproj sub2
git branch sub2 FETCH_HEAD
+
+# check if --message works for merge
+git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2
+check_equal "$(last_commit_message)" "Merged changes from subproject"
+undo
+
+# check if --message for merge works with squash too
+git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2
+check_equal "$(last_commit_message)" "Merged changes from subproject using squash"
+undo
+
git subtree merge --prefix=subdir FETCH_HEAD
git branch pre-split
+check_equal "$(last_commit_message)" "Merge commit '$(git rev-parse sub2)' into mainline"
-spl1=$(git subtree split --annotate='*' \
- --prefix subdir --onto FETCH_HEAD --rejoin)
+# check if --message works for split+rejoin
+spl1=$(git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)
echo "spl1={$spl1}"
git branch spl1 "$spl1"
+check_equal "$(last_commit_message)" "Split & rejoin"
+undo
+
+# check split with --branch
+git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --branch splitbr1
+check_equal "$(git rev-parse splitbr1)" "$spl1"
+
+# check split with --branch for an existing branch
+git branch splitbr2 sub1
+git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --branch splitbr2
+check_equal "$(git rev-parse splitbr2)" "$spl1"
+
+# check split with --branch for an incompatible branch
+result=$(git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir || echo "caught error")
+check_equal "$result" "caught error"
+
+
+git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --rejoin
+check_equal "$(last_commit_message)" "Split 'subdir/' into commit '$spl1'"
create subdir/main-sub8
git commit -m 'main-sub8'
@@ -170,6 +228,50 @@ check_equal "$(git log --pretty=format:'%s' HEAD^2 | grep -i split)" ""
# 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.*:')" ""
+
+# check if split can find proper base without --onto
+# prepare second pair of repositories
+mkdir test2
+cd test2
+
+mkdir main
+cd main
+git init
+create main1
+git commit -m "main1"
+
+cd ..
+mkdir sub
+cd sub
+git init
+create sub2
+git commit -m "sub2"
+
+cd ../main
+git fetch ../sub master
+git branch sub2 FETCH_HEAD
+git subtree add --prefix subdir sub2
+
+cd ../sub
+create sub3
+git commit -m "sub3"
+
+cd ../main
+git fetch ../sub master
+git branch sub3 FETCH_HEAD
+git subtree merge --prefix subdir sub3
+
+create subdir/main-sub4
+git commit -m "main-sub4"
+git subtree split --prefix subdir --branch mainsub4
+
+# at this point, the new commit's parent should be sub3
+# if it's not, something went wrong (the "newparent" of "master~" commit should have been sub3,
+# but it wasn't, because it's cache was not set to itself)
+check_equal "$(git log --format=%P -1 mainsub4)" "$(git rev-parse sub3)"
+
+
+
# 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.