Mercurial 合并的惊人之处 - 我错过了什么?
我使用 Mercurial 一段时间了,有一个“事实”被多次提及。
事实上,昨天观看 Fogcreek 制作的视频时,我受到了启发,该视频:Fog Creek Kiln:解锁DVCS 对贵公司的力量 似乎有些东西对我不起作用。
在该视频的 1:39 左右及之后,它强调说,虽然其他版本控制系统跟踪修订(即快照),但 DVCS 之类的 Mercurial 跟踪变更集(即快照之间发生的情况)。
这给了他们一个合并场景中的边缘,然后展示了一个示例。如果您在一个分支中移动一个函数,并在另一个分支中更改相同的函数,Mercurial 能够合并它。
我在其他地方看到过这一点,尽管我现在找不到任何直接链接。
这似乎对我不起作用。
编辑:这是 TortoiseHg 的默认“beyondcompare3”合并工具配置的问题。我将以下配置添加到我的 Mercurial.ini 文件中,现在它按预期工作。当然,如果它不能自动合并,它会转入 GUI 工具,但是现在这个问题中描述的合并在没有任何提示的情况下运行,并且只是开箱即用地做正确的事情
[ui]
merge = bc3
[merge-tools]
bc3.executable = C:\Program Files (x86)\Beyond Compare 3\bcomp.exe
bc3.args = $local $other $base $output /automerge /reviewconflicts /closescript
bc3.priority = 1
bc3.premerge = True
bc3.gui = True
为了测试这一点,我承诺将此文件复制到存储库:
void Main()
{
Function1();
Function2();
}
public void Function1()
{
Debug.WriteLine("Function 1");
for (int index = 0; index < 10; index++)
Debug.WriteLine("f1: " + index);
}
public void Function2()
{
Debug.WriteLine("Function 1");
}
然后,在从该文件分支出来的两个不同的并行变更集中,我进行了以下两项更改:
- 我将 Function1 函数移动到文件的底部
- 我更改了 Function1 内的消息,
然后尝试合并,然后 Mercurial给了我一个合并冲突窗口,试图弄清楚我做了什么。
基本上,它尝试更改 Function2 中的文本,该文本现在位于 Function1 移动之前的位置。
这是不应该发生的!
这是用于重现我的示例的源文件:
用于生成存储库的批处理文件:
@echo off
setlocal
if exist repo rd /s /q repo
hg init repo
cd repo
copy ..\example1.linq example.linq
hg commit -m "initial commit" --addremove --user "Bob" --date "2010-01-01 18:00:00"
copy ..\example2.linq example.linq
hg commit -m "moved function" --user "Bob" --date "2010-01-01 19:00:00"
hg update 0
copy ..\example3.linq example.linq
hg commit -m "moved function" --user "Alice" --date "2010-01-01 20:00:00"
文件的 3 个版本,example1.linq、example2.linq 和 example3.linq:
Example1。 linq:
<Query Kind="Program" />
void Main()
{
Function1();
Function2();
}
public void Function1()
{
Debug.WriteLine("Function 1");
for (int index = 0; index < 10; index++)
Debug.WriteLine("f1: " + index);
}
public void Function2()
{
Debug.WriteLine("Function 1");
}
示例2.linq:
<Query Kind="Program" />
void Main()
{
Function1();
Function2();
}
public void Function2()
{
Debug.WriteLine("Function 1");
}
public void Function1()
{
Debug.WriteLine("Function 1");
for (int index = 0; index < 10; index++)
Debug.WriteLine("f1: " + index);
}
示例3.linq:
<Query Kind="Program" />
void Main()
{
Function1();
Function2();
}
public void Function1()
{
Debug.WriteLine("Function 1b");
for (int index = 0; index < 10; index++)
Debug.WriteLine("f1: " + index);
}
public void Function2()
{
Debug.WriteLine("Function 1");
}
I've been using Mercurial for a while, and there's one "fact" that is given many times.
In fact, it hit me while watching a video made by Fogcreek yesterday, this video: Fog Creek Kiln: Unlock the power of DVCS for your company that there seems to be something that doesn't work for me here.
At around 1:39 in that video and onwards it makes a point of saying that while other version control systems tracks revisions (ie. snapshots), DVCS' like Mercurial track changesets (ie. what happened between the snapshots.)
This gives them an edge in merging scenarios, and then it shows an example. If you move a function in one branch, and change the same function in another branch, Mercurial is able to merge that.
And I've seen this mentioned elsewhere, though I can't find any direct links now.
This doesn't seem to work for me.
Edit: This is a problem with the default "beyondcompare3" merge tool configuration for TortoiseHg. I added the configuration below to my Mercurial.ini file, and now it works as expected. Sure, it'll punt to the GUI tool if it can't automerge, but now the merge described in this question here runs without any prompts and just does the right thing out of the box
[ui]
merge = bc3
[merge-tools]
bc3.executable = C:\Program Files (x86)\Beyond Compare 3\bcomp.exe
bc3.args = $local $other $base $output /automerge /reviewconflicts /closescript
bc3.priority = 1
bc3.premerge = True
bc3.gui = True
To test this, I committed this file to a repository:
void Main()
{
Function1();
Function2();
}
public void Function1()
{
Debug.WriteLine("Function 1");
for (int index = 0; index < 10; index++)
Debug.WriteLine("f1: " + index);
}
public void Function2()
{
Debug.WriteLine("Function 1");
}
Then in two different parallel changesets branching out from this one, I did the following two changes:
- I moved the Function1 function to the bottom of the file
- I changed the message inside Function1
I then tried to merge, and Mercurial gives me a merge conflict window, trying to figure out what I did.
Basically, it tries to change the text in Function2, which is now in the position that Function1 was before it was moved.
This was not supposed to happen!
Here's the source files for reproducing my example:
Batch file for producing repository:
@echo off
setlocal
if exist repo rd /s /q repo
hg init repo
cd repo
copy ..\example1.linq example.linq
hg commit -m "initial commit" --addremove --user "Bob" --date "2010-01-01 18:00:00"
copy ..\example2.linq example.linq
hg commit -m "moved function" --user "Bob" --date "2010-01-01 19:00:00"
hg update 0
copy ..\example3.linq example.linq
hg commit -m "moved function" --user "Alice" --date "2010-01-01 20:00:00"
The 3 versions of the file, example1.linq, example2.linq and example3.linq:
Example1.linq:
<Query Kind="Program" />
void Main()
{
Function1();
Function2();
}
public void Function1()
{
Debug.WriteLine("Function 1");
for (int index = 0; index < 10; index++)
Debug.WriteLine("f1: " + index);
}
public void Function2()
{
Debug.WriteLine("Function 1");
}
Example2.linq:
<Query Kind="Program" />
void Main()
{
Function1();
Function2();
}
public void Function2()
{
Debug.WriteLine("Function 1");
}
public void Function1()
{
Debug.WriteLine("Function 1");
for (int index = 0; index < 10; index++)
Debug.WriteLine("f1: " + index);
}
Example3.linq:
<Query Kind="Program" />
void Main()
{
Function1();
Function2();
}
public void Function1()
{
Debug.WriteLine("Function 1b");
for (int index = 0; index < 10; index++)
Debug.WriteLine("f1: " + index);
}
public void Function2()
{
Debug.WriteLine("Function 1");
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,您目前基本上遇到了任何当前 VCS(无论是否为 DVCS,这并不重要)的限制之一。
问题是,VCS 目前与语言无关,其合并算法的基础是文本差异。这意味着他们正在寻找哪些变化以及相关的背景。
这里重要的部分是上下文。无非就是你修改前后的一些行。
这意味着它们非常不擅长处理同一文件内的代码重新组织,因为您基本上搞砸了它们可以依赖的所有上下文。
通常,在您的示例中,通过切换两个函数,您不仅完全颠倒了两个变更集之间的上下文,而且更糟糕的是,通过在最新函数之后没有额外的行,您隐式地减少了最新更改的上下文,从而减少了机会合并算法能够弄清楚你到底做了什么。
我目前只知道 msft 的一种用于 XML 的 diff 工具,它试图处理更改的语义,而不仅仅是其文本表示。
我也知道PlasticSCM的人正在尝试为一些主流语言实现这样的功能,但这确实是一个有改进空间的地方。
Well, you're currently hitting one of the limitation of, basically, ANY current VCS (DVCS or not, it does not matter).
The thing is that VCS are currently language agnostic, the base of their merge algorithm is a textual diff. It means that they are looking for what change and what is the related context.
The important part here is the context. It is nothing more that some lines before and after the changes you made.
This means that they are really bad at dealing with code re-organisation inside of a same file, because you're basically screwing all the context they can rely on.
Typically, in your example, by switching the two functions, you not only completely inverted the context between the two changesets, but worse, by having no extra lines after the latest function, you reduced implicitly the context of the latest change, diminishing the chances that a merge algorithm got able to figure out what you really did.
I currently only know about one diff tool, from msft, for XML, that is trying to deal with the semantic of your change and not just its textual representation.
I also know that the guys of PlasticSCM are trying to implement such feature for some mainstream languages, but it is really a place where there is room for improvement.