# Re: fourth tree: "INHERITED" (was: wc-ng base/working nodes in a copied tree)

From: Greg Stein <gstein_at_gmail.com>
Date: Wed, 7 Apr 2010 17:25:26 -0400

After some further discussion on IRC, and some thought...

I think this may be more of a representational problem, and might not
be a "true" fourth tree. Especially because supporting the revert
scenario actually implies N trees. Bert tried to describe this a while
back, but I didn't understand his description (too many "A" nodes).
Consider the following:

\$ svn cp A X # copies A/Y/Z/file
\$ svn cp B X/Y # copies B/Z/file
\$ svn cp C X/Y/Z # copies C/file
\$ svn cp file X/Y/Z/file

We have four operation roots, and four layers of "file". Reverting
each op-root will reveal the previous layer.

In 1.6, we probably had just one layer, but if we're going to solve
this, then let's do it right.

I propose that we create a new table called NODE_DATA which is keyed
by <wc_id, local_relpath, op_depth>. The first two are the usual, and
op_depth is the "operation depth". In the above example, we have four
WORKING_NODE rows, each establishing an operation root, with
local_relpath values of [X, X/Y, X/Y/Z, X/Y/Z/file]. In the NODE_DATA
table, we have the following four rows:

<1, X/Y/Z/file, 1> # from the X op-root
<1, X/Y/Z/file, 2> # from the X/Y op-root
<1, X/Y/Z/file, 3> # from the X/Y/Z op-root
<1, X/Y/Z/file, 4> # from the X/Y/Z/file op-root

Essentially, op_depth = oproot_relpath.count('/') + 1

We can record BASE node data as op_depth == 0.

Looking up the data for "file" is a query like this:

SELECT * from NODE_DATA
WHERE wc_id = ?1 AND local_relpath = ?2
ORDER BY op_depth DESC
LIMIT 1;

That provides the "current" file data.

Some of the common columns between BASE_NODE and WORKING_NODE move to
this new NODE_DATA table. I think they are:

kind, [checksum], changed_*, properties

Those columns, plus the key, may be about it. I don't know that this
table needs a presence column, as the "visible" state is determined by
the BASE and WORKING trees. This is why I suggest that maybe we're
looking more at how to represent (in the database) the WORKING tree,
than truly adding a new "tree".

Cheers,
-g

On Wed, Apr 7, 2010 at 16:15, Greg Stein <gstein_at_gmail.com> wrote:
> On Wed, Apr 7, 2010 at 04:52, Philip Martin <philip.martin_at_wandisco.com> wrote:
>> Greg Stein <gstein_at_gmail.com> writes:
>>
>>> I believe that we have the following operations:
>>>
>>
>> Those two are much the same.  It makes little difference whether it's
>
> Well... we couldn't distinguish these two cases before. With a few
> additional presence values, we can.
>
>>
>> There is also
>>
>> replace: base node not-present + add
>>
>> the old deleted=true state.  This is explicitly excluded from being a
>> replace in svn_wc__internal_is_replaced but that may be a bug (it
>> causes revert to erroneously remove the not-present base node).
>
> It is excluded from being svn_wc_schedule_replace. I'd prefer to
> ignore that fact.
>
>
>>
>>> delete-base: deleting a base node
>>> delete-child: deleting a child of an add/copy/move
>>> moved-away(-within): same as delete*
>>>
>>> I'm thinking that we add the following presence values (for
>>> WORKING_NODE.presence):
>>>
>>> "added": same as "normal" today. clarifies what this really means. we
>>> map this to status_added, so why not use this for the presence
>>> anyways? no need to "minimize/conserve" the set of presence values.
>>>
>>> "replaced": indicates an added/copied-here/moved-here node that
>>> replaces a child of a copied-here/moved-here subtree.
>>>
>>> "deleted": same as "not-present" today. clarifies what this really means.
>>>
>>> "inherit": applied to children of copied-here/moved-here and
>>> deleted/base-deleted nodes. implies that no commit operations are
>>> required for these nodes.
>>
>> Being able to distinguish add and replace is not enough for full 1.6
>> compatibility.  When a node replaces a copied child it overwrites the
>> child's data, things like checksum and properties.  This data is not
>> derived or inherited from the copied parent, so it cannot be restored
>> after being overwriten.  In 1.6 it is possible to revert the replace
>> and restore to the copied child.
>
> Urgh. Yeah. I've only ever looked at that scenario as "no partial
> reverts (yet). revert the whole thing". In that case, meaning the
> whole copy. But that's not right, if the user is reverting
> SOME/SUBDIR/PATH. Only the change(s) to the child should be reverted.
>
>> Another problem is a copy of a mixed revision tree that includes base
>> nodes that are not-present.  In 1.6 we represent these as "fake"
>> schedule deletes in the copy, so that they are explicitly deleted when
>> the copy is committed.  This works but has problems, the main one
>> being that if one tries to revert the delete the full node information
>> is not available (because the not-present source doesn't have it).
>> Perhaps we should have a distinct presence for this type of node?
>> There are similar questions about absent and excluded nodes.
>
> Okay. In this case, reverting the delete should result in an excluded
> node. That's the best we can do. "svn update --depth=infinity
> NOTPRESENT" can restore the file.
>
> Note that we *could* revert a replaced-child into an excluded node,
> too. That would be a feature loss compared to 1.6, however.
>
>
> It seems like we need one more tree, to hold "inherit" data. If you do
> a further operation on an operation-root (something in WORKING_NODE),
> then it will alter that node and all inherited nodes. But if you
> perform an operation on an inherited node, then you're establishing a
> new operation-root in WORKING_NODE. The inherited data can still
> remain elsewhere, but WORKING_NODE now refers to a new operation-root.
> Reverting that operation would bring back the inherited node.
>
> Thus, WORKING_NODE would become *just* explicit operations. ie. the
> stuff we send during a commit.
>
> Hmm. Almost...
>
> A delete operation could mark the root in WORKING_NODE (we'd probably
> want a new name for this!), and then drop a bunch of markers in
> WORKING_NODE to occlude any children present in the inherited tree and
> the BASE tree. Those markers couldn't be individually reverted
> however, and they don't represent data to send during commit. We
> *could* alter a presence-like flag in the inherited tree instead, yet
> leave all the data intact for a revert. Or place markers in the
> inherited tree to occlude the BASE nodes.
>
> Thoughts?
>
> Cheers,
> -g
>