The git checkout documentation says:
--ours --theirs When checking out paths from the index, check out stage #2 (ours) or #3 (theirs) for unmerged paths.
What's the meaning of "stage #2" and "stage #3" during merge, rebase and cherry-pick? Is there a way to query theses "stages" before running the command to make sure it will retrieve the correct version?
These are documented (though not all that clearly, I think) in the gitrevisions
documentation:
A colon, optionally followed by a stage number (0 to 3) and a colon, followed by a path, names a blob object in the index at the given path. A missing stage number (and the colon that follows it) names a stage 0 entry. During a merge, stage 1 is the common ancestor, stage 2 is the target branch’s version (typically the current branch), and stage 3 is the version from the branch which is being merged.
To these, you then need to add knowledge about how git rebase
and git cherry-pick
work.
Normal cherry picking is well-defined: "ours" is the HEAD
version, i.e., the branch you were (and still are) on, while "theirs" is the commit you're actively picking. When you cherry-pick a single commit, it's all pretty obvious: stage #1 is the supposedly-common-ancestor commit,1 stage #2 is the version from the tip of your current branch, and stage #3 is the version you're cherry-picking.
If you cherry-pick a series of commits, this is still true, it's just true iteratively. Say you're cherry-picking three commits, for instance. Git simply does the three one-at-a-time. During the first cherry-pick, stage #2 is the tip of your branch, and stage #3 is the version from the first commit being cherry-picked. Once that commit cherry-pick finishes, git makes a new commit, advancing the tip of your branch. Then, during the second cherry-pick, stage #2 is the tip of your branch, which is the commit your first cherry-pick made, and stage #3 is the version from the second commit being picked. This repeats again for the final commit. Each time, stage #3 is "their" version.
Rebase, however, is a little tricky. Internally, it starts by getting you onto a new, anonymous branch (a "detached HEAD"). Then it runs git cherry-pick
to pick each commit from your original branch. This means that "ours" is the detached HEAD version, while "theirs" is the version from your original branch. Just like cherry-pick, this repeats iteratively for every commit to be picked (literally so in the case of an interactive rebase, where you edit the pick
lines). Once the rebase finishes, git simply shuffles the branch label around, so that the new anonymous branch that you just made is your code.
In short, you can think of rebase as "reversing the ours/theirs settings"—but this is an exaggeration. It might be more accurate to say that stage 2 is your new, melded-in code, and stage 3 is your old code.
Stage #1 is the merge base by definition. For a real merge, this is the best common ancestor commit, but cherry-pick forces this to the parent of the commit being cherry-picked. (Revert works in a similar fashion except that the commit being "copied" is the parent and the "merge base" is the commit being reverted.)
The Git documentation for merge (as well as a few other places) explains that an index file records up to three versions, or stages:
For conflicting paths, the index file records up to three versions: stage 1 stores the version from the common ancestor, stage 2 from HEAD, and stage 3 from MERGE_HEAD (you can inspect the stages with git ls-files -u). The working tree files contain the result of the "merge" program; i.e. 3-way merge results with familiar conflict markers <<< === >>>.
Here is a diagram showing what the three stages are in a typical Git merge:
Common Ancestor -> C1 --- C2 <- MERGE_HEAD (Stage 3)
(Stage 1) \
--- C3 --- C4 <- HEAD (Stage 2)
This assumes that the branch whose HEAD
is C4
is being merged back onto the branch ending in C2
.
As the documentation states, you can actually view the stages by typing:
git ls-files -u
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With