1

One thing I don't like with Git is the amount of commits. Don't get me wrong, it's a very convenient method of storing (safely) your work but in the long run there is too much granularity to search into.

Let me describe the following scenario while developing with feature branches branched from develop.

  • Feature1 is branched from develop and has 5 commits
  • Feature2 is branched from develop and has 3 commits
  • Feature3 is branched from Feature1 and has 5+2 commits.

The reason there are three branches is because there is a team working on the repository. Feature3 assumes the Feature1 is done, waiting to be merged into develop. There are other use cases for this but this is the simplest example.

My goal is to have three commits on develop when all features are done.

Using git merge/pull

The above scenario works well. Because in essence both commands move commits into the develop branch. When Feature1 is merged, develop references it's five commits. When Feature3 is going to be merge, only the last two commits will be merged into develop.

The only negative aspect of this is that develop gets too many commits. That means that when merging into master all that "noise" moves along. Therefore my goal is not achieved.

Using git merge with squash

When Feature1 is merged, develop gets a new commit which is the aggregation of all Feature1. This is very nice, as the feature is now tracked with only one commit. When Feature1 is removed, all intermediate commits are not visible any more making tracking easier. This also matches with concepts such as one pull request and one github issue.

When Feature3 is going to be merged there are conflicts and the problem starts. To solve this, you need to merge the develop back to feature3. with conflicts. This results to a new commit that has zero files changed. And now we merge squash Feature3 to develop.

My goal is achieved but with a lot of micro-management sacrifice.

Questions/Remarks

As far as I understand this is the git reality. One advice I've read is to create a sibling feature branch that holds the squashed merge commit. Use the sibling branch for the pull request therefore enforcing the usage of one commit per feature. This doesn't solve the conflicts created when trying to merge the other branches in, especially the ones inheriting from other feature branches.

  • Can a do an internal squash within the same branch? I don't think so, but doesn't hurt to ask.
  • I'm I missing something?
  • Is this the trade-off of squash merge? Resolving conflicts that potentially have zero code changes?
  • Is it worth while to go after the "one commit per feature" concept?
    • If not, then what's the purpose of squash? Who is using this?

I've read alternative with rebase and simulating the squash with reset after merge, but as far as I understand none solves the overhead mentioned to merge feature3.

Alex Sarafian
  • 634
  • 6
  • 17

2 Answers2

1

I really do think rebasing is your solution here.

If I understand, the following is your git structure:

develop \ A - B - C [feature1] \ D - E [feature3]

Your problem is once you've squashed and merged feature1 in to develop, the initial three commits feature3 references don't exist anymore. You're in essence left like the following, except feature1 doesn't really exist.

develop — feature1Merge \ A - B - C [feature1] \ D - E [feature3]

So the feature1Merge commit in develop is different to those in feature3's history.

In this case, if you tried to rebase feature3 back on to develop, commits A B and C would go with it, which in theory shouldn't lead to any problems as the changes are already on develop so git should just fast-forward those commits. Evidently, this doesn't seem to be happening though.

What I'd suggest doing in this situation is a rebase onto.

Rebase Onto

The --onto option allows you to rebase specific commits, rather than a whole branch.

So to stop git trying to use commits A B and C when rebasing, we use --onto, which takes 3 arguments:

  • The new base for the branch

  • The current base of the branch

  • The name of the branch to rebase

In your case, this would be:

git rebase --onto develop C feature3

This would check out develop and then apply all commits on feature3 since C:

develop — feature1Merge \ D - E [feature3]

Hopefully this all makes sense and achieves what you want with as little hassle as possible!

Matthew Hallatt
  • 1,310
  • 12
  • 24
  • I don't think the `git rebase --onto` works. ``` dev - F [dev+squashed feature1] \ A B C [Feature1] \ E D [Feature3] ``` If I execute `git rebase --onto dev feature1 feature3` I get > fatal: Needed a single revision > Does not point to a valid commit: dev After reading a bit more about it, I understand as commits A,B,C are not references by the dev branch since the F commit is the accumulation (squash) of them. – Alex Sarafian Feb 26 '17 at 13:14
0

Yes, the purpose for git with so many commits is to make every changes trackable. If some commits are not significant or it’s ok to record all the changes in one commit for you review the history, of cause, you can squash these commits.

For your questions:

1.Yes, you can do an internal squash. Assume your git commit history as the graph below:

                K---L---M        Feature2
              /
A---B---…---C---…                develop
     \         
      D---E---F---G---H          Feature1
                        \
                          I---J  Feature3

You can use these steps to squash each branch:

git checkout Feature1
git rebase -i HEAD~5

input i

pick D
squash E
squash F
squash G
squash H

press Esc button and then input :wq

git checkout Feature3
git rebase -i HEAD~7
pick D
squash E
squash F
squash G
squash H
squash I
squash J
git rebase Feature1
git checkout Feature2
git rebase -i HEAD~3
pick K
squash L
squash M
  1. Your process is ok.
  2. To squash commits, the first (oldest) should be use pick, and others can use squash so that it will show the branch with only one commit. If there has conflict, you can modify and save the conflict files, and then use git add . and git rebase --continue.
  3. It’s not necessary and it depend on your favorite. The main purpose of squash is make the history looks more tidy and clearly.
Marina Liu
  • 36,876
  • 5
  • 61
  • 74