使用Git中的receive.denyCurrentBranch会有什么后果?

57

我有一个Git代码库。我已经克隆了这个代码库并可以提交本地更改。当我将更改推送到服务器时,它可以正常工作。

一旦我创建了一个分支,我就会切换到该分支,提交我的工作,然后切换回主分支。然后,我将我的本地更改合并到主分支中。当我尝试将更改推送到服务器时,我会收到以下异常:

Welcome to Git (version 1.7.11-preview20120620)

Run 'git help git' to display the help index.
Run 'git help <command>' to display help for specific commands.

$ git push origin master:master
 Counting objects: 9, done.
 Delta compression using up to 4 threads.
 Compressing objects: 100% (7/7), done.
 Writing objects: 100% (8/8), 13.68 KiB, done.
 Total 8 (delta 2), reused 1 (delta 0)
 Unpacking objects: 100% (8/8), done.
 remote: error: refusing to update checked out branch: refs/heads/master
 remote: error: By default, updating the current branch in a non-bare repository
 remote: error: is denied, because it will make the index and work tree inconsistent
 remote: error: with what you pushed, and will require 'git reset --hard' to match
 remote: error: the work tree to HEAD.
 remote: error:
 remote: error: You can set 'receive.denyCurrentBranch' configuration variable to

 remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into

 remote: error: its current branch; however, this is not recommended unless you
 remote: error: arranged to update its work tree to match what you pushed in some

 remote: error: other way.
 remote: error:
 remote: error: To squelch this message and still keep the default behaviour, set

 remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
 To c:/jGit
 ! [remote rejected] master -> master (branch is currently checked out)
 error: failed to push some refs to 'c:/gitRepository'

一个解决方案是运行以下命令:

git config receive.denyCurrentBranch ignore

在使用这个选项后,程序可以正常运行,但我想知道为什么需要使用这个选项。这是唯一的选项吗?这样做会有什么后果?

我真正想做的是创建分支,将它们合并到主分支中,然后将更改推送到服务器。


1
receive.denyCurrentBranch 的默认值是什么? - lindhe
17
自Git 2.3.0(2015年2月)以来,实际上您现在可以使用“git config receive.denyCurrentBranch=updateInstead”安全地将内容推送到非裸库中。参考链接:https://dev59.com/xXRA5IYBdhLWcg3w2xsI#28262104。 - VonC
@VonC:但遗憾的是仍未在Windows上。 - ereOn
@ereOn 是的。正如我在 https://dev59.com/o3A75IYBdhLWcg3wuLjD#3144417 中提到的那样,他们正在开发一个更强大的“Git for Windows”,而不是当前的“msysgit”。 - VonC
3
@VonC是正确的,但没有方程式:git config receive.denyCurrentBranch updateInstead - Fanky
显示剩余2条评论
6个回答

32

为什么Git不允许您推送到非裸存储库

原帖作者说:

One solution is to run the following command:

git config receive.denyCurrentBranch ignore

After this it works, but I would like to know why I need to use this option. Is this the only option? What are the consequences of doing this?

作为我在类似问题的答案中指出的那样,自Git 1.6.2版本以来,默认情况下Git不允许您将内容推送到非裸仓库。这是因为git push命令仅更新远程仓库上的分支和HEAD引用。它没有更新非裸远程仓库中的工作副本和暂存区。
因此,当您在远程仓库中使用git status时,您会看到该仓库之前的状态仍然存在于工作副本(并在索引中暂存)中:
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   previous-state.txt

如果您查看第一次尝试使用默认的refuse值设置了receive.denyCurrentBranch时推送到非裸远程仓库时收到的错误消息,您会发现该消息基本上告诉您相同的内容。请注意保留HTML标签。
error: refusing to update checked out branch: refs/heads/master
error: By default, updating the current branch in a non-bare repository
error: is denied, because it will make the index and work tree inconsistent
error: with what you pushed, and will require 'git reset --hard' to match
error: the work tree to HEAD.
error:
error: You can set 'receive.denyCurrentBranch' configuration variable to
error: 'ignore' or 'warn' in the remote repository to allow pushing into
error: its current branch; however, this is not recommended unless you
error: arranged to update its work tree to match what you pushed in some
error: other way.

您应该只推送到裸的Git仓库

正如其他答案中指出的那样,您不应该将内容推送到非裸仓库,因为这样做会引起上述问题,而且Git本身也会提示您。

所以像这个答案所述,将现有的非裸仓库转换为裸仓库的简单方法是将其重新克隆为裸仓库:

git clone --bare old-repo

或者你可以尝试使用 core.bare 配置设置进行调整,具体请参考this answer


22
实际上,现在使用 Git 2.3.0(2015年2月)和命令 git config receive.denyCurrentBranch=updateInstead,你可以安全地将内容推送到非裸仓库。参见:https://dev59.com/xXRA5IYBdhLWcg3w2xsI#28262104 - VonC
2
@VonC 打错了:git config receive.denyCurrentBranch updateInstead - Fanky
@Fanky 是的,https://dev59.com/xXRA5IYBdhLWcg3w2xsI#28262104 有正确的语法。 - VonC

30

7
普通的 git 仓库有工作副本(即源文件)和隐藏的 .git 文件夹,其中存放了 git 相关内容。裸仓库只有 git 相关内容。该错误意味着你正在尝试推送到普通仓库,并且它的工作副本包含了你要推送的相同分支。服务器的 git 仓库不需要工作副本,因此请使用裸仓库。 - kan
1
我怀疑你已经在本地仓库中执行了 git config。请先尝试理解它的含义。 - kan
@kan 请帮我回答我的问题:http://stackoverflow.com/questions/28703141/how-to-check-in-repository-to-a-blank-folder-using-sourcetree - SearchForKnowledge
1
@kan 仍然不明白“git config receive.denyCurrentBranch ignore”的作用是什么?我为什么需要它? - Michael
@confile 在git打印的消息中已经很清楚地解释了:“因为它会使索引和工作树与您推送的内容不一致”。不确定您具体在问什么? - kan
显示剩余5条评论

13

我也遇到了同样的错误,需要将代码库作为开发测试页面在线运行(即保留非裸仓库)。幸运的是,通过以下命令序列来初始化代码库后,问题得以解决(自git 2.3起):

git init
git config --global user.email "your@mail.here"
git config --global user.name "Your Name"
git commit
git config receive.denyCurrentBranch updateInstead

如下所示: 无法将更改推送到 Git 存储库


5
你需要在服务器上有一个裸仓库,而不是一个带有检出工作树的仓库。Git告诉你它拒绝覆盖当前在服务器上检出的分支。
请参考此答案了解如何将服务器上的非裸仓库转换为裸仓库。

我已经尝试过这个,但没有任何区别。基本上,我已经执行了:git config --bool core.bare true。我该怎么办? - user1646481
2
你需要在服务器上的代码库上进行操作。此外,你需要删除服务器上已检出的工作树。你是否按照我提供的链接中的所有步骤进行了操作? - Carl-Eric Menzel

3

问题的解剖

当检出一个分支时,提交操作会创建一个新的提交记录,该提交记录的父节点是当前分支的头部,并将分支的头部移动到该新提交记录。

因此,

A ← B
    ↑
[HEAD,branch1]

变成

A ← B ← C
        ↑
    [HEAD,branch1]

但是如果在此期间有人向该分支推送内容,用户将会进入git中所谓的游离HEAD模式(detached head mode):

A ← B ← X
    ↑   ↑
[HEAD] [branch1]

现在用户不再处于branch1中,而且没有明确要求检出另一个分支。更糟糕的是,用户现在处于任何分支之外,任何新提交都将变为悬空状态:

     [HEAD]
        ↓
        C
      ↙
A ← B ← X
        ↑
       [branch1]

假设此时用户切换到另一个分支,那么这个悬空提交将成为 Git 垃圾回收器的目标。

你所描述的并不是分离头状态。分离头状态是指你直接检出到一个SHA,而不是一个分支。 - Andrew C
当版本推送到非裸仓库时,如果现有的工作区有一些修改,则仓库头将从主分支仓库中分离。需要应用合并或硬重置来统一分支。 - Harshal Doshi Jain
我不相信将 receive.denyCurrentBranch 设置为 warnignore 会使远程仓库进入分离的 head 状态(这也是我想要的)。 相反,推送到远程存储库似乎会更新分支引用,这意味着远程存储库中的提交会撤消推送时所做的更改。 - jamesdlin

0

我认为非裸仓库在配置git的更新钩子后,可以在推送完成后从仓库本身部署时非常有用。只是不要忘记重置仓库。如果你错过了这一步,文件将无法跟踪实际状态...


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