Git推送错误:“[remote rejected] master -> master(分支当前被检出)”

1056

昨天我在stackoverflow上发布了一个问题,询问如何从一台计算机克隆Git仓库到另一台计算机,如何从另一台机器上执行'git clone'命令?

现在我成功地从源(192.168.1.2)克隆了一个Git仓库到目标(192.168.1.1)。

但是当我编辑一个文件后,执行git commit -a -m "test"git push时,在我的目标(192.168.1.1)上出现了这个错误:

git push                                                
hap@192.168.1.2's password: 
Counting objects: 21, done.
Compressing objects: 100% (11/11), done.
Writing objects: 100% (11/11), 1010 bytes, done.
Total 11 (delta 9), reused 0 (delta 0)
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.
error: 
error: To squelch this message and still keep the default behaviour, set
error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To git+ssh://hap@192.168.1.2/media/LINUXDATA/working
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to 'git+ssh://hap@192.168.1.2/media/LINUXDATA/working'

我使用两个不同版本的Git(1.7在远程,1.5在本地机器上)。这可能是一个原因吗?


5
有没有老手能够更改接受答案为https://dev59.com/LXE85IYBdhLWcg3wXCMv#9283833并将帖子移到存档或其他地方?或者更改所有权或其他什么? - Jacek Krysztofik
13
现在,使用Git 2.3.0(2015年2月)和“git config receive.denyCurrentBranch=updateInstead”,您实际上已经拥有了一种安全地推送到非裸仓库的方法。 - VonC
这是@stigi提到的书的新链接: https://git-scm.com/book/en/v1/Git-on-the-Server - Abdelilah El Aissaoui
可能是一个重复问题,参考这篇文章:当向远程存储库推送更改时,什么是Git警告消息? - Trevor Boyd Smith
但我不明白为什么以及它是如何工作的?它能够运行,是的,但仅此而已。 - Tilak Madichetti
显示剩余2条评论
33个回答

1217

远程仓库发出的错误信息 error: refusing to update checked out branch: refs/heads/master 意味着你试图将代码推送到远程非裸仓库,而该仓库在工作目录中当前已检出不同的代码。避免这个问题的最好方法是只向裸仓库推送 - 这个问题在裸仓库中根本不会出现。

你可以简单地将远程仓库转换为裸仓库(裸仓库中没有工作副本 - 文件夹只包含实际的仓库数据)。

在你的远程仓库文件夹中执行以下命令:

git config --bool core.bare true

删除该文件夹中除.git之外的所有文件,然后您就可以执行git push到远程仓库而不会出现任何错误。


7
我不确定你是想要在服务器还是客户端删除文件...所以我什么都没删除,在执行git config --bool core.bare true后问题解决了。有特定的原因需要删除一些文件吗?如果是这样,请更明确地说明需要删除哪些文件。 - Brian Vandenberg
26
这是最佳的答案,互联网上没有其他人提供过。我认为我们都搜索过同样的错误信息,读到这个答案后非常高兴。 - Sebastián Grignoli
43
尽管它获得了很多投票,但我认为这不是对那个具体问题的一个真正充分的答案。指导用户如何干净地创建一个裸仓库会好一些,但如果文件需要保持检出状态呢?比如当用户需要在两台计算机上使用同一仓库时。 - Nowhere man
9
将源代码库更改为裸库是过度的。正如@Robert所指出的那样,你需要做的只是将其推送到源代码库中的新分支:https://dev59.com/LXE85IYBdhLWcg3wXCMv#2933656。 - Dan Solovay
4
删除那个远程文件夹中的所有文件?你的意思是要删除整个网站吗?我觉得不是这样... - Oliver Tappin
显示剩余17条评论

769

我刚开始学习Git时也遇到了相同的错误。其他一些答案显然不适合初学者!

为了让您理解,我将使用非技术性词汇。发生的情况是您有两个仓库,一个是您最初创建的原始仓库,另一个是您刚刚创建的工作仓库。

现在您在工作仓库中并且正在使用master分支。但是同时您也“登录”到了原始仓库中的同一master分支。由于您登录了原始仓库,Git担心您可能会把原始仓库搞糟。所以您需要返回到原始仓库并执行git checkout someotherbranch,然后就可以顺利推送了。


77
非常有帮助,谢谢Robert。在我的情况下将其转换为裸库是没有意义的。只需要“取消激活”您想要推送的分支即可。很有道理。 - Eric
34
请创建一个虚拟分支(git checkout -b dummy)。 - Dror Cohen
16
这个比大多数被投票选中的答案要好得多,谢谢。虽然另一个答案也提出了不错的观点 :) - Ivan Ivanic
115
只是为了更清楚,对于被推送目标的仓库:git checkout -b tmp。然后在源仓库中执行:git push。然后回到目标仓库(可选):git checkout master; git branch -d tmp - Hari Honor
21
Hari的评论是最简单的解决方案。我只需要说,虽然git在许多方面都很棒,但对于那些从svn或其他任何版本控制系统转移而来的人来说,整个过程都异常不直观。 - MickeyfAgain_BeforeExitOfSO
显示剩余11条评论

132
错误信息描述了发生的情况。更现代化的 Git 版本拒绝通过 push 更新已检出的分支。在两个非裸库之间工作的最简单方法是始终通过 pull(或 fetch 和 merge)更新存储库,或者如果必须这样做,则通过将其推送到单独的分支(导入分支),然后将该分支合并到远程计算机上的主分支中。这个限制的原因是 push 操作仅在远程 Git 存储库上操作,它无法访问索引和工作树。因此,如果允许,在已检出的分支上进行 push 将使 HEAD 与远程存储库上的索引和工作树不一致。这会很容易意外提交撤消所有已推送更改的更改,并且也很难区分尚未提交的任何本地更改和由 push 移动 HEAD 引起的新 HEAD、索引和工作树之间的差异。

1
谢谢。那么我该如何解决我的问题呢? 在我的192盒子中,我执行了'$ cd (project-directory) $ git init $ (add some files) $ git add .',然后在我的191盒子中,我执行了'git clone '并编辑了一些文件,然后尝试'git push'。 - hap497
16
我在我的回答中描述了可能的选择。你可以前往192号盒子并从191号盒子获取(你可能想将191号盒子作为命名远程添加,看一下git remote add box191 <191url>),或者你可以从191号盒子推送到另一个命名分支(例如,git push origin master:refs/heads/upload),然后到192号盒子合并(例如,git merge upload)。 - CB Bailey
4
现在有了 Git 2.3.0(2015年2月)和 git config receive.denyCurrentBranch=updateInstead,你就有了一种安全的方法可以将内容推送到非裸存储库中。参考此链接:https://dev59.com/xXRA5IYBdhLWcg3w2xsI#28262104。现在不再需要选项2了。 - VonC

127

概要

不能将代码推送到当前被检出的存储库分支,因为这样可能会对使用该存储库的用户造成数据和历史记录的丢失。但是你可以将代码推送到同一存储库的任何其他分支。

由于裸仓库没有任何分支被检出,所以你可以随时将代码推送到裸仓库的任何分支。

有多种解决方案,具体取决于你的需求。

解决方案1:使用裸仓库

如建议所述,如果在某台机器上不需要工作目录,则可以转移到裸仓库。为了避免干扰存储库,你只需克隆它即可:

machine1$ cd ..
machine1$ mv repo repo.old
machine1$ git clone --bare repo.old repo

现在,您可以将所有内容推送到与之前相同的地址。

解决方案2:推送到未检出的分支

但是,如果您需要在远程<remote>上检出代码,则可以使用特殊分支进行推送。假设在本地存储库中,您已将远程命名为origin,并且您正在主分支上。则可以执行以下操作:

machine2$ git push origin master:master+machine2

当你在origin远程仓库时,你需要合并它:

machine1$ git merge master+machine2

问题的剖析

当检出一个分支时,提交将添加一个新的提交记录,并以当前分支的头为其父节点,并将该分支的头移动到该新提交。

因此,

A ← B
    ↑
[HEAD,branch1]

成为

A ← B ← C
        ↑
    [HEAD,branch1]

但如果有人在中间推送到那个分支,用户将会进入 Git 所谓的分离头指针模式:

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

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

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

假设此时用户切换到另一个分支,则此悬空的提交将成为Git 垃圾回收器 的非法对象。


3
对于“验尸报告”(autopsy)的一个技术修正:Git实际上不会在被推送到的代码库中分离HEADHEAD仍将指向该分支,而该分支将指向新推送的提交。但工作目录和索引/暂存区将不会被修改。现在正在处理被推送到的代码库的人必须努力从推送的效果中恢复过来:找出是否有任何更改需要保存,如果需要,则小心安排以保存它们。 - torek
为了给你的答案添加额外的信息,有很少的原因会让你想要一个人们推送到的远程仓库是非裸的,所以使用裸仓库是最好的解决方案。 - user456814
2
实际上,我发现很多情况下我需要推送到非裸仓库,而且我经常使用解决方案2。此外,在非裸仓库上推送的分支绝不是临时分支,它的作用类似于远程跟踪分支。 - Nowhere man
我在我的服务器上创建了一个GIT文件夹,用于托管由多个用户使用SourceTree创建的所有存储库。我在本地PC上创建了一个master存储库。我将remotes添加到服务器文件夹中,并尝试将我的存储库推送到服务器,以便另一个用户可以拉取/获取它进行工作。但是我遇到了以下错误:![remote rejected] master -> master(分支当前已检出)。我该如何解决? - SearchForKnowledge
2
@SearchForKnowledge,你意识到你正在问我正在回答的问题吗?! - Nowhere man

68
您可以通过编辑目标服务器上的.git/config来绕过此“限制”。 添加以下内容以允许即使已“签出” Git 存储库,也可以将其推送到存储库中:
[receive]
denyCurrentBranch = warn
或者
[receive]
denyCurrentBranch = false

第一个选项会在允许 push 操作的同时警告可能会破坏分支,而第二个选项只是默默地允许。

这可以用于将代码“部署”到不用于编辑的服务器上。这不是最好的方法,但是可以快速部署代码。


7
使用这种方式来“部署”代码到服务器是行不通的。即使你禁用警告以便能够推送到已检出的分支,工作副本也永远不会在推送时更新。 - Arrowmaster
10
我使用上述方法,使用 cd .. && git reset --hard 的 post-receive 钩子进行部署。这种方式有些取巧,但是可行。 - jholster
10
后面的命令行版本为 git config receive.denyCurrentBranch warn,意思不变,更通俗易懂。 - Andre Holzner
6
这应该是被接受的答案。我们中有些人知道自己在做什么,不是Git的初学者。这是那些人的答案。 - Qix - MONICA WAS MISTREATED
2
你应该将远程仓库转换为裸仓库,而不是使用 denyCurrentBranch = false 等 hack。这样整个问题就不会再发生了。 - Mikko Rantalainen
显示剩余4条评论

58

git config --local receive.denyCurrentBranch updateInstead

https://github.com/git/git/blob/v2.3.0/Documentation/config.txt#L2155

在服务器存储库上使用它,如果没有未跟踪的覆盖,它还会更新工作树。

它在Git 2.3中被添加,正如VonC在评论中所述

我已经编译了Git 2.3并尝试了一下。样例用法:

git init server
cd server
touch a
git add .
git commit -m 0
git config --local receive.denyCurrentBranch updateInstead

cd ..
git clone server local
cd local
touch b
git add .
git commit -m 1
git push origin master:master

cd ../server
ls

输出:

a
b

耶,b被推送了!


receive.denyCurrentBranch 的 updateInstead 值已不再有效。 - Jeter-work
@Xalorous 你有参考资料吗?https://github.com/git/git/blob/965798d1f2992a4bdadb81eba195a7d465b6454a/builtin/receive-pack.c#L108 - Ciro Santilli OurBigBook.com
我输入命令,git 给出错误提示:“receive.denyCurrentBranch 的值无效”。 - Jeter-work
@Xalorous,你的Git版本是多少?我在2.18上无法复现。 - Ciro Santilli OurBigBook.com
@Xalorous 好的!是的,许多新的Git功能不断推出,一定要检查你的版本;-) - Ciro Santilli OurBigBook.com
显示剩余3条评论

46

我喜欢在远程服务器上仍然保留一个可用的代码库的想法,但是我不想使用虚拟分支,而是喜欢使用:

git checkout --detach

这似乎是 Git 的一个非常新的功能 - 我正在使用 git 版本 1.7.7.4。


9
在提交你的更改之后,你可以使用命令 git checkout master 切换回主分支。只有这样你的更改才会被应用。 - MAZDAK

31

我曾经遇到了同样的问题。解决方法是,我使用Git push将代码传输到我的服务器上。我从不在服务器端更改代码,所以这是安全的。

你正在推送的存储库类型:

git config receive.denyCurrentBranch ignore

这将允许你在工作副本中更改存储库。

在运行Git push之后,转到远程机器并输入以下内容:

git checkout -f

这将使您推送的更改在远程计算机的工作副本中得到反映。

请注意,如果您在要推送的工作副本中进行更改,则这并不总是安全的。


非常感谢您的建议,我合并了您提到的 denyCurrentBranch 并将 git checkout -f 放在 hooks 文件夹中,就像 @jack-senechal 在这里发布的那样。 - Éderson T. Szlachta
有没有办法让文件出现在远程仓库上,而无需前往远程仓库并执行该命令?即,通过本地命令实现。 - Royi

25

您可以重新创建服务器代码库,并将本地的主分支 master 推送到服务器的主分支 master。

在您的远程服务器上:


mkdir myrepo.git
cd myrepo.git
git init --bare

好的,从你本地的分支开始:

git push origin master:master

4
谢谢,这对我来说是个解决方案,因为我在远程服务器上遗漏了“--bare”。似乎这个问题的答案取决于你是否将远程服务器回购作为工作目录使用,对于后者情况来说,这就是正确的答案。 - Cas
2
我不明白:S 我试图创建和克隆一个裸仓库,但是克隆没有下载任何东西,推送也没有上传任何东西,所以这不起作用... :-) 我阅读了关于裸仓库的教程,但它们说裸仓库不包含任何文件...这绝对不是我要找的... - inf3rno

25

可能导致该问题的原因:

这种情况通常发生在你想要编写一个小程序时。你要修改一些已经正常运行的东西,所以你施放了三级术法——永久撤销的魔法:

machine1:~/proj1> git init

然后你开始添加/提交。但是接着,该项目变得更加复杂,你想要在另一台计算机上(比如你的家用电脑或笔记本电脑)上工作,所以你会执行以下操作:

machine2:~> git clone ssh://machine1/~/proj1
然后,你克隆了所有代码,看起来一切都不错,于是你开始在machine2上编写代码。但是......你试图从machine2推送提交,却收到了标题中的警告消息。
出现此消息的原因是因为你从中拉取的git仓库本意只是用于machine1上的那个文件夹。你可以成功地从其中克隆,但是推送可能会导致问题。在两个不同位置管理代码的“正确”方法是使用“裸”仓库,就像已经建议的那样。裸仓库并不是为在其中进行任何工作而设计的,它旨在协调来自多个来源的提交。这就是为什么获得最高评价的答案建议在执行 git config --bool core.bare true 之后删除除.git文件夹以外的所有文件/文件夹的原因。
澄清获得最高评价的答案:那个答案的许多评论都说“我没有从machine1中删除非.git文件,但我仍然能够从machine2提交”。没错。然而,这些其他文件现在完全与git仓库“分离”了。去尝试在其中执行 git status ,你应该会看到类似 “fatal: This operation must be run in a work tree” 的东西。所以要求删除文件并不是为了让machine2中的提交工作,而是为了避免你困惑并认为git仍在跟踪那些文件。但是,如果你仍然想要在machine1上处理这些文件,删除它们就成了一个问题,不是吗? 那么,你真正应该做什么呢? 这取决于你计划从machine1和machine2上的开发量... - 如果你已经完成了从machine1的开发,并将所有开发转移到了machine2,请按照获得最高评价的答案所建议的做法: git config --bool core.bare true ,然后可选地删除除.git以外的所有文件/文件夹,因为它们未被跟踪且可能会引起混淆。 - 如果你在machine2上的工作只是一次性的,而且你不需要在那里继续开发,请不必担心裸仓库;只需从机器*2* ftp/rsync/scp/etc.上传你的文件到machine*1*上原有的文件上,然后从machine *1*提交/推送,最后从machine *2*删除文件。其他人建议创建分支,但我认为如果你只想合并从另一台机器上进行的一次性开发,那么这有点麻烦。 - 如果你需要在machine1和machine2上继续开发,则需要正确设置事项。你需要将仓库转换为裸仓库,然后在machine1上克隆一个供你工作的副本。这样做可能最快的方法是:
machine1:~/proj1> git config --bool core.bare true
machine1:~/proj1> mv .git/ ../proj1.git
machine1:~/proj1> cd ..
machine1:~> rm -rf proj1
machine1:~> git clone proj1.git
machine1:~> cd proj1

非常重要:因为你将repo的位置从proj1移动到proj1.git,所以你需要在machine2上更新.git/config文件。完成之后,你可以从machine2提交你的更改。最后,我尝试将我的裸库放在一个中央位置,远离我的工作树(即不要将“proj1.git”放在与“proj1”相同的父文件夹中)。我建议你也这样做,但我希望保持以上步骤尽可能简单。


非常详细和有帮助。我的情况是最后一个,你的指示起了很好的作用。 - pojda
我在第三种情况下做了一些略微不同的事情:创建 git-server 目录;将 .git 文件夹移动到 git-server/proj.git 目录下,然后使用适当的 ssh:// 前缀在机器2上克隆 git-server/proj.git 到 1 和 2(或者使用 git remote origin ...)。这样,我将保留一个裸的主分支副本,就像通常在 GH 或其他 HTTP 服务器上一样,并且我将继续在两台机器上使用 push/pull。 - zakmck

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