有没有一种简单的方法来匹配.gitignore规则的文件?

时间:2016-12-13 21:13:14

标签: python git gitignore

我正在Python中编写一个git pre-commit钩子,我想在处理它们之前定义一个像.gitignore文件这样的黑名单来检查文件。有没有一种简单的方法来检查是否根据一组.gitignore规则定义了文件?规则有点神秘,我宁愿不必重新实现它们。

2 个答案:

答案 0 :(得分:4)

假设您位于包含.gitignore文件的目录中,那么一个shell命令将列出所有被忽略的文件:

git ls-files

从python您可以简单地调用:

import os
os.system("git ls-files")

,您可以像这样提取文件列表:

import subprocess
list_of_files = subprocess.check_output("git ls-files", shell=True).splitlines()

如果要列出忽略的文件(又称未跟踪文件),则可以添加选项“ --other”:

git ls-files --other

答案 1 :(得分:1)

这是相当笨拙的,但应该有效:

  • 创建临时git存储库
  • 使用您提议的.gitignore
  • 填充它
  • 还使用每个路径名一个文件填充它
  • 在生成的临时存储库中使用git status --porcelain
  • 将其清空(完全删除,或将其保留为空,以便下次通过,以较合适的方式)。
然而,这确实闻起来像XY problem Y 的笨重解决方案可能是解决实际问题 X 的不良解决方案。

评论后答案详情(和附注)

所以,你有一些lint文件,可能来自检查提交。以下代码可能比您需要的更通用(在大多数情况下我们并不需要status部分),但我将其包含在插图中:

import subprocess

proc = subprocess.Popen(['git',
     'diff-index',                        # use plumbing command, not user diff
     '--cached',                          # compare index vs HEAD
     '-r',                                # recurse into subdirectories
     '--name-status',                     # show status & pathname
     # '--diff-filter=AM',                # optional: only A and M files
     '-z',                                # use machine-readable output
     'HEAD'],                             # the commit to compare against
     stdout=subprocess.PIPE)
text = proc.stdout.read()
status = proc.wait()
# and check for failure as usual: Git returns 0 on success

现在我们需要来自Iterating over every two elements in a listpairwise

import sys

if sys.version_info[0] >= 3:
    izip = zip
else:
    from itertools import izip
def pairwise(it):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(it)
    return izip(a, a)

我们可以使用以下命令分解git status输出

for state, path in pairwise(text.split(b'\0')):
    ...

我们现在为每个文件都有一个状态(b'A' =已添加,b'M' =已修改,等等)。 (如果允许符号链接,请务必检查状态T,以防文件从普通文件更改为符号链接,反之亦然。请注意,我们依赖pairwise来丢弃未配对的空{在b''末尾的{1}}字符串,因为Git生成NUL- 终止的列表而不是NUL- 分隔的列表。)< / p>

让我们假设在某些时候我们将文件 - 可能 - lint收集到名为text.split(b'\0')的列表(或可迭代)中:

candidates

我会假设您已避免将>>> candidates [b'a.py', b'dir/b.py', b'z.py'] 放入此列表或可迭代中,因为我们计划将其用于我们自己的目的。

现在我们遇到两个大问题:忽略一些文件,并获取那些实际上会被linted的文件的版本。

仅仅因为文件被列为已修改,并不意味着工作树中的版本是将提交的版本。例如:

.gitignore

此处的第一个$ git status $ echo foo >> README $ git add README $ echo bar >> README $ git status --short MM README 表示索引版本与M不同(这是我们从HEAD获得的内容),而第二个git diff-index在这里表示索引版本与工作树版本不同。

提交的版本是索引版本,而不是工作树版本。我们需要的lint不是工作树版本,而是索引版本。

所以,现在我们需要一个临时目录。如果你的Python是旧的,那么这里使用的是M,如果不是,那么使用的是风格化的上下文管理器版本。请注意,在使用Python3时我们有上面的字节串路径名,在使用Python2时我们有普通(字符串)路径名,所以这也取决于版本。

由于这是普通的Python,而不是棘手的Git交互,我把这部分作为一个练习 - 我只是在所有的字节 - vs-strings路径名的东西上光泽。 :-)但是,对于下面的tempfile.mkdtemp位,请注意Git需要文件名列表为b --stdin -z - 分隔字节。

一旦我们拥有(空)临时目录,其格式适合传递到\0中的cwd=,我们现在需要运行subprocess.Popen。有一些选择,但让我们走这条路:

git checkout-index

现在我们要将特殊的忽略文件写入import os proc = subprocess.Popen(['git', 'rev-parse', '--git-dir'], stdout=subprocess.PIPE) git_dir = proc.stdout.read().rstrip(b'\n') status = proc.wait() if status: raise ... if sys.version_info[0] >= 3: # XXX ugh, but don't want to getcwdb etc git_dir = git_dir.decode('utf8') git_dir = os.path.join(os.getcwd(), git_dir) proc = subprocess.Popen(['git', '--git-dir={}'.format(git_dir), 'checkout-index', '-z', '--stdin'], stdin=subprocess.PIPE, cwd=tmpdir) proc.stdin.write(b'\0'.join(candidates)) proc.stdin.close() status = proc.wait() if status: raise ... 。当然,我们现在还需要os.path.join(tmpdir, '.gitignore')像自己的Git存储库一样。这三件事就可以解决问题了:

tmpdir

因为我们现在将Git的忽略规则与我们复制到import shutil subprocess.check_call(['git', 'init'], cwd=tmpdir) shutil.copy(os.path.join(git_dir, '.pylintignore'), os.path.join(tmpdir, '.gitignore')) subprocess.check_call(['git', 'add', '-A'], cwd=tmpdir) 的{​​{1}}文件一起使用。

现在我们只需要再传.pylintignore次{.gitignore git status git diff-index`来处理被忽略的文件;但是有一种更简单的方法。我们可以让Git删除所有 - 签名文件:

-z

现在b'\0' style output, like中的所有内容正是我们应该发布的内容。

警告:如果你的python linter需要看到导入的代码,你不会想要删除文件。相反,您需要使用subprocess.check_call(['git', 'clean', '-fqx'], cwd=tmpdir) shutil.rmtree(os.path.join(tmpdir, '.git')) os.remove(os.path.join(tmpdir, '.gitignore') tmpdir来计算被忽略的文件。然后,您需要重复git status,但使用git diff-index选项,将所有文件解压缩到临时目录中。

完成后,只需像往常一样删除临时目录(总是自己清理!)。

请注意,上面的某些部分是分段测试的,但是将它们全部组装成完整的Python2或Python3代码仍然是一个练习。