@matheusbsbs / matheus-bagliano / commits / 59d876ccd6
Fork 0

init: make --separate-git-dir work from within linked worktree

The intention of `git init --separate-work-dir=<path>` 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/<id> directory to <path>, 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 <henrebotha@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Eric Sunshine committed Aug 31, 2020 at 02:58 UTC 59d876ccd688ae0fe761b571afe77a7c8317eb88
2 files changed +43 -2
builtin/init-db.c
+24
@@ -642,6 +642,30 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
642 if (!git_dir)
643 git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
644
645 + /*
646 + * When --separate-git-dir is used inside a linked worktree, take
647 + * care to ensure that the common .git/ directory is relocated, not
648 + * the worktree-specific .git/worktrees/<id>/ directory.
649 + */
650 + if (real_git_dir) {
651 + int err;
652 + const char *p;
653 + struct strbuf sb = STRBUF_INIT;
654 +
655 + p = read_gitfile_gently(git_dir, &err);
656 + if (p && get_common_dir(&sb, p)) {
657 + struct strbuf mainwt = STRBUF_INIT;
658 +
659 + strbuf_addbuf(&mainwt, &sb);
660 + strbuf_strip_suffix(&mainwt, "/.git");
661 + if (chdir(mainwt.buf) < 0)
662 + die_errno(_("cannot chdir to %s"), mainwt.buf);
663 + strbuf_release(&mainwt);
664 + git_dir = strbuf_detach(&sb, NULL);
665 + }
666 + strbuf_release(&sb);
667 + }
668 +
669 if (is_bare_repository_cfg < 0)
670 is_bare_repository_cfg = guess_repository_type(git_dir);
671
t/t0001-init.sh
+19 -2
@@ -329,6 +329,15 @@ test_expect_success 'implicit bare & --separate-git-dir incompatible' '
329 test_i18ngrep "incompatible" err
330 '
331
332 +test_expect_success 'bare & --separate-git-dir incompatible within worktree' '
333 + test_when_finished "rm -rf bare.git linkwt seprepo" &&
334 + test_commit gumby &&
335 + git clone --bare . bare.git &&
336 + git -C bare.git worktree add --detach ../linkwt &&
337 + test_must_fail git -C linkwt init --separate-git-dir seprepo 2>err &&
338 + test_i18ngrep "incompatible" err
339 +'
340 +
341 test_lazy_prereq GETCWD_IGNORES_PERMS '
342 base=GETCWD_TEST_BASE_DIR &&
343 mkdir -p $base/dir &&
@@ -405,15 +414,23 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
414 test_path_is_dir realgitdir/refs
415 '
416
408 -test_expect_success 're-init to move gitdir with linked worktrees' '
417 +sep_git_dir_worktree () {
418 test_when_finished "rm -rf mainwt linkwt seprepo" &&
419 git init mainwt &&
420 test_commit -C mainwt gumby &&
421 git -C mainwt worktree add --detach ../linkwt &&
413 - git -C mainwt init --separate-git-dir ../seprepo &&
422 + git -C "$1" init --separate-git-dir ../seprepo &&
423 git -C mainwt rev-parse --git-common-dir >expect &&
424 git -C linkwt rev-parse --git-common-dir >actual &&
425 test_cmp expect actual
426 +}
427 +
428 +test_expect_success 're-init to move gitdir with linked worktrees' '
429 + sep_git_dir_worktree mainwt
430 +'
431 +
432 +test_expect_success 're-init to move gitdir within linked worktree' '
433 + sep_git_dir_worktree linkwt
434 '
435
436 test_expect_success MINGW '.git hidden' '