Subject: bug#29139: add --include for recursive searches

From: Kevin Hunter Kesling <[email-masked]>
Date: Fri, 3 Nov 2017 16:13:57 -0400

Hello List,

I've been annoyed and confused when I'm on autopilot and attempting to 
recursively compare directory trees.  I've become accustomed to Grep's 
--include functionality, and have more than a few times been surprised 
(to the tune of "What? Unrecognized option??") that diff does not have 
it.  Well, with the attached patch, it now does.

Thanks to diff and grep both using gnulib, and the expressiveness of 
add_exclude(), this was a much easier change that I was expecting when I 
embarked on this a couple of hours ago.

Further, in the past hour since implementing it, this patch has already 
saved me a ton of time (no thanks to an unfortunately slow VPN setup and 
other beleaguered business requirements).

Use is exactly like with grep.  For example:

     $ diff --recursive --brief -w --include="*.c" --include="*.h"  \
         dir1/   \
         dir2/

Would this be of use to the community?

Thanks,

Kevin
>From 9b5ff988f8c49a179ee64dbb075cb289b0c1868c Mon Sep 17 00:00:00 2001
From: Kevin Hunter Kesling <[email-masked]>
Date: Fri, 3 Nov 2017 11:36:03 -0400
Subject: [PATCH] diff: Add include command line option

Referencing a number of requests, both public[1] and internal, this
patch now enables --include, in a similar vein to grep's --include.
Heavy thanks to grep's sourcecode for having already solved this wheel.

[1] "Only include files that match a given pattern in a recursive diff"
https://stackoverflow.com/questions/10131908/
---
 NEWS                  |  3 +++
 doc/diffutils.texi    |  6 ++++++
 src/diff.c            | 14 +++++++++++---
 tests/Makefile.am     |  3 ++-
 tests/include-exclude | 23 +++++++++++++++++++++++
 5 files changed, 45 insertions(+), 4 deletions(-)
 create mode 100755 tests/include-exclude

diff --git a/NEWS b/NEWS
index 949136c..89afd91 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@ GNU diffutils NEWS                                    -*- outline -*-
 
 * Noteworthy changes in release ?.? (????-??-??) [?]
 
+** New features
+
+ With --include, diff now can include file patterns, like grep.
 
 * Noteworthy changes in release 3.6 (2017-05-21) [stable]
 
diff --git a/doc/diffutils.texi b/doc/diffutils.texi
index 8479b3f..5bca8ee 100644
--- a/doc/diffutils.texi
+++ b/doc/diffutils.texi
@@ -3842,6 +3842,12 @@ comparison of @file{d} to @file{e} might compare the contents of
 might compare the contents of @file{d/Init} and @file{inIt}.
 @xref{Comparing Directories}.
 
+@item --include=@var{glob}
+@cindex include files
+@cindex searching directory trees
+When comparing directories, only compare files whose name matches @var{glob}.
+@xref{Comparing Directories}.
+
 @item -l
 @itemx --paginate
 Pass the output through @command{pr} to paginate it.  @xref{Pagination}.
diff --git a/src/diff.c b/src/diff.c
index df3338c..8252264 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -144,6 +144,7 @@ enum
   COLOR_PALETTE_OPTION,
 
   PRESUME_OUTPUT_TTY_OPTION,
+  INCLUDE_OPTION,
 };
 
 static char const group_format_option[][sizeof "--unchanged-group-format"] =
@@ -185,6 +186,7 @@ static struct option const longopts[] =
   {"ignore-space-change", 0, 0, 'b'},
   {"ignore-tab-expansion", 0, 0, 'E'},
   {"ignore-trailing-space", 0, 0, 'Z'},
+  {"include", 1, 0, INCLUDE_OPTION},
   {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
   {"initial-tab", 0, 0, 'T'},
   {"label", 1, 0, 'L'},
@@ -648,9 +650,14 @@ main (int argc, char **argv)
 	  set_color_palette (optarg);
 	  break;
 
-        case PRESUME_OUTPUT_TTY_OPTION:
-          presume_output_tty = true;
-          break;
+	case PRESUME_OUTPUT_TTY_OPTION:
+	  presume_output_tty = true;
+	  break;
+
+	case INCLUDE_OPTION:
+	  add_exclude (excluded, optarg, 0);
+	  add_exclude (excluded, optarg, EXCLUDE_WILDCARDS | EXCLUDE_INCLUDE | EXCLUDE_ANCHORED);
+	  break;
 
 	default:
 	  try_help (NULL, NULL);
@@ -919,6 +926,7 @@ static char const * const option_help_msgid[] = {
   N_("    --no-ignore-file-name-case  consider case when comparing file names"),
   N_("-x, --exclude=PAT               exclude files that match PAT"),
   N_("-X, --exclude-from=FILE         exclude files that match any pattern in FILE"),
+  N_("    --include=PAT               include files that match PAT"),
   N_("-S, --starting-file=FILE        start with FILE when comparing directories"),
   N_("    --from-file=FILE1           compare FILE1 to all operands;\n"
      "                                  FILE1 can be a directory"),
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6668863..ef236c7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -13,7 +13,8 @@ TESTS = \
   invalid-re	\
   function-line-vs-leading-space \
   ignore-matching-lines \
-  label-vs-func	\
+  include-exclude       \
+  label-vs-func         \
   new-file \
   no-dereference \
   no-newline-at-eof \
diff --git a/tests/include-exclude b/tests/include-exclude
new file mode 100755
index 0000000..260d552
--- /dev/null
+++ b/tests/include-exclude
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Use of any --include or --exclude* option would segfault in 2.6 and 2.6.1
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+mkdir -p x1/dir x2/dir || framework_failure_
+echo aaa > x1/a     || framework_failure_
+echo bbb > x1/b     || framework_failure_
+echo ddd > x1/dir/d || framework_failure_
+echo aaa > x2/a     || framework_failure_
+echo bbb > x2/c     || framework_failure_
+echo ddd > x2/dir/d || framework_failure_
+
+printf '%s\n' "Only in x1: b" "Only in x2: c" > only-in-b-c   || framework_failure_
+
+diff -r --exclude='a*' x1 x2 > out
+sort -o out out || framework_failure_
+compare only-in-b-c out > /tmp/t
+
+diff -r --include='a*' x1 x2 > out
+sort -o out out || framework_failure_
+compare only-in-b-c out || fail=1
+
+Exit $fail
-- 
2.7.4