Help:Git rebase

From Wikitech
Jump to: navigation, search

What not to do: `git merge` or `git pull`

Don't use:

  • merge
  • pull

Do use:

  • fetch
  • rebase


Pulling and merging seems reasonable enough, but when you least expect it they will ruin your branch and, possibly, your life.

$ git merge

"git merge" piles upstream changes on top of your local changes. If you don't have local changes that's fine, but if you do then the resulting branch will gain puzzling, empty 'merge' patches and the changes that you're paying attention to will be buried. Merges are handled automatically by Gerrit -- if you are a human, never use git merge.

 $ git pull

git pull does a fetch and merge. Half of that is 'merge', and you should never use merge. There are various modifications to pull which avoid this (e.g. git pull --rebase) but because pull combines multiple commands its behavior tends to be obscure.

What to use instead: `git fetch origin`

If you aren't engaged in expert Git gymnastics, you have an upstream named origin which represents Git's understanding of the latest state up the upstream repo. To refresh this state, use

$ git fetch origin

All of the following patterns will start with 'fetch'. After you do a fetch, 'origin' will serve as a standin for 'the latest state of the upstream repo'.

Fetch and rebase

Most of the time you will want to do this:

$ git fetch origin
$ git rebase origin

That will set your current commits (if any) aside, update your branch, and then reapply your current commits. A subsequent git diff will show your patches as the most recent patches, and a git commit --amend will continue to amend your latest patch.

Don't panic

When you're getting all kinds of terrible unfamiliar errors (write permission failures, rebase -i warnings, whatever), don't panic! You can still save your patch. Branches are cheap, and yours is broken, so just abandon it. Instead, make yourself a new branch that matches the upstream.

$ git fetch origin
$ git checkout -b <newbranchname> origin

Now you're on a new branch, and it's up to date! Whatever you were doing before is preserved but generally ignorable. If you want to check in for old time's sake, do

$ git log <oldbranch>

...and there it is. Any patches on oldbranch can be rescued by cherry-picking from the old branch to your new, unbroken branch. Grab the commit-id from git log <oldbranch> and

$ git cherry-pick <commit-id>

Now the patch is on your shiny new branch, minus whatever wreckage preceded it on <oldbranch>.

OK, wait, why aren't we using merge?

Part of the advantage of git is that it makes it very easy to administer entire repositories rather than just wrangle individual files and patches. That targeted use case means that most git user guides have an administrative perspective and revel in the power of total repository control. Hypothetically, for a given project all git repositories are peers with no explicit relationship between client and server, so guides generally don't distinguish between the commands that a user would use vs. an administrator.

With Gerrit, though, the world is simpler. There really is a server, Gerrit, and a client, you. That provides you with the luxury of ignoring 95% of git commands and limiting yourself to a simple six-or-eight-word vocabulary. If, however, you treat your local checkout like a full and equal partner with Gerrit, then you will be constantly baffled and annoyed.

An illustrated parable

When you create your topic branch, it's the same as the upstream branch in Gerrit.

Everything is right with the world

After hours of hard work, you have a patch committed on your branch that is just about perfect.

 $ git commit
     Fix everything.
     # Please enter the commit message for your changes. Lines starting
     # with '#' will be ignored, and an empty message aborts the commit.


Meanwhile, some jerk has submitted a different patch to Gerrit and it was merged.

Unsettling!

Now it's the next morning and your patch is finally perfect. Time to wrap this up.

 $ git commit --amend
     Fix everything
     
     Change-Id: I0f5b5b4d1dc3cecfcd391ca12434f463e0eac675
     # Please enter the commit message for your changes. Lines starting
     # with '#' will be ignored, and an empty message aborts the commit.

Some other folks merged their patches into Gerrit while you were sleeping.

Don't those guys ever sleep?

You should probably make sure that your patch can play nicely with all that other stuff. So...

 $ git pull  # never do this, unless you're demonstrating why not to do this.

Indeed, now your patch needs a bit more work. A few quick minute of tinkering, though, and you're ready to roll again.

 $ git commit --amend
     Further Jeff's sinister goals
     
     Change-Id: Icd696a387188fbac8568133bd718418ebf2b463c
     # Please enter the commit message for your changes. Lines starting
     # with '#' will be ignored, and an empty message aborts the commit.

Uhoh, that's not your patch. Why are you suddenly amending a patch that Jeff wrote last night? And where's your patch? What's going on?

What a predicament!

What's going on is, git pull did a git merge. And you should never use git merge.

If only you'd used fetch and rebase instead! Then this would've happened:

 $ git commit --amend
     Fix everything
     
     Change-Id: I0f5b5b4d1dc3cecfcd391ca12434f463e0eac675
     # Please enter the commit message for your changes. Lines starting
     # with '#' will be ignored, and an empty message aborts the commit.

Time for ice cream