- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试在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
br1
到
br3
,将
br1
和
br2
设置为与
HEAD
相同的提交,并将
br3
设置为从
HEAD
返回三步的提交。我们不(也无法)知道哪些提交确实是新的-另一端上的预接收钩子可以弄清楚这一点,但是我们不能-但是如果遥控器的
br1
和
br2
都从
HEAD~3
,并且遥控器的
HEAD
从
br3
向后更新为
HEAD~2
,最多通过
HEAD~3
提交
HEAD~1
是新的。是否还要检查
HEAD
,因为它现在很可能显示在另一个存储库中的
HEAD~2
和
br1
上(即使它已经在
br2
上了),也取决于您。
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
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
处置临时工作树。)
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/
我是一名优秀的程序员,十分优秀!