All about checking out files or directories in git
1. How to check out one or more files or directories from another branch or commit hash into your currently-checked-out branch:
# check out all files in <paths> from branch <branch_name>
git checkout <branch_name> -- <paths>
Source: http://nicolasgallagher.com/git-checkout-specific-files-from-another-branch/.
See also man git checkout
.
Examples:
# Check out "somefile.c" from branch `my_branch`
git checkout my_branch -- somefile.c
# Check out these 4 files from `my_branch`
git checkout my_branch -- file1.h file1.cpp mydir/file2.h mydir/file2.cpp
# Check out ALL files from my_branch which are in
# directory "path/to/dir"
# - WARNING!: If you have uncommitted changes in this dir, they will be
# permanently lost when you run this command! See my lamentations in my
# comment here:
# https://stackoverflow.com/questions/2689265/git-how-to-undo-a-checkout-of-unstaged-files-which-discards-local-changes/2689318?noredirect=1#comment135379081_2689318
git checkout my_branch -- path/to/dir
If you don't specify, the branch_name
it is automatically assumed to be HEAD
, which is your most-recent commit of the currently-checked-out branch. So, you can also just do this to check out "somefile.c" and have it overwrite any local, uncommitted changes:
# Check out "somefile.c" from `HEAD`, to overwrite any local, uncommitted
# changes
git checkout -- somefile.c
# Or check out a whole folder from `HEAD`:
git checkout -- some_directory
If you have some staged deletions of the file you are trying to check out (via a previous rm somefile.c && git add somefile.c
), however, then you may see this error from git
:
$ git checkout -- somefile.c
error: pathspec 'somefile.c' did not match any file(s) known to git
In that case, you have to be explicit and specify a branch or commit, such as HEAD
:
# Check out "somefile.c" from `HEAD`, to overwrite any local, uncommitted
# changes
git checkout HEAD -- somefile.c
# Or check out a whole folder from `HEAD`:
git checkout HEAD -- some_directory
2. Going Further: How to check out any file or folder from any branch or commit hash into any location on your computer (VERY USEFUL!):
For individual files
# General form
git show my_branch_or_commit_hash:my_file.cpp > any/path/my_file.cpp
# Example: check out `main.cpp` from 3 commits ago in your currently-checked-out
# branch (3 commits prior to `HEAD`, or `HEAD~3`) into a temporary directory
mkdir ../temp
git show HEAD~3:main.cpp > ../temp/main_old.cpp
Source where I learned this: @Jakub Narębski's answer to: git-checkout older revision of a file under a new name
For whole directories
You can't use git show
like that for whole directories, but you can do these sequential steps instead to get an equivalent effect, so long as git status
is clean and you don't have any uncommitted changes:
# Check out the commit of interest
git checkout my_branch_or_commit_hash
# Copy the directory of interest to a temporary location
mkdir temp
cp -r path/to/dir_to_copy temp/
# Check out back to your original branch
# - Note that `-` here means "the previous branch you were on". It can also be
# written as `@{-1}`.
# - Therefore, calling `git checkout -` or `git checkout @{-1}` repeatedly
# will just keep toggling back and forth between the same two branches.
git checkout -
If your goal is just to reference a lot of old, deleted, or previous contents, however, it might be easier to just clone the repo again in a separate spot and check out the old branch in it. This way you can have one repo open for active work, and another open for referencing old data.
3. What if you're in the middle of resolving git merge
, git cherry-pick
, git rebase
, or git revert
changes?
Well, in that case, you better do the following. Note: to know which commit hash or branch --theirs
and --ours
are in each context, see my answer here: Who is "us" and who is "them" according to Git?:
# Keep `--theirs` for all conflicts within this file
git checkout --theirs -- path/to/some/file
# OR: keep `--ours` for all conflicts within this file
git checkout --ours -- path/to/some/file
OR:
# 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
Do NOT do the regular checkout
form in the previous section before this, unless that's what you really want to do. See the "WARNING WARNING WARNING" section in my answer referenced above: Who is "us" and who is "them" according to Git?.
DEALING WITH path does not have our version
or path does not have their version
ERRORS:
If you ever see errors like this:
error: path 'path/to/some/dir/file1.cpp' does not have our version
# OR
error: path 'path/to/some/dir/file1.cpp' does not have their version
...when running the commands above, then you simply need to git rm
those files first and then try the git checkout --ours
or git checkout --theirs
command again. See my answer here for a detailed explanation of these commands, including a form to automatically find and delete those errored files for you: git checkout --ours when file spec includes deleted file.
4. What if you want to reset a certain file or directory to exactly match that file or directory's state in another commit or branch?
In this case, git checkout my_branch -- some_file_or_dir
is NOT enough, because if you have files in the specified directory which exist in your currently-checked-out branch or commit but do NOT exist in my_branch
, then you'd like them to be deleted locally, but git checkout
does NOT delete any files which exist locally but not on the specified commit, rather, it only overwrites files locally with their versions from the specified commit. So, to also delete files locally which should not be there, so that what you end up with locally is an exact copy of what you have on commit or branch my_branch
, you must do the following:
# How to "hard reset" "path/to/some/file_or_dir" to its state exactly as it was
# at commit or branch `my_branch`
#
# WARNING: `git status` should be TOTALLY CLEAN before beginning this process!
# Otherwise, you risk PERMANENTLY LOSING any uncommitted changes shown by
# `git status`, since `git clean -fd` 'f'orce deletes ALL files
# and 'd'irectories which are in your current working tree (file system), but
# which are *not* in the path you specify below in commit or branch `my_branch`.
# Therefore, anything NOT already committed gets **permanently lost** as though
# you had used `rm` on it!
git reset my_branch -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd # SEE WARNING ABOVE!
git commit -m "hard reset path/to/some/file_or_dir to its state \
as it was at my_branch"
See my own answer here for more details on this: Why git can't do hard/soft resets by path?
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?
- [my answer] All about searching (via
grep
or similar) in your git repositories
- I show some more of these examples of
git checkout
in my answer here: Who is "us" and who is "them" according to Git?.
- [my answer on "How to do a --soft or --hard git reset by path"] Why git can't do hard/soft resets by path?
- git-checkout older revision of a file under a new name
- [my answer] git checkout --ours when file spec includes deleted file
- [my answer] Using git, how do you reset the working tree (local file system state) to the state of the index ("staged" files)?