Skip to content

Commit 7d0c688

Browse files
author
Junio C Hamano
committed
git-merge --squash
Some people tend to do many little commits on a topic branch, recording all the trials and errors, and when the topic is reasonably cooked well, would want to record the net effect of the series as one commit on top of the mainline, removing the cruft from the history. The topic is then abandoned or forked off again from that point at the mainline. The barebone porcelainish that comes with core git tools does not officially support such operation, but you can fake it by using "git pull --no-merge" when such a topic branch is not a strict superset of the mainline, like this: git checkout mainline git pull --no-commit . that-topic-branch : fix conflicts if any rm -f .git/MERGE_HEAD git commit -a -m 'consolidated commit log message' git branch -f that-topic-branch ;# now fully merged This however does not work when the topic branch is a fast forward of the mainline, because normal "git pull" will never create a merge commit in such a case, and there is nothing special --no-commit could do to begin with. This patch introduces a new option, --squash, to support such a workflow officially in both fast-forward case and true merge case. The user-level operation would be the same in both cases: git checkout mainline git pull --squash . that-topic-branch : fix conflicts if any -- naturally, there would be : no conflict if fast forward. git commit -a -m 'consolidated commit log message' git branch -f that-topic-branch ;# now fully merged When the current branch is already up-to-date with respect to the other branch, there truly is nothing to do, so the new option does not have any effect. This was brought up in #git IRC channel recently. Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 86378b3 commit 7d0c688

File tree

5 files changed

+72
-24
lines changed

5 files changed

+72
-24
lines changed

Documentation/merge-options.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
not autocommit, to give the user a chance to inspect and
77
further tweak the merge result before committing.
88

9+
--squash::
10+
Produce the working tree and index state as if a real
11+
merge happened, but do not actually make a commit or
12+
move the `HEAD`, nor record `$GIT_DIR/MERGE_HEAD` to
13+
cause the next `git commit` command to create a merge
14+
commit. This allows you to create a single commit on
15+
top of the current branch whose effect is the same as
16+
merging another branch (or more in case of an octopus).
917

1018
-s <strategy>, \--strategy=<strategy>::
1119
Use the given merge strategy; can be supplied more than

git-commit.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,9 @@ then
566566
elif test -f "$GIT_DIR/MERGE_HEAD" && test -f "$GIT_DIR/MERGE_MSG"
567567
then
568568
cat "$GIT_DIR/MERGE_MSG"
569+
elif test -f "$GIT_DIR/SQUASH_MSG"
570+
then
571+
cat "$GIT_DIR/SQUASH_MSG"
569572
fi | git-stripspace >"$GIT_DIR"/COMMIT_EDITMSG
570573

571574
case "$signoff" in
@@ -663,7 +666,7 @@ else
663666
fi
664667
if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" -a -z "$amend" ]
665668
then
666-
rm -f "$GIT_DIR/COMMIT_EDITMSG"
669+
rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
667670
run_status
668671
exit 1
669672
fi
@@ -729,7 +732,7 @@ else
729732
false
730733
fi
731734
ret="$?"
732-
rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG"
735+
rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
733736
if test -d "$GIT_DIR/rr-cache"
734737
then
735738
git-rerere

git-merge.sh

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
# Copyright (c) 2005 Junio C Hamano
44
#
55

6-
7-
USAGE='[-n] [--no-commit] [-s <strategy>]... <merge-message> <head> <remote>+'
6+
USAGE='[-n] [--no-commit] [--squash] [-s <strategy>]... <merge-message> <head> <remote>+'
87
. git-sh-setup
98

109
LF='
@@ -42,20 +41,49 @@ restorestate() {
4241
fi
4342
}
4443

44+
finish_up_to_date () {
45+
case "$squash" in
46+
t)
47+
echo "$1 (nothing to squash)" ;;
48+
'')
49+
echo "$1" ;;
50+
esac
51+
dropsave
52+
}
53+
54+
squash_message () {
55+
echo Squashed commit of the following:
56+
echo
57+
git-log --no-merges ^"$head" $remote
58+
}
59+
4560
finish () {
4661
test '' = "$2" || echo "$2"
47-
case "$merge_msg" in
48-
'')
49-
echo "No merge message -- not updating HEAD"
62+
case "$squash" in
63+
t)
64+
echo "Squash commit -- not updating HEAD"
65+
squash_message >"$GIT_DIR/SQUASH_MSG"
5066
;;
51-
*)
52-
git-update-ref HEAD "$1" "$head" || exit 1
67+
'')
68+
case "$merge_msg" in
69+
'')
70+
echo "No merge message -- not updating HEAD"
71+
;;
72+
*)
73+
git-update-ref HEAD "$1" "$head" || exit 1
74+
;;
75+
esac
5376
;;
5477
esac
55-
56-
case "$no_summary" in
78+
case "$1" in
5779
'')
58-
git-diff-tree --stat --summary -M "$head" "$1"
80+
;;
81+
?*)
82+
case "$no_summary" in
83+
'')
84+
git-diff-tree --stat --summary -M "$head" "$1"
85+
;;
86+
esac
5987
;;
6088
esac
6189
}
@@ -66,6 +94,8 @@ do
6694
-n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
6795
--no-summa|--no-summar|--no-summary)
6896
no_summary=t ;;
97+
--sq|--squ|--squa|--squas|--squash)
98+
squash=t no_commit=t ;;
6999
--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
70100
no_commit=t ;;
71101
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
@@ -152,8 +182,7 @@ f,*)
152182
?,1,"$1",*)
153183
# If head can reach all the merge then we are up to date.
154184
# but first the most common case of merging one remote.
155-
echo "Already up-to-date."
156-
dropsave
185+
finish_up_to_date "Already up-to-date."
157186
exit 0
158187
;;
159188
?,1,"$head",*)
@@ -205,8 +234,7 @@ f,*)
205234
done
206235
if test "$up_to_date" = t
207236
then
208-
echo "Already up-to-date. Yeeah!"
209-
dropsave
237+
finish_up_to_date "Already up-to-date. Yeeah!"
210238
exit 0
211239
fi
212240
;;
@@ -310,11 +338,17 @@ case "$best_strategy" in
310338
git-merge-$best_strategy $common -- "$head_arg" "$@"
311339
;;
312340
esac
313-
for remote
314-
do
315-
echo $remote
316-
done >"$GIT_DIR/MERGE_HEAD"
317-
echo "$merge_msg" >"$GIT_DIR/MERGE_MSG"
341+
342+
if test "$squash" = t
343+
then
344+
finish
345+
else
346+
for remote
347+
do
348+
echo $remote
349+
done >"$GIT_DIR/MERGE_HEAD"
350+
echo "$merge_msg" >"$GIT_DIR/MERGE_MSG"
351+
fi
318352

319353
if test "$merge_was_ok" = t
320354
then

git-pull.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ USAGE='[-n | --no-summary] [--no-commit] [-s strategy]... [<fetch-options>] <rep
88
LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
99
. git-sh-setup
1010

11-
strategy_args= no_summary= no_commit=
11+
strategy_args= no_summary= no_commit= squash=
1212
while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac
1313
do
1414
case "$1" in
@@ -17,6 +17,8 @@ do
1717
no_summary=-n ;;
1818
--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
1919
no_commit=--no-commit ;;
20+
--sq|--squ|--squa|--squas|--squash)
21+
squash=--squash ;;
2022
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
2123
--strateg=*|--strategy=*|\
2224
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
@@ -100,4 +102,5 @@ case "$strategy_args" in
100102
esac
101103

102104
merge_name=$(git-fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
103-
git-merge $no_summary $no_commit $strategy_args "$merge_name" HEAD $merge_head
105+
git-merge $no_summary $no_commit $squash $strategy_args \
106+
"$merge_name" HEAD $merge_head

git-reset.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@ case "$reset_type" in
6161
;;
6262
esac
6363

64-
rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR"
64+
rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" "$GIT_DIR/SQUASH_MSG"

0 commit comments

Comments
 (0)