Allow using --squash with "git subtree split --rejoin". It will still split off (and save to --branch) the complete subtree history, but the merge done for the "--rejoin" will be merging a squashed representation of the new subtree commits, instead of the commits themselves (similar to how "git subtree merge --squash" works). Signed-off-by: Matthew Ogilvie <mmogilvi_git@xxxxxxxxxxxx> --- I can think of a couple of possible objections to this patch. Are these (or any others) worth fixing? 1. Perhaps someone want the saved subtree (--branch) to have a squashed representation as well, as an option? Maybe we need two different --squash options? Something like "--rejoin-squash"? 2. It could definitely use some automated tests. In fact, pre-existing --squash functionality is hardly tested at all, either. See patch 4 comments for a script I use to help with mostly-manual testing. contrib/subtree/git-subtree.sh | 60 +++++++++++++++++++++++++++++++---------- contrib/subtree/git-subtree.txt | 27 ++++++++++++------- 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 7d7af03..998a9c5 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -20,14 +20,13 @@ 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 +squash merge subtree changes as a single 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 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', 'pull' and 'push' -squash merge subtree changes as a single commit " eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" @@ -229,13 +228,19 @@ find_latest_squash() sq= main= sub= + par1= + par2= 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" + --pretty=format:'START %H %P%n%s%n%n%b%nEND%n' HEAD | + while read a b c d junk; do + debug "$a $b $c $d $junk" debug "{{$sq/$main/$sub}}" case "$a" in - START) sq="$b" ;; + START) + sq="$b" + par1="$c" + par2="$d" + ;; git-subtree-mainline:) main="$b" ;; git-subtree-split:) sub="$b" ;; END) @@ -243,7 +248,8 @@ find_latest_squash() if [ -n "$main" ]; then # a rejoin commit? # Pretend its sub was a squash. - sq="$sub" + assert [ "$main" = "$par1" ] + sq="$par2" fi debug "Squash found: $sq $sub" echo "$sq" "$sub" @@ -252,6 +258,8 @@ find_latest_squash() sq= main= sub= + par1= + par2= ;; esac done @@ -565,6 +573,13 @@ cmd_split() debug "Splitting $dir..." cache_setup || exit $? + if [ -n "$rejoin" ]; then + ensure_clean + if [ -n "$squash" ]; then + first_split="$(find_latest_squash "$dir")" + fi + fi + if [ -n "$onto" ]; then debug "Reading history for --onto=$onto..." git rev-list $onto | @@ -630,13 +645,6 @@ cmd_split() die "No new revisions were found" fi - if [ -n "$rejoin" ]; then - debug "Merging split branch into HEAD..." - latest_old=$(cache_get latest_old) - git merge -s ours \ - -m "$(rejoin_msg $dir $latest_old $latest_new)" \ - $latest_new >&2 || exit $? - fi if [ -n "$branch" ]; then if rev_exists "refs/heads/$branch"; then if ! rev_is_descendant_of_branch $latest_new $branch; then @@ -649,6 +657,30 @@ cmd_split() git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $? say "$action branch '$branch'" fi + if [ -n "$rejoin" ]; then + debug "Merging split branch into HEAD..." + latest_old=$(cache_get latest_old) + new=$latest_new + + if [ -n "$squash" ]; then + debug "Squashing split branch." + + set $first_split + old=$1 + sub=$2 + if [ "$sub" = "$latest_new" ]; then + say "Subtree is already at commit $latest_new." + exit 0 + fi + new=$(new_squash_commit "$old" "$sub" "$latest_new") \ + || exit $? + debug "New squash commit: $new" + fi + + git merge -s ours -m \ + "$(rejoin_msg $dir $latest_old $latest_new)" \ + $new >&2 || exit $? + fi echo $latest_new exit 0 } diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt index e0957ee..92e7a4d 100644 --- a/contrib/subtree/git-subtree.txt +++ b/contrib/subtree/git-subtree.txt @@ -140,18 +140,20 @@ OPTIONS want to manipulate. This option is mandatory for all commands. + +OPTIONS FOR add, merge, pull, rejoin +---------------------------------- -m <message>:: --message=<message>:: - This option is only valid for add, merge and pull (unsure). - Specify <message> as the commit message for the merge commit. + 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, merge, push, pull ----------------------------------- --squash:: - This option is only valid for add, merge, push and pull - commands. - + This option is only valid for add, merge, pull, and + split '--rejoin'. + 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 @@ -180,6 +182,10 @@ OPTIONS FOR add, merge, push, pull local repository remain intact and can be later split and send upstream to the subproject. + Using '--squash' with split '--rejoin' only squashes + the merge back to the mainline, not the synthetic subtree + history. + OPTIONS FOR split ----------------- @@ -251,9 +257,10 @@ OPTIONS FOR split 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. + Fortunately, you can use '--squash' with '--rejoin' + to simplify a sequence of synthetic commits as a + single squashed commit in the mainline. The subtree + will still have full history. EXAMPLE 1. Add command -- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html