Search code examples
gitversion-controlgit-mergegit-squash

Git Merge simple vs Squash. What is the purpose? How to track one commit per feature?


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.


Solution

  • 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.