Basic "subtree merge --squash" support.
Instead of merging in the history of the entire subproject, just squash it all into one commit, but try to at least track which commits we used so that we can do future merges correctly. Bonus feature: we can actually switch branches of the subproject this way, just by "squash merging" back and forth from one tag to another.
Avery Pennarun committed
May 30, 2009 at 03:18 UTC
1cc2cfff91c61bb56236914da7be7b15584951df
1 file changed
+77
-1
git-subtree.sh
+77
-1
@@ -167,15 +167,46 @@ try_remove_previous()
167
fi
168
}
169
170
+find_latest_squash()
171
+{
172
+ debug "Looking for latest squash..."
173
+ dir="$1"
174
+ git log --grep="^git-subtree-dir: $dir\$" \
175
+ --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
176
+ while read a b junk; do
177
+ case "$a" in
178
+ START) sq="$b" ;;
179
+ git-subtree-mainline:) main="$b" ;;
180
+ git-subtree-split:) sub="$b" ;;
181
+ END)
182
+ if [ -n "$sub" ]; then
183
+ if [ -n "$main" ]; then
184
+ # a rejoin commit?
185
+ # Pretend its sub was a squash.
186
+ sq="$sub"
187
+ fi
188
+ debug "Squash found: $sq $sub"
189
+ echo "$sq" "$sub"
190
+ break
191
+ fi
192
+ sq=
193
+ main=
194
+ sub=
195
+ ;;
196
+ esac
197
+ done
198
+}
199
+
200
find_existing_splits()
201
{
202
debug "Looking for prior splits..."
203
dir="$1"
204
revs="$2"
205
git log --grep="^git-subtree-dir: $dir\$" \
176
- --pretty=format:'%s%n%n%b%nEND' $revs |
206
+ --pretty=format:'%s%n%n%b%nEND%n' $revs |
207
while read a b junk; do
208
case "$a" in
209
+ START) main="$b" ;;
210
git-subtree-mainline:) main="$b" ;;
211
git-subtree-split:) sub="$b" ;;
212
END)
@@ -244,6 +275,28 @@ rejoin_msg()
275
EOF
276
}
277
278
+squash_msg()
279
+{
280
+ dir="$1"
281
+ oldsub="$2"
282
+ newsub="$3"
283
+ oldsub_short=$(git rev-parse --short "$oldsub")
284
+ newsub_short=$(git rev-parse --short "$newsub")
285
+ cat <<-EOF
286
+ Squashed '$dir/' changes from $oldsub_short..$newsub_short
287
+
288
+ EOF
289
+
290
+ git log --pretty=tformat:'%h %s' "$oldsub..$newsub"
291
+ git log --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub"
292
+
293
+ cat <<-EOF
294
+
295
+ git-subtree-dir: $dir
296
+ git-subtree-split: $newsub
297
+ EOF
298
+}
299
+
300
toptree_for_commit()
301
{
302
commit="$1"
@@ -278,6 +331,16 @@ tree_changed()
331
fi
332
}
333
334
+new_squash_commit()
335
+{
336
+ old="$1"
337
+ oldsub="$2"
338
+ newsub="$3"
339
+ tree=$(toptree_for_commit $newsub) || exit $?
340
+ squash_msg "$dir" "$oldsub" "$newsub" |
341
+ git commit-tree "$tree" -p "$old" || exit $?
342
+}
343
+
344
copy_or_skip()
345
{
346
rev="$1"
@@ -452,6 +515,19 @@ cmd_merge()
515
fi
516
rev="$1"
517
518
+ if [ -n "$squash" ]; then
519
+ first_split="$(find_latest_squash "$dir")"
520
+ if [ -z "$first_split" ]; then
521
+ die "Can't squash-merge: '$dir' was never added."
522
+ fi
523
+ set $first_split
524
+ old=$1
525
+ sub=$2
526
+ new=$(new_squash_commit "$old" "$sub" "$rev") || exit $?
527
+ debug "New squash commit: $new"
528
+ rev="$new"
529
+ fi
530
+
531
git merge -s subtree $rev
532
}
533