Kar*_*tik 1 git git-merge git-fork
我们在组织中使用分叉模型,我正在尝试将更新从上游分支合并到本地分支。
这是我的 git 设置。
kartik@ ~/sourcecode/myproject (defect-875) $ git remote -v
origin git@github.com/kartik/myproject.git (fetch)
origin git@github.com/kartik/myproject.git (push)
upstream git@github.com:MyOrg/myproject.git (fetch)
upstream git@github.com:MyOrg/myproject.git (push)
Run Code Online (Sandbox Code Playgroud)
上游有两个分支
掌握
开发
我需要将本地分支与devel上游分支同步。
这是我尝试过的
git fetch upstream
git merge upstream/defect-875
Run Code Online (Sandbox Code Playgroud)
其中,defect-875 是我的本地分支。
我总是收到此错误消息
合并:upstream/defect-875 - 我们不能合并
我也尝试过这个
kartik@ ~/sourcecode/myproject (defect-875) $ git merge upstream/devel -m "Merging from seo redirect."
merge: upstream/devel - not something we can merge
Run Code Online (Sandbox Code Playgroud)
我该如何解决这个问题?
我认为eftshift0 的答案是正确的,但听起来你应该学习一些 Git 技巧。
\n\n关于 Git 首先要了解的是它主要与提交有关。分支 \xe2\x80\x94 或者实际上是分支名称(请参阅“分支”到底是什么意思?)\xe2\x80\x94 存在于 Git 中,以便 Git 和我们可以找到提交。
\n\n您可能已经知道提交实际上是源树的快照。(如果您没有,那么,现在您可以了!)但它还有更多功能。首先,每个提交都有一个以其哈希 ID 形式呈现的唯一“真实名称”。分支名称是表达某些提交的真实名称\xe2\x80\x94和哈希ID\xe2\x80\x94的一种方式,git rev-parse并将向您显示实际的哈希ID:
$ git rev-parse master\nc05048d43925ab8edcb36663752c2b4541911231\nRun Code Online (Sandbox Code Playgroud)\n\n这些又大又丑的ID看起来是随机的,但实际上完全是由commit本身的内容决定的。这些内容非常短\xe2\x80\x94这是上面的提交,并@<address>进行了一些修改以减少垃圾邮件:
$ git cat-file -p c05048d43925ab8edcb36663752c2b4541911231 | sed \'s/@/ /\'\ntree a446a29ef7ca90d9c64825fb00a0f1e1a099ca18\nparent e9983f8965c0875cc6727e9644c84ff1dfc99372\nauthor Junio C Hamano <gitster pobox.com> 1536096807 -0700\ncommitter Junio C Hamano <gitster pobox.com> 1536096807 -0700\n\nGit 2.19-rc2\n\nSigned-off-by: Junio C Hamano <gitster pobox.com>\nRun Code Online (Sandbox Code Playgroud)\n\n上面的树线是提交如何保存源快照(通过使用另一个 Git 内部对象)。作者和提交者行告诉我们谁进行了提交以及何时\xe2\x80\x941536096807表示。父行给出了此提交之前的提交的又大又难看的哈希 ID 。Tue Sep 4 14:33:27 2018
我们说每个有哈希 ID 的东西都指向该对象。有关此内容的更多信息,请参阅Think Like (a) Git,但对于这个答案,让我这样画出来:
\n\n... <-c05048d43925ab8edcb36663752c2b4541911231 <--master\nRun Code Online (Sandbox Code Playgroud)\n\n该名称 master 指向此提交,该提交又指向较早的提交e9983f8965c0875cc6727e9644c84ff1dfc99372。较早的提交指向更早的提交,依此类推。
您在自己的存储库中拥有所有这些分支名称,例如master和。defect-875它们不依赖于您自己的 Git 之外的任何内容。它们也指向您本地的提交,并且这些提交\xe2\x80\x94包括它们的快照及其父提交和这些提交的快照,一直追溯到历史记录\xe2\x80\x94不依赖于任何东西也可以在您自己的 Git 之外。
但您确实从其他一些 Git获得了一些提交。您可以通过让 Git 使用 Internet 调用这些 Git 来完成此操作。他们的 Git 存储库包含带有分支名称的提交。这些提交都具有唯一的哈希 ID,但如果它们的提交具有相同的哈希 ID\xe2\x80\x94 相同的真实名称\xe2\x80\x94 与您拥有的某些提交相同,那么根据定义,它们和您具有相同的哈希 ID犯罪。
\n\ngit fetch获得他们拥有而你没有的提交它们的分支名称不必匹配,但提交哈希 ID必须匹配。要么他们匹配,并且您有提交,要么他们有您没有的提交。所以你的 Git 调用他们的 Git 并说:你有什么名字,他们的提交哈希 ID 是什么? 他们的 Git 给你的 Git 这个列表,你的 Git 对自己说:啊,我有这个,或者嗯,我没有那个。 然后,你的 Git 向他们的 Git 提供他们拥有的、你的 Git想要的提交列表,然后他们的 Git 打包这些提交\xe2\x80\x94,包括他们的快照\xe2\x80\x94,并将它们发送出去。
\n\n在此过程结束时,您将拥有您的所有提交(包括您已经拥有的他们拥有的任何提交)以及他们的所有提交。请注意,它们不仅会向您发送最顶端的提交\xe2\x80\x94(分支名称直接指向的提交\xe2\x80\x94),还会根据需要发送父提交、父提交的父提交等这样你就可以得到连接回你自己的提交的提交。
\n\ngit fetch还获得他们的分支名称;怎么办?您还有他们的分支机构名称。但这些是它们的分支名称,所以现在你的 Git会重命名这些东西。如果你的 Git 调用了他们的 Git git fetch upstream,你的 Git会重命名他们的master,调用它upstream/master,并重命名他们的devel,调用它upstream/devel。
我们可以像这样绘制提交,使用 roundo代表实际的哈希 ID,尽管我现在必须开始猜测他们有多少次提交而你没有。在你运行之前git fetch upstream,你会得到这样的东西:
... <-o <-o <-o <-- master\n \\\n o <-- defect-875\nRun Code Online (Sandbox Code Playgroud)\n\n您的defect-875提交指向master分支的尖端,分支的尖端又指向某个较早的提交,依此类推。然而,一旦你运行git fetch upstream,你会得到更多类似这样的东西:
o--o <-- upstream/devel\n /\n o--o--o <-- upstream/master\n /\n...--o--o--o <-- master\n \\\n o <-- defect-875\nRun Code Online (Sandbox Code Playgroud)\n\n(绘制箭头变得太难了,所以我用线替换了大部分箭头\xe2\x80\x94,只要记住它们总是向后指向)。
\n\n当您运行时git merge,您必须告诉 Git要合并哪个提交。您通常通过为其指定分支名称或远程跟踪名称(例如upstream/devel. 该名称解析为提交哈希 ID\xe2\x80\x94,您可以运行git rev-parse查看其工作原理,正如我上面所示。它之所以有效,是因为 git fetch获得了他们的devel并将其重命名为您自己的upstream/devel。如果您还没有运行git fetch upstream,您必须先执行此操作,以便您的 Git 拥有它们的提交,并将它们重命名devel为您的upstream/devel.
此时,所有工作都在您自己的存储库中进行。 假设我的绘图是准确的;但让我们将其简化为有趣的名称,并将该单词附加HEAD到您现在签出的名称上。我还将为三个有趣的提交输入一个字母的名称:
o--R <-- upstream/devel\n /\n o--o--o\n /\n...--o--o--B\n \\\n L <-- defect-875 (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n\n运行git merge upstream/devel将找到您当前的或HEAD提交的L(对于 Left 或 Local 或--ours);您的提交标记为upstream/devel,即提交R(对于 Right 或 Remote 或--theirs);并使用L和R回到共同的起点,即提交B(对于 Base)。
然后,您的 Git 实际上将运行两个命令,以查看您更改了 \xe2\x80\x94 与vs \xe2\x80\x94git diff中的不同之处,并查看它们更改了哪些内容,即vs中的不同之处:BLBR
git diff --find-renames <hash-of-B> <hash-of-L> # what we did\ngit diff --find-renames <hash-of-B> <hash-of-R> # what they did\nRun Code Online (Sandbox Code Playgroud)\n\nGit 现在将\xe2\x80\x94 合并\ xe2\x80\x94 这两组更改。如果它们可以轻松组合,Git 会将组合的更改应用于与 commit 关联的快照B,并根据结果进行新的提交。这是合并提交,一种特殊的提交。它的特别之处在于它有两个父项而不是一个:它L首先列出您自己的提交作为其第一个父项,然后列出他们的提交R作为其第二个父项。
如果我们把图颠倒过来可能会有一点帮助,所以我会在这里这样做。结果如下:
\n\n L----------------M <-- defect-875 (HEAD)\n / /\n...--o--o--B /\n \\ /\n o--o--o /\n \\ /\n o--R <-- upstream/devel\nRun Code Online (Sandbox Code Playgroud)\n\n这个新提交M存在于您自己的defect-875分支上。
upstream现在,如果您已获得授权,您现在可以使用以下命令在 Git 引用的 Git 存储库上git push创建分支:defect-875upstream
git push upstream defect-875\nRun Code Online (Sandbox Code Playgroud)\n\n这让你的 Git 调用他们的 Git,向他们提供你拥有但他们没有的提交列表,在本例中正是两个提交L和M\xe2\x80\x94,然后向他们建议Git 他们defect-875使用 commit创建了一个名为 的分支M作为其提示提交。
如果他们遵守所有这些请求和建议,您的 Git 将记住他们这样做了,并将名称添加到您自己的一组名称中upstream/defect-875:
L----------------M <-- defect-875 (HEAD), upstream/defect-875\n / /\n...--o--o--B /\n \\ /\n o--o--o /\n \\ /\n o--R <-- upstream/devel\nRun Code Online (Sandbox Code Playgroud)\n\n您自己的分支defect-875不会以任何方式更改:您的名称defect-875仍然标识您的提交M(通过其实际的哈希 ID,无论是什么)。您只需向他们的 Git 提供这两个提交,并让他们的 Git 将其名称设置defect-875为与您的名称相匹配。
如果您愿意,现在可以将您自己的分支的上游defect-875设置为名称upstream/defect-875:
git branch --set-upstream-to=upstream/defect-875 defect-875\nRun Code Online (Sandbox Code Playgroud)\n\n如果您想在运行时同时执行这两项操作git push,则可以通过将标志添加-u到您的git push:
git push -u upstream defect-875\nRun Code Online (Sandbox Code Playgroud)\n\n但这只是一种方便的优化。
\n