How safe are automatic merges in Mercurial?
In Mercurial there is the fetch extension that you can use to emulate something like svn update
, i.e. to merge with incoming changes without even looking at them. But even if you don't use 开发者_如何学Gohg fetch
, most of your merges will "miraculously" work without resulting in a conflict. This is great, but how safe is it to trust such merges to be valid merges of, say, Java code?
Are there any examples to demonstrate why or when these merges should be (or shouldn't be) trusted?
Well, they are quite safe as long as you don't start reorganizing your code.
Consider the following example:
class A {
public void methodA() {
// do something here
// that should be great
}
public void methodB() {
// And now I'll
// do something here
// and it's gonna be good.
}
public void methodC() {
// Finally, let's
// do something here
}
}
Now, you start working and decide to add some instructions to methodC.
During this time, a co-worker decided that methodC, for whatever reason, should be placed on top of the class.
You will end up with two versions to merge.
Yours:
class A {
public void methodA() {
// do something here
// that should be great
}
public void methodB() {
// And now I'll
// do something here
// and it's gonna be good.
}
public void methodC() {
// Finally, let's
// do something here
// and here your marvelous changes
}
}
And your co-worker ones:
class A {
public void methodC() {
// Finally, let's
// do something here
}
public void methodA() {
// do something here
// that should be great
}
public void methodB() {
// And now I'll
// do something here
// and it's gonna be good.
}
}
When the merge occurs, as the default context is three lines wide, the automatic merge might consider that a valid result would be this one:
class A {
public void methodC() {
// Finally, let's
// do something here
}
public void methodA() {
// do something here
// that should be great
}
public void methodB() {
// And now I'll
// do something here
// and it's gonna be good.
}
public void methodC() {
// Finally, let's
// do something here
// and here your marvelous changes
}
}
For that reason, if possible, I try to keep methods organized so that accessors are grouped, and untouched, privates are grouped by common logic, etc... But it is not always possible.
Hopefully this case is quite rare and if there are too many changes, mercurial will request you to merge the classes manually.
I've been using Mercurial at work for several months now. The overall team is 50+ SW developers - divided down into sub-groups of 5-10 developers. We've yet to see a failed merge that was not the result of:
- a parent file being broken/corrupted going into the merge (ie: coding error)
- incorrect resolution of merge conflicts by developer
So, we've been perfectly happy with the merge results for standard text files (.c, .h, .pl, makefile, linkerfiles, etc..).
We have identified a case in which merging is a bad idea and can result in some issues, and this involves auto-generated code or models being stored as text files. Mercurial will not try to merge binary files, but it will happily merge models, auto-generated .c/.h files, and so on. You can specify merge strategies per directory or file type, but even with these settings in place, inappropriate merging can occur due to Mercurial's premerge. Outside of these cases, Hg merging has been very effective for us.
ps: if you are interested in the model/auto-gen case, find a suggestion here.
A merge commit is as serious as a "regular" code changing commit. So all rules for regular development also apply for a merge. There is no magic which makes any VCS do the right thing with the code during a merge, basically they use sophisticated text search-and-replace algorithms to place the changes of both branches into the output document. Merge conflicts only arise when the text replacement fails, but not when the semantic of the code goes wrong.
The developer performing the merge has to decide if these changes are correct, typically by reviewing the differences to both merge parents (, and check that the software compiles, and running unit tests, and having a peer review, and ... you know the drill).
I use hg fetch
all the time. I let it auto pull/merge/commit and then I build+test. If it fails you can either hg backout
the last transaction or just fix what's broken and then commit a new changeset before pushing.
If you're suspicious of the merge process, the horse has already left the barn.
Coordinated development requires just that: coordination. You shouldn't be simultaneously working on the same segment of code. Whether that means not working in the same function, class or file depends on your circumstances and the scope of your changes, a subject too deep for a simple explanation here.
The point is, if you don't know that you set out upon the changes with good coordination, neither an automated merge process nor a manual one, however smooth, provide a guarantee of a good outcome. At best it can tell you that you ended up not working in the same piece of code, but that's no comfort against semantic and logical changes that break your code or, worse, subtly pollute it. That's true regardless of whether or not it merges without complaint or even if it compiles.
The best defense is a test suite, which allows you to inspect the end product in an automated fashion. Lucikily, that also happens to be one of the best approaches to ongoing maintenance and development anyway.
All that said, most of the merges I have done have gone without a hitch. The ones that have caused trouble made themselves known as conflicts in the merge process, which was enough to tell us to look more closely at the code in question but also, and more importantly, the techniques we were using to partition our work. It's better to get it right going in, but it's also hard to know what "right" is until you've messed up a couple times. That doesn't mean we didn't introduce logical errors, and we don't test all of our code, but the world isn't perfect either.
What it boils down to is that Mercurial's merge process is the same as but better than the process without it, a net positive. You get to skip manually merging all of those things that seem innocuous, which even if they did have logical errors, you probably would miss on a cursory inspection such as a manual merge. It's just faster and zeroes in on those that are conflicts, which are your best smoking gun for the logical errors too in any case.
The only real answer is to explicitly coordinate your efforts up front as well as invest in a testing methodology, such as TDD with unit testing. Blaming the merge is weaksauce.
精彩评论