Subject: bug#42501: feature request: exclude-vcs option

From: Collin Funk <[email-masked]>
Date: Sat, 06 Jul 2024 02:10:11 -0700

I would like this feature too. I've attached a patch that I think should
work and then a very basic test case.

Also the following files should be added from GNU tar:

  /* Arch.  */
  ".arch-ids",
  "{arch}",
  "=RELEASE-ID",
  "=meta-update",
  "=update",
  /* Bazaar.  */
  ".bzr",
  ".bzrignore",
  ".bzrtags",

But I haven't used those version control systems so I am unsure which
ones are directories or not.

Thanks,
Collin

>From 9462055c1692934c02d39c9962268094b3e03113 Mon Sep 17 00:00:00 2001
From: Collin Funk <[email-masked]>
Date: Sat, 6 Jul 2024 01:36:49 -0700
Subject: [PATCH 1/2] grep: add new --exclude-vcs option

* src/grep.c (struct vcs_file): New structure.
(vcs_file_table): New variable.
(EXCLUDE_VCS_OPTION, long_options, main): Add the --exclude-vcs
option.
* src/system.h (ARRAY_CARDINALITY): New definition.
---
 src/grep.c   | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/system.h |  4 ++++
 2 files changed, 56 insertions(+)

diff --git a/src/grep.c b/src/grep.c
index 669238b..4c24e55 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -479,6 +479,36 @@ fflush_errno (void)
     stdout_errno = errno;
 }
 
+struct vcs_file
+{
+  const char *name;
+  bool directory;
+};
+
+static struct vcs_file const vcs_file_table[] =
+{
+  /* CVS.  */
+  {"CVS", true},
+  {".cvsignore", false},
+  /* RCS.  */
+  {"RCS", true},
+  /* SCCS.  */
+  {"SCCS", true},
+  /* Subversion.  */
+  {".svn", true},
+  /* Git.  */
+  {".git", true},
+  {".gitignore", false},
+  {".gitattributes", false},
+  {".gitmodules", false},
+  /* Mercurial.  */
+  {".hg", true},
+  {".hgignore", false},
+  {".hgtags", false},
+  /* Darcs.  */
+  {"_darcs", true},
+};
+
 static struct exclude *excluded_patterns[2];
 static struct exclude *excluded_directory_patterns[2];
 /* Short options.  */
@@ -493,6 +523,7 @@ enum
   EXCLUDE_DIRECTORY_OPTION,
   EXCLUDE_OPTION,
   EXCLUDE_FROM_OPTION,
+  EXCLUDE_VCS_OPTION,
   GROUP_SEPARATOR_OPTION,
   INCLUDE_OPTION,
   LINE_BUFFERED_OPTION,
@@ -521,6 +552,7 @@ static struct option const long_options[] =
   {"exclude", required_argument, nullptr, EXCLUDE_OPTION},
   {"exclude-from", required_argument, nullptr, EXCLUDE_FROM_OPTION},
   {"exclude-dir", required_argument, nullptr, EXCLUDE_DIRECTORY_OPTION},
+  {"exclude-vcs", no_argument, nullptr, EXCLUDE_VCS_OPTION},
   {"file", required_argument, nullptr, 'f'},
   {"files-with-matches", no_argument, nullptr, 'l'},
   {"files-without-match", no_argument, nullptr, 'L'},
@@ -2806,6 +2838,26 @@ main (int argc, char **argv)
           }
         break;
 
+      case EXCLUDE_VCS_OPTION:
+        for (int cmd = 0; cmd < 2; ++cmd)
+          {
+            if (!excluded_patterns[cmd])
+              excluded_patterns[cmd] = new_exclude ();
+            if (!excluded_directory_patterns[cmd])
+              excluded_directory_patterns[cmd] = new_exclude ();
+            for (idx_t i = 0; i < ARRAY_CARDINALITY (vcs_file_table); ++i)
+              {
+                struct vcs_file file = vcs_file_table[i];
+                if (file.directory)
+                  add_exclude (excluded_directory_patterns[cmd], file.name,
+                               exclude_options (cmd));
+                else
+                  add_exclude (excluded_patterns[cmd], file.name,
+                               exclude_options (cmd));
+              }
+          }
+        break;
+
       case GROUP_SEPARATOR_OPTION:
         group_separator = optarg;
         break;
diff --git a/src/system.h b/src/system.h
index 2a6611e..2266b96 100644
--- a/src/system.h
+++ b/src/system.h
@@ -129,4 +129,8 @@ __asan_unpoison_memory_region (void const volatile *addr, size_t size) { }
 # define IGNORE_DUPLICATE_BRANCH_WARNING(exp) exp
 #endif
 
+#ifndef ARRAY_CARDINALITY
+# define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
+#endif
+
 #endif
-- 
2.45.2

>From 94caab87b280b56fb84ac6a495a12616db3124d4 Mon Sep 17 00:00:00 2001
From: Collin Funk <[email-masked]>
Date: Sat, 6 Jul 2024 02:03:54 -0700
Subject: [PATCH 2/2] tests: add test for --exclude-vcs

* tests/exclude-vcs: New file.
* tests/Makefile.am (TESTS): Add exclude-vcs.
---
 tests/Makefile.am |  1 +
 tests/exclude-vcs | 41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)
 create mode 100755 tests/exclude-vcs

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9b647c4..3ea34c0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -109,6 +109,7 @@ TESTS =						\
   equiv-classes					\
   ere						\
   euc-mb					\
+  exclude-vcs					\
   false-match-mb-non-utf8			\
   fedora					\
   fgrep-infloop					\
diff --git a/tests/exclude-vcs b/tests/exclude-vcs
new file mode 100755
index 0000000..8b00533
--- /dev/null
+++ b/tests/exclude-vcs
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Check --exclude-vcs works as expected.
+
+# Copyright (C) 2024 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+fail=0
+
+# Create VCS directories in the current directory and a child directory.
+for directory in CVS RCS .svn .git .hg _darcs; do
+  mkdir -p "$directory" || framework_failure_
+  mkdir -p "x/$directory" || framework_failure_
+  echo "abc" > "$directory/file" || framework_failure_
+  echo "abc" > "x/$directory/file" || framework_failure_
+done
+
+grep -r --exclude-vcs 'abc' && fail=1
+
+# Create VCS files in the current directory and a child directory.
+for file in .gitignore .cvsignore .hgignore; do
+  echo "abc" > "$file" || framework_failure_
+  echo "abc" > "x/$file" || framework_failure_
+done
+
+grep -r --exclude-vcs 'abc' && fail=1
+
+Exit $fail
-- 
2.45.2