【问题标题】:Access the changed files path in git pre-receive hook在 git pre-receive 钩子中访问更改的文件路径
【发布时间】:2015-04-09 11:31:12
【问题描述】:

我正在远程 repo 上编写一个 git pre-receive 挂钩,以确保推送的代码符合我们公司的内部准则。

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

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

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

更新:

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

【问题讨论】:

  • 远程仓库不应该有工作目录。
  • 您需要将文件检出到一个临时工作目录并在那里运行您的检查。
  • @larsks 有没有我可以运行的 git 命令来检查这些文件到临时目录?
  • 只是为了澄清:您签入到存储库的文件不会“像那样”保留在那里。其中一些存储为其他的增量,或者它们的内容被压缩。没有任何地方可以保证这些文件处于“可使用”状态
  • 是的,joozek,这也是我的理解。我只是想知道我是否错过了关于 git 如何存储文件以及我们如何访问它们的内容。谢谢。

标签: git githooks git-remote


【解决方案1】:

要跟进 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 可比较的(全零不是)。这样您就可以检查所有被推送到新分支的对象。

虽然这个空对象散列很有用,但在使用时要小心。如果要推送的提交/分支很大,则计算量会变得很大,因为您要检查其中的每个对象。

【讨论】:

  • 一般/bin/sh不支持==,请改用=
【解决方案2】:

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

【讨论】:

  • 看起来是一个可行的选择,我会试着告诉你。
  • 现在我想起来了,我意识到这需要一个工作副本(它不应该出现在服务器上)。我会在一分钟内更新我的答案以直接从 repo 中提取
  • @SriVishnuTotakura 我编辑了我的答案以删除 WC 存在的要求。看看吧
  • 虽然可行,但我认为,当多次推送时,我仍然会遇到问题。
  • @SriVishnuTotakura 你不应该有任何问题。 git show 以所需的修订版(没有任何临时文件)输出您的文件,并且修订版本身是不可变的,所以我无法想象会出现什么问题。请提供更多关于您担心的问题的详细信息吗?
【解决方案3】:

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

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

【讨论】:

  • git archive 不是类似于复制文件吗?考虑到开发人员非常频繁地将代码推送到服务器上,有时甚至是同时推送,这是否是最佳选择?我正在寻找一个它们已经被 git 保存并且我可以访问的位置。
  • @SriVishnuTotakura 您不能指望任何此类目录位置与远程存储库相关联,因此上面的挂钩会将与提交相关联的目录树的状态复制到临时位置,以便您的检查器或验证应用程序可以完成它的工作。 Git 更新是原子的,所以钩子不必担心并发推送。如果要跟踪的目录树很大,比如 1+ GiB,您可能会有顾虑,但即便如此,我还是建议重新组织您的存储库结构。
【解决方案4】:

您可能需要编写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 保存并且我可以访问的位置。
猜你喜欢
  • 2022-01-02
  • 2017-12-30
  • 1970-01-01
  • 2015-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-28
  • 2012-02-17
相关资源
最近更新 更多