Search code examples
git

Rebase sub branch from main after main got a squashed merge


This is probably a very common scenario but to the best of my efforts I couldn't find any proper answer on it using Google search or use of an LLM chatbot.

This is a team project, I was developing on my sub branch called "devel". I did 6 commits, finally reached a stage where I can push to production so I pushed my changes. On the main branch, using Gitlab's GUI, I created a squashed merge request.

I continued developing on the same branch locally, did a fix, pushed it, created another merge request but Gitlab blocked the merge with this:

gitlab

So I pulled origin main, tried ff which fails, then tried rebase, merge and finally cherry pick but the all of them want to do 6 stages of rebase/merge/cherry-pick!!! This is totally unexpected. How do I make it understand to not apply those squashed merges from main to my devel branch? Do I have to delete the devel branch at this point and create a new branch to continue with my development? I'd like to have my commit history though.

So my question is how to resolve the Gitlab error and simply make a proper merge request to apply the last commit on devel branch to main branch.


Solution

  • Avoid squash merging if possible. As you found out, it mostly causes headaches. But all is not lost.

    Let's say you start with the following history graph, 2 commits only reachable from main and 4 commits on your devel branch:

    A-B-C-D             < main
       \
        E-F-G-H         < devel
    

    Next you create a merge request and squash merge. This gives you a new commit, that I call S (short for "squash"). It contains all changes from commits E, F, G, and H.

    A-B-C-D-S           < main
       \
        E-F-G-H         < devel
    

    Then you keep working on your branch and add a few more commits:

    A-B-C-D-S           < main
       \
        E-F-G-H-I-J-K   < devel
    

    You cannot (squash) merge that again, because the changes have already been partially applied to main. Attempting a simple rebase will also fail because S contains the changes of E (and F and G and H), so E cannot be applied again – that would be like applying E on top of H.

    But you can help Git by telling it what the old upstream was:

    git rebase --onto main H devel
    

    This command will take the commit range H..devel (i.e. I, J, K) and apply them on top of main. This should give fewer conflicts and avoids re-applying patches that already exist upstream. History after the rebase:

    A-B-C-D-S           < main
       \     \
        \     I'-J'-K'  < devel
         \
          E-F-G-H-I-J-K