将一个父项的提交重新定位到另一个父项

Rebasing commit from one parent onto another

我有一个正在处理的现有存储库,但最近我学到了很多关于良好实践和 git 本身的知识。我想按照自己的喜好改变我的历史,尤其是一些早期的提交。

以下是当前历史的片段。我想将 e5cb9b8 提交重新定位到 1a92e84,这样我就可以在历史图表中获得 one/two 级别的深度(在视觉上,更改 e5cb9b8 的父级)。我试过变基

git rebase -p --onto 1a92e84 e5cb9b8 master

并挑选到从 9ecbe00 创建的新分支。 Cherrypicking 让我的历史变得平坦,rebase 失败并显示以下消息

error: commit ca230d8c048d22de6f219da8a22e70a773827c38 is a merge but no -m option was given.

fatal: cherry-pick failed

Could not pick ca230d8c048d22de6f219da8a22e70a773827c38

* | 45a0a21 - (7 weeks ago) #17 Updates README.md - Kamil Pacanek
|/
*   ca230d8 - (8 weeks ago) Merge pull request #2 from KamilPacanek/feat/rmb-support-removing-parts - Kamil Pacanek
|\
| * e5cb9b8 - (8 weeks ago) Adds support for removing parts on ReactorCells - KamilPacanek
* |   1a92e84 - (8 weeks ago) Merge pull request #1 from KamilPacanek/enable-gh-pages - Kamil Pacanek
|\ \
| |/
| * 81761ff - (8 weeks ago) Adds GH Pages support - KamilPacanek
|/
* 9ecbe00 - (8 weeks ago) Initial commit - KamilPacanek

预计:

* | 45a0a21' - (7 weeks ago) #17 Updates README.md - Kamil Pacanek
|/
*   ca230d8' - (8 weeks ago) Merge pull request #2 from KamilPacanek/feat/rmb-support-removing-parts - Kamil Pacanek
|\
| * e5cb9b8' - (8 weeks ago) Adds support for removing parts on ReactorCells - KamilPacanek
|/
*    1a92e84 - (8 weeks ago) Merge pull request #1 from KamilPacanek/enable-gh-pages - Kamil Pacanek
|\ 
| |
| * 81761ff - (8 weeks ago) Adds GH Pages support - KamilPacanek
|/
* 9ecbe00 - (8 weeks ago) Initial commit - KamilPacanek

我在 Whosebug 上搜索了类似的问题,似乎没有人描述过如此深入历史的此类修改。

解决方案

好的,为了未来的读者和我自己,我正在写下我的问题的解决方案。 @alfunx 是解决它的关键 - 我在 git rebasegit rebase --onto 的其他主题解释中找到并开始在我的存储库上进行试验。通过反复试验,我设法*通过执行

实现了预期的历史图
git rebase -ir --onto 1a92e84 81761ff develop

并替换待办事项列表的后续片段

label onto

# Branch KamilPacanek/feat/rmb-support-removing-parts
reset onto
pick e5cb9b8 Adds support for removing parts on ReactorCells
label KamilPacanek/feat/rmb-support-removing-parts

# Branch enh/add-uranium-cells
reset 9ecbe00 # Initial commit
merge -C 1a92e84 onto # Merge pull request #1 from KamilPacanek/enable-gh-pages
merge -C ca230d8 KamilPacanek/feat/rmb-support-removing-parts # Merge pull request #2 from KamilPacanek/feat/rmb-support-removing-parts
label branch-point

以下(更改了重置位置并删除了一个,多余的,合并提交):

label onto

# Branch KamilPacanek/feat/rmb-support-removing-parts
reset onto
pick e5cb9b8 Adds support for removing parts on ReactorCells
label KamilPacanek/feat/rmb-support-removing-parts

# Branch enh/add-uranium-cells
reset onto
merge -C ca230d8 KamilPacanek/feat/rmb-support-removing-parts # Merge pull request #2 from KamilPacanek/feat/rmb-support-removing-parts
label branch-point

*) 实际上,要达到那种状态还有很多工作要做,但我不想用次要问题混淆解决方案。第一个 git rebase 失败并显示该消息:

error: refusing to update ref with bad name 'refs/rewritten/Implement-durability-loss.'

hint: Could not execute the todo command

hint: label Implement-durability-loss.

如您所见,我在提交消息的末尾有一个句号。在我解决了这个问题之后(通过 reword 命令的另一个变基)我能够继续前进。

此外,似乎 git rebase --abort doesn't cleargit rebase -r 期间创建的 refs。所以我的历史记录一团糟。通过 rm -rf .git/refs/rewritten 解决了那个问题。另一件事,我有一个剩余的 .git/sequencer 文件夹,可以通过 git revert --quit.

移除

你为 rebase 选择的基数是错误的,应该是 81761ff 而不是 e5cb9b8。我建议您进行交互式变基并使用 --rebase-merges 而不是 --preserve-merges。所以命令应该是:

git rebase -ir --onto 1a92e84 81761ff master

现在,Git 可能会产生一个额外的合并提交。为避免这种情况并产生您想要的结果,您应该将 todo-list 调整为:

label onto
pick e5cb9b8 Adds support for removing parts on ReactorCells
label new
reset onto
merge -C ca230d8 new
pick 45a0a21 #17 Updates README.md
# Remaining commits...

在todo-list中,label可以用来标记当前提交(HEAD),而reset可以用来设置HEAD 一些 commit/label。 merge 显然用于生成合并提交,-C 使合并使用与原始合并提交相同的提交消息。

这是相当高级的,实际上大多数用户和用例不需要,所以请考虑 man git-rebase,部分 Rebase Merges(或the online version here)以获得更深入的信息。那里的例子其实和你的情况很相似

如果您使用 git rebase -ir,请使用 Git 2.25(2020 年第一季度)。

由“git rebase --rebase-merges”生成的 todo 列表中使用的标签用作引用名称的一部分;提出标签的逻辑已经收紧,以避免不能这样使用的名称。

参见 commit cd55222 (17 Nov 2019) by Matthew Rogers (soniqua)
参见 commit 867bc1d (17 Nov 2019) by Johannes Schindelin (dscho)
(由 Junio C Hamano -- gitster -- in commit 917d0d6 合并,2019 年 12 月 5 日)

rebase -r: let label generate safer labels

Signed-off-by: Matthew Rogers
Signed-off-by: Johannes Schindelin

The label todo command in interactive rebases creates temporary refs in the refs/rewritten/ namespace. These refs are stored as loose refs, i.e. as files in .git/refs/rewritten/, therefore they have to conform with file name limitations on the current filesystem in addition to the accepted ref format.

This poses a problem in particular on NTFS/FAT, where e.g. the colon, double-quote and pipe characters are disallowed as part of a file name.

Let's safeguard against this by replacing not only white-space characters by dashes, but all non-alpha-numeric ones.

However, we exempt non-ASCII UTF-8 characters from that, as it should be quite possible to reflect branch names such as ↯↯↯ in refs/file names.


并且:

使用 Git 2.25(2020 年第一季度),避免由“git rebase --rebase-merges”生成的重复标​​签名称的逻辑忘记了机器本身使用“onto”作为标签名称,自动生成的标签必须避免这种情况,已更正。

参见 commit e02058a (18 Nov 2019) by Doan Tran Cong Danh (congdanhqx-zz)
(由 Junio C Hamano -- gitster -- in commit 995b1b1 合并,2019 年 12 月 5 日)

sequencer: handle rebase-merges for "onto" message

Signed-off-by: Doan Tran Cong Danh
Acked-by: Johannes Schindelin

In order to work correctly, git rebase --rebase-merges needs to make initial todo list with unique labels.

Those unique labels is being handled by employing a hashmap and appending an unique number if any duplicate is found.

But, we forget that beside those labels for side branches, we also have a special label onto for our so-called new-base.

In a special case that any of those labels for side branches named `onto', git will run into trouble.

Correct it.