Git push 时如何进行硬重置

16
我有一个位于我正在推送到的远程仓库中的post-receive hook脚本,该脚本执行了一个git reset --hard命令。
类似这样:
$ git push opal
Counting objects: 74, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (45/45), done.
Writing objects: 100% (53/53), 16.68 KiB, done.
Total 53 (delta 20), reused 0 (delta 0)
remote: warning: updating the current branch
remote: HEAD is now at 88f1e35 tweak lavalamp styles

我不理解的是,这里远程显示头部现在在XXX位置,但当我登录到服务器时,远程的工作副本根本没有更新!

有什么想法吗?


当你在使用钩子脚本时遇到问题,你应该真正地发布它。 - Chris Johnsen
Chris,我的post-receive脚本只包含了那一行。不需要发布。 - David
3个回答

25
问题在于Git命令在钩子脚本环境和正常环境中的行为差异。首先,钩子脚本以Git目录本身(即非裸库的.git/目录)作为其当前工作目录运行。其次,钩子脚本在设置了指向Git存储库的GIT_DIR环境变量后运行(同样是非裸库的.git/目录)。通常情况下,如果您尝试从.git/目录中运行git reset --hard,它将会显示以下错误信息:
fatal: This operation must be run in a work tree

但是当GIT_DIR被设置时,Git命令会假定当前目录是工作树。由于挂钩运行时的当前目录是 .git / 目录,因此您的 git reset --hard 实际上是直接将工作树文件“检出”到 .git / 而不是其父目录(即,您现在在 .git / 目录中拥有版本控制内容的副本)。
希望您的存储库中没有与Git在Git存储库本身中使用的路径名相重合的版本控制内容。如果它们相重合,则您的 git reset --hard 将覆盖存储库的某些内部结构,并且您可能需要从其他存储库重新克隆它。如果您确信版本控制内容与Git的内部路径名不冲突,则可以使用以下方法进行清理:
# make a backup of your repository first!
(cd .git && GIT_DIR=$PWD git ls-files -cz | xargs -0 rm)

这将仅删除当前跟踪的文件(它将保留已被删除但曾在活动的破损钩子期间被追踪在tip提交中的文件)。


一种解决方法是在调用Git命令之前,将当前工作目录更改为正常的工作树,并取消设置GIT_DIR和GIT_WORK_TREE。

test "${PWD%/.git}" != "$PWD" && cd .. 
unset GIT_DIR GIT_WORK_TREE
# you can now safely use Git commands

另一种解决方案是显式重置GIT_DIR,设置GIT_WORK_TREE并在其中更改目录。 Git FAQ “为什么我在“git push”后看不到远程repo中的更改?” 建议使用一个post-update脚本来实现这一点。链接的脚本也更安全,因为它在执行硬重置之前,如果索引或工作树已被修改,则进行了存储。

1
+1 哎呀,这太可怕了。更糟糕的是,我之前没有意识到在非裸仓库中运行不同钩子时,$PWD和$GIT_DIR不一致。(例如,在post-commit中,它们分别设置为工作树和.git) - Mark Longair
@Mark:嗯,我没有太多考虑“本地”钩子。看起来可能只是“远程”钩子(接收和更新变体)设置了这个有问题的环境。其他钩子大多针对需要工作树的活动,它们似乎以Git命令所需的合理配置运行,这是有道理的,因为这些钩子通常是从需要工作树的命令中调用的。 - Chris Johnsen
1
没错 - 我刚刚写了一篇博客文章,描述了GIT环境变量和每个钩子的当前目录,链接在这里:http://longair.net/blog/2011/04/09/missing-git-hooks-documentation/ - Mark Longair

18

简而言之,使用钩子一行代码:

git --git-dir=. --work-tree=$PWD/.. reset --hard

更加精确地说,编辑服务器上的文件.git/hooks/post-receive

#!/bin/sh
git --git-dir=. --work-tree=$PWD/.. reset --hard

将其设置为可执行文件:

chmod +x .git/hooks/post-receive

当从客户端推送到该仓库时,应显示类似于以下内容:

HEAD is now at abcd123 comment

1
在问题被提出之前,Git 中已经有了比改变环境更好的解决方案。 - digenishjkl

0

脚本可能没有运行。它不会在愚蠢的http服务器上运行,而是在ssh上运行。对于智能http服务器我不确定。

如果不是这个问题,您应该检查挂钩的“执行”权限(chmod +x .git/hooks/post-receive)。在此过程中,通常要检查所有权和权限。

如果看起来没问题,只需将日志语句包含在脚本的第一行(例如date“%T $0已执行”>> / tmp / debug_hook.log )中,并检查日志文件以查看是否有任何更新内容。

此外,推送实际上可能不做任何事情(全部更新)。在这种情况下,挂钩未被调用是有意义的。

如果所有这些都没有提供提示,请发布服务器上存在的.git/config(或至少部分内容)。 git log -1 HEAD在服务器上给出了预期的结果吗?您的挂钩脚本中是否包含任何可能覆盖GIT_DIR,GIT_WORK_TREE或GIT_INDEX_FILE的内容?


“HEAD is now at…”消息很可能来自于git checkoutgit reset。这两者都不是正常推送的一部分,因此钩子脚本几乎肯定正在运行。 - Chris Johnsen
你做了其他检查吗?特别是HEAD检查?根据另一篇帖子的阅读,你必须对“GIT_DIR”检查有积极的结果... - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接