Git从分支中减去推送的提交 一个简单的解决方案使这种情况更容易发生只要没有人参与,我们就不需要 新名称新名称方法还有另一个优点

我在同一个分支上为某个模块进行了一些代码的共同开发,而我也在另一个分支上与同事的模块进行了集成。基本上,我最终使两个模块相互交谈时,同时修改了两个模块的代码。现在,我想将所有代码一起提交,但不希望从分支中合并内容。

  • 假设我的模块是在A分支上开发的“ Apple”
  • 我的同事的代码是在B分支上开发的“香蕉”
  • 我想在C分支上集成“鸡尾酒”

因此,基本上,我在A上进行了开发,从B挑选了我想要的更改,并且已经进行了测试。现在,我想分开提交,以便分支A仅包含“ Apple”,分支B仅包含“ Banana”,分支C包含集成代码。我不想重命名分支,因此我已经通过cherry-pick将所有更改从分支A复制到了C。另外,我已经挑选了从分支A到B的相关更改。

现在剩下要做的就是减去分支A的提交,以便只剩下“ Apple”。我正在寻找做到这一点的最佳方法。我不认为git revert是正确的选择,并且我想我可以进行交互式变基并删除不需要的A提交,但是我正在寻找验证这是正确的方向,或者如果需要其他方法。

avafavaf 回答:Git从分支中减去推送的提交 一个简单的解决方案使这种情况更容易发生只要没有人参与,我们就不需要 新名称新名称方法还有另一个优点

分支名称对人类而言并不重要。 (您是人吗??如果是这样,我们可能会遇到一点障碍。)

对Git重要的是 commits 。提交存储库中的历史记录。每个提交都有一个唯一的哈希ID,这些哈希ID是Git查找提交的方式(我们将在稍后看到一个重要的例外)。哈希ID大而丑陋,并且 look 是随机的,基本上是人类无法使用的,这就是为什么我们使用分支名称的原因。

每个提交都包含所有文件的完整快照(嗯,所有显示在该提交中的所有文件)。而且,每个提交都保存一些元数据(有关提交的信息),例如创建者,创建时间以及对它的直接父提交的原始哈希ID(对于Git本身而言非常重要)。因此,这使Git从 last 提交开始并向后工作。

也就是说,假设哈希为H的提交是某个链中的最后个,如标记为branch-A的分支名称:

... <-F <-G <-H   <--branch-A

名称 branch-A保存链中最后一次提交的哈希ID,即提交H。提交H本身拥有较早提交G的哈希ID,该哈希ID包含较早提交F的哈希ID,依此类推。

诀窍是 name 包含 last 提交的哈希ID。没有其他简单的方法可以找到最后一个!最后一个找到倒数第二个,找到倒数第二个,依此类推,但是不仅仅是人需要这个名字:Git也需要这个名字,来找到 last 提交。

所以基本上我是在A上进行开发的,从B挑选了我想要的更改,并且已经进行了测试。

使用git cherry-pick时,您是在告诉Git进行复制(提交的效果以及其中的某些元数据)。因此,您和从事B工作的人都是从某个常见的起点开始的,例如提交H:

          I--J   <-- branch-A
         /
...--G--H   <-- common-starting-point
         \
          K   <-- branch-B

您提到您使用git cherry-pick从另一个分支复制了一些提交。将K复制到一个新的提交中,我将其称为K',以表明它与K的相似程度,

          I--J--K'  <-- branch-A
         /
...--G--H   <-- common-starting-point
         \
          K   <-- branch-B

请注意,名称 branch-A现在如何指向新的 last 提交,现在为K'。提交K'具有所有内容的完整快照,但是将K'J进行比较,您将看到与比较{{1}时相同的 change }至KH的作者和日志消息也将与K'的作者和日志消息匹配,除非您告诉Git进行更改。当然,H的父哈希是K',而J的父哈希是K

您也可以添加更多的提交,就像您可能做的那样:

H

现在剩下要做的就是减去分支A的提交,以便只剩下“ Apple”。我正在寻找做到这一点的最佳方法。

不一定有最好的方式。但是,Git的主要功能是 add 提交到分支,而 remove 的提交则更少。如果确实要删除提交,则可以告诉Git强制 name 向后移动。不再是 last 的提交现在无法找到。例如,如果我们强迫名称 I--J--K'--L <-- branch-A / ...--G--H <-- common-starting-point \ K <-- branch-B 向后移动以指向提交branch-A,我们将得到:

I

使用Git的常规提交查看工具(例如 J--K'--L [abandoned] / I <-- branch-A / ...--G--H <-- common-starting-point \ K <-- branch-B ),我们将不再看到 git logJK' 1 观众从分支名称找到的 last 提交开始,然后向后工作。

无论如何,这里最大的问题是Git是为 add 提交而构建的。您可以使自己的Git向后移动自己的分支名称,例如使用Lgit reset,但这不会使发送到的任何 other Git您的提交,请向后移动名称。


1 如果我们让他们停留在此无法访问状态足够长的时间,则提交git branch -fJ最终将成为垃圾回收。 Git时不时地运行的K'命令通常会在这种方式运行了至少30天后执行此操作,因此您至少需要30天的时间,再加上Git需要花费的时间自行运行git gc,改变主意并将其收回。


一个简单的解决方案

处理此问题的简单方法是使用新名称。由于Git不在乎名称,因此我们可以称其为git gc而不是neo-A。我们将设置branch-A指向您最初开始的现有提交neo-A

H

现在,对于 I--J--K'--L <-- branch-A / ...--G--H <-- common-starting-point,neo-A \ K <-- branch-B ,我们将添加一次提交。我们看一下现有的提交。 neo-A是我们喜欢的一种,因此我们可以复制它,也可以直接使用它,因为它很好。通过使用I朝着neo-a的方向前进一步,让Git“喜欢”具有分支名称的方式,可以直接使用它。 (当然,向L而不是L的方向移动很重要,但这很容易,因为我们处于控制之中。)

K

J--K'--L <-- branch-A / I <-- neo-A / ...--G--H <-- common-starting-point \ K <-- branch-B H方向的下一个提交是提交L。那也很好:我们可以将名称J向前推进neo-A,只要给出以下名称即可:

J

K'--L <-- branch-A / I--J <-- neo-A / ...--G--H <-- common-starting-point \ K <-- branch-B 是个问题:我们不想要它。所以我们只是不前进。不过,我们确实要K',现在我们必须复制它,因为现有的L指向L,而我们想要一个新的指向{{ 1}}。因此,我们这次需要使用K'来产生:

J

我们会继续,但是我们已经完成了所有的提交,也就是说,我们已经完成了。

我们现在可以放心git cherry-pick K'--L <-- branch-A / I--J--L' <-- neo-A / ...--G--H <-- common-starting-point \ K <-- branch-B 名称。然后,我们只需要说服所有人停止使用 old 名称即可。

使这种情况更容易发生

上述缺点是我们必须一次一步地“向前移动名称”。如果我们可以创建git push并让我们的Git来完成所有工作,那就太好了。事实证明,我们可以neo-A命令具有我们喜欢的所有设备。

我们要做的就是:

  • 创建neo-A指向相同并提交为git rebase
  • 运行neo-A
  • 将{}更改为branch-A,以实现我们想要的提交

,Git将自动倒退git rebase -i <hash of common starting point H>,然后向前或向后跳过提交。最终结果就是我们得出的结果:

pick

之所以可行,是因为Git强制名称 drop在复制完成后移动到最后复制的提交,在本例中为neo-A

这将在名称为 K'--L <-- branch-A / I--J--L' <-- neo-A (HEAD) / ...--G--H <-- common-starting-point \ K <-- branch-B 的任何 other Git中产生问题,但是我们只是将其组合起来,所以其他Git都没有。因此,现在neo-A是安全的。我们将向其他Git发送新的提交,例如L',然后要求他们设置名称neo-A指向链中的最后一个提交-git push-就像我们的名字。

只要没有人参与,我们就不需要 新名称

如果愿意,我们可以直接使用L'本身来完成neo-A。结果将是:

L'

然后我们可以使用git rebase -i或等效于 demand 的方法,让另一个Git放弃其提交branch-A K'--L [abandoned] / I--J--L' <-- branch-A (HEAD) / ...--G--H <-- common-starting-point \ K <-- branch-B 并使其成为 名称git push --force指向提交L。假设他们服从了-他们可以拒绝了-我们现在将他们的K'搬走了。

这里唯一真正的问题是,一些讨厌的人可能已经将他们的 branch-A复制到了另一个Git存储库。人们可能希望他们的L'副本仅朝正常的前向添加-新提交方向移动。这是使用不同分支名称的真正原因:避免使对分支名称有期望的人感到困惑。

如果没有人在这里造成混淆,或者如果所有其他人都事先知道可能会发生这种情况,请随时倒带并强制推送现有的分支名称。

新名称方法还有另一个优点

假设,在删除其他人的提交的同时复制提交的过程中,您犯了一个错误。

如果您使用自己的名字branch-A进行此操作,则很难找到原始提交系列。 Git有一些技巧(branch-A可以为您提供帮助,但它们更多的是用于紧急情况,而不是日常使用。我发现最好重新命名,然后重新设置名称。如果出现问题,我仍然使用 old 名称,从中可以轻松按正确的顺序查找所有旧提交。

对于可以强制推送的专用分支名称,有时会稍微更改一下顺序。而不是:

branch-A

我愿意:

branch-A

现在,如果出现问题,我将使用git reflog来记住原始提交。有时,我会保留多个“分支的旧版本”:

git checkout neo-A branch-A
git rebase -i <start-point>

直到我拥有提交的“正确”集合。每个编号的名称都会跟踪“正确提交”的每个连续近似值。

本文链接:https://www.f2er.com/2553902.html

大家都在问