git rebase后历史会消失吗

use*_*427 1 git version-control

假设我有包含三个提交的主分支

A--B----C
Run Code Online (Sandbox Code Playgroud)

我创建新分支dev并在那里创建 3 个提交

A--B----C------C1------C2
         \
          E---F----G
Run Code Online (Sandbox Code Playgroud)

现在我像这样与主人合并

git merge dev --squash

现在我认为新图是这样的但不确定

A--B----C------C1------C2--------M----
         \                      /    
          E---F----G-----------/
Run Code Online (Sandbox Code Playgroud)

之后我需要将dev分支与 master 合并以获得提交 c1 和 c2

所以会是这样的

A--B----C------C1------C2--------M----
         \                      / \   
          E---F----G-----------/----m2 ---H--I
Run Code Online (Sandbox Code Playgroud)

dev并将master并行进行

我想知道

  1. 在对提交 EFG 进行压缩后,各个提交历史记录是否会出现在 dev 分支中
  2. 我想知道如果我必须使用 rebase 而不是合并,两个分支应该并行,那么图表会是什么

  3. 如果第一个问题的答案是肯定的,那么如果我使用 rebase 命令,单个历史提交会消失吗

tor*_*rek 5

让我用一些标签重新绘制第二张图:

\n\n
A--B--C-----C1----C2    <-- master\n       \\\n        E--F----G    <-- dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

(我假设C1和只是代表在while 、和 上C2发生的一些提交被添加到)。masterEFGdev

\n\n

如果您现在启动master并运行git merge dev --squash(然后提交结果:--squash抑制最后的提交步骤),您会得到以下内容,这与您绘制的内容非常不同:

\n\n
A--B--C-----C1----C2---M    <-- master\n       \\\n        E--F----G    <-- dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

git merge 如果你没有 运行,你就会得到你画的东西--squash--squash引用文档,该标志将:

\n\n
\n

生成工作树和索引状态,就好像发生了真正的合并(合并信息除外),但实际上并不进行提交或移动 HEAD,也不记录 $GIT_DIR/MERGE_HEAD来导致下一个 git commit 命令用于创建合并提交。

\n
\n\n

换句话说,挤压合并根本不是合并(这就是为什么我对 git 的一些术语有“问题”:-) ...有些签出是签出,有些则不是;有些合并是合并,有些则不是;分支只是分支提示,除非它们不是,等等!)。“挤压合并”确实使用底层合并代码来确定下一次提交的内容应该是什么,但是如果当您进行实际提交(M上面标记的那个)时,它只是一个普通的非合并提交,这需要完成的工作\xe2\x80\x94所做的更改\xe2\x80\x94提交E通过G并将这些更改复制到C2. 将其视为“有人向您发送差异,将版本C与版本进行比较G,然后您手动将所有这些更改插入版本C2并提交(可能应该调用它C3,但我们改为调用它M)”。

\n\n
\n\n

如果您想要真正的合并,请在不进行合并的情况下进行合并 --squash. 事实上,这会给你所画的内容,我将在这里重新绘制:

\n\n
A--B--C-----C1----C2---M    <-- master\n       \\              /\n        E--F----G----/      <-- dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

(标签dev仍然指向 commitG。)

\n\n

接下来,如果您想选取C1和中的更改C2并将其放在dev,您可以执行以下两种操作之一:

\n\n
git checkout dev && git merge master\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者:

\n\n
git checkout dev && git merge --no-ff master\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里的区别在于你是否得到了m2你所绘制的提交。如果没有,git 会观察到和--no-ff的合并产生 commit ,并且只是将标签移动到指向devmasterMdevM

\n\n
A--B--C-----C1----C2---M    <-- dev, master\n       \\              /\n        E--F----G----/\n
Run Code Online (Sandbox Code Playgroud)\n\n

但与--no-ff,合并必须产生一个新的、不同的合并提交(相同的树,但仍然是不同的提交,具有不同的父年龄):

\n\n
A--B--C-----C1----C2---M    <-- master\n       \\               X\n        E--F----G------m2   <-- dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里 mergeMC2作为它的第一个父级和G第二个父级。这表明它是G2(“非第一个”父级)到master(第一个父级)的合并。m2另一方面,MergeG作为其第一个父级,并且C2第二个父级。

\n\n

换句话说,树(工作目录)是相同的,父级的集合(列表忽略顺序)是相同的,甚至提交消息和时间戳也可以是相同的:但是顺序父级的不同,仅此一点就足以保证这些将是不同的提交。

\n\n
\n\n

你的问题的标题是关于变基,而不是合并。Rebase 是完全不同的操作。假设您在分支上dev并运行git rebase master

\n\n

要进行变基,git 所做的是(本质上\xe2\x80\x94 应用了一些优化,以及一些合并提交的极端情况)简单地“挑选”沿某个分支的每个提交,然后应用这些“樱桃”到其他一些分支。让我们回到第一张图:

\n\n
A--B--C--C1--C2    <-- master\n       \\\n        E--F--G    <-- dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里的想法是“选择”提交E,然后F,然后G按顺序应用它们。让我们首先复制EE\',应用到 之上C2

\n\n
                E\'\n               /\nA--B--C--C1--C2    <-- master\n       \\\n        E--F--G    <-- dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

接下来我们复制FF\'这样:

\n\n
                E\'--F\'\n               /\nA--B--C--C1--C2    <-- master\n       \\\n        E--F--G    <-- dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

还有一个提交要复制GG\'

\n\n
                E\'--F\'--G\'\n               /\nA--B--C--C1--C2    <-- master\n       \\\n        E--F--G    <-- dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们都完成了,除了一件事:我们需要一个新的提交链的标签。我们就这么称呼它吧dev!哎呀,等等,我们已经有一个标签了dev。好吧,让我们撕下旧dev标签,然后将其粘贴到新标签上G\'

\n\n
                E\'--F\'--G\'   <-- dev\n               /\nA--B--C--C1--C2    <-- master\n       \\\n        E--F--G\n
Run Code Online (Sandbox Code Playgroud)\n\n

瞧,git rebase完成了。这就是它的作用:将旧的提交复制到新的提交。

\n\n

老人们会怎样?嗯,他们就是我所说的“被遗弃的”。它们仍然存在于您的存储库中,但它们的唯一标签仅存储在“reflogs”中。引用日志条目将在 30 天左右的默认过期超时时间内保留它们。(它是可配置的,并且实际上有两种不同的超时,但最好将其视为“一个月左右”。)因此,大约一个月后,git 将垃圾收集废弃的提交。

\n\n

有时变基是“坏的”,因为它为共享你的 git 存储库的其他人创造了更多的工作(直接地,或更可能的是,在你将你的工作推送到的某个中央存储库中)。假设其他人已经E通过 进行了提交G,并且他们做了更多的工作并H在 之上进行了提交G。然后你去复制E通过G,并放弃原来的E通过G。您现在已经完成了他们的工作,提交,但还需要更多工作:例如,H他们可能必须H在您的基础上重新建立基础。G\'

\n\n

如果没有其他人E通过您的提交G,您可以确定您没有为不存在的其他人做任何额外的工作。变基只会影响,所以大部分“坏”都会消失。

\n\n
\n\n

--no-ff最后,对于 rebase 与 merge(以及如果合并,是否以及何时使用)与“压缩合并并放弃原始开发链”与任何其他你可以想到的方案没有单一的答案。在 git 中进行提交,并通过合并、挑选和变基等操作它们的整个想法是为了更容易维护工作。所有对提交图的摆弄都是毫无意义的,除非它使维护实际工作变得更容易。

\n