Who is "us" and who is "them" according to Git?
Solution 1:
When you merge, us
refers to the branch you're merging into, as opposed to them
, the branch to be merged.
When you rebase, us
refers the upstream branch, and them
is the branch you're moving about. It's a bit counter-intuitive in case of a rebase.
The reason is that Git uses the same merge-engine for rebase, and it's actually cherry-picking your stuff into the upstream branch. us
= into, them
= from.
Solution 2:
(This also answers the question: "How does a git rebase work and what exactly is happening with it?")
In all cases:
-
"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 ismaster
, because you were on branchmaster
at the time you rangit merge feature_branch
. -
"them"/"theirs" =
feature_branch
, which is the branch you're merging intomaster
.
- Sample command:
-
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 isfeature_branch
, because you were on branchfeature_branch
at the time you rangit cherry-pick some_commit
. -
"them"/"theirs" =
some_commit
, which is the commit you're cherry-picking ontofeature_branch
.
- Sample command:
-
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 lastx
commit onmaster
, and then thereafter, some NEW commit,y'
,cherry-picked
on top of that (this one's tricky!). This is because when you typedgit rebase master
, git first checks outmaster
as the starting point to start cherry-picking yourfeature_branch
commits onto, then it determines which commits fromfeature_branch
to cherry-pick (ie: which of yourfeature_branch
commits are not already onmaster
). It does this by finding themerge-base
(the commit which is common to bothfeature_branch
andmaster
and which can be found withgit merge-base master feature_branch
), and THEN it starts cherry-picking commits from the first one after thismerge-base
and onward, working one-at-a-time, towards the last commit onfeature_branch
, onto the tip ofmaster
, thereby "rebasing" all "new"y
commits you added tofeature_branch
onto the latestmaster
, as newy'
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 typedgit rebase master
. Instead, us, orHEAD
, is either the lastx
commit onmaster
if the conflict occurs during the firstcherry-pick
, or it is whatever NEW commit,y'
, was last successfully cherry-picked ontomaster
if the conflict occurs during any later cherry-pick. Them is therefore the other commit, which is somey
commit fromfeature_branch
which is being applied to this newHEAD
via a cherry-pick, in order, FROM the firsty
commit onfeature_branch
which is immediately aftergit merge-base master feature_branch
all the way TO the lasty
commit onfeature_branch
. See explanation for "them" also, just below. -
"them"/"theirs" = some
y
commit fromfeature_branch
which is being applied to a newly-checked-outHEAD
, whereHEAD
is either the lastx
commit onmaster
for the first cherry-pick operation during the rebase, OR one of these newly-createdy'
commits on top ofmaster
asfeature_branch
is "rebased", or cherry-picked one-commit-at-a-time (along your string of new commits fromgit merge-base master feature_branch
to the last commit onfeature_branch
) ontomaster
. See explanation for "us" also, just above.
- Sample command:
-
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 isfeature_branch
, because you were on branchfeature_branch
at the time you rangit revert some_previous_commit
. More specifically, "us"/"ours" contains the changes expressed bygit diff some_previous_commit..HEAD
, which are the changes from the commit being reverted 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 offeature_branch
). In other words, "them"/"theirs" contains the changes expressed bygit diff some_previous_commit..some_previous_commit~
, wheresome_previous_commit~
is the parent commit ofsome_previous_commit
. This means that "them"/"theirs" is the opposite ofsome_previous_commit
, as it contains the opposite of its changes, in order to undosome_previous_commit
's changes.
- Sample command:
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 whattheirs
andours
means for each type of conflict resolution, as explained above. - You can also just use branch names such as
master
orfeature_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 directorypath/to/dir
which do NOT exist atfeature_branch
, it will NOT. You must remove those files manually in your local file system before or after running thecheckout
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
andgit 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