12

I'm sorry for bringing up the philosophical debate about merge vs rebase, but I have a practical question, regarding a typical company workflow (not an opensource project workflow, which might be different).

Rebasing, as pointed here, should not be used if you have pushed your changes. This is so, because others may have branched from your branch, and then rewriting history of that branch (via force-pushing) will make it hard for them.

So the proper way to use rebasing is by committing only locally. But at least in my brief experience that's not desirable:

  • what if your computer gets stolen?
  • what if you lose data due to a disk failure?
  • what if you have to work from two locations (like this person)

Keeping a feature branch local is something counter-intuitive for me. Not only due to the above points, but also due to the following use-case: you are working on a feature, but either get dragged to something more urgent, or go on holiday, or fall sick. A colleague has to complete it. Then you rebase and push (unless you are sick, in which case it stays local and nobody can complete it), and the other person completes it.

During that period there are changes in master. But the other person is already based on a remote branch, so he should either use merge, or rebase and force-push (which, again, might screw things if other people have based their branches on that one meanwhile).

These may sound like edge cases, but they aren't. At least one of these conditions (work from home, falling sick, going on holiday, finishing someone else's work, etc.) happens rather often.

So, is using "rebase" a good way to go in these usecases, or merging (with the additional "merge commits") is the safer and more straightforward option?

Community
  • 1
  • 1
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • Having a local branch is an integral part of the D in DVCS. – Sten Muchow Jun 08 '16 at 13:57
  • Your feature branches shouldn't be the base of other people's work. They would be basing their work on unfinished (unreviewed, untested, subject to change) code. It's totally fine to push out your feature branch and rebase as you will. – jaredready Jun 08 '16 at 14:18

4 Answers4

5

The precisely stated rule is: "do not rebase a branch which could be a base for another branch". Usually, you could easily achieve it only by not publishing your branch - no one sees it, so no one could use it. So that's why "should not be used if you have pushed". But in some situations it is acceptable workflow - rebase/push/rebase/push/... You just need somehow agree with others that they do not use the pushed branch for their needs or explicitly agree on a rota who is rebasing/pushing next (in case of sickness/holiday/etc), or if you just working from several locations (I hope you could agree with yourself at some point).

BTW, there is a --force-with-lease option to avoid some harm which could be caused by pushed rebases.

kan
  • 28,279
  • 7
  • 71
  • 101
  • 1
    I concur. It's perfecly fine to keep a "dirty" branch *somewhere* non-locally, and repeatedly force-push one's rebased state there. I do this personally precisely for "backup" reasons when working on a festure which is in terrible flux; before preparing "pretty" history I will merge eventually into a "blessed" history line, I may have an awkward branch I push to something like "tmp/branchname". – kostix Jun 08 '16 at 14:08
  • Using force-push as a part of the regular, day-to-day workflow, sounds .. strange. Not that it doesn't work, but still. And I've had multiple cases of people basing a branch on my unfinished branch (and the use-cases were legitimate, not hacky), and this scenario is harder/hackier with rebase – Bozho Jun 09 '16 at 06:43
  • @Bozho Sure, there are scenarios when rebase is harder, there are scenarios when it's easier. You have a right to choose when and how to use it. Just need to know how it works and be not surprised if something works not as expected. – kan Jun 09 '16 at 10:57
  • @Bozho I force-push many dozens of times a day... with `git bak` (a trivial alias I made) I push to my personal backup branch (or to a different repository; depending on the project). Whenever I get *any* progress, I commit (and often "bak", too). As obviously nobody wants to get hundreds of commits a day, these commits must get rebased into something nicer. This does not preclude later merging of the nice commits, so you can have both. – maaartinus Jun 18 '16 at 01:28
5

So I tend to favor rebasing. Git graph history is important. You want it to have some information about feature branches and how they get merged, but once you have more than a few people, the graph quickly gets into a mess. It's quite often that you would like to get a sense of what happened in the last few days, and the quality of the graph is the difference between being able to get that information from the git graph or not.

Also, note that force pushing feature branches is not that big of a deal. The problem with force pushes is that you have to notify people about git activity via non-git means – ping them on Slack, etc. This obviously doesn't work for an open source project or a branch with multiple contributors. But most feature branches have a single contributor, so they can force push as much as they want. Even if there are two or three people working on it, you can solve it by force pushing rarely – the typical for our organization is before opening a pull request and before merge. You occasionally do it in the middle of the work to get something from upstream, but normally you don't need to unless you want to.

When we merge, we do two things – we rebase on top of master and then we merge with --no-ff. This creates a merge bubble with an empty left path, which (1) groups commits together and (2) makes commits that went directly into master stand out (those are occasionally valid – hotfixes, small improvements, typos, whitespace, etc.).

The caveat of this approach is that it takes a while to learn. You can't use the GitHub merge button (it does not do what we expect) and you have to have enough git chops to do a rebase and pay attention to the graph. Most developers I've met are not there. Every new team member takes a while to grok this and usually messes the history up once or twice (we have git hooks that help). But I've found the tradeoff to be worth it big time. The benefits are:

  • Feature branches are not local.
  • The history is kept pretty clean.
  • Topical commits are grouped via a merge bubble.
  • Force pushing is not usually a problem, because (1) it happens seldom and (2) you either work alone or at most in a pair.

As a sidenote, "don't rebase things you have already pushed to origin" always sounded like a newbie rule for me. Yes, you can mess up big time when you don't know what you're doing. But once you get proficient in git, it's time for the training wheels to come of.

Stefan Kanev
  • 3,030
  • 22
  • 17
  • That sounds like a sensible workflow, though I have two issues: - using force-push as a regular part of your workflow doesn't sound to be "as Linus intended" - it makes it harder for people to branch off WIP branches (which, to me, is a legitimate usecase). But if you care about the graph, that's the way to go. If you don't, on the other hand, a merge-only workflow saves you the complexities. – Bozho Jun 09 '16 at 15:30
  • Well, the kernel and it's army of developers is a very different beast than a small-to-mid-sized team withing a product driver organization. There's more than one way to skin a cat, and git supports a variety of approaches to suit different needs. – Stefan Kanev Jun 09 '16 at 16:04
1

You can safely use rebase for update your local branch from the same remote tracked branch (like git pull --rebase), while as you said it's better to use merge when you are working on a "shared" branch with other collaborators and you don't want to rewrite remote history (using git push --force).

So, depending of your git workflow, due to the distributed nature of git, if you prefer rebase over merge is preferable to use a separate branch (or ever a separate repo, like github "forks") to push your changes and finally merge it to the destination branch.

Dario
  • 3,905
  • 2
  • 13
  • 27
1

I tend to agree with the conservative rule to not rebase pushed branches. As pointed out by other answers, it is not necessarily a problem and the --force-with-lease option removes a lot of the pain. Still the conservative strategy has two advantages:

  • It is easy to remember, there are no ifs and buts, you could even enforce it by disallowing push --force.
  • You are always on the safe side - maybe someone did work on your branch and you were not aware of it.

Nevertheless, there exists a strategy to push branches and still use rebase which is to simply create a new branch every time you rewrite history:

$ git checkout -b my_branch/WIP/01
... do some work, add some commits ...
$ git push
... notice that you want to clean up your history ...
$ git checkout -b my_branch/WIP/02
$ git rebase ...
$ git push

This way, if somebody relied on your previously published work it is still there, the history is reproducible and there is no push --force involved.

The downside of this approach is that other people who did work on my_branch/WIP/01 have to manually cherry-pick their work on the latest branch.

DanielM
  • 1,023
  • 8
  • 18