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

时间:2015-02-09 16:35:47

标签: git githooks git-remote

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

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

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

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

更新

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

4 个答案:

答案 0 :(得分:5)

  

我想知道是否有一个临时位置,文件一旦被推送就会保存在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

答案 1 :(得分:1)

您可能需要编写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

答案 2 :(得分:1)

推送的文件保存在永久位置,即存储库中的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的taint mode安全功能。

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

答案 3 :(得分:0)

要继续执行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的空树对象,可以与diff比较(不是全零)。这样,您将能够检查所有推送到新分支的对象。

尽管此空对象哈希很有用,但在使用它时要小心。如果要推送的提交/分支很大,则可能会增加计算量,因为您正在检查其中的每个对象。