1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-10-31 22:37:54 +01:00
git/chdir-notify.c
Jeff King 2b5ed37365 add chdir-notify API
If one part of the code does a permanent chdir(), then this
invalidates any relative paths that may be held by other
parts of the code. For example, setup_work_tree() moves us
to the top of the working tree, which may invalidate a
previously stored relative gitdir.

We've hacked around this case by teaching setup_work_tree()
to re-run set_git_dir() with an adjusted path, but this
stomps all over the idea of module boundaries.
setup_work_tree() shouldn't have to know all of the places
that need to be fed an adjusted path. And indeed, there's at
least one other place (the refs code) which needs adjusting.

Let's provide an API to let code that stores relative paths
"subscribe" to updates to the current working directory.
This means that callers of chdir() don't need to know about
all subscribers ahead of time; they can simply consult a
dynamically built list.

Note that our helper function to reparent relative paths
uses the simple remove_leading_path(). We could in theory
use the much smarter relative_path(), but that led to some
problems as described in 41894ae3a3 (Use simpler
relative_path when set_git_dir, 2013-10-14). Since we're
aiming to replace the setup_work_tree() code here, let's
follow its lead.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-30 12:49:57 -07:00

93 lines
1.8 KiB
C

#include "cache.h"
#include "chdir-notify.h"
#include "list.h"
#include "strbuf.h"
struct chdir_notify_entry {
const char *name;
chdir_notify_callback cb;
void *data;
struct list_head list;
};
static LIST_HEAD(chdir_notify_entries);
void chdir_notify_register(const char *name,
chdir_notify_callback cb,
void *data)
{
struct chdir_notify_entry *e = xmalloc(sizeof(*e));
e->name = name;
e->cb = cb;
e->data = data;
list_add_tail(&e->list, &chdir_notify_entries);
}
static void reparent_cb(const char *name,
const char *old_cwd,
const char *new_cwd,
void *data)
{
char **path = data;
char *tmp = *path;
if (!tmp)
return;
*path = reparent_relative_path(old_cwd, new_cwd, tmp);
free(tmp);
if (name) {
trace_printf_key(&trace_setup_key,
"setup: reparent %s to '%s'",
name, *path);
}
}
void chdir_notify_reparent(const char *name, char **path)
{
chdir_notify_register(name, reparent_cb, path);
}
int chdir_notify(const char *new_cwd)
{
struct strbuf old_cwd = STRBUF_INIT;
struct list_head *pos;
if (strbuf_getcwd(&old_cwd) < 0)
return -1;
if (chdir(new_cwd) < 0) {
int saved_errno = errno;
strbuf_release(&old_cwd);
errno = saved_errno;
return -1;
}
trace_printf_key(&trace_setup_key,
"setup: chdir from '%s' to '%s'",
old_cwd.buf, new_cwd);
list_for_each(pos, &chdir_notify_entries) {
struct chdir_notify_entry *e =
list_entry(pos, struct chdir_notify_entry, list);
e->cb(e->name, old_cwd.buf, new_cwd, e->data);
}
strbuf_release(&old_cwd);
return 0;
}
char *reparent_relative_path(const char *old_cwd,
const char *new_cwd,
const char *path)
{
char *ret, *full;
if (is_absolute_path(path))
return xstrdup(path);
full = xstrfmt("%s/%s", old_cwd, path);
ret = xstrdup(remove_leading_path(full, new_cwd));
free(full);
return ret;
}