Gitflow practices: how to get the code from origin/develop to local/feature-x with respect to having good-looking git-history

Viewed 49

I have the following git structure: local/develop, local/feature-x, origin/develop, origin/feature-x, origin/feature-y... each local is tracking its corresponding origin, and there is another developer working on feature-y and pushes his code to origin/feature-y.

While I am working on feature-x and pushing my code to origin/feature-x for -let's say- several days, the other developer has done a Merge-Request to origin/develop, and now, I need his code to be able to continue my work on feature-x, I have 2 options to update my local/feature-x code but each one has a drawback (please note that this scenario implies that feature-x should appear as the most-recent feature when seeing the history of the commits in origin/develop):

  1. merge origin/develop into local/feature-x -> this will re-write the git-history and make feature-x commits appears behind feature-y commits and I want to avoid that.

  2. rebase local/feature-x onto origin/develop -> this will make the history of local/feature-x differs from the history of its tracked version (i.e., origin/feature-x) then git will force me to pull origin/feature-x commits to local/feature-x and thus, having a duplicated of commits in local/feature-x and making the history looks ugly!

What are the ways to update the local/feature-x branch w-r-t having a meaningful git-history?

1 Answers

First of all, Git Flow doesn't dictate how you should update your feature branches. Whether you merge or rebase is up to you and your team, as both are compatible. Note that Git Flow does recommend that you force a merge commit when you merge your feature branch into develop. So even if you use a rebase strategy for updating your feature branch, the end result would be small bubble of commits fully ahead of develop. This strategy is sometimes called "Semi-Linear Merge" or "Rebase Merge".

Now some corrections:

  1. If you choose to update your feature branch with merge, then it's not the case that "this will re-write the git-history". The primary reason to choose merge is specifically so you don't rewrite the history, as some people prefer this. Note this is evidenced by the fact that the commit IDs of your feature branch do not change when you merge in origin/develop. (BTW, a secondary reason to use merge instead of rebase, is sometimes you may have fewer conflicts when you merge, compared to when you rebase, because you only have to resolve conflicts once with merge, whereas with rebase it's possible to have to resolve conflicts for multiple commits getting re-written.)
  2. If you choose to update your feature branch with rebase, then it's not the case that "git will force me to pull" which would, as you point out, leave you with duplicate commits. When you rebase your feature branch, you should then force push your branch instead of pulling. (Or, you could use pull --rebase which will rebase instead of merging the pulled in commits.)

As for what's "best", this is an endless debate and actually not even appropriate to ask here on SO, and you'll noticed I edited the question to remove that wording. You need to decide that for yourself based on the differences. It sounds like you prefer the cleaner history, so your choice of rebase should work fine for you if you tweak your workflow to simply force push your branch after a rebase.

Side Note: in all corporate environments where I've used Git Flow, we've always preferred rebase for personal feature branches only. Any shared branches we lock down so that force pushes are not allowed, and code gets merged into those via Pull Requests. For those PRs we default to a Semi-linear merge strategy so that feature branches are rebased and then merged with a forced merged commit. But we never use rebase when doing Git Flow shared branch merges such as release into develop or master into develop.

Way off to the side note: I know you are talking about desiring a "clean" history in this question, but I want to nit-pick a specific wording you used:

having a meaningful git-history

with the implication that the rebased history is "meaningful" whereas the merged history is not. If anything, a history containing the merges is slightly more "meaningful" than one re-written with rebase, because you have some amount of information loss with a rebase. This is one of the reasons I prefer to use a semi-linear merge strategy in general; you still have a little bit of information loss compared to never rebasing, but the most important information of "which commits are part of this feature" is retained. Perhaps instead of "meaningful" you could use "cleaner looking" or "having an easier to read git-history". (I realize you did say that in the title question.)

Related