1

Sometimes I have a bunch of small commits in a feature branch, and I'd like to just squash them all together, but not merge into the parent branch yet.

I know I can (in this example parent branch is master):

git rebase -i master

and then tag all of the commits after the first as "squash".

Is there a way to get the same result non-interactively?

Essentially I want to create a new commit whose tree is identical to the tree now at the head of my feature branch and whose parent is the ancestor commit that I've specified (eg: master), and then change the feature branch to point at that new commit. There should never be any conflicts (I occasionally get some with rebase -i), and with -m should be completely non-interactive. Ideally, without -m the commit message should default to a concatenation of the commits being squashed.

How can I do this?

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299

1 Answers1

4

My take to squash is to use git reset --soft. Say you want to squash the last 5 commits:

git reset --soft HEAD~5
git commit -m "Feature X"

And if you also want to rebase all of that done in a (almost) single shot, you can still do it with git reset --soft

How to squash/rebase in a single shot

eftshift0
  • 26,375
  • 3
  • 36
  • 60
  • Is it possible to use the parent branch name in place of counting commits? eg: can I "`git reset --soft master`", or will that have some unintended side-effect? – Laurence Gonsalves Mar 22 '19 at 17:59
  • You mean, in the basic recipe to "only" squash? You could try using a trick to find the last common ancestor: `git reset --soft $( git merge-base HEAD master )` (if master is your parent branch). That way you don't need to count revisions – eftshift0 Mar 22 '19 at 18:02
  • I mean in the case where `master` is an ancestor already (ie: `git rebase master` would do nothing), and I just want to squash everything between `master` and `HEAD`, but remain on `current-branch`. Wouldn't `git reset --soft master` just move `current-branch` to point at the same commit as `master`, and then we can commit everything in the working tree to make our "squashed" commit? Or does using `master` in place of `HEAD~5` trigger some other behavior in `git reset`? – Laurence Gonsalves Mar 22 '19 at 18:41
  • Wait.... what? `git rebase master` would _definitely_ do something if master has moved past the point where you started development of the feature branch (as in: both branches diverged). `git reset --soft master`would take it to the right spot in order to squash _**iif** master hasn't moved after the feature branch started_. Given that I don't know, i didn't make that assumption and that's why I provided the merge-base command to find that point where the branches _might have_ diverged. – eftshift0 Mar 22 '19 at 18:50
  • Once master has "moved past the point where you started development of the feature branch" it isn't an ancestor of the feature branch anymore, is it? – Laurence Gonsalves Mar 22 '19 at 19:02
  • Ok.... so you are being _strict_ when you mean that _master is an ancestor to your branch_. Then "in that case git rebase master would do nothing". That's correct. Just in case: I couldn't make that assumption on my response.... plus you were talking about rebasing on the question **headline**.... that makes my assumption that master moved even more compelling... so I can't understand why you don't understand that I assumed that master had moved. – eftshift0 Mar 22 '19 at 19:08
  • Just to answer the case where master hasn't moved: **Yes, you can say `git reset --soft master` if master hasn't moved.** – eftshift0 Mar 22 '19 at 19:11
  • Yes, I agree the question title probably muddied the waters somewhat. Sorry, I'll try and clear that up. The reason rebase is used in the title is because (as stated in the question) `git rebase -i master` can do what I want, except that it's interactive. I guess the real non-interactive substitute is to `git rebase master` first, then do what you stated in your answer. – Laurence Gonsalves Mar 22 '19 at 19:35
  • Looking at the title more, it's actually pretty accurate: "is there a way to non-interactively rebase (yes: `git rebase master`) and squash (yes: your answer, but you can use the parent branch name)" – Laurence Gonsalves Mar 22 '19 at 19:40