Patchwork D6613: commit: improve the files field of changelog for merges (RFC)

login
register
mail settings
Submitter phabricator
Date July 7, 2019, 4:20 p.m.
Message ID <differential-rev-PHID-DREV-hajuqbksd4yi4zuprsdh-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/40807/
State New
Headers show

Comments

phabricator - July 7, 2019, 4:20 p.m.
valentin.gatienbaron created this revision.
Herald added subscribers: mercurial-devel, mjpieters.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The main reason for the RFC tag is: the output of hg log -T {files} is
  stored in the changelog, despite it being a cache. So any fix to that
  cache changes commit hashes for impacted commits and all their
  descendants, which normally means most of the history (an exception
  being purely linear histories).
  So I wonder if this kind of change is acceptable, or whether some
  other approach is needed?
  (if this is ok, I should probably add a test with a criss-cross merge
  too to make sure the code is behaving as expected)
  
  Currently, the files list of merge commits repeats all the deletions
  (either actual deletions, or files that got renamed) that happened
  between base and p2 of the merge. If p2 is the main branch, the list
  can easily be much bigger than the change being merged.
  
  This impacts various areas of hg:
  
  - changelog becomes bigger than necessary
  - `hg log directory` lists many irrelevant commits
  - it possibly slows down adjustlinkrev, by forcing it to read more manifests, and that function can certainly be a bottleneck
  - the server side of pulls can spend a lot of time simply opening the filelogs for pointless files (the constant factors for opening even a tiny filelog is apparently pretty bad)
  
  So stop listing such files as described in the code.
  
  I don't have many concrete numbers to report, because recreating the
  files list of existing repositories is not easy:
  
  - debugupgradeformat and bundle/unbundle don't recreate the list
  - export/import tends to choke quickly applying patches or on
  
  description that contain diffs,
  
  - merge commits from the convert extension don't have the right files
  
  list for reasons orthogonal to the current commit
  
  - replaying the merge with hg update/hg merge/hg revert --all/hg
  
  commit can end up failing in hg revert
  
  - I wasn't sure that using debugsetparents + debugrebuilddirstate
  
  would really build the right thing
  
  I measured commit time before and after this change, in a case with no
  files filtered out, several files filtered out (no difference) and 5k
  files filtered out (+1% time).
  
  Recreating the 100 more recent merges in a private repo, the
  concatenated uncompressed files lists goes from 1.12MB to
  0.52MB. Excluding 3 merges that are not representative, then the
  size goes from 570k to 15k.
  I converted part of mozilla-central, and observed file list shrinking
  quite a bit too (starting at the very first merge, 733641d9feaf,
  going from 550 files to 10 files), although they have relatively
  few merges, so they probably wouldn't care.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

AFFECTED FILES
  mercurial/localrepo.py
  tests/test-backout.t
  tests/test-commit-amend.t
  tests/test-convert-filemap.t
  tests/test-convert-hg-startrev.t
  tests/test-copies.t
  tests/test-glog-beautifygraph.t
  tests/test-glog.t
  tests/test-issue672.t
  tests/test-lfconvert.t
  tests/test-merge-combination.t
  tests/test-rebase-inmemory.t
  tests/test-rebase-newancestor.t
  tests/test-revset.t
  tests/test-revset2.t
  tests/test-template-keywords.t

CHANGE DETAILS




To: valentin.gatienbaron, #hg-reviewers
Cc: mjpieters, mercurial-devel
phabricator - July 8, 2019, 6:03 a.m.
martinvonz added a comment.


  See some thoughts from me and others on https://bz.mercurial-scm.org/show_bug.cgi?id=4292. Is your goal to shrink the changelog? Or that you feel that the current behavior is incorrect? Or something else?

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers
Cc: martinvonz, mjpieters, mercurial-devel
phabricator - July 9, 2019, 3:03 a.m.
valentin.gatienbaron added a comment.


  I hadn't seen the bug you linked to, or your recent change, though I saw ctx.filesadded() and friends (when writing this) and suspected they were written the way they are to work nicely on merges.
  
  I didn't have one single goal when writing this. Hg's behavior is the source of several problems.
  
  One problem is the size of the changelog and bundles. In part because I assume this creates slowness (hg log -u .. for instance), but also because things look broken as a result: the changelog is bigger than the manifestlog, some bundles consist almost purely of this list of files. I can't really analyze these parts of hg until this is fixed because this problem would shadow everything else.
  I estimate that the changelog would be half the size with this (75% of the changelog taken by files list, 65% of uncompressed files list size come from merges, lists for merge get 60x shorter, and I assume compression ratio wouldn't get too much worse when shortening these files lists), if I could rewrite the history without changing hashes.
  
  Then a couple of weeks ago, I looked at the speed of hg pull. I noticed that server-side hg was opening 35k filelogs when it really needed 12k (~50% server time doing that IIRC). Adding the filtering from this commit into the server-side of pull made it open 13k filelogs, which sounds like a reasonable number (I can't measure whether pull time would be improved given the cost of filtering on the fly, but it certainly wouldn't be any worse).
  
  Finally, I realized that hg log can show seemingly random merge commits because of this, which makes me think this is probably causing other problems that I haven't identified yet.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers
Cc: martinvonz, mjpieters, mercurial-devel
phabricator - July 9, 2019, 4:45 p.m.
This revision now requires changes to proceed.
mharbison72 added a comment.
mharbison72 requested changes to this revision.


  If this goes forward, can this be gated by a config option (even if it is on by default)?  IIUC, this will change the hash for merges, and then cascade.  That seems problematic for convert operations.
  
  Most converts will do things that change hashes anyway, but the default options don't change anything.  This is a convenient behavior for easily migrating to/from LFS, for example.  (In theory, anyway.  In practice, I've seen repos where a convert alters the changelog.)  If there is a config option, convert should probably disable it by default to maintain the current behavior.  I can see a use case for enabling it though, to clean up the repo.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 10, 2019, 12:40 a.m.
valentin.gatienbaron added a comment.


  In D6613#96743 <https://phab.mercurial-scm.org/D6613#96743>, @mharbison72 wrote:
  
  > If this goes forward, can this be gated by a config option (even if it is on by default)?  IIUC, this will change the hash for merges, and then cascade.  That seems problematic for convert operations.
  > Most converts will do things that change hashes anyway, but the default options don't change anything.  This is a convenient behavior for easily migrating to/from LFS, for example.  (In theory, anyway.  In practice, I've seen repos where a convert alters the changelog.)  If there is a config option, convert should probably disable it by default to maintain the current behavior.  I can see a use case for enabling it though, to clean up the repo.
  
  Given the implementation, it's easy to add a knob to disable the change. But that doesn't seem enough: If you have a repository where commits have been created with a mix of hg before and after this change, there is no single value of the knob that would make convert be the identity.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 10, 2019, 4:09 a.m.
mharbison72 added a comment.


  In D6613#96749 <https://phab.mercurial-scm.org/D6613#96749>, @valentin.gatienbaron wrote:
  
  > In D6613#96743 <https://phab.mercurial-scm.org/D6613#96743>, @mharbison72 wrote:
  >
  >> If this goes forward, can this be gated by a config option (even if it is on by default)?  IIUC, this will change the hash for merges, and then cascade.  That seems problematic for convert operations.
  >> Most converts will do things that change hashes anyway, but the default options don't change anything.  This is a convenient behavior for easily migrating to/from LFS, for example.  (In theory, anyway.  In practice, I've seen repos where a convert alters the changelog.)  If there is a config option, convert should probably disable it by default to maintain the current behavior.  I can see a use case for enabling it though, to clean up the repo.
  >
  > Given the implementation, it's easy to add a knob to disable the change. But that doesn't seem enough: If you have a repository where commits have been created with a mix of hg before and after this change, there is no single value of the knob that would make convert be the identity.
  
  It's not great, but at least if you know that about the repository, you can convert with it off up to the point where it switches over.  Then run convert again with it on, and it will continue where it left off.  Not user friendly at all, but at least there's a mechanism without having to use a hacked up copy of hg.  Of course if there's a better way (maybe convert can recognize the source commit used the old way, and explicitly pass a parameter to `localrepo.commitctx()` to bypass this code), that would be nice.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 10, 2019, 12:34 p.m.
valentin.gatienbaron added a comment.


  Ok. Maybe it would be simpler or more robust to do the direct thing: optionally treat the files list in the input commit as input and reuse them blindly in the resulting commit, when doing a hg->hg conversion without filemap.
  (btw, a tweak to test-merge-combinations shows the cases where convert is not the identity: 12-- and -1--, so cases where a file is absent in p1, that p2 added or modified, and the merge redeletes the file. Commit shows such files are modified (rightly) but convert doesn't)

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 10, 2019, 5:09 p.m.
mharbison72 added a comment.
mharbison72 added a subscriber: yuja.


  In D6613#96822 <https://phab.mercurial-scm.org/D6613#96822>, @valentin.gatienbaron wrote:
  
  > Ok. Maybe it would be simpler or more robust to do the direct thing: optionally treat the files list in the input commit as input and reuse them blindly in the resulting commit, when doing a hg->hg conversion without filemap.
  
  Maybe.  I've been wondering if it's possible to just pass along the manifest and changelog instead of recalculating it.  It seems there have been other issues over the years[1].  @yuja fixed something manifest related around the time of that thread IIRC.  Even if a filemap is in use, the map may not modify early commits.  And when those are changed unexpectedly, it makes me wonder what got lost/mangled.  I looked through the repos I converted last year without any file mapping, and there were manifest node changes, but also differences in `files` and `files+` in the changelog.
  
  > (btw, a tweak to test-merge-combinations shows the cases where convert is not the identity: 12-- and -1--, so cases where a file is absent in p1, that p2 added or modified, and the merge redeletes the file. Commit shows such files are modified (rightly) but convert doesn't)
  
  Is that a non-tracked test?  Can you throw a patch up somewhere showing this?  One of the things I noticed last year was that I'd get better results if `cleanp2`[2] was forced to be empty.  I was unable to come up with a simple test case showing a different hash with shipping code and a forcibly emptied `cleanp2`, circa 4.6.  What you're describing seems to be the opposite of 216fa1ba9993 <https://phab.mercurial-scm.org/rHG216fa1ba999333e86200d2608e3b78670168d516>, but something is wrong with that calculation, so maybe it's worth a try.
  
  [1] https://www.mercurial-scm.org/pipermail/mercurial/2018-June/050921.html
  [2] https://www.mercurial-scm.org/repo/hg/file/5.0.2/hgext/convert/hg.py#l552

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: yuja, mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 10, 2019, 11:33 p.m.
JordiGH added inline comments.

INLINE COMMENTS

> localrepo.py:2658
>                      del m[f]
> +                if p2.rev() != nullrev:
> +                    @util.cachefunc

Not all merges can be detected by checking for p2 being non-null; p1 being null but p2 being non-null is perfectly acceptable everywhere else, although normally hg won't produce commits like this in most circumstances.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: JordiGH, yuja, mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 11, 2019, 12:52 a.m.
valentin.gatienbaron added a comment.


  In D6613#96908 <https://phab.mercurial-scm.org/D6613#96908>, @mharbison72 wrote:
  
  > In D6613#96822 <https://phab.mercurial-scm.org/D6613#96822>, @valentin.gatienbaron wrote:
  >
  >> Ok. Maybe it would be simpler or more robust to do the direct thing: optionally treat the files list in the input commit as input and reuse them blindly in the resulting commit, when doing a hg->hg conversion without filemap.
  >
  > Maybe.  I've been wondering if it's possible to just pass along the manifest and changelog instead of recalculating it.  It seems there have been other issues over the years[1].  @yuja fixed something manifest related around the time of that thread IIRC.  Even if a filemap is in use, the map may not modify early commits.  And when those are changed unexpectedly, it makes me wonder what got lost/mangled.  I looked through the repos I converted last year without any file mapping, and there were manifest node changes, but also differences in `files` and `files+` in the changelog.
  
  Ideally, the whole file list would move out the changelog. Then changing the list of modified files wouldn't change hashes anymore, but of course at the cost of changing everything once.
  Given that the filelist in the commit is (ignoring changes to mercurial) a function of the manifest, it may be reasonable to make convert reuse the filelist if the manifestnode gets reused. Or to put it differently, if the conversion is the identity so far for a commit, keep behaving as the identity.
  
  >> (btw, a tweak to test-merge-combinations shows the cases where convert is not the identity: 12-- and -1--, so cases where a file is absent in p1, that p2 added or modified, and the merge redeletes the file. Commit shows such files are modified (rightly) but convert doesn't)
  >
  > Is that a non-tracked test?  Can you throw a patch up somewhere showing this?  One of the things I noticed last year was that I'd get better results if `cleanp2`[2] was forced to be empty.  I was unable to come up with a simple test case showing a different hash with shipping code and a forcibly emptied `cleanp2`, circa 4.6.  What you're describing seems to be the opposite of 216fa1ba9993 <https://phab.mercurial-scm.org/rHG216fa1ba999333e86200d2608e3b78670168d516>, but something is wrong with that calculation, so maybe it's worth a try.
  
  Saying "the cases where convert misbehaves" is probably optimistic. It shows some cases where convert misbehaves. test-merge-combination.t is in the previous differential. cleanp2 seems orthogonal, as fiddling with it doesn't change what I see. This is the tweak I was talking about (based on this differential):
  
    diff --git a/tests/test-merge-combination.t b/tests/test-merge-combination.t
    --- a/tests/test-merge-combination.t
    +++ b/tests/test-merge-combination.t
    @@ -58,9 +58,16 @@ revision. "C" indicates that hg merge ha
       >           else expected=a
       >           fi
       >           got=`hg log -r 3 --template '{files}\n' | tr --delete 'e '`
    +  >           hg --config extensions.convert= convert . repo-convert -s hg -d hg --sourcesort -q
    +  >           got2=`hg --cwd repo-convert log -r 3 --template '{files}\n' | tr --delete 'e '`
       >           if [ "$got" = "$expected" ]
    -  >           then echo "$line$conflicts: agree on \"$got\""
    -  >           else echo "$line$conflicts: hg said \"$got\", expected \"$expected\""
    +  >           then echo -n "$line$conflicts: agree on \"$got\""
    +  >           else echo -n "$line$conflicts: hg said \"$got\", expected \"$expected\""
    +  >           fi
    +  >           if [ "$got" = "$got2" ]; then
    +  >             echo
    +  >           else
    +  >             echo ", commit:\"$got\" vs convert:\"$got2\""
       >           fi
       >           cd ../
       >           rm -rf repo
    @@ -115,7 +122,7 @@ All the merges of various file contents.
       12-1 C: agree on "a"
       12-2 C: hg said "", expected "a"
       12-3 C: agree on "a"
    -  12-- C: agree on "a"
    +  12-- C: agree on "a", commit:"a" vs convert:""
       1-11  : hg said "", expected "a"
       1-12  : agree on "a"
       1-1-  : agree on ""
    @@ -135,7 +142,7 @@ All the merges of various file contents.
       -12- C: agree on "a"
       -1-1  : agree on ""
       -1-2  : agree on "a"
    -  -1--  : agree on "a"
    +  -1--  : agree on "a", commit:"a" vs convert:""
       --11  : agree on ""
       --12  : agree on "a"
       --1-  : agree on "a"

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: JordiGH, yuja, mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 11, 2019, 1:45 a.m.
valentin.gatienbaron added inline comments.

INLINE COMMENTS

> JordiGH wrote in localrepo.py:2658
> Not all merges can be detected by checking for p2 being non-null; p1 being null but p2 being non-null is perfectly acceptable everywhere else, although normally hg won't produce commits like this in most circumstances.

It'd be trivial to change the code to check both parents, but I find what you say surprising. Aren't there plenty of places in hg that do checks similar to this one:

https://www.mercurial-scm.org/repo/hg-committed/file/3bc400ccbf99/mercurial/context.py#l443
https://www.mercurial-scm.org/repo/hg-committed/file/3bc400ccbf99/mercurial/context.py#l2414
https://www.mercurial-scm.org/repo/hg-committed/file/3bc400ccbf99/mercurial/localrepo.py#l2407

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: JordiGH, yuja, mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 11, 2019, 12:51 p.m.
JordiGH added inline comments.

INLINE COMMENTS

> valentin.gatienbaron wrote in localrepo.py:2658
> It'd be trivial to change the code to check both parents, but I find what you say surprising. Aren't there plenty of places in hg that do checks similar to this one:
> 
> https://www.mercurial-scm.org/repo/hg-committed/file/3bc400ccbf99/mercurial/context.py#l443
> https://www.mercurial-scm.org/repo/hg-committed/file/3bc400ccbf99/mercurial/context.py#l2414
> https://www.mercurial-scm.org/repo/hg-committed/file/3bc400ccbf99/mercurial/localrepo.py#l2407

Hm, yes, you're right. I suppose it's a coincidence that a lot of things still work if p1 is null but p2 isn't. It's certainly not something that hg verify nor hg log complains about.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: JordiGH, yuja, mharbison72, martinvonz, mjpieters, mercurial-devel
phabricator - July 14, 2019, 7:31 p.m.
valentin.gatienbaron added a comment.


  @mharbison72  I dealt with the problem of convert in the previous commit. I didn't add a config option to disable the new code path, because it's really awkward to document, and I think it would be annoying to use if I made more fixes (the other case where files are spuriously listed, involving exec bits, probably has little impact on repo size/pull performance but it may be worth fixing anyway because it causes confusion). But if there are downsides to what I did, the config option is still on the table.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D6613/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D6613

To: valentin.gatienbaron, #hg-reviewers, mharbison72
Cc: JordiGH, yuja, mharbison72, martinvonz, mjpieters, mercurial-devel

Patch

diff --git a/tests/test-template-keywords.t b/tests/test-template-keywords.t
--- a/tests/test-template-keywords.t
+++ b/tests/test-template-keywords.t
@@ -807,13 +807,13 @@ 
   $ hg merge 10 -q
   $ hg ci -m 'merge'
   $ hg log -l1 -T '{files}\n'
-  a fourth
+  
   $ hg log -l1 -T '{file_mods}\n'
   
   $ hg log -l1 -T '{file_adds}\n'
   
   $ hg log -l1 -T '{file_dels}\n'
-  a fourth
+  
 
 Test file copies dict:
 
diff --git a/tests/test-revset2.t b/tests/test-revset2.t
--- a/tests/test-revset2.t
+++ b/tests/test-revset2.t
@@ -100,7 +100,6 @@ 
   $ log 'parents(outgoing() or removes(a))'
   1
   4
-  5
   8
 
 test that `or` operation combines elements in the right order:
@@ -805,17 +804,17 @@ 
 (real pair)
 
   $ hg diff -r 'tip^^' -r 'tip'
-  diff -r 2326846efdab -r 24286f4ae135 .hgtags
+  diff -r 2326846efdab -r d2e607fcf9e4 .hgtags
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/.hgtags	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
-  +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
+  +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
   $ hg diff -r 'tip^^::tip'
-  diff -r 2326846efdab -r 24286f4ae135 .hgtags
+  diff -r 2326846efdab -r d2e607fcf9e4 .hgtags
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/.hgtags	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
-  +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
+  +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
 
 (single rev)
 
@@ -829,13 +828,13 @@ 
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/.hgtags	* (glob)
   @@ -0,0 +1,1 @@
-  +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
+  +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
   $ hg diff -r 'tip^ or tip^'
   diff -r d5d0dcbdc4d9 .hgtags
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/.hgtags	* (glob)
   @@ -0,0 +1,1 @@
-  +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
+  +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
 
 (no rev)
 
@@ -1231,59 +1230,59 @@ 
 
 issue4553: check that revset aliases override existing hash prefix
 
-  $ hg log -qr e
-  6:e0cc66ef77e8
+  $ hg log -qr d5e
+  6:d5e6808a8607
 
-  $ hg log -qr e --config revsetalias.e="all()"
+  $ hg log -qr d5e --config revsetalias.d5e="all()"
   0:2785f51eece5
   1:d75937da8da0
   2:5ed5505e9f1c
   3:8528aa5637f2
   4:2326846efdab
   5:904fa392b941
-  6:e0cc66ef77e8
-  7:013af1973af4
+  6:d5e6808a8607
+  7:586353d483b3
   8:d5d0dcbdc4d9
-  9:24286f4ae135
+  9:d2e607fcf9e4
 
-  $ hg log -qr e: --config revsetalias.e="0"
+  $ hg log -qr d5e: --config revsetalias.d5e="0"
   0:2785f51eece5
   1:d75937da8da0
   2:5ed5505e9f1c
   3:8528aa5637f2
   4:2326846efdab
   5:904fa392b941
-  6:e0cc66ef77e8
-  7:013af1973af4
+  6:d5e6808a8607
+  7:586353d483b3
   8:d5d0dcbdc4d9
-  9:24286f4ae135
+  9:d2e607fcf9e4
 
-  $ hg log -qr :e --config revsetalias.e="9"
+  $ hg log -qr :d5e --config revsetalias.d5e="9"
   0:2785f51eece5
   1:d75937da8da0
   2:5ed5505e9f1c
   3:8528aa5637f2
   4:2326846efdab
   5:904fa392b941
-  6:e0cc66ef77e8
-  7:013af1973af4
+  6:d5e6808a8607
+  7:586353d483b3
   8:d5d0dcbdc4d9
-  9:24286f4ae135
+  9:d2e607fcf9e4
 
-  $ hg log -qr e:
-  6:e0cc66ef77e8
-  7:013af1973af4
+  $ hg log -qr d5e:
+  6:d5e6808a8607
+  7:586353d483b3
   8:d5d0dcbdc4d9
-  9:24286f4ae135
+  9:d2e607fcf9e4
 
-  $ hg log -qr :e
+  $ hg log -qr :d5e
   0:2785f51eece5
   1:d75937da8da0
   2:5ed5505e9f1c
   3:8528aa5637f2
   4:2326846efdab
   5:904fa392b941
-  6:e0cc66ef77e8
+  6:d5e6808a8607
 
 issue2549 - correct optimizations
 
@@ -1471,7 +1470,7 @@ 
 (check operator priority)
 
   $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
-  $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
+  $ log "cat2n2(2785f5, 1eece5, d2e607, fcf9e4)"
   0
   4
 
diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -1721,11 +1721,9 @@ 
   4
   $ log 'modifies("*")'
   4
-  6
   $ log 'modifies("set:modified()")'
   4
   $ log 'id(5)'
-  2
   $ log 'only(9)'
   8
   9
@@ -1834,12 +1832,12 @@ 
 
 Test hexadecimal revision
   $ log 'id(2)'
-  $ log 'id(5)'
-  2
-  $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'id(x5)'
-  2
-  $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'x5'
-  2
+  $ log 'id(8)'
+  3
+  $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'id(x8)'
+  3
+  $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'x8'
+  3
   $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'id(x)'
   $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'x'
   abort: 00changelog.i@: ambiguous identifier!
@@ -2096,7 +2094,6 @@ 
   2
   $ log 'removes(a)'
   2
-  6
   $ log 'roots(all())'
   0
   $ log 'reverse(2 or 3 or 4 or 5)'
@@ -2710,7 +2707,6 @@ 
 
   $ log 'sort(outgoing() or reverse(removes(a)), rev)'
   2
-  6
   8
   9
 
@@ -2719,7 +2715,6 @@ 
   $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
   9
   8
-  6
   2
 
 test empty sort key which is noop
diff --git a/tests/test-rebase-newancestor.t b/tests/test-rebase-newancestor.t
--- a/tests/test-rebase-newancestor.t
+++ b/tests/test-rebase-newancestor.t
@@ -108,7 +108,7 @@ 
   $ hg tglog
   @  7: e08089805d82 'default: f-other stuff'
   |
-  | o  6: 9455ee510502 'dev: merge default' dev
+  | o  6: 010ced67e558 'dev: merge default' dev
   |/|
   o |  5: 462860db70a1 'default: remove f-default'
   | |
@@ -136,10 +136,10 @@ 
   file 'f-default' was deleted in local [dest] but was modified in other [source].
   What do you want to do?
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
-  rebasing 6:9455ee510502 "dev: merge default"
-  saved backup bundle to $TESTTMP/ancestor-merge/.hg/strip-backup/1d1a643d390e-43e9e04b-rebase.hg
+  rebasing 6:010ced67e558 "dev: merge default"
+  saved backup bundle to $TESTTMP/ancestor-merge/.hg/strip-backup/1d1a643d390e-4a6f6d17-rebase.hg
   $ hg tglog
-  o  6: fbc098e72227 'dev: merge default'
+  o  6: de147e4f69cf 'dev: merge default'
   |
   o  5: eda7b7f46f5d 'dev: merge default'
   |
@@ -166,10 +166,10 @@ 
   file 'f-default' was deleted in local [dest] but was modified in other [source].
   What do you want to do?
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
-  rebasing 6:9455ee510502 "dev: merge default"
-  saved backup bundle to $TESTTMP/ancestor-merge-2/.hg/strip-backup/ec2c14fb2984-62d0b222-rebase.hg
+  rebasing 6:010ced67e558 "dev: merge default"
+  saved backup bundle to $TESTTMP/ancestor-merge-2/.hg/strip-backup/ec2c14fb2984-827d7a44-rebase.hg
   $ hg tglog
-  o  7: fbc098e72227 'dev: merge default'
+  o  7: de147e4f69cf 'dev: merge default'
   |
   o  6: eda7b7f46f5d 'dev: merge default'
   |
diff --git a/tests/test-rebase-inmemory.t b/tests/test-rebase-inmemory.t
--- a/tests/test-rebase-inmemory.t
+++ b/tests/test-rebase-inmemory.t
@@ -744,7 +744,7 @@ 
   $ hg tglog
   @  6: 676538af172d 'untracked rename of d to e'
   |
-  | *    5: 71cb43376053 'merge'
+  | *    5: 574d92ad16fc 'merge'
   | |\
   | | x  4: 2c8b5dad7956 'rename d to e'
   | | |
@@ -758,8 +758,8 @@ 
   
   $ hg rebase -b 5 -d tip
   rebasing 3:ca58782ad1e4 "b"
-  rebasing 5:71cb43376053 "merge"
-  note: not rebasing 5:71cb43376053 "merge", its destination already has all its changes
+  rebasing 5:574d92ad16fc "merge"
+  note: not rebasing 5:574d92ad16fc "merge", its destination already has all its changes
 
   $ cd ..
 
diff --git a/tests/test-merge-combination.t b/tests/test-merge-combination.t
--- a/tests/test-merge-combination.t
+++ b/tests/test-merge-combination.t
@@ -118,7 +118,7 @@ 
   12-- C: agree on "a"
   1-11  : hg said "", expected "a"
   1-12  : agree on "a"
-  1-1-  : hg said "a", expected ""
+  1-1-  : agree on ""
   1-21 C: agree on "a"
   1-22 C: hg said "", expected "a"
   1-23 C: agree on "a"
diff --git a/tests/test-lfconvert.t b/tests/test-lfconvert.t
--- a/tests/test-lfconvert.t
+++ b/tests/test-lfconvert.t
@@ -158,7 +158,7 @@ 
   initializing destination largefiles-repo
   $ cd largefiles-repo
   $ hg log -G --template "{rev}:{node|short}  {desc|firstline}\n"
-  o    5:8e05f5f2b77e  merge
+  o    5:9cc5aa7204f0  merge
   |\
   | o  4:a5a02de7a8e4  remove large, normal3
   | |
@@ -249,7 +249,7 @@ 
   3 remove large, normal3
   2 merge
   1 add anotherlarge (should be a largefile)
-  0 Added tag mytag for changeset abacddda7028
+  0 Added tag mytag for changeset 17126745edfd
   $ cd ../normal-repo
   $ cat >> .hg/hgrc <<EOF
   > [extensions]
@@ -302,7 +302,7 @@ 
   3 remove large, normal3
   2 merge
   1 add anotherlarge (should be a largefile)
-  0 Added tag mytag for changeset abacddda7028
+  0 Added tag mytag for changeset 17126745edfd
 
   $ hg -R largefiles-repo-hg log -G --template "{rev}:{node|short}  {desc|firstline}\n"
   o  7:2f08f66459b7  Added tag mytag for changeset 17126745edfd
@@ -372,7 +372,7 @@ 
   4 remove large, normal3
   3 merge
   2 add anotherlarge (should be a largefile)
-  1 Added tag mytag for changeset abacddda7028
+  1 Added tag mytag for changeset 17126745edfd
   0 change branch name only
 
 Ensure empty commits aren't lost in the conversion
diff --git a/tests/test-issue672.t b/tests/test-issue672.t
--- a/tests/test-issue672.t
+++ b/tests/test-issue672.t
@@ -60,13 +60,13 @@ 
     checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: c64f439569a9, local: e327dca35ac8+, remote: 746e9549ea96
+   ancestor: c64f439569a9, local: f4a9cff3cd0b+, remote: 746e9549ea96
    preserving 1a for resolve of 1a
   starting 4 threads for background file closing (?)
    1a: local copied/moved from 1 -> m (premerge)
   picked tool ':merge' for 1a (binary False symlink False changedelete False)
   merging 1a and 1 to 1a
-  my 1a@e327dca35ac8+ other 1@746e9549ea96 ancestor 1@c64f439569a9
+  my 1a@f4a9cff3cd0b+ other 1@746e9549ea96 ancestor 1@c64f439569a9
    premerge successful
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
@@ -82,14 +82,14 @@ 
     checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: c64f439569a9, local: 746e9549ea96+, remote: e327dca35ac8
+   ancestor: c64f439569a9, local: 746e9549ea96+, remote: f4a9cff3cd0b
    preserving 1 for resolve of 1a
   removing 1
   starting 4 threads for background file closing (?)
    1a: remote moved from 1 -> m (premerge)
   picked tool ':merge' for 1a (binary False symlink False changedelete False)
   merging 1 and 1a to 1a
-  my 1a@746e9549ea96+ other 1a@e327dca35ac8 ancestor 1@c64f439569a9
+  my 1a@746e9549ea96+ other 1a@f4a9cff3cd0b ancestor 1@c64f439569a9
    premerge successful
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
diff --git a/tests/test-glog.t b/tests/test-glog.t
--- a/tests/test-glog.t
+++ b/tests/test-glog.t
@@ -1951,7 +1951,7 @@ 
 
   $ hg up -q 6
   $ hg log -G --git --patch --follow-first e
-  @    changeset:   6:fc281d8ff18d
+  @    changeset:   6:9feeac35a70a
   |\   tag:         tip
   | ~  parent:      5:99b31f1c2782
   |    parent:      4:17d952250a9d
@@ -1998,7 +1998,7 @@ 
   $ hg log -G --template "{rev} {desc|firstline}\n"
   o  8 add g
   |
-  | o  7 Added tag foo-bar for changeset fc281d8ff18d
+  | o  7 Added tag foo-bar for changeset 9feeac35a70a
   |/
   o    6 merge 5 and 4
   |\
@@ -2161,17 +2161,17 @@ 
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
+  # Node ID 9feeac35a70aa325519bbf3178683271113f2b8f
   # Parent  99b31f1c2782e2deb1723cef08930f70fc84b37b
   # Parent  17d952250a9d03cc3dc77b199ab60e959b9b0260
   merge 5 and 4
   
-  diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
+  diff -r 99b31f1c2782 -r 9feeac35a70a dir/b
   --- a/dir/b	Thu Jan 01 00:00:00 1970 +0000
   +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +0,0 @@
   -a
-  diff -r 99b31f1c2782 -r fc281d8ff18d e
+  diff -r 99b31f1c2782 -r 9feeac35a70a e
   --- a/e	Thu Jan 01 00:00:00 1970 +0000
   +++ b/e	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +1,1 @@
@@ -2181,24 +2181,24 @@ 
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
-  # Parent  fc281d8ff18d999ad6497b3d27390bcd695dcc73
-  Added tag foo-bar for changeset fc281d8ff18d
+  # Node ID 9febbb9c8b2e09670a2fb550cb1e4e01a2c7e9fd
+  # Parent  9feeac35a70aa325519bbf3178683271113f2b8f
+  Added tag foo-bar for changeset 9feeac35a70a
   
-  diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
+  diff -r 9feeac35a70a -r 9febbb9c8b2e .hgtags
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/.hgtags	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
-  +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
+  +9feeac35a70aa325519bbf3178683271113f2b8f foo-bar
   # HG changeset patch
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
-  # Parent  fc281d8ff18d999ad6497b3d27390bcd695dcc73
+  # Node ID 3bd4551ec3fe1c0696241f236abe857a53c6d6e7
+  # Parent  9feeac35a70aa325519bbf3178683271113f2b8f
   add g
   
-  diff -r fc281d8ff18d -r 24c2e826ddeb g
+  diff -r 9feeac35a70a -r 3bd4551ec3fe g
   --- a/g	Thu Jan 01 00:00:00 1970 +0000
   +++ b/g	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,2 +1,1 @@
@@ -2286,7 +2286,7 @@ 
   []
   <spanset- 0:9>
   $ hg log -G --template '{rev} {desc}\n'
-  o  7 Added tag foo-bar for changeset fc281d8ff18d
+  o  7 Added tag foo-bar for changeset 9feeac35a70a
   |
   o    6 merge 5 and 4
   |\
@@ -2384,9 +2384,9 @@ 
 node template with changesetprinter:
 
   $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
-  7  7:02dbb8e276b8
+  7  7:9febbb9c8b2e
   |
-  6    6:fc281d8ff18d
+  6    6:9feeac35a70a
   |\
   | ~
   5  5:99b31f1c2782
@@ -2410,7 +2410,7 @@ 
 
   $ hg log -Gqr 7 --config extensions.color= --color=debug \
   > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
-  [branch.default|7]  [log.node|7:02dbb8e276b8]
+  [branch.default|7]  [log.node|7:9febbb9c8b2e]
   |
   ~
 
diff --git a/tests/test-glog-beautifygraph.t b/tests/test-glog-beautifygraph.t
--- a/tests/test-glog-beautifygraph.t
+++ b/tests/test-glog-beautifygraph.t
@@ -2101,7 +2101,7 @@ 
 
   $ hg up -q 6
   $ hg log -G --git --patch --follow-first e
-  \xe2\x97\x8d  changeset:   6:fc281d8ff18d (esc)
+  \xe2\x97\x8d  changeset:   6:9feeac35a70a (esc)
   \xe2\x94\x82\xe2\x95\xb2   tag:         tip (esc)
   \xe2\x94\x82 \xe2\x95\xa7  parent:      5:99b31f1c2782 (esc)
   \xe2\x94\x82    parent:      4:17d952250a9d (esc)
@@ -2148,7 +2148,7 @@ 
   $ hg log -G --template "{rev} {desc|firstline}\n"
   \xe2\x97\x8b  8 add g (esc)
   \xe2\x94\x82 (esc)
-  \xe2\x94\x82 \xe2\x97\x8b  7 Added tag foo-bar for changeset fc281d8ff18d (esc)
+  \xe2\x94\x82 \xe2\x97\x8b  7 Added tag foo-bar for changeset 9feeac35a70a (esc)
   \xe2\x94\x82\xe2\x95\xb1 (esc)
   \xe2\x97\x8b  6 merge 5 and 4 (esc)
   \xe2\x94\x82\xe2\x95\xb2 (esc)
@@ -2311,17 +2311,17 @@ 
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
+  # Node ID 9feeac35a70aa325519bbf3178683271113f2b8f
   # Parent  99b31f1c2782e2deb1723cef08930f70fc84b37b
   # Parent  17d952250a9d03cc3dc77b199ab60e959b9b0260
   merge 5 and 4
   
-  diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
+  diff -r 99b31f1c2782 -r 9feeac35a70a dir/b
   --- a/dir/b	Thu Jan 01 00:00:00 1970 +0000
   +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +0,0 @@
   -a
-  diff -r 99b31f1c2782 -r fc281d8ff18d e
+  diff -r 99b31f1c2782 -r 9feeac35a70a e
   --- a/e	Thu Jan 01 00:00:00 1970 +0000
   +++ b/e	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,1 +1,1 @@
@@ -2331,24 +2331,24 @@ 
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
-  # Parent  fc281d8ff18d999ad6497b3d27390bcd695dcc73
-  Added tag foo-bar for changeset fc281d8ff18d
+  # Node ID 9febbb9c8b2e09670a2fb550cb1e4e01a2c7e9fd
+  # Parent  9feeac35a70aa325519bbf3178683271113f2b8f
+  Added tag foo-bar for changeset 9feeac35a70a
   
-  diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
+  diff -r 9feeac35a70a -r 9febbb9c8b2e .hgtags
   --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   +++ b/.hgtags	Thu Jan 01 00:00:00 1970 +0000
   @@ -0,0 +1,1 @@
-  +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
+  +9feeac35a70aa325519bbf3178683271113f2b8f foo-bar
   # HG changeset patch
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
-  # Parent  fc281d8ff18d999ad6497b3d27390bcd695dcc73
+  # Node ID 3bd4551ec3fe1c0696241f236abe857a53c6d6e7
+  # Parent  9feeac35a70aa325519bbf3178683271113f2b8f
   add g
   
-  diff -r fc281d8ff18d -r 24c2e826ddeb g
+  diff -r 9feeac35a70a -r 3bd4551ec3fe g
   --- a/g	Thu Jan 01 00:00:00 1970 +0000
   +++ b/g	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,2 +1,1 @@
@@ -2436,7 +2436,7 @@ 
   []
   <spanset- 0:9>
   $ hg log -G --template '{rev} {desc}\n'
-  \xe2\x97\x8b  7 Added tag foo-bar for changeset fc281d8ff18d (esc)
+  \xe2\x97\x8b  7 Added tag foo-bar for changeset 9feeac35a70a (esc)
   \xe2\x94\x82 (esc)
   \xe2\x97\x8b  6 merge 5 and 4 (esc)
   \xe2\x94\x82\xe2\x95\xb2 (esc)
@@ -2534,9 +2534,9 @@ 
 node template with changesetprinter:
 
   $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
-  7  7:02dbb8e276b8
+  7  7:9febbb9c8b2e
   \xe2\x94\x82 (esc)
-  6    6:fc281d8ff18d
+  6    6:9feeac35a70a
   \xe2\x94\x82\xe2\x95\xb2 (esc)
   \xe2\x94\x82 \xe2\x95\xa7 (esc)
   5  5:99b31f1c2782
@@ -2560,7 +2560,7 @@ 
 
   $ hg log -Gqr 7 --config extensions.color= --color=debug \
   > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
-  [branch.default\xe2\x94\x827]  [log.node|7:02dbb8e276b8] (esc)
+  [branch.default\xe2\x94\x827]  [log.node|7:9febbb9c8b2e] (esc)
   \xe2\x94\x82 (esc)
   \xe2\x95\xa7 (esc)
 
diff --git a/tests/test-copies.t b/tests/test-copies.t
--- a/tests/test-copies.t
+++ b/tests/test-copies.t
@@ -268,7 +268,7 @@ 
   $ hg ci -m 'merge rename from p2'
   $ hg l
   @    3 merge rename from p2
-  |\   x
+  |\
   | o  2 add z
   | |  z
   o |  1 rename x to y
@@ -483,9 +483,9 @@ 
   created new head
   $ hg l
   @    5 merge 3 into 1
-  |\   y z
+  |\   z
   +---o  4 merge 1 into 3
-  | |/   x z
+  | |/   z
   | o  3 modify z
   | |  z
   | o  2 rename y to z
diff --git a/tests/test-convert-hg-startrev.t b/tests/test-convert-hg-startrev.t
--- a/tests/test-convert-hg-startrev.t
+++ b/tests/test-convert-hg-startrev.t
@@ -54,7 +54,7 @@ 
   $ glog full
   o  5 "5: change a" files: a
   |
-  o    4 "4: merge 2 and 3" files: e f
+  o    4 "4: merge 2 and 3" files: e
   |\
   | o  3 "3: change a" files: a
   | |
@@ -83,7 +83,7 @@ 
   $ glog full
   o  5 "5: change a" files: a
   |
-  o    4 "4: merge 2 and 3" files: e f
+  o    4 "4: merge 2 and 3" files: e
   |\
   | o  3 "3: change a" files: a
   | |
@@ -130,7 +130,7 @@ 
 (It seems like a bug in log that the following doesn't show rev 1.)
 
   $ hg log --follow --copies e
-  changeset:   2:82bbac3d2cf4
+  changeset:   2:8d3c3fe67bb7
   user:        test
   date:        Thu Jan 01 00:00:04 1970 +0000
   summary:     4: merge 2 and 3
diff --git a/tests/test-convert-filemap.t b/tests/test-convert-filemap.t
--- a/tests/test-convert-filemap.t
+++ b/tests/test-convert-filemap.t
@@ -760,9 +760,8 @@ 
   converted/b
   x
   $ hg -R merge-test2 log -G -T '{shortest(node)} {desc}\n{files % "- {file}\n"}\n'
-  o    6eaa merge a & b
+  o    e2ff merge a & b
   |\   - converted/a
-  | |  - toberemoved
   | |
   | o  2995 add b
   | |  - converted/b
diff --git a/tests/test-commit-amend.t b/tests/test-commit-amend.t
--- a/tests/test-commit-amend.t
+++ b/tests/test-commit-amend.t
@@ -649,7 +649,7 @@ 
   (no more unresolved files)
   $ hg ci -m 'merge bar'
   $ hg log --config diff.git=1 -pr .
-  changeset:   20:163cfd7219f7
+  changeset:   20:5aba7f3726e6
   tag:         tip
   parent:      19:30d96aeaf27b
   parent:      18:1aa437659d19
@@ -682,7 +682,7 @@ 
   $ HGEDITOR="sh .hg/checkeditform.sh" hg ci --amend -m 'merge bar (amend message)' --edit
   HGEDITFORM=commit.amend.merge
   $ hg log --config diff.git=1 -pr .
-  changeset:   21:bca52d4ed186
+  changeset:   21:4b0631ef043e
   tag:         tip
   parent:      19:30d96aeaf27b
   parent:      18:1aa437659d19
@@ -715,7 +715,7 @@ 
   $ hg mv zz z
   $ hg ci --amend -m 'merge bar (undo rename)'
   $ hg log --config diff.git=1 -pr .
-  changeset:   22:12594a98ca3f
+  changeset:   22:06423be42d60
   tag:         tip
   parent:      19:30d96aeaf27b
   parent:      18:1aa437659d19
@@ -751,9 +751,9 @@ 
   $ echo aa >> aaa
   $ hg ci -m 'merge bar again'
   $ hg log --config diff.git=1 -pr .
-  changeset:   24:dffde028b388
+  changeset:   24:a89974a20457
   tag:         tip
-  parent:      22:12594a98ca3f
+  parent:      22:06423be42d60
   parent:      23:4c94d5bc65f5
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -800,9 +800,9 @@ 
   $ hg mv aaa aa
   $ hg ci --amend -m 'merge bar again (undo rename)'
   $ hg log --config diff.git=1 -pr .
-  changeset:   25:18e3ba160489
+  changeset:   25:282080768800
   tag:         tip
-  parent:      22:12594a98ca3f
+  parent:      22:06423be42d60
   parent:      23:4c94d5bc65f5
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -843,9 +843,9 @@ 
   use (c)hanged version, (d)elete, or leave (u)nresolved? c
   $ hg ci -m 'merge bar (with conflicts)'
   $ hg log --config diff.git=1 -pr .
-  changeset:   28:b4c3035e2544
+  changeset:   28:ed15db12298d
   tag:         tip
-  parent:      27:4b216ca5ba97
+  parent:      27:eb5adec0b43b
   parent:      26:67db8847a540
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -855,9 +855,9 @@ 
   $ hg rm aa
   $ hg ci --amend -m 'merge bar (with conflicts, amended)'
   $ hg log --config diff.git=1 -pr .
-  changeset:   29:1205ed810051
+  changeset:   29:0eeafd043f63
   tag:         tip
-  parent:      27:4b216ca5ba97
+  parent:      27:eb5adec0b43b
   parent:      26:67db8847a540
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -974,7 +974,7 @@ 
   HG: M: 
   HG: A: foo
   HG: R: 
-  HG: diff -r 1205ed810051 foo
+  HG: diff -r 0eeafd043f63 foo
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/foo	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
@@ -988,12 +988,12 @@ 
   HG: M: 
   HG: A: foo y
   HG: R: 
-  HG: diff -r 1205ed810051 foo
+  HG: diff -r 0eeafd043f63 foo
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/foo	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
   HG: +foo
-  HG: diff -r 1205ed810051 y
+  HG: diff -r 0eeafd043f63 y
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/y	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
@@ -1006,18 +1006,18 @@ 
   HG: M: 
   HG: A: foo y
   HG: R: a
-  HG: diff -r 1205ed810051 a
+  HG: diff -r 0eeafd043f63 a
   HG: --- a/a	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -1,2 +0,0 @@
   HG: -a
   HG: -a
-  HG: diff -r 1205ed810051 foo
+  HG: diff -r 0eeafd043f63 foo
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/foo	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
   HG: +foo
-  HG: diff -r 1205ed810051 y
+  HG: diff -r 0eeafd043f63 y
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/y	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
@@ -1030,23 +1030,23 @@ 
   HG: M: 
   HG: A: foo y
   HG: R: a x
-  HG: diff -r 1205ed810051 a
+  HG: diff -r 0eeafd043f63 a
   HG: --- a/a	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -1,2 +0,0 @@
   HG: -a
   HG: -a
-  HG: diff -r 1205ed810051 foo
+  HG: diff -r 0eeafd043f63 foo
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/foo	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
   HG: +foo
-  HG: diff -r 1205ed810051 x
+  HG: diff -r 0eeafd043f63 x
   HG: --- a/x	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -1,1 +0,0 @@
   HG: -x
-  HG: diff -r 1205ed810051 y
+  HG: diff -r 0eeafd043f63 y
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/y	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
@@ -1061,23 +1061,23 @@ 
   HG: M: 
   HG: A: foo y
   HG: R: a x
-  HG: diff -r 1205ed810051 a
+  HG: diff -r 0eeafd043f63 a
   HG: --- a/a	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -1,2 +0,0 @@
   HG: -a
   HG: -a
-  HG: diff -r 1205ed810051 foo
+  HG: diff -r 0eeafd043f63 foo
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/foo	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
   HG: +foo
-  HG: diff -r 1205ed810051 x
+  HG: diff -r 0eeafd043f63 x
   HG: --- a/x	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -1,1 +0,0 @@
   HG: -x
-  HG: diff -r 1205ed810051 y
+  HG: diff -r 0eeafd043f63 y
   HG: --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   HG: +++ b/y	Thu Jan 01 00:00:00 1970 +0000
   HG: @@ -0,0 +1,1 @@
diff --git a/tests/test-backout.t b/tests/test-backout.t
--- a/tests/test-backout.t
+++ b/tests/test-backout.t
@@ -583,12 +583,12 @@ 
   (branch merge, don't forget to commit)
   $ hg ci -d '4 0' -m 'merge backout of branch1'
   $ hg id
-  22149cdde76d (branch2) tip
+  d97a8500a969 (branch2) tip
   $ hg st -A
   C default
   C file2
   $ hg summary
-  parent: 4:22149cdde76d tip
+  parent: 4:d97a8500a969 tip
    merge backout of branch1
   branch: branch2
   commit: (clean)
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -2655,6 +2655,47 @@ 
                 drop = sorted([f for f in removed if f in m])
                 for f in drop:
                     del m[f]
+                if p2.rev() != nullrev:
+                    @util.cachefunc
+                    def mas():
+                        p1n = p1.node()
+                        p2n = p2.node()
+                        cahs = self.changelog.commonancestorsheads(p1n, p2n)
+                        return [self[r].manifest() for r in cahs]
+                    def deletionfromparent(f):
+                        # When a file is removed relative to p1 in a merge, this
+                        # function determines whether the absence is due to a
+                        # deletion from a parent, or whether the merge commit
+                        # itself deletes the file. We decide this by doing a
+                        # simplified three way merge of the manifest entry for
+                        # the file. There are two ways we decide the merge
+                        # itself didn't delete a file:
+                        # - neither parent (nor the merge) contain the file
+                        # - exactly one parent contains the file, and that
+                        #   parent has the same filelog entry as the merge
+                        #   ancestor (or all of them if there two). In other
+                        #   words, that parent left the file unchanged while the
+                        #   other one deleted it.
+                        # One way to think about this is that deleting a file is
+                        # similar to emptying it, so the list of changed files
+                        # should be similar either way. The computation
+                        # described above is not done directly in _filecommit
+                        # when creating the list of changed files, however
+                        # it does something very similar by comparing filelog
+                        # nodes.
+                        if f in m1:
+                            return (f not in m2
+                                    and mas()
+                                    and all(f in ma and ma.find(f) == m1.find(f)
+                                            for ma in mas()))
+                        elif f in m2:
+                            return (mas()
+                                    and all(f in ma and ma.find(f) == m2.find(f)
+                                            for ma in mas()))
+                        else:
+                            return True
+                    removed = [f for f in removed if not deletionfromparent(f)]
+
                 files = changed + removed
                 md = None
                 if not files: