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.
(This also answers the question: "How does a git rebase work and what exactly is happening with it?")
HEAD
) at the moment git does the action which causes the conflict (more on this later), and: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.
git merge
(intuitive):
git checkout master
git merge feature_branch # merge feature_branch into master
HEAD
, which is master
, because you were on branch master
at the time you ran git merge feature_branch
.feature_branch
, which is the branch you're merging into master
.git cherry-pick
(intuitive):
git checkout feature_branch
git cherry-pick some_commit # apply some_commit to feature_branch
HEAD
, which is feature_branch
, because you were on branch feature_branch
at the time you ran git cherry-pick some_commit
.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):
git checkout feature_branch
git rebase master # rebase feature_branch onto latest master
# 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
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.
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):
git checkout feature_branch
# create a new commit to undo the changes from some_previous_commit
# within feature_branch
git revert some_previous_commit
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 commit being reverted to the commit we are on now.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.# 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.
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.
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.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
# 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
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:
--soft
or --hard
git reset by pathgit revert
] Who is `them` and `us` in a `git revert`?
HEAD
, and "them/theirs" is always the other branch or commit] Who is `them` and `us` in a `git revert`?
git merge
and git rebase
] Who is "us" and who is "them" according to Git?
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").
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