dir.c: Make git-status --ignored more consistent

The current behavior of git-status is inconsistent and misleading. Especially when used with --untracked-files=all option: - files ignored in untracked directories will be missing from status output. - untracked files in committed yet ignored directories are also missing. - with --untracked-files=normal, untracked directories that contains only ignored files are dropped too. Make the behavior more consistent across all possible use cases: - "--ignored --untracked-files=normal" doesn't show each specific files but top directory. It instead shows untracked directories that only contains ignored files, and ignored tracked directories with untracked files. - "--ignored --untracked-files=all" shows all ignored files, either because it's in an ignored directory (tracked or untracked), or because the file is explicitly ignored. Signed-off-by: Antoine Pelisse <apelisse@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Antoine Pelisse committed Dec 30, 2012 at 15:39 UTC 721ac4edde75723ef8dbbb68989bda89fbf6fc66
2 files changed +81 -21
dir.c
+78 -20
@@ -774,8 +774,9 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
774 * traversal routine.
775 *
776 * Case 1: If we *already* have entries in the index under that
777 - * directory name, we always recurse into the directory to see
778 - * all the files.
777 + * directory name, we recurse into the directory to see all the files,
778 + * unless the directory is excluded and we want to show ignored
779 + * directories
780 *
781 * Case 2: If we *already* have that directory name as a gitlink,
782 * we always continue to see it as a gitlink, regardless of whether
@@ -789,6 +790,9 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
790 * just a directory, unless "hide_empty_directories" is
791 * also true and the directory is empty, in which case
792 * we just ignore it entirely.
793 + * if we are looking for ignored directories, look if it
794 + * contains only ignored files to decide if it must be shown as
795 + * ignored or not.
796 * (b) if it looks like a git directory, and we don't have
797 * 'no_gitlinks' set we treat it as a gitlink, and show it
798 * as a directory.
@@ -801,12 +805,15 @@ enum directory_treatment {
805 };
806
807 static enum directory_treatment treat_directory(struct dir_struct *dir,
804 - const char *dirname, int len,
808 + const char *dirname, int len, int exclude,
809 const struct path_simplify *simplify)
810 {
811 /* The "len-1" is to strip the final '/' */
812 switch (directory_exists_in_index(dirname, len-1)) {
813 case index_directory:
814 + if ((dir->flags & DIR_SHOW_OTHER_DIRECTORIES) && exclude)
815 + break;
816 +
817 return recurse_into_directory;
818
819 case index_gitdir:
@@ -826,13 +833,72 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
833 }
834
835 /* This is the "show_other_directories" case */
829 - if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
836 +
837 + /*
838 + * We are looking for ignored files and our directory is not ignored,
839 + * check if it contains only ignored files
840 + */
841 + if ((dir->flags & DIR_SHOW_IGNORED) && !exclude) {
842 + int ignored;
843 + dir->flags &= ~DIR_SHOW_IGNORED;
844 + dir->flags |= DIR_HIDE_EMPTY_DIRECTORIES;
845 + ignored = read_directory_recursive(dir, dirname, len, 1, simplify);
846 + dir->flags &= ~DIR_HIDE_EMPTY_DIRECTORIES;
847 + dir->flags |= DIR_SHOW_IGNORED;
848 +
849 + return ignored ? ignore_directory : show_directory;
850 + }
851 + if (!(dir->flags & DIR_SHOW_IGNORED) &&
852 + !(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
853 return show_directory;
854 if (!read_directory_recursive(dir, dirname, len, 1, simplify))
855 return ignore_directory;
856 return show_directory;
857 }
858
859 +/*
860 + * Decide what to do when we find a file while traversing the
861 + * filesystem. Mostly two cases:
862 + *
863 + * 1. We are looking for ignored files
864 + * (a) File is ignored, include it
865 + * (b) File is in ignored path, include it
866 + * (c) File is not ignored, exclude it
867 + *
868 + * 2. Other scenarios, include the file if not excluded
869 + *
870 + * Return 1 for exclude, 0 for include.
871 + */
872 +static int treat_file(struct dir_struct *dir, struct strbuf *path, int exclude, int *dtype)
873 +{
874 + struct path_exclude_check check;
875 + int exclude_file = 0;
876 +
877 + if (exclude)
878 + exclude_file = !(dir->flags & DIR_SHOW_IGNORED);
879 + else if (dir->flags & DIR_SHOW_IGNORED) {
880 + /*
881 + * Optimization:
882 + * Don't spend time on indexed files, they won't be
883 + * added to the list anyway
884 + */
885 + struct cache_entry *ce = index_name_exists(&the_index,
886 + path->buf, path->len, ignore_case);
887 +
888 + if (ce)
889 + return 1;
890 +
891 + path_exclude_check_init(&check, dir);
892 +
893 + if (!path_excluded(&check, path->buf, path->len, dtype))
894 + exclude_file = 1;
895 +
896 + path_exclude_check_clear(&check);
897 + }
898 +
899 + return exclude_file;
900 +}
901 +
902 /*
903 * This is an inexact early pruning of any recursive directory
904 * reading - if the path cannot possibly be in the pathspec,
@@ -971,27 +1037,14 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
1037 if (dtype == DT_UNKNOWN)
1038 dtype = get_dtype(de, path->buf, path->len);
1039
974 - /*
975 - * Do we want to see just the ignored files?
976 - * We still need to recurse into directories,
977 - * even if we don't ignore them, since the
978 - * directory may contain files that we do..
979 - */
980 - if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
981 - if (dtype != DT_DIR)
982 - return path_ignored;
983 - }
984 -
1040 switch (dtype) {
1041 default:
1042 return path_ignored;
1043 case DT_DIR:
1044 strbuf_addch(path, '/');
990 - switch (treat_directory(dir, path->buf, path->len, simplify)) {
1045 +
1046 + switch (treat_directory(dir, path->buf, path->len, exclude, simplify)) {
1047 case show_directory:
992 - if (exclude != !!(dir->flags
993 - & DIR_SHOW_IGNORED))
994 - return path_ignored;
1048 break;
1049 case recurse_into_directory:
1050 return path_recurse;
@@ -1001,7 +1054,12 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
1054 break;
1055 case DT_REG:
1056 case DT_LNK:
1004 - break;
1057 + switch (treat_file(dir, path, exclude, &dtype)) {
1058 + case 1:
1059 + return path_ignored;
1060 + default:
1061 + break;
1062 + }
1063 }
1064 return path_handled;
1065 }
wt-status.c
+3 -1
@@ -516,7 +516,9 @@ static void wt_status_collect_untracked(struct wt_status *s)
516
517 if (s->show_ignored_files) {
518 dir.nr = 0;
519 - dir.flags = DIR_SHOW_IGNORED | DIR_SHOW_OTHER_DIRECTORIES;
519 + dir.flags = DIR_SHOW_IGNORED;
520 + if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
521 + dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
522 fill_directory(&dir, s->pathspec);
523 for (i = 0; i < dir.nr; i++) {
524 struct dir_entry *ent = dir.entries[i];