2424import os
2525import re
2626import shlex
27+ import subprocess
2728import sys
2829import textwrap
2930from collections .abc import Iterable , Sequence
@@ -558,6 +559,15 @@ def convert_arg_line_to_args(self, arg_line: str) -> list[str]:
558559 'you\' d give "*.eps,*.txt" to this option.' ,
559560 )
560561
562+ parser .add_argument (
563+ "--use-git-ignore" ,
564+ action = "store_true" ,
565+ default = False ,
566+ dest = "use_git_ignore" ,
567+ help = "respect .gitignore by using 'git ls-files' to identify files to check. "
568+ "Only files tracked by git will be checked." ,
569+ )
570+
561571 parser .add_argument (
562572 "-x" ,
563573 "--exclude-file" ,
@@ -1228,6 +1238,56 @@ def flatten_clean_comma_separated_arguments(
12281238 ]
12291239
12301240
1241+ def get_git_tracked_files (paths : list [str ]) -> list [str ]:
1242+ """Get list of files tracked by git using git ls-files."""
1243+ try :
1244+ # If no paths specified, use current directory
1245+ if not paths :
1246+ paths = ["." ]
1247+
1248+ all_files = []
1249+ for path in paths :
1250+ # If path is a directory, run git ls-files from within it
1251+ if os .path .isdir (path ):
1252+ result = subprocess .run (
1253+ ["git" , "ls-files" ],
1254+ capture_output = True ,
1255+ text = True ,
1256+ check = True ,
1257+ cwd = path ,
1258+ )
1259+ # Prepend the path to each file
1260+ files = [
1261+ os .path .join (path , f )
1262+ for f in result .stdout .strip ().split ("\n " )
1263+ if f
1264+ ]
1265+ all_files .extend (files )
1266+ else :
1267+ # For specific files, check if they're tracked
1268+ # Get the directory and filename
1269+ dirname = os .path .dirname (path ) or "."
1270+ basename = os .path .basename (path )
1271+ result = subprocess .run (
1272+ ["git" , "ls-files" , "--" , basename ],
1273+ capture_output = True ,
1274+ text = True ,
1275+ check = True ,
1276+ cwd = dirname ,
1277+ )
1278+ files = result .stdout .strip ().split ("\n " )
1279+ if files and files [0 ]:
1280+ all_files .append (path )
1281+
1282+ return all_files
1283+ except (subprocess .CalledProcessError , FileNotFoundError ) as e :
1284+ print (
1285+ f"ERROR: Failed to run 'git ls-files': { e } " ,
1286+ file = sys .stderr ,
1287+ )
1288+ return []
1289+
1290+
12311291def _script_main () -> int :
12321292 """Wrap to main() for setuptools."""
12331293 try :
@@ -1411,52 +1471,21 @@ def main(*args: str) -> int:
14111471 )
14121472
14131473 bad_count = 0
1414- for filename in sorted (options .files ):
1415- # ignore hidden files
1416- if is_hidden (filename , options .check_hidden ):
1417- continue
14181474
1419- if os .path .isdir (filename ):
1420- for root , dirs , files in os .walk (filename ):
1421- if glob_match .match (root ): # skip (absolute) directories
1422- dirs .clear ()
1423- continue
1424- if is_hidden (root , options .check_hidden ): # dir itself hidden
1425- continue
1426- for file_ in sorted (files ):
1427- # ignore hidden files in directories
1428- if is_hidden (file_ , options .check_hidden ):
1429- continue
1430- if glob_match .match (file_ ): # skip files
1431- continue
1432- fname = os .path .join (root , file_ )
1433- if glob_match .match (fname ): # skip paths
1434- continue
1435- bad_count += parse_file (
1436- fname ,
1437- colors ,
1438- summary ,
1439- misspellings ,
1440- ignore_words_cased ,
1441- exclude_lines ,
1442- file_opener ,
1443- word_regex ,
1444- ignore_word_regex ,
1445- uri_regex ,
1446- uri_ignore_words ,
1447- context ,
1448- options ,
1449- )
1475+ # Use git ls-files if requested
1476+ if options .use_git_ignore :
1477+ git_files = get_git_tracked_files (options .files )
1478+ if not git_files :
1479+ return EX_OK
14501480
1451- # skip (relative) directories
1452- dirs [:] = [
1453- dir_
1454- for dir_ in dirs
1455- if not glob_match . match ( dir_ )
1456- and not is_hidden (dir_ , options .check_hidden )
1457- ]
1481+ for filename in git_files :
1482+ # Apply skip patterns
1483+ if glob_match . match ( filename ):
1484+ continue
1485+ # Check if file should be processed (hidden file check )
1486+ if is_hidden (filename , options .check_hidden ):
1487+ continue
14581488
1459- elif not glob_match .match (filename ): # skip files
14601489 bad_count += parse_file (
14611490 filename ,
14621491 colors ,
@@ -1472,6 +1501,69 @@ def main(*args: str) -> int:
14721501 context ,
14731502 options ,
14741503 )
1504+ else :
1505+ # Original directory walking behavior
1506+ for filename in sorted (options .files ):
1507+ # ignore hidden files
1508+ if is_hidden (filename , options .check_hidden ):
1509+ continue
1510+
1511+ if os .path .isdir (filename ):
1512+ for root , dirs , files in os .walk (filename ):
1513+ if glob_match .match (root ): # skip (absolute) directories
1514+ dirs .clear ()
1515+ continue
1516+ if is_hidden (root , options .check_hidden ): # dir itself hidden
1517+ continue
1518+ for file_ in sorted (files ):
1519+ # ignore hidden files in directories
1520+ if is_hidden (file_ , options .check_hidden ):
1521+ continue
1522+ if glob_match .match (file_ ): # skip files
1523+ continue
1524+ fname = os .path .join (root , file_ )
1525+ if glob_match .match (fname ): # skip paths
1526+ continue
1527+ bad_count += parse_file (
1528+ fname ,
1529+ colors ,
1530+ summary ,
1531+ misspellings ,
1532+ ignore_words_cased ,
1533+ exclude_lines ,
1534+ file_opener ,
1535+ word_regex ,
1536+ ignore_word_regex ,
1537+ uri_regex ,
1538+ uri_ignore_words ,
1539+ context ,
1540+ options ,
1541+ )
1542+
1543+ # skip (relative) directories
1544+ dirs [:] = [
1545+ dir_
1546+ for dir_ in dirs
1547+ if not glob_match .match (dir_ )
1548+ and not is_hidden (dir_ , options .check_hidden )
1549+ ]
1550+
1551+ elif not glob_match .match (filename ): # skip files
1552+ bad_count += parse_file (
1553+ filename ,
1554+ colors ,
1555+ summary ,
1556+ misspellings ,
1557+ ignore_words_cased ,
1558+ exclude_lines ,
1559+ file_opener ,
1560+ word_regex ,
1561+ ignore_word_regex ,
1562+ uri_regex ,
1563+ uri_ignore_words ,
1564+ context ,
1565+ options ,
1566+ )
14751567
14761568 if summary :
14771569 print ("\n -------8<-------\n SUMMARY:" )
0 commit comments