在搬一些目录。
我的意思是,MERGE,多conflicting档案,因为他们有其他开发商承诺的变化。两个egit MERGE工具和git mergetool说,文件被删除或remotely局部。看到的图像。
在这些MERGE怎么变化?
- 这真是一团糟,因为故事的一个版本是其他开发人员处理某些文件,而另一个版本是删除(移动)这些文件。理想情况下,在您计划移动或重命名文件的同时,不应对其进行任何更改。
- 有多少文件发生冲突?
- 大约150。(为了使用特定的Maven设置,我移动了整个源目录。)
- 这可能会变得难看。您可以从文件的原始位置(您的合作者在其中编辑文件)复制冲突文件的内容,并覆盖新位置。最好是放弃更改,提取最新的,然后移动文件夹,告诉其他人在完成之前停止工作。
- 谢谢您。我知道Git不跟踪文件重命名。但它确实有一些内容跟踪功能。目录的移动足够大,需要进一步开发工具和进行一些测试;在这段时间内,我不能要求其他开发人员停止工作。是否有一些特殊的方法来移动允许保留历史记录的文件?stackoverflow.com/questions/1094269/git‌&8203;-mv的目的是什么
- eGit似乎也支持这一点:eclipse.org/forums/index.php/t/204077
- 等等……你的问题是如何进行合并,还是如何保存历史?
- 主要是——如何进行合并。但同时,我想保留历史。
文件历史记录和重命名检测
你不必担心在Git中"保存历史"。Git根本没有文件历史记录,它只有提交历史记录。也就是说,每个提交"指向"(包含其父级的哈希ID),或者对于合并,它的父级和这都是历史:commit E前面是commit D,commit D前面是commit C等等。只要你有承诺,你就有历史。好的。
这就是说,Git可以尝试使用git log --follow合成一个特定文件的历史。您可以指定一个开始提交和路径名,以及git检查(commit by commit),以查看在将当前提交的父级与当前提交进行比较时是否重命名了该文件。它使用git的重命名检测来标识commit l(左)中的文件a/b.txt与commit r(右)中的文件c/d.txt是"同一个文件"。好的。
重命名检测有很多精细的旋钮,但在基本级别上,基本上是这样的:好的。
- Git查看commit l中的所有文件名。
- Git查看commit r中的所有文件名。
- 如果有一个文件名从l中消失并出现在r中,比如a/b.txt消失了,c/d.txt是全新的,那为什么,这是检测到的重命名的候选。
- 现在有了候选文件(不成对的L文件和不成对的R文件),git将比较这些不成对文件的内容。
未配对的文件进入一个配对队列(一个用于L,一个用于R),git散列所有文件的内容。它已经有了内部的git散列,所以它首先直接比较所有这些散列。如果一个文件完全不变,它在L和R中具有相同的git散列ID(但名称不同),并且可以立即配对并从配对队列中删除。好的。
现在精确的匹配被删除了,Git尝试了长时间缓慢的工作。它需要一个不成对的L文件,并为每个R文件计算一个"相似性索引"。如果某个r文件足够相似或有多个r文件,则它将获取"最相似"的r文件并将其与l文件配对。如果没有足够相似的文件,则L文件保持不成对(从队列中取出),并被视为"从L中删除"。最终,在不成对的L队列中没有文件,并且无论未成对的R队列中保留什么文件,这些文件都是"添加的"(R中的新文件)。同时,所有配对文件都已重命名。好的。
这意味着:当比较(git diffcommit l到r时,如果两个文件足够相似,它们将作为重命名进行配对。默认的相似性索引是50%,所以文件需要50%的匹配(这意味着相似性索引的计算有点不透明),但是对于Git来说,精确的匹配更加容易和快速。好的。
请注意,git log --follow启用了重命名检测(在一个目标R文件上,当我们在日志中向后工作时,将父提交与子文件中已知的一个文件进行比较)。自从Git 2.9版以来,git diff和git log -p现在都自动打开了重命名检测。在旧版本中,您必须使用-M选项来设置相似性阈值,或将diff.renames配置为true,以使git diff和git log -p进行重命名检测。好的。
配对队列也有一个最大长度。这已经翻了两倍,一次是1.5.6吉特,一次是1.7.5吉特。您可以自己控制它:它可以配置为diff.renameLimit和merge.renameLimit。当前限制为400和1000。(如果将这些设置为零,Git会使用自己的内部最大值,这会占用大量的CPU时间,这就是为什么这两个限制首先存在的原因。如果设置diff.renameLimit而不是merge.renameLimit,git merge将使用差异设置。)好的。
这就引出了一条适用于git log --follow的经验法则:如果可能,当您打算重命名某个文件或一组文件时,请自行执行重命名步骤,而不更改任何文件内容。如果可能的话,将重命名文件的数量保持在相当小的水平:例如,在400或以下。您可以通过多个步骤提交更多的重命名,一次400个。但请记住,您在权衡git log --follow的能力和速度,以避免用无意义的提交来混乱您的历史:如果您需要重命名50000个文件,也许您应该这样做。好的。
但这对合并有何影响?好吧,git merge和git log --follow一样,总是打开重命名检测。但哪个承诺是L,哪个承诺是R?好的。合并和重命名检测
无论何时跑步:好的。
1
| git merge <commit-specifier> |
Git必须找到当前(head)提交和指定的其他提交之间的合并基。(通常这只是git merge 。通过将分支名称解析为它指向的提交来选择另一个分支的提示提交。根据git中"branch name"的定义,这是该分支的提示提交,因此这个"只起作用"。但您可以通过散列ID指定任何提交。)让我们调用这个合并基提交B(对于基)。我们已经知道我们自己的承诺是HEAD,尽管有些事情称之为"本地"。让我们称另一个commit为o(对于另一个),尽管有些东西称之为"远程"(这很愚蠢:Git中没有任何东西是远程的!).好的。
实际上,git有两个git diffs,一个比较b和head,因此对于这个特殊的diff,l是b,r是head。Git将根据我们在上面看到的规则检测或检测不到重命名。然后Git做另一个git diff,比较b和o。Git将根据相同的规则再次检测或检测不到重命名。好的。
如果在b-vs-head中重命名了某个文件,则git会像往常一样对其内容进行区分。如果在b-vs-o中重命名了某个文件,git会像往常一样对其内容进行区分。如果在head和o中将单个b文件f重命名为两个不同的名称,那么git会在该文件上声明重命名/重命名冲突,并将这两个名称保留在工作树中供您清理。如果只在一个diff中重命名,那么在head或o中仍然称为f,那么git将使用从哪一侧重命名的新名称将文件存储在工作树中。在任何情况下,git都会像往常一样尝试组合这两组更改(从b-vs-head和b-vs-o)。好的。
当然,要让git检测重命名,文件的内容必须足够相似,就像往常一样。这对于Java文件(有时Python)尤其有问题,其中文件名嵌入到导入语句中。如果一个模块主要由import语句组成,并且只有几行自己的代码,那么由重命名引起的更改将覆盖其余的文件内容,并且这些文件甚至不会达到50%的匹配。好的。
有一个解决办法,虽然有点难看。根据git log --follow的经验法则,我们可以先提交重命名,然后将更改内容的"修复所有导入"提交为单独的提交。然后,当我们进行合并时,我们可以进行两个甚至三个合并:好的。
1 2
| git checkout ... # whatever branch we plan to merge into
git merge <hash> # merge with everything just before the Great Renaming |
由于没有重命名任何文件,所以这种合并也会像往常一样进行,或进行得很糟糕。这是结果,以图表的形式。注意,我们提供给git merge命令的散列是commit A的散列,就在R之前,它执行所有重命名:好的。
1 2 3
| ...--*--o--...--o--M <-- mainline
\ /
o--o--...-A--R--...--o <-- develop, with renames at R |
号
然后:好的。
由于每个文件的内容完全相同,名称也完全相同,在其他Rcommit之间,合并基是commit A,这里的效果只是获取所有重命名。我们将文件内容保留在head commit M中,但保留R中的名称。此合并应自动成功:好的。
1 2 3
| ...--*--o--...--o--M--N <-- mainline
\ / /
o--o--...-A--R--...--o <-- develop, with renames at R |
。
现在我们可以开始合并开发部门了。好的。
在许多情况下,我们不需要合并M,但如果我们只需要合并N,那么这样做对所有重命名来说可能不是一个坏主意。原因是commit R不起作用:它的导入名称错误。在平分过程中必须跳过commit R。这意味着合并N同样是不起作用的,在对分时必须跳过。有M在场可能会很好,因为M实际上可以工作。好的。
请注意,如果您这样做,您就是为了取悦版本控制系统而扭曲/扭曲源代码。情况不太好。它可能比你的其他选择更糟,但不要告诉你自己它是好的。好的。
1I仍然需要了解当存在重命名/重命名冲突时,文件的两个副本会发生什么情况。由于Git将两个名称都保留在工作树中,这两个名称是否包含相同的合并内容,如果需要,还包含任何冲突标记?也就是说,如果文件名为base.txt,现在名为head.txt和other.txt,那么head.txt和other.txt的工作树版本是否始终匹配?好的。好啊。
- 谢谢您!非常彻底。我需要在不更改内容的情况下移动SRC目录(对于基于Maven的工具的文件系统结构),我可以使用Eclipse移动文件,然后在Eclipse中提交吗?似乎即使是数百个文件,每个文件都会找到一对相同的文件。或者我应该有一个为每个单独的文件执行git mv和commit的脚本。我有数百个提交,但是保证每个文件都是成对的。这样做之后,pre-move分支上的开发人员将编辑文件,我也将编辑文件,但我可以很容易地合并。这有道理吗?
- 我对日食一无所知。只要它只运行Git命令,就应该得到相同的行为,但是从其他的注释和问题看来,EGIT是它自己的Git的Java实现,因此它可能有它自己不同的怪癖。例如,它可能会做自己的配对,有不同的限制。我会再次注意到,创建许多承诺只是为了让风投高兴是不好的:如果这一切只与一个这样的承诺一起工作,那就更好了。
- 关于重命名/合并的最佳答案,我已经读过了。我特别喜欢你解释log —follow和merge之间的共同点。