git与重命名的文件合并

时间:2010-04-23 20:27:05

标签: git

我有一个大型网站,我正在进入一个新的框架,并在此过程中添加git。当前站点没有任何版本控制。

我首先将网站复制到新的git存储库中。我创建了一个新的分支,并进行了使其与新框架一起工作所需的所有更改。其中一个步骤是更改所有页面的文件扩展名。

现在,在我处理新网站的时候,已经对旧网站上的文件进行了更改。所以我切换到master并复制了所有这些更改。

问题是当我将分支与新框架合并回master时,主分支上的每个文件都会发生冲突。

我不会担心它,但有几百个文件有变化。我试过git rebasegit rebase --merge没有运气。

如何在不处理每个文件的情况下合并这两个分支?

6 个答案:

答案 0 :(得分:59)

从git 1.7.4开始,您可以将合并的重命名阈值指定为git merge -X rename-threshold=25,以便控制25%的相似性已经足以考虑两个文件重命名候选。根据案例和-X ignore-space-change,这可能会使重命名检测更可靠。

然而,我希望有更多的直接控制,并在最后几天制作相关的脚本。也许它有所帮助 - 让我知道。

https://gist.github.com/894374

答案 1 :(得分:16)

由于重命名检测,应该可以自动工作。下面是示例会话:

$ git init test
Initialized empty Git repository in /tmp/jnareb/test/.git/
$ cp ~/git/README .    # example file, large enough so that rename detection works
$ git add .
$ git commit -m 'Initial commit'
[master (root-commit) b638320] Initial commit
 1 files changed, 54 insertions(+), 0 deletions(-)
 create mode 100644 README
$ git checkout -b new-feature        
Switched to a new branch 'new-feature'
$ git mv README README.txt
$ git commit -m 'Renamed README to README.txt'
[new-feature ce7b731] Renamed README to README.txt
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename README => README.txt (100%)
$ git checkout master
Switched to branch 'master'
$ sed -e 's/UNIX/Unix/g' README+ && mv -f README+ README
$ git commit -a -m 'README changed'
[master 57b1114] README changed
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git merge new-feature 
Merge made by recursive.
 README => README.txt |    0
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename README => README.txt (100%)

如果您在'new-feature'分支上执行“git merge master”而不是像上面那样在'master'上执行“git merge new-feature”,那么您将得到:

$ git merge master
Merge made by recursive.
 README.txt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

你能说出你在做什么不同吗?

请注意,普通的“git rebase”(和“git pull --rebase”)不会重新命名:您需要运行“git rebase -m”或交互式rebase。

答案 2 :(得分:2)

我想出了一个解决办法。由于文件的重命名是由脚本完成的,因此我能够复制新的.php文件并在合并之前重新运行脚本。由于文件具有相同的名称,因此合并无冲突。

以下是整个过程的步骤。

  1. 创建git repo git init
  2. 复制
  3. 中的现有文件
  4. 提交
  5. 运行脚本以重命名文件
  6. 提交
  7. 创建分支但不检查
  8. 随时修改提交更改
  9. 查看您在第6步中创建的分支
  10. 复制新版本的文件
  11. 运行脚本以重命名文件(这应该替换第一次运行的文件)
  12. 提交
  13. 结账大师
  14. 将分支合并为主
  15. 这是有效的,因为git对使用新名称的文件进行了更改。

答案 3 :(得分:2)

在我重命名检测失败的情况下,我发现在合并解析期间我可以执行以下操作:

假设:

fileA: A modified file that was moved to the new place but is currently in the old place.
destB: The location where fileB was moved to. This could include a new filename.

运行以下命令:

git add fileA
git mv fileA destB

这就是我必须做的一切。然后我承诺并继续坚持。

答案 4 :(得分:1)

十年后,随着 Git 2.31(2021 年第一季度)的推出,新的合并策略应该会有所帮助:ORT(“表面上递归的双胞胎”)。

它在重命名检测方面做了大量的性能优化工作,超越了简单的 rename-threshold

参见 commit f78cf97commit 07c9a7fcommit bd24aa2commit da09f65commit a35df33commit f384525commit 829514c(2021 年 2 月 14 日) ,以及 commit f15eb7c(2021 年 2 月 3 日)由 Elijah Newren (newren).
(由 Junio C Hamano -- gitster --commit 12bd175 合并,2021 年 3 月 1 日)

例如:

<块引用>

diffcore-rename:计算源和目标候选的基名

签字人:Elijah Newren

<块引用>

我们希望在剩余的源文件和目标文件中使用唯一的基本名称来帮助通知重命名检测,以便可以首先检查更有可能的配对。
(如果在剩余的已删除和添加的文件中没有其他“src/moduleA/foo.txt”文件,则 source/module/A/foo.txtfoo.txt 很可能相关。)

添加一个尚未使用的新函数,该函数创建 rename_src 内的唯一基名和 rename_dst, 内的另一个以及 rename_src/rename_dst 内显示这些基名的索引的映射。

非唯一的基本名称仍会显示在地图中,但索引无效 (-1)。

此功能的灵感来自这样一个事实:在现实世界的存储库中,文件经常在不更改名称的情况下跨目录移动。

以下是一些示例存储库及其保留基本名称的历史重命名(截至 2020 年初)的百分比:

  • Linux:76%
  • gcc:64%
  • 壁虎:79%
  • webkit:89%

仅凭这些统计数据并不能证明该领域的优化会有帮助或有多大帮助,因为还有未配对的添加和删除、对我们考虑的基名的限制等,但它确实激发了这个想法在这方面尝试一些东西。

还有:

<块引用>

diffcore-rename:完成find_basename_matches()

签字人:Elijah Newren

<块引用>

在现实世界的存储库中,大多数文件重命名不更改文件的基本名称的情况并不少见;即

大多数“重命名”只是将文件移动到不同目录中。

我们可以利用这一点来避免将所有重命名源候选与所有重命名目标候选进行比较,首先将源与具有相同基本名称的目标进行比较。

  • 如果具有相同基名的两个文件足够相似,我们记录重命名; - 如果没有,我们会将这些文件包含在更详尽的矩阵比较中。

这意味着我们要添加一组初步的额外比较,但对于每个文件,我们最多只将它与一个其他文件进行比较。

例如,如果有一个include/media/device.h被删除,一个src/module/media/device.h被添加,并且之后添加和删除的文件的剩余集合中没有其他device.h文件精确重命名检测,那么这两个文件将在初步步骤中进行比较。

这个提交实际上还没有使用这个新的优化,它只是添加了一个可以用于此目的的函数。

请注意,与未进行优化相比,此优化可能会给我们带来不同的结果,因为尽管具有相同基名的文件足够相似可以被视为重命名,但可能会在不具有相同基名的文件之间提供更好的匹配。

我认为这是可以的,原因有四个:

  • (1) 如果确实发生过,很容易向用户解释发生了什么(甚至让他们直观地弄清楚),
  • (2) 作为下一个补丁将显示它提供了如此大的性能提升,值得权衡,并且
  • (3) 尽管具有唯一匹配的基本名称,但其他文件作为更好的匹配项的可能性不大。
    原因 (4) 需要整整一段来解释...

如果前三个原因还不够,请考虑重命名检测已经做了什么。
中断检测不是默认设置,这意味着如果文件具有相同的 _fullname_,,那么即使它们的相似度为 0%,也将它们视为相关。
事实上,在这种情况下,我们甚至不需要比较文件以查看它们是否相似,更不用说将它们与所有其他文件进行比较以查看它们最相似的内容。

基本上,我们会根据足够的文件名相似度覆盖内容相似度
如果没有文件名相似性(目前实现为文件名的精确匹配),我们将钟摆摆向相反的方向,并说文件名相似性无关紧要,然后比较完整的 N x M 源和目的地矩阵,找出哪些内容最相似.
这种优化只是添加了另一种形式的文件名相似性比较,但也增加了文件内容相似性检查。
基本上,如果两个文件具有相同的基本名称并且足够相似以被视为重命名,则将它们标记为这样,而不将这两个文件与所有其他重命名候选对象进行比较。

这导致:

<块引用>

diffcore-rename:指导基于基名的不精确重命名检测

签字人:Elijah Newren

<块引用>

利用在最后两个补丁中添加的新 find_basename_matches() 函数,在我们可以根据基本名称匹配文件的情况下更快地找到重命名。

作为快速提醒(有关更多详细信息,请参阅最后两条提交消息),例如,这意味着如果没有剩余的 'docs/extensions.txt ' 文件在添加和删除的文件中的其他位置,如果相似性检查确认它们相似,则将它们标记为重命名,而无需在其他文件中寻找更好的相似性匹配。

这是一个行为变化,在之前的提交消息中有更详细的介绍。

我们不会将此启发式与中断或复制检测一起使用。

断点检测的意义在于,文件名相似度并不意味着文件内容相似度,我们只想知道文件内容相似度。

复制检测的重点是使用更多资源来检查额外的相似性,虽然这是一种使用更少资源但也可能导致发现的相似性略少的优化。

因此,此优化背后的想法与这两个功能都背道而驰,并且将针对这两个功能关闭。

对于 commit 557ac03 ("docs/config/extensions.txt: begin performance work; instrument with extensions.txt calls", 2020-10-28, Git v2.31.0-rc0 -- { {3}} 列在 merge) 中,此更改改进了如下性能:

merge-ort

在 Git 2.32(2021 年第二季度)中,重命名检测返工继续进行。

batch #8commit 81afdf7commit 333899ecommit 1ad69ebcommit b147301commit b6e3d27commit cd52e00、{{3} }、commit 0c4fd73commit ae8cf74(2021 年 2 月 27 日),作者:commit bde8b9f
(由 commit 37a2514Elijah Newren (newren) 合并,2021 年 3 月 22 日)

<块引用>

Junio C Hamano -- gitster --:从trace2_region_*

计算Before After s ± 0.062 s 13.294 s ± 0.103 s s ± 0.493 s 187.248 s ± 0.882 s s ± 0.019 s 5.557 s ± 0.017 s

审核人:Derrick Stolee
签字人:Elijah Newren

<块引用>

dir_rename_guess 有一个映射的映射,特别是它有

dir_rename_counts

我们想要一个简单的映射

dir_rename_counts

基于给定 old_dir => { new_dir => count } 中哪个 old_dir => new_dir 的计数最高。
计算它并将其存储在 new_dir 中。

这是我们猜测当基名不唯一时将文件移动到哪个目录所需的最后一块拼图。

(最后一块commit dd4048d

<块引用>

对于 diffcore-rename ("old_dir: begin performance work; instrument with dir_rename_guess calls", 2020-10-28, Git v2.31.0-rc0 -- { {3}} 列在 based on commit 37a2514) 中,此更改改进了如下性能:

merge-ort

仍然使用 Git 2.32(2021 年第二季度),ort 合并后端已通过跳过不相关的重命名进行了优化。

commit 557ac03mergebatch #8commit e4fd06ecommit f89b4f2commit 174791fcommit 2fd9eda、{{3} }(2021 年 3 月 11 日)作者:commit a68e6ce
(由 commit beb0614commit 32a56df 合并,2021 年 4 月 8 日)

<块引用>

commit 9799889:如果可能,完全跳过重命名检测

签字人:Elijah Newren

<块引用>

trace2_region_* 将进行一系列设置,然后检查准确的重命名,如果没有更多的源或目标需要匹配,则在不准确的重命名检测之前中止。
然而,有时情况是这样的,要么

  • 我们从没有任何来源或目的地开始
  • 我们从没有相关来源开始>在这两种情况中的第一种情况下,设置和精确重命名检测将非常便宜,因为有 0 个文件可供操作。
    在第二种情况下,很可能有数千个文件而没有一个源文件是相关的。
    当我们确定不需要进行重命名检测时,请避免在 Before After s ± 0.062 s 12.596 s ± 0.061 s s ± 0.284 s 130.465 s ± 0.259 s s ± 0.019 s 3.958 s ± 0.010 s 之前调用 diffcore_rename_extended() 甚至某些设置。

对于 Elijah Newren (newren) ("diffcore_rename_extended(): begin performance work; instrument with diffcore_rename_extended() calls", 2020-10-28, Git v2.31.0-rc0 -- { {3}} 列在 Junio C Hamano -- gitster --) 中,此更改改进了如下性能:

merge-ort

这项工作在 Git 2.33(2021 年第 3 季度)中继续进行,其中优化了一系列合并操作中的重复重命名检测。

commit 1b31224merge-ortcommit 557ac03mergebatch #8commit 25e65b6commit cbdca28、{{3} }、commit 86b41b3commit d509802commit 19ceb48commit 64aceb6(2021 年 5 月 20 日)和 commit 2734f2e(2021 年 5 月 4 日)由 commit d29bd6d.< br/> (由 commit a22099fcommit f950026 合并,2021 年 6 月 14 日)

<块引用>

commit caba91c:尽可能使用缓存重命名

签字人:Elijah Newren

<块引用>

当一系列提交的旧基和新基之间有很多重命名时,commit bb80333commit 15f3e1eElijah Newren (newren) 传统上拆分工作的方式导致重新命名- 在移植的每个提交中检测相同的重命名。
为了解决这个问题,最近几次提交一直在创建重命名检测结果的缓存,确定在后续合并操作中何时可以安全地使用这样的缓存,添加辅助函数等。

对于 Junio C Hamano -- gitster -- ("trace2_region_*: begin performance work; instrument with Before After s ± 0.048 s 5.708 s ± 0.111 s s ± 0.236 s 102.171 s ± 0.440 s s ± 0.017 s 3.471 s ± 0.015 s calls", 2020-10-28, Git v2.31.0-rc0 -- { {3}} 列在 commit 169914e) 中,此更改改进了如下性能:

merge-ort

这是一个相当小的改进,但主要是因为之前的优化对于这些特定的测试用例非常有效;此优化仅在其他优化不启动时才会启动。
如果我们取消 basename-guided rename detection 和 skip-inrelevant-renames 优化,那么我们会看到这个系列本身提高了如下性能:

trace2_region_*

由于此优化开始有助于加速先前优化不适用的情况,因此最后的比较表明此缓存重命名优化有可能在不满足其他优化要求的情况下提供显着帮助有效。

答案 5 :(得分:0)

添加@Tilman的回答,使用最近的git重命名选项为-X find-renames=<n>