git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [RFC PATCH] add|rm|mv: fix bug that prevent the update of non-sparse

From
Derrick Stolee <stolee@gmail.com>
Date
Oct 25, 2021, 16:40 UTC
Message-ID
<a11d4fb0-184a-b811-64a0-e29e9cfd91a3@gmail.com>
In-Reply-To
<80b5ba61861193daf7132aa64b65fc7dde90dacb.1634866698.git.matheus.bernardino@usp.br>
On 10/21/2021 10:28 PM, Matheus Tavares wrote:
Show 32 quoted lines
> On Mon, Oct 18, 2021 at 6:28 PM Sean Christopherson <seanjc@google.com> wrote:
>>
>> $ cat .git/info/sparse-checkout
>> !arch/*
>> !tools/arch/*
>> !virt/kvm/arm/*
>> /*
>> arch/.gitignore
>> arch/Kconfig
>> arch/x86
>> tools/arch/x86
>> tools/include/uapi/linux/kvm.h
>> !Documentation
>> !drivers
>>
>> $ git read-tree -mu HEAD
>>
>> $ rm arch/x86/kvm/x86.c
> [...]
>> $ git add arch/x86
>> The following paths and/or pathspecs matched paths that exist
>> outside of your sparse-checkout definition, so will not be
>> updated in the index:
>> arch/x86
> 
> I think the problem may be that we are performing pattern matching
> slightly different in add, mv, and rm, in comparison to "git
> sparse-checkout". On "git sparse-checkout init" (or reapply), we call
> clear_ce_flags() which calls path_matches_pattern_list() for each
> component of the working tree paths. If the full path gives a match
> result of UNDECIDED, we recursively try to use the match result from
> the parent dir (or NOT_MATCHED if we reach the top with UNDECIDED).
Yes! I think this is absolutely the problem. Thanks for pointing
this out!
 
Show 16 quoted lines
> In Sean's example, we get UNDECIDED for "arch/x86/kvm/x86.c", but
> "arch/x86" gives MATCHED, so we end up using that for the full path.
> 
> However, in add|mv|rm we only call path_matches_pattern_list() for the
> full path and get UNDECIDED, which we consider the same as NOT_MATCHED,
> and end up disallowing the path update operation with a warning message.
> 
> The commands do work if we replace the sparsity pattern "arch/x86" with
> "arch/x86/" (with a trailing slash), but note that it only works
> because the pattern is relative to the root (see dir.c:1297). If we
> change it to "x86/", it would no longer work.
> 
> So far, the only way I could think of to fix this would be to perform
> pattern matching for the leading components of the paths too. That
> doesn't seem very nice, though, as it can probably be quite expensive...
> But here is a patch for discussion:

I agree that it is expensive, but that's already the case for the non-cone sparse-checkout patterns. Hopefully it is sufficient that these cases are restricted to modified files (in the case of `git add .`) or specific pathspecs (in the case of `git mv` and `git rm`).

Show 37 quoted lines
> -- >8 --
> Subject: [RFC PATCH] add|rm|mv: fix bug that prevent the update of non-sparse dirs
> 
> These three commands recently learned to avoid updating paths that do
> not match the sparse-checkout patterns even if they are missing the
> SKIP_WORKTREE bit. This is done using path_in_sparse_checkout(), which
> tries to match the path with the current set of sparsity rules using
> path_matches_pattern_list(). This is similar to what clear_ce_flags()
> does when we run "git sparse-checkout init" or "git sparse-checkout
> reapply". But note that clear_ce_flags() has a recursive behavior,
> calling path_matches_pattern_list() for each component in a path,
> whereas path_in_sparse_checkout() only calls it for the full path. This
> makes the function miss matches such as the one between path "a/b/c" and
> the pattern "b/". So if the user has the sparsity rules "!/a" and "b/",
> for example, add, rm, and mv will fail to update the path "a/b/c" and
> end up displaying a warning about "a/b/c" being outside the sparse
> checkout even though it isn't. Note that this problem only occurs with
> non-cone mode.
> 
> Fix this by making path_in_sparse_checkout() perform pattern matching
> for every component in the given path when cone mode is disabled. (This
> can be expensive, and we might want to do some form of caching for the
> match results of the leading components. However, this is not
> implemented in this patch.) Also add two tests for each command (add,
> rm, and mv) to check that they behave correctly with the said pattern
> matching. The first test would previously fail without this patch, while
> the second already succeeded. It is added mostly to make sure that we
> are not breaking the existing pattern matching for directories that are
> really sparse, and also as a protection against any future
> regressions.
> 
> Note that two other existing tests had to be changed: one test in t3602
> checks that "git rm -r <dir>" won't remove sparse entries, but it
> didn't allow the non-sparse entries inside <dir> to be removed. The
> other one, in t7002, tested that "git mv" would correctly display a
> warning message for sparse paths, but it accidentally expected the
> message to include two non-sparse paths as well.
Show 45 quoted lines
> @@ -1504,8 +1504,9 @@ static int path_in_sparse_checkout_1(const char *path,
>  				     struct index_state *istate,
>  				     int require_cone_mode)
>  {
> -	const char *base;
>  	int dtype = DT_REG;
> +	enum pattern_match_result ret = NOT_MATCHED;
> +	const char *p, *last_slash = NULL;
>  
>  	/*
>  	 * We default to accepting a path if there are no patterns or
> @@ -1516,11 +1517,31 @@ static int path_in_sparse_checkout_1(const char *path,
>  	     !istate->sparse_checkout_patterns->use_cone_patterns))
>  		return 1;
>  
> -	base = strrchr(path, '/');
> -	return path_matches_pattern_list(path, strlen(path), base ? base + 1 : path,
> -					 &dtype,
> -					 istate->sparse_checkout_patterns,
> -					 istate) > 0;
> +	if (istate->sparse_checkout_patterns->use_cone_patterns) {
> +		const char *base = strrchr(path, '/');
> +		return path_matches_pattern_list(path, strlen(path),
> +				base ? base + 1 : path, &dtype,
> +				istate->sparse_checkout_patterns, istate) > 0;
> +	}
> +
> +	for (p = path; ; p++) {
> +		enum pattern_match_result match;
> +
> +		if (*p && *p != '/')
> +			continue;
> +
> +		match  = path_matches_pattern_list(path, p - path,
> +				last_slash ? last_slash + 1 : path, &dtype,
> +				istate->sparse_checkout_patterns, istate);
> +
> +		if (match != UNDECIDED)
> +			ret = match;
> +		if (!*p)
> +			break;
> +		last_slash = p;
> +	}
> +
> +	return ret;
This implementation makes sense to me.
Show 6 quoted lines
>  test_expect_success 'recursive rm does not remove sparse entries' '
>  	git reset --hard &&
>  	git sparse-checkout set sub/dir &&
> -	test_must_fail git rm -r sub &&
> -	git rm --sparse -r sub &&
> +	git rm -r sub &&

Interesting that the new pattern-matching already presents a change of behavior in this test case.

Show 5 quoted lines
>  	git status --porcelain -uno >actual &&
>  	cat >expected <<-\EOF &&
> +	D  sub/dir/e
> +	EOF
> +	test_cmp expected actual &&

And here is why. Excellent. I suppose that setting the pattern to be "sub/dir/" would have shown this behavior before.

Show 10 quoted lines
> +
> +	git rm --sparse -r sub &&
> +	git status --porcelain -uno >actual2 &&
> +	cat >expected2 <<-\EOF &&
>  	D  sub/d
>  	D  sub/dir/e
>  	EOF
> -	test_cmp expected actual
> +	test_cmp expected2 actual2
>  '
The rest of the test cases add new checks that are very valuable.

I love this idea and I agree that it would be better to change the loop direction to match the full path first (as you mention in your response).

Thanks, -Stolee

Previous: Matheus Tavares
Message 116 of 116 in “[RFC] Sparse-checkout: modify 'git add', 'git rm', and 'git add' behavior”
  1. 00/13 [RFC] Sparse-checkout: modify 'git add', 'git rm', and 'git add' behaviorDerrick Stolee via GitGitGadget, Aug 24, 2021
  2. 01/13 t1092: behavior for adding sparse filesDerrick Stolee via GitGitGadget, Aug 24, 2021
  3. 02/13 dir: extract directory-matching logicDerrick Stolee via GitGitGadget, Aug 24, 2021
  4. 03/13 dir: select directories correctlyDerrick Stolee via GitGitGadget, Aug 24, 2021
  5. René ScharfeSep 24, 2021
  6. 04/13 dir: fix pattern matching on dirsDerrick Stolee via GitGitGadget, Aug 24, 2021
  7. 05/13 add: fail when adding an untracked sparse fileDerrick Stolee via GitGitGadget, Aug 24, 2021
  8. Matheus Tavares BernardinoAug 27, 2021
  9. Matheus Tavares BernardinoAug 27, 2021
  10. Derrick StoleeSep 8, 2021
  11. 06/13 add: skip paths that are outside sparse-checkout coneDerrick Stolee via GitGitGadget, Aug 24, 2021
  12. Matheus TavaresAug 27, 2021
  13. Derrick StoleeSep 8, 2021
  14. Derrick StoleeSep 8, 2021
  15. Derrick StoleeSep 8, 2021
  16. 07/13 add: implement the --sparse optionDerrick Stolee via GitGitGadget, Aug 24, 2021
  17. Matheus Tavares BernardinoAug 27, 2021
  18. 08/13 add: prevent adding sparse conflict filesDerrick Stolee via GitGitGadget, Aug 24, 2021
  19. Matheus Tavares BernardinoAug 27, 2021
  20. 09/13 rm: add --sparse optionDerrick Stolee via GitGitGadget, Aug 24, 2021
  21. Matheus Tavares BernardinoAug 27, 2021
  22. Derrick StoleeSep 8, 2021
  23. 10/13 rm: skip sparse paths with missing SKIP_WORKTREEDerrick Stolee via GitGitGadget, Aug 24, 2021
  24. Matheus Tavares BernardinoAug 27, 2021
  25. 11/13 mv: refuse to move sparse pathsDerrick Stolee via GitGitGadget, Aug 24, 2021
  26. Matheus Tavares BernardinoAug 27, 2021
  27. Matheus Tavares BernardinoAug 27, 2021
  28. Derrick StoleeSep 8, 2021
  29. 12/13 mv: add '--sparse' option to ignore sparse-checkoutDerrick Stolee via GitGitGadget, Aug 24, 2021
  30. Matheus Tavares BernardinoAug 28, 2021
  31. 13/13 advice: update message to suggest '--sparse'Derrick Stolee via GitGitGadget, Aug 24, 2021
  32. 00/14 Sparse-checkout: modify 'git add', 'git rm', and 'git add' behaviorDerrick Stolee via GitGitGadget, Sep 12, 2021
  33. 01/14 t3705: test that 'sparse_entry' is unstagedDerrick Stolee via GitGitGadget, Sep 12, 2021
  34. Elijah NewrenSep 15, 2021
  35. Derrick StoleeSep 15, 2021
  36. Matheus TavaresSep 15, 2021
  37. Derrick StoleeSep 15, 2021
  38. 02/14 t1092: behavior for adding sparse filesDerrick Stolee via GitGitGadget, Sep 12, 2021
  39. Ævar Arnfjörð BjarmasonSep 12, 2021
  40. Derrick StoleeSep 13, 2021
  41. 03/14 dir: extract directory-matching logicDerrick Stolee via GitGitGadget, Sep 12, 2021
  42. 04/14 dir: select directories correctlyDerrick Stolee via GitGitGadget, Sep 12, 2021
  43. Ævar Arnfjörð BjarmasonSep 12, 2021
  44. Derrick StoleeSep 15, 2021
  45. Elijah NewrenSep 15, 2021
  46. Derrick StoleeSep 15, 2021
  47. 05/14 dir: fix pattern matching on dirsDerrick Stolee via GitGitGadget, Sep 12, 2021
  48. 06/14 add: fail when adding an untracked sparse fileDerrick Stolee via GitGitGadget, Sep 12, 2021
  49. 07/14 add: skip tracked paths outside sparse-checkout coneDerrick Stolee via GitGitGadget, Sep 12, 2021
  50. 09/14 add: update --chmod to skip sparse pathsDerrick Stolee via GitGitGadget, Sep 12, 2021
  51. 08/14 add: implement the --sparse optionDerrick Stolee via GitGitGadget, Sep 12, 2021
  52. Elijah NewrenSep 15, 2021
  53. Derrick StoleeSep 20, 2021
  54. 10/14 add: update --renormalize to skip sparse pathsDerrick Stolee via GitGitGadget, Sep 12, 2021
  55. 11/14 rm: add --sparse optionDerrick Stolee via GitGitGadget, Sep 12, 2021
  56. 14/14 advice: update message to suggest '--sparse'Derrick Stolee via GitGitGadget, Sep 12, 2021
  57. Ævar Arnfjörð BjarmasonSep 12, 2021
  58. Derrick StoleeSep 15, 2021
  59. 12/14 rm: skip sparse paths with missing SKIP_WORKTREEDerrick Stolee via GitGitGadget, Sep 12, 2021
  60. 13/14 mv: refuse to move sparse pathsDerrick Stolee via GitGitGadget, Sep 12, 2021
  61. Elijah NewrenSep 15, 2021
  62. 00/14 Sparse-checkout: modify 'git add', 'git rm', and 'git add' behaviorDerrick Stolee via GitGitGadget, Sep 20, 2021
  63. 02/14 t1092: behavior for adding sparse filesDerrick Stolee via GitGitGadget, Sep 20, 2021
  64. Junio C HamanoSep 22, 2021
  65. Derrick StoleeSep 23, 2021
  66. 01/14 t3705: test that 'sparse_entry' is unstagedDerrick Stolee via GitGitGadget, Sep 20, 2021
  67. Junio C HamanoSep 22, 2021
  68. 03/14 dir: extract directory-matching logicDerrick Stolee via GitGitGadget, Sep 20, 2021
  69. Junio C HamanoSep 22, 2021
  70. Derrick StoleeSep 23, 2021
  71. Derrick StoleeSep 23, 2021
  72. Junio C HamanoSep 23, 2021
  73. Derrick StoleeSep 24, 2021
  74. 04/14 dir: select directories correctlyDerrick Stolee via GitGitGadget, Sep 20, 2021
  75. 05/14 dir: fix pattern matching on dirsDerrick Stolee via GitGitGadget, Sep 20, 2021
  76. 06/14 add: fail when adding an untracked sparse fileDerrick Stolee via GitGitGadget, Sep 20, 2021
  77. 07/14 add: skip tracked paths outside sparse-checkout coneDerrick Stolee via GitGitGadget, Sep 20, 2021
  78. 08/14 add: implement the --sparse optionDerrick Stolee via GitGitGadget, Sep 20, 2021
  79. 09/14 add: update --chmod to skip sparse pathsDerrick Stolee via GitGitGadget, Sep 20, 2021
  80. 10/14 add: update --renormalize to skip sparse pathsDerrick Stolee via GitGitGadget, Sep 20, 2021
  81. 11/14 rm: add --sparse optionDerrick Stolee via GitGitGadget, Sep 20, 2021
  82. 12/14 rm: skip sparse paths with missing SKIP_WORKTREEDerrick Stolee via GitGitGadget, Sep 20, 2021
  83. 13/14 mv: refuse to move sparse pathsDerrick Stolee via GitGitGadget, Sep 20, 2021
  84. 14/14 advice: update message to suggest '--sparse'Derrick Stolee via GitGitGadget, Sep 20, 2021
  85. Elijah NewrenSep 24, 2021
  86. 00/13 Sparse-checkout: modify 'git add', 'git rm', and 'git mv' behaviorDerrick Stolee via GitGitGadget, Sep 24, 2021
  87. 01/13 t3705: test that 'sparse_entry' is unstagedDerrick Stolee via GitGitGadget, Sep 24, 2021
  88. 02/13 t1092: behavior for adding sparse filesDerrick Stolee via GitGitGadget, Sep 24, 2021
  89. 03/13 dir: select directories correctlyDerrick Stolee via GitGitGadget, Sep 24, 2021
  90. 04/13 dir: fix pattern matching on dirsDerrick Stolee via GitGitGadget, Sep 24, 2021
  91. Glen ChooNov 2, 2021
  92. Junio C HamanoNov 2, 2021
  93. Derrick StoleeNov 2, 2021
  94. Derrick StoleeNov 2, 2021
  95. Ævar Arnfjörð BjarmasonNov 2, 2021
  96. Derrick StoleeNov 3, 2021
  97. Junio C HamanoNov 3, 2021
  98. 05/13 add: fail when adding an untracked sparse fileDerrick Stolee via GitGitGadget, Sep 24, 2021
  99. 06/13 add: skip tracked paths outside sparse-checkout coneDerrick Stolee via GitGitGadget, Sep 24, 2021
  100. 07/13 add: implement the --sparse optionDerrick Stolee via GitGitGadget, Sep 24, 2021
  101. 08/13 add: update --chmod to skip sparse pathsDerrick Stolee via GitGitGadget, Sep 24, 2021
  102. 09/13 add: update --renormalize to skip sparse pathsDerrick Stolee via GitGitGadget, Sep 24, 2021
  103. 10/13 rm: add --sparse optionDerrick Stolee via GitGitGadget, Sep 24, 2021
  104. 11/13 rm: skip sparse paths with missing SKIP_WORKTREEDerrick Stolee via GitGitGadget, Sep 24, 2021
  105. 12/13 mv: refuse to move sparse pathsDerrick Stolee via GitGitGadget, Sep 24, 2021
  106. 13/13 advice: update message to suggest '--sparse'Derrick Stolee via GitGitGadget, Sep 24, 2021
  107. Elijah NewrenSep 27, 2021
  108. Junio C HamanoSep 27, 2021
  109. Sean ChristophersonOct 18, 2021
  110. Derrick StoleeOct 19, 2021
  111. Sean ChristophersonOct 19, 2021
  112. Junio C HamanoOct 20, 2021
  113. Sean ChristophersonOct 20, 2021
  114. add|rm|mv: fix bug that prevent the update of non-sparseMatheus Tavares, Oct 22, 2021
  115. Matheus TavaresOct 22, 2021
  116. Derrick StoleeOct 25, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.