Search code examples
gitmergegit-merge

How to achieve simplified merge-commits


Rational: In out environment review takes place on the branch that is here denoted as master>. I would like to squash (better: combine) commits together in order to have all changes to a certain development-step together. Thus:

How do I get from left to right?

                                                   master> * C <feature  
                                                           |\
            * H <feature   - just local                    | * H     (merged)
            |                                              | |
            * G     - pushed to remote          =??=>      | * G     (merged)
master> B * |       - pushed to remote                   B * |
          | * F     - pushed to remote                     | * F     (merged)
          |/                                               |/
          *                                              A *                

In reality the number of commits on the feature path is large (10+) and I'd like to squash them all. Parts of them are already published to the remote repository (and should not be changed or altered). However I just want to create one single commit on the master branch.

git merge --squash apparently performs this, but it does not does not mark the commits as merged. Thus if I continue work on <feature-Branch the next call to git merge will lead to lots of conflicts. I found an explanation on stackoverflow: git merge --squash leads to a patch unrelated to the source-commits.

So, what I want to achieve is to get from the right to the left situation and all commits on featurebranch (F, G and H) will be marked as merged*) - if I continue working on the Featurebranch next merge does not lead to conflicts with my own changes. This Question is solved, in case all Changes on the Featurebranch are not published to the remote repository (see git merge --squash-Question ).

PS: Of course it would be sufficient to mark the commits individually as merged, but I neither know how to do this, nor how git manages it's merge information.

PSS: Normal merge does it, but it fails iff there is no commit on master.

*): "marked as merged" means, that in gitk - iff you select the commit, the list of branches includes feature, remotes/origin/feature, master, remotes/origin/master. As result those commits wont be considered in subsequent call to git merge.


Solution

  • After some research I learned the following: git will select the method how to merge based on the state of the involved Branches - In the trivial (figure b) from my sketch below) git selects fast-foreward, thats when only one of the two branches has changes. In this case it might occure, that all changes from the feater-branch will be placed on top of the master branch.

    Off-topic: If there are changes on more than one of the involved branches (figure a) from my sketch below) git selects a merge-Strategie according to the current state of the involved branches and the git-configuration. In additon You migt use the commandline to enforce certain behavior (https://git-scm.com/docs/merge-strategies or http://gitbu.ch/ch03.html).

    With this knowlege there is an answer to my questions:

    Answer: favor --no-ff over --squash If ever I want to avoid "fast-forward" merge you should use

    git merge --no-ff --no-commit feature
    

    and after this you should syncronize feature to main

    git checkout feature
    git merge --ff-only master
    

    this brings you from left to right off the sketch in the Question AND each commit original made to the feature-Branch will be marked as Present on the master branch (This info is displayed in gitk when you select the commit).

    There might be another reason to avoid "fast-foreward": The achieved situation will document, wich changes are originally checked in to the feature-Branch and which are done to the main-Branch.

    Sketch to illustrate fast-foreward:

        a) real merge                          b) trivial - no chanes on master
    
                    * H <feature                           * H <feature
                    |                                      |
                    * G <feature/origin                    * G  <feature/origin
        master> B * |                                      |
                  | * F                                    * F 
                  |/                                      /
                  *                            master> A *                
    

    a) is the usual case whith commits on both sides where a mergestrategi is involved to resolve 'em. b) is the trivial case, where fast foreward is posible.

    Summary: In either case the given commands combines all the work commitet on the feature branch to a single commit on master without loosing the merge information.