Property Inheritance

Background

What's This?

Some ideas on how we might make versioned properties inheritable.

What Is Property Inheritance?

Property inheritance provides a mechanism to find the versioned properties set on the path-wise parents of a given path. Conversely it can be viewed as a mechanism by which a versioned property set on a given path also applies to that path's children.

What's Driving This?

Desire for some form of inherited properties has existed almost from the dawn of Subversion. This latest effort is in response to a recent proposal regarding Server Dictated Configuration (see http://svn.haxx.se/dev/archive-2012-01/0032.shtml). That proposal made use of a new mechanism by which server dictated configurations (notably auto-props and global-ignores) would be communicated from the server to the client. In the feedback on the proposal several people pointed out that versioned properties provide a possible alternative solution (and that the TortoiseSVN project was already using pseudo-inheritable properties to address some of the same problems -- see http://tortoisesvn.net/docs/nightly/TortoiseSVN_en/tsvn-dug-propertypage.html). Despite its origins in the server defined configuration work, this wiki describes only a generic inheritable properties design (though my the ultimate goal is to use these inheritable properties to implement the server dictated configuration feature -- pburba).

What This Design Is and What It Isn't

This design provides the bare minimum to support the basics of inherited properties:

That's it, it's really just a small extension of our existing versioned property capabilities. Since any versioned property can be inherited, there is no concept of setting "inheritable" properties. Our existing APIs are sufficient. If your own personal vision of inherited properties includes something outside of these two bullets (e.g. merging inherited properties), then that is outside of this design's scope (at least initially).

How Is This Design Different?

It's not really. A lot of the ideas here come from the dev list, #svn-dev, and conversations with other devs. The difference, if there is any, is that this design aims to be as simple as possible and steer clear of complications which have thwarted inheritable properties in the past. If there is more than one reasonable behavior to choose from, it usually goes with the one that is simpler to explain/understand/implement/maintain. This means that not everyone will be happy, it may not cover every use-case, but I hope it covers most of them -- pburba.

Inheritable Properties Today

Subversion has supported merge-tracking using the svn:mergeinfo property since the 1.5 release. The svn:mergeinfo property is inheritable in some merge-sensitive contexts, notably 'svn merge', 'svn log -g', and 'svn mergeinfo'. For example, say we have this simple branch structure at r700 in our repository:

projX/
projX/branches/
projX/branches/src/
projX/branches/src/foo.c
projX/branches/inc
projX/branches/inc/bar.h

And suppose the mergeinfo 'projX/trunk:509-612' is set on 'projX/branches'. If we perform a merge targeting 'projX/branches/src/foo.c', the merge logic considers the svn:mergeinfo property on this target path to effectively be 'projX/trunk/src/foo.c:509-612'. However, unlike a true inheritable property however, svn:mergeinfo is not inheritable outside of merge-tracking aware" contexts. For example, the propget and proplist subcommands recognize the svn:mergeinfo when it is explicitly set on a path, but does not "see" it on those paths' descendants:

>svn pg svn:mergeinfo -v projX/branches Properties on 'projX/branches':
  svn:mergeinfo
    /projX/trunk:509-612

>svn pg svn:mergeinfo -v branches/projX/src/foo.c

>

Design

Differentiating 'Inheritable' Vs. 'Normal' Properties

This is easy, there is no difference, there is no such thing as an "inheritable" property. Versioned properties, both Subversion's reserved properties and custom user properties, can be interpreted as inheritable, but otherwise function as they always have in terms of repository storage, setting properties, valid property names and values, etc.. What is proposed here is a new way of looking at the familiar versioned property. The only differentiation that is important as far as the design is this: Is a property value inherited or explicit? A path may have property 'PropName' explicitly set on it or property values may be inherited by a child path from some parent path(s) which also have 'PropName' explicitly set on them.

General Inheritance Rules

A path (we'll refer to this as the 'child' path from here on) inherits a given property from any of the child's path-wise ancestors (the 'parent' paths) on which the given property is explicitly set. This means that a given path can inherit multiple values for a given property from different parents. Further, the child path may also have the given property explicitly set on itself. This obviously differs from svn:mergeinfo, where inheritance only occurs when a child lacks explicit mergeinfo and only the nearest parent's mergeinfo is inherited1. The new APIs will provide a list of parent paths/URLs and the properties inherited from each. It will be up to the caller, user, script, etc., to decide what it wants to do with this information. The goal here is maximum flexibility. It will be possible to implement a mergeinfo-like inheritance scheme or to merge multiple values together, again, whatever the consumer wants to do.

If a child path which inherits a property is copied, the inherited properties are not copied with it. The new destination path inherits properties from its new parents. This means that depending on the copy destination the new path may or may not inherit the same property, and even if it does, it may inherit a different value. This puts the onus on users to set their inheritable properties as close to the root of the repository tree as makes sense for the property in question (while considering any authorization restrictions that might thwart inheritance). Have a property you want to apply to the whole repository? Set it on the root. Want it to apply only to a single project? Set it on the root of the project. Note that if a path has an inheritable property explicitly set on it, the property is copied just like any other versioned property.

[1] Generic inherited properties also differ from mergeinfo in that the property value a child inherits from a parent is not based on the path-difference between the parent and child. The exact property value on the parent is the value the child will inherit.

Repository Inheritance Rules

Inheritance of properties within the repository is pretty straightforward, for a given property 'PropName':

Working Copy Inheritance Rules

Inherited Properties Cache

A child path that inherits a property from its parent may not have ready access to that parent in the working copy (e.g. the root of the working copy is a subtree of the parent path). To ensure that traditionally disconnected operations (i.e. those that require no access to the repository, like 'svn add') remain disconnected, we will maintain a cache of properties inherited by the root of the working copy. Whenever a new working copy is checked out, any properties inherited by the root of the working copy will be cached in the working copy. If a subtree within a working copy is switched, a separate cache will be created for the root of that subtree. File externals will have their own cache as well. Whenever the WC is updated the cache(s) will be refreshed.

The cache will be stored in a new BLOB column in the NODES table called 'inherited_props'. If the node is the base node of a WC root then this column is populated with a serialized skel of the node's inherited properties. In all other cases it is NULL.

Externals

Directory externals are treated as self-contained working copies with their own inherited properties cache. File externals, despite not being their own WC, are also treated as WC roots and get their own cache. The end result: Property inheritance never crosses external boundaries in the WC.

Authentication

The svn:mergeinfo property can be inherited by any child path the user has read access to, even if the user has no access to the parent path the svn:mergeinfo property is explicitly set on. Generic property inheritance is more restrictive:

For example, given a repository structure like this:

^/Projects
^/Projects/ProjX
^/Projects/ProjX/Docs
^/Projects/ProjX/Docs/Manual

And this authz:

[/Projects]
jrandom=r
[/Projects/ProjX]
jrandom=
[/Projects/ProjX/Docs]
jrandom=rw

If jrandom asks via the appropriate API what properties ^/Projects/ProjX/Docs/Manual inherits, he would see the property:value pairs from ^/Projects/ProjX/Docs and ^/Projects, but not ^/Projects/ProjX.

Yes, this means that an administrator could set up a restrictive authz configuration that could thwart what might be desirable inheritance, e.g. very few users have read access to the repository root, but properties are set on the root that are intended to have repository-wide application. The assumption is that if an administrator sets inheritable properties on the repository root intending that they apply to the entire repos for all users, then he will ensure that all users have read access to the root.

Merging Inherited Properties

This design purposefully avoids any concept of how inherited properties (possibly from multiple parents) might be merged together with explicit properties. In some cases if a path has an inheritable property set on it, then it might make sense to consider that property's value as the complete and full value for that path, end of story (e.g. svn:mergeinfo). On the other hand, it's easy to imagine use-cases where it might be useful to merge a path's explicit and inherited properties. However, both cases depend on the semantics of the particular inherited property. There is no way to develop a one-size-fits-all approach to merging inheritable properties. So while the API changes below support the ability to get a path's explicit and inherited properties, how to merge these values (if at all) will be handled on a case-by-case basis.

Subcommand Changes

Since "inherited" properties are really just a new way of looking at versioned properties, most subcommands will only notice the paths on which explicit properties are set. For example, if we have an unmodified working copy and then make a local change to a parent path's explicit property:

The are a few exceptions to this business-as-usual behavior. A few subcommands require some non-user-visible changes:

Two subcommands, propget (pget, pg) and proplist (plist, pl), will support a new option enabling users to view inherited properties on a path: '--show-inherited-props'. When used, any properties inherited by the target of the subcommand will be displayed. Properties inherited by subtrees of the target are not shown. For example: Given this repository structure with the explicit properties noted:

/                prop:foo=bar
branch/          prop:foo=bat
branch/src/      prop:foo=bax
branch/src/pop.c
branch/inc
branch/inc/pop.h
trunk/
trunk/src/
trunk/src/pop.c
trunk/inc
trunk/inc/pop.h

Without the --show-inherited-props option only explicit properties are shown (as has always been the case):

>svn pl -vR ^/branch
Properties on '%ROOT_URL%/branch':
  prop:foo
    bat
Properties on '%ROOT_URL%/branch/src':
  prop:foo
    bax

>svn pg -R prop:foo ^/
%ROOT_URL% - bar
%ROOT_URL%/branch - bat
%ROOT_URL%/branch/src - bax

>svn pg -v prop:foo ^/branch
Properties on '%ROOT_URL%/branch':
  prop:foo
    bat

>svn pg prop:foo ^/trunk

>

With the --show-inherited-props option, explicit properties are shown as normal, but any properties inherited by the target are also shown. For example, a recursive propget on ^/branch shows the explicit properties on the target and its children, but also shows that the target inherits prop:foo from the root of the repository:

>svn pl -R --show-inherited-props ^/branch
Properties inherited from '%ROOT_URL%':
  prop:foo
Properties on '%ROOT_URL%/branch':
  prop:foo
Properties on '%ROOT_URL%/branch/src':
  prop:foo

A target with no explicit properties might still inherit a property from a parent:

>svn pg -v --show-inherited-props svn:inheritable:foo ^/trunk
Properties inherited from '%ROOT_URL%':
  prop:foo
    bar

If a path inherits a property from multiple parents, all the parents are shown:

>svn pg -v --show-inherited-props prop:foo ^/branch/src/pop.c
Properties inherited from '%ROOT_URL%':
  prop:foo
    bar
Properties inherited from '%ROOT_URL%/branch':
  prop:foo
    bat
Properties inherited from '%ROOT_URL%/branch/src':
  prop:foo
    bax

If the target is a WC path and some of the inherited properties come from the repository, those from the repository are listed by URL rather than WC path:

>svn co %ROOT_URL%/branch branch-WC
A    branch-WC\src
A    branch-WC\src\pop.c
A    branch-WC\inc
A    branch-WC\inc\pop.h
 U   branch-WC
Checked out revision 11.

>cd branch-WC

>svn pg -vR prop:foo
Properties inherited from '%ROOT_URL%':
  prop:foo
    bar
Properties on '.':
  prop:foo
    bat
Properties on 'src':
  prop:foo
    bax

The above examples apply only to the general case. Subversion reserved properties that we deem inheritable may exhibit differing behavior, depending on how we define inheritance merging for the property in question. For example, might show either the explicit mergeinfo on or the mergeinfo existing on nearest path-wise parent with explicit mergeinfo (or possible nothing if there was no explicit mergeinfo on the target or its parents).

We may want to support different values for --show-inherited-props, possibly:

--show-inherited-props=[ 'all' (default) | 'nearest' ]

'all' - Shows the target's explicit properties and all path-wise parents with inheritable properties.

'nearest' - Shows the target's explicit properties and the nearest path-wise parent for any properties which are *not* explicitly set on the target. This would be useful for seeing inheritable properties which follow a strict override model like svn:mergeinfo.

API Changes

Rev svn_proplist_receiver_t to svn_proplist_receiver2_t, adding the argument apr_array_header_t *inherited_props. Also allow the possibility that the existing apr_hash_t *prop_hash argument may be null:

   1 /**
   2  * The callback invoked by svn_client_proplist4().  Each invocation
   3  * provides the regular and/or inherited properties of @a path, which is
   4  * either a working copy path or a URL.  If @a prop_hash is not @c NULL, then
   5  * it maps explicit <tt>const char *</tt> property names to
   6  * <tt>svn_string_t *</tt> explicit property values.  If @a inherited_props
   7  * is not @c NULL, then it is a depth-first ordered array of
   8  * #svn_prop_inherited_item_t * structures representing the
   9  * properties inherited by @a path.  Use @a scratch_pool for all temporary
  10  * allocations.
  11  *
  12  * @since New in 1.8.
  13  */
  14 typedef svn_error_t *(*svn_proplist_receiver2_t)(
  15   void *baton,
  16   const char *path,
  17   apr_hash_t *prop_hash,
  18   apr_array_header_t *inherited_props,
  19   apr_pool_t *scratch_pool);

  • [JAF] What do the keys of 'inherited_prop_hash' represent if 'path' is a working copy path but some of the inherited properties are set on repository paths that are above the WC root?

  • [PTB] JAF - It's a bit awkward, but I was thinking WC relative paths for props inherited from WC parents and URLs for props inherited from the repository (via the inherited prop cache). We obviously can't use WC keys in all cases, but using URLs in all cases won't work either because a WC parent might not exist in the repository due to local changes.

  • [PTB] Instead of using a hash mapping parent "paths" to a hash of properties, it probably makes more sense to use an array of structures which contain the path/URL and the property hash as members. The array would be sorted by the depth of the path/URL and would allow the caller to easily determine the nearest parent of PATH if that is all that it needs (think of a property with a svn:mergeinfo-like straight override model of inheritance where all that ever matters is the nearest parent). Done - change noted above.

Rev svn_client_proplist3 to svn_client_proplist4, adding the argument svn_boolean_t get_target_inherited_props as well as a scratch pool and use the new svn_proplist_receiver2_t callback:

   1 /**
   2  * Invoke @a receiver with @a receiver_baton to return the regular explicit, and
   3  * possibly the inherited, properties of @a target, a URL or working copy path.
   4  * @a receiver will be called for each path encountered.
   5  *
   6  * @a target is a WC path or a URL.
   7  *
   8  * If @a revision->kind is #svn_opt_revision_unspecified, then get the
   9  * explicit (and possibly the inherited) properties from the working copy,
  10  * if @a target is a working copy path, or from the repository head if
  11  * @a target is a URL.  Else get the properties as of @a revision.
  12  * The actual node revision selected is determined by the path as it exists
  13  * in @a peg_revision.  If @a peg_revision->kind is
  14  * #svn_opt_revision_unspecified, then it defaults to #svn_opt_revision_head
  15  * for URLs or #svn_opt_revision_working for WC targets.  Use the
  16  * authentication baton cached in @a ctx for authentication if contacting
  17  * the repository.
  18  *
  19  * If @a depth is #svn_depth_empty, list only the properties of
  20  * @a target itself.  If @a depth is #svn_depth_files, and
  21  * @a target is a directory, list the properties of @a target
  22  * and its file entries.  If #svn_depth_immediates, list the properties
  23  * of its immediate file and directory entries.  If #svn_depth_infinity,
  24  * list the properties of its file entries and recurse (with
  25  * #svn_depth_infinity) on directory entries.  #svn_depth_unknown is
  26  * equivalent to #svn_depth_empty.  All other values produce undefined
  27  * results.
  28  *
  29  * @a changelists is an array of <tt>const char *</tt> changelist
  30  * names, used as a restrictive filter on items whose properties are
  31  * listed; that is, don't list properties on any item unless it's a member
  32  * of one of those changelists.  If @a changelists is empty (or
  33  * altogether @c NULL), no changelist filtering occurs.
  34  *
  35  * If @a get_target_inherited_props is true, then also return any inherited
  36  * properties when @a receiver is called for @a target.  If @a target is a
  37  * working copy path, then properties inherited by @a target as far as the
  38  * root of the working copy are obtained from the working copy's actual
  39  * property values.  Properties inherited from above the working copy
  40  * root come from the inherited properties cache.  If @a target is a URL,
  41  * then the inherited properties come from the repository.
  42  * If @a get_target_inherited_props is false, then no inherited properties
  43  * are returned to @a receiver.
  44  *
  45  * If @a target is not found, return the error #SVN_ERR_ENTRY_NOT_FOUND.
  46  *
  47  * @since New in 1.8.
  48  */
  49 svn_error_t *
  50 svn_client_proplist4(const char *target,
  51                      const svn_opt_revision_t *peg_revision,
  52                      const svn_opt_revision_t *revision,
  53                      svn_depth_t depth,
  54                      const apr_array_header_t *changelists,
  55                      svn_boolean_t get_target_inherited_props,
  56                      svn_proplist_receiver2_t receiver,
  57                      void *receiver_baton,
  58                      svn_client_ctx_t *ctx,
  59                      apr_pool_t *result_pool,
  60                      apr_pool_t *scratch_pool);

Rev svn_client_propget4 to svn_client_propget5, adding the argument apr_array_header_t **inherited_props:

   1 /**
   2  * Set @a *props to a hash table whose keys are absolute paths or URLs
   3  * of items on which property @a propname is explicitly set, and whose
   4  * values are <tt>svn_string_t *</tt> representing the property value for
   5  * @a propname at that path.
   6  *
   7  * If @a inherited_props is not @c NULL, then set @a *inherited_props to a
   8  * depth-first ordered array of #svn_prop_inherited_item_t * structures
   9  * representing the properties inherited by @a target.  If @a target is a
  10  * working copy path, then properties inherited by @a target as far as the
  11  * root of the working copy are obtained from the working copy's actual
  12  * property values.  Properties inherited from above the working copy root
  13  * come from the inherited properties cache.  If @a target is a URL, then
  14  * the inherited properties come from the repository.  If @a inherited_props
  15  * is not @c NULL and no inheritable properties are found, then set
  16  * @a *inherited_props to an empty array.
  17  *
  18  * Allocate @a *props, its keys, and its values in @a pool, use
  19  * @a scratch_pool for temporary allocations.
  20  *
  21  * @a target is a WC absolute path or a URL.
  22  *
  23  * Don't store any path, not even @a target, if it does not have a
  24  * property named @a propname.
  25  *
  26  * If @a revision->kind is #svn_opt_revision_unspecified, then: get
  27  * properties from the working copy if @a target is a working copy
  28  * path, or from the repository head if @a target is a URL.  Else get
  29  * the properties as of @a revision.  The actual node revision
  30  * selected is determined by the path as it exists in @a peg_revision.
  31  * If @a peg_revision->kind is #svn_opt_revision_unspecified, then
  32  * it defaults to #svn_opt_revision_head for URLs or
  33  * #svn_opt_revision_working for WC targets.  Use the authentication
  34  * baton in @a ctx for authentication if contacting the repository.
  35  * If @a actual_revnum is not @c NULL, the actual revision number used
  36  * for the fetch is stored in @a *actual_revnum.
  37  *
  38  * If @a depth is #svn_depth_empty, fetch the property from
  39  * @a target only; if #svn_depth_files, fetch from @a target and its
  40  * file children (if any); if #svn_depth_immediates, from @a target
  41  * and all of its immediate children (both files and directories); if
  42  * #svn_depth_infinity, from @a target and everything beneath it.
  43  *
  44  * @a changelists is an array of <tt>const char *</tt> changelist
  45  * names, used as a restrictive filter on items whose properties are
  46  * gotten; that is, don't get @a propname on any item unless it's a member
  47  * of one of those changelists.  If @a changelists is empty (or
  48  * altogether @c NULL), no changelist filtering occurs.
  49  *
  50  * If error, don't touch @a *props, otherwise @a *props is a hash table
  51  * even if empty.
  52  *
  53  * This function returns SVN_ERR_UNVERSIONED_RESOURCE when it is called on
  54  * unversioned nodes.
  55  *
  56  * @since New in 1.8.
  57  */
  58 svn_error_t *
  59 svn_client_propget5(apr_hash_t **props,
  60                     apr_array_header_t **inherited_props,
  61                     const char *propname,
  62                     const char *target,  /* abspath or URL */
  63                     const svn_opt_revision_t *peg_revision,
  64                     const svn_opt_revision_t *revision,
  65                     svn_revnum_t *actual_revnum,
  66                     svn_depth_t depth,
  67                     const apr_array_header_t *changelists,
  68                     svn_client_ctx_t *ctx,
  69                     apr_pool_t *result_pool,
  70                     apr_pool_t *scratch_pool);

Rev svn_ra_get_dir2 to svn_ra_get_dir3, adding the argument apr_array_header_t **inherited_props. Also allow the possibility that the existing apr_hash_t **props argument may be null:

   1 /**
   2  * If @a dirents is non @c NULL, set @a *dirents to contain all the entries
   3  * of directory @a path at @a revision.  The keys of @a dirents will be
   4  * entry names (<tt>const char *</tt>), and the values dirents
   5  * (<tt>@c svn_dirent_t *</tt>).  Use @a pool for all allocations.
   6  *
   7  * @a dirent_fields controls which portions of the <tt>@c svn_dirent_t</tt>
   8  * objects are filled in.  To have them completely filled in just pass
   9  * @c SVN_DIRENT_ALL, otherwise pass the bitwise OR of all the @c SVN_DIRENT_
  10  * fields you would like to have returned to you.
  11  *
  12  * @a path is interpreted relative to the URL in @a session.
  13  *
  14  * If @a revision is @c SVN_INVALID_REVNUM (meaning 'head') and
  15  * @a *fetched_rev is not @c NULL, then this function will set
  16  * @a *fetched_rev to the actual revision that was retrieved.  (Some
  17  * callers want to know, and some don't.)
  18  *
  19  * If @a props is non @c NULL, set @a *props to contain the properties of
  20  * the directory.  This means @em all properties: not just ones controlled by
  21  * the user and stored in the repository fs, but non-tweakable ones
  22  * generated by the SCM system itself (e.g. 'wcprops', 'entryprops',
  23  * etc.)  The keys are <tt>const char *</tt>, values are
  24  * <tt>@c svn_string_t *</tt>.
  25  *
  26  * If the server advertises the #SVN_RA_CAPABILITY_INHERITED_PROPS capability
  27  * and if @a inherited_props is not @c NULL, then set @a *inherited_props to
  28  * a depth-first ordered array of #svn_prop_inherited_item_t * structures
  29  * representing the properties inherited by @a path at @a revision (or the
  30  * 'head' revision if @a revision is @c SVN_INVALID_REVNUM.  If
  31  * @a inherited_props is not @c NULL and no inheritable properties are found,
  32  * then set @a *inherited_props to an empty array.  If the server does not
  33  * advertise the #SVN_RA_CAPABILITY_INHERITED_PROPS capability and
  34  * @a inherited_props is not @c NULL, then set @a *inherited_props to
  35  * @c NULL.
  36  *
  37  * @since New in 1.8.
  38  */
  39 svn_error_t *
  40 svn_ra_get_dir3(svn_ra_session_t *session,
  41                 apr_hash_t **dirents,
  42                 svn_revnum_t *fetched_rev,
  43                 apr_hash_t **props,
  44                 apr_array_header_t **inherited_props,
  45                 const char *path,
  46                 svn_revnum_t revision,
  47                 apr_uint32_t dirent_fields,
  48                 apr_pool_t *result_pool,
  49                 apr_pool_t *scratch_pool);

Rev svn_ra_get_file to svn_ra_get_file2, adding the argument apr_array_header_t **inherited_props. Also allow the possibility that the existing apr_hash_t **props argument may be null:

   1 /**
   2  * Fetch the contents and properties of file @a path at @a revision.
   3  * @a revision may be SVN_INVALID_REVNUM, indicating that the HEAD
   4  * revision should be used.  Interpret @a path relative to the URL in
   5  * @a session.  Use @a pool for all allocations.
   6  *
   7  * If @a revision is @c SVN_INVALID_REVNUM and @a fetched_rev is not
   8  * @c NULL, then set @a *fetched_rev to the actual revision that was
   9  * retrieved.
  10  *
  11  * If @a stream is non @c NULL, push the contents of the file at @a
  12  * stream, do not call svn_stream_close() when finished.
  13  *
  14  * If @a props is non @c NULL, set @a *props to contain the properties of
  15  * the file.  This means @em all properties: not just ones controlled by
  16  * the user and stored in the repository fs, but non-tweakable ones
  17  * generated by the SCM system itself (e.g. 'wcprops', 'entryprops',
  18  * etc.)  The keys are <tt>const char *</tt>, values are
  19  * <tt>@c svn_string_t *</tt>.
  20  *
  21  * If the server advertises the #SVN_RA_CAPABILITY_INHERITED_PROPS capability
  22  * and if @a inherited_props is not @c NULL, then set @a *inherited_props to
  23  * a depth-first ordered array of #svn_prop_inherited_item_t * structures
  24  * representing the properties inherited by @a path at @a revision (or the
  25  * 'head' revision if @a revision is @c SVN_INVALID_REVNUM.  If
  26  * @a inherited_props is not @c NULL and no inheritable properties are found,
  27  * then set @a *inherited_props to an empty array.  If the server does not
  28  * advertise the #SVN_RA_CAPABILITY_INHERITED_PROPS capability and
  29  * @a inherited_props is not @c NULL, then set @a *inherited_props to
  30  * @c NULL.
  31  *
  32  * The stream handlers for @a stream may not perform any RA
  33  * operations using @a session.
  34  *
  35  * @since New in 1.8.
  36  */
  37 svn_error_t *
  38 svn_ra_get_file2(svn_ra_session_t *session,
  39                  const char *path,
  40                  svn_revnum_t revision,
  41                  svn_stream_t *stream,
  42                  svn_revnum_t *fetched_rev,
  43                  apr_hash_t **props,
  44                  apr_array_header_t **inherited_props,
  45                  apr_pool_t *result_pool,
  46                  apr_pool_t *scratch_pool);

Rev svn_fs_node_proplist to svn_fs_node_proplist2, adding the argument apr_hash_t **inherited_table_p. Also allow the possibility that the existing apr_hash_t **table_p argument may be null:

   1 /** If @a table_p is not null, then set @a *table_p to the entire property
   2  * list of @a path in @a root.  The resulting table maps
   3  * property names to pointers to #svn_string_t objects containing the
   4  * property value.
   5  *
   6  * If @a inherited_values is not @c NULL, then set @a *inherited_values to
   7  * a depth-first ordered array of #svn_prop_inherited_item_t * structures
   8  * (the path_or_url members of which are relative filesystem paths)
   9  * representing the properties inherited by @a path.  If @a inherited_values
  10  * is not @c NULL and no properties are inherited, then set
  11  * @a *inherited_values to an empty array.
  12  *
  13  * @since New in 1.8.
  14  */
  15 svn_error_t *
  16 svn_fs_node_proplist2(apr_hash_t **table_p,
  17                       apr_array_header_t **inherited_props,
  18                       svn_fs_root_t *root,
  19                       const char *path,
  20                       apr_pool_t *result_pool,
  21                       apr_pool_t *scratch_pool);

New APIs and Public Structs

   1 /** A structure to represent inherited properties.
   2  *
   3  * @since New in 1.8.
   4  */
   5 typedef struct svn_prop_inherited_item_t
   6 {
   7   /** The absolute working copy path, relative filesystem path, or URL from
   8    * which the properties in @a prop_hash are inherited. */
   9   const char *path_or_url;
  10 
  11   /** A hash of (const char *) inherited property names, and (svn_string_t *)
  12    * property values. */
  13   apr_hash_t *prop_hash;
  14 
  15 } svn_prop_inherited_item_t;

   1 /**
   2  * Set @a *inherited_props to a depth-first ordered array of
   3  * #svn_prop_inherited_item_t * structures representing the properties
   4  * inherited by @a path at @a revision (or the 'head' revision if
   5  * @a revision is @c SVN_INVALID_REVNUM).  Interpret @a path relative to
   6  * the URL in @a session.  Use @a pool for all allocations.  If no
   7  * inheritable properties are found, then set @a *inherited_props to
   8  * an empty array.
   9  *
  10  * Allocated @a *inherited_props in @a result_pool, use @a scratch_pool
  11  * for temporary allocations.
  12  *
  13  * @since New in 1.8.
  14  */
  15 svn_error_t *
  16 svn_ra_get_inherited_props(svn_ra_session_t *session,
  17                            apr_array_header_t **inherited_props,
  18                            const char *path,
  19                            svn_revnum_t revision,
  20                            apr_pool_t *result_pool,
  21                            apr_pool_t *scratch_pool);

   1  * Return a deep copy of @a inherited_prop, allocated in @a result_pool.
   2  * Use @a scratch_pool for temporary allocations.
   3  * @since New in 1.8.
   4  */
   5 svn_prop_inherited_item_t *
   6 svn_prop_inherited_item_dup(const svn_prop_inherited_item_t *inherited_prop,
   7                             apr_pool_t *result_pool,
   8                             apr_pool_t *scratch_pool);

   1 /**
   2  * Return a deep copy the array @a prop_array of svn_prop_inherited_item_t *
   3  * items in @a result_pool.  Use @a scratch_pool for temporary allocations.
   4  *
   5  * @since New in 1.8.
   6  */
   7 apr_array_header_t *
   8 svn_prop_inherited_array_dup(const apr_array_header_t *prop_array,
   9                              apr_pool_t *result_pool,
  10                              apr_pool_t *scratch_pool)

   1 /**
   2  * Set @a *inherited_values to a depth-first ordered array of
   3  * #svn_prop_inherited_item_t * structures (the path_or_url members of which
   4  * are relative filesystem paths)  representing the properties inherited by
   5  * @a path at @a revision in @a repos.  If no properties are inherited, then
   6  * set @a *inherited_values to an empty array.
   7  *
   8  * If @a revision is #SVN_INVALID_REVNUM, it defaults to youngest.
   9  *
  10  * If optional @a authz_read_func is non-NULL, then use this function
  11  * (along with optional @a authz_read_baton) to check the readability
  12  * of each parent path from which properties are inherited. Silently omit
  13  * properties for unreadable parent paths.
  14  *
  15  * Allocate @a *inherited_props in @a result_pool.  Use @a scratch_pool for
  16  * temporary allocations.
  17  *
  18  * @since New in 1.8.
  19  */
  20 svn_error_t *
  21 svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props,
  22                                  svn_repos_t *repos,
  23                                  const char *path,
  24                                  svn_revnum_t revision,
  25                                  svn_repos_authz_func_t authz_read_func,
  26                                  void *authz_read_baton,
  27                                  apr_pool_t *result_pool,
  28                                  apr_pool_t *scratch_pool);

New Capabilities

   1 /**
   2  * The capability to get inherited properties.
   3  *
   4  * @since New in 1.8.
   5  */
   6 #define SVN_RA_CAPABILITY_INHERITED_PROPS "inherited-props"
   7 

Drawbacks

The suggested design above has some known drawbacks and trade-offs:

  1. Generic inherited properties also differ from mergeinfo in that the property value a child inherits from a parent is not based on the path-difference between the parent and child. The exact property value on the parent is the value the child will inherit. (1)

  2. See args to svn_client_proplist4 and svn_client_propget5 in the new APIs section. These are the primary APIs for getting inherited props from the WC. (2)

  3. svn_opt_revision_unspecified or svn_opt_revision_working. (3)

  4. The working tree plus property and text changes. (4)

InheritedProperties (last edited 2012-09-14 19:34:25 by JulianFoad)