19

I have a non-bare repository at my server (dirs /home/andrew/web and /home/andrew/web/.git), set receive.denyCurrentBranch to ignore and created post-receive hook:

#!/bin/sh
GIT_WORK_TREE=/home/andrew/web git checkout -f

When I run sh .git/hooks/post-receive, everything works fine. But when I push from my PC I get this error:

remote: fatal: Not a git repository: '.'

Is there a way how can I solve this issue? And eventually without having to switch bare repo?

Thank you

EDIT: Here's my new post-receive hook. Why it is like this has been described in the accepted answer.

echo "\nChecking out $PWD"
GIT_DIR=/home/andrew/web/.git
GIT_WORK_TREE=/home/andrew/web git checkout -f
A123321
  • 837
  • 11
  • 21
  • What command do you use to push and particularly: what is the remote url? – Simon May 09 '12 at 00:28
  • @Simon I use "git push origin" and my remote URL is "andrew@example.com:web". – A123321 May 09 '12 at 00:55
  • You can try the full path `andrew@example.com:/home/andrew/web` but I think the real problem is that your remote repo is not bare. You could try to force push: `git push -f origin` – Simon May 09 '12 at 02:55
  • Ok so I gave bare repos another try and it is a hard struggle. I have web.git/ but want to have the files in web/ - I tried to set "git config core.worktree /home...web/" but then it complained about making no sense bare with worktree. I also tried to make a "ln -s /home...web.git .git" in the web/ dir, but then if I type "git log" in web/ and web.git/ I don't see the most recent commits in web/. Is there a way how to do this? – A123321 May 09 '12 at 08:50

1 Answers1

31

[Edit, Feb 2017: this old answer still gets hit a bit, so let's add a few notes. (1) This kind of live update is often a bad idea: be sure you know why you're doing it, and that you won't clobber your own work. (2) In Git since 2.3, you can now configure receive.denyCurrentBranch to updateInstead, plus a hook tweak in Git 2.4 and later. For details, see the git config documentation.]

The post-receive hook is run with $GIT_DIR set to .. This causes git to look for ./HEAD, ./refs/heads/master, etc., rather than .git/HEAD, .git/refs/heads/master, etc. But, since you don't do anything to change $PWD in the hook (as shown anyway), the hook will be running in the .git subdirectory (/home/andrew/web/.git), and hence this failure is quite mysterious: . will in fact be a valid git repository.

One standard trick that avoids hard-coding the path name is to use cd ..; git checkout -f as the post-receive hook. This is where the setting of $GIT_DIR becomes a problem, because after cd .. the hook is running in (still assuming this case) /home/andrew/web and of course at that point, $GIT_DIR should be .git rather than .. The standard fix for that is simply to unset GIT_DIR (setting it to .git would also work).

Your post-receive hook as shown works fine for me, though (with appropriate hard-coded-path changes). Then again I am pushing from a Unix-like machine, not a PC. Is it possible there's something else happening, that changes directories out of the .git subdirectory? You can do something like echo running in $PWD in the hook to see where you are.

torek
  • 448,244
  • 59
  • 642
  • 775
  • 1
    THANK YOU! I spent ages with this, nobody was able to explain. – A123321 May 09 '12 at 10:28
  • 3
    Setting `$GIT_DIR` to `.git` and `cd`ing to the directory worked for me. Thanks! – bkconrad Jun 05 '12 at 23:41
  • I just commented to another answer of you. Can I imagine `git checkout master -f` in a `hook` as the copying from a bare to a working dir when given `--git-dir` and `--work-tree` with `[f]orce`? – Timo May 15 '21 at 10:36
  • 1
    @Timo: I'm not sure what you're asking with this particular comment (I answered the other one already), but in general, the `--git-dir` and `--work-tree` options to the front end `git` command actually just set the environment variables `GIT_DIR` and `GIT_WORK_TREE`. The front end, `git`, prepares the environment, adds Git's own `git-core` directory to `$PATH`, does any other appropriate housekeeping, and then—having been invoked as `git [flags] subcommand [more-flags-and-args]`—runs `git-subcommand` with the extra flags and arguments. [continued] – torek May 15 '21 at 18:07
  • 1
    It's the setting of `GIT_WORK_TREE` in the environment, at this point, that causes Git commands that would normally stop because of `core.bare` being `true` to ignore the `core.bare` setting and go ahead and work with the specified working tree. So `git checkout master -f` runs `git-checkout master -f`, with those env variables set. The rest is up to the `git-checkout` program itself. [continued] – torek May 15 '21 at 18:09
  • 1
    What `git-checkout` does, provided it does anything at all, is: (1) make sure the working tree exists (either by creating it, or by testing it, who knows which version of Git will do which one). (2) read the index to see what it thinks will be there. (3) read the commit-to-be-checked-out to see what needs to be there (instead or in addition). (4) without `-f`, verify that it's OK to remove and/or replace any files that need to be swapped out and/or create any directories that need creating. With -f, just proceed. (6) remove, create, etc as needed. (7) update the index. – torek May 15 '21 at 18:11
  • 1
    The seven step process above leaves out a lot of detail. For instance, if the index describes a fully populated work-tree but the work-tree exists and *is empty*, Git realizes that the index is out of date and ignores it. This means you can move an existing work-tree out of the way, `mkdir` a new empty one, and populate it, for instance. This is "less efficient" (more files need to be copied out) but can enable the closest thing you can get to an atomic swap, if you need to update live software. – torek May 15 '21 at 18:14