14

On the server I have bare repository which is origin for development process and to simplify deployment to QA environment.

So in post-receive it simply does

GIT_WORK_TREE=/home/dev git checkout -f

But as product gets more complicated there are some other things should be happening. So now it is handled by deploy.sh script which is also tracked by repository. So what I want to do is to be able instead of checking out whole repository is to checkout only deploy.sh and run it. I thought something like that would work:

SOURCE_PATH="/home/dev"
GIT_WORK_TREE=$SOURCE_PATH git checkout deploy.sh
$SOURCE_PATH"/deploy.sh"

But it does not work giving error:

error: pathspec 'deploy.sh' did not match any file(s) known to git.

What am I doing wrong? Or is it just impossible to do this way?

Alexey Kamenskiy
  • 2,560
  • 3
  • 31
  • 51

3 Answers3

16

As I explain in "checkout only one file from git", you cannot checkout just one file without cloning or fetching first.

But you git show that file, which means you can dump its content into a /another/path./deploy.sh file, and execute that file.

git-show HEAD:full/repo/path/to/deploy.sh > /another/path./deploy.sh
/another/path./deploy.sh

Since you execute that from a post-receive hook, the git-show will show the latest version of the deploy.sh file.


The other alternative would be try

 GIT_WORK_TREE=$SOURCE_PATH git checkout -- path/to/deploy.sh

And checkout only that file, directly in your working tree.

The '--' help the git command to understand it is a file, not another parameter like a tag or a named branch.

From the OP AlexKey's test, it requires that the working tree has been checked out (fully) at least once.

Community
  • 1
  • 1
VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
  • Maybe that's right, but it is a bit weird. So in bare repository we actually can checkout by `simulating` appended work tree, but that will allow us checkout whole tree only. Which sounds for me not really `thought through` implementation. Right now (for time saving purpose) i do checkout all tree twice, first in hook and then in deployment script. Meanwhile i will look for a proper solution and if will find nothing then only thing i will have to do is to use this `trick`. – Alexey Kamenskiy Nov 28 '12 at 10:43
  • @AlexKey did you try to checkout just `deploy.sh` with the `--`? I believe this is what you wanted to do. – VonC Nov 28 '12 at 10:44
  • Haven't tried that solution. In that case what exactly would path/to/deploy.sh be? For example if in my repo this script is at `./deploy.sh` the command will look like `GIT_WORK_TREE=$SOURCE_PATH git checkout -- deploy.sh`? – Alexey Kamenskiy Nov 28 '12 at 10:47
  • +1 Thanks, i did not know about `--`. This is exactly what i needed. – Alexey Kamenskiy Nov 28 '12 at 10:53
  • Seems that this works only if the whole tree was already checked out at least once. Otherwise it gives same error. – Alexey Kamenskiy Nov 28 '12 at 11:36
  • @AlexKey ok, back to the `git show` then: you would not need to checkout the tree twice that way, just to get `deploy.sh`. – VonC Nov 28 '12 at 11:45
  • There i meant that once it can be checked out manually. But then I can remove resulting tree and on next push and later on it will checkout only that file with no problem. I am not much aware of how this works in git, but assume that on first checkout it builds index of files that were checked out or something like that and then uses it to find file if you try to check it out. Still will try experiment with it. However, now on my server `--` solution works fine. I only had to do `git checkout -f` first time. – Alexey Kamenskiy Nov 28 '12 at 12:03
  • @AlexKey not sure what you mean: the idea (with `git show`) is to dump the content of one file (outside of any git repo), and then that file does all the checkout you need. – VonC Nov 28 '12 at 12:05
  • Yes, i got that. But out of chase for perfection i would like to find more beautiful solution :) I definitely accept that option with `git-show`, thus i marked this answer as accepted, but if we talk about alternative `--` option, which is also present in your answer, then i would like to be more precise and specify how it works. Otherwise someone who will read this answer later may run into the same issue as me when it still produces the error. – Alexey Kamenskiy Nov 28 '12 at 12:19
  • @AlexKey so you are saying that, if the working tree hasn't been checked out at least once, the `git checkout` will fail, right? – VonC Nov 28 '12 at 12:26
  • Exactly. I have seen your edit and immediately tried it on test repo on my local system and it worked because i already used there `GIT_WORK_TREE=/path git checkout -f`. And then i implemented this on our QA server, tested and it produced error, then i did same `git checkout -f` on QA server and after that `--` works as a charm. – Alexey Kamenskiy Nov 28 '12 at 12:29
  • @AlexKey and now that a full checkout has been done once, could we say the next checkout of that one file will always work? I have edited the answer to make that step more apparent. – VonC Nov 28 '12 at 13:22
  • exactly like that. I have no idea why is it happening this way, but it does. I've tried on git v1.5 and on v1.7. Same outcome on both versions. – Alexey Kamenskiy Nov 29 '12 at 03:52
  • Nice, but in the first method (e.g. inside a hook) how to preserve file permissions? – AkiRoss Dec 13 '14 at 13:35
  • @AkiRoss which kind of permissions do you want to preserve? Git only records two (644 755: http://stackoverflow.com/a/26603078/6309). It is best in the hook to configure it in order for a file to be checked out with the expected permission (like with umask) – VonC Dec 13 '14 at 13:38
  • @VonC Only the executable flag: I'm creating a server hook that uses `git show` to get a file, but the executable permission is lost. I would like to keep it. – AkiRoss Dec 13 '14 at 13:43
  • @AkiRoss if need be, a simple chmod in the hook would ensure that this file regain the expected protection. – VonC Dec 13 '14 at 13:44
  • The problem is that some files are +x, others -x. – AkiRoss Dec 13 '14 at 14:37
  • @AkiRoss what is the value of git config core.fileMode? (http://git-scm.com/docs/git-config). Git depends on the system to restore the proper protection, and it depends on the specifics of the environment. See for instance http://superuser.com/q/512393/141 or http://stackoverflow.com/q/20958888/6309. Interesting read: http://git.661346.n2.nabble.com/why-not-preserve-file-permissions-td1619926.html – VonC Dec 13 '14 at 14:44
  • 1
    Problem was that at a certain commit the executable flag may be different from a previous commit, and before doing chmod I had to know what were the current permissions: I ended up using the first column of `git ls-tree -r master`. – AkiRoss Dec 15 '14 at 23:24
3

I know this is ooooooooold, but I found my own use-case for this functionality and looked around for a while for a better solution to this before combining a few solutions into a simple one-liner:

GIT_WORK_TREE=/home/dev git checkout $branch -- deploy.sh

In my case, I just wanted to be able to "peek" into some of my bare repositories without unpacking the whole thing (some of them are huge). People were talking about sparse checkouts and other such things, but I just needed one-off functionality. To check out just the "Documents/Health Records" folder, for example, I would do the following:

GIT_WORK_TREE=/tmp/my-files git checkout master -- "Documents/Health Records"

And lo! There it did appear.

kael
  • 6,095
  • 5
  • 17
  • 24
  • So this was really useful for me as I'm trying to deploy a part of a monorepo after using Capistrano with `:repo_tree`. (See https://stackoverflow.com/questions/57360952/linking-to-multiple-subdirectories-using-repo-tree ) – jwir3 Aug 10 '19 at 14:39
2

This git show or similar git cat-file blob approaches work more-or-less fine for text files, but they are hopeless for binary files.

Better approach which works reliably for any kind of files and even allows to checkout entire folders:

git archive mybranch folder/file.txt --output result.tar

It creates a tar archive with desired content, exactly the file that sits in the source control. Works perfectly fine with binary files.

The only thing you need to do is to extract this tar file

tar -xf result.tar

See my blogpost for more details

mnaoumov
  • 1,988
  • 2
  • 19
  • 29