如何在没有任何更改的情况下复制其他分支中的提交?

时间:2015-11-02 16:59:07

标签: git

假设我们有主分支,并提交a0-> a1-> a2-> a3。 在提交a1中,我们创建另一个名为dev的分支:a1-> b2-> b3,即:

a0->a1->a2->a3 (master)
     -->b2->b3 (dev)

a3与b3完全不同,意味着他们有很多冲突。

我想要的是在master分支中,我希望b3没有任何变化,如下所示: a0-> a1-> a2-> a3-> b3(主)

如何处理git命令? 谢谢!

更多:我的意思是完全忽略a3。在某些情况下,你知道b3足够好了,a3失败了。所以只需完全接受b3。

1 个答案:

答案 0 :(得分:1)

您需要在这些实体之间明确区分:

  • commit ,包含作者/提交者姓名,电子邮件和消息;
  • 与任何给定提交相关联的源树;
  • 提交(部分或全部DAG);和
  • 分支名称,例如masterdev

请记住,每个提交都附加了一个完整的源代码树:提交时工作树的快照。在某些方面,这是最重要的部分,但它是我们最后谈论的内容,因为提交和图形都在阻碍。

每个提交还有一个指向其父提交的指针。这就是你所绘制的图形片段存在轻微问题的地方:指针指向错误的方式("前进"及时,从最旧到最新,而不是向后,最新到最古老的。)

让我们用正确的方式重绘图形片段:

a0 <- a1 <- a2 <- a3   <-- master
       ^
        `-- b2 <- b3   <-- dev

这些提交都不能更改。将b3点提交回b2,将b2提交回a1,依此类推。 (更准确地说,每个提交都存储了其父级的SHA-1 ID。在此级别,合并只是一个至少有两个父ID的提交。)

名称,在这种情况下masterdev可以进行更改。他们所拥有的只是指向(更确切地说,是单个起始点提交的SHA-1 ID)的指针:在这种情况下,分别是a3b3。这些是分支上的最新提交。

图形本身是通过获取这些起始点并遵循所有提交父指针而形成的。这就是我们如何得到上面的图片:master指向我们a3dev指向我们b3,我们合并a3和{ {1}}及其所有父ID指针。

此图中缺少的是与每次提交相关联的。我把它排除在外有两个很好的理由:(1)我没有它,只有你拥有它,(2)它无论如何都不合适。 :-)但是从你上面所说的内容来看,听起来与提交b3相关的源树本质上是没有价值的。

您没有说过与提交a3a2相关联的源树。我们该怎么做?

提到所有这些,让我们来看看你能做些什么。您无法更改提交,但您可以制作副本几乎相同的&#34;&#34;因此&#34;同样好&#34;。 1 您也可以任意更改分支指针。但请注意,如果您复制提交,那么共享此存储库(或其b2副本)的每个人都需要获取这些副本,并且如果您以不同的方式更改分支指针&# 39;期望,它会让事情变得更加困难。

让我们看看人们期望的事情。

他们期望分支到&#34;成长&#34;,即获得指向现有提交的新提交。如果--bare增长了一个指向master的新提交a4,那么这就完全不足为奇了。

他们希望出现新的分支名称:a3可能会突然出现,指向某个现有提交(可能是feature3a1)。

他们偶尔甚至会希望分支名称消失,通常是在该分支合并到其他分支之后。

他们没有预料到,至少在没有预先警告的情况下,分支是为了使分支名称指向一个曾经只是在其历史中的提交。例如,如果我们要完全放弃提交b2,我们就会得到这张图:

a3

请注意,可以执行此操作!只是人们不期待它 - 而共享此存储库的其他人可以,并且很容易就可以放回a0 <- a1 <- a2 <-- master ^ `-- b2 <- b3 <-- dev 偶然发生, 2 ,如果他们没有为这种分支倒带做好准备,他们甚至不考虑它。 (我从大约7年前的事情中获得了个人经验。我们最终通过SHA-1 ID&#34拒绝了某些提交;在裸机库中预先接收了钩子。)

所以,现在你必须决定是否要回放a3&#34;使它看起来像提交master已经消失。如果这样做,请确保共享存储库的每个人都知道它,以免他们意外地重新引入它。然后只需重新设置a3以指向master,即更改排列,以便a2a2的最新历史记录。

这可能更好来进行这种倒带,正是因为这种倒带导致的共享问题。您说(我认为)您想要放弃mastera2之间所做的更改,这是我们进入提交的源代码树的地方

假设我们没有删除a3,而是添加了一个新的a3,其相同的源代码树为a4

如果我们根本没有git - 例如,如果我们只是将每棵树保存在某处,作为备份副本,并在其上添加名称和日期 - 我们有两种方法可以做到这一点:

  1. 撤消工作树中的所有内容:无论在何处添加一行,都将其删除;删除线路的任何地方,恢复它;无论何处改变一个词,都要改回来;或

  2. 只需从保存的备份副本中恢复工作树。

  3. 使用git,我们有两个相同的选项,因为每个提交都有一个保存的整个树的备份副本。

    方法(1),撤消所有内容,如果你没有git可能会很困难,但使用git非常容易,因为它是一个基本的操作:a2。 (方法2是内置,正如我们所见。)

    这是我们进入git存储内容这一事实的地方。我在上面提到过,在某些方面,这是git所做的最重要的部分。这是因为git所做的一切 - 管理提交图和提交消息,以便记住谁做了什么,何时以及为什么 - 只有在你有一些内容时才有意义。

    但是,Git与许多其他系统的不同之处在于,每个提交都存储一个完整的树。在其他系统中,每个提交存储更改;在git中不是这种情况。

    但是,这意味着,为了看到更改,您必须指示git查看两个提交。 3 但是{{ 1}}只接受一个提交ID。这是commit graph 的用武之地:git revert可以将该提交的源树与其父提交的源树进行比较。

    在这种情况下,例如,恢复提交git revert会很有意义。为此,git只需将revert与其父级a3进行比较。无论将a3转换为a2 - 添加一些行,删除一些行,甚至添加或删除整个文件-git都可以简单地反转这些更改。如果你应用这个&#34;反向改变&#34;从a2到源树并进行新的提交a3,git保证新a3的源树与a4的源树相同。

    您可能会问我们为什么这样做而不是a4&#34;中更简单的复制树。在这种特殊情况下,他们都会做同样的事情。但是假设我们已经提交了一个很好的提交a2?如果我们将树从a2复制到a4,我们就会丢失a2(vs a5)中的更改。但是,如果我们在保留a4的同时撤消a3的更改,我们会得到一个新的a3,即#34; {{1} },加a4,加a5,减去a2&#34;。幸运的是,加号和减号相互抵消,a3a4,原样。

    让我们画出:

    a3

    其中a5a2 plus a4的还原。这使得a0 <- a1 <- a2 <- a3 <- a4 <-- master ^ `-- b2 <- b3 <-- dev 中的源树与a4中的源树相同:如果您运行a3,则不会显示差异。这两个提交是不同的 - 它们的SHA-1 ID不同,它们的消息不同,它们的父SHA-1 ID不同 - 但它们的源树是相同的。

    此外,从此存储库共享的任何人都会看到他们期望的提交图更改类型:a4增加了新的提交a2

    您现在可以按照自己喜欢的方式组合来自git diff <id-of-a2> master分支的更改(樱桃挑选和合并是两种常用方法,尽管这两种方法在源树方面做了很多不同的事情,并且在提交图的条款)。

    或者,您可以master掌握回a4,之后您可以按照自己喜欢的方式合并dev的更改。就生成的源树而言,您将获得完全相同的结果。就提交图而言,您不再拥有提交rewind,也不会拥有其还原提交a2(您赢得了#t}需要)。将来查看您的存储库的人永远不会看到发生故障的dev或其恢复,因为它们就好像它们从未存在过一样。但是,任何共享已经拥有a3的存储库的人都需要自己做额外的工作来处理&#34;倒带&#34; a4(基本上他们也需要丢弃他们的a3)。

    1 是否真的 &#34;同样好&#34;取决于你使用它的原因,以及你复制它的工作有多好。

    2 一旦你添加了新的提交,这种可能性就会降低,但它仍然可能。如果你的用户合并,很容易意外发生,如果你强迫他们进行重组,通常会变得更难。

    3 两个或更多,真的,但是在处理两个以上的提交时会变得更复杂。此外,git只能使用树(或索引)而不是完整的提交。