Combining multiple commits before pushing in Git [duplicate]
Asked Answered
G

8

508

I have a bunch of commits on my local repository which are thematically similar. I'd like to combine them into a single commit before pushing up to a remote. How do I do it? I think rebase does this, but I can't make sense of the docs.

Galagalactagogue answered 4/8, 2011 at 0:4 Comment(5)
For rebasing/squashing an arbitrarily large number of commits, see my answer to "Squash/combine/rebase an arbitrarily large number of commits".Lyte
Related How can I squash my last X commits together using git?.Lyte
"I can't make sense of the docs" your not aloneRegulate
in general you should think twice before you do that. more granular (smaller) commits have many practical benefits. You could instead put them all in a separate branch and merge that branch into the main branch with git merge --no-ff myOtherBranch, if you need to group them. alternatively, you could use a commit message prefix or a tag in the commit messages. git history is not the public changelog, but the "internal" structure with which the devs work.Clad
Related: #26972227Disappear
A
782

What you want to do is referred to as "squashing" in git. There are lots of options when you're doing this (too many?) but if you just want to merge all of your unpushed commits into a single commit, do this:

git rebase -i origin/master

This will bring up your text editor (-i is for "interactive") with a file that looks like this:

pick 16b5fcc Code in, tests not passing
pick c964dea Getting closer
pick 06cf8ee Something changed
pick 396b4a3 Tests pass
pick 9be7fdb Better comments
pick 7dba9cb All done

Change all the pick to squash (or s) except the first one:

pick 16b5fcc Code in, tests not passing
squash c964dea Getting closer
squash 06cf8ee Something changed
squash 396b4a3 Tests pass
squash 9be7fdb Better comments
squash 7dba9cb All done

Save your file and exit your editor. Then another text editor will open to let you combine the commit messages from all of the commits into one big commit message.

Voila! Googling "git squashing" will give you explanations of all the other options available.

Allsopp answered 4/8, 2011 at 0:27 Comment(12)
By origin/master, do you mean <upstream-branch>? If the upstream branch is configured, is it implicit or must I still specify it?Tasia
That's a cool way to reduce the number of commits, but what if I just want to delete one subcommit, for example: delete "c964dea Getting closer" this subcommit in the commit, Is it possible?Appomattox
Use fixup instead of squash to skip the step of creating a new commit message. The last commit message("All done") will be used automatically for the final commit created by rebase.Forbearance
@Appomattox you can remove the changes from a commit simply by deleting that line.Allsopp
Anyone know if you can have a multiple lined commit msg in your "pick" line, or will it confuse the parser?Argentous
good! you could also use s instead of squashSuzannesuzerain
@Allsopp Does deleting line to "remove the changes" from that commit retain changes to be organized into another squashed commit? Should squash be used to reorganize local commits in that way before pushing upstream?Bismuthinite
@Allsopp Let's assume I'm working on a repo and there are no remote repos, and that I want to combine multiple arbitrary commits into a single commit. The ones I want to combine are not necessarily contiguous. How would I do this?Swordfish
For Windows users Save your faile and exit in VIM is hard. You need key combination ESC key , then type :wq then press Enter. You will need to do same for second text file too, which will be loaded after squashing.Pash
git rebase -i @{u}.. in case your upstream isn't origin/master.Belgium
@TPAKTOPA: For unix users: Save your file and exit in Windows editors is hard. You need about 3 different mouse clicks; alternatively, most editors accept the standard XOFF byte (Ctrl+S) in order to save the file (!). Closing programs is usually possible through the "File" (!) menu, or you press a combination of Alt+F4 (Alt Gr won't work). Make sure you don't have the desktop selected when you do that because it would try to end your Windows session (!). If you are good with mouse aiming you can also try to hit the little "x" button top right.Fecula
What if there are 500 pick?Hydric
S
128

If you have lots of commits and you only want to squash the last X commits, find the commit ID of the commit from which you want to start squashing and do

git rebase -i <that_commit_id>

Then proceed as described in leopd's answer, changing all the picks to squashes except the first one.

Example:

871adf OK, feature Z is fully implemented      --- newer commit --┐
0c3317 Whoops, not yet...                                         |
87871a I'm ready!                                                 |
643d0e Code cleanup                                               |-- Join these into one
afb581 Fix this and that                                          |
4e9baa Cool implementation                                        |
d94e78 Prepare the workbench for feature Z     -------------------┘
6394dc Feature Y                               --- older commit

You can either do this (write the number of commits):

git rebase --interactive HEAD~[7]

Or this (write the hash of the last commit you don't want to squash):

git rebase --interactive 6394dc
Saba answered 22/1, 2014 at 9:28 Comment(3)
It took me some time to figure out, but it seems <that_commit_id> itself is NOT going to be squashed.Psychosocial
It should say "find the commit ID of the commit just prior to the set of commits you want to squash into one".Ladoga
Is there something special in this? I have a PR which has 5 commits - including one merged merge-conflict fix - and if I specify the commit ID of the first commit in my PR log, I only get some random unrelated commits in the rebase pick/squash list.Kropotkin
B
56

There are quite a few working answers here, but I found this the easiest. This command will open up an editor, where you can just replace pick with squash in order to remove/merge them into one

git rebase -i HEAD~4

where, 4 is the number of commits you want to squash into one. This is explained here as well.

If you are looking to squash the commits which also includes the very first commit, then use this command

git rebase -i --root
Bhakti answered 5/10, 2016 at 23:16 Comment(0)
S
35

You can do this with git rebase -i, passing in the revision that you want to use as the 'root':

git rebase -i origin/master

will open an editor window showing all of the commits you have made after the last commit in origin/master. You can reject commits, squash commits into a single commit, or edit previous commits.

There are a few resources that can probably explain this in a better way, and show some other examples:

http://book.git-scm.com/4_interactive_rebasing.html

and

http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html

are the first two good pages I could find.

Sorrento answered 4/8, 2011 at 0:8 Comment(1)
The last link helped out a lot! It goes into the details of squashing commits, made my day thanks, stranger.Blanketing
A
18

I came up with

#!/bin/sh

message=`git log --format=%B origin..HEAD | sort | uniq | grep -v '^$'`
git reset --soft origin
git commit -m "$message"

Combines, sorts, unifies and remove empty lines from the commit message. I use this for local changes to a github wiki (using gollum)

Annetteannex answered 27/3, 2015 at 20:55 Comment(2)
This is a very clever idea indeed!Bromleigh
For some reason (maybe it's my config or the way I use GIT), origin..HEAD gave me an ambiguous argument error. To make sure it's always fetching the difference with the current branch's head, I needed to adapt the first line with the git log to this git fetch && git log --format=%B FETCH_HEAD..HEAD [...] Fetching first makes sure that GIT has the latest knowledge needed and creates a FETCH_HEAD ref that we can use.Heldentenor
T
11

You can squash (join) commits with an Interactive Rebase. There is a pretty nice YouTube video which shows how to do this on the command line or with SmartGit:

If you are already a SmartGit user then you can select all your outgoing commits (by holding down the Ctrl key) and open the context menu (right click) to squash your commits.

It's very comfortable:

enter image description here

There is also a very nice tutorial from Atlassian which shows how it works:

Twinge answered 10/11, 2015 at 17:1 Comment(0)
S
8

And my way of squashing multiple push is (perhaps you pushed to your own branch many commits and now you wish to do a pull request and you don't want to clutter them with many commits which you have already pushed). The way I do that (no other simpler option as far as I can tell is).

  1. Create new branch for the sake of squash (branch from the original branch you wish to pull request to).
  2. Push the newly created branch.
  3. Merge branch with commits (already pushed) to new branch.
  4. Rebase new branch and squash.
  5. Push new branch.
  6. Create new pull request for new branch which now has single commit.

Example:

git checkout from_branch_you_wish_to_pull_request_to
git checkout -b new_branch_will_have_single_squashed_commit
git push -u new_branch_will_have_single_squashed_commit
git merge older_branch_with_all_those_multiple_commits
git rebase -i (here you squash)
git push origin new_branch_will_have_single_squashed_commit

You can now pull request into from_branch_you_wish_to_pull_request_to

Saintmihiel answered 15/11, 2015 at 11:20 Comment(1)
Instead of merge + rebase you could also do mege --squash. I've used it recently for the exactly same use case: create a new branch from develop, merge --squash the feature branch and give it a meaningful commit message. https://mcmap.net/q/12643/-how-can-i-merge-multiple-commits-onto-another-branch-as-a-single-squashed-commitEuphrasy
B
3

You probably want to use Interactive Rebasing, which is described in detail in that link.

You can find other good resources if you search for "git rebase interactive".

Benoit answered 4/8, 2011 at 0:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.