Git 使用重命名的文件进行变基

Git rebase with renamed files

我有一个重命名许多文件的分支,我正在尝试将其重新定位到 master 原始文件已被修改的地方(最好不要将其演变成手动解决冲突的噩梦) .

情况

  1. 我一直在将 JavaScript 项目移植到我本地 typescript 分支中的 TypeScript。所有 .js 文件现在都变成了 .ts 文件,并且一些语法已经升级。

  2. 同时,master 分支上发生了对原始 .js 文件的更改。

  3. 我想 rebase 我的 typescript 分支到 master -- 但更改没有正确合并,因为文件未检测到重命名——因此我们在对 .js 文件进行更改时发生冲突,git 认为这些文件已被删除(但它们实际上已重命名为 .ts 文件).

我认为我知道的

Git 图:

o-[typescript] .js files become .ts
|
| o-[master] changes to the .js files
| |
| o
|/
o-[root] common ancestor

因此,当站在 root 分支时:

解决思路?

  1. (不) 也许从 root 开始,我可以在检测重命名时合并到 typescript(像这样:git merge -X find-renames=10% typescript).然后,root 将与 typescript 一样,除了重命名(而不是质量 deletions/additions)。

    • 从那里开始,我希望我可以 git rebase master,并且在重命名到位后,操作将顺利进行并将编辑内容放入正确的文件中。
      • 更新:我试过了,但我后来的变基并没有比以前好多少。
  2. (是的) 我想也许 rebase 本身需要使用 find-renames 选项来执行。我正在调查这个。 .

    • git rebase -X find-renames=10% master -- 没有
    • git rebase -X --find-renames=10% master -- 没有
    • git rebase --find-renames=10% master -- 没有
    • git rebase -X recursive --find-renames=10% master -- 没有
    • git rebase --merge -X find-renames=10% master -- 没有
    • git rebase --strategy-option="rename-threshold=10" master -- 这就是门票!有效!
  3. (不) 也许我需要换个角度看问题?也许我应该从 master 开始,然后通过重命名检测对 typescript 进行某种压缩合并(而不是任何类型的变基?)

    • 我不需要 typescript 分支历史,无论如何它都会被压缩成一个 fat commit 以供审查..
    • 更新: 当我尝试这个策略时,看起来 Git 停止工作了。当我站在 root 分支时,我可以运行 git merge -X find-renames=10 typescript——它快进到 typescript..(不是我所希望的,我希望有一个重命名的新提交而不是 [=169= ..)
      • 当我站在 master 分支时,我 运行 完全相同的命令 , git 告诉我这个:fatal: Unknown option for merge-recursive: -Xfind-renames=10...
      • 现在,这让我很困扰,因为我实际上没有输入它引用的内容(我确实包括了 space),唯一对我来说不同的是我现在站在哪个分支在 -- 如果选项是 "Unknown",那么为什么它在不同的分支中 有效
      • 无论如何,这很令人毛骨悚然,而且似乎让这种方法走上了死胡同。

首先在 master 分支重做文件重命名可能会有所帮助。这似乎是一项相当机械的任务,如果需要可以自动化。

那么在此基础上对 typescript 分支进行变基可能会相当容易,因为已经有了共同点。变基 typescript 的一些提交最终(几乎)是空的,因为它们的更改已经包含在 master.

出于这个原因,您可能需要考虑将 typescript 合并到 master 中,以保持原始的 .js.ts 转换历史完整无缺。

您可以像这样在检测重命名时变基您的分支:

git rebase --strategy-option="rename-threshold=10" master

编辑: 从 Git 2.8.0 开始,术语 'rename-threshold' 已被弃用,取而代之的是 'find-renames' .

目前我使用的是 Git 2.7.4,因此我只能实际验证上述命令是否适用于我的情况——您可能需要使用术语 'find-renames' 如果上述命令在您的新版本 Git...

上对您的情况不起作用

在这个例子中,

  • 当前分支重新基于master
  • 指定了 10% 的重命名阈值
  • master 中对原始文件的更改将放在新的(重命名的)文件中 (与 运行 仅 git rebase master 相对)。
  • 请注意语法与 git diffgit loggit merge 命令中的类似重命名检测功能相比有何不同

git rebase 文档对于这个重命名检测方面不是很清楚。我想我在某个地方读到另一个命令,术语 "rename-threshold" 已被弃用,虽然在这个context find-renames 似乎不能用作同义词 - 所以如果有人知道 运行 这个相同命令的更好方法,请提及 :)

除了 find-rename rebase/merge strategy 之外,在 Git 2.29(2020 年第 4 季度)中,用于解释定序器机制放置的冲突块每一面的提交标签已变得更易于人类阅读.

参见 commit 7d056de (12 Aug 2020) by Elijah Newren (newren)
(由 Junio C Hamano -- gitster -- in commit 6cceea1 合并,2020 年 8 月 19 日)

sequencer: avoid garbled merge machinery messages due to commit labels

Signed-off-by: Elijah Newren
Reviewed-by: Taylor Blau
Acked-by: Johannes Schindelin

sequencer's get_message() exists to provide good labels on conflict hunks;
see commits

  • d68565402a ("revert: clarify label on conflict hunks", 2010-03-20, Git v1.7.1-rc0)
  • bf975d379d ("cherry-pick, revert: add a label for ancestor", 2010-03-20, Git v1.7.1-rc0)
  • 043a4492b3 ("sequencer: factor code out of revert builtin", 2012-01-11, Git v1.7.1-rc0).
    for background on this function.

These labels are of the form ... or parent of ...

These labels are then passed as branch names to the merge machinery.

However, these labels, as formatted, often also serve to confuse.

For example, if we have a rename involved in a content merge, then it results in text such as the following:

<<<<<<<< HEAD:foo.c
  int j;
========
  int counter;
>>>>>>>> b01dface... Removed unnecessary stuff:bar.c  

Or in various conflict messages, it can make it very difficult to read:

CONFLICT (rename/delete): foo.c deleted in b01dface... Removed
unnecessary stuff and renamed in HEAD.  Version HEAD of foo.c left
in tree.  

CONFLICT (file location): dir1/foo.c added in b01dface... Removed
unnecessary stuff inside a directory that was renamed in HEAD,
suggesting it should perhaps be moved to dir2/foo.c.  

Make a minor change to remove the ellipses and add parentheses around the commit summary; this makes all three examples much easier to read:

<<<<<<<< HEAD:foo.c
  int j;
========
  int counter;
>>>>>>>> b01dface (Removed unnecessary stuff):bar.c  

CONFLICT (rename/delete): foo.c deleted in b01dface (Removed
unnecessary stuff) and renamed in HEAD.  Version HEAD of foo.c left
in tree.  

CONFLICT (file location): dir1/foo.c added in b01dface (Removed
unnecessary stuff) inside a directory that was renamed in HEAD,
suggesting it should perhaps be moved to dir2/foo.c.