From fe9aa0b22e18e5cd1ae4a9323edad0d3c8177c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 25 Sep 2016 10:14:36 +0700 Subject: init: correct re-initialization from a linked worktree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When 'git init' is called from a linked worktree, we treat '.git' dir (which is $GIT_COMMON_DIR/worktrees/something) as the main '.git' (i.e. $GIT_COMMON_DIR) and populate the whole repository skeleton in there. It does not harm anything (*) but it is still wrong. Since 'git init' calls set_git_dir() at preparation time, which indirectly calls get_common_dir() and correctly detects multiple worktree setup, all git_path_buf() calls in create_default_files() will return correct paths in both single and multiple worktree setups. The only thing left is copy_templates(), which targets $GIT_DIR, not $GIT_COMMON_DIR. Fix that with get_git_common_dir(). This function will return $GIT_DIR in single-worktree setup, so we don't have to make a special case for multiple-worktree here. (*) It does in fact, thanks to another bug. More on that later. Noticed-by: Max Nordlund Helped-by: Michael J Gruber Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/init-db.c b/builtin/init-db.c index cc09fca..d5d7558 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -138,7 +138,7 @@ static void copy_templates(const char *template_dir) goto close_free_return; } - strbuf_addstr(&path, get_git_dir()); + strbuf_addstr(&path, get_git_common_dir()); strbuf_complete(&path, '/'); copy_templates_1(&path, &template_path, dir); close_free_return: diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 8ffbbea..488564b 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -393,4 +393,19 @@ test_expect_success 'remote init from does not use config from cwd' ' test_cmp expect actual ' +test_expect_success 're-init from a linked worktree' ' + git init main-worktree && + ( + cd main-worktree && + test_commit first && + git worktree add ../linked-worktree && + mv .git/info/exclude expected-exclude && + find .git/worktrees -print | sort >expected && + git -C ../linked-worktree init && + test_cmp expected-exclude .git/info/exclude && + find .git/worktrees -print | sort >actual && + test_cmp expected actual + ) +' + test_done -- cgit v0.10.2-6-g49f6 From 33158701e2cac4244a236cd18d9d336f66e535f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 25 Sep 2016 10:14:37 +0700 Subject: init: call set_git_dir_init() from within init_db() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The next commit requires that set_git_dir_init() must be called before init_db(). Let's make sure nobody can do otherwise. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/clone.c b/builtin/clone.c index 6616392..29b1832 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -928,23 +928,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix) set_git_work_tree(work_tree); } - junk_git_dir = git_dir; + junk_git_dir = real_git_dir ? real_git_dir : git_dir; if (safe_create_leading_directories_const(git_dir) < 0) die(_("could not create leading directories of '%s'"), git_dir); - set_git_dir_init(git_dir, real_git_dir, 0); - if (real_git_dir) { - git_dir = real_git_dir; - junk_git_dir = real_git_dir; - } - if (0 <= option_verbosity) { if (option_bare) fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir); else fprintf(stderr, _("Cloning into '%s'...\n"), dir); } - init_db(option_template, INIT_DB_QUIET); + + init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET); + + if (real_git_dir) + git_dir = real_git_dir; + write_config(&option_config); git_config(git_default_config, NULL); diff --git a/builtin/init-db.c b/builtin/init-db.c index d5d7558..5cb05d9 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -311,8 +311,9 @@ static void create_object_directory(void) strbuf_release(&path); } -int set_git_dir_init(const char *git_dir, const char *real_git_dir, - int exist_ok) +static int set_git_dir_init(const char *git_dir, + const char *real_git_dir, + int exist_ok) { if (real_git_dir) { struct stat st; @@ -359,10 +360,14 @@ static void separate_git_dir(const char *git_dir) write_file(git_link, "gitdir: %s", git_dir); } -int init_db(const char *template_dir, unsigned int flags) +int init_db(const char *git_dir, const char *real_git_dir, + const char *template_dir, unsigned int flags) { int reinit; - const char *git_dir = get_git_dir(); + + set_git_dir_init(git_dir, real_git_dir, flags & INIT_DB_EXIST_OK); + + git_dir = get_git_dir(); if (git_link) separate_git_dir(git_dir); @@ -582,7 +587,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) set_git_work_tree(work_tree); } - set_git_dir_init(git_dir, real_git_dir, 1); - - return init_db(template_dir, flags); + flags |= INIT_DB_EXIST_OK; + return init_db(git_dir, real_git_dir, template_dir, flags); } diff --git a/cache.h b/cache.h index b2d77f3..7fc875f 100644 --- a/cache.h +++ b/cache.h @@ -525,9 +525,10 @@ extern void verify_non_filename(const char *prefix, const char *name); extern int path_inside_repo(const char *prefix, const char *path); #define INIT_DB_QUIET 0x0001 +#define INIT_DB_EXIST_OK 0x0002 -extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int); -extern int init_db(const char *template_dir, unsigned int flags); +extern int init_db(const char *git_dir, const char *real_git_dir, + const char *template_dir, unsigned int flags); extern void sanitize_stdfds(void); extern int daemonize(void); -- cgit v0.10.2-6-g49f6 From 1bd1907951a42040fa14fd19e432df9cb4107180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 25 Sep 2016 10:14:38 +0700 Subject: init: kill set_git_dir_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a pure code move, necessary to kill the global variable git_link later (and also helps a bit in the next patch). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/init-db.c b/builtin/init-db.c index 5cb05d9..68c1ae3 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -311,34 +311,6 @@ static void create_object_directory(void) strbuf_release(&path); } -static int set_git_dir_init(const char *git_dir, - const char *real_git_dir, - int exist_ok) -{ - if (real_git_dir) { - struct stat st; - - if (!exist_ok && !stat(git_dir, &st)) - die(_("%s already exists"), git_dir); - - if (!exist_ok && !stat(real_git_dir, &st)) - die(_("%s already exists"), real_git_dir); - - /* - * make sure symlinks are resolved because we'll be - * moving the target repo later on in separate_git_dir() - */ - git_link = xstrdup(real_path(git_dir)); - set_git_dir(real_path(real_git_dir)); - } - else { - set_git_dir(real_path(git_dir)); - git_link = NULL; - } - startup_info->have_repository = 1; - return 0; -} - static void separate_git_dir(const char *git_dir) { struct stat st; @@ -364,9 +336,29 @@ int init_db(const char *git_dir, const char *real_git_dir, const char *template_dir, unsigned int flags) { int reinit; + int exist_ok = flags & INIT_DB_EXIST_OK; - set_git_dir_init(git_dir, real_git_dir, flags & INIT_DB_EXIST_OK); + if (real_git_dir) { + struct stat st; + if (!exist_ok && !stat(git_dir, &st)) + die(_("%s already exists"), git_dir); + + if (!exist_ok && !stat(real_git_dir, &st)) + die(_("%s already exists"), real_git_dir); + + /* + * make sure symlinks are resolved because we'll be + * moving the target repo later on in separate_git_dir() + */ + git_link = xstrdup(real_path(git_dir)); + set_git_dir(real_path(real_git_dir)); + } + else { + set_git_dir(real_path(git_dir)); + git_link = NULL; + } + startup_info->have_repository = 1; git_dir = get_git_dir(); if (git_link) -- cgit v0.10.2-6-g49f6 From 6311cfaf93716bcc43dd1151cb1763e3f80d8099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 25 Sep 2016 10:14:39 +0700 Subject: init: do not set unnecessary core.worktree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function needs_work_tree_config() that is called from create_default_files() is supposed to be fed the path to ".git" that looks as if it is at the top of the working tree, and decide if that location matches the actual worktree being used. This comparison allows "git init" to decide if core.worktree needs to be recorded in the working tree. In the current code, however, we feed the return value from get_git_dir(), which can be totally different from what the function expects when "gitdir" file is involved. Instead of giving the path to the ".git" at the top of the working tree, we end up feeding the actual path that the file points at. This original location of ".git" however is only known to init_db(). Make init_db() save it and have it passed to create_default_files() as a new parameter, which passes the correct location down to needs_work_tree_config() to fix this. Noticed-by: Max Nordlund Helped-by: Michael J Gruber Helped-by: Junio C Hamano Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/init-db.c b/builtin/init-db.c index 68c1ae3..8069cd2 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -171,7 +171,8 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree) return 1; } -static int create_default_files(const char *template_path) +static int create_default_files(const char *template_path, + const char *original_git_dir) { struct stat st1; struct strbuf buf = STRBUF_INIT; @@ -263,7 +264,7 @@ static int create_default_files(const char *template_path) /* allow template config file to override the default */ if (log_all_ref_updates == -1) git_config_set("core.logallrefupdates", "true"); - if (needs_work_tree_config(get_git_dir(), work_tree)) + if (needs_work_tree_config(original_git_dir, work_tree)) git_config_set("core.worktree", work_tree); } @@ -337,6 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir, { int reinit; int exist_ok = flags & INIT_DB_EXIST_OK; + char *original_git_dir = xstrdup(real_path(git_dir)); if (real_git_dir) { struct stat st; @@ -375,7 +377,7 @@ int init_db(const char *git_dir, const char *real_git_dir, */ check_repository_format(); - reinit = create_default_files(template_dir); + reinit = create_default_files(template_dir, original_git_dir); create_object_directory(); @@ -412,6 +414,7 @@ int init_db(const char *git_dir, const char *real_git_dir, git_dir, len && git_dir[len-1] != '/' ? "/" : ""); } + free(original_git_dir); return 0; } diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 488564b..b8fc588 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -400,9 +400,11 @@ test_expect_success 're-init from a linked worktree' ' test_commit first && git worktree add ../linked-worktree && mv .git/info/exclude expected-exclude && + cp .git/config expected-config && find .git/worktrees -print | sort >expected && git -C ../linked-worktree init && test_cmp expected-exclude .git/info/exclude && + test_cmp expected-config .git/config && find .git/worktrees -print | sort >actual && test_cmp expected actual ) -- cgit v0.10.2-6-g49f6 From 822d9406c0b397bac7cfc4fda7b355aa1dd2cc52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 25 Sep 2016 10:14:40 +0700 Subject: init: kill git_link variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/init-db.c b/builtin/init-db.c index 8069cd2..37e318b 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -22,7 +22,6 @@ static int init_is_bare_repository = 0; static int init_shared_repository = -1; static const char *init_db_template_dir; -static const char *git_link; static void copy_templates_1(struct strbuf *path, struct strbuf *template, DIR *dir) @@ -312,7 +311,7 @@ static void create_object_directory(void) strbuf_release(&path); } -static void separate_git_dir(const char *git_dir) +static void separate_git_dir(const char *git_dir, const char *git_link) { struct stat st; @@ -349,22 +348,15 @@ int init_db(const char *git_dir, const char *real_git_dir, if (!exist_ok && !stat(real_git_dir, &st)) die(_("%s already exists"), real_git_dir); - /* - * make sure symlinks are resolved because we'll be - * moving the target repo later on in separate_git_dir() - */ - git_link = xstrdup(real_path(git_dir)); set_git_dir(real_path(real_git_dir)); + git_dir = get_git_dir(); + separate_git_dir(git_dir, original_git_dir); } else { set_git_dir(real_path(git_dir)); - git_link = NULL; + git_dir = get_git_dir(); } startup_info->have_repository = 1; - git_dir = get_git_dir(); - - if (git_link) - separate_git_dir(git_dir); safe_create_dir(git_dir, 0); -- cgit v0.10.2-6-g49f6