使Git跨文件移动/重命名应用合并
我肯定我在这里做错了什么,但我不确定是什么 我有一个使Git跨文件移动/重命名应用合并,git,git-merge,Git,Git Merge,我肯定我在这里做错了什么,但我不确定是什么 我有一个主机和一个分支,这两个分支最终会合并回来,但目前这两个分支的开发都在进行中 这意味着我会定期将master中的最新更改合并到branch中 问题是在分支中包含大量文件移动和重命名 我目前的程序是: 在分支中 将my control.html重命名为my control.js 阶段更改并提交-Git发现这是一个移动,而不是删除+添加 更新my control.js 提交my control.js更改 my control.js现在有了来自my
主机
和一个分支
,这两个分支最终会合并回来,但目前这两个分支的开发都在进行中
这意味着我会定期将master
中的最新更改合并到branch
中
问题是在分支中
包含大量文件移动和重命名
我目前的程序是:
- 在
分支中
- 将
重命名为my control.html
my control.js
- 阶段更改并提交-Git发现这是一个
,而不是移动
删除+添加
- 更新
my control.js
- 提交
更改my control.js
现在有了来自my control.js
my control.html
- 将
- 在
master中
- 更改
my control.html
- 提交更改
- 更改
- 返回分支机构
- 合并来自
master的更改
- 合并来自
my control.js
的更改,但大约有一半的时间我只是在branch
中得到my control.html
发生这种情况时,my control.js
具有所有历史记录,my control.html
具有所有历史记录,以及来自master
的1或2次提交
- 我做错了什么
- 为什么这有时会发生,有时会起作用
- 我能做些什么来修复它
- 有没有办法告诉Git“不,这些更改应该应用于此文件”
my control.js
拥有所有历史记录,my control.html
拥有所有历史记录以及来自master的1或2次提交
Git没有文件历史记录。Git只有提交历史记录。更准确地说,提交是历史记录,文件与此无关。提交包含文件,但不以任何方式控制历史:提交是历史
我有更多关于这方面的信息,例如,我对的回答。如果您要求Git跨重命名跟踪文件,Git将使用其历史简化来仅显示与命名文件接触的提交,当其中一个“接触”是“Git检测到重命名”时,Git将在该点开始查找新名称,并停止查找旧名称。(或者,由于Git正在倒退,最好说它开始寻找旧名称,而不再寻找新名称。)
很明显,这种技术在合并时可能会失败,因为合并的一个分支可能有“错误”的名称。然而,不管怎样,历史简化通常只会导致合并的一个阶段
如果不使用--follow
,而是使用git log--path
或等效工具,git根本不需要检测重命名:它只是使用给定的路径简化历史
有点牵强的类比
我做错了什么
什么都没有,也许什么都没有。问题是Git有时可以,有时不能,在某一点上识别名为Bob的文件,在另一点上识别名为Robert的文件引用同一个人。它可以或不能正确识别文件对。鲍勃和罗伯特是同一个人吗
为什么这有时会发生,有时会起作用
这至少有一个可靠的答案:如果两个文件足够相似,Git可以识别它们,其他条件也适用。也就是说,您向Git显示两个快照,其中包含一些文件(“人”),并让Git猜测谁是谁以及谁移动了。如果在前一张照片中只有一个文件上有“鲍勃”的标签,在后一张照片中有一个文件上有“罗伯特”的标签,Git可能能够检测到他们是同一个人,只要他没有失去一条腿或多出一个头或诸如此类的东西。然而,如果两张照片上都有人戴着“鲍勃”和“罗伯特”的名字标签,Git会假设两个“鲍勃”是同一个人,两个“罗伯特”是同一个人,而且越早的鲍勃永远不是越晚的罗伯特,反之亦然
技术:git merge
、提交图和git diff--find重命名
让我们看看git merge的实际工作原理。要做到这一点,我们必须从两件事开始:提交图和git diff--find renames
提交图是合并的关键。每个提交记录其父提交的原始散列ID(如果是普通提交,或者如果是合并提交,则记录其父提交的两个(或所有1个)散列ID)。通常,合并只有两个父级。让我们画一个提交图作为示例,并挑选几个具体的提交进行讨论。与其使用完整、大而难看的哈希ID,不如使用大写字母来指定特定的提交(圆点表示不太有趣的提交)。我们将有分支branch
和main
,它们在提交B
时分开,但过去至少合并过一次:
o--o---D--o--o--E <-- branch
/ \
...--o--B--o----C--M--o--o--F <-- main
Git然后将这两组更改组合在一起,将组合的更改应用于保存在B
中的快照,并执行结果合并提交M
因为M
是一个合并提交,所以它同时记住C
和D
。记住,当Git遍历由提交组成的历史时,无论何时从M
向后移动,它都必须访问双亲
我们现在将运行git checkout main;git合并分支
。也就是说,我们将选择commitF
作为当前提交并询问
git diff --find-renames <hash-of-B> <hash-of-C> # what we did, on main
git diff --find-renames <hash-of-B> <hash-of-D> # what they did, on branch
git diff --find-renames <hash-of-D> <hash-of-F> # what we did on main
git diff --find-renames <hash-of-D> <hash-of-E> # what they did on branch
git diff -M10
git show $hash:$basepath > file.base
git show HEAD:file > file.ours
git show MERGE_HEAD:$theirpath > file.theirs