git/t/t3602-rm-sparse-checkout.sh
Jeff King 4d7f95ed1f sparse-checkout: pass string literals directly to add_pattern()
The add_pattern() function takes a pattern string, but neither makes a
copy of it nor takes ownership of the memory. So it is the caller's
responsibility to make sure the string hangs around as long as the
pattern_list which references it.

There are a few cases in sparse-checkout where we use string literal
patterns by stuffing them into a strbuf, detaching the buffer, and then
passing the result into add_pattern(). This creates a leak when the
pattern_list is eventually cleared, since we don't retain a copy of the
detached buffer to free.

But we can observe that the whole strbuf dance is unnecessary. The point
was presumably[1] to satisfy the lifetime requirement of the string. But
string literals have static duration; we can count on them lasting for
the whole program.

So we can fix the leak by just passing them directly. And as a bonus,
that simplifies the code. The leaks can be seen in t7002, which drops
from 25 leaks to 22 with this patch. It also makes t3602 and t1090
leak-free.

In the long run, we will also want to clean up this (undocumented!)
memory lifetime requirement of add_pattern(). But that can come in a
later patch; passing the string literals directly will be the right
thing either way.

[1] The code in question comes from 416adc8711 (sparse-checkout: update
    working directory in-process for 'init', 2019-11-21) and 99dfa6f970
    (sparse-checkout: use in-process update for disable subcommand,
    2019-11-21), but I didn't see anything in their commit messages or
    on the list explaining the strbufs.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-06-04 10:38:23 -07:00

141 lines
3.8 KiB
Bash
Executable File

#!/bin/sh
test_description='git rm in sparse checked out working trees'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' "
mkdir -p sub/dir &&
touch a b c sub/d sub/dir/e &&
git add -A &&
git commit -m files &&
cat >sparse_error_header <<-EOF &&
The following paths and/or pathspecs matched paths that exist
outside of your sparse-checkout definition, so will not be
updated in the index:
EOF
cat >sparse_hint <<-EOF &&
hint: If you intend to update such entries, try one of the following:
hint: * Use the --sparse option.
hint: * Disable or modify the sparsity rules.
hint: Disable this message with \"git config advice.updateSparsePath false\"
EOF
echo b | cat sparse_error_header - >sparse_entry_b_error &&
cat sparse_entry_b_error sparse_hint >b_error_and_hint
"
for opt in "" -f --dry-run
do
test_expect_success "rm${opt:+ $opt} does not remove sparse entries" '
git sparse-checkout set --no-cone a &&
test_must_fail git rm $opt b 2>stderr &&
test_cmp b_error_and_hint stderr &&
git ls-files --error-unmatch b
'
done
test_expect_success 'recursive rm does not remove sparse entries' '
git reset --hard &&
git sparse-checkout set sub/dir &&
git rm -r sub &&
git status --porcelain -uno >actual &&
cat >expected <<-\EOF &&
D sub/dir/e
EOF
test_cmp expected actual &&
git rm --sparse -r sub &&
git status --porcelain -uno >actual2 &&
cat >expected2 <<-\EOF &&
D sub/d
D sub/dir/e
EOF
test_cmp expected2 actual2
'
test_expect_success 'recursive rm --sparse removes sparse entries' '
git reset --hard &&
git sparse-checkout set "sub/dir" &&
git rm --sparse -r sub &&
git status --porcelain -uno >actual &&
cat >expected <<-\EOF &&
D sub/d
D sub/dir/e
EOF
test_cmp expected actual
'
test_expect_success 'rm obeys advice.updateSparsePath' '
git reset --hard &&
git sparse-checkout set a &&
test_must_fail git -c advice.updateSparsePath=false rm b 2>stderr &&
test_cmp sparse_entry_b_error stderr
'
test_expect_success 'do not advice about sparse entries when they do not match the pathspec' '
git reset --hard &&
git sparse-checkout set a &&
test_must_fail git rm nonexistent 2>stderr &&
grep "fatal: pathspec .nonexistent. did not match any files" stderr &&
! grep -F -f sparse_error_header stderr
'
test_expect_success 'do not warn about sparse entries when pathspec matches dense entries' '
git reset --hard &&
git sparse-checkout set a &&
git rm "[ba]" 2>stderr &&
test_must_be_empty stderr &&
git ls-files --error-unmatch b &&
test_must_fail git ls-files --error-unmatch a
'
test_expect_success 'do not warn about sparse entries with --ignore-unmatch' '
git reset --hard &&
git sparse-checkout set a &&
git rm --ignore-unmatch b 2>stderr &&
test_must_be_empty stderr &&
git ls-files --error-unmatch b
'
test_expect_success 'refuse to rm a non-skip-worktree path outside sparse cone' '
git reset --hard &&
git sparse-checkout set a &&
git update-index --no-skip-worktree b &&
test_must_fail git rm b 2>stderr &&
test_cmp b_error_and_hint stderr &&
git rm --sparse b 2>stderr &&
test_must_be_empty stderr &&
test_path_is_missing b
'
test_expect_success 'can remove files from non-sparse dir' '
git reset --hard &&
git sparse-checkout disable &&
mkdir -p w x/y &&
test_commit w/f &&
test_commit x/y/f &&
git sparse-checkout set --no-cone w !/x y/ &&
git rm w/f.t x/y/f.t 2>stderr &&
test_must_be_empty stderr
'
test_expect_success 'refuse to remove non-skip-worktree file from sparse dir' '
git reset --hard &&
git sparse-checkout disable &&
mkdir -p x/y/z &&
test_commit x/y/z/f &&
git sparse-checkout set --no-cone !/x y/ !x/y/z &&
git update-index --no-skip-worktree x/y/z/f.t &&
test_must_fail git rm x/y/z/f.t 2>stderr &&
echo x/y/z/f.t | cat sparse_error_header - sparse_hint >expect &&
test_cmp expect stderr
'
test_done