Skip to content

Commit 6b1db43

Browse files
Samuel Lijingitster
authored andcommitted
clean: teach clean -d to preserve ignored paths
There is an implicit assumption that a directory containing only untracked and ignored paths should itself be considered untracked. This makes sense in use cases where we're asking if a directory should be added to the git database, but not when we're asking if a directory can be safely removed from the working tree; as a result, clean -d would assume that an "untracked" directory containing ignored paths could be deleted, even though doing so would also remove the ignored paths. To get around this, we teach clean -d to collect ignored paths and skip an untracked directory if it contained an ignored path, instead just removing the untracked contents thereof. To achieve this, cmd_clean() has to collect all untracked contents of untracked directories, in addition to all ignored paths, to determine which untracked dirs must be skipped (because they contain ignored paths) and which ones should *not* be skipped. For this purpose, correct_untracked_entries() is introduced to prune a given dir_struct of untracked entries containing ignored paths and those untracked entries encompassed by the untracked entries which are not pruned away. A memory leak is also fixed in cmd_clean(). This also fixes the known breakage in t7300, since clean -d now skips untracked directories containing ignored paths. Signed-off-by: Samuel Lijin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent bbf504a commit 6b1db43

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

builtin/clean.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,38 @@ static void interactive_main_loop(void)
851851
}
852852
}
853853

854+
static void correct_untracked_entries(struct dir_struct *dir)
855+
{
856+
int src, dst, ign;
857+
858+
for (src = dst = ign = 0; src < dir->nr; src++) {
859+
/* skip paths in ignored[] that cannot be inside entries[src] */
860+
while (ign < dir->ignored_nr &&
861+
0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign]))
862+
ign++;
863+
864+
if (ign < dir->ignored_nr &&
865+
check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) {
866+
/* entries[src] contains an ignored path, so we drop it */
867+
free(dir->entries[src]);
868+
} else {
869+
struct dir_entry *ent = dir->entries[src++];
870+
871+
/* entries[src] does not contain an ignored path, so we keep it */
872+
dir->entries[dst++] = ent;
873+
874+
/* then discard paths in entries[] contained inside entries[src] */
875+
while (src < dir->nr &&
876+
check_dir_entry_contains(ent, dir->entries[src]))
877+
free(dir->entries[src++]);
878+
879+
/* compensate for the outer loop's loop control */
880+
src--;
881+
}
882+
}
883+
dir->nr = dst;
884+
}
885+
854886
int cmd_clean(int argc, const char **argv, const char *prefix)
855887
{
856888
int i, res;
@@ -910,6 +942,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
910942

911943
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
912944

945+
if (remove_directories)
946+
dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
947+
913948
if (read_cache() < 0)
914949
die(_("index file corrupt"));
915950

@@ -925,6 +960,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
925960
prefix, argv);
926961

927962
fill_directory(&dir, &pathspec);
963+
correct_untracked_entries(&dir);
928964

929965
for (i = 0; i < dir.nr; i++) {
930966
struct dir_entry *ent = dir.entries[i];
@@ -952,6 +988,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
952988
string_list_append(&del_list, rel);
953989
}
954990

991+
for (i = 0; i < dir.nr; i++)
992+
free(dir.entries[i]);
993+
994+
for (i = 0; i < dir.ignored_nr; i++)
995+
free(dir.ignored[i]);
996+
955997
if (interactive && del_list.nr > 0)
956998
interactive_main_loop();
957999

t/t7300-clean.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ test_expect_success 'git clean -d respects pathspecs (pathspec is prefix of dir)
653653
test_path_is_dir foobar
654654
'
655655

656-
test_expect_failure 'git clean -d skips untracked dirs containing ignored files' '
656+
test_expect_success 'git clean -d skips untracked dirs containing ignored files' '
657657
echo /foo/bar >.gitignore &&
658658
echo ignoreme >>.gitignore &&
659659
rm -rf foo &&

0 commit comments

Comments
 (0)