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

Re: [PATCH v4 6/6] clean: teach clean -d to skip dirs containing ignored files

From
Samuel Lijin <sxlijin@gmail.com>
Date
May 22, 2017, 05:58 UTC
Message-ID
<CAJZjrdX9BnuxY3tmpswG+yEdDm1+AR8rc5wKGZyVCMp-jP218A@mail.gmail.com>
In-Reply-To
<xmqqtw4do5tf.fsf@gitster.mtv.corp.google.com>
On Mon, May 22, 2017 at 12:48 AM, Junio C Hamano <gitster@pobox.com> wrote:
Show 40 quoted lines
> Samuel Lijin <sxlijin@gmail.com> writes:
>
>> +     for (j = i = 0; i < dir.nr;) {
>> +             for (;
>> +                  j < dir.ignored_nr &&
>> +                    0 <= cmp_dir_entry(&dir.entries[i], &dir.ignored[j]);
>> +                  j++);
>> +
>> +             if ((j < dir.ignored_nr) &&
>> +                             check_dir_entry_contains(dir.entries[i], dir.ignored[j])) {
>> +                     /* skip any dir.entries which contains a dir.ignored */
>> +                     free(dir.entries[i]);
>> +                     dir.entries[i++] = NULL;
>> +             } else {
>> +                     /* prune the contents of a dir.entries which will be removed */
>> +                     struct dir_entry *ent = dir.entries[i++];
>> +                     for (;
>> +                          i < dir.nr &&
>> +                            check_dir_entry_contains(ent, dir.entries[i]);
>> +                          i++) {
>> +                             free(dir.entries[i]);
>> +                             dir.entries[i] = NULL;
>> +                     }
>> +             }
>> +     }
>
> The second loop in the else clause is a bit tricky, and the comment
> "which will be removed" is not all that helpful to explain why the
> loop is there.
>
> But I think the code is correct.  Here is how I understood it.
>
>     While looking at dir.entries[i], the code noticed that nothing
>     in that directory is ignored.  But entries in dir.entries[] that
>     come later may be contained in dir.entries[i] and we just want
>     to show the top-level untracked one (e.g. "a/" and "a/b/" were
>     in entries[], there is nothing in "a/", so naturally there is
>     nothing in "a/b/", but we do not want to bother showing
>     both---showing "a/" alone saying "the entire a/ is untracked" is
>     what we want).
Yep, that's the gist of it.

More specifically: the contents of untracked dirs have to be picked up so that clean -d can distinguish between purely untracked dirs and untracked dirs which also contain ignored files. In the case of a purely untracked dir, clean -d can remove the dir itself, and should just skip over all the dir's contents; in the case of a mixed untracked dir, clean -d should not remove the dir itself, just the untracked contents.

> We may want to have a test to ensure "a/b/" is indeed omitted in
> such a situation from the output, though.

Is there a reason to ensure specifically a/b/ is tested, or are the current tests, which do cover the a/b (i.e. where a/b is a file, not where a/b/ is a dir) case, insufficient for some reason?

Show 6 quoted lines
> By the way, instead of putting NULL, it may be easier to follow if
> you used two pointers, src and dst, into dir.entries[], just like
> you did in your latest version of [PATCH 4/6].  That way, you do not
> have to change anything in the later loop that walks over elements
> in the dir.entries[] array.  It would also help the logic easier to
> follow if the above loop were its own helper function.

Agreed on the helper function. On the src-dst thing: I considered it, but I figured another O(n) set of array moves was unnecessary. I guess this is one of those cases where premature optimization doesn't make sense?

Show 106 quoted lines
> Putting them all together, here is what I came up with that can be
> squashed into your patch.  I am undecided myself if this is easier
> to follow than your version, but it seems to pass your test ;-)
>
> Thanks.
>
>  builtin/clean.c | 70 ++++++++++++++++++++++++++++++++++-----------------------
>  1 file changed, 42 insertions(+), 28 deletions(-)
>
> diff --git a/builtin/clean.c b/builtin/clean.c
> index dd3308a447..c8712e7ac8 100644
> --- a/builtin/clean.c
> +++ b/builtin/clean.c
> @@ -851,9 +851,49 @@ static void interactive_main_loop(void)
>         }
>  }
>
> +static void simplify_untracked(struct dir_struct *dir)
> +{
> +       int src, dst, ign;
> +
> +       for (src = dst = ign = 0; src < dir->nr; src++) {
> +               /*
> +                * Skip entries in ignored[] that cannot be inside
> +                * entries[src]
> +                */
> +               while (ign < dir->ignored_nr &&
> +                      0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign]))
> +                       ign++;
> +
> +               if (dir->ignored_nr <= ign ||
> +                   !check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) {
> +                       /*
> +                        * entries[src] does not contain an ignored
> +                        * path -- we need to keep it.  But we do not
> +                        * want to show entries[] that are contained
> +                        * in entries[src].
> +                        */
> +                       struct dir_entry *ent = dir->entries[src++];
> +                       dir->entries[dst++] = ent;
> +                       while (src < dir->nr &&
> +                              check_dir_entry_contains(ent, dir->entries[src])) {
> +                               free(dir->entries[src++]);
> +                       }
> +                       /* compensate for the outer loop's loop control */
> +                       src--;
> +               } else {
> +                       /*
> +                        * entries[src] contains an ignored path --
> +                        * drop it.
> +                        */
> +                       free(dir->entries[src]);
> +               }
> +       }
> +       dir->nr = dst;
> +}
> +
>  int cmd_clean(int argc, const char **argv, const char *prefix)
>  {
> -       int i, j, res;
> +       int i, res;
>         int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
>         int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
>         int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
> @@ -928,30 +968,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
>                        prefix, argv);
>
>         fill_directory(&dir, &pathspec);
> -
> -       for (j = i = 0; i < dir.nr;) {
> -               for (;
> -                    j < dir.ignored_nr &&
> -                      0 <= cmp_dir_entry(&dir.entries[i], &dir.ignored[j]);
> -                    j++);
> -
> -               if ((j < dir.ignored_nr) &&
> -                               check_dir_entry_contains(dir.entries[i], dir.ignored[j])) {
> -                       /* skip any dir.entries which contains a dir.ignored */
> -                       free(dir.entries[i]);
> -                       dir.entries[i++] = NULL;
> -               } else {
> -                       /* prune the contents of a dir.entries which will be removed */
> -                       struct dir_entry *ent = dir.entries[i++];
> -                       for (;
> -                            i < dir.nr &&
> -                              check_dir_entry_contains(ent, dir.entries[i]);
> -                            i++) {
> -                               free(dir.entries[i]);
> -                               dir.entries[i] = NULL;
> -                       }
> -               }
> -       }
> +       simplify_untracked(&dir);
>
>         for (i = 0; i < dir.nr; i++) {
>                 struct dir_entry *ent = dir.entries[i];
> @@ -959,9 +976,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
>                 struct stat st;
>                 const char *rel;
>
> -               if (!ent)
> -                       continue;
> -
>                 if (!cache_name_is_other(ent->name, ent->len))
>                         continue;
>
Previous: Junio C HamanoNext: Junio C Hamano
Message 62 of 89 in “Bug Report: .gitignore behavior is not matching in git clean and git status”
  1. Chris JohnsonApr 28, 2017
  2. Junio C HamanoMay 1, 2017
  3. Chris JohnsonMay 1, 2017
  4. Junio C HamanoMay 1, 2017
  5. Samuel LijinMay 1, 2017
  6. Samuel LijinMay 1, 2017
  7. Junio C HamanoMay 2, 2017
  8. 0/7 Keep git clean -d from inadvertently removing ignored filesSamuel Lijin, May 3, 2017
  9. 1/7 t7300: skip untracked dirs containing ignored filesSamuel Lijin, May 3, 2017
  10. Stefan BellerMay 3, 2017
  11. Samuel LijinMay 3, 2017
  12. Stefan BellerMay 3, 2017
  13. 7/7 t7061: check for ignored file in untracked dirSamuel Lijin, May 3, 2017
  14. 5/7 dir: change linkage of cmp_name() and check_contains()Samuel Lijin, May 3, 2017
  15. 3/7 dir: add method to check if a dir_entry lexically contains anotherSamuel Lijin, May 3, 2017
  16. Stefan BellerMay 3, 2017
  17. 6/7 builtin/clean: teach clean -d to skip dirs containing ignored filesSamuel Lijin, May 3, 2017
  18. 2/7 dir: recurse into untracked dirs for ignored filesSamuel Lijin, May 3, 2017
  19. 4/7 dir: hide untracked contents of untracked dirsSamuel Lijin, May 3, 2017
  20. 0/9 Keep git clean -d from inadvertently removing ignored filesSamuel Lijin, May 5, 2017
  21. 1/9 t7300: skip untracked dirs containing ignored filesSamuel Lijin, May 5, 2017
  22. Torsten BögershausenMay 7, 2017
  23. Samuel LijinMay 8, 2017
  24. 2/9 t7061: expect failure where expected behavior will changeSamuel Lijin, May 5, 2017
  25. Junio C HamanoMay 8, 2017
  26. 4/9 dir: add method to check if a dir_entry lexically contains anotherSamuel Lijin, May 5, 2017
  27. 6/9 dir: change linkage of cmp_name() and check_contains()Samuel Lijin, May 5, 2017
  28. Junio C HamanoMay 8, 2017
  29. 3/9 dir: recurse into untracked dirs for ignored filesSamuel Lijin, May 5, 2017
  30. 7/9 builtin/clean: teach clean -d to skip dirs containing ignored filesSamuel Lijin, May 5, 2017
  31. 8/9 t7300: clean -d now skips untracked dirs containing ignored filesSamuel Lijin, May 5, 2017
  32. 5/9 dir: hide untracked contents of untracked dirsSamuel Lijin, May 5, 2017
  33. 9/9 t7061: expect ignored files in untracked dirsSamuel Lijin, May 5, 2017
  34. Junio C HamanoMay 8, 2017
  35. Junio C HamanoMay 8, 2017
  36. Samuel LijinMay 8, 2017
  37. Junio C HamanoMay 9, 2017
  38. 0/8 Fix clean -d and status --ignoredSamuel Lijin, May 16, 2017
  39. 1/8 t7300: clean -d should skip dirs with ignored filesSamuel Lijin, May 16, 2017
  40. 3/8 dir: recurse into untracked dirs for ignored filesSamuel Lijin, May 16, 2017
  41. Junio C HamanoMay 17, 2017
  42. Samuel LijinMay 17, 2017
  43. 2/8 t7061: status --ignored should search untracked dirsSamuel Lijin, May 16, 2017
  44. 4/8 dir: hide untracked contents of untracked dirsSamuel Lijin, May 16, 2017
  45. Junio C HamanoMay 17, 2017
  46. Samuel LijinMay 17, 2017
  47. Junio C HamanoMay 17, 2017
  48. 5/8 dir: expose cmp_name() and check_contains()Samuel Lijin, May 16, 2017
  49. 6/8 clean: teach clean -d to skip dirs containing ignored filesSamuel Lijin, May 16, 2017
  50. Junio C HamanoMay 18, 2017
  51. Samuel LijinMay 18, 2017
  52. 7/8 t7300: clean -d now skips untracked dirs containing ignored filesSamuel Lijin, May 16, 2017
  53. Junio C HamanoMay 18, 2017
  54. 8/8 t7061: status --ignored now searches untracked dirsSamuel Lijin, May 16, 2017
  55. Junio C HamanoMay 18, 2017
  56. 0/6 Fix clean -d and status --ignoredSamuel Lijin, May 18, 2017
  57. 1/6 t7300: clean -d should skip dirs with ignored filesSamuel Lijin, May 18, 2017
  58. 5/6 dir: expose cmp_name() and check_contains()Samuel Lijin, May 18, 2017
  59. Junio C HamanoMay 22, 2017
  60. 6/6 clean: teach clean -d to skip dirs containing ignored filesSamuel Lijin, May 18, 2017
  61. Junio C HamanoMay 22, 2017
  62. Samuel LijinMay 22, 2017
  63. Junio C HamanoMay 22, 2017
  64. Samuel LijinMay 23, 2017
  65. Junio C HamanoMay 23, 2017
  66. 3/6 dir: recurse into untracked dirs for ignored filesSamuel Lijin, May 18, 2017
  67. 2/6 t7061: status --ignored should search untracked dirsSamuel Lijin, May 18, 2017
  68. 4/6 dir: hide untracked contents of untracked dirsSamuel Lijin, May 18, 2017
  69. Junio C HamanoMay 22, 2017
  70. 0/6 Fix clean -d and status --ignoredSamuel Lijin, May 23, 2017
  71. 1/6 t7300: clean -d should skip dirs with ignored filesSamuel Lijin, May 23, 2017
  72. 2/6 t7061: status --ignored should search untracked dirsSamuel Lijin, May 23, 2017
  73. 3/6 dir: recurse into untracked dirs for ignored filesSamuel Lijin, May 23, 2017
  74. 5/6 dir: expose cmp_name() and check_contains()Samuel Lijin, May 23, 2017
  75. 4/6 dir: hide untracked contents of untracked dirsSamuel Lijin, May 23, 2017
  76. 6/6 clean: teach clean -d to preserve ignored pathsSamuel Lijin, May 23, 2017
  77. Junio C HamanoMay 23, 2017
  78. Samuel LijinMay 23, 2017
  79. 0/6 Fix clean -d and status --ignoredSamuel Lijin, May 23, 2017
  80. 1/6 t7300: clean -d should skip dirs with ignored filesSamuel Lijin, May 23, 2017
  81. 2/6 t7061: status --ignored should search untracked dirsSamuel Lijin, May 23, 2017
  82. 3/6 dir: recurse into untracked dirs for ignored filesSamuel Lijin, May 23, 2017
  83. 5/6 dir: expose cmp_name() and check_contains()Samuel Lijin, May 23, 2017
  84. 4/6 dir: hide untracked contents of untracked dirsSamuel Lijin, May 23, 2017
  85. 6/6 clean: teach clean -d to preserve ignored pathsSamuel Lijin, May 23, 2017
  86. Junio C HamanoMay 23, 2017
  87. Torsten BögershausenMay 24, 2017
  88. Samuel LijinMay 25, 2017
  89. Junio C HamanoMay 26, 2017

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.