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@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