合并两个分支,拆分一个目录
Merge two branches, splitting a directory
在我们的一个 git 存储库中,我们有两个分支,每个分支都在某些目录上工作,以至于它们大相径庭1。我们现在要合并两个分支,保留两个版本。
我试过重命名一个目录,使它们在磁盘上不重叠,但是当我合并分支时 git 知道它们最初都来自同一源,并且很有帮助 "moves" 文件从一个到另一个,伴随合并冲突。
我也尝试过使用 git merge -s ours branchname
,然后使用 git checkout branchname -- directory/
,但这看起来破坏了 "theirs" 分支的历史,使文件看起来像是突然出现的.理想情况下,我想保留对预合并分支中的文件进行修改的能力,合并能够找到文件的正确版本。
有没有办法告诉 git 合并两个分支但保持某些 files/directories 为 "separate",尽管共享来源?或者换句话说,有没有办法吐出一个文件的历史,这样 git 知道它在一个分支中移动但在另一个分支中没有移动?
1 这些是 documentation/test 目录,因此对代码重复的标准关注很少甚至不存在。
这里有坏消息,也有好消息。
Git 不关心(在某种程度上,例如,在打包文件的工作方式中有秘密的关心位,这也是我要提到的)关于路径名在提交。它只关心内容:文件中的位,以及放置这些内容的名称。除了父 ID 之外,每个提交都完全独立于它之前或(最终)之后的任何提交。因此,"files" 根本没有任何历史记录。
显然,文件 do 有历史记录,因为如果你比较两个提交(这是 git show
在显示提交时所做的),你会看到一个补丁从 "previous version of foo" 到 "new version of foo",您可以执行 "git blame foo" 之类的操作来查看历史记录。
Git 通过构建一个历史 每次你请求一个 ,使用内容来调和这两个对立面。如果您 运行 git show
,或 git log -p
,要查看更改内容,git 会根据内容立即重建历史记录。
在查找被移动/重命名的文件方面,git 使用了几种技巧中的一种或多种,具体取决于您的指导方式。您可以告诉 git diff
(包括大多数获得差异的命令,其中包括合并操作)根本不要检查。这是最快的方法。
你可以告诉它使用一个主要是快速的(但仍然是 O(n2))算法,该算法只查看仅在两个提交之一中的路径名diff 正在比较。这是合并的默认方法(您可以通过配置 diff.renameLimit
将其配置为差异的默认方法,或者您可以使用 -M
选项提供它)。
或者,您可以告诉它使用慢速甚至非常慢的方法,使用 --find-copies
(又名 -C
)或 --find-copies-harder
。
默认的 mostly-fast 方法确实使用路径名,而 very-slow 方法则不使用。不过,两者都仍然依赖于内容。特别是,在复制或重命名检测方面,文件被视为 "the same",如果它们是 "at least 50% similar",或者您选择的与 -M
and/or -C
参数 diff
.
这既是好消息也是坏消息。本质上,每次你 git 比较两个提交——包括任何将来回顾这些提交的合并基础的合并——git 都会找到一些重命名,而不会找到其他一些重命名 and/or 副本,取决于你给它的标记和内容相似性。您可以在合并期间对检测值大惊小怪(-X rename-threshold
而不是 -M
),但这里的控件非常粗糙。
(请注意,git blame
和 git log --follow
在尝试发现重命名时也会执行这种基于名称和内容的匹配。git log --follow
的算法仅在以下情况下有效及时向后移动,从当前路径到上一个路径,因此与 --reverse
组合时失败。)
在我们的一个 git 存储库中,我们有两个分支,每个分支都在某些目录上工作,以至于它们大相径庭1。我们现在要合并两个分支,保留两个版本。
我试过重命名一个目录,使它们在磁盘上不重叠,但是当我合并分支时 git 知道它们最初都来自同一源,并且很有帮助 "moves" 文件从一个到另一个,伴随合并冲突。
我也尝试过使用 git merge -s ours branchname
,然后使用 git checkout branchname -- directory/
,但这看起来破坏了 "theirs" 分支的历史,使文件看起来像是突然出现的.理想情况下,我想保留对预合并分支中的文件进行修改的能力,合并能够找到文件的正确版本。
有没有办法告诉 git 合并两个分支但保持某些 files/directories 为 "separate",尽管共享来源?或者换句话说,有没有办法吐出一个文件的历史,这样 git 知道它在一个分支中移动但在另一个分支中没有移动?
1 这些是 documentation/test 目录,因此对代码重复的标准关注很少甚至不存在。
这里有坏消息,也有好消息。
Git 不关心(在某种程度上,例如,在打包文件的工作方式中有秘密的关心位,这也是我要提到的)关于路径名在提交。它只关心内容:文件中的位,以及放置这些内容的名称。除了父 ID 之外,每个提交都完全独立于它之前或(最终)之后的任何提交。因此,"files" 根本没有任何历史记录。
显然,文件 do 有历史记录,因为如果你比较两个提交(这是 git show
在显示提交时所做的),你会看到一个补丁从 "previous version of foo" 到 "new version of foo",您可以执行 "git blame foo" 之类的操作来查看历史记录。
Git 通过构建一个历史 每次你请求一个 ,使用内容来调和这两个对立面。如果您 运行 git show
,或 git log -p
,要查看更改内容,git 会根据内容立即重建历史记录。
在查找被移动/重命名的文件方面,git 使用了几种技巧中的一种或多种,具体取决于您的指导方式。您可以告诉 git diff
(包括大多数获得差异的命令,其中包括合并操作)根本不要检查。这是最快的方法。
你可以告诉它使用一个主要是快速的(但仍然是 O(n2))算法,该算法只查看仅在两个提交之一中的路径名diff 正在比较。这是合并的默认方法(您可以通过配置 diff.renameLimit
将其配置为差异的默认方法,或者您可以使用 -M
选项提供它)。
或者,您可以告诉它使用慢速甚至非常慢的方法,使用 --find-copies
(又名 -C
)或 --find-copies-harder
。
默认的 mostly-fast 方法确实使用路径名,而 very-slow 方法则不使用。不过,两者都仍然依赖于内容。特别是,在复制或重命名检测方面,文件被视为 "the same",如果它们是 "at least 50% similar",或者您选择的与 -M
and/or -C
参数 diff
.
这既是好消息也是坏消息。本质上,每次你 git 比较两个提交——包括任何将来回顾这些提交的合并基础的合并——git 都会找到一些重命名,而不会找到其他一些重命名 and/or 副本,取决于你给它的标记和内容相似性。您可以在合并期间对检测值大惊小怪(-X rename-threshold
而不是 -M
),但这里的控件非常粗糙。
(请注意,git blame
和 git log --follow
在尝试发现重命名时也会执行这种基于名称和内容的匹配。git log --follow
的算法仅在以下情况下有效及时向后移动,从当前路径到上一个路径,因此与 --reverse
组合时失败。)