git push:refs/heads/my/subbranch 存在,无法创建

172

在服务器上的一个仓库里创建子文件夹是否不可能?

如果我这样做:

git push origin dev/master 

一切正常工作。

但是如果我这样做:

git push origin dev/sub/master

我拿到了这个:

error: 'refs/heads/dev/sub' exists; cannot create 'refs/heads/dev/sub/master'

我使用 git branch -r 命令和 SSH 直接查询了一下,但是并没有发现 dev/sub 文件夹已经被创建。

出了什么问题?


1
git ls-remote origin 返回什么? - Ikke
1
开发/主控 dev/master 开发/主控/子控 dev/master-/sub - Muhammed Aydogan
13个回答

278

它并不是存在一个文件夹,而是存在一个分支。 (可能在某个地方涉及到一个文件夹/目录,或者——当引用被“打包”并停止作为目录中的文件存在时,也许没有这种情况。)

  • 如果分支b已经存在,则不能创建名为b/anything的分支。
  • 同样地,如果分支dev/b存在,则无法创建dev/b/c

这是Git内部的限制。 在这种情况下,远程的 origin 有一个名为 dev/sub 的分支(无论您是否拥有它,重要的是远程是否有它)。 要在 origin 上创建一个名为 dev/sub/master 的分支,必须首先删除 origin 上名为 dev/sub 的分支:

git push origin :dev/sub

当然,删除这个分支可能会删除那边的一些重要内容,所以请确保你知道你在做什么。通常情况下,你可能想先执行git fetch origin,抓取他们的 dev/sub 分支作为你的 origin/dev/sub。然后你可以创建一个本地的 dev/renamed-sub 分支指向相同的提交,再在远程创建 dev/renamed-sub 分支,删除远程的 dev/sub 分支,最后在远程创建 dev/sub/master 分支。


如果你能登录到远程主机(即托管 origin 的系统),那么你可以进入其代码库并直接重命名本地的 dev/sub 分支。(根据下面的评论,我怀疑那里也有一个已损坏的自动部署脚本,应该修复为仅部署“可部署”的分支,而不是推送的所有内容。但这只是我的猜测。)


1
我想我们只是在纠结术语问题。我所说的“内部限制”是指git可以克服它而不改变任何人使用git的方式。一个无限制的限制将是更根本的事情,比如,如果有人打破SHA1:即使这可以被克服(例如,转移到SHA256),40个字符的SHA-1的细节仍然暴露在大多数钩子中,因此这种限制泄漏到外部。 - torek
19
考虑到你可能想要拥有release/1.1release/1.2,以及release/1.1/hofix/prevent-upload-borkrelease/1.1/hotfix/assure-user-of-competence等分支,这个限制令人感到奇怪和恼火。此外,错误信息也没有直接说明这个限制,而是让人来到了这里(谢谢你)。但还是要感谢你提供简单明了的解释。 - Benjohn
2
"这是一个Git内部限制。" -- 好的,但为什么?是因为某些逻辑更容易以这种方式实现吗?还是因为某些树遍历?或者其他原因? - D. Kovács
2
@D.Kovács:通过禁止这种情况,Git 可以将引用哈希写入文件中,文件名是通过将引用视为路径名生成的。例如,如果 Git 同时允许 refs/tags/foorefs/tags/foo/2,它将无法这样做:它需要实现自己的键值存储。我认为 Git 最终将不得不实现自己的键值存储,但我不知道他们是否会在那时移除这个限制。 - torek
1
@ĐinhAnhHuy:我不知道有任何正式的文档描述这个限制。但是,子模块在完全独立的命名空间中:子模块名称和分支名称不应该像分支名称和文件名那样发生冲突(即使它们会发生冲突,但你可以添加一个消歧标识符:git checkout -- master 表示检出名为 master文件,而不是分支名称)。 - torek
显示剩余15条评论

100

我的仓库内存在关于我从未检出的不存在的远程分支的信息,导致我甚至无法进行拉取操作。我通过以下组合命令解决了此问题(感谢@torek):

  • git branch -r 列出本地拷贝的远程分支
  • git ls-remote 列出远程分支
  • git fetch --prune origin 更新本地拷贝的远程分支 (实际上对我没有帮助)
  • git remote prune origin 删除已删除的远程分支的信息 (这个命令解决了我的问题)

17
为了解决这个错误状态,我运行了一个命令git remote prune origin。实际上这个错误信息有误导性 - 在我的情况中,我试图推送一个"release/2.6.0"分支,但我已经删除了整个'refs/remotes/origin/release*'目录,然而Git一直抱怨无法更新引用'refs/remotes/origin/release/2.6.0':无法锁定引用'refs/remotes/origin/release/2.6.0':'refs/remotes/origin/release'存在;无法创建'refs/remotes/origin/release/2.6.0',每当我尝试push/pull/fetch时。 - conny
这里有更详细的解释来解决这种情况:https://dev59.com/Hqfja4cB1Zd3GeqPuVzr#76012255 - NeilG

63

对于我 -->

错误 =

fatal: cannot lock ref 'refs/heads/release/wl/2.3': 'refs/heads/release/wl' 
exists; cannot create 'refs/heads/release/wl/2.3'

解决方案=

$~ git update-ref -d refs/heads/release/wl
$~ git checkout release/wl/2.3

1
git update-ref -d refs/remotes... 解决了我的问题,因为'refs/remotes/origin/hotfix'已经存在,无法创建'refs/remotes/origin/hotfix/ISSUE...'。 - Eduardo
1
它也解决了我的问题。 - Mikhail Morfikov
我在尝试将本地分支从“bugfix/sort-rows-of-common-tables”重命名为“develop/sort-rows-of-common-tables”时,运行了“git branch -m develop/sort-rows-of-common-tables”,但出现了错误:“error: 'refs/heads/develop' exists; cannot create 'refs/heads/develop/sort-rows-of-common-tables'”。因为已经存在了“develop”分支。通过运行“git update-ref -d refs/heads/develop”然后再运行“git branch -m develop/sort-rows-of-common-tables”解决了这个问题,谢谢! - Tms91
然而,现在我无法将该分支推送到远程,因为在远程上它给我的错误与在本地给我的错误相同。请参见https://dev59.com/sGkMtIcB2Jgan1znSiRJ - Tms91
已解决该问题,特别是对于不想在公司代码库中执行“git remote prune origin”的人。 - Mehmet Burak Sayıcı

21

目前被接受的答案对我没有帮助,因为我没有要删除的远程仓库的ref - 它只存在于我的本地!所以如果你处于这种情况下,以下是需要做的:

我面临的问题如下:

$ git fetch origin
error: cannot lock ref 'refs/remotes/origin/fix/sub-branch': 
'refs/remotes/origin/fix' exists; cannot create 
'refs/remotes/origin/fix/sub-branch'
From <repo URL>
 ! [new branch]      fix/sub-branch          -> origin/fix/sub-branch
 (unable to update local ref)

我尝试了被采纳答案的建议,但得到了这个结果:

$ git push origin :fix
error: unable to delete 'fix': remote ref does not exist
error: failed to push some refs to <repo URL>

所以引用甚至不存在于 origin 上 - 很明显它只是挂在我的本地仓库的某个地方。 因此我运行了$ git remote show me,输出结果为:

Therefore, the reference was not even present on origin - it was obviously just hanging around somewhere in my local repository. So, I ran $ git remote show me, which generated:

Remote branches:
...
refs/remotes/origin/fix             stale (use 'git remote prune' to remove)
...

那么问题的解决方案也就变得清晰明了:

$ git remote prune origin
Pruning origin
URL: <redacted>
 * [pruned] origin/fix

有了这个,问题就消失了:

$ git fetch origin
remote: Counting objects: 5, done.
remote: Total 5 (delta 2), reused 2 (delta 2), pack-reused 3
Unpacking objects: 100% (5/5), done.
From <repo URL>
 * [new branch]      fix/sub-branch          -> origin/fix/sub-branch

3
没错,我遇到了和你一样的问题,执行 git remote prune origin 解决了它。谢谢! - ToddH

6

我知道这个问题已经有答案了,但对我没有用。 我不关心本地更改,因为它已经被推送了,但拉回时遇到问题。 在我的情况下,我们在"hotfix"分支和以父文件夹作为"hotfix"的文件夹系统之间进行了更改。

-- hotfix ----hotfix/1234_bug ----hotfix/3456_bug

所以我得到了以下错误:

Fetching from origin Error: cannot lock ref 'refs/remotes/origin/hotfix/1234_bug': 'refs/remotes/origin/hotfix' exists; cannot create 'refs/remotes/origin/hotfix'

在寻找类似错误后,我最终在讨论串这里找到了解决方案。

git remote prune origin

4
尝试使用以下命令进行修复:
git gc

为在当前存储库中运行一些维护任务,并通过调用git prunegit fsck --unreachable移除不可到达的对象。

更多信息请参阅:git help gc (文档) 和 git help prune (文档)。

git-gc - 清理不必要的文件并优化本地存储库

git-prune - 从对象数据库中修剪所有不可访问的对象


3

dev/sub重命名为dev/sub/something,然后你就可以创建dev/sub/master分支。


太棒了!谢谢! - Tamil Vendhan Kanagarasu

2
作为一个Windows用户,到目前为止,没有任何解决方案可以解决我的问题。我看到这个错误的原因是因为(使用OP的分支名称)我试图创建一个名为dev/sub的分支,但其他人已经创建了一个名为Dev的分支,正如我们所知,Windows具有不区分大小写的文件系统。

因此,当Windows尝试拉取dev/sub时,它首先尝试创建文件夹dev,但无法创建,因为Dev已经存在。

解决方法是使用git branch -d Dev && git push origin :Dev在本地和远程删除Dev分支。之后运行git pull就可以了。

另一个教训是,以后分支名称应该始终小写,以避免这种问题。


2

如果其它方法都失败了,检查一下你的代码库系统是否对分支名称有限制。在我的情况下,只能创建以SD-<number>开头的分支。任何其它命名都会得到一个通用的错误提示:

remote: error: cannot lock ref 'refs/heads/mybranch': 'refs/heads/mybranch/environment-variables' exists; cannot create 'refs/heads/mybranch'
To git.example.com:project/repository.git
 ! [remote rejected] mybranch -> mybranch (failed to update ref)
error: failed to push some refs to 'git@git.example.com:project/repository.git'

1
#!/usr/bin/env bash
echo "update-ref delete refs/tags"
log="git-update-ref-errors.log"
script="./git-update-ref-exist-tags-delete.sh"
git_command="git update-ref -d refs/tags"

echo "log errors from ${git_command} to ${log}"
${git_command} 2>&1 | > ${log}
echo "show errors to ${log}"
cat ${log}
echo create ${script}
touch ${script}
echo "add execute (+x) permissions to ${script}"
chmod +x ${script}
echo "generate ${script} from errors log ${log}"
${git_command} 2>&1 | grep 'exists' | sed -n "s:.*\: 'refs/tags/\(.*\)' exists;.*:git tag -d '\1':p" >> ${script}
echo "execute ${script}"
${script}

echo fetch
log="git-fetch-errors.log"
script="./git-fetch-exist-tags-delete.sh"
git_command="git fetch"
echo "log errors from ${git_command} to ${log}"
${git_command} 2>&1 | > ${log}
echo "show errors from ${log}"
cat ${log}
echo create ${script}
touch ${script}
echo "add execute (+x) permissions to ${script}"
chmod +x ${script}
echo "generate ${script} from errors log ${log}"
${git_command} 2>&1 | grep 'exists' | sed -n "s:.*\: 'refs/tags/\(.*\)' exists;.*:git tag -d '\1':p" >> ${script}
echo "execute ${script}"
${script}
git fetch

echo pull
log="git-pull-errors.log"
script="./git-pull-exist-tags-delete.sh"
git_command="git pull"
echo "log errors from ${git_command} to ${log}"
${git_command} 2>&1 | > ${log}
echo "show errors from ${log}"
cat ${log}
echo create ${script}
touch ${script}
echo "add execute (+x) permissions to ${script}"
chmod +x ${script}
echo "generate ${script} from errors log ${log}"
${git_command} 2>&1 | grep 'exists' | sed -n "s:.*\: 'refs/tags/\(.*\)' exists;.*:git tag -d '\1':p" >> ${script}
echo "execute ${script}"
${script}
git pull

echo push
log="git-push-errors.log"
script="./git-push-exist-tags-delete.sh"
git_command="git push"
echo "log errors from ${git_command} to ${log}"
${git_command} 2>&1 | > ${log}
echo "show errors from ${log}"
cat ${log}
echo create ${script}
touch ${script}
echo "add execute (+x) permissions to ${script}"
chmod +x ${script}
echo "generate ${script} from errors log ${log}"
${git_command} 2>&1 | grep 'exists' | sed -n "s:.*\: 'refs/tags/\(.*\)' exists;.*:git tag -d '\1':p" >> ${script}
echo "execute ${script}"
${script}
git push

上述脚本将错误记录到XXX-errors.log文件中,并通过生成并自动运行XXX-exist-tags-delete.sh来修复它们,使用以下命令从XXX-errors.log文件进行操作:
  1. git update-ref -d refs/tags
  2. git fetch
  3. git pull
  4. git push

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