setup: introduce startup_info->original_cwd

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>

Elijah Newren committed Dec 9, 2021 at 05:08 UTC e6f8861bd43636b27651150e6a3caa0a937fb418
3 files changed +71
cache.h
+2
@@ -1834,8 +1834,10 @@ void overlay_tree_on_index(struct index_state *istate,
1834 struct startup_info {
1835 int have_repository;
1836 const char *prefix;
1837 + const char *original_cwd;
1838 };
1839 extern struct startup_info *startup_info;
1840 +extern const char *tmp_original_cwd;
1841
1842 /* merge.c */
1843 struct commit_list;
common-main.c
+4
@@ -26,6 +26,7 @@ static void restore_sigpipe_to_default(void)
26 int main(int argc, const char **argv)
27 {
28 int result;
29 + struct strbuf tmp = STRBUF_INIT;
30
31 trace2_initialize_clock();
32
@@ -49,6 +50,9 @@ int main(int argc, const char **argv)
50 trace2_cmd_start(argv);
51 trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
52
53 + if (!strbuf_getcwd(&tmp))
54 + tmp_original_cwd = strbuf_detach(&tmp, NULL);
55 +
56 result = cmd_main(argc, argv);
57
58 trace2_cmd_exit(result);
setup.c
+65
@@ -12,6 +12,7 @@ static int work_tree_config_is_bogus;
12
13 static struct startup_info the_startup_info;
14 struct startup_info *startup_info = &the_startup_info;
15 +const char *tmp_original_cwd;
16
17 /*
18 * The input parameter must contain an absolute path, and it must already be
@@ -432,6 +433,69 @@ void setup_work_tree(void)
433 initialized = 1;
434 }
435
436 +static void setup_original_cwd(void)
437 +{
438 + struct strbuf tmp = STRBUF_INIT;
439 + const char *worktree = NULL;
440 + int offset = -1;
441 +
442 + if (!tmp_original_cwd)
443 + return;
444 +
445 + /*
446 + * startup_info->original_cwd points to the current working
447 + * directory we inherited from our parent process, which is a
448 + * directory we want to avoid removing.
449 + *
450 + * For convience, we would like to have the path relative to the
451 + * worktree instead of an absolute path.
452 + *
453 + * Yes, startup_info->original_cwd is usually the same as 'prefix',
454 + * but differs in two ways:
455 + * - prefix has a trailing '/'
456 + * - if the user passes '-C' to git, that modifies the prefix but
457 + * not startup_info->original_cwd.
458 + */
459 +
460 + /* Normalize the directory */
461 + strbuf_realpath(&tmp, tmp_original_cwd, 1);
462 + free((char*)tmp_original_cwd);
463 + tmp_original_cwd = NULL;
464 + startup_info->original_cwd = strbuf_detach(&tmp, NULL);
465 +
466 + /*
467 + * Get our worktree; we only protect the current working directory
468 + * if it's in the worktree.
469 + */
470 + worktree = get_git_work_tree();
471 + if (!worktree)
472 + goto no_prevention_needed;
473 +
474 + offset = dir_inside_of(startup_info->original_cwd, worktree);
475 + if (offset >= 0) {
476 + /*
477 + * If startup_info->original_cwd == worktree, that is already
478 + * protected and we don't need original_cwd as a secondary
479 + * protection measure.
480 + */
481 + if (!*(startup_info->original_cwd + offset))
482 + goto no_prevention_needed;
483 +
484 + /*
485 + * original_cwd was inside worktree; precompose it just as
486 + * we do prefix so that built up paths will match
487 + */
488 + startup_info->original_cwd = \
489 + precompose_string_if_needed(startup_info->original_cwd
490 + + offset);
491 + return;
492 + }
493 +
494 +no_prevention_needed:
495 + free((char*)startup_info->original_cwd);
496 + startup_info->original_cwd = NULL;
497 +}
498 +
499 static int read_worktree_config(const char *var, const char *value, void *vdata)
500 {
501 struct repository_format *data = vdata;
@@ -1330,6 +1394,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
1394 setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
1395 }
1396
1397 + setup_original_cwd();
1398
1399 strbuf_release(&dir);
1400 strbuf_release(&gitdir);