From: Eric Sunshine Date: Mon, 31 Aug 2020 06:58:00 +0000 (-0400) Subject: init: make --separate-git-dir work from within linked worktree X-Git-Tag: v2.29.0-rc0~88^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=59d876ccd688ae0fe761b571afe77a7c8317eb88;p=thirdparty%2Fgit.git init: make --separate-git-dir work from within linked worktree The intention of `git init --separate-work-dir=` is to move the .git/ directory to a location outside of the main worktree. When used within a linked worktree, however, rather than moving the .git/ directory as intended, it instead incorrectly moves the worktree's .git/worktrees/ directory to , thus disconnecting the linked worktree from its parent repository and breaking the worktree in the process since its local .git file no longer points at a location at which it can find the object database. Fix this broken behavior. An intentional side-effect of this change is that it also closes a loophole not caught by ccf236a23a (init: disallow --separate-git-dir with bare repository, 2020-08-09) in which the check to prevent --separate-git-dir being used in conjunction with a bare repository was unable to detect the invalid combination when invoked from within a linked worktree. Therefore, add a test to verify that this loophole is closed, as well. Reported-by: Henré Botha Signed-off-by: Eric Sunshine Signed-off-by: Junio C Hamano --- diff --git a/builtin/init-db.c b/builtin/init-db.c index 7b915d88ab..cd3e760541 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -642,6 +642,30 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) if (!git_dir) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; + /* + * When --separate-git-dir is used inside a linked worktree, take + * care to ensure that the common .git/ directory is relocated, not + * the worktree-specific .git/worktrees// directory. + */ + if (real_git_dir) { + int err; + const char *p; + struct strbuf sb = STRBUF_INIT; + + p = read_gitfile_gently(git_dir, &err); + if (p && get_common_dir(&sb, p)) { + struct strbuf mainwt = STRBUF_INIT; + + strbuf_addbuf(&mainwt, &sb); + strbuf_strip_suffix(&mainwt, "/.git"); + if (chdir(mainwt.buf) < 0) + die_errno(_("cannot chdir to %s"), mainwt.buf); + strbuf_release(&mainwt); + git_dir = strbuf_detach(&sb, NULL); + } + strbuf_release(&sb); + } + if (is_bare_repository_cfg < 0) is_bare_repository_cfg = guess_repository_type(git_dir); diff --git a/t/t0001-init.sh b/t/t0001-init.sh index e489eb4ddb..2f7c3dcd0f 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -329,6 +329,15 @@ test_expect_success 'implicit bare & --separate-git-dir incompatible' ' test_i18ngrep "incompatible" err ' +test_expect_success 'bare & --separate-git-dir incompatible within worktree' ' + test_when_finished "rm -rf bare.git linkwt seprepo" && + test_commit gumby && + git clone --bare . bare.git && + git -C bare.git worktree add --detach ../linkwt && + test_must_fail git -C linkwt init --separate-git-dir seprepo 2>err && + test_i18ngrep "incompatible" err +' + test_lazy_prereq GETCWD_IGNORES_PERMS ' base=GETCWD_TEST_BASE_DIR && mkdir -p $base/dir && @@ -405,15 +414,23 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' ' test_path_is_dir realgitdir/refs ' -test_expect_success 're-init to move gitdir with linked worktrees' ' +sep_git_dir_worktree () { test_when_finished "rm -rf mainwt linkwt seprepo" && git init mainwt && test_commit -C mainwt gumby && git -C mainwt worktree add --detach ../linkwt && - git -C mainwt init --separate-git-dir ../seprepo && + git -C "$1" init --separate-git-dir ../seprepo && git -C mainwt rev-parse --git-common-dir >expect && git -C linkwt rev-parse --git-common-dir >actual && test_cmp expect actual +} + +test_expect_success 're-init to move gitdir with linked worktrees' ' + sep_git_dir_worktree mainwt +' + +test_expect_success 're-init to move gitdir within linked worktree' ' + sep_git_dir_worktree linkwt ' test_expect_success MINGW '.git hidden' '