I check my code into a Git branch every few minutes or so, and the comments end up being things like "Everything broken starting again" and other absurdities.
Then every few minutes/hours/days I do a serious commit with a real comment like, "Fixed bug #22.55, 3rd time." How can I separate these two concepts? I would like to be able to remove all my frequent-commits and just leave the serious ones.
Using Squash Instead
Recently, I've been working in another branch and using
squash
. The other branch is called temp, and then I usegit merge temp --squash
to bring it into the real branch that gets pushed to the server.Workflow is something like this, assuming I'm working in
Ticket65252
:Advantages over using
rebase
? Way less complicated.Advantages over using
reset --hard
and thenreset --soft
? Less confusing and slightly less error prone.Edited answer with now (in the second half of this entry) the new Git1.7 fixup! action and
--autosquash
option for quick commit reordering and message editing.First, the classic squashing process, as done before Git1.7.
(Git1.7 has the same process, only made faster by the possibility of automatic commit reordering as opposed to manual reordering, and by cleaner squashing messages)
This is called squashing commits.
You have some good example of "comit cleaning" in this Git ready article:
(Note: the rebase interactive feature came along since September 2007, and allows for squashing or splitting or removing or reordering commits: see also the GitPro page)
alt text http://www.gitready.com/images/squash1.png
Basically this tells Git to combine all four commits into the the first commit in the list. Once this is done and saved, another editor pops up with the following:
alt text http://www.gitready.com/images/squash2.png
Note: for "commit squashing" purposes, Git1.7 (February 2010) has introduced 2 new elements (as mentioned by Dustin in the comment):
Both (fixup action and
--autosquash
option) are illustrated in this Thechnosorcery Networks blog entry. Those features have been cooking since last June 2009 and debated further last December.The
fixup
action or directive is for squashing a commit you would have manually reordered in the commit edit list of arebase --interactive
, while ignoring the second commit message, which will make the message edition step faster (you can just save it: the squashed commit will have the first commit message only)The resulting commit message will only be the first commit one.
The
--autosquash
option is about making the commit reordering process automatically for you:(Actually, the
squash!
can only use the beginning of another commit message)See? Here the third commit uses only the beginning of the first commit message.
A
rebase --interactive --autosquash
will move the squashed commit below the relevant one:The message edition would be:
Meaning by default you would keep the squashing operation recorded in the commit message.
But with the fixup! directive, you could keep that squashing "invisible" in the commit message, while still benefiting from the automatic commit reordering with the
--autosquash
option (and the fact that your second commit message is based on the first commit you want to be squashed with).The message by default will be:
Now if you want to fixup or squash based on the previous commit you just did, Jacob Helwig (the author of the Technosorcery Networks blog entry) recommends the following aliases:
And for doing a rebase interactive which will always benefit from the automatic reordering of commits meant to be squashed:
Update for Git 2.18 (Q2 2018): "
git rebase -i
" sometimes left intermediate "# This is a combination of N commits
" message meant for the human consumption inside an editor in the final result in certain corner cases, which has been fixed.See commit 15ef693, commit dc4b5bc, commit e12a7ef, commit d5bc6f2 (27 Apr 2018) by Johannes Schindelin (
dscho
).(Merged by Junio C Hamano --
gitster
-- in commit 4a3bf32, 23 May 2018)Git 2.19 (Q3 2018) fixes a bug: When "
git rebase -i
" is told to squash two or more commits into one, it labeled the log message for each commit with its number.It correctly called the first one "1st commit", but the next one was "
commit #1
", which was off-by-one(!).See commit dd2e36e (15 Aug 2018) by Phillip Wood (
phillipwood
).(Merged by Junio C Hamano --
gitster
-- in commit 36fd1e8, 20 Aug 2018)Use git rebase -i to pick and squash your commits together.
Using Soft Reset Instead of Rebase to Squash GIT History
I think the length of VonC's answers speaks volumes -- literally -- about how complicated
git rebase
is. This is my extension of another answer to a question of mine.ticket-201
that you branched frommaster
. You want to pretend that all the commits fromticket-201
never happened, but that you did all the work in one shot.git reset --soft hash
wherehash
should be a commit hash that is inticket-201
's log.Making Up Histories From Arbitrary Commits in Different Branches
Using resets you can rewrite the history as you see fit, though your edits will lose the charm of having the right timestamp. Assuming you don't care about that (the times/dates on your files will be enough, perhaps?), or if you want to fiddle with the commits as you go, you can follow these steps:
commit0
(pretend that's a hash):git checkout -b new-history commit0
commit5
:git reset --hard commit5
git reset --soft commit0
This idea is simple, effective and flexible.