In addition to making git_path() aware of certain file names that need to be handled differently e.g. when running in worktrees, the commit 557bd833bb (git_path(): be aware of file relocation in $GIT_DIR, 2014-11-30) also snuck in a new option for `git rev-parse`: `--git-path`. On the face of it, there is no obvious bug in that commit's diff: it faithfully calls git_path() on the argument and prints it out, i.e. `git rev-parse --git-path <filename>` has the same precise behavior as calling `git_path("<filename>")` in C. The problem lies deeper, much deeper. In hindsight (which is always unfair), implementing the .git/ directory discovery in `setup_git_directory()` by changing the working directory may have allowed us to avoid passing around a struct that contains information about the current repository, but it bought us many, many problems. In this case, when being called in a subdirectory, `git rev-parse` changes the working directory to the top-level directory before calling `git_path()`. In the new working directory, the result is correct. But in the working directory of the calling script, it is incorrect. Example: when calling `git rev-parse --git-path HEAD` in, say, the Documentation/ subdirectory of Git's own source code, the string `.git/HEAD` is printed. Side note: that bug is hidden when running in a subdirectory of a worktree that was added by the `git worktree` command: in that case, the (correct) absolute path of the `HEAD` file is printed. In the interest of time, this patch does not go the "correct" route to introduce a struct with repository information (and removing global state in the process), instead this patch chooses to detect when the command was called in a subdirectory and forces the result to be an absolute path. Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> --- Published-As: https://github.com/dscho/git/releases/tag/git-path-in-subdir-v1 Fetch-It-Via: git fetch https://github.com/dscho/git git-path-in-subdir-v1 builtin/rev-parse.c | 6 +++++- t/t0060-path-utils.sh | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index ff13e59e1d..f9d5762bf2 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -597,9 +597,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (!strcmp(arg, "--git-path")) { + const char *path; if (!argv[i + 1]) die("--git-path requires an argument"); - puts(git_path("%s", argv[i + 1])); + path = git_path("%s", argv[i + 1]); + if (prefix && !is_absolute_path(path)) + path = real_path(path); + puts(path); i++; continue; } diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 444b5a4df8..790584fcc5 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -271,6 +271,8 @@ relative_path "<null>" "<empty>" ./ relative_path "<null>" "<null>" ./ relative_path "<null>" /foo/a/b ./ +test_git_path "mkdir sub && cd sub && test_when_finished cd .. &&" \ + foo "$(pwd)/.git/foo" test_git_path A=B info/grafts .git/info/grafts test_git_path GIT_GRAFT_FILE=foo info/grafts foo test_git_path GIT_GRAFT_FILE=foo info/////grafts foo base-commit: 6e3a7b3398559305c7a239a42e447c21a8f39ff8 -- 2.11.1.windows.1