为什么我不能推送到非裸存储库的签出分支?
Why can't I push to a checked out branch of a non-bare repository?
我对自己创建的场景感到困惑。我在 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
如果你使用
git clone --bare http://github.com/xxx/A B
那你就可以从C推到B了,你当然不能
将 B 用于工作,因为它是空的。
我还是不明白你为什么想要 "extra" B 存储库?
一些基本的东西
从开发人员的角度来看,无论何时 git clone
,您都希望处理刚刚克隆的代码。所以 GIT 给了你一个 "working tree" 来处理。它被称为一棵树,因为当您考虑您所做的所有提交和分支并放在图表上时,它类似于一棵树。
克隆的存储库称为非裸存储库。要创建一个非裸存储库,您只需执行一个简单的 git init
- 这就是原始程序员使用 GIT 开始跟踪克隆代码所做的。您也可以将它克隆到 裸存储库 但它的详细信息和用途应该是关于它的适当问题的答案。
裸存储库 不包含工作树。它仅用于存储您的代码 - 如果您愿意,可以使用 GIT 管理的代码服务器。要创建裸存储库,您只需执行一个简单的 git init --bare name_of_repository.git
。它将创建一个名为 name_of_repository.git 的目录,其中包含 GIT 所需的所有文件。 git 扩展只是一个约定;它不是必需的,可以是任何东西,也可以什么都不是。
在GIT中有一个类似于指针的东西叫做HEAD。它指向在您工作的分支中处于活动状态的最新提交,无论是裸仓库还是非裸仓库。
Branches 就像 'different copies' 您刚刚从远程存储库中提取的代码(可能有不同的修改,也可能没有)。它有开发人员认为合适的任何名称。它们很有用,因为您可以处理不同的功能或解决不同的问题,而不必担心您或其他人正在开发的当前代码。之后,您可以随时将所有内容合并到主分支 - 通常是 master - 然后删除那些不再需要的合并分支。
GIT 尽最大努力 避免文件版本之间的问题 不同的位置或分支机构。所以他不允许你 git push
在某些情况下它至少可以说是 混乱 。 GIT 永远不会错,因为它要求您检查、更改或强制执行您正在做的事情。所以任何错误都不是 GIT 的错,而只是你的错。
了解情况
让我们考虑以下问题:
- A 存储库是一个裸存储库。 B 和 C 存储库都是非裸存储库。这意味着 A 没有工作目录,仅用于存储。 B 和 C 用于您需要做的工作。
- 一般来说,您(通常)有分支机构。通常情况下,初学者不会创建分支,因为他正在学习,甚至可能还不知道分支——尽管它们出于多种原因很有用。所以他几乎总是在 'master' 分支上——默认分支。
话虽如此,假设您修改了 B 中的一些文件。您可以根据需要多次执行 git commit
,甚至在最后执行 git push
。或者你什么都不做。但是你在 master 分支上。
稍后,您在 C 中修改文件。您提交并尝试推送到 A。记住:您在 C 的 master 分支上。git push
有效!
然后,您也尝试将 C 推送到 B。 没用。
结果:GIT将(不是)字面上尖叫,警告你正试图defile(更新)非裸存储库 B 的 master 分支,它的 HEAD 指向另一个提交!如果他让你推送,你会弄乱存储库 B 上 GIT 跟踪的历史记录。它不会再知道 B 发生了什么!您甚至可以覆盖 B 上具有相同名称的分支上的修改!所以不,如果两者都是非裸存储库,则不能从 C 推送到 B!
现在怎么办?!我的世界会就这样结束吗?!伟大的我能做什么?! GIT怎么可能不顾主人的意愿?!这纯粹是异端邪说!
解决方案
1 - 在 B 上有两个分支 - 主分支和一个临时分支。并使 head 指向临时分支。示例:
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 指向它。我使用了更长的方法,因为你应该也知道更长的方法;
- 如前所述,如果您以后要使用它,请不要删除该分支。但我确实建议这样做以避免在 pulling/pushing 甚至合并时两个存储库中的 临时分支 出现问题。根据需要创建临时分支 - 使用终端历史记录非常容易 - 然后删除它;
我对自己创建的场景感到困惑。我在 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
如果你使用
git clone --bare http://github.com/xxx/A B
那你就可以从C推到B了,你当然不能 将 B 用于工作,因为它是空的。
我还是不明白你为什么想要 "extra" B 存储库?
一些基本的东西
从开发人员的角度来看,无论何时
git clone
,您都希望处理刚刚克隆的代码。所以 GIT 给了你一个 "working tree" 来处理。它被称为一棵树,因为当您考虑您所做的所有提交和分支并放在图表上时,它类似于一棵树。克隆的存储库称为非裸存储库。要创建一个非裸存储库,您只需执行一个简单的
git init
- 这就是原始程序员使用 GIT 开始跟踪克隆代码所做的。您也可以将它克隆到 裸存储库 但它的详细信息和用途应该是关于它的适当问题的答案。裸存储库 不包含工作树。它仅用于存储您的代码 - 如果您愿意,可以使用 GIT 管理的代码服务器。要创建裸存储库,您只需执行一个简单的
git init --bare name_of_repository.git
。它将创建一个名为 name_of_repository.git 的目录,其中包含 GIT 所需的所有文件。 git 扩展只是一个约定;它不是必需的,可以是任何东西,也可以什么都不是。在GIT中有一个类似于指针的东西叫做HEAD。它指向在您工作的分支中处于活动状态的最新提交,无论是裸仓库还是非裸仓库。
Branches 就像 'different copies' 您刚刚从远程存储库中提取的代码(可能有不同的修改,也可能没有)。它有开发人员认为合适的任何名称。它们很有用,因为您可以处理不同的功能或解决不同的问题,而不必担心您或其他人正在开发的当前代码。之后,您可以随时将所有内容合并到主分支 - 通常是 master - 然后删除那些不再需要的合并分支。
GIT 尽最大努力 避免文件版本之间的问题 不同的位置或分支机构。所以他不允许你
git push
在某些情况下它至少可以说是 混乱 。 GIT 永远不会错,因为它要求您检查、更改或强制执行您正在做的事情。所以任何错误都不是 GIT 的错,而只是你的错。
了解情况
让我们考虑以下问题:
- A 存储库是一个裸存储库。 B 和 C 存储库都是非裸存储库。这意味着 A 没有工作目录,仅用于存储。 B 和 C 用于您需要做的工作。
- 一般来说,您(通常)有分支机构。通常情况下,初学者不会创建分支,因为他正在学习,甚至可能还不知道分支——尽管它们出于多种原因很有用。所以他几乎总是在 'master' 分支上——默认分支。
话虽如此,假设您修改了 B 中的一些文件。您可以根据需要多次执行 git commit
,甚至在最后执行 git push
。或者你什么都不做。但是你在 master 分支上。
稍后,您在 C 中修改文件。您提交并尝试推送到 A。记住:您在 C 的 master 分支上。git push
有效!
然后,您也尝试将 C 推送到 B。 没用。
结果:GIT将(不是)字面上尖叫,警告你正试图defile(更新)非裸存储库 B 的 master 分支,它的 HEAD 指向另一个提交!如果他让你推送,你会弄乱存储库 B 上 GIT 跟踪的历史记录。它不会再知道 B 发生了什么!您甚至可以覆盖 B 上具有相同名称的分支上的修改!所以不,如果两者都是非裸存储库,则不能从 C 推送到 B!
现在怎么办?!我的世界会就这样结束吗?!伟大的我能做什么?! GIT怎么可能不顾主人的意愿?!这纯粹是异端邪说!
解决方案
1 - 在 B 上有两个分支 - 主分支和一个临时分支。并使 head 指向临时分支。示例:
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 指向它。我使用了更长的方法,因为你应该也知道更长的方法;- 如前所述,如果您以后要使用它,请不要删除该分支。但我确实建议这样做以避免在 pulling/pushing 甚至合并时两个存储库中的 临时分支 出现问题。根据需要创建临时分支 - 使用终端历史记录非常容易 - 然后删除它;