Yes, it's called squashing. I usually use the interactive rebase feature for that.
First, find the commit before the oldest commit you want to be part of the resulting squashed commit:
$ git log --oneline
701c7f8 (HEAD -> master) commit c
4c0270a commit b
8ca1f6e commit a
bb0a92c (origin/master) previous commit
Then do git rebase -i <commit>
:
$ git rebase -i bb0a92c
Your configured editor will open up presenting you a list of the commits that are part of the rebase operation, starting with the oldest:
pick 8ca1f6e commit a
pick 4c0270a commit b
pick 701c7f8 commit c
# Rebase bb0a92c..701c7f8 onto bb0a92c (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
Change the first three lines to look like this:
pick 8ca1f6e commit a
squash 4c0270a commit b
squash 701c7f8 commit c
Or, actually, the first letter suffices:
pick 8ca1f6e commit a
s 4c0270a commit b
s 701c7f8 commit c
Save and exit the editor. Another editor instance will give you the opportunity to edit the commit message for the resulting squashed commit and the end result will be one commit encompassing the changes of commits 8ca1f6e
, 4c0270a
, 701c7f8
.