git 显示它正在将同一分支合并到自身中

Gau*_*hah 3 git github

我不确定我执行了什么样的 git 命令,但是当我运行时,git log我收到以下消息

将https://github.com/aaa/my_repo_name的分支 'xxx-2222' 合并到 xxx-2222

我想我做了以下事情。

  1. 我有一个功能分支 (xxx-1111),并从中创建了一个新的功能分支 (xxx-2222)。
  2. 然后,我从开发分支重新建立了 xxx-1111 分支,然后将所有提交合并到开发分支中。
  3. 我开始在我的新分支 xxx-2222 上工作
  4. 我从开发分支 rebase xxx-2222 并执行了 git pull 。

我想在此之后我会在日志中收到以下消息。

有人可以告诉我这是什么意思以及为什么会发生。如何可能追踪它。最后,在将其合并到开发中之前,我是否需要做任何事情来修复它。

tor*_*rek 7

长话短说

\n\n

你已经跑了git pull。该git pull命令的意思是:

\n\n
    \n
  1. git fetch为我奔跑吧
  2. \n
  3. 提取完成后,立即为我运行第二个 Git 命令。
  4. \n
\n\n

第二个命令默认为git merge,正是这个git merge导致了您的问题。阅读下面的长篇讨论以了解原因。

\n\n

我建议 Git 新用户避免使用 git pull. git fetch自己跑吧 然后,如果合适的话,运行第二个 Git 命令\xe2\x80\x94git mergegit rebase,或者您自己可能想要使用的第二个命令\xe2\x80\x94 。这让您有机会停下来看看所git fetch获取的内容,然后再开始运行第二个可能根本不合适的命令。

\n\n

长的

\n\n

首先,这不是同一个分支。这是同名的不同分支

\n\n

类比是糟糕的推理方式,但有时它们很有用。想象一下你正在参加一个聚会,那里的每个人都叫鲍勃。他们都有相同的名字。这是否意味着他们都是同一个人? 当然不是\xe2\x80\x94,但它们都是“Bob”。您只需使用其他名称来区分它们即可。

\n\n

这就是 Git 在这里尝试的:

\n\n
\n
merge branch ... of ... into xxx-2222\n
Run Code Online (Sandbox Code Playgroud)\n
\n\n

这里的两个空白处填写:

\n\n
    \n
  • 他们所谓的分支,以及
  • \n
  • 与他们交谈时使用的名字
  • \n
\n\n

这样您以后就可以意识到:哦,那不是我的xxx-2222 ,那是他们的xxx-2222。这有点像意识到你不是在和鲍勃·琼斯说话,而是在和鲍勃·史密斯说话。

\n\n

那么,让我们来谈谈 Git 中的命名问题。

\n\n

提交具有独特(但丑陋)的名称;人类使用分支名称

\n\n

在任何一个 Git 存储库中,每次都有一个值得您信赖的名称。该名称是一个哈希 ID。哈希 ID 是打印的丑陋的大十六进制数字字符串git log,例如8dca754b1e874719a732bc9ab7b0e14b21b1bc10. 这些 ID 是唯一的并且不会重复,因此您要么确实提交8dca754b1e874719a732bc9ab7b0e14b21b1bc10(这是 Git 的 Git 存储库中的提交),要么没有(大概是因为您从未将您的Git 存储库与 Git 的存储库混合在一起)本身:除非您要编写一些代码来修改 Git,否则为什么要这样做?)。

\n\n

每次提交都会存储所有文件的快照以及一些元数据。元数据包括谁进行了提交\xe2\x80\x94、名称、电子邮件地址以及日期和时间戳\xe2\x80\x94,还包括新提交之前的提交的原始哈希 ID。这意味着每次提交都会通过原始哈希 ID记住其直接前任或父提交。

\n\n

意味着我们可以使用向后箭头绘制提交图片,如下所示:

\n\n
... <-F <-G <-H\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这里,H代表我们最后一次提交的哈希 ID。它会记住前一次提交的哈希 ID GG依次记住 commit 的哈希 ID F,依此类推。

\n\n

要使用 commit H,我们必须记住它又大又难看的哈希 ID。我们不再需要记住G's 了,因为那是 H. 我们不需要记住F\'s,因为我们可以使用Hfind G,并且GF\'s 的哈希 ID。这种模式一直持续下去:我们需要做的就是记住最后一次提交的哈希 ID。

\n\n

但是,当我们拥有计算机时,我们为什么要记住它呢?我们可以让计算机记住哈希 ID。例如,让名称master记住 commit 的哈希 ID H,如下所示:

\n\n
...--F--G--H   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,当我们进行新的提交时,我们将从git checkout master\xe2\x80\x94 开始,这让我们提交H工作,并记住我们“在分支主机上”,就像git status\xe2\x80\ x94,我们将做一些工作,,git addgit commit。Git 将保存我们所有文件的新快照,并提供一些新的、独特的、丑陋的哈希 ID,我们将其称为I。新提交I将记住提交的哈希 ID H

\n\n
...--F--G--H\n            \\\n             I\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,作为提交的最后一步,Git 会将新的哈希 ID 存储到我们的 name 中master,以便master指向I而不是H

\n\n
...--F--G--H--I   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

I记住H,它记住G,等等,所以让我们master只记住 的新哈希 ID是可以的I

\n\n

Git 是分布式的,这意味着有很多独立的存储库

\n\n

您这里有两个不同的 Git 存储库。一种是您自己的本地计算机上的 Git 存储库,您可以在其中运行git checkoutgit add等等git commit。该存储库确实属于您:您对其拥有 100% 的完全控制权。

\n\n

另一个已经在 GitHub 上完成了。从技术上讲,这是他们的(GitHub)存储库。他们已将其大部分控制权移交给您,因此在最有用的方面,它也是您的,但更方便\xe2\x80\x94加上技术上更准确\xe2\x80\x94称其为他们存储库,所以让我们在这里这样做。

\n\n

存储库可以共享

\n\n

这两个存储库不需要具有相同的提交,但一般来说,您可能希望在存储库中进行的任何新提交都进入它们存储库。如果他们有任何新的提交,而您的提交中没有,您可能希望将它们添加到您的提交中。为此,您需要将您的 Git 连接到他们的 Git,并让它们交换提交。

\n\n

他们将通过哈希 ID 进行此交换,因为哈希 ID 是真正通用的。他们的 Git 要么有一些哈希 ID,要么没有。您的 Git 要么有,要么没有一些哈希 ID。他们拥有而你没有的任何哈希 ID 都是你可以从他们那里获取的对象,之后你们都拥有它。任何你有但他们没有的身份证件你都可以给他们,之后你们就都拥有了。

\n\n

Git 很大程度上是围绕添加到存储库的想法构建的。将他们的东西添加到你的 Git 中,以及将你的东西添加到他们的 Git 中都是非常容易的。要将它们的内容放入您的 Git,您需要运行git fetch(或者git pull,它以运行 开头git fetch)。要将你的东西放入他们的 Git 中,你可以运行git push.

\n\n

有时,您根本不想添加东西。有时你想摆脱一些提交!你可以做到\xe2\x80\x94,但 Git 不是为此构建的,所以它更难。我们稍后会看到这一点。

\n\n

考虑到这一切,让我们看看变基的剖析

\n\n

需要了解的git rebase是它会复制提交。也就是说,它需要一些现有的提交,提取它,进行一些更改,然后从中进行新的提交。这个新的提交只是\xe2\x80\x94一个新的提交\xe2\x80\x94,它使旧的提交完全不受影响。

\n\n

Git 必须这样做,因为提交\xe2\x80\x94 或任何 Git 对象的哈希 ID 实际上\xe2\x80\x94 只是该提交内容的加密校验和。如果您取出一个提交并更改它并进行新的提交,您将获得一个新的、不同的提交。因为 Git 是为了添加东西而构建的,所以这只是将一个新对象添加到您的存储库中。如果你有:

\n\n
...--F--G--H--I   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

并且您制作了一个稍微不同的新副本,I其父级已存在H,您将得到:

\n\n
...--F--G--H--I\n            \\\n             I\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

I\'副本在哪里。现有的提交根本没有被触及。

\n\n

棘手的部分是:分支名称会发生​​什么? 嗯,一般来说,我们使用的原因git rebase是:

\n\n
    \n
  • 获取一系列现有的提交,这些提交本来就可以,但不是基于正确的起点,并复制它们,以便它们基于正确的起点;或者
  • \n
  • 采取一系列现有的不太正确的提交,并改进它们。
  • \n
\n\n

(有时我们会同时进行这两项操作,并且还有其他一些可能性,但这是进行 rebase 的两个重要原因。)

\n\n

也就是说,我们可能有:

\n\n
...--F--G--H   <-- master\n         \\\n          I--J--K   <-- feature\n
Run Code Online (Sandbox Code Playgroud)\n\n

where 提交I通过K就很好,但我们希望它们在之后H而不是在之后G。提交的父级是冻结的、不可更改的提交本身的一部分,因此为了获得我们想要的内容,我们必须将这三个提交复制到新的提交中:

\n\n
             I\'-J\'-K\'   <-- feature\n            /\n...--F--G--H   <-- master\n         \\\n          I--J--K   [abandoned in favor of the new improved feature]\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是假设,在我们这样做之前git rebase,我们已经与其他一些 Git(例如 GitHub 上的 Git)共享了原始的三个提交\xe2\x80\x94 及其独特的又大又难看的哈希 ID\xe2\x80\x94?我们向他们发送了这些提交,并告诉他们设置分支名称feature记住哈希 ID K。他们这样做了,并且在他们的 Git 中仍然保留了我们想要放弃和摆脱的这三个旧提交。

\n\n

(在我们的Git 中,我们可能仍然有一个附加到 commit 的名称K。这个名称是origin/feature。图表实际上应该是:

\n\n
             I\'-J\'-K\'   <-- feature\n            /\n...--F--G--H   <-- master\n         \\\n          I--J--K   <-- origin/feature\n
Run Code Online (Sandbox Code Playgroud)\n\n

但这不是关键部分。)

\n\n

假设我们现在让 Git 调用他们的 Git\xe2\x80\x94(我们正在调用的 GitHub 上的 Git \xe2\x80\x94)并说:嘿,你origin这个其他 Git,告诉我你有哪些提交,以及您为它们使用的名称。 他们会说:我有,这是提交我已经做到了,那就是承诺 因此,如果我们实际上已经摆脱了,我们现在将重新下载(并​​且因为我们必须获得整个链)。https://github.com/...masterHfeatureKKKIJ

\n\n

如果我们运行git fetch,我们将确保获取I-J-K并更新我们的origin/feature标签,以便我们知道它们的 feature名称 commit K,我们现在/再次/仍然共享。但git pull不只是运行git fetch

\n\n

git pull运行git merge(无论如何默认)

\n\n

默认情况下, a 的第二步git pull是运行git merge。该git merge命令接受一些参数来告诉它要合并什么,并git fetch提供它们。在这种特殊情况下\xe2\x80\x94如果上面的插图与您的情况匹配\xe2\x80\x94则参数git merge将是:

\n\n
    \n
  • 合并提交K
  • \n
  • 将结果合并中的消息设置为merge branch \'xxx-2222\' of https://github.com/aaa/my_repo_name into xxx-2222
  • \n
\n\n

这部分\xe2\x80\x94请立即合并提交Kgit pull是\xe2\x80\x94的一部分,因为在您的git fetchGit之后,您的存储库中有这些提交:

\n\n
             I\'-J\'-K\'   <-- xxx-2222\n            /\n...--F--G--H   <-- master\n         \\\n          I--J--K   <-- origin/xxx-2222\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,您的 Git 尽职尽责地找到提交的合并基础KK\'(即 commit G),并完成执行和提交合并的所有工作,为您提供:

\n\n
             I\'-J\'-K\'-------M   <-- xxx-2222\n            /              /\n...--F--G--H   <-- master /\n         \\       ________/\n          I--J--K   <-- origin/xxx-2222\n
Run Code Online (Sandbox Code Playgroud)\n\n

您现在将看到看起来像提交IJK\xe2\x80\x94的两个副本,K\'因为确实是 的副本K,并且J\'确实是 的副本J,依此类推。

\n\n

从本质上讲,你已经告诉 Git:是的,我喜欢我的新的和改进的提交......而且我也喜欢我的旧提交,所以让我进行合并,将两个集合连接在一起,并使我的分支名称xxx-2222指向新的合并提交M

\n\n

你可能想要的是运行git push --force-with-lease

\n\n

而不是运行git pull\xe2\x80\x94 或其两个组件,git fetch以及git merge\xe2\x80\x94 当你有以下情况时你可能想做的事情:

\n\n
             I\'-J\'-K\'   <-- xxx-2222\n            /\n...--F--G--H   <-- master\n         \\\n          I--J--K   <-- origin/xxx-2222\n
Run Code Online (Sandbox Code Playgroud)\n\n

是让你的 Git 调用他们的(来源 / GitHub 的)Git 并向他们提供你的新提交I\'J\'K\'。这些是您用 所做的替换,它们以某种方式git rebase改进了原始序列。I-J-K然后你希望你的 Git 告诉其他 Git:现在,我想你xxx-2222记得了K。如果是这样,我命令你让你的名字xxx-2222记住提交K\'!。

\n\n

如果你让你的 Git 结束这个git push操作:我现在礼貌地请求你,GitHub-Git,设置你的名字xxx-2222,以便它记住 commitK\',他们会说:不,我不会那样做,因为如果我那样做,我将放弃我的I-J-K承诺。 当然,这正是您希望他们做的。

\n\n

这里的风险是他们的I-J-K-L存储库中现在可能有 , 。也就是说,他们可能会记住一些新的提交,这些新的提交会记住提交等等。您可以使用此选项来应对该风险。这使用你的\xe2\x80\x94你的 Git\ 对他们 Git\的\xe2\x80\x94的记忆来表示我认为你......。xxx-2222LK--force-with-leaseorigin/xxx-2222xxx-2222xxx-2222

\n\n

您可以使用git push --force,它会删除命令中的I think ... if so...部分。这是更危险的,但也更有力,可能git push会让他们继续听从你的命令。

\n\n

结论

\n\n

记住以下几件事始终很重要:

\n\n
    \n
  • 提交图是什么样的?
  • \n
  • 谁(哪个 Git)记住了哪些提交哈希 ID,用哪些名称?
  • \n
\n\n

该命令将提交从您的 Git 发送到另一个 Git,然后要求或命令它们git push设置一些名称以记住某些提交哈希 ID(每个名称一个哈希 ID)。该命令从另一个 Git 获取对您的 Git 的提交,然后根据您的 Git 从其 Git 看到的内容设置您的或其他远程跟踪名称。git fetchorigin/*

\n\n

这些并不完全对称!使用,您的远程跟踪名称会更新,但这对您的分支git fetch名称没有影响。使用,他们的分支名称会被更新\xe2\x80\x94,或者他们会拒绝你的礼貌请求,因为更新会丢失一些提交。git push

\n\n

由于git rebase 副本已提交,对于新的和改进的副本,您现在让 Git 放弃旧的和不太好的副本,转而支持新的和改进的副本,您需要强制告诉他们的Git执行以下操作同样的:放弃一些旧的不太好的承诺,转而支持新的和改进的承诺。

\n\n

请注意,当您执行此操作时,当您使用git push --forcegit push --force-with-leasexe2x80x94 时,您将告诉一个Git 存储库丢失一些提交。如果这些提交已经扩散到更多Git 存储库怎么办?每个运行git fetch到 GitHub 存储库的人都已获取了他们可以从存储库获取的所有新提交。你的旧的和不那么伟大的承诺现在可能会广泛传播,并且很难摆脱。确保每个可能使用它们的人都明白您打算撤销和替换它们!

\n