假设我们有以下工作流:
D <-HEAD
|
C
|
B
|
A假设我想在C和D之间添加一个新的提交X,这样它就可以:
D
|
X
|
C
|
B
|
A所以我使用git rebase -i HEAD~2并为commit C选择'edit‘,这样当rebase到达“编辑”commit C时,我修改了一些东西,然后使用git add .和git commit,之后我应用git rebase --continue来结束rebase。但我得到了以下信息:
D
/
X C
| /
B
|
A这不是我在提交C之后添加了一个新的提交吗?为什么新的提交X紧随B之后
发布于 2020-03-29 03:10:03
假设我想在C和D之间添加一个新的
X,这样它就可以:
D|X|C|B|A
实际上根本不会是这样的。
让我用新的提交向右水平绘制这些,因为我认为这不那么令人困惑(而且对我来说也更容易)。您最初拥有:
...--A--B--C--D <-- branch当您执行这种rebase (或任何一系列产生您想要的结果的Git命令)时,您最终会将旧的提交D复制到一个新的、改进的提交D2或D' (我将使用D'表示法)。这样做的原因是,关于任何提交的任何内容都不能更改,这包括它的父提交散列。现有提交C的散列ID是固定的,现有提交D本身也是固定的,并且包含现有提交C的散列ID。因此,必须放弃提交D,而支持新的改进的提交。
你根本不打算做任何事情来提交A,所以Git可以不去管它,不需要重新复制它。B和C也是如此。根据您运行git rebase的方式,您可以使用强制 Git将这些内容重新复制到新的提交中(使用新的和不同的散列ID)。如果你这样做了,这些新的提交将在除两个之外的所有方面与原始提交相匹配:
,
但是,如果你不强制rebase复制,它就会在可能的情况下让原始提交保持原样。因此,因为不需要更改A、B或C,所以Git也不需要更改。
不过,要添加X,Git需要创建新的提交X,其父对象是现有的提交C
D <-- branch
/
...--A--B--C--X <-- HEAD [rebuild in progress]如果您在构造提交X之后检查您的提交图,您将看到以下内容。
通过git rebase -i和暂停构造提交X之后,您就可以运行git rebase --continue了。这会将现有的提交D复制到新的提交D'
D <-- branch
/
...--A--B--C--X--D' <-- HEAD [rebuild in progress]如果您设法在这一点上停止了Git (例如,将D标记为edit就可以做到这一点)并仔细检查提交图,您现在就会看到上面的内容。但是,让Git继续下去,git rebase的最后一步是移动分支名称,并将HEAD重新附加到分支名称,如下所示:
D [abandoned]
/
...--A--B--C--X--D' <-- branch (HEAD)由于没有可以轻松找到提交D的名称,因此即使使用git log --all --decorate --oneline --graph,git log也不会显示它。您现在将只看到D'。
如果您不记得原始提交D的实际散列ID,那么看起来新的提交D'就是原始提交D。不是的!Git知道它不是,因为Git使用散列it来识别提交。分支名称无关紧要:重要的是散列ID。
Git使用分支名称查找起始散列ID,这是链中的最后一个提交。这就是为什么,在这些较新的提交位于右侧的绘图中,我们将分支名称放在最右侧,并带有一个向后箭头。Git总是向后工作:名称查找链中的最后一个提交,然后从那里,Git使用存储的散列ID向后工作,一次一个提交。
(当Git遇到具有两个或更多向后箭头的合并提交时,Git仍然必须一次执行一个提交,主要是这样。为了处理这个问题,这些Git命令保留了一个散列ID队列。队列是一个优先级队列,其中较高优先级的提交在较低优先级的提交之前被访问。什么使提交优先级更高或更低取决于您为git log或git rev-list提供的--topo-order、--graph、--date-order、--committer-date-order或其他排序选项。)
https://stackoverflow.com/questions/60901503
复制相似问题