29

I have a branch with about 20 commits.

The first SHA on the branch is bc3c488...
The last SHA on the branch is 2c2be6...

How can I merge all the commits together?

I want to do this without using interactive rebase as there are so many commits.

I need this for a github Pull Request where I am being asked to merge my commits.

Need to do this without doing a git merge --squash as I need to squash locally and another developer does the merge and wants me to do the squash first before merging.

Michael Durrant
  • 93,410
  • 97
  • 333
  • 497
  • 9
    `git reset --soft ${FIRST_SHA}` then make a new commit will squash them all. I've got a longer answer somewhere but search isn't finding it. – Andrew C Nov 24 '15 at 19:28
  • 1
    **FIRST** here refers to an _older_ commit? and **SECOND** refers to a _newer_ (more recent) commit? – Felipe Alvarez Mar 19 '17 at 10:05

4 Answers4

8

If the first SHA is HEAD you can also use this approach:

git reset --soft $OLD_SHA; git add -A; git commit --amend --no-edit

be careful, this command will change the history of the repo.

If you want to squash commits that are in the middle of your history:

|---* --- 0 --- 1 ---- 2 --- 3 --- * --- * --- * --- HEAD

like in this case the commits 1, 2 and 3

I would really recommend to use rebase -i

Elazar
  • 20,415
  • 4
  • 46
  • 67
IgnazioC
  • 4,554
  • 4
  • 33
  • 46
2

The first SHA on the branch is bc3c488...
The last SHA on the branch is 2c2be6...

# non dangerous implementation that creates a new branch.
git checkout 2c2be6
git rebase -i bc3c488~
git checkout -b your_new_squashed_branch
# then squash the commits by replacing the pick with s

The tilda (~) at the end of the commit means previous commit;
Ensuring commit bc3c488 is visible on the interactive rebase.

# dangerous implementation that rewrites history
git checkout -b new_branch_with_rewritten_history
git reset --hard 2c2be6
git rebase -i bc3c488~
# then squash the commits by replacing the pick with s

Alternatively this answers the question:

git checkout master; 
git merge --squash yourbranch;
git checkout yourbranch;
git reset --hard master # warning this is destructive.

Alternatively if you cant trust master branch...:

git checkout 2c2be6~; 
git checkout -b commit-before-your-branch
git merge --squash yourbranch;
git checkout yourbranch;
git reset --hard commit-before-your-branch # warning this is destructive.

reflog can help you recover if you lose a commit sha.

Michael Dimmitt
  • 826
  • 10
  • 23
  • There is no risk to squashing consecutive commits. you should not be afraid of this, if your gripe is number of keystrokes, vim + visual block can solve in less than 6 keystrokes. Also this is the safest method as it adds visibility. – Michael Dimmitt Jun 07 '22 at 21:40
  • also added the alternative solutions, but they are worse than rebase -i – Michael Dimmitt Jun 07 '22 at 21:40
1

Interactive rebasing can help here.

Let's say your branch is based off of your upstream's master branch. (and let's say your upstream is defined by the "upstream" remote)

Do this:

git rebase -i upstream/master

"upstream/master" can be replaced by any SHA if you need to be more exact.

    git rebase -i bc3c488

You will be placed in an editor defined by your $EDITOR environment variable. Change "pick" to "squash" (or "s" for short) for every line except the very top one. This is squashing all those commits into one.

As with any merge or rebase against another line of work there is the possibility of a code conflict. If this occurs, do a "git status" to see which files are conflicted, edit those files (the conflict will be delimited with <<< and >>> symbols) and do a "git rebase --continue"

Rebasing replays commit's one at a time, keep this in mind if you find yourself fixing the same conflict over and over again (there are tools to help with this as well).

You will then be presented with an opportunity to edit the commit message for your new squashed commit.

Push to your remote branch with -f (this re-writes history which you want, but be careful with this one).

This is a pretty good tutorial on interactive rebasing: https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase-i

Jonathan.Brink
  • 23,757
  • 20
  • 73
  • 115
  • 1
    I recommend this one be marked as the accepted answer. It has the most detail and is probably the safest way to accomplish the task asked in the question. – John Chesshir Jun 26 '20 at 21:43
1

you can use the interactive shell in git rebase to selectively pick which commits to rebase.

git rebase -i bc3c488...

then change the commits that you want to squash to say squash instead of pick

checkout https://ariejan.net/2011/07/05/git-squash-your-latests-commits-into-one/

pwilmot
  • 586
  • 2
  • 8