diff options
author | Johannes Schindelin <johannes.schindelin@gmx.de> | 2024-04-12 09:45:28 +0200 |
---|---|---|
committer | Johannes Schindelin <johannes.schindelin@gmx.de> | 2024-04-19 12:38:37 +0200 |
commit | 93a88f42db7ed9a975768df0e5f4516317c50dda (patch) | |
tree | 425bd4d9bc74c6a172a6d29e28c468158f094a96 /t | |
parent | repository: avoid leaking `fsmonitor` data (diff) | |
parent | Git 2.39.4 (diff) | |
download | git-93a88f42db7ed9a975768df0e5f4516317c50dda.tar.xz git-93a88f42db7ed9a975768df0e5f4516317c50dda.zip |
Sync with 2.39.4
* maint-2.39: (38 commits)
Git 2.39.4
fsck: warn about symlink pointing inside a gitdir
core.hooksPath: add some protection while cloning
init.templateDir: consider this config setting protected
clone: prevent hooks from running during a clone
Add a helper function to compare file contents
init: refactor the template directory discovery into its own function
find_hook(): refactor the `STRIP_EXTENSION` logic
clone: when symbolic links collide with directories, keep the latter
entry: report more colliding paths
t5510: verify that D/F confusion cannot lead to an RCE
submodule: require the submodule path to contain directories only
clone_submodule: avoid using `access()` on directories
submodules: submodule paths must not contain symlinks
clone: prevent clashing git dirs when cloning submodule in parallel
t7423: add tests for symlinked submodule directories
has_dir_name(): do not get confused by characters < '/'
docs: document security issues around untrusted .git dirs
upload-pack: disable lazy-fetching by default
fetch/clone: detect dubious ownership of local repositories
...
Diffstat (limited to 't')
-rw-r--r-- | t/helper/test-path-utils.c | 10 | ||||
-rwxr-xr-x | t/t0000-basic.sh | 28 | ||||
-rwxr-xr-x | t/t0033-safe-directory.sh | 24 | ||||
-rwxr-xr-x | t/t0060-path-utils.sh | 41 | ||||
-rwxr-xr-x | t/t0411-clone-from-partial.sh | 78 | ||||
-rwxr-xr-x | t/t1450-fsck.sh | 37 | ||||
-rwxr-xr-x | t/t1800-hook.sh | 15 | ||||
-rwxr-xr-x | t/t5510-fetch.sh | 24 | ||||
-rwxr-xr-x | t/t5601-clone.sh | 66 | ||||
-rwxr-xr-x | t/t7400-submodule-basic.sh | 31 | ||||
-rwxr-xr-x | t/t7406-submodule-update.sh | 48 | ||||
-rwxr-xr-x | t/t7423-submodule-symlinks.sh | 67 | ||||
-rwxr-xr-x | t/t7450-bad-git-dotfiles.sh | 34 | ||||
-rw-r--r-- | t/test-lib.sh | 1 |
14 files changed, 502 insertions, 2 deletions
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c index f69709d674..0e0de21807 100644 --- a/t/helper/test-path-utils.c +++ b/t/helper/test-path-utils.c @@ -495,6 +495,16 @@ int cmd__path_utils(int argc, const char **argv) return !!res; } + if (argc == 4 && !strcmp(argv[1], "do_files_match")) { + int ret = do_files_match(argv[2], argv[3]); + + if (ret) + printf("equal\n"); + else + printf("different\n"); + return !ret; + } + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 8ea31d187a..402b201b5a 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -1201,6 +1201,34 @@ test_expect_success 'very long name in the index handled sanely' ' test $len = 4098 ' +# D/F conflict checking uses an optimization when adding to the end. +# make sure it does not get confused by `a-` sorting _between_ +# `a` and `a/`. +test_expect_success 'more update-index D/F conflicts' ' + # empty the index to make sure our entry is last + git read-tree --empty && + cacheinfo=100644,$(test_oid empty_blob) && + git update-index --add --cacheinfo $cacheinfo,path5/a && + + test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/file && + test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/file && + test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/c/file && + + # "a-" sorts between "a" and "a/" + git update-index --add --cacheinfo $cacheinfo,path5/a- && + + test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/file && + test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/file && + test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/c/file && + + cat >expected <<-\EOF && + path5/a + path5/a- + EOF + git ls-files >actual && + test_cmp expected actual +' + test_expect_success 'test_must_fail on a failing git command' ' test_must_fail git notacommand ' diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh index dc3496897a..11c3e8f28e 100755 --- a/t/t0033-safe-directory.sh +++ b/t/t0033-safe-directory.sh @@ -80,4 +80,28 @@ test_expect_success 'safe.directory in included file' ' git status ' +test_expect_success 'local clone of unowned repo refused in unsafe directory' ' + test_when_finished "rm -rf source" && + git init source && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit -C source initial + ) && + test_must_fail git clone --local source target && + test_path_is_missing target +' + +test_expect_success 'local clone of unowned repo accepted in safe directory' ' + test_when_finished "rm -rf source" && + git init source && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit -C source initial + ) && + test_must_fail git clone --local source target && + git config --global --add safe.directory "$(pwd)/source/.git" && + git clone --local source target && + test_path_is_dir target +' + test_done diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 68e29c904a..73d0e1a7f1 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -560,4 +560,45 @@ test_expect_success !VALGRIND,RUNTIME_PREFIX,CAN_EXEC_IN_PWD '%(prefix)/ works' test_cmp expect actual ' +test_expect_success 'do_files_match()' ' + test_seq 0 10 >0-10.txt && + test_seq -1 10 >-1-10.txt && + test_seq 1 10 >1-10.txt && + test_seq 1 9 >1-9.txt && + test_seq 0 8 >0-8.txt && + + test-tool path-utils do_files_match 0-10.txt 0-10.txt >out && + + assert_fails() { + test_must_fail \ + test-tool path-utils do_files_match "$1" "$2" >out && + grep different out + } && + + assert_fails 0-8.txt 1-9.txt && + assert_fails -1-10.txt 0-10.txt && + assert_fails 1-10.txt 1-9.txt && + assert_fails 1-10.txt .git && + assert_fails does-not-exist 1-10.txt && + + if test_have_prereq FILEMODE + then + cp 0-10.txt 0-10.x && + chmod a+x 0-10.x && + assert_fails 0-10.txt 0-10.x + fi && + + if test_have_prereq SYMLINKS + then + ln -sf 0-10.txt symlink && + ln -s 0-10.txt another-symlink && + ln -s over-the-ocean yet-another-symlink && + ln -s "$PWD/0-10.txt" absolute-symlink && + assert_fails 0-10.txt symlink && + test-tool path-utils do_files_match symlink another-symlink && + assert_fails symlink yet-another-symlink && + assert_fails symlink absolute-symlink + fi +' + test_done diff --git a/t/t0411-clone-from-partial.sh b/t/t0411-clone-from-partial.sh new file mode 100755 index 0000000000..b3d6ddc4bc --- /dev/null +++ b/t/t0411-clone-from-partial.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +test_description='check that local clone does not fetch from promisor remotes' + +. ./test-lib.sh + +test_expect_success 'create evil repo' ' + git init tmp && + test_commit -C tmp a && + git -C tmp config uploadpack.allowfilter 1 && + git clone --filter=blob:none --no-local --no-checkout tmp evil && + rm -rf tmp && + + git -C evil config remote.origin.uploadpack \"\$TRASH_DIRECTORY/fake-upload-pack\" && + write_script fake-upload-pack <<-\EOF && + echo >&2 "fake-upload-pack running" + >"$TRASH_DIRECTORY/script-executed" + exit 1 + EOF + export TRASH_DIRECTORY && + + # empty shallow file disables local clone optimization + >evil/.git/shallow +' + +test_expect_success 'local clone must not fetch from promisor remote and execute script' ' + rm -f script-executed && + test_must_fail git clone \ + --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ + evil clone1 2>err && + grep "detected dubious ownership" err && + ! grep "fake-upload-pack running" err && + test_path_is_missing script-executed +' + +test_expect_success 'clone from file://... must not fetch from promisor remote and execute script' ' + rm -f script-executed && + test_must_fail git clone \ + --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ + "file://$(pwd)/evil" clone2 2>err && + grep "detected dubious ownership" err && + ! grep "fake-upload-pack running" err && + test_path_is_missing script-executed +' + +test_expect_success 'fetch from file://... must not fetch from promisor remote and execute script' ' + rm -f script-executed && + test_must_fail git fetch \ + --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ + "file://$(pwd)/evil" 2>err && + grep "detected dubious ownership" err && + ! grep "fake-upload-pack running" err && + test_path_is_missing script-executed +' + +test_expect_success 'pack-objects should fetch from promisor remote and execute script' ' + rm -f script-executed && + echo "HEAD" | test_must_fail git -C evil pack-objects --revs --stdout >/dev/null 2>err && + grep "fake-upload-pack running" err && + test_path_is_file script-executed +' + +test_expect_success 'clone from promisor remote does not lazy-fetch by default' ' + rm -f script-executed && + test_must_fail git clone evil no-lazy 2>err && + grep "lazy fetching disabled" err && + test_path_is_missing script-executed +' + +test_expect_success 'promisor lazy-fetching can be re-enabled' ' + rm -f script-executed && + test_must_fail env GIT_NO_LAZY_FETCH=0 \ + git clone evil lazy-ok 2>err && + grep "fake-upload-pack running" err && + test_path_is_file script-executed +' + +test_done diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index fdb886dfe4..6277ce6b20 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -1023,4 +1023,41 @@ test_expect_success 'fsck error on gitattributes with excessive size' ' test_cmp expected actual ' +test_expect_success 'fsck warning on symlink target with excessive length' ' + symlink_target=$(printf "pattern %032769d" 1 | git hash-object -w --stdin) && + test_when_finished "remove_object $symlink_target" && + tree=$(printf "120000 blob %s\t%s\n" $symlink_target symlink | git mktree) && + test_when_finished "remove_object $tree" && + cat >expected <<-EOF && + warning in blob $symlink_target: symlinkTargetLength: symlink target too long + EOF + git fsck --no-dangling >actual 2>&1 && + test_cmp expected actual +' + +test_expect_success 'fsck warning on symlink target pointing inside git dir' ' + gitdir=$(printf ".git" | git hash-object -w --stdin) && + ntfs_gitdir=$(printf "GIT~1" | git hash-object -w --stdin) && + hfs_gitdir=$(printf ".${u200c}git" | git hash-object -w --stdin) && + inside_gitdir=$(printf "nested/.git/config" | git hash-object -w --stdin) && + benign_target=$(printf "legit/config" | git hash-object -w --stdin) && + tree=$(printf "120000 blob %s\t%s\n" \ + $benign_target benign_target \ + $gitdir gitdir \ + $hfs_gitdir hfs_gitdir \ + $inside_gitdir inside_gitdir \ + $ntfs_gitdir ntfs_gitdir | + git mktree) && + for o in $gitdir $ntfs_gitdir $hfs_gitdir $inside_gitdir $benign_target $tree + do + test_when_finished "remove_object $o" || return 1 + done && + printf "warning in blob %s: symlinkPointsToGitDir: symlink target points to git dir\n" \ + $gitdir $hfs_gitdir $inside_gitdir $ntfs_gitdir | + sort >expected && + git fsck --no-dangling >actual 2>&1 && + sort actual >actual.sorted && + test_cmp expected actual.sorted +' + test_done diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh index 3506f627b6..0f0c706d07 100755 --- a/t/t1800-hook.sh +++ b/t/t1800-hook.sh @@ -195,4 +195,19 @@ test_expect_success 'stdin to hooks' ' test_cmp expect actual ' +test_expect_success 'clone protections' ' + test_config core.hooksPath "$(pwd)/my-hooks" && + mkdir -p my-hooks && + write_script my-hooks/test-hook <<-\EOF && + echo Hook ran $1 + EOF + + git hook run test-hook 2>err && + grep "Hook ran" err && + test_must_fail env GIT_CLONE_PROTECTION_ACTIVE=true \ + git hook run test-hook 2>err && + grep "active .core.hooksPath" err && + ! grep "Hook ran" err +' + test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 34a1261520..854ebd3542 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -1248,6 +1248,30 @@ EOF test_cmp fatal-expect fatal-actual ' +test_expect_success SYMLINKS 'clone does not get confused by a D/F conflict' ' + git init df-conflict && + ( + cd df-conflict && + ln -s .git a && + git add a && + test_tick && + git commit -m symlink && + test_commit a- && + rm a && + mkdir -p a/hooks && + write_script a/hooks/post-checkout <<-EOF && + echo WHOOPSIE >&2 + echo whoopsie >"$TRASH_DIRECTORY"/whoops + EOF + git add a/hooks/post-checkout && + test_tick && + git commit -m post-checkout + ) && + git clone df-conflict clone 2>err && + ! grep WHOOPS err && + test_path_is_missing whoops +' + . "$TEST_DIRECTORY"/lib-httpd.sh start_httpd diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index b7d5551262..1bcf652b10 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -633,6 +633,21 @@ test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' ' test_i18ngrep "the following paths have collided" icasefs/warning ' +test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \ + 'colliding symlink/directory keeps directory' ' + git init icasefs-colliding-symlink && + ( + cd icasefs-colliding-symlink && + a=$(printf a | git hash-object -w --stdin) && + printf "100644 %s 0\tA/dir/b\n120000 %s 0\ta\n" $a $a >idx && + git update-index --index-info <idx && + test_tick && + git commit -m initial + ) && + git clone icasefs-colliding-symlink icasefs-colliding-symlink-clone && + test_file_not_empty icasefs-colliding-symlink-clone/A/dir/b +' + test_expect_success 'clone with GIT_DEFAULT_HASH' ' ( sane_unset GIT_DEFAULT_HASH && @@ -756,6 +771,57 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe git clone --filter=blob:limit=0 "file://$(pwd)/server" client ' +test_expect_success 'clone with init.templatedir runs hooks' ' + git init tmpl/hooks && + write_script tmpl/hooks/post-checkout <<-EOF && + echo HOOK-RUN >&2 + echo I was here >hook.run + EOF + git -C tmpl/hooks add . && + test_tick && + git -C tmpl/hooks commit -m post-checkout && + + test_when_finished "git config --global --unset init.templateDir || :" && + test_when_finished "git config --unset init.templateDir || :" && + ( + sane_unset GIT_TEMPLATE_DIR && + NO_SET_GIT_TEMPLATE_DIR=t && + export NO_SET_GIT_TEMPLATE_DIR && + + git -c core.hooksPath="$(pwd)/tmpl/hooks" \ + clone tmpl/hooks hook-run-hookspath 2>err && + ! grep "active .* hook found" err && + test_path_is_file hook-run-hookspath/hook.run && + + git -c init.templateDir="$(pwd)/tmpl" \ + clone tmpl/hooks hook-run-config 2>err && + ! grep "active .* hook found" err && + test_path_is_file hook-run-config/hook.run && + + git clone --template=tmpl tmpl/hooks hook-run-option 2>err && + ! grep "active .* hook found" err && + test_path_is_file hook-run-option/hook.run && + + git config --global init.templateDir "$(pwd)/tmpl" && + git clone tmpl/hooks hook-run-global-config 2>err && + git config --global --unset init.templateDir && + ! grep "active .* hook found" err && + test_path_is_file hook-run-global-config/hook.run && + + # clone ignores local `init.templateDir`; need to create + # a new repository because we deleted `.git/` in the + # `setup` test case above + git init local-clone && + cd local-clone && + + git config init.templateDir "$(pwd)/../tmpl" && + git clone ../tmpl/hooks hook-run-local-config 2>err && + git config --unset init.templateDir && + ! grep "active .* hook found" err && + test_path_is_missing hook-run-local-config/hook.run + ) +' + . "$TEST_DIRECTORY"/lib-httpd.sh start_httpd diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index eae6a46ef3..3e8cf9b885 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -1436,4 +1436,35 @@ test_expect_success 'recursive clone respects -q' ' test_must_be_empty actual ' +test_expect_success '`submodule init` and `init.templateDir`' ' + mkdir -p tmpl/hooks && + write_script tmpl/hooks/post-checkout <<-EOF && + echo HOOK-RUN >&2 + echo I was here >hook.run + exit 1 + EOF + + test_config init.templateDir "$(pwd)/tmpl" && + test_when_finished \ + "git config --global --unset init.templateDir || true" && + ( + sane_unset GIT_TEMPLATE_DIR && + NO_SET_GIT_TEMPLATE_DIR=t && + export NO_SET_GIT_TEMPLATE_DIR && + + git config --global init.templateDir "$(pwd)/tmpl" && + test_must_fail git submodule \ + add "$submodurl" sub-global 2>err && + git config --global --unset init.templateDir && + grep HOOK-RUN err && + test_path_is_file sub-global/hook.run && + + git config init.templateDir "$(pwd)/tmpl" && + git submodule add "$submodurl" sub-local 2>err && + git config --unset init.templateDir && + ! grep HOOK-RUN err && + test_path_is_missing sub-local/hook.run + ) +' + test_done diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index f094e3d7f3..dae87090e0 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -1179,4 +1179,52 @@ test_expect_success 'submodule update --recursive skip submodules with strategy= test_cmp expect.err actual.err ' +test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \ + 'submodule paths must not follow symlinks' ' + + # This is only needed because we want to run this in a self-contained + # test without having to spin up an HTTP server; However, it would not + # be needed in a real-world scenario where the submodule is simply + # hosted on a public site. + test_config_global protocol.file.allow always && + + # Make sure that Git tries to use symlinks on Windows + test_config_global core.symlinks true && + + tell_tale_path="$PWD/tell.tale" && + git init hook && + ( + cd hook && + mkdir -p y/hooks && + write_script y/hooks/post-checkout <<-EOF && + echo HOOK-RUN >&2 + echo hook-run >"$tell_tale_path" + EOF + git add y/hooks/post-checkout && + test_tick && + git commit -m post-checkout + ) && + + hook_repo_path="$(pwd)/hook" && + git init captain && + ( + cd captain && + git submodule add --name x/y "$hook_repo_path" A/modules/x && + test_tick && + git commit -m add-submodule && + + printf .git >dotgit.txt && + git hash-object -w --stdin <dotgit.txt >dot-git.hash && + printf "120000 %s 0\ta\n" "$(cat dot-git.hash)" >index.info && + git update-index --index-info <index.info && + test_tick && + git commit -m add-symlink + ) && + + test_path_is_missing "$tell_tale_path" && + git clone --recursive captain hooked 2>err && + ! grep HOOK-RUN err && + test_path_is_missing "$tell_tale_path" +' + test_done diff --git a/t/t7423-submodule-symlinks.sh b/t/t7423-submodule-symlinks.sh new file mode 100755 index 0000000000..3d3c7af3ce --- /dev/null +++ b/t/t7423-submodule-symlinks.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +test_description='check that submodule operations do not follow symlinks' + +. ./test-lib.sh + +test_expect_success 'prepare' ' + git config --global protocol.file.allow always && + test_commit initial && + git init upstream && + test_commit -C upstream upstream submodule_file && + git submodule add ./upstream a/sm && + test_tick && + git commit -m submodule +' + +test_expect_success SYMLINKS 'git submodule update must not create submodule behind symlink' ' + rm -rf a b && + mkdir b && + ln -s b a && + test_path_is_missing b/sm && + test_must_fail git submodule update && + test_path_is_missing b/sm +' + +test_expect_success SYMLINKS,CASE_INSENSITIVE_FS 'git submodule update must not create submodule behind symlink on case insensitive fs' ' + rm -rf a b && + mkdir b && + ln -s b A && + test_must_fail git submodule update && + test_path_is_missing b/sm +' + +prepare_symlink_to_repo() { + rm -rf a && + mkdir a && + git init a/target && + git -C a/target fetch ../../upstream && + ln -s target a/sm +} + +test_expect_success SYMLINKS 'git restore --recurse-submodules must not be confused by a symlink' ' + prepare_symlink_to_repo && + test_must_fail git restore --recurse-submodules a/sm && + test_path_is_missing a/sm/submodule_file && + test_path_is_dir a/target/.git && + test_path_is_missing a/target/submodule_file +' + +test_expect_success SYMLINKS 'git restore --recurse-submodules must not migrate git dir of symlinked repo' ' + prepare_symlink_to_repo && + rm -rf .git/modules && + test_must_fail git restore --recurse-submodules a/sm && + test_path_is_dir a/target/.git && + test_path_is_missing .git/modules/a/sm && + test_path_is_missing a/target/submodule_file +' + +test_expect_success SYMLINKS 'git checkout -f --recurse-submodules must not migrate git dir of symlinked repo when removing submodule' ' + prepare_symlink_to_repo && + rm -rf .git/modules && + test_must_fail git checkout -f --recurse-submodules initial && + test_path_is_dir a/target/.git && + test_path_is_missing .git/modules/a/sm +' + +test_done diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh index 0d0c3f2c68..60d627583d 100755 --- a/t/t7450-bad-git-dotfiles.sh +++ b/t/t7450-bad-git-dotfiles.sh @@ -294,7 +294,7 @@ test_expect_success WINDOWS 'prevent git~1 squatting on Windows' ' fi ' -test_expect_success 'git dirs of sibling submodules must not be nested' ' +test_expect_success 'setup submodules with nested git dirs' ' git init nested && test_commit -C nested nested && ( @@ -312,9 +312,39 @@ test_expect_success 'git dirs of sibling submodules must not be nested' ' git add .gitmodules thing1 thing2 && test_tick && git commit -m nested - ) && + ) +' + +test_expect_success 'git dirs of sibling submodules must not be nested' ' test_must_fail git clone --recurse-submodules nested clone 2>err && test_i18ngrep "is inside git dir" err ' +test_expect_success 'submodule git dir nesting detection must work with parallel cloning' ' + test_must_fail git clone --recurse-submodules --jobs=2 nested clone_parallel 2>err && + cat err && + grep -E "(already exists|is inside git dir|not a git repository)" err && + { + test_path_is_missing .git/modules/hippo/HEAD || + test_path_is_missing .git/modules/hippo/hooks/HEAD + } +' + +test_expect_success 'checkout -f --recurse-submodules must not use a nested gitdir' ' + git clone nested nested_checkout && + ( + cd nested_checkout && + git submodule init && + git submodule update thing1 && + mkdir -p .git/modules/hippo/hooks/refs && + mkdir -p .git/modules/hippo/hooks/objects/info && + echo "../../../../objects" >.git/modules/hippo/hooks/objects/info/alternates && + echo "ref: refs/heads/master" >.git/modules/hippo/hooks/HEAD + ) && + test_must_fail git -C nested_checkout checkout -f --recurse-submodules HEAD 2>err && + cat err && + grep "is inside git dir" err && + test_path_is_missing nested_checkout/thing2/.git +' + test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 62136caee5..93df6d6545 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -334,6 +334,7 @@ nr_san_dir_leaks_ () { find "$TEST_RESULTS_SAN_DIR" \ -type f \ -name "$TEST_RESULTS_SAN_FILE_PFX.*" 2>/dev/null | + xargs grep -lv "Unable to get registers from thread" | wc -l } |