From: Elijah Newren <newren@xxxxxxxxx> Removing the current working directory causes all subsequent git commands run from that directory to get confused and fail with a message about being unable to read the current working directory: $ git status fatal: Unable to read current working directory: No such file or directory Non-git commands likely have similar warnings or even errors, e.g. $ bash -c 'echo hello' shell-init: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory hello This confuses end users, particularly since the command they get the error from is not the one that caused the problem; the problem came from the side-effect of some previous command. We would like to avoid removing the current working directory of our parent process; towards this end, introduce a new variable, startup_info->original_cwd, that tracks the current working directory that we inherited from our parent process. For convenience of later comparisons, we prefer that this new variable store a path relative to the toplevel working directory (thus much like 'prefix'), except without the trailing slash. Subsequent commits will make use of this new variable. Signed-off-by: Elijah Newren <newren@xxxxxxxxx> --- cache.h | 1 + git.c | 2 ++ setup.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/cache.h b/cache.h index eba12487b99..d7903c65b57 100644 --- a/cache.h +++ b/cache.h @@ -1834,6 +1834,7 @@ void overlay_tree_on_index(struct index_state *istate, struct startup_info { int have_repository; const char *prefix; + const char *original_cwd; }; extern struct startup_info *startup_info; diff --git a/git.c b/git.c index 5ff21be21f3..2c98ab48936 100644 --- a/git.c +++ b/git.c @@ -866,6 +866,8 @@ int cmd_main(int argc, const char **argv) trace_command_performance(argv); + startup_info->original_cwd = xgetcwd(); + /* * "git-xxxx" is the same as "git xxxx", but we obviously: * diff --git a/setup.c b/setup.c index 347d7181ae9..f30657723ea 100644 --- a/setup.c +++ b/setup.c @@ -432,6 +432,54 @@ void setup_work_tree(void) initialized = 1; } +static void setup_original_cwd(void) +{ + struct strbuf tmp = STRBUF_INIT; + const char *worktree = NULL; + int offset = -1; + + /* + * startup_info->original_cwd wass set early on in cmd_main(), unless + * we're an auxiliary tool like git-remote-http or test-tool. + */ + if (!startup_info->original_cwd) + return; + + /* + * startup_info->original_cwd points to the current working + * directory we inherited from our parent process, which is a + * directory we want to avoid incidentally removing. + * + * For convience, we would like to have the path relative to the + * worktree instead of an absolute path. + * + * Yes, startup_info->original_cwd is usually the same as 'prefix', + * but differs in two ways: + * - prefix has a trailing '/' + * - if the user passes '-C' to git, that modifies the prefix but + * not startup_info->original_cwd. + */ + + /* Normalize the directory */ + strbuf_realpath(&tmp, startup_info->original_cwd, 1); + free((char*)startup_info->original_cwd); + startup_info->original_cwd = strbuf_detach(&tmp, NULL); + + /* Find out if this is in the worktree */ + worktree = get_git_work_tree(); + if (worktree) + offset = dir_inside_of(startup_info->original_cwd, worktree); + if (offset >= 0) { + /* + * original_cwd was inside worktree; precompose it just as + * we do prefix so that built up paths will match + */ + startup_info->original_cwd = \ + precompose_string_if_needed(startup_info->original_cwd + + offset); + } +} + static int read_worktree_config(const char *var, const char *value, void *vdata) { struct repository_format *data = vdata; @@ -1330,6 +1378,7 @@ const char *setup_git_directory_gently(int *nongit_ok) setenv(GIT_PREFIX_ENVIRONMENT, "", 1); } + setup_original_cwd(); strbuf_release(&dir); strbuf_release(&gitdir); -- gitgitgadget