Skip to content

Commit

Permalink
use gitignore file
Browse files Browse the repository at this point in the history
  • Loading branch information
terryyin committed Feb 3, 2025
1 parent 24645d4 commit b537d12
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 2 deletions.
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ Exclude anything in the tests folder:

lizard mySource/ -x"./tests/*"

Use .gitignore file:

::

lizard mySource/

If there is a .gitignore file in the given path, lizard will automatically use it as an additional filter to exclude files that match the gitignore patterns. This is useful when you want to analyze only the tracked files in your git repository.

Options
~~~~~~~
Expand Down
32 changes: 31 additions & 1 deletion lizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -896,14 +896,43 @@ def md5_hash_file(full_path_name):
def get_all_source_files(paths, exclude_patterns, lans):
'''
Function counts md5 hash for the given file and checks if it isn't a
duplicate using set of hashes for previous files '''
duplicate using set of hashes for previous files.
If a .gitignore file is found in any of the given paths, it will be used
to filter out files that match the gitignore patterns.
'''
hash_set = set()
gitignore_spec = None
base_path = None

def _load_gitignore():
nonlocal gitignore_spec, base_path
try:
import pathspec
for path in paths:
gitignore_path = os.path.join(path, '.gitignore')
if os.path.exists(gitignore_path):
with open(gitignore_path, 'r') as gitignore_file:
# Read lines and strip whitespace and empty lines
patterns = [line.strip() for line in gitignore_file.readlines()]
patterns = [p for p in patterns if p and not p.startswith('#')]
gitignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', patterns)
base_path = path
break
except ImportError:
pass

def _support(reader):
return not lans or set(lans).intersection(
reader.language_names)

def _validate_file(pathname):
if gitignore_spec is not None and base_path is not None:
rel_path = os.path.relpath(pathname, base_path)
# Normalize path separators for consistent matching
rel_path = rel_path.replace(os.sep, '/')
if gitignore_spec.match_file(rel_path):
return False
return (
pathname in paths or (
get_reader_for(pathname) and
Expand All @@ -926,6 +955,7 @@ def all_listed_files(paths):
for filename in files:
yield os.path.join(root, filename)

_load_gitignore()
return filter(_validate_file, all_listed_files(paths))


Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def run(self):
packages=['lizard_ext', 'lizard_languages'],
#data_files=[('lizard_ext', [])],
py_modules=['lizard'],
install_requires=['pygments'],
install_requires=['pygments', 'pathspec'],
entry_points={'console_scripts': ['lizard = lizard:main']},
author='Terry Yin',
author_email='[email protected]',
Expand Down
31 changes: 31 additions & 0 deletions test/testFilesFilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,34 @@ def test_fail_to_open_file_should_be_allowed(self, mock_open, mock_os_walk):
else:
file_names = ["./f1.cpp", "./f2.cpp"]
self.assertEqual(file_names, list(files))

@patch.object(os.path, "exists")
@patch.object(os.path, "relpath")
@patch.object(os, "walk")
@patch("builtins.open", create=True)
def test_gitignore_filter(self, mock_open, mock_os_walk, mock_relpath, mock_exists):
mock_os_walk.return_value = (['.',
None,
['temp.c', 'node_modules/file.js', 'useful.cpp']], )

def exists_side_effect(path):
return path.endswith('.gitignore')
mock_exists.side_effect = exists_side_effect

def relpath_side_effect(path, start):
# Return paths in a normalized format
if path.startswith('./'):
path = path[2:]
return path.replace(os.sep, '/')
mock_relpath.side_effect = relpath_side_effect

mock_file = mock_open.return_value.__enter__.return_value
mock_file.readlines.return_value = ["node_modules/\n", "*.c\n"]
mock_file.read.return_value = "node_modules/\n*.c\n"

files = get_all_source_files(["dir"], [], [])
if which_system() == "Windows":
file_names = [".\\useful.cpp"]
else:
file_names = ["./useful.cpp"]
self.assertEqual(file_names, list(files))

0 comments on commit b537d12

Please sign in to comment.