3

I have two branches in github preprod and production, both are protected and enforce linear history that means all the changes to that branches needs to be done by "rebase/merge" or "squash/merge".

Every new feature added to the preprod branch is made by feature braches.

a          <-- production
 \
  b        <-- preprod
   \
    c -- d <-- featureA

When I merge featureA to preprod with squash/merge, 'c' and 'd' create a new commit in preprod.

a           <-- production
 \
  b -- e    <-- preprod
   \    ^
    \    \
     c -- d <-- featureA
       

After a couple of new features I merge preprod to production with the same method squash/merge. This end up looking like this.

a ------------ h     <-- production
 \              ^
  \              \
   b -- e -- f -- g  <-- preprod

After a couple of merges from preprod to production the branches had the same code but the commits history is different because the squash/merge create commits in production branch that are not in preprod, this is creating too many conflicts each time I do a new pull request, because is trying to merge all the changes from commits 'b','e','f','g','i' and 'j'. It looks like this.

a ------------ h ------- k     <-- production
 \              ^         ^
  \              \         \
   b -- e -- f -- g -- i -- j  <-- preprod

Update: as @matt mention correctly, this is the real graph

a ------------ h ------- k     <-- production
 \                       
  \                      
   b -- e -- f -- g -- i -- j  <-- preprod

This is getting worst over time, I need a way to sync preprod branch with production so in new merges the pull request only look for the new commits no for the onces already merged.

At this stage a can't do rebase/merge becase of the conflicts so I don't know what to do. Last time, to merge changes from preprod to production I was block by github becuase of the conflicts, I solved it following this guide

# in case branchA is not our current branch
git checkout branchA

# make merge commit but without conflicts!!
# the contents of 'ours' will be discarded later
git merge -s ours branchB    

# make temporary branch to merged commit
git branch branchTEMP         

# get contents of working tree and index to the one of branchB
git reset --hard branchB

# reset to our merged commit but 
# keep contents of working tree and index
git reset --soft branchTEMP

# change the contents of the merged commit
# with the contents of branchB
git commit --amend

# get rid off our temporary branch
git branch -D branchTEMP

# verify that the merge commit contains only contents of branchB
git diff HEAD branchB

That solved the conflicts becase I can just use the version that is on preprod and ignore the conflicts.

I want to have this long live branches in sync and protected becase I have some github actions running the build and the tests and I think both branches need to be protected and have a linear history becase both are automatically deploy to its corresponding server after build and test passed. keeping linear history allow me to easily see the changes bewteen deploys, but merging from preprod to production is becoming a bottle neck.

Any idea how to solve this?

Simon Puente
  • 451
  • 1
  • 8
  • 21

2 Answers2

4

this is creating too many conflicts each time I do a new pull request, because is trying to merge all the changes

Yup, well, long-lived branches and squash merges are opposites, for this very reason. You've picked a bad way to go. There is a reason why real merges exist: they move the merge-base for future merges. You are not taking advantage of that fact.

Your diagrams are wrong and may be misleading you. You have, for example:

a ------------ h ------- k     <-- production
 \              ^         ^
  \              \         \
   b -- e -- f -- g -- i -- j  <-- preprod

No. The line between g and h, the line between j and k, is phoney. The reality is:

a ------------ h ------- k     <-- production
 \          
  \           
   b -- e -- f -- g -- i -- j  <-- preprod

There is absolutely no relationship, topological, historical, or otherwise, between g and h, or between j and k. On the contrary, h and k are created mysteriously out of the blue. But they are still formed using merge logic. The merge base used for that merge logic never moves: it will always be a. And so, as life goes on, if preprod stays alive and you try to do this again and again, it will become harder and harder to do, as reflected in the ever growing number of conflicts.

To sum up: what you're doing is not the purpose for which rebase/squash "merges" are designed. These are not merges. In my opinion you should give up your goal of "linear history" — it isn't history at all, that's the whole problem. You should embrace true merges.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • you right that's the real graph, I just wanted to be a little more explicit, I'll update the question, if I remove liner history am I still able to keep long live branches? and the CI I have setup with no problem? – Simon Puente May 19 '21 at 19:45
  • 2
    Your whole issue here is the pretense that `prod` is a "branch" in some meaningful sense. It isn't. It is merely some of the commits of `preprod`, without regard to any history. Nothing links `a` and `h`; `h` did not "grow" out of `a` in any historical way. That gets worse over time, which is what you now complaining about. What _most_ people do, I think, is use occasional tags on one branch, and not a separate pair of branches at all. In other words, just have a branch `prod` (instead of `preprod`) and when you want to call something a release, tag it. – matt May 19 '21 at 19:54
  • sounds good, thanks, I'll try that, I just need to know how to trigger a deploy based on tags instead of pushes. – Simon Puente May 19 '21 at 19:59
  • 1
    Or just use true merges. I really don't see what your objection is. If you merge `prod` into `preprod`, possibly preceded by a reverse merge, you get a perfectly decent history. – matt May 19 '21 at 20:21
  • no objection, I just wanted to understand it better, I just removed the linear history constraint, everything now goes better now, thanks – Simon Puente May 19 '21 at 20:52
0

From what I understand of your process, I would either:

  • not squash when merging preprod to prod, this would still give you a linear history assuming no commit ever happen on prod branch directly ?
  • regularly drop the preprod branch and recreate it from prod
  • or optionally regularly rebase preprod on prod but this will be slightly more complex for not so much benefit

With a strong preference for 1st option but this is debatable.

Gaël J
  • 11,274
  • 4
  • 17
  • 32