From 6da401386ea37f1bda3f6717d7c77fb0e5c351b2 Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Wed, 6 Jan 2010 23:11:43 +0100 Subject: [PATCH 1/9] added -p alias for --prefix --- git-subtree.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-subtree.sh b/git-subtree.sh index 8baa376fe5..28fb8e81fb 100755 --- a/git-subtree.sh +++ b/git-subtree.sh @@ -16,7 +16,7 @@ git subtree split --prefix= 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 options for 'split' annotate= add a prefix to commit message of new commits b,branch= create a new branch from the split subtree @@ -76,7 +76,7 @@ while [ $# -gt 0 ]; do --annotate) annotate="$1"; shift ;; --no-annotate) annotate= ;; -b) branch="$1"; shift ;; - --prefix) prefix="$1"; shift ;; + -p) prefix="$1"; shift ;; --no-prefix) prefix= ;; --onto) onto="$1"; shift ;; --no-onto) onto= ;; From 2da0969a794eac50401fd25ea072224d4378a5fe Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Sat, 9 Jan 2010 19:55:35 +0100 Subject: [PATCH 2/9] added -m/--message option for setting merge commit message --- git-subtree.sh | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/git-subtree.sh b/git-subtree.sh index 28fb8e81fb..96118735b2 100755 --- a/git-subtree.sh +++ b/git-subtree.sh @@ -17,6 +17,7 @@ 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' 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() { @@ -77,6 +79,7 @@ while [ $# -gt 0 ]; do --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= ;; @@ -266,8 +269,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 +283,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' + $message git-subtree-dir: $dir git-subtree-mainline: $latest_old @@ -441,7 +463,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" | @@ -561,7 +583,7 @@ cmd_merge() rev="$new" fi - git merge -s subtree $rev + git merge -s subtree --message="$message" $rev } cmd_pull() From 0a562948ae1fe7130f4e9a29ce4107311ab93b91 Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Sat, 9 Jan 2010 19:56:05 +0100 Subject: [PATCH 3/9] allow using --branch with existing branches if it makes sense --- git-subtree.sh | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/git-subtree.sh b/git-subtree.sh index 96118735b2..09992e39d5 100755 --- a/git-subtree.sh +++ b/git-subtree.sh @@ -161,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 $newrev | grep $branch_hash) + + if [ -n "$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. @@ -476,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 $? @@ -510,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 @@ -548,9 +559,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 From da949cc554304bf9dc2b20ffcd470fb6b8a35576 Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Sat, 9 Jan 2010 23:01:39 +0100 Subject: [PATCH 4/9] fix for subtree split not finding proper base for new commits --- git-subtree.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/git-subtree.sh b/git-subtree.sh index 09992e39d5..cdf7b0992b 100755 --- a/git-subtree.sh +++ b/git-subtree.sh @@ -538,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" From 6e25f79f353a1c1f454ae0c44aa45ff3ab44551c Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Tue, 12 Jan 2010 22:38:21 +0100 Subject: [PATCH 5/9] changed alias for --prefix from -p to -P --- git-subtree.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-subtree.sh b/git-subtree.sh index cdf7b0992b..0a5cafa77f 100755 --- a/git-subtree.sh +++ b/git-subtree.sh @@ -16,7 +16,7 @@ git subtree split --prefix= h,help show the help q quiet d show debug messages -p,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 @@ -78,7 +78,7 @@ while [ $# -gt 0 ]; do --annotate) annotate="$1"; shift ;; --no-annotate) annotate= ;; -b) branch="$1"; shift ;; - -p) prefix="$1"; shift ;; + -P) prefix="$1"; shift ;; -m) message="$1"; shift ;; --no-prefix) prefix= ;; --onto) onto="$1"; shift ;; From 12629161a89511847ec5703ca457ee373deb33cb Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Tue, 12 Jan 2010 22:38:34 +0100 Subject: [PATCH 6/9] fixed bug in commit message for split --- git-subtree.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-subtree.sh b/git-subtree.sh index 0a5cafa77f..48bc570390 100755 --- a/git-subtree.sh +++ b/git-subtree.sh @@ -317,7 +317,7 @@ rejoin_msg() commit_message="Split '$dir/' into commit '$latest_new'" fi cat <<-EOF - $message + $commit_message git-subtree-dir: $dir git-subtree-mainline: $latest_old From 13ea2b5e0786e70780f91637fb82ca18dd03a730 Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Tue, 12 Jan 2010 23:04:25 +0100 Subject: [PATCH 7/9] added tests for recent changes --- test.sh | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 2 deletions(-) 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. From 4a6ea5ce301c9844722bfdb9291bd9cb7797d9ed Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Tue, 12 Jan 2010 23:04:56 +0100 Subject: [PATCH 8/9] added temporary test dirs to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) 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 From 6bd910a82155ae3def5cf38acb27d36a192c449e Mon Sep 17 00:00:00 2001 From: Jakub Suder Date: Tue, 12 Jan 2010 23:34:52 +0100 Subject: [PATCH 9/9] improved rev_is_descendant_of_branch() function --- git-subtree.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-subtree.sh b/git-subtree.sh index 48bc570390..66ce251eaa 100755 --- a/git-subtree.sh +++ b/git-subtree.sh @@ -166,9 +166,9 @@ rev_is_descendant_of_branch() newrev="$1" branch="$2" branch_hash=$(git rev-parse $branch) - match=$(git rev-list $newrev | grep $branch_hash) + match=$(git rev-list -1 $branch_hash ^$newrev) - if [ -n "$match" ]; then + if [ -z "$match" ]; then return 0 else return 1