如何列出所有未被Git忽略规则排除的文件?
实现类似find且遵循Git忽略规则的文件列表工具
核心思路拆解
你提到的需求抓得很准:先拿到所有文件(完全不管Git的追踪、修改状态),再用.git/info/exclude和所有层级的.gitignore规则做过滤。git ls-files之所以不适用,就是因为它从Git的语义出发,只关注已追踪或符合特定Git逻辑的文件,完全不符合你要的「先全量再过滤」的路子。
具体实现方案
这里分享几个从快速脚到代码集成的可行方向,你可以根据场景选:
1. find+git check-ignore组合(快速脚本方案)
这是最直接的轻量方案,用原生命令就能搞定:
find . -not -path './.git/*' -type f -exec git check-ignore -v {} + | awk '!$1' | cut -d' ' -f2-
- 逐段解释:
find . -not -path './.git/*' -type f:拿当前目录下所有普通文件,直接跳过.git目录(毕竟这部分不属于项目文件)git check-ignore -v {} +:批量检查每个文件是否被忽略,-v会输出忽略规则的来源(被忽略的文件才会有这个输出)awk '!$1':过滤掉被忽略的文件——被忽略的行第一个字段是规则来源(非空),未被忽略的行输出是:: 文件路径,所以!$1会留下这些未被忽略的条目cut -d' ' -f2-:把前面的::截掉,只保留纯文件路径
如果要包含目录,去掉-type f就行:
find . -not -path './.git/*' -exec git check-ignore -v {} + | awk '!$1' | cut -d' ' -f2-
2. Python代码集成方案(灵活可控)
如果要把这个逻辑写到项目代码里,用Python的第三方库会更方便:
首先安装依赖:
pip install gitignore-parser
然后写脚本:
import os from gitignore_parser import parse_gitignore def collect_all_files(root_dir): all_items = [] for dirpath, dirnames, filenames in os.walk(root_dir): # 跳过.git目录,避免处理Git内部文件 if '.git' in dirnames: dirnames.remove('.git') # 添加文件 for filename in filenames: all_items.append(os.path.join(dirpath, filename)) # 添加目录 for dirname in dirnames: all_items.append(os.path.join(dirpath, dirname)) return all_items def main(): root = '.' # 收集所有要应用的忽略文件:所有.gitignore + .git/info/exclude ignore_files = [] # 遍历所有子目录找.gitignore for dirpath, _, _ in os.walk(root): gi_path = os.path.join(dirpath, '.gitignore') if os.path.exists(gi_path): ignore_files.append(gi_path) # 添加.git/info/exclude info_exclude = os.path.join(root, '.git', 'info', 'exclude') if os.path.exists(info_exclude): ignore_files.append(info_exclude) # 合并所有忽略规则:只要匹配任意一条规则就忽略 match_func = None for file_path in ignore_files: current_match = parse_gitignore(file_path) if match_func is None: match_func = current_match else: def combined(path): return match_func(path) or current_match(path) match_func = combined # 获取全量文件/目录,再过滤 all_items = collect_all_files(root) if match_func: filtered_items = [item for item in all_items if not match_func(item)] else: filtered_items = all_items # 输出结果 for item in filtered_items: print(item) if __name__ == '__main__': main()
- 这个方案的优势是完全可控,可以根据需求调整过滤逻辑,比如增加自定义规则、处理符号链接等。
3. 用fd工具(现代化find替代)
如果你习惯用现成工具,fd(一个比find更快更友好的工具)可以直接实现需求:
# 先确保bash开启globstar(不然**/.gitignore匹配不到所有子目录的规则) shopt -s globstar # 执行命令 fd --no-ignore-vcs --ignore-file .git/info/exclude --ignore-file **/.gitignore
- 解释:
--no-ignore-vcs:关闭fd默认的Git忽略逻辑,避免它自动跳过已忽略文件--ignore-file:手动指定要应用的忽略文件,**/.gitignore会匹配所有子目录的.gitignore
关键注意点
- 务必跳过
.git目录:不管用哪种方法,都要排除.git目录,里面的文件不属于项目范畴,Git忽略规则也不会处理它们 - 规则优先级:子目录的
.gitignore规则优先级高于根目录的,上面的方案都遵循了Git的原生优先级逻辑 - 符号链接:如果需要处理符号链接,要调整对应参数——比如
find加-L,Python的os.walk加followlinks=True
内容的提问来源于stack exchange,提问作者John Yates
相关产品推荐
相关产品推荐

