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

Re: [PATCH v4 00/10] Sparse index: delete ignored files outside sparse cone

From
Derrick Stolee <stolee@gmail.com>
Date
Aug 30, 2021, 13:54 UTC
Message-ID
<d55363df-c2e9-3847-d98c-3d07e22d8a5d@gmail.com>
In-Reply-To
<CABPp-BG01tXnd_UhYJDX4Tm_EcXGju9CkB8BXU0XS-YAWEuaeQ@mail.gmail.com>
On 8/27/2021 5:56 PM, Elijah Newren wrote:
Show 7 quoted lines
> On Tue, Aug 24, 2021 at 2:51 PM Derrick Stolee via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
>>
>> UPDATE: I had to base this version on a merge of ds/add-with-sparse-index
>> and v2.33.0-rc1 because of some changes to dir.h that became important!
> 
> Isn't this stale (i.e. just copied from v2/v3)?

Sorry, yes. It was an update to the original cover letter, but was not updated in later versions.

Show 8 quoted lines
>>  * I split the commit that involves cache_tree_update(). I have not yet
>>    succeeded in creating tests to demonstrate why this is required outside
>>    of needing it in the Scalar functional tests, which includes a version of
>>    partial clone. I will continue to investigate recreating this scenario in
>>    the Git test suite, but I wanted to send this version out to get feedback
>>    on the things that have changed.
> 
> Are you talking about patch 3 or patch 4?

I'm talking about patches 3 and 4, which (at the time that I created them a few months ago, before later merging them) they each had solved some problems in the Scalar functional tests. The issue I'm seeing now is that since so many commands have integrated with the sparse index, those functional tests are not testing convert_to_sparse() as heavily, so simply reverting each does not show the problem any more.

Show 21 quoted lines
> For patch 4, as I commented on that patch, my guess would be that you
> start with a full tree, pick some directory that you will sparsify
> away, and before sparsifying it away, change some file under that
> directory and stage the change.  Then when you sparsify the directory
> away with cone mode, I think the cache_tree_update() will need to
> write the tree.  At least, some quick greps of cache_tree_update
> suggest there aren't that many codepaths that attempt to update it, so
> I don't think anything else would have written it yet at that point.
> 
> For patch 3, since it comes before patch 5, it would be triggerable
> with the same case.  Is it possible you originally found both patches
> based off similar initial conditions that just resulted in slightly
> different behavioral reports, and thus that patch 4 is good enough
> without patch 3?  Trying to ignore that possibility and looking at the
> cache_tree_update() code, the only other reasons for failure of
> cache_tree_update() that I can see are (a) someone having invalid
> hashes recorded in the index (e.g. someone using `git update-index
> --index-info` with some invalid hashes, or someone manually deleting
> some loose blobs from under their .git/ directory), or (b) disk being
> full or read-only so that writing new tree objects to the object store
> fail.
Just for full context:
* Patch 3 removes a warning and "return -1" when cache_tree_update()
  fails. This makes convert_to_sparse() resilient to any failure that
  might happen in cache_tree_update(), whatever it might be.
* Patch 4 adds the WRITE_TREE_MISSING_OK, which removes one possible
  reason that cache_tree_update() could fail (and removes an attempted
  promisor fetch in the case of partial clone).

So I could see that either of the two patches would be sufficient to make tests pass in all normally-testable scenarios. However, patch 3 is valuable for possibly more complicated failure scenarios such as corrupted data, while patch 4 is valuable for performance reasons in a very specific environment.

Show 15 quoted lines
>> base-commit: 80b8d6c56b8a5f5db1d5c2a0159fd808e8a7fc4f
>> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1009%2Fderrickstolee%2Fsparse-index%2Fignored-files-v4
>> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1009/derrickstolee/sparse-index/ignored-files-v4
>> Pull-Request: https://github.com/gitgitgadget/git/pull/1009
> 
> A super micro nit:
> 
> It appears this is based on your other branches that have since been
> incorporated into master.  Not a big deal, but rebasing on master
> might make it easier for others to take a look at the series (I was
> confused at first why this series depended on other unmerged series,
> before realizing those other series had since merged down).  I know we
> normally don't like rebasing series, but I suspect it wouldn't change
> the range-diff (as I don't think others are touching these areas of
> the code), and thus be transparent to Junio.

The 'git add' integration only recently hit 'master', so I have not had a chance to base on a version of that branch with the necessary dependencies.

Show 25 quoted lines
>>   6:  c9e100e68f8 !  7:  e496f3cee66 attr: be careful about sparse directories
>>      @@ attr.c: static struct attr_stack *read_attr_from_index(struct index_state *istat
>>                 return NULL;
>>
>>       + /*
>>      -+  * In the case of cone-mode sparse-checkout, getting the
>>      -+  * .gitattributes file from a directory is meaningless: all
>>      -+  * contained paths will be sparse if the .gitattributes is also
>>      -+  * sparse. In the case of a sparse index, it is critical that we
>>      -+  * don't go looking for one as it will expand the index.
>>      ++  * The .gitattributes file only applies to files within its
>>      ++  * parent directory. In the case of cone-mode sparse-checkout,
>>      ++  * the .gitattributes file is sparse if and only if all paths
>>      ++  * within that directory are also sparse. Thus, don't load the
>>      ++  * .gitattributes file since it will not matter.
>>      ++  *
>>      ++  * In the case of a sparse index, it is critical that we don't go
>>      ++  * looking for a .gitattributes file, as the index will expand.
> 
> This comment is better, and I appreciate the thought behind adding the
> second paragraph, but the wording seems fuzzy and potentially
> confusing to future readers.  Perhaps changing the wording to:
> 
> ...looking for a .gitattributes file, as doing so would cause the
> index to expand.
Thanks.
Show 17 quoted lines
>>   8:  febef675f05 !  9:  c19d93ec5d7 sparse-checkout: clear tracked sparse dirs
>>      @@ Commit message
>>           the sparse directory. This depends on the indexed version of the file,
>>           so the sparse directory must be expanded.
>>
>>      +    We must take special care to look for untracked, non-ignored files in
>>      +    these directories before deleting them. We do not want to delete any
>>      +    meaningful work that the users were doing in those directories and
>>      +    perhaps forgot to add and commit before switching sparse-checkout
>>      +    definitions. Since those untracked files might be code files that
>>      +    generated ignored build output, also do not delete any ignored files
>>      +    from these directories in that case. The users can recover their state
>>      +    by resetting their sparse-checkout definition to include that directory
>>      +    and continue. Alternatively, they can see the warning that is presented
>>      +    and delete the directory themselves to regain the performance they
>>      +    expect.
>>      +
...
Show 9 quoted lines
>>      ++When changing the sparse-checkout patterns in cone mode, Git will inspect each
>>      ++tracked directory that is not within the sparse-checkout cone to see if it
>>      ++contains any untracked files. If all of those files are ignored due to the
>>      ++`.gitignore` patterns, then the directory will be deleted. If any of the
>>      ++untracked files within that directory is not ignored, then no deletions will
>>      ++occur within that directory and a warning message will appear. If these files
>>      ++are important, then reset your sparse-checkout definition so they are included,
>>      ++use `git add` and `git commit` to store them, then remove any remaining files
>>      ++manually to ensure Git can behave optimally.
...
Show 30 quoted lines
> Thanks for the extra explanations; those help.
> 
> You haven't yet addressed how this choice will interact with
> .gitignore files, though (as I commented on v3).  You alluded in patch
> 7 to how it was critical to avoid looking for .gitattributes files
> because doing so would require the index to expand, possibly
> recursively all the way down.  Don't .gitignore files have the same
> problem?  Nuking all the ignored files so that you can delete extra
> directories and thus not need to walk into them during operations
> helps, but does the fact that we sometimes avoid removing directories
> mean we have to walk into them anyway?  Do we only need to walk into
> such directories when they are (still? again?) present in the working
> copy?  Does there need to be some code added somewhere that checks for
> the presence of a directory in the working copy and unsparsifies
> sparse directory entries in the index when such directories are found?
> 
> Also, does non-cone mode sparsity paths have weird bugs around letting
> users specify patterns that would happen to also exclude .gitignore
> files from the working copy, but keep other sibling files within the
> same directory?  In particular, if the .gitignore files aren't
> present, does git add -A add a bunch of supposed-to-be-ignored files?
> (If so, that's an issue that's probably tangential to this series
> since it's not about cone mode or the sparse index, but it's certainly
> an interesting thought.)
> 
> Maybe the whole .gitignore situation with cone mode has a simple
> answer, but since this whole series is about gaining performance by
> avoiding recursing into directories corresponding to sparse directory
> entries, it feels the answers to how .gitignore will be handled should
> at least be addressed.

The goal of this series is to allow Git to operate as normal (respecting .gitattributes and .gitignore files as part of that) without needing to expand a sparse index. The way it achieves this is by deleting the directories that leave the sparse-checkout cone. If those directories don't exist, then we don't care about the .gitattributes or .gitignore files in those locations. When the directory _does_ exist, then we need to look for the .gitignore file which expands the index to full. This is done naturally by Git using index_name_pos() for "<dir>/.gitignore" which finds that "<dir>" (or one of its parent directories) is a sparse directory, then expands the index and searches for "<dir>/.gitignore" again, returning the position if it exists in the fully-expanded index.

The difference between the .gitattributes and .gitignore files is that we don't need to do anything with the .gitignore files to prevent Git from accessing them with their contained directories don't exist. The .gitattributes change in attr.c is reachable from some code path even when the directory doesn't exist, so it needs special logic to avoid that expansion.

When we don't succeed in deleting sparse directories, performance suffers. That's the reason behind having a very-visible warnings to users that something is blocking the removal. We will be monitoring performance of users where it is _very clear_ when this happens. From that experience, we might discover new ways to assist them to get into a better state (perhaps a warning during "git status" would help?).

Thanks, -Stolee

Previous: Elijah NewrenNext: Elijah Newren
Message 76 of 92 in “Sparse index: delete ignored files outside sparse cone”
  1. 0/2 Sparse index: delete ignored files outside sparse coneDerrick Stolee via GitGitGadget, Jul 29, 2021
  2. 1/2 t7519: rewrite sparse index testDerrick Stolee via GitGitGadget, Jul 29, 2021
  3. 2/2 sparse-checkout: clear tracked sparse dirsDerrick Stolee via GitGitGadget, Jul 29, 2021
  4. Elijah NewrenJul 30, 2021
  5. Derrick StoleeAug 2, 2021
  6. Elijah NewrenAug 2, 2021
  7. Derrick StoleeAug 5, 2021
  8. Elijah NewrenAug 5, 2021
  9. Elijah NewrenJul 30, 2021
  10. 0/8 Sparse index: delete ignored files outside sparse coneDerrick Stolee via GitGitGadget, Aug 10, 2021
  11. 1/8 t7519: rewrite sparse index testDerrick Stolee via GitGitGadget, Aug 10, 2021
  12. 2/8 sparse-index: silently return when not using cone-mode patternsDerrick Stolee via GitGitGadget, Aug 10, 2021
  13. 3/8 sparse-index: silently return when cache tree failsDerrick Stolee via GitGitGadget, Aug 10, 2021
  14. Elijah NewrenAug 19, 2021
  15. Derrick StoleeAug 20, 2021
  16. 4/8 unpack-trees: fix nested sparse-dir searchDerrick Stolee via GitGitGadget, Aug 10, 2021
  17. 5/8 sparse-checkout: create helper methodsDerrick Stolee via GitGitGadget, Aug 10, 2021
  18. Derrick StoleeAug 12, 2021
  19. 6/8 attr: be careful about sparse directoriesDerrick Stolee via GitGitGadget, Aug 10, 2021
  20. 7/8 sparse-index: add SPARSE_INDEX_IGNORE_CONFIG flagDerrick Stolee via GitGitGadget, Aug 10, 2021
  21. 8/8 sparse-checkout: clear tracked sparse dirsDerrick Stolee via GitGitGadget, Aug 10, 2021
  22. 0/8 Sparse index: delete ignored files outside sparse coneDerrick Stolee via GitGitGadget, Aug 17, 2021
  23. 2/8 sparse-index: silently return when not using cone-mode patternsDerrick Stolee via GitGitGadget, Aug 17, 2021
  24. 1/8 t7519: rewrite sparse index testDerrick Stolee via GitGitGadget, Aug 17, 2021
  25. Johannes SchindelinAug 19, 2021
  26. Derrick StoleeAug 20, 2021
  27. Eric SunshineAug 20, 2021
  28. 3/8 sparse-index: silently return when cache tree failsDerrick Stolee via GitGitGadget, Aug 17, 2021
  29. 5/8 sparse-checkout: create helper methodsDerrick Stolee via GitGitGadget, Aug 17, 2021
  30. Johannes SchindelinAug 19, 2021
  31. Derrick StoleeAug 20, 2021
  32. 4/8 unpack-trees: fix nested sparse-dir searchDerrick Stolee via GitGitGadget, Aug 17, 2021
  33. Johannes SchindelinAug 19, 2021
  34. Derrick StoleeAug 20, 2021
  35. René ScharfeAug 20, 2021
  36. René ScharfeAug 20, 2021
  37. Elijah NewrenAug 19, 2021
  38. 8/8 sparse-checkout: clear tracked sparse dirsDerrick Stolee via GitGitGadget, Aug 17, 2021
  39. Johannes SchindelinAug 19, 2021
  40. Derrick StoleeAug 20, 2021
  41. Elijah NewrenAug 20, 2021
  42. Elijah NewrenAug 20, 2021
  43. Johannes SchindelinAug 23, 2021
  44. 7/8 sparse-index: add SPARSE_INDEX_IGNORE_CONFIG flagDerrick Stolee via GitGitGadget, Aug 17, 2021
  45. Derrick StoleeAug 18, 2021
  46. 6/8 attr: be careful about sparse directoriesDerrick Stolee via GitGitGadget, Aug 17, 2021
  47. Johannes SchindelinAug 19, 2021
  48. Derrick StoleeAug 20, 2021
  49. Elijah NewrenAug 19, 2021
  50. Derrick StoleeAug 20, 2021
  51. Elijah NewrenAug 20, 2021
  52. Elijah NewrenAug 17, 2021
  53. 00/10 Sparse index: delete ignored files outside sparse coneDerrick Stolee via GitGitGadget, Aug 24, 2021
  54. 01/10 t7519: rewrite sparse index testDerrick Stolee via GitGitGadget, Aug 24, 2021
  55. 02/10 sparse-index: silently return when not using cone-mode patternsDerrick Stolee via GitGitGadget, Aug 24, 2021
  56. 03/10 sparse-index: silently return when cache tree failsDerrick Stolee via GitGitGadget, Aug 24, 2021
  57. 04/10 sparse-index: use WRITE_TREE_MISSING_OKDerrick Stolee via GitGitGadget, Aug 24, 2021
  58. Elijah NewrenAug 27, 2021
  59. Derrick StoleeAug 30, 2021
  60. Elijah NewrenAug 30, 2021
  61. 05/10 unpack-trees: fix nested sparse-dir searchDerrick Stolee via GitGitGadget, Aug 24, 2021
  62. René ScharfeAug 24, 2021
  63. Derrick StoleeAug 25, 2021
  64. 06/10 sparse-checkout: create helper methodsDerrick Stolee via GitGitGadget, Aug 24, 2021
  65. 07/10 attr: be careful about sparse directoriesDerrick Stolee via GitGitGadget, Aug 24, 2021
  66. 08/10 sparse-index: add SPARSE_INDEX_MEMORY_ONLY flagDerrick Stolee via GitGitGadget, Aug 24, 2021
  67. 09/10 sparse-checkout: clear tracked sparse dirsDerrick Stolee via GitGitGadget, Aug 24, 2021
  68. 10/10 sparse-checkout: add config to disable deleting dirsDerrick Stolee via GitGitGadget, Aug 24, 2021
  69. Elijah NewrenAug 27, 2021
  70. Derrick StoleeAug 30, 2021
  71. Elijah NewrenAug 30, 2021
  72. Elijah NewrenAug 27, 2021
  73. Elijah NewrenAug 27, 2021
  74. Derrick StoleeAug 30, 2021
  75. Elijah NewrenAug 30, 2021
  76. Derrick StoleeAug 30, 2021
  77. Elijah NewrenAug 30, 2021
  78. 0/9 Sparse index: delete ignored files outside sparse coneDerrick Stolee via GitGitGadget, Sep 8, 2021
  79. 1/9 t7519: rewrite sparse index testDerrick Stolee via GitGitGadget, Sep 8, 2021
  80. 2/9 sparse-index: silently return when not using cone-mode patternsDerrick Stolee via GitGitGadget, Sep 8, 2021
  81. 3/9 unpack-trees: fix nested sparse-dir searchDerrick Stolee via GitGitGadget, Sep 8, 2021
  82. 5/9 sparse-index: use WRITE_TREE_MISSING_OKDerrick Stolee via GitGitGadget, Sep 8, 2021
  83. 4/9 sparse-index: silently return when cache tree failsDerrick Stolee via GitGitGadget, Sep 8, 2021
  84. 6/9 sparse-checkout: create helper methodsDerrick Stolee via GitGitGadget, Sep 8, 2021
  85. 7/9 attr: be careful about sparse directoriesDerrick Stolee via GitGitGadget, Sep 8, 2021
  86. 8/9 sparse-index: add SPARSE_INDEX_MEMORY_ONLY flagDerrick Stolee via GitGitGadget, Sep 8, 2021
  87. 9/9 sparse-checkout: clear tracked sparse dirsDerrick Stolee via GitGitGadget, Sep 8, 2021
  88. Junio C HamanoSep 8, 2021
  89. Junio C HamanoSep 8, 2021
  90. Derrick StoleeSep 8, 2021
  91. Junio C HamanoSep 8, 2021
  92. Elijah NewrenSep 8, 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.