Subject: Rebase safely (Re: cherry picking and merge)

From: Nico Williams <[email-masked]>
Date: Wed, 6 Aug 2014 14:44:59 -0500

On Wed, Aug 06, 2014 at 12:11:16PM -0700, Mike Stump wrote:
> On Aug 1, 2014, at 4:40 PM, Nico Williams <[email-masked]> wrote:
> > As for rebase, I still don't understand why it doesn't work for you.
> 
> http://git-scm.com/docs/git-rebase says:
> 
>   Rebasing (or any other form of rewriting) a branch that others have
>   based work on is a bad idea
> 
> If you read stack-overflow, you will discover a ton of people that
> like doing this, and they get hammered because of it.  My use case
> fits exactly (as near as I can tell) the hammer case.

It's not a good idea to rebase a branch in a repo that others pull from.

There's nothing wrong with rebasing your patches on that same branch in
your clone as long as the end result is a fast forward merge when you
push.

    $ git clone https://..../blah
    $ cd blah
    $ <do some work>
    $ git commit ...
    $ git fetch origin
--->$ git rebase origin/master
    $ git push origin master

There's NOTHING wrong with that rebase.  It's perfectly safe because
it's only in your *private* clone.

(If later you publish that clone and expect people to track your master
branch, then rebase becomes problematic, but that's not something most
people ever do with their clones.)

The only use-case I've seen where a rebase-based workflow doesn't work
is where you have multiple upstreams that you're following.  I.e., the
upstream forked and you want to take some commits from one, some from
the other, or otherwise keep a merge of both.

(Also, if an upstream is ever rebased you can usually recover on the
downstream side by rebasing with the --onto option, so it's not the end
of the world.)

> Now, I found the stack-overflow commentary first, and all the horrors
> of it, and all the nuances.  I carefully read what people were doing,
> how what I wanted to related to what they were doing, and it sure felt
> like I was in the, don’t go there camp.

A lot of people rant about rebase.  They're wrong.  They've distorted
your perspective.

> So, I like to know if I’m driving off a cliff, before I do.  I’m the
> [...]

There's just two simple rules to follow and you'll be safe:

1) NEVER git push -f (--force) to a "published" repo/branch.

   The upstream should enforce this with a receive hook.

2) NEVER work directly in a published repo.  Instead work in a private
   clone.  To help make sure of this, never publish a non-bare repo
   (bare == has no workspace; non-bare == has a workspace).

If you ever do a rebase that produces results you're unhappy with you
can undo that rebase like so:

 - use git reflog to find the branch's previous HEAD commit
 - reset the branch to point to that commit

It really helps to think of git as a pile of commits arranged in a
Merkle has tree.  Branches and tags are just symbolic names for specific
commits.  Rebase builds a new line of commits in the tree then it
changes the symbolic branch name's HEAD to point to the head of that new
line of commits, BUT NOTHING IS LOST in the pile of commits that is the
repo, not until you git-prune(1) to remove commits not reachable from
symbolic names (branches and tags).

> > The only case where I can imagine not using a
> > rebase-heavy workflow is where I have to track multiple forked
> > upstreams and so I want to merge each into my branch.
> 
> So, sounds like I fit that use case and rebase could be my friend.

Excellent.

> How do I square what you said and:
> 
>   Rebasing (or any other form of rewriting) a branch that others have
>   based work on is a bad idea
> 
> ?

See above.

> I want all old refs in old emails to work.  I want all refs in

They will if you stick to the two rules I mention above.

> bugzilla to work.  I want to see the original dates of all the work.

Ditto.

> I want git blame to report those artifacts in email and bugzilla.  I
> have coworkers that I push to, pull from (through a single sharing
> point, we call the master tree).  We work on gcc, we pull git gcc down
> to a local copy, then merge it into our tree.  I want to cherry pick
> changes from upstream.  I do work and push to our master, I pull work
> of coworkers from the master, my coworkers do the same.  Isn’t this
> the canonical open source use case?

That means that you have/maintain an intermediate upstream, yes?

This is a bit trickier since once in a while that intermediate upstream
and everyone downstream of it has to catch up with the real upstream.

Here you have two options:

 - the intermediate diverges from the real upstream, and then you
   merge/cherry-pick from the upstream as needed
   
   The intermediate's maintainer must still merge/rebase/cherry-pick
   from the intermediate branch and onto a branch of the upstream in
   order to push to the upstream.

or

 - the intermediate occasionally rebases onto the upstream, and then the
   repos downstream of the intermediate must also rebase with --onto.

   In this case the intermediate's maintainer must tell the downstreams
   what rebase command to execute.

   This makes it easier to push from the intermediate to the upstream:
   the intermediate's commits should always be on top of the upstream
   (at rebase time) so it's easy to push them.

The latter is the workflow we used at Sun for decades for large
projects.  We followed that workflow long before git, first with
Teamware, then later with Mercurial (even though Mercurial technically
had no support for rebasing at that time; we just made it work).

(We always left symbolic names for the pre-rebase branch HEADs, mind
you, to make life easier for everyone.)

> > (I find that many users are allergic to rebasing.  Many people have
> > told me that rebase is lying, that history must be immutable, and so
> > on, all ignoring that: git users don't rebase published branches,
> 
> So, when I push, and someone else pulls, is that published?  I thought
> it was.

Yes.  You shouldn't push -f.  As long as you don't there's no problem.

Nico
--