git - 如果整个文件夹结构发生更改,git 如何管理更改?
问题描述
我正在处理分支,我最近重构了我的文件夹结构,很多文件被移动到这里和那里,许多文件也被重命名。但是当我将 master(旧结构)合并到我当前的分支时,git 能够理解文件的位置并自动合并代码而不会发生冲突。这怎么可能?
解决方案
这个“如何”有两个部分,可以这样概括:
对于提交,Git不在乎。它只是制作快照。无论你告诉 Git 使用什么文件名来保存你告诉 Git 使用的任何文件内容——所有这些文件在你运行时都存储在你的索引中
git commit
,这就是为什么你必须一直保存git add
文件,以便将它们复制到旧的索引中的版本——当你运行git commit
. 那些冻结的文件是提交的快照。换句话说,在您重新排列所有内容
git mv
(更改索引和工作树中的名称)并git add
根据需要更新内容之后git commit
,您将获得包含新名称和任何更新内容的新快照。旧的快照保持原样:所有现有的快照都被永远冻结,或者至少在提交本身存在的时候。(默认设置是它们永远存在。可以删除提交,但这只是简单的,直到您将它们传播到其他存储库,之后即使您删除它们,它们也会继续从其他存储库重新感染您的存储库来自你自己的。)为了比较——包括合并——Git 必须发现/检测重命名的文件。Git 通过比较文件的内容来做到这一点。
这里第二个要点中的声明实际上有点夸张,但是让我们通过用 来说明它是如何工作的git diff
,它——至少在我们在这里关心的模式下——比较了两个提交。请记住,每个提交都代表所有文件的完整快照。我们将找到两个提交的哈希 ID,然后运行:
git diff --find-renames <hash of earlier commit> <hash of later commit>
此时 Git 将做的是提取两个提交中的每一个。(“提取”尽可能地短路,这通常很多:Git 通常可以直接检查冻结的提交。但这只是速度优化;你可以认为这是 Git 将两个提交完全提取到一个临时工作区。)让我们在这里将较早的提交称为旧的,将后的提交称为新的。1 的工作git diff
是告诉您如何将旧的更改为新的。这不一定是任何进行更改的人所做的,只是一些会产生相同结果的指令。
要找到这些说明,Git 将:
首先,在old和new中找到所有同名的文件。Git 假设如果old有一个名为 的文件,
README
而new有一个名为 的文件README
,那么它们必须是“相同的”文件。这些文件是配对的:它们现在被排除在等式之外,目前。Git 还没有弄清楚如何更改配对文件,它只是将它们配对。(您可以在此处使用该
-B
选项将一个步骤插入到具有该选项的命令中。但我们现在将忽略它,因为它只会使这变得复杂。)现在,如果有未配对的文件,这些文件代表旧文件丢失和/或新文件突然出现......还是他们?也许它们是被重命名的文件,在old中有一些名称 O ,在new中有一些不同的名称 N 。在这里,Git为每个可能的文件对计算相似性索引号。
出于速度目的,Git 可以非常快速地配对任何两个100% 相同的文件(一个来自old,一个来自new )。这通常会大大缩小必须进行硬比较的文件池。
最后,Git 归结为未配对的文件,即使它们不是 100%,位对位相同,它也应该考虑配对。Git 现在对每对文件进行完整的相似性索引计算(使用Git 用于在包文件中进行 delta 压缩的类似 xdelta 的代码)。(这需要真正提取所有这些文件的数据。)如果分数超过您选择的最小值,则将获得最佳配对分数的文件配对在一起,默认为“50% 相似”。
在完成所有这些额外工作后仍未配对的文件要么被删除,要么被重新创建。
--find-copies
(如果您添加or ,这里会引入一些更复杂的情况--find-copies-harder
,但同样,我们将在这里忽略它们。)
现在文件已经配对了——也就是说,现在git diff
知道了,比如说,旧README.md
文件中的文件大多与新文件中的文件匹配,因此这两个文件必须是一个文件,具有一个身份,而不是两个不同的文件两个身份——<em>现在 Git 会比较每个配对文件以生成指令:README.rst
-
:从旧文件的版本中删除这一行+
:将此行添加到旧文件的版本中
如果您遵循所有说明,包括顶部给出的任何“重命名此文件”说明,则会将old中找到的文件更改为new中找到的文件。
1如果愿意,您可以反转哈希 ID。然后 Git 会告诉你如何将较新的提交转换为较旧的提交。
如何git merge
使用git diff
当合并两个提交时,Git 使用提交图——通过查看每个提交的父哈希或哈希将所有单独的提交连接到一个大 DAG 中形成的有向无环图——来找到最佳的共同祖先提交。此提交是两个指定提交的合并基础。
然后,该git merge
命令实际上运行了两个 git diff
命令。两者都已--find-renames
启用,相似度阈值默认为 50%。您可以使用-X find-renames=<number>
更改此阈值,以允许更多或更少名称不匹配的文件配对。
这两个差异是:
git diff --find-renames <hash of merge base> <hash of HEAD commit>
和:
git diff --find-renames <hash of merge base> <hash of other commit>
两个 diff 在内部git diff
.
并发症
添加-B
告诉 Git打破自动配对的文件:仅仅因为一个文件README
在两个提交中都被命名并不意味着这真的是同一个文件。例如,如果您重命名README
为old/README
,然后重命名new/README
为README
怎么办?在这种情况下,Git 将在我之前提到的那个步骤中对自动配对的文件进行相似度计算。如果相似度太低,Git 会破坏配对。稍后,如果相似度不是太低,并且配对仍然断开,Git 将重新加入这两个文件,因此-B
需要两个数字,而不仅仅是一个。
合并命令不允许您提供-B
参数。(可以说,它应该。)
如果您使用--find-copies
or --find-copies-harder
,Git 将查看部分或全部源(“旧”)文件,以查看是否从中复制了新创建的目标(“新”)文件。这些使用相同的相似性指数。此步骤发生在重命名检测之后,并且有时只会将修改后的文件视为可能的来源,因为它的计算成本很高。
合并命令也不允许您指定查找副本选项。
推荐阅读
- wordpress - 是否可以在插件激活时添加菜单项来吸引我的帐户页面?
- mysql - 变量列之间的高效连接
- c# - 模型和实例类分离
- python - 如何使用 discord.py 和 Python 更改所有文本通道的权限?
- css - 如何减小角材料中占位符的字体大小
- c# - 使用现有数据库数据在 Entity Framework 6 中创建种子迁移
- mysql - 每个派生表都必须有自己的别名 mysql 错误代码 1248
- sql - 在 Laravel 中计算单个列的不同值
- pyspark - 在 pyspark 中删除所有重复的实例
- apache - .htaccess 用于 SPA,将传入请求重写为 index.html,静态资源除外