在我们的一个git存储库中,我们有两个分支,每个分支都在某些目录上工作,以至于它们大不相同 1 。我们现在想要合并两个分支,保留两个版本。
我已经玩过重命名一个目录,以便它们不会在磁盘上重叠,但是当我合并分支时,git知道它们最初都来自同一个源,并且有助于"移动& #34;文件从一个到另一个,伴随着合并冲突。
我还尝试使用git merge -s ours branchname
然后使用git checkout branchname -- directory/
,但这看起来会破坏他们的历史记录#34;分支,使它看起来像突然出现的文件。理想情况下,我希望保留对预合并分支中的文件进行修改的能力,合并能够找到文件的正确版本。
有没有办法告诉git合并两个分支,但保留某些文件/目录为"分开",尽管共享起源?或者换句话说,有没有办法吐出文件的历史记录,以便git知道它在一个分支中移动但在另一个分支中没有移动?
1 这些是文档/测试目录,因此关于代码重复的标准问题很少到不存在。
答案 0 :(得分:1)
这里有坏消息和好消息。
Git并不关心(在某种程度上,在包文件的工作方式中存在秘密关注点,并且我还要提及的是关于提交中的路径名)。它只关心内容:文件中的位,以及放置这些内容的名称。除了父ID之外,每个提交完全独立于之前或(最终)之后的任何提交。因此,“文件”根本没有任何历史记录。
显然,文件做有历史记录,因为如果你区分两次提交(这是git show
在显示提交时所做的那样),你会看到一个来自“之前版本的补丁” foo“to”foo的新版本“,你可以做”git blame foo“这样的事情来看历史。
Git通过使用内容每次要求一个来构建历史记录来协调这两个对立面。如果您运行git show
或git log -p
,以查看更改的内容,git会根据内容重新构建历史记录。
在查找已移动/重命名的文件方面,git使用一个或多个技巧,具体取决于您如何指导它。您可以告诉git diff
(包括获取差异的大多数命令,包括合并操作)根本不检查。这是最快的方法。
你可以告诉它使用一个快速(但仍然是O(n 2 ))算法,该算法只查看仅在diff比较的两个提交之一中的路径名。这是合并的默认方法(您可以通过配置diff.renameLimit
将其配置为diff的默认方法,或者您可以使用-M
选项为其提供。)
或者,您可以通过--find-copies
(又名-C
)或--find-copies-harder
告诉它使用缓慢甚至非常慢的方法。
默认的大多数快速方法确实使用路径名,而非常慢的方法不使用路径名。尽管如此,两者仍然依赖于内容。特别是,在复制或重命名检测方面,如果文件“至少50%相似”,或者您使用-M
和/或{{-C
选择的任何其他相似性比率,则文件被视为“相同” 1}} diff
的参数。
这既是好消息,也是坏消息。基本上,每次你得到git来比较两个提交 - 包括任何未来的合并,回顾这些为他们的merge-bases-git将找到一些重命名,而不是找到一些其他重命名和/或副本,取决于你给它的标志和内容的相似性。您可以在合并期间(-X rename-threshold
而不是-M
)对检测值大惊小怪,但这里的控件非常粗糙。
(请注意,git blame
和git log --follow
在尝试发现重命名时也会执行此类基于名称和内容的匹配。git log --follow
的算法仅在向后移动时才有效及时,从当前路径到之前的路径,因此当与--reverse
结合使用时会失败。)