Git rebase 的 --merge 选项有什么作用?
What does the --merge option of Git rebase do?
手册页 git-rebase(1) 说:
-m
--merge
Use merging strategies to rebase. [...]
当然,不使用 --merge
选项也可以 运行 变成 "merge conflicts"。所以在那种情况下也必须有任何 "merge strategy" 来处理这些冲突。
变基的 --merge
选项有何不同。
这似乎是相当基本的东西:对于 rebase --merge
,Git 将其工作文件存储在名为 $GIT_DIR/rebase-merge
的文件夹中(就像它对交互式变基所做的那样)。如果未使用 --merge
选项(并且 rebase 是非交互式的),则该文件夹被命名为 $GIT_DIR/rebase-apply
.
一句话,-m
或--merge
对git rebase
的作用是确保rebase在内部使用git cherry-pick
。
强制选择的 -m
标志通常是多余的,但并非总是如此。特别是,任何 interactive rebase 总是使用 cherry-pick。作为 ,指定任何 -s
或 -X
选项也会强制使用 cherry-pick。 -k
也是如此,如下所述。
长(或至少更长)
Rebase 在 Git 中有着悠久的历史:第一个 rebase 操作是通过将每个要重新定位的提交格式化为补丁,然后将补丁应用于其他提交来完成的。也就是说,最初 git rebase
主要是:
branch=$(git symbolic-ref --short HEAD)
target=$(git rev-parse ${onto:-$upstream})
git format-patch $upstream..HEAD > $temp_file
git checkout $target
git am -3 $temp_file
git checkout -B $branch HEAD
(除了参数处理、所有错误检查以及 git am
可以因错误而停止的事实,需要手动修复和 git rebase --continue
;另外,上面的脚本是我的可读性降低的版本,可能与原始脚本不太相似)。
这种 rebase 可以很好地处理大多数情况。它 不能 处理的最常见的情况涉及跨一些文件重命名的变基。它也不能复制 "empty" 提交——补丁为空的提交,也就是说——因为 git format-patch
不允许省略补丁部分。
即使使用 -m
,这些空提交通常也会被 git rebase
忽略;您必须添加 -k
才能保留它们。为了保留它们,git rebase
必须切换到 cherry-pick 变体,如果它还没有这样做的话。
要传递 -s
或 -X
参数,rebase 必须调用 git cherry-pick
而不是 git am
,因此这些标志中的任何一个也需要 cherry-pick 变体。
使用 git format-patch
从不进行任何重命名检测。因此,如果您正在复制的提交流都应该对 HEAD
应用重命名检测,则 -m
标志非常重要。举一个具体的例子,考虑这一系列的提交:
B--C--D <-- topic
/
...--o--A--E--F--G <-- mainline
假设A
到B
、B
到C
、C
到D
的差异都在内部处理一个名为 lib-foo.ext
的文件。但是在提交 F
中,此文件 重命名 为 lib/foo.ext
。 A..D
的 git format-patch
将显示对文件 lib-foo.ext
所做的更改,其中 none 将正确应用于提交 G
,因为没有 [=42] =] 文件。整个变基将失败。
提交 B
的 git cherry-pick
当 HEAD
标识提交 G
时,将找到重命名并应用 A
-vs-B
在提交 G
:
中更改 lib/foo.ext
的版本
B--C--D <-- topic
/
...--o--A--E--F--G <-- mainline
\
B' <-- HEAD [detached]
C
的下一个 cherry-pick,而 HEAD
标识 B'
,将发现 B
-to-C
更改为 libfoo.ext
应该应用于重命名的 lib/foo.ext
,D
的最后一个 cherry-pick 也会做同样的事情,这样 rebase 就会成功。
重命名检测代码很慢,因此 没有 重命名的 rebase,没有 "empty" 提交保留,可以 运行 很多通过 git format-patch | git am
系统 运行 时更快。这是原始方法优于 cherry-pick 变体的唯一方式:它在受限情况下更快。 (但是,只有当有很多重命名 候选 ,但其中 none 是实际的 renames 或none 其中很重要。)
(旁注:-3
参数,或 --3way
使用更长的拼写,告诉 git am
将该标志传递给每个 git apply
,其中如果需要,apply 将尝试进行三向合并,使用 diff 中 index
行中的 blob 哈希。在某些情况下,这似乎 可能 足以处理重命名的文件——特别是如果 blob 哈希完全匹配。cherry-pick 方法进行完全重命名检测,处理不精确匹配;-3
不能那样做。另见 , as 。)
手册页 git-rebase(1) 说:
-m
--merge
Use merging strategies to rebase. [...]
当然,不使用 --merge
选项也可以 运行 变成 "merge conflicts"。所以在那种情况下也必须有任何 "merge strategy" 来处理这些冲突。
变基的 --merge
选项有何不同。
这似乎是相当基本的东西:对于 rebase --merge
,Git 将其工作文件存储在名为 $GIT_DIR/rebase-merge
的文件夹中(就像它对交互式变基所做的那样)。如果未使用 --merge
选项(并且 rebase 是非交互式的),则该文件夹被命名为 $GIT_DIR/rebase-apply
.
一句话,-m
或--merge
对git rebase
的作用是确保rebase在内部使用git cherry-pick
。
强制选择的 -m
标志通常是多余的,但并非总是如此。特别是,任何 interactive rebase 总是使用 cherry-pick。作为 -s
或 -X
选项也会强制使用 cherry-pick。 -k
也是如此,如下所述。
长(或至少更长)
Rebase 在 Git 中有着悠久的历史:第一个 rebase 操作是通过将每个要重新定位的提交格式化为补丁,然后将补丁应用于其他提交来完成的。也就是说,最初 git rebase
主要是:
branch=$(git symbolic-ref --short HEAD)
target=$(git rev-parse ${onto:-$upstream})
git format-patch $upstream..HEAD > $temp_file
git checkout $target
git am -3 $temp_file
git checkout -B $branch HEAD
(除了参数处理、所有错误检查以及 git am
可以因错误而停止的事实,需要手动修复和 git rebase --continue
;另外,上面的脚本是我的可读性降低的版本,可能与原始脚本不太相似)。
这种 rebase 可以很好地处理大多数情况。它 不能 处理的最常见的情况涉及跨一些文件重命名的变基。它也不能复制 "empty" 提交——补丁为空的提交,也就是说——因为 git format-patch
不允许省略补丁部分。
即使使用 -m
,这些空提交通常也会被 git rebase
忽略;您必须添加 -k
才能保留它们。为了保留它们,git rebase
必须切换到 cherry-pick 变体,如果它还没有这样做的话。
要传递 -s
或 -X
参数,rebase 必须调用 git cherry-pick
而不是 git am
,因此这些标志中的任何一个也需要 cherry-pick 变体。
使用 git format-patch
从不进行任何重命名检测。因此,如果您正在复制的提交流都应该对 HEAD
应用重命名检测,则 -m
标志非常重要。举一个具体的例子,考虑这一系列的提交:
B--C--D <-- topic
/
...--o--A--E--F--G <-- mainline
假设A
到B
、B
到C
、C
到D
的差异都在内部处理一个名为 lib-foo.ext
的文件。但是在提交 F
中,此文件 重命名 为 lib/foo.ext
。 A..D
的 git format-patch
将显示对文件 lib-foo.ext
所做的更改,其中 none 将正确应用于提交 G
,因为没有 [=42] =] 文件。整个变基将失败。
提交 B
的 git cherry-pick
当 HEAD
标识提交 G
时,将找到重命名并应用 A
-vs-B
在提交 G
:
lib/foo.ext
的版本
B--C--D <-- topic
/
...--o--A--E--F--G <-- mainline
\
B' <-- HEAD [detached]
C
的下一个 cherry-pick,而 HEAD
标识 B'
,将发现 B
-to-C
更改为 libfoo.ext
应该应用于重命名的 lib/foo.ext
,D
的最后一个 cherry-pick 也会做同样的事情,这样 rebase 就会成功。
重命名检测代码很慢,因此 没有 重命名的 rebase,没有 "empty" 提交保留,可以 运行 很多通过 git format-patch | git am
系统 运行 时更快。这是原始方法优于 cherry-pick 变体的唯一方式:它在受限情况下更快。 (但是,只有当有很多重命名 候选 ,但其中 none 是实际的 renames 或none 其中很重要。)
(旁注:-3
参数,或 --3way
使用更长的拼写,告诉 git am
将该标志传递给每个 git apply
,其中如果需要,apply 将尝试进行三向合并,使用 diff 中 index
行中的 blob 哈希。在某些情况下,这似乎 可能 足以处理重命名的文件——特别是如果 blob 哈希完全匹配。cherry-pick 方法进行完全重命名检测,处理不精确匹配;-3
不能那样做。另见