Who is "us"/"ours" and "them"/"theirs" according to Git?
(This also answers the question: "How does a git rebase work and what exactly is happening with it?")
When you have conflicts in the middle of a git merge
, cherry-pick
, rebase
, or revert
, you may need to pick a side to choose to keep conflicting content from just the --ours
or --theirs
side (and to keep non-conflicting content from both sides otherwise), leading to the question: who is "us" vs "them" in each of those cases?
Conflict resolution might involve the following, for instance:
git checkout master
git merge feature_branch
git checkout --theirs -- path/to/some/dir
git add path/to/some/dir
git status
git merge --continue
But, what does the git checkout --theirs
line do, and what other options do we have? Here are some options:
# ------------------------------------------------------------------------------
# Do this:
# - Generally, one of these might be useful during conflict resolution:
# ------------------------------------------------------------------------------
# Integrate changes from both sides, but in the lines which conflict, keep the
# `--theirs` changes for all conflicts within files inside this directory.
git checkout --theirs -- path/to/some/dir
# OR: Integrate changes from both sides, but in the lines which conflict, keep
# the `--ours` changes for all conflicts within files inside this directory.
git checkout --ours -- path/to/some/dir
# ------------------------------------------------------------------------------
# Do *not* do this:
# - Generally, do *not* do this during conflict resolution:
# ------------------------------------------------------------------------------
# Discard all changes from both sides by hard resetting all the contents inside of
# this directory back to their state at the `some_branch` commit.
# - But, oddly enough, this does *not* do deletions of files inside this directory
# which don't exist at this `some_branch` commit but are there now. So, this
# isn't even a proper hard reset of this directory. It's a mishmash.
git checkout some_branch -- path/to/some/dir
See the "best conflict resolution example" below, as well as all examples within the "Example uses-cases" section, for details. See also the "WARNING WARNING WARNING!" section though. In most cases, you do not want to do git checkout some_branch -- path/to/some/dir
in an attempt to resolve conflicts, as that discards the entire merge
, cherry-pick
, rebase
or revert
, undoing all changes from one side rather than keeping changes from both sides but favoring the one side in the lines which conflict. So, using --ours
or --theirs
, as shown above, is generally the right course of action instead of using some_branch
.
Study those sections at the bottom of my answer for details.
Now on to the main answer:
In all cases:
In rough layman's terms (thank you, @user20358):
"us" is the code already in the repo, and "them" is the code [you are] trying to merge in
In more-complete terms:
- "us" (or "ours") = the currently-checked out commit (
HEAD
) at the moment git does the action which causes the conflict (more on this later), and:
- "them" (or "theirs") = the other commit, NOT checked-out by git at the moment git does the action which causes the conflict (more on this later).
IMPORTANT: HEAD
at the moment it does the action which causes the conflict is NOT necessarily the HEAD
at the moment you type the git command. This is essential to understand. Git may do some checkouts and change the HEAD
(which commit is checked-out) before running the action which causes the conflict, thereby causing "us" and "them" to appear swapped or backwards to the untrained eye.
The 4 cases, in detail: merge, cherry-pick, rebase, revert:
git merge
(intuitive):
- Sample command:
git checkout master
git merge feature_branch # merge feature_branch into master
- "us"/"ours" =
HEAD
, which is master
, because you were on branch master
at the time you ran git merge feature_branch
.
- "them"/"theirs" =
feature_branch
, which is the branch you're merging into master
.
git cherry-pick
(intuitive):
- Sample command:
git checkout feature_branch
git cherry-pick some_commit # apply some_commit to feature_branch
- "us"/"ours" =
HEAD
, which is feature_branch
, because you were on branch feature_branch
at the time you ran git cherry-pick some_commit
.
- "them"/"theirs" =
some_commit
, which is the commit you're cherry-picking onto feature_branch
.
git rebase
(counter-intuitive, but totally makes sense once you understand the mechanics of how it works):
- Sample command:
git checkout feature_branch
git rebase master # rebase feature_branch onto latest master
- Diagram of this (drawn at https://asciiflow.com), with the latest or newest commits on top and/or to the right:
# Prior to rebase: feature_branch
# received new commits while
# master did too
#
# master
# x
# | feature_branch
# x y
# | |
# x y
# | /
# git merge-base ────► x--y--y--y
# master feature_branch |
# x
#
#
# After rebase: feature_branch has
# been completely cherry-picked onto
# the end of master
#
# feature_branch
# y'
# |
# y'
# /
# y'--y'--y'
# |
# master x
# |
# x
# |
# x
# |
# x
# |
# x
- "us"/"ours" =
HEAD
, which is the upstream branch: initially the last x
commit on master
, and then thereafter, some NEW commit, y'
, cherry-picked
on top of that (this one's tricky!). This is because when you typed git rebase master
, git first checks out master
as the starting point to start cherry-picking your feature_branch
commits onto, then it determines which commits from feature_branch
to cherry-pick (ie: which of your feature_branch
commits are not already on master
). It does this by finding the merge-base
(the commit which is common to both feature_branch
and master
and which can be found with git merge-base master feature_branch
), and THEN it starts cherry-picking commits from the first one after this merge-base
and onward, working one-at-a-time, towards the last commit on feature_branch
, onto the tip of master
, thereby "rebasing" all "new" y
commits you added to feature_branch
onto the latest master
, as new y'
commits. Therefore, "us"/"ours" = HEAD
, but since git did a new checkout behind-the-scenes to perform this rebase, HEAD
is NOT the branch you were on when you typed git rebase master
. Instead, us, or HEAD
, is either the last x
commit on master
if the conflict occurs during the first cherry-pick
, or it is whatever NEW commit, y'
, was last successfully cherry-picked onto master
if the conflict occurs during any later cherry-pick. Them is therefore the other commit, which is some y
commit from feature_branch
which is being applied to this new HEAD
via a cherry-pick, in order, FROM the first y
commit on feature_branch
which is immediately after git merge-base master feature_branch
all the way TO the last y
commit on feature_branch
. See explanation for "them" also, just below.
- "them"/"theirs" = some
y
commit from feature_branch
which is being applied to a newly-checked-out HEAD
, where HEAD
is either the last x
commit on master
for the first cherry-pick operation during the rebase, OR one of these newly-created y'
commits on top of master
as feature_branch
is "rebased", or cherry-picked one-commit-at-a-time (along your string of new commits from git merge-base master feature_branch
to the last commit on feature_branch
) onto master
. See explanation for "us" also, just above.
git revert
(sort of intuitive):
- Sample command:
git checkout feature_branch
# create a new commit to undo the changes from some_previous_commit
# within feature_branch
git revert some_previous_commit
- For some of the detailed, low-level mechanics of this one, see my "Results and Conclusions" section at the bottom of my other answer here, as well as this very long and detailed answer by @torek here.
- "us"/"ours" =
HEAD
, which is feature_branch
, because you were on branch feature_branch
at the time you ran git revert some_previous_commit
. More specifically, "us"/"ours" contains the changes expressed by git diff some_previous_commit..HEAD
, which are the changes from the point at which the commit being reverted (some_previous_commit
) was committed to the commit we are on now.
- "them"/"theirs" = (the inverse or opposite of)
some_previous_commit
, which is the commit whose changes you're reverting (undoing, by creating a new commit on top of feature_branch
). In other words, "them"/"theirs" contains the changes expressed by git diff some_previous_commit..some_previous_commit~
, where some_previous_commit~
is the parent commit of some_previous_commit
. This means that "them"/"theirs" is the opposite of some_previous_commit
, as it contains the opposite of its changes, in order to undo some_previous_commit
's changes.
Example use-cases:
Merge conflict resolution examples:
# 1. Merge `feature_branch` into `master`, accepting ALL of
# `master`'s (`ours`) changes in the event of
# any merge conflicts!
git checkout master
git merge -X ours feature_branch
# 2. Merge `feature_branch` into `master`, accepting ALL of
# `feature_branch`'s (`theirs`) changes in the event of
# any merge conflicts!
git checkout master
git merge -X theirs feature_branch
Here are some more. These are my most-commonly-used techniques, rather than the 2 examples just above.
# 3. Assuming this merge attempt results in merge conflicts in
# these 3 files, but we know all of the changes on the `master`
# branch side are the ones we wish to keep, check out these 3
# files from `master` (`--ours`) to overwrite the conflicted
# files. Then, add these now-fixed files to stage them for
# committing, and continue (finish) the merge.
git checkout master
git merge feature_branch
git checkout --ours -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git add path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git status
git merge --continue
# 4. Assuming this merge attempt results in merge conflicts in
# these 3 files, but we know all of the changes on the `feature_branch`
# side are the ones we wish to keep, check out these 3
# files from `feature_branch` (`--theirs`) to overwrite the conflicted
# files. Then, add these now-fixed files to stage them for
# committing, and continue (finish) the merge.
git checkout master
git merge feature_branch
git checkout --theirs -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git add path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git status
git merge --continue
VERY USEFUL: If an entire folder of conflicts exists, you can also specify to accept all conflicting changes from the --ours
or --theirs
branch for the entire folder at once, like this!:
**BEST MERGE CONFLICT RESOLUTION EXAMPLE:**
# 5. [BEST EXAMPLE] Assuming this merge attempt results in merge conflicts in
# a bunch of files, some of which are inside `path/to/some/dir`, I can
# choose to accept the changes from one side or the other **for
# all conflicts within files inside this directory**, like this!:
git checkout master
git merge feature_branch
# Keep `--theirs` for all conflicts within files inside this dir
git checkout --theirs -- path/to/some/dir
# OR: keep `--ours` for all conflicts within files inside this dir
git checkout --ours -- path/to/some/dir
# Add (stage for committing) all changes within files inside this dir
# all at once
git add path/to/some/dir
git status
git merge --continue
DEALING WITH path does not have our version
or path does not have their version
ERRORS:
If you ever run something like this:
git checkout --ours -- path/to/some/dir
...and it didn't work! It didn't do anything. Instead, it output these errors:
error: path 'path/to/some/dir/file1.cpp' does not have our version
error: path 'path/to/some/dir/file2.cpp' does not have our version
error: path 'path/to/some/dir/file3.cpp' does not have our version
The problem is that these errored files are deleted files on the our
side, so we must git rm
each of them manually BEFORE running git checkout --ours -- path/to/some/dir
.
git rm path/to/some/dir/file1.cpp path/to/some/dir/file2.cpp \
path/to/some/dir/file3.cpp
# then try again
git checkout --ours -- path/to/some/dir
You can also just do this instead to automate the process:
git checkout --ours -- path/to/some/dir \
|& gawk '{ print $3 }' | xargs git rm
git checkout --ours -- path/to/some/dir
See my answer here for a detailed explanation of the commands above: git checkout --ours when file spec includes deleted file.
WARNING WARNING WARNING!
For a more-detailed example of the problem described below, see my other answer here.
Do NOT do git checkout -- path/to/some/dir
, nor git checkout some_branch -- path/to/some/dir
in the middle of a conflict resolution (such as during a merge
conflict like in the examples above), UNLESS YOU INTEND TO CHECK OUT ALL FILES FROM HEAD
, or from some_branch
, respectively, in directory path/to/some/dir
, AND OVERWRITE THE LOCAL FILES WITH THOSE FILES, thereby not just accepting the conflicted changes from one side or the other.
In other words, during the middle of a conflict resolution, this:
GOOD:
# GOOD :)
# Accept all conflicts from one side or the other (while still
# merging changes from both sides into one, in the event of being
# in the middle of a `git merge`).
git checkout --ours -- path/to/some/dir
# OR
git checkout --ours -- path/to/some/file
will accept just the changes from side --ours
, which is always good and safe if that's what we want, whereas this:
BAD:
# BAD :(
# OVERWRITE all files with these files from `some_branch` instead,
# thereby _losing_ any changes and/or files contained in the other
# side but which are not in `some_branch`.
git checkout some_branch -- path/to/some/dir
# OR
git checkout some_branch -- path/to/some/file
will fully check out and overwrite the ENTIRE DIRECTORY or ENTIRE FILE, as specified, rather than only the conflicting changes themselves. This means you may be inadvertently deleting changes from one side or the other by doing a full checkout with git checkout some_branch
rather than a conflict resolution with git checkout --ours
or git checkout --theirs
. FOR THIS REASON, IT IS RECOMMENDED TO USE git checkout --ours -- file_or_dir_paths
or git checkout --theirs -- file_or_dir_paths
, NOT git checkout some_branch -- file_or_dir_paths
whenever you are in the middle of a conflict resolution such as for git merge
, git cherry-pick
, git rebase
, or git revert
.
HOWEVER, if you DO run git checkout some_branch -- file_or_dir_paths
because you understand this behavior and that's what you want, then you need to be aware of this too: checking out an entire directory like that does NOT delete local files in that dir which do not exist at some_branch
, like you'd expect it would. Instead, it leaves them alone in your local file system if they exist locally but not at some_branch
. So, you must MANUALLY remove all of those files instead. Keep that in mind or else it will leave you very very confused in the end. Read more about this in my other answers here (this answer has a good solution to that too) and here.
Going further / additional notes and tips:
- The above examples of file conflict resolution can apply in the event of conflicts in any of the various types of operations (
git merge
, git cherry-pick
, git rebase
, git revert
, etc.), except you need to keep in mind what theirs
and ours
means for each type of conflict resolution, as explained above.
- You can also just use branch names such as
master
or feature_branch
in place of -X ours
/-X theirs
and --ours
and --theirs
. WARNING: Passing branch names may seem easier and clearer, but CAN BE DANGEROUS to your changes, as doing it this way does a FULL FILE OR DIRECTORY REPLACEMENT, RATHER THAN A CONFLICT RESOLUTION FOR JUST THE CONFLICTS WITHIN THE FILES. See the "WARNING WARNING WARNING" section above. If you DO want to do a full file replacement, however, rather than just accepting conflicting changes from one side or the other, here's how:
# See "WARNING WARNING WARNING" section above.
git checkout feature_branch -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
- You can also check out entire directories rather than specifying files individually! See this answer here and my answer here and my other answer here. I just tested it. This works too. Again, however, pay attention to the "WARNING WARNING WARNING" section above. This does a full directory replacement, rather than just accepting conflicting changes from one side or the other for an entire folder:
# Check out ALL files from feature_branch which are in
# directory "path/to/dir". See "WARNING WARNING WARNING"
# section above.
git checkout feature_branch -- path/to/dir
- Remember, if you do intentionally use
git checkout feature_branch -- path/to/dir
, expecting/hoping it will delete local files in directory path/to/dir
which do NOT exist at feature_branch
, it will NOT. You must remove those files manually in your local file system before or after running the checkout
command. Read more in my answers here:
- All about checking out files or directories in git
- How to do a
--soft
or --hard
git reset by path
References:
- [my answer, which I reference all the time!] "All about checking out files or directories in git": How to get just one file from another branch
- [my answer] Why git can't do hard/soft resets by path? --> see especially the git terminology and definitions in the "Background knowledge" section at the end!
- [my own answer on what "them" and "us" mean during
git revert
] Who is `them` and `us` in a `git revert`?
- [@LeGEC's answer which points out that "ours/us" is always
HEAD
, and "them/theirs" is always the other branch or commit] Who is `them` and `us` in a `git revert`?
- [the pre-existing answer which I cross-checked against regarding my understandings of
git merge
and git rebase
] Who is "us" and who is "them" according to Git?
- From
man git checkout
: "Note that during git rebase and git pull --rebase, ours and theirs may appear swapped":
--ours, --theirs
When checking out paths from the index, check out stage #2 (ours) or #3 (theirs) for
unmerged paths.
Note that during git rebase and git pull --rebase, ours and theirs may appear swapped;
--ours gives the version from the branch the changes are rebased onto, while --theirs
gives the version from the branch that holds your work that is being rebased.
This is because rebase is used in a workflow that treats the history at the remote as
the shared canonical one, and treats the work done on the branch you are rebasing as the
third-party work to be integrated, and you are temporarily assuming the role of the
keeper of the canonical history during the rebase. As the keeper of the canonical
history, you need to view the history from the remote as ours (i.e. "our shared
canonical history"), while what you did on your side branch as theirs (i.e. "one
contributor’s work on top of it").
- [answer to my question]: you can pass directory paths to git too to check out all files from entire directories, rather than having to specify files individually: How do I accept git merge conflicts from "their" branch for only a certain directory?
- [my answer] git checkout --ours when file spec includes deleted file
- For drawing pretty ASCII pictures or diagrams to place in code: https://asciiflow.com/.
Additional Study:
- [
I NEED TO STUDY THIS ANSWER MYSELF STILL!--done; I've figured it out and updated my answer here now as of 7 Jan. 2020] Who is `them` and `us` in a `git revert`?
- [I NEED TO STUDY AND READ THIS STILL] git checkout --ours does not remove files from unmerged files list
See also:
- Quick links to my answers I reference frequently and consider to be "git fundamentals":
- Various ways to create a branch in git from another branch
- All about checking out files or directories in git
- Who is "us" and who is "them" according to Git?