This is a Request For Comments on the creation of an API for reading a
tree of dirs and files from an arbitrary source via a common API.
RATIONALE
1.
I want to re-write svn_client_diff() so that it can diff any tree
against any other tree, where a tree is any of:
* versioned (rooted at URL_at_REV in a repository)
* WC base (rooted at a local abspath in a WC)
* WC working (rooted at a local abspath in a WC)
* unversioned on disk (rooted at a local abspath)
and potentially other sources.
I envisage two main code paths:
diff_two_trees(tree1, tree2)
Takes two references to trees, and reads directories and files from
tree1 and tree2 as required to find differences and present a unidiff
(or whatever kind of output).
diff_tree_with_delta(tree1, delta)
Takes a reference to a base tree, and an svn_delta_editor_t type
delta based on tree1, and reads dirs and files from tree1 as necessary
to present the delta as a unidiff (or whatever kind of output).
2.
It's the right way to design software. Witness how successful the
pluggable RA system and the delta_editor interfaces have been. (Note
that the need for editor v2 does not mean the editor was a bad idea.)
3.
I want to re-write all our libsvn_client read-from-tree APIs such as
'cat', 'propget', 'export' etc. so that they use a common "pull from a
tree" API, in order to reduce complexity and unintended differences and
bugs in those implementations.
4.
I want other people to be able to write such functions/features easily.
DESIGN
I'm thinking something like this for a start.
/* Present as a tree:
* an unversioned disk tree;
* a WC base tree
* a WC working tree
* a repository tree
*
* The consumer "pulls" parts of the tree and can omit unwanted parts.
* Consumer can pull any subtree "recursively" for efficient streaming.
*/
/**
* A readable tree. This object is used to perform read requests to a
* repository tree or a working-copy (base or working) tree or any other
* readable tree.
*
* @since New in 1.8.
*/
typedef struct svn_client_tree_t svn_client_tree_t;
/* */
typedef svn_io_dirent2_t svn_client_tree_dirent_t;
/* V-table for #svn_client_tree_t.
*
* Paths are relpaths, relative to the tree root.
* Revision numbers and repository ids are #SVN_INVALID_REVNUM and NULL
* for an unversioned node (including a node that is a local add/copy/move
* in a WC working tree).
*/
typedef struct svn_client_tree__vtable_t
{
/* Fetch the node kind of the node at @a relpath.
* (### and other metadata? revnum? props?)
*
* Set @a *kind to the node kind.
*/
svn_error_t *(*get_kind)(svn_client_tree_t *tree,
svn_node_kind_t *kind,
const char *relpath,
apr_pool_t *scratch_pool);
/* Fetch the contents and properties of the file at @a relpath.
*
* If @a stream is non-NULL, set @a *stream to a readable stream yielding
* the contents of the file at @a relpath. (### ? The stream
* handlers for @a stream may not perform any operations on @a tree.)
*
* If @a props is non-NULL, set @a *props to contain the regular
* versioned properties of the file (not 'wcprops', 'entryprops', etc.).
* The hash maps (const char *) names to (#svn_string_t *) values.
*/
svn_error_t *(*get_file)(svn_client_tree_t *tree,
svn_stream_t **stream,
apr_hash_t **props,
const char *relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Fetch the entries and properties of the directory at @a relpath.
*
* If @a dirents is non-NULL, set @a *dirents to contain all the entries
* of directory @a relpath. The keys will be (<tt>const char *</tt>)
* entry names, and the values (#svn_client_tree_dirent_t *) dirents.
* Only the @c kind and @c filesize fields are filled in.
* ### @c special would be useful too.
*
* If @a props is non-NULL, set @a *props to contain the regular
* versioned properties of the file (not 'wcprops', 'entryprops', etc.).
* The hash maps (const char *) names to (#svn_string_t *) values.
*/
svn_error_t *(*get_dir)(svn_client_tree_t *tree,
apr_hash_t **dirents,
apr_hash_t **props,
const char *relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Push a sub-tree into an editor, as a delta against an empty tree.
* This is useful for efficiency when streaming a (sub-)tree from a
* remote source. */
svn_error_t *(*push_as_delta_edit)(svn_client_tree_t *tree,
const char *relpath,
svn_delta_editor_t *editor,
void *edit_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
} svn_client_tree__vtable_t;
/* */
struct svn_client_tree_t
{
const svn_client_tree__vtable_t *vtable;
/* Pool used to manage this session. */
apr_pool_t *pool;
/* Private data for the tree implementation. */
void *priv;
};
It will no doubt need a bit more sophistication, which I'll discover
when I try to implement and use it.
Thoughts and comments so far? Any objection to me starting such a thing
in trunk if it sounds like a good idea?
- Julian
Received on 2011-10-07 15:57:19 CEST