Skip to content

Commit 487f3e8

Browse files
committed
Add --status and --empty-files-as-absent support to scanner-based grepdiff
Implement the -s/--status option for grepdiff in the scanner-based implementation (when --enable-scanner-patchfilter is used). This option displays file status indicators: + for file additions - for file deletions ! for file modifications Also implement --empty-files-as-absent option which works with -s to treat files with empty content as absent, affecting status determination. For example, a file with only additions in the first hunk becomes a new file (+) instead of a modification (!). Key implementation details: - Added show_status and empty_files_as_absent global flags - Extended buffered_file structure to track initial_status, orig_is_empty, and new_is_empty - Empty file detection examines only the first hunk (orig_count == 0 or new_count == 0 indicates an empty file) - Status calculation uses determine_file_status() from headers, then adjusts based on empty file logic if --empty-files-as-absent is set - Output uses display_filename_extended() to show status prefix This change allows tests/grepdiff-status/run-test to pass, so it has been removed from XFAIL_TESTS in Makefile.am. Tested with all 6 test cases in grepdiff-status, covering unified diffs, context diffs, and --empty-files-as-absent edge cases. Assisted-by: Claude Code
1 parent b34e833 commit 487f3e8

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

Makefile.am

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,6 @@ endif
463463
# (features not yet implemented in scanner-based grepdiff)
464464
if USE_SCANNER_PATCHFILTER
465465
XFAIL_TESTS += \
466-
tests/grepdiff-status/run-test \
467466
tests/grepdiff-annotate/run-test
468467
endif
469468

src/grep.c

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ static enum output_mode output_mode = OUTPUT_LIST;
6868
static enum match_filter match_filter = MATCH_ALL;
6969
static enum numbered_mode numbered_mode = NUMBERED_NONE;
7070
static int extended_regexp = 0; /* -E, --extended-regexp */
71+
static int show_status = 0; /* -s, --status */
72+
static int empty_files_as_absent = 0; /* --empty-files-as-absent */
7173

7274
/* Grep patterns */
7375
static regex_t *grep_patterns = NULL;
@@ -109,6 +111,9 @@ struct buffered_file {
109111
int max_hunks;
110112
int has_match; /* Does this file have any matching hunks? */
111113
int is_context_diff;
114+
char initial_status; /* Initial file status from headers (+, -, !) */
115+
int orig_is_empty; /* Is original file empty (from first hunk)? */
116+
int new_is_empty; /* Is new file empty (from first hunk)? */
112117
};
113118

114119
/* Forward declarations */
@@ -134,6 +139,7 @@ static void syntax(int err)
134139
fprintf(f, "Usage: %s [OPTION]... PATTERN [FILE]...\n", "grepdiff");
135140
fprintf(f, "Show files modified by patches containing a regexp.\n\n");
136141
fprintf(f, "Options:\n");
142+
fprintf(f, " -s, --status show file additions (+), removals (-), and modifications (!)\n");
137143
fprintf(f, " -n, --line-number show line numbers\n");
138144
fprintf(f, " -N, --number-files show file numbers (for use with filterdiff --files)\n");
139145
fprintf(f, " -H, --with-filename show patch file names\n");
@@ -156,6 +162,7 @@ static void syntax(int err)
156162
fprintf(f, " (PCRE regexes are used by default)\n");
157163
#endif
158164
fprintf(f, " -f FILE, --file=FILE read regular expressions from FILE\n");
165+
fprintf(f, " --empty-files-as-absent treat empty files as absent (with -s)\n");
159166
fprintf(f, " --help display this help and exit\n");
160167
fprintf(f, " --version output version information and exit\n");
161168
fprintf(f, "\nReport bugs to <[email protected]>.\n");
@@ -407,6 +414,14 @@ static void process_patch_file(FILE *fp, const char *filename)
407414
current_file.header_line = global_line_offset + content->data.headers->start_line;
408415
current_file.is_context_diff = (content->data.headers->type == PATCH_TYPE_CONTEXT);
409416

417+
/* Determine initial status from headers (for -s/--status) */
418+
if (show_status) {
419+
current_file.initial_status = determine_file_status(content->data.headers, empty_files_as_absent);
420+
/* Initialize empty file tracking - assume empty until we see hunks */
421+
current_file.orig_is_empty = 1;
422+
current_file.new_is_empty = 1;
423+
}
424+
410425
/* Copy header lines for file/hunk output modes */
411426
if (output_mode != OUTPUT_LIST) {
412427
const struct patch_headers *hdrs = content->data.headers;
@@ -442,6 +457,16 @@ static void process_patch_file(FILE *fp, const char *filename)
442457
current_hunk->context = xstrdup(hunk->context);
443458
}
444459

460+
/* Track empty files from first hunk only (for --empty-files-as-absent) */
461+
if (show_status && current_file.num_hunks == 1) {
462+
if (hunk->orig_count > 0) {
463+
current_file.orig_is_empty = 0;
464+
}
465+
if (hunk->new_count > 0) {
466+
current_file.new_is_empty = 0;
467+
}
468+
}
469+
445470
/* Initialize line number tracking */
446471
orig_line = hunk->orig_offset;
447472
new_line = hunk->new_offset;
@@ -542,7 +567,34 @@ static void output_buffered_file(struct buffered_file *file)
542567
/* In list mode, just print filename if it has matches */
543568
if (output_mode == OUTPUT_LIST) {
544569
if (file->has_match) {
545-
display_filename(file->best_filename, file->patchname, file->header_line);
570+
/* Calculate final status for -s/--status */
571+
if (show_status) {
572+
char final_status = file->initial_status;
573+
574+
/* Adjust status based on --empty-files-as-absent */
575+
if (empty_files_as_absent) {
576+
int orig_absent = (file->orig_is_empty != 0);
577+
int new_absent = (file->new_is_empty != 0);
578+
579+
if (orig_absent && !new_absent) {
580+
final_status = '+'; /* Treat as file addition */
581+
} else if (!orig_absent && new_absent) {
582+
final_status = '-'; /* Treat as file deletion */
583+
} else if (!orig_absent && !new_absent) {
584+
final_status = '!'; /* Treat as modification */
585+
}
586+
/* If both absent, skip the file (shouldn't normally happen) */
587+
if (orig_absent && new_absent) {
588+
return;
589+
}
590+
}
591+
592+
/* Display with status prefix */
593+
display_filename_extended(file->best_filename, file->patchname, file->header_line,
594+
final_status, show_status);
595+
} else {
596+
display_filename(file->best_filename, file->patchname, file->header_line);
597+
}
546598

547599
/* In verbose mode with line numbers, show hunk information */
548600
if (verbose > 0 && show_line_numbers) {
@@ -878,11 +930,13 @@ int run_grep_mode(int argc, char *argv[])
878930
/* Add tool-specific long options */
879931
long_options[next_idx++] = (struct option){"help", 0, 0, 1000 + 'H'};
880932
long_options[next_idx++] = (struct option){"version", 0, 0, 1000 + 'V'};
933+
long_options[next_idx++] = (struct option){"status", 0, 0, 's'};
881934
long_options[next_idx++] = (struct option){"extended-regexp", 0, 0, 'E'};
882935
long_options[next_idx++] = (struct option){"file", 1, 0, 'f'};
883936
long_options[next_idx++] = (struct option){"output-matching", 1, 0, 1000 + 'M'};
884937
long_options[next_idx++] = (struct option){"only-match", 1, 0, 1000 + 'm'};
885938
long_options[next_idx++] = (struct option){"as-numbered-lines", 1, 0, 1000 + 'L'};
939+
long_options[next_idx++] = (struct option){"empty-files-as-absent", 0, 0, 1000 + 'e'};
886940
/* Mode options (handled by patchfilter, but need to be recognized) */
887941
long_options[next_idx++] = (struct option){"list", 0, 0, 1000 + 'l'};
888942
long_options[next_idx++] = (struct option){"filter", 0, 0, 1000 + 'F'};
@@ -897,7 +951,7 @@ int run_grep_mode(int argc, char *argv[])
897951

898952
/* Combine common and tool-specific short options */
899953
char short_options[64];
900-
snprintf(short_options, sizeof(short_options), "%sEf:", get_common_short_options());
954+
snprintf(short_options, sizeof(short_options), "%ssEf:", get_common_short_options());
901955

902956
int c = getopt_long(argc, argv, short_options, long_options, NULL);
903957
if (c == -1)
@@ -916,12 +970,18 @@ int run_grep_mode(int argc, char *argv[])
916970
case 1000 + 'V':
917971
printf("grepdiff - patchutils version %s\n", VERSION);
918972
exit(0);
973+
case 's':
974+
show_status = 1;
975+
break;
919976
case 'E':
920977
extended_regexp = 1;
921978
break;
922979
case 'f':
923980
add_patterns_from_file(optarg);
924981
break;
982+
case 1000 + 'e':
983+
empty_files_as_absent = 1;
984+
break;
925985
case 1000 + 'M':
926986
if (!strncmp(optarg, "file", 4)) {
927987
output_mode = OUTPUT_FILE;

0 commit comments

Comments
 (0)