gpt4 book ai didi

python - 哪些git命令可以遍历被推送的文件?

转载 作者:行者123 更新时间:2023-12-03 03:01:41 29 4
gpt4 key购买 nike

我正在尝试在python中实现pre-push git钩子,以在将文件推送到远程仓库之前验证文件。

我之前编写了pre-commit git钩子,以在将文件提交到本地存储库之前验证文件并获取提交中的文件列表,然后运行git diff-index --cached --name-status HEAD

对于pre-push脚本,我可以运行哪些git命令来遍历将要推送的所有提交,然后遍历各个提交中的所有文件,以便可以验证它们?

到目前为止,我正在使用命令:git diff --name-status @{u}..

编辑:我认为同样重要的是要注意,可以在将要推送的多个提交之间修改同一文件-因此最好不要多次验证同一文件。

FINAL_SOLUTION:

由于@Vampire和@Torek的回答,这是我最终使用的代码...

#!/usr/bin/env python

# read the args provided by git from stdin that are in the following format...
# <local ref> SP <local sha1> SP <remote ref> SP <remote sha1> LF
# the line above represents a branch being pushed
# Note: multiple branches may be pushed at once

lines = sys.stdin.read().splitlines()

for line in lines:
local_ref, local_sha1, remote_ref, remote_sha1 = line.split()

if remote_sha1 == "0000000000000000000000000000000000000000":
print_error("Local branch '%s' cannot be found on the remote repo - push only the branch without any commits first!" % local_ref)
sys.exit(1)

# get changed files
changed_files = subprocess.check_output(["git", "diff", "--name-status", local_sha1, remote_sha1], universal_newlines=True)

# get the non deleted files while getting rid of M\t or A\t (etc) characters from the diff output
non_deleted_files = [ f[2:] for f in changed_files.split("\n") if f and not f.startswith("D") ]

# validation here...
if validation_failed:
sys.exit(1) # terminate the push

sys.exit(0)

最佳答案

问题1:提交



获取提交列表只是中等困难,因为您最需要运行git rev-list。但是,这里有一些极端情况。正如the githooks documentation所说:


挂钩的标准输入中提供了有关要推送的内容的信息,格式为:

<local ref> SP <local sha1> SP <remote ref> SP <remote sha1> LF

例如,如果运行命令git push origin master:foreign,则该钩子将收到如下一行:

refs/heads/master 67890 refs/heads/foreign 12345

尽管将提供完整的40个字符的SHA-1。如果外部引用不存在,则<remote SHA-1>将为40 0。如果要删除引用,则<local ref>将作为(delete)提供,而<local SHA-1>将为40 0。如果本地提交是由可以扩展的名称(例如HEAD〜或SHA-1)以外的其他名称指定的,则将按最初提供的名称提供。


因此,您必须阅读每条stdin行并将其解析为其组件,然后确定:


这是一个分支更新吗? (即,远程引用是否具有refs/heads/*形式,作为全局匹配?)否则,您是否仍要检查任何提交?
参考是被创建还是被销毁?如果是这样,您应该怎么办?
您是否有由外部哈希指定的对象? (如果不成功,则推送成功-很有可能失败-这将丢弃一定数量的提交对象,但您无法确定是哪个。此外,您无法正确列出要传输的本地提交内容:您知道自己在问什么他们将他们的名字设置为,但是您不知道您和他们有哪些共同点,因为您无法遍历他们的历史。)


假设您已经确定了这些问题的答案,比如说它们是“否”,“跳过”和“本地拒绝无法分析的推送”,那么我们继续列出提交,而这仅仅是以下内容的输出:

git rev-list remotehash..localhash


您可以这样做:

proc = subprocess.Popen(['git', 'rev-list',
'{}..{}'.format(remotehash, localhash)], stdout=subprocess.PIPE)
text = proc.stdout.read()
if proc.wait():
raise ... # some appropriate error, as Git failed here
if not isinstance(text, str): # i.e., if python3
text = text.decode('utf-8') # convert bytes to str
lines = text.split('\n')
# now work with each commit hash


请注意,如果远程哈希或本地哈希为全零,或者远程哈希用于本地存储库中不存在的对象,则此 git rev-list调用将失败(状态为非零退出)。 git rev-parse --verify --quiet并检查返回状态,或者在此处使用失败作为您无法检查提交的指示,尽管在创建新分支时还有其他选择)。

请注意,您必须为要更新的每个引用运行上述 git rev-list。可能会为不同的引用发送相同的提交或相同提交的某些子集。例如:

git push origin HEAD:br1 HEAD:br2 HEAD~3:br3


将要求远程更新三个分支 br1br3,将 br1br2设置为与 HEAD相同的提交,并将 br3设置为从 HEAD返回三步的提交。我们不(也无法)知道哪些提交确实是新的-另一端上的预接收钩子可以弄清楚这一点,但是我们不能-但是如果遥控器的 br1br2都从 HEAD~3,并且遥控器的 HEADbr3向后更新为 HEAD~2,最多通过 HEAD~3提交 HEAD~1是新的。是否还要检查 HEAD,因为它现在很可能显示在另一个存储库中的 HEAD~2br1上(即使它已经在 br2上了),也取决于您。

问题2:文件

现在您遇到了更困难的问题。您在编辑中提到:


编辑:我认为同样重要的是要注意,可以在将要推送的多个提交之间修改同一文件-因此最好不要多次验证同一文件。


每个要发送的提交都具有存储库的完整快照。也就是说,每个提交都有每个文件。我不知道您打算运行什么验证,但是您是正确的:例如,如果总共发送六个提交,则很可能所有六个提交中的大多数文件都是相同的,并且只有少数文件被修改。但是,文件 br3可能在提交 foo.py中进行了修改(相对于 1234567的父提交),然后又在提交 1234567中进行了修改,您可能应该检查两个版本。

此外,当提交是合并提交时,它具有(至少)两个不同的父级。您是否应该检查文件与父文件是否不同?还是仅当它与父母双方不同时才检查它,表明它包含合并的“两面”的更改?如果仅从“一侧”进行了更改,则可能通过对另一侧的提交所做的任何检查来“预先检查”该文件,因此可能不需要重新检查(尽管取决于检查的类型)。

(对于章鱼合并,即与两个以上父母合并,这个问题很难思考。)

相对于其父级或父级,很容易查看在提交中更改了哪些文件:只需运行带有适当选项的 fedcba9(尤其是 git diff-tree即可递归到提交的子树中)。尽管您可能想添加 -r以便更轻松地直接在Python中进行处理,但是默认的输出格式完全可以通过计算机解析。如果您一次执行这些操作(您也可以这样做),则可能还需要 -z,这样就不必阅读和跳过提交标头。

是否要启用重命名检测以及是否启用重命名取决于您自己。再一次,确切地取决于您要执行的验证文件的工作,最好不要进行重命名检测:通过这种方式,您将“看到”重命名的文件,作为旧路径的删除和新路径的添加。

--no-commit-id在特定提交上的输出如下所示:


:000000 100644 0000000000000000000000000000000000000000 b0b4c36f9780eaa600232fec1adee9e6ba23efe5 A  Documentation/RelNotes/2.13.0.txt
:100755 100755 6a208e92bf30c849028268b5fca54b902f671bbd 817d1cf7ef2a2a99ab11e5a88a27dfea673fec79 M GIT-VERSION-GEN
:120000 120000 d09c3d51093ac9e4da65e8a127b17ac9023520b5 125bf78f3b9ed2f1444e1873ed02cce9f0f4c5b8 M RelNotes



哈希ID是旧的和新的Blob哈希;字母代码和路径名已记录在案。然后,您可以使用新哈希ID上的 git diff-tree -r --no-commit-id检索文件内容。如果您的Git足够新,您甚至可以通过添加 git cat-file -p .gitattributes--textconv(或使用文件路径以及提交ID来进行任何基于 --filters的过滤和行尾转换) ( --path=<path>),以命名要提取的对象的哈希值。或者,如果过滤器不重要,则可以使用存储在存储库中的对象的形式。

但是,根据检查的内容,您可能需要将整个提交提取到一个临时工作树中。 (例如,静态分析器可能要执行任何 --path=...。)在这种情况下,您也可以使用 import环境变量(照常通过 git checkout传递)来运行 GIT_INDEX_FILE。临时索引文件,以免干扰主索引。用 subprocess或通过 --work-tree=环境变量指定备用工作树。在任何情况下, GIT_WORK_TREE都会告诉您哪些文件已修改,因此应进行检查。 (测试完成后,可以使用 git diff-tree处置临时工作树。)

如果要检查合并提交,请特别注意 description of combined diffs done for merges,因为它们将需要一些不同的处理(或使用 shutil.rmtree拆分合并)。

编辑:一些代码来显示我的意思

这是一些代码,用于获取所有输入并显示将每个提交添加到每个外部分支。请注意,如果仅删除提交,则添加的提交列表将为空。这也只是经过非常轻松的测试,并不旨在具有鲁棒性,可维护性,良好的风格等,仅是一个最小的示例。

import re, subprocess, sys

lines = sys.stdin.read().splitlines()
for line in lines:
localref, localhash, foreignref, foreignhash = line.split()
if not foreignref.startswith('refs/heads/'):
print('skip {}'.format(foreignref))
continue
if re.match('0+$', localhash):
print('deleting {}, do nothing'.format(foreignref))
continue
if re.match('0+$', foreignhash):
print('creating {}, too hard for now'.format(foreignref))
continue
proc = subprocess.Popen(['git', 'rev-parse', '--quiet', '--verify',
foreignhash],
stdout=subprocess.PIPE)
_ = proc.stdout.read()
status = proc.wait()
if status:
print('we do not have {} for {}, try '
'git fetch'.format(foreignhash, foreignref))
# can try to run git fetch here ourselves, but for now:
continue
print('sending these commits for {}:'.format(foreignref))
subprocess.call(['git', 'rev-list', '{}..{}'.format(localhash, foreignhash)])

关于python - 哪些git命令可以遍历被推送的文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42964217/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com