繁体   English   中英

在git pre-receive hook中访问已更改的文件路径

[英]Access the changed files path in git pre-receive hook

我正在远程仓库上写一个git预接收挂钩,以确保推送的代码与我们公司的内部指南一致。

我能够找到在触发预接收挂钩时要检查的所有文件,但是,我没有这些文件的路径来使用普通文件操作打开它们(例如cat git_working_directory/file_name会抛出No such file or directory错误)。 验证代码的应用程序需要将文件路径作为参数,以便它可以打开文件并运行其检查。

考虑这种情况:开发人员创建了一个新文件并将其推送到服务器并触发了预接收挂钩。 在此阶段,新文件未保存到远程工作目录,因为预接收挂钩仍在运行。

我想知道是否有一个临时位置,文件一旦被推送就保存在git中,以便我可以将该目录传递给应用程序来运行检查?

更新:

我可以结账到临时位置并在那里运行检查,这可能是一个选项,但考虑到开发人员经常推动的事实,有时甚至在同一时间和回购非常大,这个选项似乎不可行。 我正在寻找一个解决方案,我可以使用文件的路径,如果它以某种方式可用。

我想知道是否有一个临时位置,文件一旦被推送就保存在git中,以便我可以将该目录传递给应用程序来运行检查?

不,没有这样的地方。 这些文件存储为blob,可以简化为增量和/或压缩,因此不能保证它们在“准备消费”状态下可用。

检查标准的应用程序需要将文件路径作为参数,以便它可以打开文件并运行其检查。

如果您使用的是Linux,则可以将/dev/stdin指向输入文件并将文件放入管道。

#!/bin/sh
while read oldrev newrev refname; do
 git diff --name-only $oldrev $newrev | while read file; do
   git show $newrev:$file | validate /dev/stdin || exit 1
     done
done

您可能需要编写pre-receive挂钩来检出临时位置的文件。 通常,这意味着您将git clone裸存储库到临时目录,然后检查特定的提交并运行您的检查。 例如,类似于:

#!/bin/sh

REPO=$PWD

check_files_in () {
  rev=$1

  # create a temporary working directory
  workdir=$(mktemp -d gitXXXXXX)
  (
    # arrange to clean up the workding directory
    # when the subshell exits
    trap "cd /; rm -rf $workdir" EXIT

    # unset GIT_DIR because it would confuse things
    unset GIT_DIR

    # clone the repository
    cd $workdir
    git clone $REPO check

    # checkout the specific revision we're checking
    cd check
    git checkout $rev

    # perform some sort of validation.  The exit code of this 
    # command will be the exit code of this function, so
    # returning an error will reject the push.
    run-validation-scripts
  )
}

while read oldrev newrev refname; do
  check_files_in $newrev || exit 1
done

推送的文件保存在永久位置,即存储库中的git对象。 提取它们的一种方法是使用git archive

#! /usr/bin/perl -T

use strict;
use warnings;

# replace with your real check program and optional arguments
my @CHECK_PROGRAM = qw/ ls -la /;
#my @CHECK_PROGRAM = qw/ false /;

use File::Temp qw/ tempdir /;

$ENV{PATH} = "/bin:/usr/bin";

while (<>) {
  # <old-value> SP <new-value> SP <ref-name> LF
  my($oldsha,$newsha,$refname) = /\A([^ ]+) ([^ ]+) ([^ ]+)\x0A\z/;
  die "$0: unexpected input: $_" unless defined $refname;

  my $tmp = tempdir "prerecv-$$-XXXXXX", DIR => "/tmp", CLEANUP => 1;

  system(qq{git archive --format=tar $newsha | tar -C "$tmp" -x}) == 0
    or die "$0: git-archive $newsha failed";

  system(@CHECK_PROGRAM, $tmp) == 0 or die "$0: check failed";
}

因为代码代表另一个用户运行,所以它使用-T开关启用Perl的污点模式安全功能。

请注意,您的检查程序将在不知道哪些文件已更改的情况下将整个树视为已推送。 如果检查器也需要有关delta的信息,请调查git diff-tree以及可能的--diff-filter选项。

要跟进joozek的回答,如果你需要检查oldrev是全零(你正在推动一个新的分支),你仍然可以通过在joozek的解决方案中添加以下内容来阅读新的提交:

#!/bin/sh
z40=0000000000000000000000000000000000000000
while read oldrev newrev refname; do

 if [ $oldrev == $z40 ]; then
   # Commit being pushed is for a new branch
   oldrev=4b825dc642cb6eb9a060e54bf8d69288fbee4904
 fi
 git diff --name-only $oldrev $newrev | while read file; do
   git show $newrev:$file | validate /dev/stdin || exit 1
     done
done

散列“4b825dc642cb6eb9a060e54bf8d69288fbee4904”是git的空树对象,它是差异可比的(全部为零)。 这样,您就可以检查被推送到新分支的所有对象。

虽然这个空对象哈希很有用,但在使用它时要小心。 如果推送的提交/分支很大,它可能会计算成本很高,因为您正在检查其中的每个对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM