1

I recently ran into this on a web project:

git branch new-feature
git checkout new-feature

hack hack hack...

"Bossman: hey, there's a bug in master that needs fixing, pronto!"

git commit -a -m "Partially completed migration changes"
git checkout master
git branch bugfix
git checkout bugfix

hack hack hack... test test test...

"OK, fixed!"

git checkout master
git merge bugfix

Onto completing the feature, but let's not reintroduce the bug...

git checkout new-feature
git rebase master

First, rewinding head to replay your work on top of it...
Applying: some commit info
Using index info to reconstruct a base tree...
...
Auto-merging /some/buggy/file
CONFLICT (content): /some/buggy/file
Auto-merging /some/buggy/file
CONFLICT (content): /some/buggy/file2
Auto-merging /some/buggy/file
CONFLICT (content): /some/buggy/file3
...

If I happen to know that specific files are irrelevant to the new feature, how do I interactively force files to copy from the rebase source?

Is there a way to tell git to prompt me with something like:

copy /some/buggy/file master? y/n
copy /some/buggy/file2 master? y/n
...

1 Answers1

2

Rebase consists of repeated git cherry-pick operations (or one single en-masse pick in some cases, but that amounts to the same thing): it takes all the commits selected by your rebase specification (basically <upstream>..HEAD) and copies those to new commits with the new chain grown on the --onto argument (which defaults to the tip of your <upstream> argument).

Doing the cherry-picking requires a small change of perspective: rebase starts by checking out an anonymous (detached HEAD) branch at the --onto point, and then iterates through the commit-IDs it saved away earlier from the <upstream>..HEAD before it detached HEAD. It's these cherry-pick steps that hit the merge conflict(s) (which is why you often have to solve the merge conflicts repeatedly, once for each commit being cherry-picked).

This means that to answer your question we may want to look at the git cherry-pick documentation in addition to the git rebase documentation. There, we find:

When it is not obvious how to apply a change, the following happens:

  1. The current branch and HEAD pointer stay at the last commit successfully made.

  2. The CHERRY_PICK_HEAD ref is set to point at the commit that introduced the change that is difficult to apply.

This means that in this state, we can use either git checkout HEAD -- path/to/file (the -- is not actually required here, but is a good habit) or git checkout CHERRY_PICK_HEAD -- path/to/file.

Since we're now cherry-picking, not rebasing (the rebase is still going on, we're just in the cherry-pick phase at this point), HEAD refers to the most recent successful commit. If the conflict is at the very first commit, HEAD refers to the same commit as master in your particular example, while CHERRY_PICK_HEAD refers to the commit being copied from the original new-feature branch. If the conflict is a bit further along, HEAD refers to one of the new commits that has been grown on the anonymous branch leading into the future from (again, using your example) master. In either case that's the one you actually want, here.

(Side note: if you know that you didn't touch the file, it's going to be the same, in this case, in both the CHERRY_PICK_HEAD commit and the master commit, so you can use git checkout master -- path/to/file instead, which is easier to type. However, this assumes that you're rebasing onto the tip of master, while using the name CHERRY_PICK_HEAD makes this independent of the target commit.)

Once the cherry-picking phase is done, git rebase takes over again and completes the rebase by changing the rebased branch name—in this case, new-feature—to point to the tip-most commit on the new anonymous branch. The old (pre-rebase) branch tip, from before all this cherry-picking, remains in the reflog for new-feature (as new-feature@{1} or any of the other ways to name it). It's also available in ORIG_HEAD at this point, until you run something that overwrites ORIG_HEAD (various git commands, including git am, git merge, and git reset, also write to ORIG_HEAD). This is particularly handy if you accidentally goof up the rebase.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775