假设我们有主分支,并提交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。
答案 0 :(得分:1)
您需要在这些实体之间明确区分:
master
或dev
。请记住,每个提交都附加了一个完整的源代码树:提交时工作树的快照。在某些方面,这是最重要的部分,但它是我们最后谈论的内容,因为提交和图形都在阻碍。
每个提交还有一个指向其父提交的指针。这就是你所绘制的图形片段存在轻微问题的地方:指针指向错误的方式("前进"及时,从最旧到最新,而不是向后,最新到最古老的。)
让我们用正确的方式重绘图形片段:
a0 <- a1 <- a2 <- a3 <-- master
^
`-- b2 <- b3 <-- dev
这些提交都不能更改。将b3
点提交回b2
,将b2
提交回a1
,依此类推。 (更准确地说,每个提交都存储了其父级的SHA-1 ID。在此级别,合并只是一个至少有两个父ID的提交。)
名称,在这种情况下master
和dev
,可以进行更改。他们所拥有的只是指向(更确切地说,是单个起始点提交的SHA-1 ID)的指针:在这种情况下,分别是a3
和b3
。这些是分支上的最新提交。
图形本身是通过获取这些起始点并遵循所有提交父指针而形成的。这就是我们如何得到上面的图片:master
指向我们a3
,dev
指向我们b3
,我们合并a3
和{ {1}}及其所有父ID指针。
此图中缺少的是与每次提交相关联的源。我把它排除在外有两个很好的理由:(1)我没有它,只有你拥有它,(2)它无论如何都不合适。 :-)但是从你上面所说的内容来看,听起来与提交b3
相关的源树本质上是没有价值的。
您没有说过与提交a3
和a2
相关联的源树。我们该怎么做?
提到所有这些,让我们来看看你能做些什么。您无法更改提交,但您可以制作副本几乎相同的&#34;&#34;因此&#34;同样好&#34;。 1 您也可以任意更改分支指针。但请注意,如果您复制提交,那么共享此存储库(或其b2
副本)的每个人都需要获取这些副本,并且如果您以不同的方式更改分支指针&# 39;期望,它会让事情变得更加困难。
让我们看看人们做期望的事情。
他们期望分支到&#34;成长&#34;,即获得指向现有提交的新提交。如果--bare
增长了一个指向master
的新提交a4
,那么这就完全不足为奇了。
他们希望出现新的分支名称:a3
可能会突然出现,指向某个现有提交(可能是feature3
或a1
)。
他们偶尔甚至会希望分支名称消失,通常是在该分支合并到其他分支之后。
他们没有预料到,至少在没有预先警告的情况下,分支是为了使分支名称指向一个曾经只是在其历史中的提交。例如,如果我们要完全放弃提交b2
,我们就会得到这张图:
a3
请注意,可以执行此操作!只是人们不期待它 - 而共享此存储库的其他人可以,并且很容易就可以放回a0 <- a1 <- a2 <-- master
^
`-- b2 <- b3 <-- dev
。 偶然发生, 2 ,如果他们没有为这种分支倒带做好准备,他们甚至不考虑它。 (我从大约7年前的事情中获得了个人经验。我们最终通过SHA-1 ID&#34拒绝了某些提交;在裸机库中预先接收了钩子。)
所以,现在你必须决定是否要回放a3
&#34;使它看起来像提交master
已经消失。如果这样做,请确保共享存储库的每个人都知道它,以免他们意外地重新引入它。然后只需重新设置a3
以指向master
,即更改排列,以便a2
是a2
的最新历史记录。
这可能更好不来进行这种倒带,正是因为这种倒带导致的共享问题。您说(我认为)您想要放弃master
和a2
之间所做的更改,这是我们进入提交的源代码树的地方
假设我们没有删除a3
,而是添加了一个新的a3
,其相同的源代码树为a4
?
如果我们根本没有git - 例如,如果我们只是将每棵树保存在某处,作为备份副本,并在其上添加名称和日期 - 我们有两种方法可以做到这一点:
撤消工作树中的所有内容:无论在何处添加一行,都将其删除;删除线路的任何地方,恢复它;无论何处改变一个词,都要改回来;或
只需从保存的备份副本中恢复工作树。
使用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;。幸运的是,加号和减号相互抵消,a3
为a4
,原样。
让我们画出:
a3
其中a5
是a2 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只能使用树(或索引)而不是完整的提交。