预接收钩子无法读取提交的文件以推送到远程主服务器 [英] pre-receive hook unable to read the committed file to push into remote master

查看:58
本文介绍了预接收钩子无法读取提交的文件以推送到远程主服务器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在服务器的预接收文件中尝试了以下内容,但我在本地 git 存储库中的预提交工作没有任何问题

i tried the following in pre-receive file in my server but my pre-commit on my local git repository works without any issues

#!/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

but get the error like remote: sed: can't read /App/abc/TestFile.java:No such file or directory

请帮我解决它?

推荐答案

这有点乱.

让我们先从你的预提交钩子开始:

Let's just take this part first, from your pre-commit hook:

git diff --name-only HEAD^ HEAD $(git write-tree)

内部 git write-tree 将索引写入树并返回其哈希值.为了举例,假设它是 01234567.

The inner git write-tree writes the index to a tree and returns its hash value. For the sake of example let's say it's 01234567.

然后使用三个 commit-or-tree-ish 参数运行 git diff(所有三个参数都可以解析为树标识符,这是 git 在这里关心的):

You then run git diff with three commit-or-tree-ish arguments (all three arguments can be resolved to a tree identifier, which is what git cares about here):

HEAD^ HEAD 01234567

这会在 git diff 中调用一个未记录的行为:它产生一个组合差异".组合差异的输入被认为是几个父级(除了第一个参数之外的所有)和一个子级(第一个参数),因此这将处理您刚刚编写的树,以及 HEAD 在存储库,作为两个父提交,HEAD^ 作为子提交.

This invokes an undocumented bit of behavior in git diff: it produces a "combined diff". The inputs to the combined diff are considered to be several parents (all but the first argument) and one child (the first argument), so this treats the tree you just wrote, and the HEAD commit in the repository, as the two parent commits, with HEAD^ as the child commit.

git diff 文档 指出组合差异仅列出从所有父项修改过的文件."在这种情况下,同样,两个父级"是提议的新提交的树(来自 git write-tree)和 HEAD 提交(当前位于提示当前分支的).其中两者HEAD^(当前分支的tip的第一个父级)不同,git会显示一个差异.这不是你想要的!(因为您还指定了 --name-only,git 将只显示文件名,而不是实际的差异.)

The git diff documentation notes that a combined diff "lists only files which were modified from all parents." In this case, again, the two "parents" are the proposed new commit's tree (from git write-tree) and the HEAD commit (that which is currently at the tip of the current branch). Where both of those differ from HEAD^ (the first parent of the tip of the current branch), git will show you a diff. This is not what you want! (Since you also specified --name-only, git will show you just the file names, not the actual diff.)

然后,您获取这些名称并在这些文件中查找 git 的冲突标记"(<<<>>> 标记周围冲突地区).这部分没有那么错误(但仍然有一点点损坏),但此时事情已经错了,因为您可能正在查看错误的文件.

You then take those names and look in those files for git's "conflict markers" (the <<< and >>> marks around conflicting regions). This part is not as wrong (but is still slightly broken), but at this point things are already wrong because you may be looking at the wrong files.

以commit HEAD^缺少文件f2为例,commit HEAD添加文件f2>,并且当前索引修改了文件 f3 但里面有一个 git 冲突:

Consider, for instance, the case where commit HEAD^ lacks file f2, commit HEAD adds file f2, and the current index modifies file f3 but has a git conflict in it:

$ 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,我们会看到组合的 diff 输出:

There's the problem: the combined diff did not look at f3 as it is not modified in both "parents" vs the "child" (of course these "parent"/"child" relationships are nonsensical anyway). Without --name-only we see the combined diff output:

$ 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 以交互方式选择要添加的部分和推迟添加的部分.因此,索引的内容可能与工作目录不匹配.)有多种方法可以做到这一点;请参阅这个问题及其对一种方法的回答,以及以下(使用 git show 进行修订-and-path) 用于另一个.您现在拥有的代码适用于某些情况,但绝对不是全部.

If you want to check whether your proposed new commit's tree has some files with conflict markers, you need to examine the proposed "blobs", rather than the current working tree. (This is because you can git add a file, then modify it further; or git add -p to interactively select parts to add and parts to defer adding. Hence, the contents of the index may not match the working directory.) There are a number of ways to do this; see this question and its answer for one method, and below (using git show with a revision-and-path) for another. The code you have now will work for some cases, but definitely not all.

除此之外,我看到 Ikke 已经回答了另一个问题,这是一个存储库——git push 操作的通常目标,以及运行预接收钩子的地方——没有工作树,所以你不能查看该工作树中的文件.预接收钩子通常更难编写,因为您必须处理许多情况:

With that out of the way, I see that Ikke has already answered the other issue, which is that a bare repository—the usual target for git push operations, and the place where you would run a pre-receive hook—has no work tree, so you can't look at files in that work tree. Pre-receive hooks are generally more difficult to write as you must handle many cases:

  • 多次提交
  • 不是分支(标签)的引用
  • 未提交的对象(带注释的标签)
  • 分支的创建和删除以及更新

当一个分支(形式为 refs/heads/name 的引用)被提议更新时,预接收钩子获取其当前的 SHA-1 和提议的新 SHA-1.然后,如果您允许更新,您可以使用 git rev-list 查找将位于(或不再位于)分支上的对象序列.对于每个这样的对象,如果它是一个提交,您将检查附加到该提交的树,以查看该树中的所有 blob(文件)是否通过检查.

When a branch (a reference of the form refs/heads/name) is proposed to be updated, the pre-receive hook gets its current SHA-1 and a proposed new SHA-1. You can then use git rev-list to find the sequence of objects that will be on (or no longer on) the branch if you allow the update. For each such object, if it's a commit, you would examine the tree attached to that commit, to see if all the blobs (files) in that tree pass inspection.

请注意,pre-receiveupdate 钩子与其他 git pre" 钩子非常不同: 在这两种情况下,建议新的提交和/或带注释的标签实际上已经在存储库中(尽管如果您的钩子拒绝它们,它们可能会再次被删除),并且您通常应该通过对象 ID (SHA-1) 来引用这些提议的 git 对象.(遍历提交树是可以的;事实上,在许多情况下,您必须这样做.)这里的要点是,对于预提交钩子来说正确的东西几乎可以肯定是错误的预接收钩子,反之亦然.

Please note that pre-receive and update hooks are very different from other git "pre" hooks: in both cases, the proposed new commits and/or annotated-tags are actually already in the repository (although they may be stripped out again if your hook rejects them), and you should generally refer to these proposed git objects by object-ID (SHA-1). (It's OK to walk the commit tree; in fact, you must do this in many cases.) The point here is that what is correct for a pre-commit hook is almost guaranteed to be wrong for a pre-receive hook, and vice versa.

这个过程的粗略概述可能是:

A rough outline of this process might be:

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 冲突标记. 如果你选择这样做, 如果有应该允许的情况, 请确保你允许某种方式绕过它.)

(Note that this is entirely untested. I expect it has a bug in the "file deleted" case, where there is nothing to git show in the new revision. Also, it's not necessarily a good idea to check for <<< and the like though. What happens if there's a text file illustrating how git conflicts look? You can choose the kind of inspection to do based on the file name, perhaps, but even then, some files might legitimately contain what look like, but are not actually, git conflict markers. If you choose to do this, make sure you allow some way around it if there is a case when it should be allowed.)

这篇关于预接收钩子无法读取提交的文件以推送到远程主服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆