从另一个从 master 变基的分支变基
Rebase a branch from another branch that is rebased from master
我有这个场景:
o---o---o---o---o master
\
o---o---o---o---o next
\
o---o---o topic
如上所述,我在 master 上做了一些提交,我想更新其他两个分支并且有:
o---o master
\
o---o---o---o---o next
\
o---o---o topic
我已经在 next
分支上做了 git rebase master
,没想到 topic
现在还没有 next
分支的基础提交。
我现在可以做什么?我立即对中间分支进行变基是错误的吗?我该怎么办?
这个问题有两个相对简单的解决方案。一种是尝试 rebase,这可能会奏效。但如果没有,它仍然很容易。这是第二种,更强大的方法,其次是 why:
git checkout topic; git rebase --onto next next@{1}
发生了什么:变基如何工作
我认为,如果您以不同的方式绘制图形,视图会更清晰。这是原始的,略有重绘:
A---B---C---D---E <-- master
\
F---G---H---I---J <-- next
\
K---L---M <-- topic
这是 git checkout next; git rebase master
之后的结果:
A---B---C---D---E <-- master
\ \
| F'-G'-H'-I'-J' <-- next
\
F---G---H---I---J [was "next", but not anymore]
\
K---L---M <-- topic
这是因为变基是通过复制 提交来工作的,有一些轻微的(或主要的)变化。新提交 F'
是原始 F
的副本,G'
是 G
的副本,依此类推。
如果您现在 运行 git checkout topic; git rebase next
,它 通常 就可以正常工作,但并非总是如此。这样做的原因是,默认情况下,rebase
选择 提交复制 和 从单个参数复制后 .这有点复杂,所以让我们分两步进行。
当你在这里说 git rebase next
时,唯一的参数是 next
:副本将在 next
的 tip 提交之后(现在是 J'
)。这部分非常简单:一个标识符 next
找到附加副本的提交 --onto
。 (我拼写 --onto
而不是 "onto" 是有原因的。:-) )
复制的内容部分比较棘手,有两个方面。首先,next
指定不复制什么,而当前分支(topic
)指定到复制什么。 Git 获取可从 topic
访问的所有提交的列表——即提交 A
、B
、F
、G
、H
,以此类推一直到 L
。然后它 排除 所有可从 next
到达的提交,即提交 A
、B
、C
、D
、 E
、F'
(但不是 F
)、G'
(但不是 G
),依此类推直到 J'
。一些被排除的提交不在包含列表中并不重要:我们只是排除那些在包含列表中。
不幸的是,这将提交 F
、G
、H
、I
和 J
留在待提交的一堆中复制。正如您所注意到的,这就是出错的地方。但不知何故,大部分时间,这仍然有效。工作时发生了什么?为什么它 总是 有效?
答案是变基也排除"patch-equivalent commits"。这里的一般想法是,当你变基时,Git 扫描 "exclude" 列表以查看是否有任何这些提交 做同样的事情 作为提交在 "include" 列表中。由于 F'
是 F
的副本,它很可能是 "the same" 作为 F
,至少在补丁等效意义上是这样。 G
与 G'
、H
与 H'
等也是如此。
只要提交与其重新定位的版本具有相同的补丁,它们就会被排除在外。
如果这是真的——通常是这样——你只需 git checkout topic; git rebase next
就可以了。当它 不是 为真时——当 F
到 H
中的一些在被复制时被修改 "too much",并且它们的副本不再是补丁等效的-它失败了。
作为一种经验法则,1 如果 Git 能够独自完成第一次变基,那么它也能够完成第二次变基本身也是。主要是当它因冲突而停止并让你手动修复它时,补丁就会变成 "not-patch-equivalent"。但是处理这个很容易,因为 git rebase
有更长的形式。
而不是:
git rebase next
我们可以这样写:
git rebase --onto next <something>
根据我们绘制的图表,此处使用的 <something>
是 任何指定提交 J
的内容。也就是说,我们需要找到next
是的地方。 next
指向什么提交,在 我们 rebase 之前?它现在指向 J'
,但它过去指向 J
。我们需要 Git 排除的提交是 next
曾经 达到的提交:A
、B
、F
、G
、H
、I
和 J
。如果 Git 自己找不到这些,通过等价补丁,我们可以 告诉 它。
这仍然留下了如何拼写标识提交的名称的问题 J
。一种方法是使用原始哈希 ID,它总是有效,但有点麻烦(查找和复制,尽管剪切和粘贴对于复制哈希非常有效)。但是再看看重新绘制的图形,上面写着 was "next", but not anymore
。如果我们能告诉 Git 查找 next
.
的 previous 值就好了
但是等等! Git 有 个引用日志 !我们可以告诉它这样做! next
的先前值拼写为 next@{1}
。 (每次我们更改标签 next
时,数字都会增加,例如通过向其添加新的提交,但我们刚才所做的只是 rebase
,所以它仍然是 next@{1}
.) 所以在这一点上我们可以这样做:
git checkout topic; git rebase --onto next next@{1}
(取决于您的 shell,您可能需要在带有大括号的内容周围使用引号,例如 'next@{1}'
或 next@\{1\}
或类似的)。
1有一些例外。例如,在这种情况下,如果 git diff E F'
不会产生与 git diff B F
相同的变更集,因为 diff 选择了不同的半不相关的匹配项,例如空行或大括号行,那将造成问题。但是,处理这些的答案与处理由于冲突而手动应用的 rebase 的答案相同。
我有这个场景:
o---o---o---o---o master
\
o---o---o---o---o next
\
o---o---o topic
如上所述,我在 master 上做了一些提交,我想更新其他两个分支并且有:
o---o master
\
o---o---o---o---o next
\
o---o---o topic
我已经在 next
分支上做了 git rebase master
,没想到 topic
现在还没有 next
分支的基础提交。
我现在可以做什么?我立即对中间分支进行变基是错误的吗?我该怎么办?
这个问题有两个相对简单的解决方案。一种是尝试 rebase,这可能会奏效。但如果没有,它仍然很容易。这是第二种,更强大的方法,其次是 why:
git checkout topic; git rebase --onto next next@{1}
发生了什么:变基如何工作
我认为,如果您以不同的方式绘制图形,视图会更清晰。这是原始的,略有重绘:
A---B---C---D---E <-- master
\
F---G---H---I---J <-- next
\
K---L---M <-- topic
这是 git checkout next; git rebase master
之后的结果:
A---B---C---D---E <-- master
\ \
| F'-G'-H'-I'-J' <-- next
\
F---G---H---I---J [was "next", but not anymore]
\
K---L---M <-- topic
这是因为变基是通过复制 提交来工作的,有一些轻微的(或主要的)变化。新提交 F'
是原始 F
的副本,G'
是 G
的副本,依此类推。
如果您现在 运行 git checkout topic; git rebase next
,它 通常 就可以正常工作,但并非总是如此。这样做的原因是,默认情况下,rebase
选择 提交复制 和 从单个参数复制后 .这有点复杂,所以让我们分两步进行。
当你在这里说 git rebase next
时,唯一的参数是 next
:副本将在 next
的 tip 提交之后(现在是 J'
)。这部分非常简单:一个标识符 next
找到附加副本的提交 --onto
。 (我拼写 --onto
而不是 "onto" 是有原因的。:-) )
复制的内容部分比较棘手,有两个方面。首先,next
指定不复制什么,而当前分支(topic
)指定到复制什么。 Git 获取可从 topic
访问的所有提交的列表——即提交 A
、B
、F
、G
、H
,以此类推一直到 L
。然后它 排除 所有可从 next
到达的提交,即提交 A
、B
、C
、D
、 E
、F'
(但不是 F
)、G'
(但不是 G
),依此类推直到 J'
。一些被排除的提交不在包含列表中并不重要:我们只是排除那些在包含列表中。
不幸的是,这将提交 F
、G
、H
、I
和 J
留在待提交的一堆中复制。正如您所注意到的,这就是出错的地方。但不知何故,大部分时间,这仍然有效。工作时发生了什么?为什么它 总是 有效?
答案是变基也排除"patch-equivalent commits"。这里的一般想法是,当你变基时,Git 扫描 "exclude" 列表以查看是否有任何这些提交 做同样的事情 作为提交在 "include" 列表中。由于 F'
是 F
的副本,它很可能是 "the same" 作为 F
,至少在补丁等效意义上是这样。 G
与 G'
、H
与 H'
等也是如此。
只要提交与其重新定位的版本具有相同的补丁,它们就会被排除在外。
如果这是真的——通常是这样——你只需 git checkout topic; git rebase next
就可以了。当它 不是 为真时——当 F
到 H
中的一些在被复制时被修改 "too much",并且它们的副本不再是补丁等效的-它失败了。
作为一种经验法则,1 如果 Git 能够独自完成第一次变基,那么它也能够完成第二次变基本身也是。主要是当它因冲突而停止并让你手动修复它时,补丁就会变成 "not-patch-equivalent"。但是处理这个很容易,因为 git rebase
有更长的形式。
而不是:
git rebase next
我们可以这样写:
git rebase --onto next <something>
根据我们绘制的图表,此处使用的 <something>
是 任何指定提交 J
的内容。也就是说,我们需要找到next
是的地方。 next
指向什么提交,在 我们 rebase 之前?它现在指向 J'
,但它过去指向 J
。我们需要 Git 排除的提交是 next
曾经 达到的提交:A
、B
、F
、G
、H
、I
和 J
。如果 Git 自己找不到这些,通过等价补丁,我们可以 告诉 它。
这仍然留下了如何拼写标识提交的名称的问题 J
。一种方法是使用原始哈希 ID,它总是有效,但有点麻烦(查找和复制,尽管剪切和粘贴对于复制哈希非常有效)。但是再看看重新绘制的图形,上面写着 was "next", but not anymore
。如果我们能告诉 Git 查找 next
.
但是等等! Git 有 个引用日志 !我们可以告诉它这样做! next
的先前值拼写为 next@{1}
。 (每次我们更改标签 next
时,数字都会增加,例如通过向其添加新的提交,但我们刚才所做的只是 rebase
,所以它仍然是 next@{1}
.) 所以在这一点上我们可以这样做:
git checkout topic; git rebase --onto next next@{1}
(取决于您的 shell,您可能需要在带有大括号的内容周围使用引号,例如 'next@{1}'
或 next@\{1\}
或类似的)。
1有一些例外。例如,在这种情况下,如果 git diff E F'
不会产生与 git diff B F
相同的变更集,因为 diff 选择了不同的半不相关的匹配项,例如空行或大括号行,那将造成问题。但是,处理这些的答案与处理由于冲突而手动应用的 rebase 的答案相同。