关于 git rebase 到选项的一些问题
Posted
技术标签:
【中文标题】关于 git rebase 到选项的一些问题【英文标题】:Some question about git rebase onto option 【发布时间】:2022-01-19 06:42:57 【问题描述】:主分支:
百灵枝:
变基后 奇怪!,lark_file 在哪里?嗯,应该是三个文件(init、main_file、lark_file)吧?
创建环境的脚本。
#!/bin/env bash
git init
touch init
git add -- .
git commit -am init
echo a >> main_file
git add --all
git commit -am 'main a'
echo b >> main_file
git commit -am 'main b'
echo c >> main_file
git commit -am 'main c'
echo d >> main_file
git commit -am 'main d'
echo e >> main_file
git commit -am 'main e'
git checkout HEAD~5
git checkout -B lark
echo a >> lark_file
git add --all
git commit -am 'lark a'
echo b >> lark_file
git commit -am 'lark b'
echo c >> lark_file
git commit -am 'lark c'
echo d >> lark_file
git commit -am 'lark d'
echo e >> lark_file
git commit -am 'lark e'
echo f >> lark_file
git commit -am 'lark f'
【问题讨论】:
【参考方案1】:TL;DR:由于 rebase 的重命名检测,Git 将更改应用到错误的文件。 (--onto
标志不是获得此效果所必需的。您只需要有一个差异,在其中 Git 会产生错误/不正确的重命名。)
长
首先,让我稍微修改一下您的复制器,以便每个人都可以使用它:
$ cat repro.sh
#! /bin/sh -e
mkdir t
cd t
git init
touch init
git add -- .
git commit -am init
echo a >> main_file
git add --all
git commit -am 'main a'
echo b >> main_file
git commit -am 'main b'
git tag tag-onto
echo c >> main_file
git commit -am 'main c'
echo d >> main_file
git commit -am 'main d'
echo e >> main_file
git commit -am 'main e'
git checkout HEAD~5
git checkout -B lark
echo a >> lark_file
git add --all
git commit -am 'lark a'
echo b >> lark_file
git commit -am 'lark b'
echo c >> lark_file
git commit -am 'lark c'
echo d >> lark_file
git commit -am 'lark d'
echo e >> lark_file
git commit -am 'lark e'
echo f >> lark_file
git commit -am 'lark f'
git rebase --onto tag-onto HEAD~3
这有一些简单的变化:它在其中创建了一个临时目录 t
和 cd
-s,这样我们就可以在完成后删除临时目录,并且复制器脚本本身不会不要卡在存储库中。更有用的是,它将最后一个 git rebase
更改为:
git rebase --onto tag-onto HEAD~3
也就是说,我们删除了最终的HEAD
——它毫无意义地产生了一个分离的 HEAD——我们使用标签 tag-onto
作为--onto
目标,我们将在该目标上复制lark d
提交.
运行此脚本会重现该问题:
$ ./repro.sh
[much output snipped]
CONFLICT (content): Merge conflict in main_file
error: could not apply 1a3193f... lark d
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 1a3193f... lark d
这里的设置是我们尝试将提交 lark~2
挑选到提交 tag-onto
(master~3
或 main~3
取决于您的初始分支)。
我们需要意识到git cherry-pick
是git merge
的一种。它进行三向合并,合并基础是提交的父级。这意味着它运行两个git diff
s,从父lark~3
到当前提交HEAD
,从父lark~3
到提交lark~2
。让我们看一下这两个差异中的第一个:
$ git diff lark~3 HEAD
diff --git a/lark_file b/main_file
similarity index 66%
rename from lark_file
rename to main_file
index de98044..422c2b7 100644
--- a/lark_file
+++ b/main_file
@@ -1,3 +1,2 @@
a
b
-c
这表示我们应该重命名文件:现在应该将其命名为 main_file
,而不是 lark_file
。
第二个差异当然显示了您在提交 lark d
中添加的内容:
$ git diff lark~3 lark~2
diff --git a/lark_file b/lark_file
index de98044..d68dd40 100644
--- a/lark_file
+++ b/lark_file
@@ -1,3 +1,4 @@
a
b
c
+d
所以 Git 决定我们需要将 lark_file
重命名为 main_file
并在末尾添加 d
,同时从 main_file
的末尾删除 c
。
这确实是 Git 所做的:我们现在有一个文件 main_file
,而不是两个单独的文件 lark_file
和 main_file
,我们看到了冲突。我的是diff3
风格而不是merge
风格,所以它有更多信息:
$ cat main_file
a
b
<<<<<<< HEAD:main_file
||||||| parent of 1a3193f... lark d:lark_file
c
=======
c
d
>>>>>>> 1a3193f... lark d:lark_file
Git 的行为符合 Git 规则。
Git 合并规则时不时会产生意外,这就是为什么您必须始终检查 任何 合并结果的原因。这包括挑选结果,即合并结果。
【讨论】:
好吧,樱桃采摘部分真的让我很困惑,我不得不说我没有遵循。 樱桃挑选是一种合并形式。通过查找 三个 提交而不是两个提交来进行合并。这对于传统的合并更有意义,在这种合并中,您实际上是从一个共同的起点开始合并工作,但它也用于挑选樱桃。 Rebase 是重复挑选的,因此每个“复制提交”步骤都使用git cherry-pick
。这就是所有这些的来源。要正确理解merging,你应该先进入merging这个话题。以上是关于关于 git rebase 到选项的一些问题的主要内容,如果未能解决你的问题,请参考以下文章