set_work_tree: use chdir_notify

When we change to the top of the working tree, we manually re-adjust $GIT_DIR and call set_git_dir() again, in order to update any relative git-dir we'd compute earlier. Instead of the work-tree code having to know to call the git-dir code, let's use the new chdir_notify interface. There are two spots that need updating, with a few subtleties in each: 1. the set_git_dir() code needs to chdir_notify_register() so it can be told when to update its path. Technically we could push this down into repo_set_gitdir(), so that even repository structs besides the_repository could benefit from this. But that opens up a lot of complications: - we'd still need to touch set_git_dir(), because it does some other setup (like setting $GIT_DIR in the environment) - submodules using other repository structs get cleaned up, which means we'd need to remove them from the chdir_notify list - it's unlikely to fix any bugs, since we shouldn't generally chdir() in the middle of working on a submodule 2. setup_work_tree now needs to call chdir_notify(), and can lose its manual set_git_dir() call. Note that at first glance it looks like this undoes the absolute-to-relative optimization added by 044bbbcb63 (Make git_dir a path relative to work_tree in setup_work_tree(), 2008-06-19). But for the most part that optimization was just _undoing_ the relative-to-absolute conversion which the function was doing earlier (and which is now gone). It is true that if you already have an absolute git_dir that the setup_work_tree() function will no longer make it relative as a side effect. But: - we generally do have relative git-dir's due to the way the discovery code works - if we really care about making git-dir's relative when possible, then we should be relativizing them earlier (e.g., when we see an absolute $GIT_DIR we could turn it relative, whether we are going to chdir into a worktree or not). That would cover all cases, including ones that 044bbbcb63 did not. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Mar 30, 2018 at 14:35 UTC 8500e0de3fe2e33ba503d432a4a5301ce2fb60fa
2 files changed +25 -7
environment.c
+22 -1
@@ -13,6 +13,7 @@
13 #include "refs.h"
14 #include "fmt-merge-msg.h"
15 #include "commit.h"
16 +#include "chdir-notify.h"
17
18 int trust_executable_bit = 1;
19 int trust_ctime = 1;
@@ -296,7 +297,7 @@ char *get_graft_file(void)
297 return the_repository->graft_file;
298 }
299
299 -void set_git_dir(const char *path)
300 +static void set_git_dir_1(const char *path)
301 {
302 if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
303 die("could not set GIT_DIR to '%s'", path);
@@ -304,6 +305,26 @@ void set_git_dir(const char *path)
305 setup_git_env();
306 }
307
308 +static void update_relative_gitdir(const char *name,
309 + const char *old_cwd,
310 + const char *new_cwd,
311 + void *data)
312 +{
313 + char *path = reparent_relative_path(old_cwd, new_cwd, get_git_dir());
314 + trace_printf_key(&trace_setup_key,
315 + "setup: move $GIT_DIR to '%s'",
316 + path);
317 + set_git_dir_1(path);
318 + free(path);
319 +}
320 +
321 +void set_git_dir(const char *path)
322 +{
323 + set_git_dir_1(path);
324 + if (!is_absolute_path(path))
325 + chdir_notify_register(NULL, update_relative_gitdir, NULL);
326 +}
327 +
328 const char *get_log_output_encoding(void)
329 {
330 return git_log_output_encoding ? git_log_output_encoding
setup.c
+3 -6
@@ -3,6 +3,7 @@
3 #include "config.h"
4 #include "dir.h"
5 #include "string-list.h"
6 +#include "chdir-notify.h"
7
8 static int inside_git_dir = -1;
9 static int inside_work_tree = -1;
@@ -378,7 +379,7 @@ int is_inside_work_tree(void)
379
380 void setup_work_tree(void)
381 {
381 - const char *work_tree, *git_dir;
382 + const char *work_tree;
383 static int initialized = 0;
384
385 if (initialized)
@@ -388,10 +389,7 @@ void setup_work_tree(void)
389 die(_("unable to set up work tree using invalid config"));
390
391 work_tree = get_git_work_tree();
391 - git_dir = get_git_dir();
392 - if (!is_absolute_path(git_dir))
393 - git_dir = real_path(get_git_dir());
394 - if (!work_tree || chdir(work_tree))
392 + if (!work_tree || chdir_notify(work_tree))
393 die(_("this operation must be run in a work tree"));
394
395 /*
@@ -401,7 +399,6 @@ void setup_work_tree(void)
399 if (getenv(GIT_WORK_TREE_ENVIRONMENT))
400 setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
401
404 - set_git_dir(remove_leading_path(git_dir, work_tree));
402 initialized = 1;
403 }
404