Reputation: 5298
I've been using Git Extensions for a while now (it's awesome!) but I haven't found a simple answer to the following:
Sometimes, when typing a commit message, a make a typo. My friend showed me how to fix it the following way (in Git Extentions):
Right-Click on the commit > Advanced > Fixup commit
Then I simply check the box "Amend" and rewrite my message and voila! My commit message is fixed.
However this other option "Squash commit"... I have always wondered what it does?!
My question is:
Would someone simply explain me what is the exact difference between Squash commit and Fixup commit in Git/Git Extentions? They look kind of... "similar" to me:
Upvotes: 213
Views: 110175
Reputation: 30289
Simply put, when rebasing a series of commits, each commit marked as a squash
, gives you the opportunity to use its message as part of a pick
or reword
commit message.
When you use fixup
the message from that commit is discarded.
In other words, if you want to squash
a set of commits into a pick/reword
, and you don't care about those commit messages, fixup
is a good option.
e.g.
Our git log with junk commit messages
a31420a1 Fix #1038
cdc30d2e Fix typo
b37dd782 Debugging CI/ops
a5edbd21 some more backtracking
d0300c9a CI config edit
0a6ac804 CI config edit
ef97905e Add collectErrors tests for CommandParams
Start an interactive rebase, (Note: listed in reverse.) Our rebase target commit is ef97905e
. (We'll mark it pick when we set the rebase options. Rebase works backwards, git's trying to help by putting the last commit first.)
pick ef97905e Add collectErrors tests for CommandParams
pick 0a6ac804 CI config edit
pick d0300c9a CI config edit
pick a5edbd21 some more backtracking
pick b37dd782 Debugging CI/ops
pick cdc30d2e Fix typo
pick a31420a1 Fix #1038
Set the rebase options for the commits.
pick ef97905e Add collectErrors tests for CommandParams
fixup 0a6ac804 CI config edit
fixup d0300c9a CI config edit
fixup a5edbd21 some more backtracking
fixup b37dd782 Debugging CI/ops
fixup cdc30d2e Fix typo
squash a31420a1 Fix #1038
When we are given the rebase commit message to edit, we get:
Fix #1038
Add collectErrors tests for CommandParams
(Along with comment lines for the fixup commits.)
Complete the rebase and we'll have our combined commit meesage (after a bit of hand editing) in a new commit SHA.
b37da51c Fix #1038 Add collectErrors test for CommandParams
Upvotes: 110
Reputation: 841
In addition to previous answers I wanted to highlight the new options that can also be used with fixup
that further differentiate it from using squash
.
I am using git version 2.32.1
Lets imagine you are rebasing a branch called my-new-feature
onto main
.
In order to do this you are doing an interactive rebase using the command git rebase -i main
whilst you have the my-new-feature
branch checked out.
(If you are using a GUI for git like the OP is using Git Extensions, if they are up to date they should also contain these new options)
Running git rebase -i main
opens the following in your editor:
pick 996b0 work in progress
pick 59f3a feature finished
# Rebase b6914..59f3a onto b6914 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# 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
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified); use -c <commit> to reword the commit message
Note the new -C | -c
flag.
s, squash = use commit, but meld into previous commit
f, fixup [-C | -c] = 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
Looking at our 2 commits:
pick 996b0 work in progress
pick 59f3a feature finished
Let's imagine you want to meld them together (something which can be done with squash
or fixup
) but you want to only keep the helpful commit message of the second commit feature finished
.
Using fixup
as we have in the past like so:
pick 996b0 work in progress
f 59f3a feature finished
will not do what we want. It will meld the commits together but it will leave us with the work in progress
commit message, which is not very helpful.
We could use squash
like so:
pick 996b0 work in progress
s 59f3a feature finished
this will open a new editor which will allow you comment out the first commit message manually and persist the feature finished
commit message. This does work but it forces you to have an extra manual step; aint nobody got time for dat
This is where the new option we can pass to fixup
is super handy.
pick 996b0 work in progress
f -C 59f3a feature finished
This will meld the commits together but discard the work in progress
commit message and only keep the feature finished
commit message.
These new options make fixup
even more powerful and allows you to rely even less on using squash
when you don't need to.
Upvotes: 4
Reputation: 14081
Both squash
and fixup
will allow code changes to be introduced by their respective diff's. (not normally mentioned)
The distinction between squash
and fixup
is in how the commit messages are treated.
In the fixup
case the original commit message is kept, unchanged, and any text within the fixup
commit message is ignored (unceremoniously dumped).
In the squash
case both commit messages are combined ('melded'), one after the other, and offered to the user's editor, so that they can be amalgamated/fused together as desired by the user.
The initial combined message (squash) will contain comment marks showing where the messages start and end. Multiple squash
and fixup
s can be applied.
The terminology can be confusing when discussing small code corrections (without need to change the message) as often one may suggest that the code is 'squeezed' in, when a squash
isn't actually required.
Upvotes: 8
Reputation: 9958
If the question is what's the difference between squash
and fixup
in git when doing git rebase --interactive, then the answer is the commit message.
s, squash <commit>
= use commit, but meld into previous commit
f, fixup <commit>
= like "squash", but discard this commit's log message
For example:
pick 22a4667 father commit message
squash 46d7c0d child commit message # case 1
# fixup 46d7c0d child commit message # case 2
The commit message after rebasing in case 1 would be:
father commit message
child commit message
while the commit message in case 2 is:
father commit message
# no sub messages
Upvotes: 67
Reputation: 446
From git-rebase doc, "interactive mode" section:
If you want to fold two or more commits into one, replace the command "pick" for the second and subsequent commits with "squash" or "fixup". If the commits had different authors, the folded commit will be attributed to the author of the first commit. The suggested commit message for the folded commit is the concatenation of the commit messages of the first commit and of those with the "squash" command, but omits the commit messages of commits with the "fixup" command.
Upvotes: 18
Reputation: 12787
Why not ask git itself?
When you rebase with git-bash
, it says:
pick 512b1d7 (some comment)
# Rebase 621b2e4..512b1d7 onto 621b2e4 (1 command)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
D:/code/fenixito-legacy-api/.git/rebase-merge/git-rebase-todo [unix] (11:57 23/10/2019) 1,1 start
"D:/code/xxx/.git/rebase-merge/git-rebase-todo" [UNIX] 27L, 1170C
So you see:
s, squash = use commit, but meld into previous commit
f, fixup = like "squash", but discard this commit's log message
Upvotes: 1
Reputation: 3464
I do not know what Git Extensions does with it specifically, but git rebase
has an option to automatically squash or fixup commits with squash! or fixup! prefixes, respectively:
--autosquash, --no-autosquash
When the commit log message begins with "squash! ..." (or "fixup!
..."), and there is a commit whose title begins with the same ...,
automatically modify the todo list of rebase -i so that the commit
marked for squashing comes right after the commit to be modified,
and change the action of the moved commit from pick to squash (or
fixup).
The difference between squash and fixup is that during the rebase, the squash
operation will prompt you to combine the messages of the original and the squash commit, whereas the fixup
operation will keep the original message and discard the message from the fixup commit.
Upvotes: 256
Reputation: 8333
I tinkered with git extensions and couldn't get it to squash many commits into one. To do that, I had to resort to the command line and found this post helpful
git rebase -i Head~2
This is interactive rebase, and note the following:
Upvotes: 1