我在我的服务器中的预接收文件中尝试了以下内容,但我在本地git存储库上的预提交工作没有任何问题
#!/bin/bash
git rev-parse -q --verify HEAD && sed -n -e '/^<<\+ /q1' -n -e '/^>>\+ /q1' $(git diff --name-only HEAD^ HEAD $(git write-tree)) || { echo "Git conflict (<<<<) or (>>>>) exists in one of the committed files."; exit 1; }
但是会收到remote: sed: can't read /App/abc/TestFile.java:No such file or directory
请帮我解决一下?
答案 0 :(得分:2)
这有点乱。
让我们先从你的预提交钩子中取出这部分:
git diff --name-only HEAD^ HEAD $(git write-tree)
内部git write-tree
将索引写入树并返回其哈希值。为了举例,我们说它是01234567
。
然后使用三个commit-or-tree-ish参数运行git diff
(所有三个参数都可以解析为树标识符,这是git在这里关心的):
HEAD^ HEAD 01234567
这在git diff中调用了一个未记录的行为:它产生了一个“组合差异”。组合diff的输入被认为是几个父项(除第一个参数之外的所有参数)和一个子(第一个参数),因此这将处理您刚刚编写的树以及存储库中的HEAD
提交,如两个父提交,HEAD^
作为子提交。
git diff
documentation注意到组合差异“只列出了从所有父母修改过的文件”。在这种情况下,同样,两个“父”是建议的新提交树(来自git write-tree
)和HEAD
提交(当前位于当前分支的尖端)。其中的两个与HEAD^
(当前分支的尖端的第一个父级)不同,git将显示差异。这不是你想要的! (由于您还指定了--name-only
,git将只显示文件名,而不是实际差异。)
然后,您可以使用这些名称并在这些文件中查找git的“冲突标记”(冲突区域周围的<<<
和>>>
标记)。这部分没有错(但仍然有点破坏),但此时事情已经错了,因为你可能正在查看错误的文件。
例如,考虑提交HEAD^
缺少文件f2
,提交HEAD
添加文件f2
,当前索引修改文件f3
的情况但它有一个git冲突:
$ mkdir /tmp/repo; cd /tmp/repo; git init
Initialized empty Git repository in /tmp/repo/.git/
$ echo ordinary file > f1; git add f1
$ echo another ordinary file > f3
$ git add f1 f3; git commit -m initial
[master (root-commit) f181096] initial
2 files changed, 2 insertions(+)
create mode 100644 f1
create mode 100644 f3
$ echo new file f2 > f2; git add f2
$ git commit -m 'add f2'
[master c06f8d1] add f2
1 file changed, 1 insertion(+)
create mode 100644 f2
$ (echo '<<< conflict'; echo '==='; echo '>>> end conflict') > f3
$ git add f3 # but we never resolved our (fake) conflict
$ git diff --name-only HEAD^ HEAD $(git write-tree)
f2
存在问题:合并后的差异没有看f3
因为它没有在“父母”和“孩子”中被修改(当然这些“父母”/“孩子”关系无论如何都是荒谬的) 。没有--name-only
,我们会看到组合差异输出:
$ git diff HEAD^ HEAD $(git write-tree)
diff --cc f2
index 9d57e62,9d57e62..0000000
deleted file mode 100644,100644
--- a/f2
+++ /dev/null
@@@ -1,1 -1,1 +1,0 @@@
--new file f2
如果要检查建议的新提交树是否有一些带有冲突标记的文件,则需要检查建议的“blob”,而不是当前工作树。 (这是因为您可以git add
一个文件,然后进一步修改它;或者git add -p
以交互方式选择要添加的部分和部分以推迟添加。因此,索引的内容可能与工作目录不匹配。)有很多方法可以做到这一点;请参阅this question及其对一种方法及其下方的答案(使用带有修订版和路径的git show
)。您现在拥有的代码适用于一些案例,但绝对不是全部。
有了这个,我看到Ikke has already answered the other issue,这是一个裸存储库 - git push
操作的常用目标,以及运行预接收挂钩的地方 - 没有工作树,因此您无法查看该工作树中的文件。预接收挂钩通常更难写,因为您必须处理许多情况:
当建议更新分支(形式为refs/heads/name
的引用)时,预接收挂钩将获得其当前的SHA-1和建议的新SHA-1。然后,如果允许更新,则可以使用git rev-list
查找将在分支上(或不再在分支上)的对象序列。对于每个这样的对象,如果它是一个提交,你将检查附加到该提交的树,以查看该树中的所有blob(文件)是否通过了检查。
请注意,pre-receive
和update
挂钩与其他git“pre”挂钩非常不同:在这两种情况下,建议的新提交和/或带注释的标签实际上已经存在于存储库中(尽管如果你的钩子拒绝它们可能会再次被剥离),你通常应该通过object-ID(SHA-1)来引用这些提议的git对象。 (可以使用提交树;实际上,在许多情况下,必须执行此操作。)这里的要点是,预先提交挂钩的正确性几乎可以保证是错误的。预接收挂钩,反之亦然。
这个过程的大致概述可能是:
NULL_SHA1=0000000000000000000000000000000000000000
check_revs()
{
local range branch rev rtype path
range=$1
branch=$2
git rev-list $range |
while read rev; do
rtype=$(git cat-file -t $rev)
case $rtype in
commit) ;;
*) continue;; # skip annotated tags
esac
git diff --name-only ${rev}^ $rev |
while read path; do
if git show ${rev}:$path | grep forbidden-item; then
echo "error: branch ${branch}: ${rev}:$path contains forbidden-item" 1>&2
exit 1
fi
done
done
}
check_branch()
{
local old new branch
old=$1
new=$2
branch=$3
if [ $old = $NULL_SHA1 -o $new = $NULL_SHA1 ]; then
# branch will be created or deleted, not updated
# do whatever is appropriate here
else
# branch will be updated, if we allow it
check_revs $old..$new $branch
fi
}
while read oldsha newsha fullref; do
case "$fullref" in
refs/heads/*) check_branch $oldsha $newsha ${fullref#refs/heads/};;
# add cases for refs/tags/* if desired, etc
*) ;;
esac
exit 0 # if we got this far it must be OK to do it
(请注意,这是完全未经测试的。我希望它在“已删除文件”的情况下有一个错误,在新版本中没有git show
。此外,检查不一定是个好主意。对于<<<
等等。如果有一个文本文件说明git冲突的样子会怎样?你可以选择基于文件名的检查类型,但即使这样,一些文件可能合法地包含看起来是什么,但实际上并不是git冲突标记。如果你选择这样做,请确保在允许的情况下允许它绕过它。)
答案 1 :(得分:1)
问题是sed尝试从文件系统读取文件,但裸存储库没有工作树。
git write-tree
创建一个新的树对象,它不会检出文件,因此它不适合你想要的。
此外,您只检查上次提交的差异,而有人可以推送许多提交。 pre-receive挂钩提供有关在stdin
上推送的提交的信息(请参阅格式的手册页)。
你必须迭代每一行并使用它来创建一个你可以输入sed
的差异
答案 2 :(得分:0)
git diff "$old_commit"..."$latest_commit"
其中old_commit和latest_commit是预先接收的默认输入。它显示了最古老的&amp;之间的差异。最新的提交。