接收前和接收后挂钩中的Git存放未按预期工作

时间:2019-12-23 17:28:24

标签: git githooks git-stash

一点背景

我正在开发一种流程,该流程将允许人们访问公共环境。 我的想法是要有一个只能放入的存储库,而不是一个裸露的存储库,因为我希望人们能够阅读它。我们称之为 COMMON
这是因为我不希望人们必须克隆存储库才能访问数据。

要做到这一点,我具有以下结构:

  1. 本地存储库:想要开发流程的人员可以在其中执行修改,提交并推送到远程存储库
  2. 远程存储库:裸存储库,也用作网关。每当推送到该裸存储库时,它都会重新执行到COMMON存储库
  3. COMMON信息库:公共的“读取”目录。只有推送才能修改其历史记录,甚至禁止提交(通过预提交钩子)

问题

COMMON存储库不应有任何直接修改。但是,由于有多个用户在使用sudoers,所以我不能100%确保有人不会直接对其进行修改。
或者,相应地,对于Murphy,我100%确信有人有一天会在COMMON资源库中本地修改某些内容...

为了确保当有人正确地将其推入远程目录并因此推入COMMON存储库时没有冲突,我考虑了以下用于COMMON的钩子:

  • 预接收
#!/bin/bash
#Stash local modifications if there are ones to avoid conflicts
echo "## Local modifications on the Product-Line are stashed with the tag: 'local modifications @ $(date)' "
git stash push -m "local modifications @ $(date)"
exit 0
  • 接收后
#!/bin/bash
#Checks out the the branch that contains the last commit
git checkout $( git log --branches -1 --pretty=format:'%D' | sed  -e 's/.*-> //g' -e 's/.*, //g' )
#Forces synchronization with the last commit
git stash
git stash drop stash@{0}

exit 0

但是,隐藏命令似乎无法按预期运行。在本地存储库中进行推送时返回的日志如下:

$> echo toto >> stash_test ; git commit -am "test" ; git push
[tbranch f6e8fbb] test
 1 file changed, 1 insertion(+)
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 48 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 351 bytes | 351.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: remote: ## Local modifications on the Product-Line are stashed with the tag: 'local modifications @ Mon Dec 23 17:44:59 CET 2019'         
remote: remote: No local changes to save        
remote: remote: Already on 'tbranch'        
remote: remote: M   stash_test        
remote: remote: Saved working directory and index state WIP on tbranch: f6e8fbb test        
remote: remote: stash@{0}: WIP on tbranch: f6e8fbb test        
remote: remote: Dropped stash@{0} (b29b76ceb2b2dfd2fcf6d0577c7200e517641328)        
remote: To ../pl
remote:    f75b5d1..f6e8fbb  tbranch -> tbranch
To /home/usr/TEMP/remote
   f75b5d1..f6e8fbb  tbranch -> tbranch

这里有两个问题:

  • 为了测试COMMON存储库中的自动隐藏,我修改了一个文件。但是,当预接收挂钩到达git stash push -m "msg"时,它将返回No local changes to save
  • 在接收后挂钩中,git stashgit stash drop stash@{0}应该模拟对当前分支的最后一次提交的更新。在日志中,似乎该操作有效,但是,如果我在COMMON信息库中获得了状态,则可以获得:
On branch tbranch
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   stash_test
    modified:   test

  • stash_test是上次提交修改的文件
  • test是在本地修改以测试所有过程的文件

双龙效应:  1. git stash list为空,其中应包含test的修改。但是,该日志与其保持一致,因为它没有将test隐藏在预接收钩子中
 2. stash_test未更新为最后一次提交...这里很奇怪,因为日志表明一切正常...

我迷路的地方是我尝试手动执行这些步骤,并且...按预期工作。
但是我在git管理方面还很陌生,所以我可能会错过一些东西...

问题

  • git stash命令在钩子中是否表现出令人讨厌的行为,使我无法做我想做的事情?
  • 还有另一种(也许更好)的方式来实现我想要做的事吗?

编辑:确定了根本原因

执行挂钩时,它位于我的COMMON目录的.git目录中。 因此,git stash无法检测到修改。
如果在预接收挂钩中执行git status --porcelain,则会得到:

?? HEAD
?? ORIG_HEAD
?? config
?? description
?? gitk.cache
?? hooks/applypatch-msg.sample
?? hooks/commit-msg.sample
?? hooks/fsmonitor-watchman.sample
?? hooks/post-receive
?? hooks/post-update.sample
?? hooks/pre-applypatch.sample
?? hooks/pre-commit
?? hooks/pre-commit.sample
?? hooks/pre-push.sample
?? hooks/pre-rebase.sample
?? hooks/pre-receive
?? hooks/pre-receive.sample
...

这是预期的,因为.git dir的内容不受版本控制。

实际上,即使@knittl建议使用git reset --hard也不起作用,因为它不是在common的“ work”目录中完成的。

我想到的简单解决方法是使用以下命令将操作封装在钩子中:

pwd # returns COMMON/.git
pushd .. > /dev/null
pwd # returns COMMON
#hook operations
popd > /dev/null

在钩子中使用pwd命令进行检查我终于在正确的目录中执行存储/重置了。
但是,执行此操作时,远程消息将返回我:

remote: remote: fatal: not a git repository: '.'        

所以现在我有点困惑,因为我实际上是我的COMMON的根,所以应该将其视为git存储库...

即使按照@knittl和@torek的建议,这也不是部署某些东西的黄金方式,我还是想了解为什么存在这些限制以及是否存在覆盖它们的方法

EDIT2:藏在预提交中

在pre-commit钩子中,我执行了一个存储操作以保存本地修改并中止提交,因此“鲁udo的sudoer”无法提交其更改,但可以稍后选择其修改。 在此钩子中,git stash的执行预期。

我想这个问题与以下事实有关:接收前/接收后挂钩是服务器端挂钩,通常只应用于裸仓库。

1 个答案:

答案 0 :(得分:0)

我终于发现piece of documentation困扰着我:

  

在Git调用一个钩子之前,它将其工作目录更改为裸存储库中的$ GIT_DIR或非裸存储库中工作树的根。推送(预接收,更新,接收后,更新,推送到签出)期间触发的钩子是例外,它们总是在$ GIT_DIR中执行。

因此,GIT_DIR环境变量使我的钩子在COMMON / .git中运行。 因此,为了覆盖,我终于将当前工作目录更改为COMMON并设置了GIT_DIR=$(pwd -P)/.git
如果未以这种方式设置GIT_DIR,则git不会将COMMON视为git目录(因为默认情况下,GIT_DIR='.'且git会在较高级别中查找.git目录)

作为参考,我的接收后钩子最终看起来像:

#!/bin/bash

# Make sure that we are on the branch containing the last commit
git checkout -f $( git log --branches -1 --pretty=format:'%D' | sed  -e 's/.*-> //g' -e 's/.*, //g' )

# Forces synchronization with the last commit by discarding the local modifications. A bit trickerish but it works...
cd ..
export GIT_DIR=$(\pwd -P)/.git
git reset --hard

exit 0