【问题标题】:Why can't I push to a checked out branch of a non-bare repository?为什么我不能推送到非裸存储库的签出分支?
【发布时间】:2017-01-29 16:59:15
【问题描述】:

我对自己创建的场景感到困惑。我在 Github 上创建了一个存储库(我们称之为 A)并将代码推送到它。之后,我将该存储库克隆到我的本地(我们称之为 B),这样我本地的来源就是远程存储库 A。

现在我从本地 B 克隆以创建另一个本地实例 C。现在我将 C 的远程来源作为 repo B 并且 C 的上游是 A。

A → B → C

这类似于分叉,但这里我在客户端而不是服务器端创建了克隆。

现在,如果我尝试使用从 C 到其原点 B 的推送:

git push origin 

然后我收到一条错误消息,指出我无法推送到非裸存储库。我知道推送到非裸存储库可能会导致远程提交丢失,而本地不存在。

但是,这种情况与我将代码从 B 推送到 A 的情况不同吗?

我很困惑,如果 B 到 A 是可能的,那为什么不是 C 到 B。

为了合并到 A,我们可以推送到上游:

git push upstream

【问题讨论】:

  • 我可以推送到非裸存储库(只要我不推送到当前在非裸远程存储库的工作树中签出的分支)。你确定这不是正在发生的事情吗?在这种情况下,我会收到一条很长、信息量很大的错误消息以及:! [远程拒绝] HEAD -> master(当前已签出分支)
  • 是的@Alderath,你是对的。我收到了同样的信息。我无法理解的是,它与从 B 推到 A 有什么不同,我可以在没有任何警告的情况下做到这一点。
  • 我收到一条描述原因的错误消息。如果它允许您将更改推送到非裸存储库的签出分支,那么这将导致该存储库的工作树与推送的分支不一致。
  • 今天我了解了这个 git 的行为。想想 B,就好像它是在同一个项目中工作的同事。您不会将您的更改推送到他正在处理的同一个回购/分支! Git 不允许你这样做。如果你真的想推到那里(也许要备份你的工作),请在 B 中执行 git config [--global] receive.denyCurrentBranch ignore 以允许它。另一方面,您可以推送到 A,因为 GitHub 存储库是裸露的。没有人可以在那里编辑文件,因此您永远不会通过推送您的更改来破坏更改。

标签: git github dvcs git-bare git-non-bare-repository


【解决方案1】:

一些基本的东西

  • 无论何时git clone,从开发人员的角度来看,您都需要处理刚刚克隆的代码。所以 GIT 给了你一个“工作树”来工作。它之所以被称为一棵树,是因为当您考虑您所做的所有提交和分支并放在图表上时,它类似于一棵树。

  • 克隆的存储库称为非裸存储库。要创建一个非裸存储库,您只需执行一个简单的git init - 这是原始程序员开始使用 GIT 跟踪克隆代码时所做的。您也可以将它克隆到一个裸存储库,但它的详细信息和有用性应该是它自己的答案。

  • 裸存储库 不包含工作树。它仅用于存储您的代码 - 如果您愿意,它是由 GIT 管理的代码的服务器。要创建一个裸存储库,您只需执行简单的git init --bare name_of_repository.git。它将创建一个名为 name_of_repository.git 的目录,其中包含 GIT 所需的所有文件。 git 扩展名只是一个使用的约定;它不是必需的,可以是任何东西,也可以什么都不是。

  • 在 GIT 中有一个类似于 指针 的东西,称为 HEAD。它指向您工作的分支中处于活动状态的最新提交,无论是裸存储库还是非裸存储库。

  • 分支就像您刚刚从远程存储库中提取的代码的“不同副本”(可能有不同的修改或没有)。它具有开发人员认为合适的任何名称。它们很有用,因为您可以处理不同的功能或解决不同的问题,而不必担心您或其他人正在开发的当前代码。之后,您可以随时将所有内容合并到主分支 - 通常是 master - 然后删除那些不再需要的合并分支。

  • GIT 尽力避免不同位置或分支的文件版本之间出现问题。所以他不会让你git push 在某些情况下至少可以说是混乱。 GIT 永远不会出错,因为它要求您检查、更改或强制您正在做的事情。所以任何错误都不是 GIT 的错,而是你的错。

了解情况

让我们考虑以下几点:

  • A 存储库是一个裸存储库。 B 和 C 存储库都是非裸存储库。这意味着 A 没有工作目录,仅用于存储。 B 和 C 用于您需要做的工作。
  • 一般来说,您(通常)有分支机构。通常初学者不会创建分支,因为他正在学习,甚至可能还不知道分支——即使它们出于许多原因很有用。所以他几乎总是在一个“主”分支上——默认分支。

话虽如此,假设您修改了 B 中的一些文件。您可以多次执行git commit,最后甚至是git push。或者你什么都不做。但是你在 master 分支上。

稍后,您在 C 中修改文件。然后您提交并尝试推送到 A。记住:您在 C 的主分支上。git push 有效!

然后,您也尝试将 C 推到 B。 它不起作用。

结果:GIT 会(不) 真正地尖叫,警告你试图defile(更新)主分支它的 HEAD 指向另一个提交的非裸存储库 B!如果他让你推送,你会弄乱 GIT 在存储库 B 上跟踪的历史记录。它不会再知道 B 发生了什么!您甚至可以用 B 上的相同名称覆盖该分支上的修改!所以不,如果两者都是非裸存储库,则不能从 C 推送到 B!

现在怎么办?!我的世界会这样结束吗?!伟大的能做到什么?! GIT怎么可能无视他主人的意愿?!这是纯粹的异端邪说!

解决方案

1 - 在 B 上有两个分支 - 主分支和临时分支。并使头部指向临时分支。示例:

cd B                  # change to B's working directory
git branch temp       # create 'temp' branch
git checkout temp     # change from master branch to branch temp

2 - 现在,移动到 C 工作目录(简称​​wd)并拉取 B 的内容。请注意,我认为 B 是远程C 的(正如你在你的案例中提到的):

cd ../C               # change to C's working directory
git pull B master     # pulls B's modifications to C

3 - 在 C 中修改您的文件。请注意,您在 C 的主分支上。然后,在提交 C 的修改后,将其推送给 B 的 master:

git push B master     # pushes C's mods to B's master branch

4 - 现在回到 B wd 并使 HEAD 指向 master 分支:

cd ../B               # change to B's working directory
git checkout master   # change from temp branch to branch master

5 - 如果您不再使用临时分支,可以将其删除:

git branch -d temp    # delete branch temp

6 - 如果您在 C 中进行新的修改,则不需要执行第 4 步和第 5 步。如果您这样做,任何时候您希望在 C 中进行修改,您将需要事先完成第 1 步和第 2 步。

这解决了你的问题! 可能...

澄清和强化

  • git branch name_of_the_branch 创建一个新分支;
  • git checkout name_of_the_branch 使 HEAD 指向这个新分支;
  • git checkout -b name_of_the_branch 创建一个分支并在一个命令中使 HEAD 指向它。我使用了较长的方法,因为您也应该知道较长的方法;
  • 如前所述,如果以后要使用该分支,请不要删除它。但我确实建议这样做,以避免在拉/推甚至合并时两个存储库中的 临时分支 出现问题。根据需要创建临时分支 - 使用终端历史非常容易 - 然后将其删除;

【讨论】:

    【解决方案2】:

    如果你使用

    git clone --bare http://github.com/xxx/A B
    

    然后你可以从 C 推到 B。你当然不能 用 B 来工作,因为它是光秃秃的。

    我还是不明白你为什么想要“额外的”B repo?

    【讨论】:

    • 这只是一个我想试验的随机设置。我想了解为什么在这种情况下行为会发生变化。
    • 在您的设置中,您可以在 B 和 C 之间合并,然后从 B 推送到 A 或从 C 推送到 A。
    猜你喜欢
    • 2011-09-03
    • 2013-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-02
    • 1970-01-01
    • 2022-01-27
    • 2011-04-29
    相关资源
    最近更新 更多