Chr*_* B. 8 python git gitignore
我正在用Python写一个git pre-commit钩子,我想定义一个像.gitignore文件一样的黑名单,以便在处理文件之前检查文件。有没有一种简单的方法来检查是否根据一组.gitignore规则定义了文件?这些规则有点神秘,我宁愿不必重新执行它们。
假设您位于包含.gitignore文件的目录中,那么一个shell命令将列出所有不被忽略的文件:
git ls-files
Run Code Online (Sandbox Code Playgroud)
从python您可以简单地调用:
import os
os.system("git ls-files")
Run Code Online (Sandbox Code Playgroud)
您可以像这样提取文件列表:
import subprocess
list_of_files = subprocess.check_output("git ls-files", shell=True).splitlines()
Run Code Online (Sandbox Code Playgroud)
如果要列出被忽略(也称为未跟踪)的文件,则可以添加选项“ --other”:
git ls-files --other
Run Code Online (Sandbox Code Playgroud)
这相当笨拙,但应该有效:
\n\n.gitignoregit status --porcelain在生成的临时存储库上然而,这确实有点像XY 问题。Y 的笨拙解决方案对于实际问题X来说可能是一个糟糕的解决方案可能是一个糟糕的解决方案。
\n\n因此,您有一些文件需要检查,可能是通过检查提交来检查的。以下代码可能比您需要的更通用(我们实际上并不需要status部分),但我将其包含在内是为了说明:
import subprocess\n\nproc = subprocess.Popen([\'git\',\n \'diff-index\', # use plumbing command, not user diff\n \'--cached\', # compare index vs HEAD\n \'-r\', # recurse into subdirectories\n \'--name-status\', # show status & pathname\n # \'--diff-filter=AM\', # optional: only A and M files\n \'-z\', # use machine-readable output\n \'HEAD\'], # the commit to compare against\n stdout=subprocess.PIPE)\ntext = proc.stdout.read()\nstatus = proc.wait()\n# and check for failure as usual: Git returns 0 on success\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们需要类似迭代列表中的每两个元素pairwise的东西东西:
import sys\n\nif sys.version_info[0] >= 3:\n izip = zip\nelse:\n from itertools import izip\ndef pairwise(it):\n "s -> (s0, s1), (s2, s3), (s4, s5), ..."\n a = iter(it)\n return izip(a, a)\nRun Code Online (Sandbox Code Playgroud)\n\n我们可以分解git status为:
for state, path in pairwise(text.split(b\'\\0\')):\n ...\nRun Code Online (Sandbox Code Playgroud)\n\n现在,每个文件都有一个状态(b\'A\'= 添加、b\'M\'= 修改等)。(如果允许符号链接,请务必检查状态T,以防文件从普通文件更改为符号链接,反之亦然。请注意,我们依赖于丢弃末尾pairwise不成对的空字符串,这是因为Git 生成一个以 NUL结尾的列表,而不是一个以 NUL分隔的列表。)b\'\'text.split(b\'\\0\')
让我们假设在某个时候我们将文件收集到一个名为的列表(或可迭代)中candidates:
>>> candidates\n[b\'a.py\', b\'dir/b.py\', b\'z.py\']\nRun Code Online (Sandbox Code Playgroud)\n\n我假设您已避免将其放入.gitignore此列表或可迭代对象中,因为我们计划将其用于我们自己的目的。
现在我们有两个大问题:忽略一些文件,并获取这些文件的实际将被 linted 的版本。
\n\n仅仅因为文件被列为已修改,并不意味着工作树中的版本就是将提交的版本。例如:
\n\n$ git status\n$ echo foo >> README\n$ git add README\n$ echo bar >> README\n$ git status --short\nMM README\nRun Code Online (Sandbox Code Playgroud)\n\n这里的第一个M意味着索引版本不同于HEAD(这是我们从上面得到的git diff-index),而第二个M意味着索引版本也不同于工作树版本。
将提交的版本是索引版本,而不是工作树版本。我们需要检查的不是工作树版本而是索引版本。
\n\n所以,现在我们需要一个临时目录。这里要使用的是tempfile.mkdtemp如果你的Python是旧的,或者如果不是的话,使用幻想的上下文管理器版本。请注意,使用 Python3 时我们有上面的字节字符串路径名,而使用 Python2 时我们有普通(字符串)路径名,因此这也与版本相关。
由于这是普通的 Python,而不是棘手的 Git 交互,因此我将这部分留作练习\xe2\x80\x94,并且我将忽略所有字节与字符串路径名内容。:-) 但是,对于--stdin -z下面的部分,请注意 Git 将需要以 b 分隔的字节形式的文件名列表\\0。
cwd=一旦我们有了适合传递给in 的格式的(空)临时目录subprocess.Popen,我们现在需要运行git checkout-index. 有几个选项,但让我们这样做:
import os\n\nproc = subprocess.Popen([\'git\', \'rev-parse\', \'--git-dir\'],\n stdout=subprocess.PIPE)\ngit_dir = proc.stdout.read().rstrip(b\'\\n\')\nstatus = proc.wait()\nif status:\n raise ...\nif sys.version_info[0] >= 3: # XXX ugh, but don\'t want to getcwdb etc\n git_dir = git_dir.decode(\'utf8\')\ngit_dir = os.path.join(os.getcwd(), git_dir)\n\nproc = subprocess.Popen([\'git\',\n \'--git-dir={}\'.format(git_dir),\n \'checkout-index\', \'-z\', \'--stdin\'],\n stdin=subprocess.PIPE, cwd=tmpdir)\nproc.stdin.write(b\'\\0\'.join(candidates))\nproc.stdin.close()\nstatus = proc.wait()\nif status:\n raise ...\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们要将特殊的忽略文件写入os.path.join(tmpdir, \'.gitignore\'). 当然我们tmpdir现在也需要像它自己的 Git 存储库一样。这三件事就可以解决问题:
import shutil\n\nsubprocess.check_call([\'git\', \'init\'], cwd=tmpdir)\nshutil.copy(os.path.join(git_dir, \'.pylintignore\'),\n os.path.join(tmpdir, \'.gitignore\'))\nsubprocess.check_call([\'git\', \'add\', \'-A\'], cwd=tmpdir)\nRun Code Online (Sandbox Code Playgroud)\n\n因为我们现在将使用 Git 的忽略规则来处理.pylintignore我们复制到的文件.gitignore。
现在我们只需要再git status通过一次(使用-zgit b\'\\0\' style output, likediff-index`)来处理被忽略的文件;但有一个更简单的方法。我们可以让 Git 删除所有未忽略的文件:
subprocess.check_call([\'git\', \'clean\', \'-fqx\'], cwd=tmpdir)\nshutil.rmtree(os.path.join(tmpdir, \'.git\'))\nos.remove(os.path.join(tmpdir, \'.gitignore\')\nRun Code Online (Sandbox Code Playgroud)\n\n现在所有内容都tmpdir正是我们应该检查的。
警告:如果您的 python linter 需要查看导入的代码,您将不想删除文件。相反,您将想要使用git status或git diff-index计算被忽略的文件。然后您将需要重复该操作git checkout-index,但可以选择将所有-a文件提取到临时目录中。
完成后,只需像往常一样删除临时目录(务必自行清理!)。
\n\n请注意,上面的某些部分是分段测试的,但将其全部组装成完整工作的 Python2 或 Python3 代码仍然是一项练习。
\n| 归档时间: |
|
| 查看次数: |
631 次 |
| 最近记录: |