我在远程仓库上编写了一个git预接收挂钩,以确保推送的代码符合我们公司的内部指南。
我能够在触发预接收挂钩时找到要检查的所有文件,但是,我没有这些文件的路径可以使用普通文件操作打开它们(例如cat git_working_directory/file_name
会抛出No such file or directory
错误)。 验证代码的应用程序需要文件路径作为参数,以便它可以打开文件并运行其检查。
考虑这种情况:开发人员创建了一个新文件并将其推送到服务器并触发了预接收挂钩。在此阶段,新文件不会保存到远程工作目录中,因为预接收挂钩仍在运行。
我想知道是否有一个临时位置,文件一旦被推送就会保存在git中,以便我可以将该目录传递给应用程序来运行检查?
更新
我可以结账到一个临时位置并在那里运行检查,这可能是一个选项,但考虑到开发人员经常推动的事实,有时甚至在同一时间和回购非常大,这个选项不是似乎是可行的。我正在寻找一个解决方案,我可以使用该文件的路径,如果它以某种方式可用。
答案 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比较(不是全零)。这样,您将能够检查所有推送到新分支的对象。
尽管此空对象哈希很有用,但在使用它时要小心。如果要推送的提交/分支很大,则可能会增加计算量,因为您正在检查其中的每个对象。