Coverage Report

Created: 2024-09-08 06:23

/src/git/builtin/check-ignore.c
Line
Count
Source (jump to first uncovered line)
1
#include "builtin.h"
2
#include "config.h"
3
#include "dir.h"
4
#include "gettext.h"
5
#include "quote.h"
6
#include "pathspec.h"
7
#include "parse-options.h"
8
#include "repository.h"
9
#include "submodule.h"
10
#include "write-or-die.h"
11
12
static int quiet, verbose, stdin_paths, show_non_matching, no_index;
13
static const char * const check_ignore_usage[] = {
14
"git check-ignore [<options>] <pathname>...",
15
"git check-ignore [<options>] --stdin",
16
NULL
17
};
18
19
static int nul_term_line;
20
21
static const struct option check_ignore_options[] = {
22
  OPT__QUIET(&quiet, N_("suppress progress reporting")),
23
  OPT__VERBOSE(&verbose, N_("be verbose")),
24
  OPT_GROUP(""),
25
  OPT_BOOL(0, "stdin", &stdin_paths,
26
     N_("read file names from stdin")),
27
  OPT_BOOL('z', NULL, &nul_term_line,
28
     N_("terminate input and output records by a NUL character")),
29
  OPT_BOOL('n', "non-matching", &show_non_matching,
30
     N_("show non-matching input paths")),
31
  OPT_BOOL(0, "no-index", &no_index,
32
     N_("ignore index when checking")),
33
  OPT_END()
34
};
35
36
static void output_pattern(const char *path, struct path_pattern *pattern)
37
0
{
38
0
  const char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
39
0
  const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
40
0
  if (!nul_term_line) {
41
0
    if (!verbose) {
42
0
      write_name_quoted(path, stdout, '\n');
43
0
    } else {
44
0
      if (pattern) {
45
0
        quote_c_style(pattern->pl->src, NULL, stdout, 0);
46
0
        printf(":%d:%s%s%s\t",
47
0
               pattern->srcpos,
48
0
               bang, pattern->pattern, slash);
49
0
      }
50
0
      else {
51
0
        printf("::\t");
52
0
      }
53
0
      quote_c_style(path, NULL, stdout, 0);
54
0
      fputc('\n', stdout);
55
0
    }
56
0
  } else {
57
0
    if (!verbose) {
58
0
      printf("%s%c", path, '\0');
59
0
    } else {
60
0
      if (pattern)
61
0
        printf("%s%c%d%c%s%s%s%c%s%c",
62
0
               pattern->pl->src, '\0',
63
0
               pattern->srcpos, '\0',
64
0
               bang, pattern->pattern, slash, '\0',
65
0
               path, '\0');
66
0
      else
67
0
        printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0');
68
0
    }
69
0
  }
70
0
}
71
72
static int check_ignore(struct dir_struct *dir,
73
      const char *prefix, int argc, const char **argv)
74
0
{
75
0
  const char *full_path;
76
0
  char *seen;
77
0
  int num_ignored = 0, i;
78
0
  struct path_pattern *pattern;
79
0
  struct pathspec pathspec;
80
81
0
  if (!argc) {
82
0
    if (!quiet)
83
0
      fprintf(stderr, "no pathspec given.\n");
84
0
    return 0;
85
0
  }
86
87
  /*
88
   * check-ignore just needs paths. Magic beyond :/ is really
89
   * irrelevant.
90
   */
91
0
  parse_pathspec(&pathspec,
92
0
           PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
93
0
           PATHSPEC_SYMLINK_LEADING_PATH |
94
0
           PATHSPEC_KEEP_ORDER,
95
0
           prefix, argv);
96
97
0
  die_path_inside_submodule(the_repository->index, &pathspec);
98
99
  /*
100
   * look for pathspecs matching entries in the index, since these
101
   * should not be ignored, in order to be consistent with
102
   * 'git status', 'git add' etc.
103
   */
104
0
  seen = find_pathspecs_matching_against_index(&pathspec, the_repository->index,
105
0
                 PS_HEED_SKIP_WORKTREE);
106
0
  for (i = 0; i < pathspec.nr; i++) {
107
0
    full_path = pathspec.items[i].match;
108
0
    pattern = NULL;
109
0
    if (!seen[i]) {
110
0
      int dtype = DT_UNKNOWN;
111
0
      pattern = last_matching_pattern(dir, the_repository->index,
112
0
              full_path, &dtype);
113
0
      if (!verbose && pattern &&
114
0
          pattern->flags & PATTERN_FLAG_NEGATIVE)
115
0
        pattern = NULL;
116
0
    }
117
0
    if (!quiet && (pattern || show_non_matching))
118
0
      output_pattern(pathspec.items[i].original, pattern);
119
0
    if (pattern)
120
0
      num_ignored++;
121
0
  }
122
0
  free(seen);
123
0
  clear_pathspec(&pathspec);
124
125
0
  return num_ignored;
126
0
}
127
128
static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
129
0
{
130
0
  struct strbuf buf = STRBUF_INIT;
131
0
  struct strbuf unquoted = STRBUF_INIT;
132
0
  char *pathspec[2] = { NULL, NULL };
133
0
  strbuf_getline_fn getline_fn;
134
0
  int num_ignored = 0;
135
136
0
  getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
137
0
  while (getline_fn(&buf, stdin) != EOF) {
138
0
    if (!nul_term_line && buf.buf[0] == '"') {
139
0
      strbuf_reset(&unquoted);
140
0
      if (unquote_c_style(&unquoted, buf.buf, NULL))
141
0
        die("line is badly quoted");
142
0
      strbuf_swap(&buf, &unquoted);
143
0
    }
144
0
    pathspec[0] = buf.buf;
145
0
    num_ignored += check_ignore(dir, prefix,
146
0
              1, (const char **)pathspec);
147
0
    maybe_flush_or_die(stdout, "check-ignore to stdout");
148
0
  }
149
0
  strbuf_release(&buf);
150
0
  strbuf_release(&unquoted);
151
0
  return num_ignored;
152
0
}
153
154
int cmd_check_ignore(int argc, const char **argv, const char *prefix)
155
0
{
156
0
  int num_ignored;
157
0
  struct dir_struct dir = DIR_INIT;
158
159
0
  git_config(git_default_config, NULL);
160
161
0
  argc = parse_options(argc, argv, prefix, check_ignore_options,
162
0
           check_ignore_usage, 0);
163
164
0
  if (stdin_paths) {
165
0
    if (argc > 0)
166
0
      die(_("cannot specify pathnames with --stdin"));
167
0
  } else {
168
0
    if (nul_term_line)
169
0
      die(_("-z only makes sense with --stdin"));
170
0
    if (argc == 0)
171
0
      die(_("no path specified"));
172
0
  }
173
0
  if (quiet) {
174
0
    if (argc > 1)
175
0
      die(_("--quiet is only valid with a single pathname"));
176
0
    if (verbose)
177
0
      die(_("cannot have both --quiet and --verbose"));
178
0
  }
179
0
  if (show_non_matching && !verbose)
180
0
    die(_("--non-matching is only valid with --verbose"));
181
182
  /* read_cache() is only necessary so we can watch out for submodules. */
183
0
  if (!no_index && repo_read_index(the_repository) < 0)
184
0
    die(_("index file corrupt"));
185
186
0
  setup_standard_excludes(&dir);
187
188
0
  if (stdin_paths) {
189
0
    num_ignored = check_ignore_stdin_paths(&dir, prefix);
190
0
  } else {
191
0
    num_ignored = check_ignore(&dir, prefix, argc, argv);
192
0
    maybe_flush_or_die(stdout, "ignore to stdout");
193
0
  }
194
195
0
  dir_clear(&dir);
196
197
0
  return !num_ignored;
198
0
}