41

I can find a few answers out there that come close to what I want, but I'm afraid I'm not really experienced enough with Git to exactly understand how to achieve what I want.

Given a local branch with commits A-B-C-D-E-F-G-H-I-J on it, I want to squash some of the commits together so that I end up with, for example, the equivalent of A-BCD-E-FGH-IJ.

Is this possible? The intention is to do this to a local branch, after rebasing, but before merging back into the master branch, in order to create a more concise and informative history.

Martin
  • 1,289
  • 3
  • 13
  • 22
  • Possible duplicate of [Git squash commits in the middle of a branch](https://stackoverflow.com/questions/39023360/git-squash-commits-in-the-middle-of-a-branch) – nwinkler Oct 18 '18 at 10:59

2 Answers2

87

You can do this with rebase. Assuming commits A–J are on a local branch branchname built on top of master, then you can do this:

git checkout branchname
git rebase -i master

You'll be presented with an interactive window like this:

pick A Commit message A
pick B Commit message B
pick C Commit message C
pick D Commit message D
pick E Commit message E
...

Interactive rebase provides instructions for your scenario, so you should change "pick" to "squash" for C and D (and the same for G, H and J):

pick A Commit message A
pick B Commit message B
squash C Commit message C
squash D Commit message D
pick E Commit message E

This will squash B, C and D into a single commit, allowing you to change the commit message.

If you're happy to use the commit message of B, you can use "fixup" instead of "squash" and you won't be prompted to update the commit message.


The interactive window is very powerful, and you can reorder commits as much as you want, or even just delete the line (or change the prefix to "drop") and that commit will be removed. For instance, if E fixes a typo that you made in A, you can do this:

pick A Commit message A
fixup E Commit message E
pick B Commit message B
pick C Commit message C
pick D Commit message D

However, when you reorder commits, you run the risk of having to resolve conflicts along the way. You can always use git rebase --abort to return to your original state.

Wes
  • 2,100
  • 1
  • 17
  • 31
cmbuckley
  • 40,217
  • 9
  • 77
  • 91
  • 4
    what if I want to squash commit E to A, but B,C,D has to be still remain? – adrianriyadi Apr 15 '18 at 13:11
  • 12
    You'd need to reorder the commits so that E immediately follows A. The interactive rebase presents you with an editor, which allows you to do this; simply move line E between lines A and B, and change `pick E` to `squash E` just the same. Because you're altering commit history here, you may need to deal with conflicts. – cmbuckley Apr 15 '18 at 16:47
  • Is there a way to suppose squash C into A and not the commit before that i.e. B? @cmbuckley – RamPrasadBismil Oct 22 '19 at 21:15
  • @RamPrasadBismil that's the same question as adrianriyadi has asked - you need to reorder the commits. – cmbuckley Oct 23 '19 at 09:23
  • I did the first 2 commands on the top of your suggestion @cmbuckley. But the 2nd comment didn't work... could it be "git rebase -i origin/master" instead? – LED Fantom Jul 08 '21 at 16:34
  • @LEDFantom notice the first paragraph before those commands — it depends what branch you built from. You should also know that `origin/master` is a remote branch, `master` is a local branch, and both are just references to commits. You can use any commit as the upstream, provided it is contained within the branch you want to rebase. – cmbuckley Jul 08 '21 at 23:15
7
git log --oneline
git checkout your-branch
git rebase -i HEAD~3

write your comments, then :wq(write and quit) and press Enter.

git log --oneline

enter image description here

oiyio
  • 5,219
  • 4
  • 42
  • 54