### page under construction
common, abstract interfaces for Subversion data trees
It is intended that defining and then implementing common, abstract interfaces will make it easier to develop conceptually simple features such as Shelving and Checkpointing that fundamentally require little more than the ability to copy versioned data between a WC, a repository, a shelf, and other locations yet to be defined (such as a streamy 'patch file' format or an external service).
Introduction: The Problem
In order to implement features such as shelving, we need to be able to move data between the WC and repository and other places – initially from the WC to a "shelf" storage location and back again.
So far (up to Subversion 1.10) each subsystem's APIs work in different ways – writing a new revision at the RA layer uses the "delta editor" which is used in several other places, whereas reading or writing the working tree in the WC requires using a combination of many separate functions, and a different set again for accessing the WC base tree. Some higher level semantics are inconsistent – for example, some property getter and setter APIs may operate on only user-visible properties, while others also handle hidden properties (e.g. those called "WC props" or "DAV props"). Read and write APIs are not symmetric.
Introduction: The Solution
These trees – a repository revision, the WC base, the WC working tree – have very much in common, as well as significant differences. The idea is to factor out the common parts of the interface while providing an elegant way to deal with their differences. A conceptually simple application-level task such as copying a WC working sub-tree to a shelf will require only setting up the source and destination and calling a generic "read from tree A, write to tree B" routine, and will not require a deep implementation of a new specific variant of that routine.
It will be easier to test data transfer to and from subsystems, using round-trip testing. For example, it will be easy to write a test that performs "export" followed by "import" and checks for no change, and run it against every subsystem that can store versioned data.
There is an emphasis here on aspects of a tree as represented in a Working Copy, which are particularly useful for Shelving-related use cases.
Generic tree traversal interfaces are being prototyped in the "tree-api" branch.
Common Tree Interfaces
These interfaces operate on any tree that Subversion can work with (dirs, files, symlinks, each with properties), as found in a repo or in a WC or even an unversioned tree.
tree node data types
Each of these describes some aspect of a tree.
Tree (node) types used throughout Subversion:
content | dirs, files, symlinks, each with properties | describes the (potentially) versioned content of a tree |
---|---|---|
layout | revisions, URLs (in-repository paths), depths | describes a WC base shape (potentially mixed-rev, switched, sparse) by reference to a repository |
other WC base state | last-changed-*, lock-token, changelist, incomplete, ... | describes the rest of a WC base state |
other WC working/actual state | conflicts, missing/obstructed, copy-from/move, ... | describes the rest of a WC working/actual state |
tree traversal interfaces
Generic references to trees and tree nodes, and tree walking facilities.
The smallest tree that can be described is (a single non-existent node? a single file or symlink or directory?).
walk | visit each node |
---|---|
walk-dirs | visit each directory |
walk-two | walk two trees in parallel |
tree data interfaces
state | could be implemented as delta against empty state | |
---|---|---|
delta | assuming state is X, change state to Y (else undefined) | a unidirectional non-context difference; not suitable for merging, on its own |
derived tree interfaces
pair | access to state X, state Y, delta(X:Y), delta(Y:X) | gives producer the flexibility to store one state and one delta, or two states, deriving the rest on demand, without the consumer caring |
---|---|---|
proto-rev | base-layout + pair(base-content, content-delta) + rev-props | base-content can be dereferenced |
WC APIs
WC Content APIs
get content state (base) | n/a (closest: export.c) | |
get content state (revert-base) | top working layer; used in friendly diff | n/a |
get content state (actual) | n/a (closest: export.c) | |
get content delta (base : actual) | (closest: svn_wc__diff7() -> diff_processor_t) | |
get content delta (revert-base : actual) | used in friendly diff | (closest: svn_wc__diff7() + svn_diff__tree_processor_copy_as_changed_create()) |
apply content delta (base) | what should happen to working/actual state? maybe in present WC design this is tied to merge-into-actual | |
apply content delta (actual) | (closest: see diff_processor in merge.c?) |
WC Layout APIs
get layout state | used in "info", "viewspec export", ... | presently: svn_wc_crawl_revisions5() -> svn_ra_reporter3_t |
apply layout delta | used in "--set-depth", updating (server-exclude), "viewspec apply", ... | n/a |
Shelving
shelve:
if we're going to store the base content as well:
- wc.get-base-layout() -> shelf.store-base-layout()
- wc.get-content-pair-delta(base, actual) -> shelf.store-content-pair()
if we're going to store a base description but not its content:
- wc.get-base-layout() -> maybe add Merkle hashes or something? -> shelf.store-base-layout()
- wc.get-content-delta() -> shelf.store-content-delta()
unshelve:
unshelve, requiring the WC base layout matches the shelf's base layout:
- check wc base layout matches (abort, warn, require force if not)
- compare(shelf.get-base-layout(), wc.get-base-layout())
- apply changes
- shelf.get-content-delta() -> wc(actual).apply-content-delta()
unshelve, changing the WC base layout to match the shelf's base layout:
- set wc base layout
- shelf.read-base-layout() -> wc.set-base-layout()
- apply changes
- shelf.read-content-delta() -> wc(actual).apply-content-delta()
unshelve, requiring the WC content matches the shelf's base content:
- either: check the WC actual content matches the shelf base content (perhaps using a Merkle tree);
- or (not covering every case) check the WC base matches the shelf base content and no local mods in WC
unshelve, merging with the present WC content:
- merge changes
- shelf.get-content-diff() -> wc(actual).apply-merge()