@matheusbsbs / matheus-bagliano / commits / 1cc2cfff91
Fork 0

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