153

I have a branch 'firstproject' with 2 commits. I want to get rid of these commits and make them appear as a single commit.

The command git merge --squash sounds promising, but when I run git merge --squash my terminal just brings up options for the command. What is the correct command?

Commit 1:
Added 'homepage.html'
Added 'contacts.html'

Commit 2:
Added 'homepage.php'
Added 'homepage.php'
Deleted 'homepage.html'
Deleted 'contacts.html'
user229044
  • 232,980
  • 40
  • 330
  • 338
Don P
  • 60,113
  • 114
  • 300
  • 432
  • I don't think that's the right command. Also, I am doubting that you even can change commits like that. – BSull Sep 21 '12 at 00:00
  • 1
    Nah you can change multiple commits into one commit: http://git-scm.com/book/en/Git-Tools-Rewriting-History my question is just about the specific command for it. – Don P Sep 21 '12 at 00:02
  • @BSull You absolutely can changes commits in any way you want. They are completely editable at literally any time. If you're sharing code with others, doing so will rewrite your history and you'll effectively be working with a different branch than everybody else. – user229044 Sep 21 '12 at 02:26
  • This should have never been duped. In fact, that other one should have been closed as too broad. The other question this is marked as a dupe of is needless complex as it involves abandoning a prior rebase. – Evan Carroll Nov 04 '19 at 04:11

5 Answers5

221

You want to git rebase -i to perform an interactive rebase.

If you're currently on your "commit 1", and the commit you want to merge, "commit 2", is the previous commit, you can run git rebase -i HEAD~2, which will spawn an editor listing all the commits the rebase will traverse. You should see two lines starting with "pick". To proceed with squashing, change the first word of the second line from "pick" to "squash". Then save your file, and quit. Git will squash your first commit into your second last commit.

Note that this process rewrites the history of your branch. If you are pushing your code somewhere, you'll have to git push -f and anybody sharing your code will have to jump through some hoops to pull your changes.

Note that if the two commits in question aren't the last two commits on the branch, the process will be slightly different.

user229044
  • 232,980
  • 40
  • 330
  • 338
  • 3
    How to do `Then write your file, and quit`? I'm using Android Studio Terminal, git console itself Or even opened git console by SourceTree software. But how to save and quit? – Dr.jacky Sep 16 '17 at 06:54
  • 3
    How to combine 2 commits that aren't the last two commits on the branch? – Weishi Z Sep 27 '17 at 19:15
  • Does it make any difference if I want to squash the 2nd into the 1st instead of the 1st into the 2nd as you describe? – PandaWood Jul 28 '18 at 04:21
  • @PandaWood that doesn't really make sense. A squashed into B is the same thing as B squashed into A. `(1 + 2) = (2 + 1)`. – user229044 Jul 28 '18 at 13:57
  • 1
    @meagar I think it does make sense... if you check this issue (a duplicate of this), squashing 'direction' made a big difference and was the root of the entire issue (see the first comment with around 200 upvotes about 'squashing the wrong way') - https://stackoverflow.com/a/2568581/43453 – PandaWood Jul 30 '18 at 03:20
  • 1
    @PandaWood You're misreading the answer. The first way was simple *wrong*, not *different*. You can *only* squash backwards, it makes no sense to think of squashing an older commit into a newer one, but if you *could* it would be the same operation. – user229044 Jul 30 '18 at 03:47
  • 1
    I understood up to " change the first word of the second line" - second line of what? – Andrew Torr Feb 25 '19 at 21:26
  • What do you mean by "anybody sharing your code will have to jump through some hoops to pull your changes"? – PlsWork Aug 16 '21 at 11:23
110

Lazy simple version for forgetfuls like me:

git rebase -i HEAD~3 or however many commits instead of 3.

Turn this

pick YourCommitMessageWhatever
pick YouGetThePoint
pick IdkManItsACommitMessage

into this

pick YourCommitMessageWhatever
s YouGetThePoint
s IdkManItsACommitMessage

and do some action where you hit esc then enter to save the changes. [1]

When the next screen comes up, get rid of those garbage # lines [2] and create a new commit message or something, and do the same escape enter action. [1]

Wowee, you have fewer commits. Or you just broke everything.


[1] - or whatever works with your git configuration. This is just a sequence that's efficient given my setup.

[2] - you'll see some stuff like # this is your n'th commit a few times, with your original commits right below these message. You want to remove these lines, and create a commit message to reflect the intentions of the n commits that you're combining into 1.

ks1322
  • 33,961
  • 14
  • 109
  • 164
Stachu
  • 5,677
  • 3
  • 30
  • 34
  • 4
    Very concise solution - thank you. Might be handy to outline that `s` used here stands for squash. To use the commit, but meld into the previous commit – UrbanConor Aug 08 '19 at 15:07
  • 2
    If you have just `2` commits and want to combine the same `2` commit, you will need to run `git reset --soft HEAD~` and `git commit --amend`. – scrutari Oct 22 '20 at 15:00
  • @Stachu When I command push after this, It will ask for pull and merge. What I can do to avoid that? – Abhay Koradiya Dec 15 '20 at 15:50
  • Why wouldn't you want to pull and merge? – Stachu Dec 15 '20 at 21:35
  • If you want to keep the commit message for `pick YourCommitMessageWhatever` and just merge the other commits into it, you can replace `s` with `f` (fixup). Don't forget to make sure your commits are in the correct order, so you don't get unexpected results! (use `git log -p` afterwards to see the log with changes) – Eduardo06sp Aug 28 '22 at 23:41
31
  1. Checkout your branch and count quantity of all your commits.
  2. Open git bash and write: git rebase -i HEAD~<quantity of your commits> (i.e. git rebase -i HEAD~5)
  3. In opened txt file change pick keyword to squash for all commits, except first commit (which is on the top). For top one change it to reword (which means you will provide a new comment for this commit in the next step) and click SAVE! If in vim, press esc then save by entering wq! and press enter.
  4. Provide Comment.
  5. Open Git and make "Fetch all" to see new changes.

Done

JGallardo
  • 11,074
  • 10
  • 82
  • 96
user5820972
  • 421
  • 4
  • 5
17

This is what I do. It's easier(for me) than doing an interactive rebase:

git reset HEAD~2.  // go back 2 commits.
git commit -am “My squashed single commit”
git push --force 
RayLoveless
  • 19,880
  • 21
  • 76
  • 94
  • 3
    This should be the accepted answer!! or at least get more votes! All other solutions are so complex, and require mass typing frenzy, that a GUI software is better off used (such as sourcetree or tortoise) where you could get the same result only with a few mouse clicks. Your solution, however, is the cleanest, fastest, and great for solo projects, or team projects where you're on a side branch of your own. – Tal Kohavy May 08 '22 at 22:05
  • 2
    `git reset HEAD~2.` is not a valid command – tala9999 Sep 23 '22 at 20:04
  • @tala9999 What do you mean ? I just used it sucessfully – cassepipe Aug 04 '23 at 14:02
  • What you want is `git reset --soft`. With this solution you'd have to re-add changes to the staging area before making your commit. See: https://stackoverflow.com/a/61171280/10469162 – cassepipe Aug 04 '23 at 14:04
0

Interactive rebase and merge with reset was mentioned earlier. Another approach is more safe for me because doesn't use all previous things (interactive and reset) and doesn't interact into exist data. It uses git merge --squash, like the topic starter suggested. It creates a new branch and merge with squash into it:

git branch <result_branch_name> <point from where you want to merge commits>
git checkout <result_branch_name>
git merge --squash <branch_with_all_your_commits>

As a result you will receive staged changes from all commits between point, where you created the result branch, and the final commit. The commit message will contain all messages from all squashed commits. Then just enter git commit, modify the commit message and you will get the only commit with all necessary changes. Original branch can be deleted with git branch -D <branch_name>, when you are ready to say it good bye =)

Val
  • 61
  • 1
  • 3