1

I have a valid git repository named /tmp/A. When I cd to the /tmp/A and run any git command from bash it works good, but when I run git commands from callCommand call in haskell programm I get error:

fatal: Not a git repository: '.'

If I run in callCommand pwd and ls -la before git command, like:

callCommand $ "pwd; ls -la; git status"

It shows that it is at right path /tmp/A and ls shows that repository exists and has .git directory, but git returns error.

What wrong I do?

Upd.

ls output is

drwxrwxr-x 11 xxx xxx 4096 Mar 22 11:44 .
drwxrwxr-x  3 xxx xxx 4096 Mar 22 11:44 ..
drwxrwxr-x  8 xxx xxx 4096 Mar 22 11:44 .git
-rw-rw-r--  1 xxx xxx  270 Mar 22 11:44 .gitignore
drwxrwxr-x  7 xxx xxx 4096 Mar 22 11:44 dir2
drwxrwxr-x  8 xxx xxx 4096 Mar 22 11:44 dir1
drwxrwxr-x  9 xxx xxx 4096 Mar 22 11:44 test

Upd. Upd.

Program which fails is called from hook post-receive of cloned git repository. When run same program not from hook of this repository, it works fine. Why self cloning repository from hook does not works?

Shadasviar
  • 466
  • 5
  • 15

1 Answers1

2

TL;DR: you probably want unset GIT_DIR at the front of your script (well, however Haskell spells this operation).


The issue here is that while /tmp/A is a sensible Git repository, the actual repository itself is in the .git sub-directory. That is, /tmp/A/ is the work-tree and /tmp/A/.git is the repository proper.

In normal operation, we run git subcommand arguments ..., e.g., git status or git commit -m message from our work-trees. The work-tree contains a .git directory or, in some cases,1 a .git file. The top-level git command checks for .git, finds it, and says aha, the repository is ./.git. Or, it does not find .git, so it looks one level up—e.g., from /tmp/A it would climb one level to /tmp itself, and check there for .git. This repeats until some sort of stopping point.2

In all cases, this search process must either stop successfully—by finding the repository proper—or else git subcommand dies with the fatal: Not a git repository ... message. Now comes the critical, and hidden in plain sight, secret: At this point, the top-level git command sets an environment variable named GIT_DIR to contain the path name of the actual repository. It then runs the sub-command, which uses $GIT_DIR to locate the repository. In our normal use-case, where we are in /tmp/A and /tmp/A contains a .git directory, this sets $GIT_DIR to /tmp/A/.git and the sub-commands all work.

But in your case, $GIT_DIR is already set. Specifially, it's set to .. So now the top-level git command stops searching. It just verifies that $GIT_DIR is valid and names a Git repository, or dies with the fatal error message. Since /tmp/A isn't a repository—it's really /tmp/A/.git—this causes the problem.

Unsetting the environment variable turns the search back on, fixing the problem. You can also use --git-dir as an argument, or set a correct value in $GIT_DIR. Note that the same rules apply with $GIT_WORK_TREE: the --work-tree argument sets $GIT_WORK_TREE, and if you don't use that and $GIT_WORK_TREE isn't set, the front-end git command uses its climb up the file system tree to a .git directory or file code to find the root of your work-tree. So if $GIT_DIR or $GIT_WORK_TREE are set when you run git subcommand, Git obeys them unless you override them with --git-dir and/or --work-tree.

Hooks always have $GIT_DIR set. They may or may not have $GIT_WORK_TREE set as well. Most of them run in the top level of the work-tree, but, as the githooks documentation notes, pre-receive, update, post-receive, post-update, and push-to-checkout all run in $GIT_DIR.


1The .git-as-a-file trick is used by both submodules and added work-trees, in modern Git. Git 1.7 and early 1.8 left submodules with embedded .git directories, and does not support added work-trees.

2The obvious stopping point is upon reaching /, when there is nowhere left to climb. However, Git is by default careful, at least on Unix and Unix-like systems, to avoid climbing through a mount point. Mount points allow you to graft different file system layers and/or storage pools into a single tree-structured hierarchy. Typically /tmp itself might be a memory file system, for instance, and large systems might isolate "system storage" (/ and the like) from "user storage" (/home). Docker uses mount points to restructure file systems in the docker image, and so on.

torek
  • 448,244
  • 59
  • 642
  • 775