Subject: [PATCH 5/8] setup.c: allow other code to be notified when $CWD moves

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 28 Mar 2018 19:55:34 +0200

From: Duy Nguyen <[email-masked]>

When the current directory is moved, all relative paths may become
invalid because they are still relative to the old current
directory. At this point in setup_work_tree() many objects have been
initialized and some keep relative paths in their data structure.

$GIT_DIR and $GIT_WORK_TREE for example are the two examples which are
dealt with explicitly in this code: $GIT_WORK_TREE is reset to "." and
set_git_dir() is called the second time with a new relative path.

Introduce a more generic mechanism to let all code get notified and do
the path adjustment themselves instead of pulling all path adjustment
logic in here. The $GIT_DIR and $GIT_WORK_TREE adjustments will
removed from this code in later patches.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 cache.h |  5 +++++
 setup.c | 27 ++++++++++++++++++++++++++-
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/cache.h b/cache.h
index 05f32c9659..895abe7e7e 100644
--- a/cache.h
+++ b/cache.h
@@ -522,6 +522,11 @@ extern void set_git_work_tree(const char *tree);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
+typedef void (*cwd_updated_fn)(const char *old_cwd,
+			       const char *new_cwd,
+			       void *cb_data);
+void add_cwd_update_callback(cwd_updated_fn fn, void *cb_data);
+void removet_cwd_update_callback(cwd_updated_fn fn, void *cb_data);
 extern void setup_adjust_path(const char *name, char **path,
 			      const char *old_cwd,
 			      const char *new_cwd);
diff --git a/setup.c b/setup.c
index e26f44185e..e340ee2130 100644
--- a/setup.c
+++ b/setup.c
@@ -376,6 +376,24 @@ int is_inside_work_tree(void)
 	return inside_work_tree;
 }
 
+struct cwd_update_callback {
+	cwd_updated_fn fn;
+	void *cb_data;
+};
+
+static struct cwd_update_callback *cwd_callbacks;
+static int nr_cwd_callbacks;
+
+void add_cwd_update_callback(cwd_updated_fn fn, void *cb_data)
+{
+	struct cwd_update_callback *cb;
+
+	REALLOC_ARRAY(cwd_callbacks, nr_cwd_callbacks + 1);
+	cb = cwd_callbacks + nr_cwd_callbacks++;
+	cb->fn = fn;
+	cb->cb_data = cb_data;
+}
+
 void setup_adjust_path(const char *name, char **path,
 		       const char *old_cwd,
 		       const char *new_cwd)
@@ -398,8 +416,10 @@ void setup_adjust_path(const char *name, char **path,
 
 void setup_work_tree(void)
 {
-	const char *work_tree, *git_dir;
 	static int initialized = 0;
+	const char *work_tree, *git_dir;
+	struct strbuf old_cwd = STRBUF_INIT;
+	int i;
 
 	if (initialized)
 		return;
@@ -411,6 +431,7 @@ void setup_work_tree(void)
 	git_dir = get_git_dir();
 	if (!is_absolute_path(git_dir))
 		git_dir = real_path(get_git_dir());
+	strbuf_get_pwd_cwd(&old_cwd);
 	if (!work_tree || chdir(work_tree))
 		die(_("this operation must be run in a work tree"));
 
@@ -421,6 +442,10 @@ void setup_work_tree(void)
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
+	for (i = 0; i < nr_cwd_callbacks; i++)
+		cwd_callbacks[i].fn(old_cwd.buf, work_tree,
+				    cwd_callbacks[i].cb_data);
+	strbuf_release(&old_cwd);
 	set_git_dir(remove_leading_path(git_dir, work_tree));
 	initialized = 1;
 }
-- 
2.17.0.rc1.439.gca064e2955