diff --git a/dir.c b/dir.c index 6ca2ef5f04..d7cfb08e44 100644 --- a/dir.c +++ b/dir.c @@ -1861,7 +1861,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir, */ enum path_treatment state; int matches_how = 0; - int nested_repo = 0, check_only, stop_early; + int check_only, stop_early; int old_ignored_nr, old_untracked_nr; /* The "len-1" is to strip the final '/' */ enum exist_status status = directory_exists_in_index(istate, dirname, len-1); @@ -1893,16 +1893,37 @@ static enum path_treatment treat_directory(struct dir_struct *dir, if ((dir->flags & DIR_SKIP_NESTED_GIT) || !(dir->flags & DIR_NO_GITLINKS)) { + /* + * Determine if `dirname` is a nested repo by confirming that: + * 1) we are in a nonbare repository, and + * 2) `dirname` is not an immediate parent of `the_repository->gitdir`, + * which could occur if the git_dir or worktree location was + * manually configured by the user; see t2205 testcases 1-3 for + * examples where this matters + */ + int nested_repo; struct strbuf sb = STRBUF_INIT; strbuf_addstr(&sb, dirname); nested_repo = is_nonbare_repository_dir(&sb); + + if (nested_repo) { + char *real_dirname, *real_gitdir; + strbuf_addstr(&sb, ".git"); + real_dirname = real_pathdup(sb.buf, 1); + real_gitdir = real_pathdup(the_repository->gitdir, 1); + + nested_repo = !!strcmp(real_dirname, real_gitdir); + free(real_gitdir); + free(real_dirname); + } strbuf_release(&sb); - } - if (nested_repo) { - if ((dir->flags & DIR_SKIP_NESTED_GIT) || - (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC)) - return path_none; - return excluded ? path_excluded : path_untracked; + + if (nested_repo) { + if ((dir->flags & DIR_SKIP_NESTED_GIT) || + (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC)) + return path_none; + return excluded ? path_excluded : path_untracked; + } } if (!(dir->flags & DIR_SHOW_OTHER_DIRECTORIES)) { diff --git a/t/t2205-add-worktree-config.sh b/t/t2205-add-worktree-config.sh new file mode 100755 index 0000000000..43d950de64 --- /dev/null +++ b/t/t2205-add-worktree-config.sh @@ -0,0 +1,265 @@ +#!/bin/sh + +test_description='directory traversal respects user config + +This test verifies the traversal of the directory tree when the traversal begins +outside the repository. Two instances for which this can occur are tested: + + 1) The user manually sets the worktree. For this instance, the test sets + the worktree two levels above the `.git` directory and checks whether we + are able to add to the index those files that are in either (1) the + manually configured worktree directory or (2) the standard worktree + location with respect to the `.git` directory (i.e. ensuring that the + encountered `.git` directory is not treated as belonging to a foreign + nested repository). + 2) The user manually sets the `git_dir` while the working directory is + outside the repository. The test checks that files inside the + repository can be added to the index. + ' + +. ./test-lib.sh + +test_expect_success '1a: setup--config worktree' ' + mkdir test1 && + ( + cd test1 && + test_create_repo repo && + git --git-dir="repo/.git" config core.worktree "$(pwd)" && + + mkdir -p outside-tracked outside-untracked && + mkdir -p repo/inside-tracked repo/inside-untracked && + >file-tracked && + >file-untracked && + >outside-tracked/file && + >outside-untracked/file && + >repo/file-tracked && + >repo/file-untracked && + >repo/inside-tracked/file && + >repo/inside-untracked/file && + + cat >expect-tracked-unsorted <<-EOF && + ../file-tracked + ../outside-tracked/file + file-tracked + inside-tracked/file + EOF + + cat >expect-untracked-unsorted <<-EOF && + ../file-untracked + ../outside-untracked/file + file-untracked + inside-untracked/file + EOF + + cat >expect-all-dir-unsorted <<-EOF && + ../file-untracked + ../file-tracked + ../outside-untracked/ + ../outside-tracked/ + ./ + EOF + + cat expect-tracked-unsorted expect-untracked-unsorted >expect-all-unsorted && + + cat >.gitignore <<-EOF + .gitignore + actual-* + expect-* + EOF + ) +' + +test_expect_success '1b: pre-add all' ' + ( + cd test1 && + local parent_dir="$(pwd)" && + git -C repo ls-files -o --exclude-standard "$parent_dir" >actual-all-unsorted && + sort actual-all-unsorted >actual-all && + sort expect-all-unsorted >expect-all && + test_cmp expect-all actual-all + ) +' + +test_expect_success '1c: pre-add dir all' ' + ( + cd test1 && + local parent_dir="$(pwd)" && + git -C repo ls-files -o --directory --exclude-standard "$parent_dir" >actual-all-dir-unsorted && + sort actual-all-dir-unsorted >actual-all && + sort expect-all-dir-unsorted >expect-all && + test_cmp expect-all actual-all + ) +' + +test_expect_success '1d: post-add tracked' ' + ( + cd test1 && + local parent_dir="$(pwd)" && + ( + cd repo && + git add file-tracked && + git add inside-tracked && + git add ../outside-tracked && + git add "$parent_dir/file-tracked" && + git ls-files "$parent_dir" >../actual-tracked-unsorted + ) && + sort actual-tracked-unsorted >actual-tracked && + sort expect-tracked-unsorted >expect-tracked && + test_cmp expect-tracked actual-tracked + ) +' + +test_expect_success '1e: post-add untracked' ' + ( + cd test1 && + local parent_dir="$(pwd)" && + git -C repo ls-files -o --exclude-standard "$parent_dir" >actual-untracked-unsorted && + sort actual-untracked-unsorted >actual-untracked && + sort expect-untracked-unsorted >expect-untracked && + test_cmp expect-untracked actual-untracked + ) +' + +test_expect_success '2a: setup--set git-dir' ' + mkdir test2 && + ( + cd test2 && + test_create_repo repo && + # create two foreign repositories that should remain untracked + test_create_repo repo-outside && + test_create_repo repo/repo-inside && + + mkdir -p repo/inside-tracked repo/inside-untracked && + >repo/file-tracked && + >repo/file-untracked && + >repo/inside-tracked/file && + >repo/inside-untracked/file && + >repo-outside/file && + >repo/repo-inside/file && + + cat >expect-tracked-unsorted <<-EOF && + repo/file-tracked + repo/inside-tracked/file + EOF + + cat >expect-untracked-unsorted <<-EOF && + repo/file-untracked + repo/inside-untracked/file + repo/repo-inside/ + repo-outside/ + EOF + + cat >expect-all-dir-unsorted <<-EOF && + repo/ + repo-outside/ + EOF + + cat expect-tracked-unsorted expect-untracked-unsorted >expect-all-unsorted && + + cat >.gitignore <<-EOF + .gitignore + actual-* + expect-* + EOF + ) +' + +test_expect_success '2b: pre-add all' ' + ( + cd test2 && + git --git-dir=repo/.git ls-files -o --exclude-standard >actual-all-unsorted && + sort actual-all-unsorted >actual-all && + sort expect-all-unsorted >expect-all && + test_cmp expect-all actual-all + ) +' + +test_expect_success '2c: pre-add dir all' ' + ( + cd test2 && + git --git-dir=repo/.git ls-files -o --directory --exclude-standard >actual-all-dir-unsorted && + sort actual-all-dir-unsorted >actual-all && + sort expect-all-dir-unsorted >expect-all && + test_cmp expect-all actual-all + ) +' + +test_expect_success '2d: post-add tracked' ' + ( + cd test2 && + git --git-dir=repo/.git add repo/file-tracked && + git --git-dir=repo/.git add repo/inside-tracked && + git --git-dir=repo/.git ls-files >actual-tracked-unsorted && + sort actual-tracked-unsorted >actual-tracked && + sort expect-tracked-unsorted >expect-tracked && + test_cmp expect-tracked actual-tracked + ) +' + +test_expect_success '2e: post-add untracked' ' + ( + cd test2 && + git --git-dir=repo/.git ls-files -o --exclude-standard >actual-untracked-unsorted && + sort actual-untracked-unsorted >actual-untracked && + sort expect-untracked-unsorted >expect-untracked && + test_cmp expect-untracked actual-untracked + ) +' + +test_expect_success '3a: setup--add repo dir' ' + mkdir test3 && + ( + cd test3 && + test_create_repo repo && + + mkdir -p repo/inside-tracked repo/inside-ignored && + >repo/file-tracked && + >repo/file-ignored && + >repo/inside-tracked/file && + >repo/inside-ignored/file && + + cat >.gitignore <<-EOF && + .gitignore + actual-* + expect-* + *ignored + EOF + + cat >expect-tracked-unsorted <<-EOF && + repo/file-tracked + repo/inside-tracked/file + EOF + + cat >expect-ignored-unsorted <<-EOF + repo/file-ignored + repo/inside-ignored/ + .gitignore + actual-ignored-unsorted + expect-ignored-unsorted + expect-tracked-unsorted + EOF + ) +' + +test_expect_success '3b: ignored' ' + ( + cd test3 && + git --git-dir=repo/.git ls-files -io --directory --exclude-standard >actual-ignored-unsorted && + sort actual-ignored-unsorted >actual-ignored && + sort expect-ignored-unsorted >expect-ignored && + test_cmp expect-ignored actual-ignored + ) +' + +test_expect_success '3c: add repo' ' + ( + cd test3 && + git --git-dir=repo/.git add repo && + git --git-dir=repo/.git ls-files >actual-tracked-unsorted && + sort actual-tracked-unsorted >actual-tracked && + sort expect-tracked-unsorted >expect-tracked && + test_cmp expect-tracked actual-tracked + ) +' + +test_done