Reputation: 37792
Suppose I have some commits:
<sha1> bug due to function1
<sha2> bug due to function2
... other commits
and I would like to squash commits 1 and 2 together, keeping only the message of the second commit, then I would use git rebase -i
, edit to:
pick <sha1> bug due to function1
squash <sha2> bug due to function2
... other commits
And I would always need to edit the combined messages and delete the first one.
I know I could rearrange the commits and use fixup
like this:
pick <sha2> bug due to function2
fixup <sha1> bug due to function1
pick <sha3> other commit
but then I have the risk that, reversing the order of the two commits, there might be some conflicts.
How could I achieve the same result with less manipulations, especially avoiding the editing of the combined message. Note that there might be many commits before commit 1 and after commit 2.
Upvotes: 4
Views: 906
Reputation: 3841
Use fixup -C
in the rebase todo editor. [1]
pick <sha1> bug due to function1
fixup -C <sha2> bug due to function2
... other commits
Documented in the editor session.
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
# commit's log message, unless -C is used, in which case
# keep only this commit's message; -c is same as -C but
# opens the editor
Note that the squash commit will get the author and timestamp from <sha1>
, not from <sha2>
(thanks to Johannes in the comments).
Upvotes: 2
Reputation: 1328122
Since the OP's question, do note the behavior of an interactive rebase did change (and is now fixed, Q1 2021).
When "git rebase -i
"(man) processes fixup insn, there is no reason to clean up the commit log message, but we did the usual stripspace processing.
This has been corrected with Git 2.31 (Q1 2021).
See commit f7d42ce (28 Jan 2021) by Johannes Schindelin (dscho
).
(Merged by Junio C Hamano -- gitster
-- in commit 7e94720, 10 Feb 2021)
rebase -i
: do leave commit message intact in fixup! chainsReported-by: Vojtěch Knyttl
Helped-by: Martin Ågren
Signed-off-by: Johannes Schindelin
In 6e98de7 ("sequencer (rebase -i): add support for the 'fixup' and 'squash' commands", 2017-01-02, Git v2.12.0-rc0 -- merge listed in batch #8), this developer introduced a change of behavior by mistake: when encountering a
fixup!
commit (or multiplefixup!
commits) without anysquash!
commit thrown in, the finalgit commit
(man) was invoked with--cleanup=strip
.
Prior to that commit, the commit command had been called without that--cleanup
option.Since we explicitly read the original commit message from a file in that case, there is really no sense in forcing that clean-up.
We actually need to actively suppress that clean-up lest a configured
commit.cleanup
may interfere with what we want to do: leave the commit message unchanged.
Git 2.43 (Q4 2023) updates an error message (which would probably never been seen).
See commit 82af2c6 (03 Sep 2023) by Oswald Buddenhagen (ossilator
).
(Merged by Junio C Hamano -- gitster
-- in commit d070b77, 13 Sep 2023)
sequencer
: fix error message on failure to copySQUASH_MSG
Signed-off-by: Oswald Buddenhagen
Acked-by: Phillip Wood
The message talked about renaming, while the actual action is copying.
This was introduced by 6e98de7 ("sequencer (rebase -i): add support for the 'fixup' and 'squash' commands", 2017-01-02, Git v2.12.0-rc0 -- merge listed in batch #8).
Instead of:
could not rename '%s' to '%s'
You now have:
could not copy '%s' to '%s'
Upvotes: 0
Reputation: 37792
A fully automated version. Suppose a git history like this:
... (as many commits as you like)
6acdc6f - commit message 3
46c9468 - commit message 2
9b28fd5 - commit message 1
then rebasing to squash commit 1 and 2 together and keep commit message 2:
git rebase -i 9b28fd5~
and then editing like this:
pick 9b28fd5 commit message 1
pick 46c9468 commit message 2
exec HH=$(git rev-parse HEAD); git reset --soft HEAD~2; git commit -C $HH
pick 6acdc6f commit message 3
a small explanation of the shell commands:
HH=$(git rev-parse HEAD) # store the HEAD sha1 in a variable; since the next call will change HEAD to HEAD~2
git reset --soft HEAD~2 # destroy the last two commits keeping the changes staged.
git commit -C $HH # now commit all changes reusing the commit message from commit2 (using the sha1 that we saved in the variable)
Upvotes: 2
Reputation: 3388
First do git log
to view the hashes of the last four commits. Reset to the last commit before the two that you want to squash:
git reset --hard <sha0>
...where is the last commit before the two that you want to squash.
Then, try the following commands in order:
git cherry-pick -n <sha1>
git cherry-pick -n <sha2>
git commit
git cherry-pick <sha3>
The first two commands will combine <sha1>
and <sha2>
in your local staging sandbox. When you do git commit
with no arguments, it will commit the changes using the commit message of the last commit. All you need to do is exit the editor after reviewing the commit message. The final cherry-pick applies the last commit, unchanged.
Upvotes: 2