Question

We have switched to Mercurial recently. All had been going well until we had two incidents of committed changes going missing. Examining the logs has not made us any wiser.

Below is an example. The files committed at (1) revert to a previous state at (2) even though those files are not mentioned in the merge.

What can I check to understand why the files reverted?

revision graph

Was it helpful?

Solution

There are three interesting changesets in this graph that can influence the (2) merge:

  • Teal changeset: not shown, but looks like it's just below the graph. This is the first parent of (2)
  • Blue changeset: number five from the bottom, labelled "Fix test". This is the second parent of (2).
  • Common ancestor of the parents: also not shown, will be further below. Strangely, it looks like the teal changeset could be the common ancestor, but Mercurial will now allow you to make such a degenerate merge under normal circumstances.

When Mercurial does a merge, these are the only three changesets that matter: the two heads you merge and their common ancestor. In a three-way merge the logic is now:

ancestor  parent1  parent2  =>  merge
X         X        Y            Y (clean)
X         Y        X            Y (clean)
X         Y        Y            Y (clean)
X         Y        Z            W (conflict)

Read the table like this: "if the ancestor was X, and the first parent was also X and the second parent was Y, then the merge will contain Y". In other words: a three-way merge favors change and will let a modification win.

You can find the ancestor with

$ hg log -r "ancestor(p1(changeset-2), p2(changeset-2))"

where changeset-2 is the one marked with (2) above. When you say

The files committed at (1) revert to a previous state at (2) even though those files are not mentioned in the merge.

then it's important to understand that "a merge" is just a snapshot that shows how to mix two other changesets. The change made "in" a merge is the difference between this snapshot and its two parent changesets:

$ hg status --rev "p1(changeset-2):changeset-2"
$ hg status --rev "p2(changeset-2):changeset-2"

This shows how the merge changeset is different from its first and second parent, respectively. I'm sure the files are mentioned in one of those lists — unless the merge isn't the culprit after all.

When you examine the three changesets and the differences between them, then you will probably see that someone has to resolve a conflict (the fourth line in the merge table above) and picked the wrong file at some step along the way.

OTHER TIPS

The merge at 2 is between a very old branch (dark blue, forked from the mainline/green branch just after commit 1) and an even older branch (light blue, hasn't been in sync with mainline since before commit 1)

It seems likely that the merge at 2 picked the wrong version of the file - can't tell from here if that was the tool picking the wrong version of the file, or the user manually selecting the wrong version.

Edited to add:

To help track down exactly what changed at 2, you can use hg diff -r REV1 -r REV2, which will show you the line-by-line differences between any two revisions.

When you know that the badness was introduced sometime between point 1 and point 2, hg bisect may help you track down the exact source of the badness:

hg bisect [-gbsr] [-U] [-c CMD] [REV]

subdivision search of changesets

This command helps to find changesets which introduce problems. To use,

mark the earliest changeset you know exhibits the problem as bad, then mark the latest changeset which is free from the problem as good.

Bisect will update your working directory to a revision for testing (unless the -U/--noupdate option is specified). Once you have performed tests, mark the working directory as good or bad, and bisect will either update to another candidate changeset or announce that it has found the bad revision.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top