Git:壁球做什么?为什么樱桃选择较旧的提交?

时间:2013-12-10 07:38:22

标签: git

最新的提交应该是逐步包含先前提交的所有更改的提交,对吧?

如果是这样,那么-squash用于将所有提交压缩为一个用于清理历史记录的用途是什么?

什么壁球实际上只是删除除最新提交之外的所有提交?

樱桃采摘也是如此。如果最新提交包含先前提交的所有更改,那么为什么需要选择较旧的提交?

2 个答案:

答案 0 :(得分:12)

我不确定你是否在这里误解了工作。许多较旧的源代码管理系统将每个提交(或签入或他们称之为的任何内容)存储为"更改"。 Git没有:提交使每个文件完整无缺, 1 存储为" blob"类型的存储库对象。

" blob"对象位于底层,就像它所指向的那样,#34;树"对象(包含文件/目录的名称和可执行位),并且" commit"指向树。对象。 (还有一个最后的存储库级对象类型,"带注释的标记",通常指向提交。)因此,给定提交SHA-1和类似dir/file的存储库路径,git首先提取提交对象,这会导致树需要有dir的条目。该条目需要指向另一个树,该树必须具有file的条目,然后 条目应该是一个blob,并且{{1}的版本出现在该提交中。

分支和标记名称,如dir/file,只是人类可读的单词,给出了"真名称"底层存储库对象的SHA-1。提交对象具有父母SHA-1"其中的值,允许git动态提取提交图。

当然,您仍然可以通过git获取更改集。它只是每次动态计算它们。

假设我们有这个提交图,我使用一个大写字母来表示每个40个字符的SHA-1,以及一些分支名称:

master

名称A - B - C - D <-- master \ E - F <-- branch 只是名称提交master(可能真的是D)。该提交有一个父项dcfaa9d9767a010c143ffd42b01b84d2abb4cffc(真正C)。 222c4dd...有一个父CB有一个父BA根本没有父母 - 它是根提交。

从提交A开始并通过任何父母向后工作,无法达到提交F;它只能通过其分支名称D访问。 branch的父级是F,而E的(单个)父级提交是E,所以从{{1}开始我们可以通过BF向后工作。

这是常规合并的来源:它们在提交图上运行。如果我们将B合并到A&#34; -let&#39;暂时执行此操作,然后再将其退出:

branch

- 我们制作了一个新的合并提交master

$ git checkout master
Switched to branch 'master'
$ git merge -m regular-merge branch
[snip]

这(真实的,非&#34;壁球&#34;)合并有两个父母,MA - B - C - D - M <-- master \ / E - F <-- branch 。 (而且,重要的是,D是&#34;第一个&#34;父级:这会在合并之前告诉您F&#34;上的哪个提交最初是#34;所以现在以前只能通过D(从master开始向后工作)才能访问的两个提交也可以在branch上找到。

各种文件内容怎么样?嗯,这取决于谁合并。使用git的自动合并,如果你将F合并到master&#34;没有相互矛盾的变化,你会得到你期望的东西。但是,您可以与branch合并,以放弃master-s ours中更改的内容。您仍将获得合并提交E,但其树将与提交F中的树相同。 2

在任何时候你也可以要求git从一次提交到另一次提交产生变更集。因此,如果您想查看MD之间的更改,您可以找到两者的SHA-1并执行:

B

要查看Egit diff <sha1-for-B> <sha1-for-E> 之间的更改,您只需使用他们的 SHA-1,并查看&#34;分支{{1对于EF使用SHA-1。

作为一个非常方便的方便,看看一些提交和它的父级之间发生了什么 - 让我们不要担心这里的合并,因为他们有多个父母 - 我们可以使用,例如:

branch

B命令会(以及其他内容)查找F的父级,并在git show <sha1-for-F> git show之间运行差异,以向我们展示有变化。

我们可以只使用分支名称

,而不是写出完整(或部分)SHA-1
F

通常,如果原始SHA-1起作用,分支名称也会起作用(但反之亦然)。

当然,由于SHA-1不合适,所以有很多方法可以命名这些东西;但是,让我们暂时忽略它,最后进入&#34;壁球合并&#34;和&#34;樱桃挑选&#34;。

让我们&#34;取消&#34;合并到E,以便Fgit show branch 再次分开: 3

master

我们现在回到此提交图:

master

让我们说改变branch修复了一个讨厌的错误,我们想在$ git status # just checking! "git reset --hard" could lose work # On branch master nothing to commit, working directory clean $ git reset --hard master^ [snip] 中找到它的副本。我们尝试使用A - B - C - D <-- master \ E - F <-- branch 来接受它,但这也带来了变化F,但还没有准备好。

所以,现在我们只是&#34;樱桃挑选&#34;提交master

git merge branch

像往常一样,我们可以使用分支名称来识别分支顶端的(单个)提交。

这告诉git收集EF之间的更改,就像$ git cherry-pick branch [snip] 一样。但是,git不是向我们展示它们,而是尝试将这些更改修补到当前(E)提交中。由于我们在分支主服务器上,F提交是提交git show。因此,这会将更改从HEAD提取到HEAD,将它们应用于D,如果成功,则进行新的提交。我们称之为E(选择):

F

此处D内容可能与P内容完全不同,但更改 (从A - B - C - D - P <-- master \ E - F <-- branch P)与从FD更改相同。 PE的差异输出应该非常相似 - 行号可能会改变一点(甚至很多),但显示的更改应该相同。

让我们抛出F与我们之前抛出合并git show master的方式相同。 4 请注意,我们仍然在分支{{} 1}}这里,它仍然很干净(没有任何事情发生,没有什么可以承诺,即使我这次没有打扰git show branch):

P

这一次,我们来做一次&#34;挤压合并&#34; M

master的操作与常规合并非常相似,只是不使用两个父项合并提交git status,而是设置&# 34;壁球合并&#34;提交,让我们称之为$ git reset --hard master^ [snip] ,只有一个父级。它实际上并没有进行提交(壁球意味着branch)因此我们必须明确地执行提交部分:

git merge

现在我们有了这个:

M

提交S - 所有文件的集合 - 将与您通过常规合并获得的树相同。在这种情况下,这相当于将--no-commit$ git merge --squash branch Squash commit -- not updating HEAD Automatic merge went well; stopped before committing as requested $ git commit -m squash-merge [snip] 之间的A - B - C - D - S <-- master \ E - F <-- branch 作为单个补丁应用于提交S的树内容。< sup> 5

换句话说,git diff具有&#34; BF之间的变化加上DS&#34;之间的变化,适用于B。但它只有一个单亲。它的提交图差异使得它成为一个&#34;壁球合并&#34;而不是定期合并。

当然,如果常规合并没有工作 - 提交E尚未准备好E - 壁球合并也不会起作用。所以樱桃挑选是明智的选择。

重要的是:注意每次我们做了什么来分支F添加了新的提交 - 合并D,樱桃选择E或者squash-commit {{ 1}} - 分支master自动&#34;向前移动&#34;指向最新的提交。这就是git中将分支(或者#34;本地分支&#34;)与其他标签区分开来的原因。分支名称只是在添加新提交时自动移动的提交ID。标记名称的工作方式与分支名称完全相同,只是它们不会移动。


1 嗯,斑点使它们完好无损,但压缩(压缩压缩),只要它们是松散的物体&#34;。最终松散的物品被包装好了#34;为了节省更多空间,然后包可以进行增量压缩&#34;,在旧的基于delta的SCM中节省所有空间 - 实际上更多,因为任何一个对象都可以压缩任何其他对象对象,至少在理论上。 File foo不必仅针对&#34;以前版本的文件foo&#34;。

进行压缩

2 这主要是为了记录&#34;杀掉&#34;一个分支,作为简单地放弃它的替代方案。

3 master做了两件事:它将工作目录修改回它在提交M时的状态,并且它改变了分支标签{{1再次指向提交P。这里简单的S后缀告诉git遵循&#34;第一个父亲&#34;。另一个主要语法,&#34;返回N提交&#34; -eg,master返回3 - 也跟随&#34;第一个父母&#34;,所以来自merge commit reset --hardD将重新计入master,然后D,然后^。但是,重置生效后,master~3名称会再次提交M,因此返回3会转到master~3,然后是D,然后是C

4 顺便说一下,你可能 - 实际上,你应该 -wonder:这些提交会发生什么,我们随便&#34;抛出&#34 ;?答案是:他们住在回购中,通过&#34; reflog&#34;标记,直到reflog条目超时。默认情况下,reflog条目&#34;到期&#34; 90天之后如果它可以到达&#34; - 定义这个脚注过于技术性已经太长了 - 如果它已经过了30天而且#34;不可达&#34 ;.这些&#34;无法访问&#34;,因此它们将在大约一个月后到期。在那之后,提交以及只有这些被抛出的提交使用的任何树和blob在下一个B上被垃圾收集。

5 这假设您不会做一些奇怪的事情,例如将master策略应用于南瓜合并,但那将毫无用处。 (另外,与not-part-of-git D命令不同,git在进行合并和挑选时非常聪明,因为它通常可以判断您是否已经应用了某些特定补丁,而不是尝试申请两次。)

答案 1 :(得分:2)

是的,挤压会破坏提交历史记录。例如,如果您一直在单独的分支上处理某个功能,并且您不希望污染具有数百次提交的主线分支,则可以执行此操作。另一方面,您可能只想压缩正在关闭的分支,因为您失去了非压缩合并的跟踪优势。 (你可以查看树,看看在“正常”合并的情况下合并来自哪个提交;不是“压缩”合并。)

通常你会挑选其他分支的变化;您当前的分支将不包含您想要挑选的提交。