3

I have a old branch foo, and wanna make it up to the latest master.

If I execute $ git rebase origin master on the foo branch, somewhy the changes made in foo branch disappears and foo gets just same as the HEAD of master.

Why does this happen? And How to rebase my foo branch to the master?

Here is my git reflog. foo is equal to komoto/crop_image.

fd8d9c1d (HEAD -> komoto/crop_image, origin/master, origin/HEAD, master) HEAD@{0}: rebase finished: returning to refs/heads/komoto/crop_image
fd8d9c1d (HEAD -> komoto/crop_image, origin/master, origin/HEAD, master) HEAD@{1}: rebase: checkout master
92284980 (tag: beta-test, origin/komoto/crop_image) HEAD@{2}: checkout: moving from master to komoto/crop_image
Taichi
  • 2,297
  • 6
  • 25
  • 47

1 Answers1

3

Beware: git rebase origin master means git checkout master; git rebase origin (and then return to the original branch). That's why your reflog shows:

HEAD@{1}: rebase: checkout master

(and then HEAD@{0}: rebase finished: returning to refs/heads/komoto/crop_image: obviously you were on komoto/crop_image when you started).

What you did actually run

When you write origin by itself in this positional argument, your Git follows the six-step process outlined in the gitrevisions documentation. Step 6 reads:

  1. otherwise, refs/remotes/<refname>/HEAD if it exists.

So if git rev-parse origin/HEAD succeeds (and the five preceding steps fail), the sequence of commands:

git checkout master
git rebase origin

is equivalent to the sequence:

git checkout master
git rebase origin/master

If there are no commits on your master that are not already reachable from origin/master, this does nothing. For a good introduction to reachability, see Think Like (a) Git.

What you should have run

I have a old branch foo, and wanna make it up to the latest master.

In general, I prefer to do this as:

git checkout foo
git rebase master

but you can—because of the shortcut that I think people should avoid (it has bad failure modes in older versions of Git)—run:

git rebase master foo

Remember that rebase works by copying commits, so this will first enumerate all non-merge commits reachable from foo that are not reachable from master, in a topologically-sorted order,1 saving their hash IDs somewhere.2 It will then use the equivalent of git cherry-pick3 to copy each such commit, with the copy landing after the previously-copied commit. The first-copied commit lands right after the tip of master:

...--A--B--C--...--N--O   <-- master
         \
          G--H--I   <-- foo

becomes:

                        G'-H'-I'  <-- foo
                       /
...--A--B--C--...--N--O   <-- master
         \
          G--H--I   [abandoned, but still findable as foo@{1}]

where G'-H'-I' is the sequence of cherry-picked, i.e., copied, commits that correspond to the original commits G-H-I.


1For a simple chain of commits with no branching and merging, the only correct topological sort is: in order from first to last. If there are branch-and-merge sequences in the chain of commits you are rebasing, rebase picks some valid topological sort, linearizing the commits in the process and omitting merge commits. This is actually sometimes a good idea and what you want, but more often, it's not. Git is growing a new kind of rebase that will be able to handle this better; currently there is an option called --preserve-merges that attempts to do the job, but is not really adequate and should be avoided except in emergencies.

2This somewhere is literally in the file full of pick directives when you use git rebase -i. Each pick hash directive tells Git to do a cherry-pick of that particular commit, listing its true name: its hash ID. for some other kinds of rebases, the IDs are not as easily visible—but they are still saved somewhere, with the ability to stop the copying process when a copy step fails, then resume from the point where it stopped after you have manually fixed things up.

3An interactive rebase, or a rebase run with -m or -s flags, literally uses git cherry-pick. All rebases probably should default to this, but currently, non-interactive rebases that do not use -m or -s use an older method that combines git format-patch with git am --3way. See also What is the difference between git cherry-pick and git format-patch | git am?

torek
  • 448,244
  • 59
  • 642
  • 775