169

I want to use something similar to:

git checkout -- <path>/<file>

but I want to checkout the file to some folder I choose, rather than the overwriting the local <path>/<file>.

Any idea?

Arulkumar
  • 12,966
  • 14
  • 47
  • 68
Rafid
  • 18,991
  • 23
  • 72
  • 108

11 Answers11

239

Another solution which is a bit cleaner - just specify a different work tree.

To checkout everything from your HEAD (not index) to a specific out directory:

git --work-tree=/path/to/outputdir checkout HEAD -- .

To checkout a subdirectory or file from your HEAD to a specific directory:

git --work-tree=/path/to/outputdir checkout HEAD -- subdirname
Adrian Macneil
  • 13,017
  • 5
  • 57
  • 70
  • 5
    Minor note - you do need an absolute path as shell tilde expansion doesn't occur, i.e. `--work-tree=/home/thomasg/okcopy` rather than `--work-tree=~/okcopy` (possibly using a relative path while sitting inside the same git tree works too, but that way lies madness and `git status` outputs in R'lyehian) – Tom Goodfellow May 29 '14 at 18:34
  • 10
    It works as expected with old commits too (e.g. give a SHA in place of HEAD), however `git status` then shows a lot of mods (presumably because the index now matches the other directory and not the untouched normal work tree). `git reset` got it back to a good state. – Tom Goodfellow May 30 '14 at 07:45
  • 2
    Same here `git status` shows a lot of mods, and `git reset` doesn't help. I had to `git checkout -f HEAD` to restore the state of my repo. – DUzun Jan 16 '15 at 13:13
  • 1
    Tom, the `=` can be replaced with a space and then the tilde expansion happens normally. It's a common convention for long options. – proski Jun 11 '15 at 00:33
  • 17
    FYI: you need to create the directory by yourself, otherwise you get this error: `fatal: This operation must be run in a work tree` – timaschew Feb 02 '16 at 11:27
  • 1
    @DUzun `git checkout -f HEAD` throws all local changes away, which could be dangerous. `git reset HEAD` is doing the right thing, just fix the difference between the stage and working directory. These changes are opposed, so if you "merge" them you get an empty diff. – timaschew Feb 03 '16 at 10:21
  • I tried several git commands but it doesn't seem work: - e.g. : git show sha1:FILEPATH > NEW LOCATION Could you provide some information how to solve this problem? – Odrai Apr 17 '16 at 12:50
  • git version 2.7.2 prints `fatal: Unable to create '/path/to/outputdir/.git/index.lock': No such file or directory` – sigy Jun 09 '16 at 08:03
  • Even though we have created a `demo` directory, when we run `git --work-tree=/demo checkout head~1 -- src/*` we receive the "fatal: This operation must be run in a work tree" error. – Shaun Luttin Oct 09 '16 at 20:51
  • Adrian, how can I point to remote `/path/to/outputdir`? `--work-tree=user@192.168.2.2:/home/user/path/to/outputdir` gives `fatal: Could not swith to` error. – horse Mar 11 '17 at 13:33
  • 18
    **This disastrously modifies the index of the main working tree,** which is typically bad. As @TomGoodfellow suggests, `git reset` suffices to restore the main working tree to sanity. To safely export repository subdirectories at any SHA1, branch, or tag *without* modifying the main working tree, see [Charles Bailey's](https://stackoverflow.com/users/19563/charles-bailey) [infamous `git archive` solution](https://stackoverflow.com/a/163769/2809027). Likewise, to safely checkout multiple branches at the same time, the new `git worktree add` subcommand is your friend. – Cecil Curry Aug 10 '17 at 06:45
  • Works great for me. Note that you get some unexpected behaviour if the directory isn't empty as the command doesn't warn you if you're targeting a non-empty directory. Be careful! – henryJack Feb 13 '18 at 11:30
64

As per Do a "git export" (like "svn export")?

You can use git checkout-index for that, this is a low level command, if you want to export everything, you can use -a,

git checkout-index -a -f --prefix=/destination/path/

To quote the man pages:

The final "/" [on the prefix] is important. The exported name is literally just prefixed with the specified string.

If you want to export a certain directory, there are some tricks involved. The command only takes files, not directories. To apply it to directories, use the 'find' command and pipe the output to git.

find dirname -print0 | git checkout-index --prefix=/path-to/dest/ -f -z --stdin

Also from the man pages:

Intuitiveness is not the goal here. Repeatability is.

Community
  • 1
  • 1
hasen
  • 161,647
  • 65
  • 194
  • 231
  • Sadly this won't work from a bare git repository, even with the --prefix set :-( (tested with git 1.9.1) – apeiros Jul 19 '14 at 23:58
  • 2
    if you want to check out a branch/commit of a bare git repository, use `GIT_WORK_TREE=../path/to/place git checkout` – allicoder Jan 29 '16 at 14:04
  • 7
    `git worktree` (see below) imho is the canonical answer today and might be added here. – geek-merlin May 29 '18 at 12:52
  • agree with @geek-merlin here is the official git documentation for that feature https://git-scm.com/docs/git-worktree I have been using it in work for the last year and had been very useful allows me to be on top of different features and answer questions for the team faster or put on hold some others to give priority to other things. – kisai Dec 17 '20 at 18:28
49

If you're working under your feature and don't want to checkout back to master, you can run:

cd ./myrepo

git worktree add ../myrepo_master master

git worktree remove ../myrepo_master

It will create ../myrepo_master directory with master branch commits, where you can continue work

itsnikolay
  • 17,415
  • 4
  • 65
  • 64
  • 3
    Best answer - simple, and unlike `git --work-tree=/path/to/outputdir checkout HEAD -- .` it doesn't do anything to the index, just copies the selected branch to the specified location (with the addition of a .git file). – Roger Dueck Oct 25 '17 at 16:43
  • And how would I undo this change? – Scott P. Jan 28 '19 at 23:55
  • @ScottP.just remove `myrepo_master` directory – itsnikolay Jan 31 '19 at 11:38
  • 2
    undoing this is a subcommand from worktree: `git worktree remove ../myrepo_master` – Martijn Dashorst Mar 23 '19 at 07:44
  • the first command adds / creates a working tree of the WHOLE repository, while the question was about checking out a specific file or a folder (a PART of the repository) into a specific place – user3804598 Feb 18 '21 at 09:50
34

For a single file:

git show HEAD:abspath/to/file > file.copy
Tobu
  • 24,771
  • 4
  • 91
  • 98
  • 2
    +1 and to clarify on the "certain previous commit" (instead of HEAD): It refers to the `SHA1 ID` that can easily be found via `gitk`. If I only need to "checkout" that file to a temporary location (i.e. not reverting), then I would use the `show` subcommand: `git show 82e54378856215ef96c5db1ff1160a741b5dcd70:MyProj/proguard/mapping.txt > myproj_mapping.txt` – ef2011 Oct 14 '12 at 23:49
20

The above solutions didn't work for me because I needed to check out a specific tagged version of the tree. That's how cvs export is meant to be used, by the way. git checkout-index doesn't take the tag argument, as it checks out files from index. git checkout <tag> would change the index regardless of the work tree, so I would need to reset the original tree. The solution that worked for me was to clone the repository. Shared clone is quite fast and doesn't take much extra space. The .git directory can be removed if desired.

git clone --shared --no-checkout <repository> <destination>
cd <destination>
git checkout <tag>
rm -rf .git

Newer versions of git should support git clone --branch <tag> to check out the specified tag automatically:

git clone --shared --branch <tag> <repository> <destination>
rm -rf <destination>/.git
proski
  • 3,603
  • 27
  • 27
  • 3
    `git --work-tree=/path/to/outputdir checkout -- .` didn't work for you? – warvariuc Dec 03 '13 at 06:58
  • 1
    No, it doesn't. The actual file in the original work directory is unchanged, but the staged version is lost and replaced with the version from the tag. It's especially bad if the staged version contained a carefully picked set of changes to be committed. I don't want the export command to touch my index, period. – proski Jun 11 '15 at 00:27
12

Adrian's answer threw "fatal: This operation must be run in a work tree." The following is what worked for us.

git worktree add <new-dir> --no-checkout --detach
cd <new-dir>
git checkout <some-ref> -- <existing-dir>

Notes:

  • --no-checkout Do not checkout anything into the new worktree.
  • --detach Do not create a new branch for the new worktree.
  • <some-ref> works with any ref, for instance, it works with HEAD~1.
  • Cleanup with git worktree prune.
Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
  • +1 for commenting on how to cleanup - everyone else is happy to make a mess of the current repo. only this one has no side-effects. – Andrew Hill Sep 03 '18 at 01:01
4

Use git archive branch-index | tar -x -C your-folder-on-PC to clone a branch to another folder. I think, then you can copy any file that you need

dqthe
  • 683
  • 5
  • 8
2

Addition to @hasen's answer. You might want to use git ls-files instead of find to list files to checkout like:

git ls-files -z *.txt | git checkout-index --prefix=/path-to/dest/ -f -z --stdin

git ls-files ignores uncommitted files.

snipsnipsnip
  • 2,268
  • 2
  • 33
  • 34
  • Thank you for properly using `-z`! Nice that you provide script that's not vulnerable to certain kinds of injection attacks. – ErikE Jul 11 '20 at 00:13
1

I'm using this alias for checking out a branch in a temporary directory:

[alias]
    cot = "!TEMP=$(mktemp -d); f() { git worktree prune && git worktree add $TEMP $1 && zsh -c \"cd $TEMP; zsh\";}; f" # checkout branch in temporary directory

Usage:

git cot mybranch

You are then dropped in a new shell in the temporary directory where you can work on the branch. You can even use git commands in this directory.

When you're done, delete the directory and run:

git worktree prune

This is also done automatically in the alias, before adding a new worktree.

Felix Dietze
  • 602
  • 7
  • 16
0

I defined an git alias to achieve just this (before I found this question).

It's a short bash function which saves the current path, switch to the git repo, does a checkout and return where it started.

git checkto develop ~/my_project_git

This e.g. would checkout the develop branch into "~/my_project_git" directory.

This is the alias code inside ~/.gitconfig:

[alias]
    checkTo = "!f(){ [ -z \"$1\" ] && echo \"Need to specify branch.\" && \
               exit 1; [ -z \"$2\" ] && echo \"Need to specify target\
               dir\" && exit 2; cDir=\"$(pwd)\"; cd \"$2\"; \
               git checkout \"$1\"; cd \"$cDir\"; };f"
cb0
  • 8,415
  • 9
  • 52
  • 80
-1

If you are working in mydir, why not simply cp -r mydir mydir2 and then cd mydir2; git checkout commit#

Sam Weisenthal
  • 2,791
  • 9
  • 28
  • 66