Patchwork [3,of,3] merge: add file ancestor linknode to mergestate

login
register
mail settings
Submitter Durham Goode
Date Jan. 7, 2016, 1:40 a.m.
Message ID <a8adf78352f494c7cf88.1452130808@dev8486.prn1.facebook.com>
Download mbox | patch
Permalink /patch/12586/
State Superseded
Commit 8abd9f785030793485836f3708d8ab21e8724cf3
Delegated to: Martin von Zweigbergk
Headers show

Comments

Durham Goode - Jan. 7, 2016, 1:40 a.m.
# HG changeset patch
# User Durham Goode <durham@fb.com>
# Date 1452127911 28800
#      Wed Jan 06 16:51:51 2016 -0800
# Node ID a8adf78352f494c7cf88221bd346ecdda2a9cdfa
# Parent  7afd0e010fd8d7fea0e6339f5f280fa192c1e176
merge: add file ancestor linknode to mergestate

During a merge, each file has a current filenode, an other filenode, and an
ancestor filenode. The ancestor filenode's linknode is not stored though, and we
rely on the ability for the filectx() to look up the linknode by using the
revlog's linkrev. In alternative backends (like remotefilelog), linkrev's may
not be present or may have restriction that prevent arbitrary linkrev look up
given a filenode.

This patch accounts for that by storing the ancestor filenode's linknode in the
merge state so that it is available later at resolution time.

This results in some minor test changes due to the extra state, and due to us no
longer always using the earliest commit (i.e. the linkrev target) for that
filenode (which is ok, since we're still using an ancestor of current and other
that uses the correct filenode).
Durham Goode - Jan. 7, 2016, 1:48 a.m.
On 1/6/16 5:40 PM, Durham Goode wrote:
> # HG changeset patch
> # User Durham Goode <durham@fb.com>
> # Date 1452127911 28800
> #      Wed Jan 06 16:51:51 2016 -0800
> # Node ID a8adf78352f494c7cf88221bd346ecdda2a9cdfa
> # Parent  7afd0e010fd8d7fea0e6339f5f280fa192c1e176
> merge: add file ancestor linknode to mergestate
>
> During a merge, each file has a current filenode, an other filenode, and an
> ancestor filenode. The ancestor filenode's linknode is not stored though, and we
> rely on the ability for the filectx() to look up the linknode by using the
> revlog's linkrev. In alternative backends (like remotefilelog), linkrev's may
> not be present or may have restriction that prevent arbitrary linkrev look up
> given a filenode.
>
> This patch accounts for that by storing the ancestor filenode's linknode in the
> merge state so that it is available later at resolution time.
>
> This results in some minor test changes due to the extra state, and due to us no
> longer always using the earliest commit (i.e. the linkrev target) for that
> filenode (which is ok, since we're still using an ancestor of current and other
> that uses the correct filenode).
>
For anyone curious, the following is a list of all the places in 
Mercurial that construct filectx's without providing a changeid/ctx:

     File "/data/users/durham/hg/hgext/children.py", line 47, in children
     File "/data/users/durham/hg/hgext/keyword.py", line 231, in linkctx
     File "/data/users/durham/hg/mercurial/commands.py", line 5335, in 
parents
     File "/data/users/durham/hg/mercurial/context.py", line 1711, in 
parents
     File "/data/users/durham/hg/mercurial/context.py", line 900, in parents
     File "/data/users/durham/hg/mercurial/copies.py", line 268, in makectx
     File "/data/users/durham/hg/mercurial/merge.py", line 1040, in 
applyupdates
     File "/data/users/durham/hg/mercurial/tags.py", line 120, in 
findglobaltags
     File "/data/users/durham/hg/mercurial/tags.py", line 122, in 
findglobaltags

Eventually I'd like to get rid of all of these (except perhaps the tag 
stuff), so we can enable non-revlog based storage.
Durham Goode - Jan. 12, 2016, 8:22 p.m.
On 1/6/16 5:40 PM, Durham Goode wrote:
> # HG changeset patch
> # User Durham Goode <durham@fb.com>
> # Date 1452127911 28800
> #      Wed Jan 06 16:51:51 2016 -0800
> # Node ID a8adf78352f494c7cf88221bd346ecdda2a9cdfa
> # Parent  7afd0e010fd8d7fea0e6339f5f280fa192c1e176
> merge: add file ancestor linknode to mergestate
>
> During a merge, each file has a current filenode, an other filenode, and an
> ancestor filenode. The ancestor filenode's linknode is not stored though, and we
> rely on the ability for the filectx() to look up the linknode by using the
> revlog's linkrev. In alternative backends (like remotefilelog), linkrev's may
> not be present or may have restriction that prevent arbitrary linkrev look up
> given a filenode.
>
> This patch accounts for that by storing the ancestor filenode's linknode in the
> merge state so that it is available later at resolution time.
>
> This results in some minor test changes due to the extra state, and due to us no
> longer always using the earliest commit (i.e. the linkrev target) for that
> filenode (which is ok, since we're still using an ancestor of current and other
> that uses the correct filenode).
>
Ping! Given that this is an optional parameter, and is a dictionary (so 
extensible), I feel this shouldn't be too crazy of an accept.
Martin von Zweigbergk - Jan. 13, 2016, 6:47 p.m.
On Wed, Jan 6, 2016 at 5:40 PM, Durham Goode <durham@fb.com> wrote:
> # HG changeset patch
> # User Durham Goode <durham@fb.com>
> # Date 1452127911 28800
> #      Wed Jan 06 16:51:51 2016 -0800
> # Node ID a8adf78352f494c7cf88221bd346ecdda2a9cdfa
> # Parent  7afd0e010fd8d7fea0e6339f5f280fa192c1e176
> merge: add file ancestor linknode to mergestate
>
> During a merge, each file has a current filenode, an other filenode, and an
> ancestor filenode. The ancestor filenode's linknode is not stored though, and we
> rely on the ability for the filectx() to look up the linknode by using the
> revlog's linkrev. In alternative backends (like remotefilelog), linkrev's may
> not be present or may have restriction that prevent arbitrary linkrev look up
> given a filenode.
>
> This patch accounts for that by storing the ancestor filenode's linknode in the
> merge state so that it is available later at resolution time.
>
> This results in some minor test changes due to the extra state, and due to us no
> longer always using the earliest commit (i.e. the linkrev target) for that
> filenode (which is ok, since we're still using an ancestor of current and other
> that uses the correct filenode).

Why is the linknode looked up at merge time not always the same as
that looked up at resolution time? I'm guessing I misunderstood the
description.
Durham Goode - Jan. 13, 2016, 7:42 p.m.
On 1/13/16, 10:47 AM, "Martin von Zweigbergk" <martinvonz@google.com> wrote:

>On Wed, Jan 6, 2016 at 5:40 PM, Durham Goode <durham@fb.com> wrote:

>> # HG changeset patch

>> # User Durham Goode <durham@fb.com>

>> # Date 1452127911 28800

>> #      Wed Jan 06 16:51:51 2016 -0800

>> # Node ID a8adf78352f494c7cf88221bd346ecdda2a9cdfa

>> # Parent  7afd0e010fd8d7fea0e6339f5f280fa192c1e176

>> merge: add file ancestor linknode to mergestate

>>

>> During a merge, each file has a current filenode, an other filenode, and an

>> ancestor filenode. The ancestor filenode's linknode is not stored though, and we

>> rely on the ability for the filectx() to look up the linknode by using the

>> revlog's linkrev. In alternative backends (like remotefilelog), linkrev's may

>> not be present or may have restriction that prevent arbitrary linkrev look up

>> given a filenode.

>>

>> This patch accounts for that by storing the ancestor filenode's linknode in the

>> merge state so that it is available later at resolution time.

>>

>> This results in some minor test changes due to the extra state, and due to us no

>> longer always using the earliest commit (i.e. the linkrev target) for that

>> filenode (which is ok, since we're still using an ancestor of current and other

>> that uses the correct filenode).

>

>Why is the linknode looked up at merge time not always the same as

>that looked up at resolution time? I'm guessing I misunderstood the

>description.


Because at merge time it's using the commit node from the common commit ancestor (not the actual linkrev target of the file node).

For instance, if you had a repo with a DAG like:

C->B->A
D->B->A
Where A creates file foo and C and D edit file foo (but B does not).

If we merge C and D, the merge time commit node is 'B' since it is the commit common ancestor, but the linkrev for foo-v1 points at A, since that's what introduced foo-v1.  But using B is ok, since it also contains foo-v1.
Martin von Zweigbergk - Jan. 14, 2016, 5:09 p.m.
On Wed, Jan 13, 2016 at 11:42 AM, Durham Goode <durham@fb.com> wrote:
>
>
>
>
>
> On 1/13/16, 10:47 AM, "Martin von Zweigbergk" <martinvonz@google.com> wrote:
>
>>On Wed, Jan 6, 2016 at 5:40 PM, Durham Goode <durham@fb.com> wrote:
>>> # HG changeset patch
>>> # User Durham Goode <durham@fb.com>
>>> # Date 1452127911 28800
>>> #      Wed Jan 06 16:51:51 2016 -0800
>>> # Node ID a8adf78352f494c7cf88221bd346ecdda2a9cdfa
>>> # Parent  7afd0e010fd8d7fea0e6339f5f280fa192c1e176
>>> merge: add file ancestor linknode to mergestate
>>>
>>> During a merge, each file has a current filenode, an other filenode, and an
>>> ancestor filenode. The ancestor filenode's linknode is not stored though, and we
>>> rely on the ability for the filectx() to look up the linknode by using the
>>> revlog's linkrev. In alternative backends (like remotefilelog), linkrev's may
>>> not be present or may have restriction that prevent arbitrary linkrev look up
>>> given a filenode.
>>>
>>> This patch accounts for that by storing the ancestor filenode's linknode in the
>>> merge state so that it is available later at resolution time.
>>>
>>> This results in some minor test changes due to the extra state, and due to us no
>>> longer always using the earliest commit (i.e. the linkrev target) for that
>>> filenode (which is ok, since we're still using an ancestor of current and other
>>> that uses the correct filenode).
>>
>>Why is the linknode looked up at merge time not always the same as
>>that looked up at resolution time? I'm guessing I misunderstood the
>>description.
>
> Because at merge time it's using the commit node from the common commit ancestor (not the actual linkrev target of the file node).
>
> For instance, if you had a repo with a DAG like:
>
> C->B->A
> D->B->A
> Where A creates file foo and C and D edit file foo (but B does not).
>
> If we merge C and D, the merge time commit node is 'B' since it is the commit common ancestor, but the linkrev for foo-v1 points at A, since that's what introduced foo-v1.  But using B is ok, since it also contains foo-v1.

Thanks for the explanation. I think I see what's going on now. It
seems like your patch doesn't seem to match the commit message. You
say "This patch accounts for that by storing the ancestor filenode's
linknode in the merge state so that it is available later at
resolution time." (i.e. commit A in your example), but what you
actually store is the merge's common ancestor (commit B in your
example). The first paragraph in the commit message could also use
some rephrasing, specifically the "The ancestor filenode's linknode is
not stored though", which to me implies that that's what you're about
to change.
Augie Fackler - Jan. 14, 2016, 6:26 p.m.
On Tue, Jan 12, 2016 at 12:22:45PM -0800, Durham Goode wrote:
> On 1/6/16 5:40 PM, Durham Goode wrote:
> ># HG changeset patch
> ># User Durham Goode <durham@fb.com>
> ># Date 1452127911 28800
> >#      Wed Jan 06 16:51:51 2016 -0800
> ># Node ID a8adf78352f494c7cf88221bd346ecdda2a9cdfa
> ># Parent  7afd0e010fd8d7fea0e6339f5f280fa192c1e176
> >merge: add file ancestor linknode to mergestate
> >
> >During a merge, each file has a current filenode, an other filenode, and an
> >ancestor filenode. The ancestor filenode's linknode is not stored though, and we
> >rely on the ability for the filectx() to look up the linknode by using the
> >revlog's linkrev. In alternative backends (like remotefilelog), linkrev's may
> >not be present or may have restriction that prevent arbitrary linkrev look up
> >given a filenode.
> >
> >This patch accounts for that by storing the ancestor filenode's linknode in the
> >merge state so that it is available later at resolution time.
> >
> >This results in some minor test changes due to the extra state, and due to us no
> >longer always using the earliest commit (i.e. the linkrev target) for that
> >filenode (which is ok, since we're still using an ancestor of current and other
> >that uses the correct filenode).
> >
> Ping! Given that this is an optional parameter, and is a dictionary (so
> extensible), I feel this shouldn't be too crazy of an accept.

I'll take some time to look at these patches today or tomorrow, as
they sound highly relevant to my long-term interests.

> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel
Durham Goode - Jan. 14, 2016, 7:26 p.m.
On 1/14/16, 9:09 AM, "Martin von Zweigbergk" <martinvonz@google.com> wrote:

>On Wed, Jan 13, 2016 at 11:42 AM, Durham Goode <durham@fb.com> wrote:

>>

>>

>>

>>

>>

>> On 1/13/16, 10:47 AM, "Martin von Zweigbergk" <martinvonz@google.com> wrote:

>>

>>>On Wed, Jan 6, 2016 at 5:40 PM, Durham Goode <durham@fb.com> wrote:

>>>> # HG changeset patch

>>>> # User Durham Goode <durham@fb.com>

>>>> # Date 1452127911 28800

>>>> #      Wed Jan 06 16:51:51 2016 -0800

>>>> # Node ID a8adf78352f494c7cf88221bd346ecdda2a9cdfa

>>>> # Parent  7afd0e010fd8d7fea0e6339f5f280fa192c1e176

>>>> merge: add file ancestor linknode to mergestate

>>>>

>>>> During a merge, each file has a current filenode, an other filenode, and an

>>>> ancestor filenode. The ancestor filenode's linknode is not stored though, and we

>>>> rely on the ability for the filectx() to look up the linknode by using the

>>>> revlog's linkrev. In alternative backends (like remotefilelog), linkrev's may

>>>> not be present or may have restriction that prevent arbitrary linkrev look up

>>>> given a filenode.

>>>>

>>>> This patch accounts for that by storing the ancestor filenode's linknode in the

>>>> merge state so that it is available later at resolution time.

>>>>

>>>> This results in some minor test changes due to the extra state, and due to us no

>>>> longer always using the earliest commit (i.e. the linkrev target) for that

>>>> filenode (which is ok, since we're still using an ancestor of current and other

>>>> that uses the correct filenode).

>>>

>>>Why is the linknode looked up at merge time not always the same as

>>>that looked up at resolution time? I'm guessing I misunderstood the

>>>description.

>>

>> Because at merge time it's using the commit node from the common commit ancestor (not the actual linkrev target of the file node).

>>

>> For instance, if you had a repo with a DAG like:

>>

>> C->B->A

>> D->B->A

>> Where A creates file foo and C and D edit file foo (but B does not).

>>

>> If we merge C and D, the merge time commit node is 'B' since it is the commit common ancestor, but the linkrev for foo-v1 points at A, since that's what introduced foo-v1.  But using B is ok, since it also contains foo-v1.

>

>Thanks for the explanation. I think I see what's going on now. It

>seems like your patch doesn't seem to match the commit message. You

>say "This patch accounts for that by storing the ancestor filenode's

>linknode in the merge state so that it is available later at

>resolution time." (i.e. commit A in your example), but what you

>actually store is the merge's common ancestor (commit B in your

>example). The first paragraph in the commit message could also use

>some rephrasing, specifically the "The ancestor filenode's linknode is

>not stored though", which to me implies that that's what you're about

>to change.


Fair enough, I'll resend with a more correct message.  Thanks for looking

Patch

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -404,6 +404,7 @@  class mergestate(object):
                            fca.path(), hex(fca.filenode()),
                            fco.path(), hex(fco.filenode()),
                            fcl.flags()]
+        self._stateextras[fd] = { 'ancestorlinknode' : hex(fca.node()) }
         self._dirty = True
 
     def __contains__(self, dfile):
@@ -449,10 +450,16 @@  class mergestate(object):
         stateentry = self._state[dfile]
         state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
         octx = self._repo[self._other]
+        extras = self.extras(dfile)
+        anccommitnode = extras.get('ancestorlinknode')
+        if anccommitnode:
+            actx = self._repo[anccommitnode]
+        else:
+            actx = None
         fcd = self._filectxorabsent(hash, wctx, dfile)
         fco = self._filectxorabsent(onode, octx, ofile)
         # TODO: move this to filectxorabsent
-        fca = self._repo.filectx(afile, fileid=anode)
+        fca = self._repo.filectx(afile, fileid=anode, changeid=actx)
         # "premerge" x flags
         flo = fco.flags()
         fla = fca.flags()
diff --git a/tests/test-backout.t b/tests/test-backout.t
--- a/tests/test-backout.t
+++ b/tests/test-backout.t
@@ -686,6 +686,7 @@  Test usage of `hg resolve` in case of co
   * version 2 records
   local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
   other: a30dd8addae3ce71b8667868478542bc417439e6
+  file extras: foo (ancestorlinknode = 91360952243723bd5b1138d5f26bd8c8564cb553)
   file: foo (record type "F", state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
     local path: foo (flags "")
     ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
diff --git a/tests/test-graft.t b/tests/test-graft.t
--- a/tests/test-graft.t
+++ b/tests/test-graft.t
@@ -188,10 +188,10 @@  Graft out of order, skipping a merge and
    e: versions differ -> m (premerge)
   picked tool ':merge' for e (binary False symlink False changedelete False)
   merging e
-  my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
+  my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
    e: versions differ -> m (merge)
   picked tool ':merge' for e (binary False symlink False changedelete False)
-  my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
+  my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   abort: unresolved conflicts, can't continue
   (use hg resolve and hg graft --continue --log)
diff --git a/tests/test-histedit-non-commute-abort.t b/tests/test-histedit-non-commute-abort.t
--- a/tests/test-histedit-non-commute-abort.t
+++ b/tests/test-histedit-non-commute-abort.t
@@ -81,6 +81,7 @@  insert unsupported advisory merge record
   local: 8f7551c7e4a2f2efe0bc8c741baf7f227d65d758
   other: e860deea161a2f77de56603b340ebbb4536308ae
   unrecognized entry: x	advisory record
+  file extras: e (ancestorlinknode = 0000000000000000000000000000000000000000)
   file: e (record type "F", state "u", hash 58e6b3a414a1e090dfc6029add0f3555ccba127f)
     local path: e (flags "")
     ancestor path: e (node null)
@@ -94,6 +95,7 @@  insert unsupported mandatory merge recor
   * version 2 records
   local: 8f7551c7e4a2f2efe0bc8c741baf7f227d65d758
   other: e860deea161a2f77de56603b340ebbb4536308ae
+  file extras: e (ancestorlinknode = 0000000000000000000000000000000000000000)
   file: e (record type "F", state "u", hash 58e6b3a414a1e090dfc6029add0f3555ccba127f)
     local path: e (flags "")
     ancestor path: e (node null)
diff --git a/tests/test-issue672.t b/tests/test-issue672.t
--- a/tests/test-issue672.t
+++ b/tests/test-issue672.t
@@ -68,7 +68,7 @@  https://bz.mercurial-scm.org/672
    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@81f4b099af3d
+  my 1a@e327dca35ac8+ 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)
@@ -91,7 +91,7 @@  https://bz.mercurial-scm.org/672
    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@81f4b099af3d
+  my 1a@746e9549ea96+ other 1a@e327dca35ac8 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-merge-changedelete.t b/tests/test-merge-changedelete.t
--- a/tests/test-merge-changedelete.t
+++ b/tests/test-merge-changedelete.t
@@ -77,14 +77,17 @@  Non-interactive merge:
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -136,14 +139,17 @@  Interactive merge:
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "r", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -205,14 +211,17 @@  Interactive merge with bad input:
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "r", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -261,14 +270,17 @@  Interactive merge with not enough input:
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -306,14 +318,17 @@  Choose local versions of files
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "r", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "r", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -347,14 +362,17 @@  Choose other versions of files
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "r", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "r", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -389,14 +407,17 @@  Fail
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -439,14 +460,17 @@  Force prompts with no input (should be s
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -491,14 +515,17 @@  Force prompts
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -541,14 +568,17 @@  Choose to merge all files
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file extras: file3 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -697,10 +727,12 @@  Non-interactive linear update
   * version 2 records
   local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
@@ -735,10 +767,12 @@  Choose local versions of files
   * version 2 records
   local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "r", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
@@ -771,10 +805,12 @@  Choose other versions of files
   * version 2 records
   local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "r", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
@@ -809,10 +845,12 @@  Fail
   * version 2 records
   local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
@@ -853,10 +891,12 @@  Force prompts with no input
   * version 2 records
   local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
@@ -898,10 +938,12 @@  Choose to merge all files
   * version 2 records
   local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
     other path: file1 (node null)
+  file extras: file2 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff)
   file: file2 (record type "C", state "u", hash null)
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
diff --git a/tests/test-merge-criss-cross.t b/tests/test-merge-criss-cross.t
--- a/tests/test-merge-criss-cross.t
+++ b/tests/test-merge-criss-cross.t
@@ -85,10 +85,10 @@  Criss cross merging
    f2: versions differ -> m (premerge)
   picked tool ':dump' for f2 (binary False symlink False changedelete False)
   merging f2
-  my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@40494bf2444c
+  my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@0f6b37dbe527
    f2: versions differ -> m (merge)
   picked tool ':dump' for f2 (binary False symlink False changedelete False)
-  my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@40494bf2444c
+  my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@0f6b37dbe527
   1 files updated, 0 files merged, 0 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
   [1]
diff --git a/tests/test-rebase-abort.t b/tests/test-rebase-abort.t
--- a/tests/test-rebase-abort.t
+++ b/tests/test-rebase-abort.t
@@ -76,6 +76,7 @@  Insert unsupported advisory merge record
   local: 3e046f2ecedb793b97ed32108086edd1a162f8bc
   other: 46f0b057b5c061d276b91491c22151f78698abd2
   unrecognized entry: x	advisory record
+  file extras: common (ancestorlinknode = 3163e20567cc93074fbb7a53c8b93312e59dbf2c)
   file: common (record type "F", state "u", hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1)
     local path: common (flags "")
     ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
@@ -90,6 +91,7 @@  Insert unsupported mandatory merge recor
   * version 2 records
   local: 3e046f2ecedb793b97ed32108086edd1a162f8bc
   other: 46f0b057b5c061d276b91491c22151f78698abd2
+  file extras: common (ancestorlinknode = 3163e20567cc93074fbb7a53c8b93312e59dbf2c)
   file: common (record type "F", state "u", hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1)
     local path: common (flags "")
     ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
diff --git a/tests/test-resolve.t b/tests/test-resolve.t
--- a/tests/test-resolve.t
+++ b/tests/test-resolve.t
@@ -263,10 +263,12 @@  insert unsupported advisory merge record
   local: 57653b9f834a4493f7240b0681efcb9ae7cab745
   other: dc77451844e37f03f5c559e3b8529b2b48d381d1
   unrecognized entry: x	advisory record
+  file extras: file1 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
   file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
     other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
+  file extras: file2 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
   file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
     local path: file2 (flags "")
     ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
@@ -282,10 +284,12 @@  insert unsupported mandatory merge recor
   * version 2 records
   local: 57653b9f834a4493f7240b0681efcb9ae7cab745
   other: dc77451844e37f03f5c559e3b8529b2b48d381d1
+  file extras: file1 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
   file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
     local path: file1 (flags "")
     ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
     other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
+  file extras: file2 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
   file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
     local path: file2 (flags "")
     ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)