我厌倦了 python god 模块,并将其分解为两个较小的模块,除了将函数/对象拉出来并将它们放入更具逻辑意义的文件中之外,重构也很有限。 不幸的是,这足以混淆 git 通常的合并逻辑,因为它认为第一个文件已被删除,并且添加了两个全新的文件。 现在,每当我必须合并 main 时,我都不会得到有用的合并,而是得到三个文件并被告知找出它们有何不同。
有没有办法获得更有用的合并,以显示原始文件和两个较小文件之间发生了什么变化?
有没有办法获得更有用的合并,以显示原始文件和两个较小文件之间发生了什么变化?
是的。作为示例内容,我使用了 Onat Korucu 的 this answer 中的代码,它是一个 Java 文件 (
LoginManager.java
),然后分成多个较小的文件(其中包括 DecryptHandler.java
)。
最初的一个文件内容被签入分支
one_file
,最重要的是,文件被分割成多个文件的新提交被签入分支 multiple_files
。之后,one_file
分支已更新,用一些虚拟代码替换其中一个函数的 TODO 主体。
接下来的挑战是将此更新从
one_file
分支引入/合并到 multiple_files
分支。只是尝试将 one_file
合并到 multiple_files
失败了,就像您所描述的那样。
让 git 执行此操作的关键是分多个步骤执行此操作,并为每个步骤使用单独的分支。对于原始文件分割成的每个文件,需要分别重复这些步骤,例如在你的情况下两次。
第一步是将原始文件重命名为文件分割的目标之一,例如在我的例子中
DecryptHandler.java
。我创建了一个分支
intermediate/DecryptHandler/filename
为此(即在此分支上 DecryptHandler.java
现在与原始 LoginManager.java
相同)。
第二步是使用
multiple_files
分支的内容更新重命名的文件,以便 DecryptHandler.java
现在在两个分支上相同。我为此创建了一个分支intermediate/DecryptHandler/content
。
历史现在看起来像这样:
gitk
:
第三步是将来自
one_file
的更新合并到文件名分支中。由于这个分支只是重命名,所以 git 没有任何问题,并且合并也没有任何问题。
第四步现在是将更新的
intermediate/DecryptHandler/filename
分支合并到 intermediate/DecryptHandler/content
分支中(注意,不是相反)。这次 git 将因冲突而放弃,但这只是正常的合并冲突,其中 git 记录了同一文件名的所有三个贡献版本,并且使用 KDiff3 轻而易举,KDiff3 自动解决了所有问题。
此时,我可以将
intermediate/DecryptHandler/content
合并到 multiple_files
中,然后就到此为止(因为我只有一个更改影响一个文件。当您有两个文件时,您可能需要对另一个文件重复操作)。
历史在
gitk
中看起来像这样:
然而,这将提供有问题的提交的历史记录,这些提交会破坏
git bisect
、git test
等,因此最好“合并”结果而不实际将其记录为合并。这可以通过实际合并完成的分支中的 git diff ... | git apply -
来完成。
所以第五步是合并到
multiple_files
以外的分支,但具有相同的提交。
git branch intermediate/DecryptHandler/merge multiple_files
git switch intermediate/DecryptHandler/merge
git merge intermediate/DecryptHandler/content
# Conflict because same file added independently on two branches.
# Simple to resolve with KDiff3 and it is clear that we want the B version.
git resolve-conflict-using-kdiff3
git commit
第六步是复制合并提交而不合并:
git switch multiple_files
git diff multiple_files intermediate/DecryptHandler/merge | git apply -
git add DecryptHandler.java
git commit -m "Stealth merge of one_file branch" -m "git diff multiple_files intermediate/DecryptHandler/merge | git apply -"
现在给出了一个
multiple_files
分支,它通过一些中间步骤“合并”了 one_file
中的内容。
历史现在看起来像这样:
gitk
:
现在,历史记录看起来有点嘈杂,很可能您希望在更新完成后排除那些中间分支的显示1。通过提供
--exclude=...
选项可以很简单地做到这一点。每次启动 gitk
时手动指定都太麻烦了,因此假设您有一个名为 gitkall
的包装脚本,用于 gitk --all
将其更新为以下内容:
#!/bin/sh
exec gitk \
"--exclude=refs/notes/*" \
"--exclude=refs/heads/hide/*" \
"--exclude=refs/heads/intermediate/*" \
--all \
"$@" &
忽略注释是针对
git test
保存的测试结果,我想隐藏所有名称以“hide/”开头的分支。第三个排除是忽略此答案中的中间分支。 (这里的顺序很重要,--exclude 选项必须位于--all 之前)。
用这个启动 gitk 将给出一个更简单的视图:
1此时您也可以删除它们,但是下次您想要进行更改时需要重新创建它们。