]> git.ipfire.org Git - thirdparty/git.git/commitdiff
setup: introduce startup_info->original_cwd
authorElijah Newren <newren@gmail.com>
Thu, 9 Dec 2021 05:08:26 +0000 (05:08 +0000)
committerJunio C Hamano <gitster@pobox.com>
Thu, 9 Dec 2021 21:33:12 +0000 (13:33 -0800)
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.

Acked-by: Derrick Stolee <stolee@gmail.com>
Acked-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
cache.h
common-main.c
setup.c

diff --git a/cache.h b/cache.h
index eba12487b99caae71cbd4367e609e156ea9867ff..92e181ea7594301953b3cc59c88341b15118f184 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1834,8 +1834,10 @@ 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;
+extern const char *tmp_original_cwd;
 
 /* merge.c */
 struct commit_list;
index 71e21dd20a3b141bed0d37512cdc9196321dc315..aa8d5aba5bbfdd941e62020e41e83c781058c6cf 100644 (file)
@@ -26,6 +26,7 @@ static void restore_sigpipe_to_default(void)
 int main(int argc, const char **argv)
 {
        int result;
+       struct strbuf tmp = STRBUF_INIT;
 
        trace2_initialize_clock();
 
@@ -49,6 +50,9 @@ int main(int argc, const char **argv)
        trace2_cmd_start(argv);
        trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
 
+       if (!strbuf_getcwd(&tmp))
+               tmp_original_cwd = strbuf_detach(&tmp, NULL);
+
        result = cmd_main(argc, argv);
 
        trace2_cmd_exit(result);
diff --git a/setup.c b/setup.c
index 347d7181ae907c027f01cbc8ec42a2b64de06ebc..af3b8c09abe398edd0f45828b81ecec72b3900b6 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -12,6 +12,7 @@ static int work_tree_config_is_bogus;
 
 static struct startup_info the_startup_info;
 struct startup_info *startup_info = &the_startup_info;
+const char *tmp_original_cwd;
 
 /*
  * The input parameter must contain an absolute path, and it must already be
@@ -432,6 +433,69 @@ 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;
+
+       if (!tmp_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 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, tmp_original_cwd, 1);
+       free((char*)tmp_original_cwd);
+       tmp_original_cwd = NULL;
+       startup_info->original_cwd = strbuf_detach(&tmp, NULL);
+
+       /*
+        * Get our worktree; we only protect the current working directory
+        * if it's in the worktree.
+        */
+       worktree = get_git_work_tree();
+       if (!worktree)
+               goto no_prevention_needed;
+
+       offset = dir_inside_of(startup_info->original_cwd, worktree);
+       if (offset >= 0) {
+               /*
+                * If startup_info->original_cwd == worktree, that is already
+                * protected and we don't need original_cwd as a secondary
+                * protection measure.
+                */
+               if (!*(startup_info->original_cwd + offset))
+                       goto no_prevention_needed;
+
+               /*
+                * 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);
+               return;
+       }
+
+no_prevention_needed:
+       free((char*)startup_info->original_cwd);
+       startup_info->original_cwd = NULL;
+}
+
 static int read_worktree_config(const char *var, const char *value, void *vdata)
 {
        struct repository_format *data = vdata;
@@ -1330,6 +1394,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
                setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
        }
 
+       setup_original_cwd();
 
        strbuf_release(&dir);
        strbuf_release(&gitdir);