You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 23 Next »

INHERITED PROPERTIES : BACKGROUND

What's This?

Some ideas on how we might implement simple inherited properties.

What Are Inherited Properties?

Inherited properties are versioned properties that apply not only to the path the property is explicitly set on, but also to all of that path's path-wise descendants (at the same revision) which do not explicitly have the same property set.

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://wiki.apache.org/subversion/ServerDictatedConfiguration and 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 pseduo-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:

  • A way to identify properties that are inheritable vs. those that are not.
  • A way to set inheritable properties (not really anything to do here, we support this today).
  • A way to get a path's inherited properties.

That's it, it's really just a small extension of our existing versioned property capabilities. If your own personal vision of inherited properties includes something outside of these three 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

>

INHERITED PROPERTIES : DESIGN

Differentiating Inheritable Vs. 'Normal' Properties

Inheritable properties will be identified by a prefix on the property name.

  1. Custom user properties will use the "svn:inheritable:" prefix.
  2. Existing Subversion reserved properties will not become inheritable, they will function as they always have. New reserved Subversion properties may be introduced that are inheritable by definition, but such properties are not required to use any special namespace, beyond the normal "svn:" prefix.
  3. Valid property names for both custom user and Subversion reserved inheritable properties are as per any other other versioned property (i.e. text only).
  4. Valid property values for both custom user and Subversion reserved inheritable properties are as per any other Subversion reserved property (i.e. text only and stored internally as UTF-8). This means that binary property values are not supported even in the case of custom user properties.

Inheritance Rules

Inheritance will be more flexible than the current svn:mergeinfo inheritance model. For a given inheritable property 'svn:inheritable:X':

  1. A path may have the svn:inheritable:X' explicitly set on it (i.e. exactly how versioned properties work today). _ _
  2. A repository path@REV (we'll refer to this as the 'child' path from here on) may inherit the value of the svn:inheritable:X property from the path's nearest path-wise ancestor@REV with the 'svn:inheritable:X' property explicitly set on it (we'll refer to this as the 'parent' path).
  3. A working copy child path may inherit the svn:inheritable:X property from the path's nearest path-wise ancestor in the working copy.
    • For working copies with no switched subtrees, this inheritance can occur from any parent path up to the root of the working copy.
    • If the path is located within a switched subtree then the inheritance can occur up to the root of the switched subtree.
    • Unlike svn:mergeinfo and like tsvn:auto-props, inheritance across mixed-revision boundaries in the working copy is allowed.
    • If a working copy child path doesn't find a parent with 'svn:inheritable:X' that it can inherit from before it reaches the working copy (or switched subtree) root, then it may inherit the property from the inherited properties cache (see below).

      [JAF] What is the unifying principle behind the set of rules? For example, a principle could be:

      "Inheritance within a WC is based on inheritance within a single revision of the tree structure. Thus, a WC base node inherits from its repository parent in its repository revision, no matter whether that is present in the WC. The working version of the WC tree is treated as a single revision: an as yet unnumbered prototype for a revision that might be committed into the repository (or might not)[1]. Thus, a WC working node[2] inherits from the parent node that it would have in the repository if the current WC were committed in full."

      I think that principle would match all your rules if the 'mixed-revision' rule applies to working nodes but not base nodes.

      [1] This particular principle for how we treat the working version of a WC is something I've had in mind for a long time and I think it can be very useful in guiding how we design several aspects of WC behaviour, not just for inheritable properties.

      [2] When I say 'working node' I mean the 'topmost' version of the node in the WC, no matter whether that is different from or the same as the base version.

      [PTB] JAF - I was envisioning a much simpler principle: Inheritance within the WC is within the actual tree[1]. This is easy for a user to explain and easy to understand. For example, no matter what the state of the WC a user can always say, "I see that 'my-WC/trunk' has the inheritable property 'InhPropName=PropVal'. That means that 'my-WC/trunk/README' inherits 'InhPropName=PropVal'. That makes sense!" If I understand correctly what you suggest, a user could face the case where a child path inherits a property value that differs from its actual WC parent's value (if it inherits anything at all). That strikes me as a recipe for user confusion.

      [1] By actual tree I mean the topmost (i.e. MAX(op_depth)) version of the node in the WC, pretty much the same thing your 2nd footnote means(question). I thought we differentiated actual vs. working such that the former included property and text changes?

  4. Note an important implication of #2 and #3: Unlike svn:mergeinfo, a child path with the svn:inheritable:X property explicitly set on it can also inherit the svn:inheritable:X property from a parent. Our default approach will be for child values to simply override parent values (exactly how svn:mergeinfo works today). However, the APIs will support getting both explicit and/or inherited property values. So it's possbile that child values might append to parent values or more complex merging of properties may be performed based on the specific property.
  5. Unlike svn:mergeinfo, the property value a child inherits from a parent will not be modified based on the path-difference between the parent and child. The property value on the parent is the value the child will inherit.
  6. While setting inheritable properties on a file has no meaning from the standpoint of inheritance, the property still applies to the file itself. Thus there will be no prohibitions on setting inheritable properties on files.
  7. 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. 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.

### TBD: Externals: Do they work like switched subtrees?

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. Whenever an update occurs the cache(s) will be refreshed.

The cache will be stored in a new wc-ng table:

 

 

TABLE: INHERITABLE_PROPS

 

 

Name

Data Type

Primary Key

Foreign Key

Notes

wc_id

integer

Yes

References NODES.WC_ID

 

local_relpath

text

Yes

References NODES.LOCAL_RELPATH

 

op_depth

integer

Yes

References NODES.OP_DEPTH

 

repos_parent_path

text

Yes

Location of parent with one or more inheritable properties relative the the repository root.

revision

integer

Revision of repos_parent_path.

inheritable_props

blob

The parent's inheritable properties.

Authentication

In exactly the same way that svn:mergeinfo is handled, generic inheritable properties can be inherited by any path the user has read access to, even if the user has no access to the parent path the inheritable property is explicitly set on.

Merging Inherited Properties

It should be clear from reading this far that no concept of 'merging' inherited properties is proposed. If a path has an inheritable property set on it, then that property's value is the complete and full value for that path, end of story. Alternatively, if a path inherits a property, the inherited value is the complete and full value. Explicit and inherited property values will not be combined in any way. This means, for example, that if a child path inherits "svn:inheritable:someprop=SomeVal" from its parent, and you use 'svn propset' to set "svn:inheritable:someprop=NewVal" on the child path, then the latter is the new value for the child. The propset command doesn't care that you overrode the path's inherited property with an explicit one. It's easy to imagine use-cases where it is useful for a path's explicit and inherited properties to be merged, or for the inherited properties of a copy source to be merged with the copy destination's new inherited properties. But these depend on the purpose of the particular inherited properties. There is no way to develop a one-size-fits-all approach to merging inheritable properties, so any support for this needs to be special cased for the particular property in question.

Subcommand Changes

In general inherited properties will be treated like any other versioned property, so most subcommands will only notice the paths on which explicit properties are set, regardless of whether these are inheritable or not. For example, if we have an unmodified working copy and then make a local change to a parent path's explicit inheritable property:

  • 'svn status' won't show any property mods on the parent's children paths.
  • 'svn diff' will only show the property difference on the parent path.

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

  • checkout (co): Populates the inherited properties cache.
  • switch (sw): Creates/clears the inherited properties cache for the root of the switched/unswitched subtree.
  • update (up): Updates the inherited properties cache(s).
  • upgrade: Creates an empty inherited properties cache, but doesn't populate it since that would require contacting the repository which upgrade shouldn't need to do.

Two subcommands, propget (pget, pg) and proplist (plist, pl), will support a new option enabling users to find inherited properties on a path: '--show-inherited-props'. When used, any inherited properties for the target of the subcommand will be displayed. At operational depths greater than empty however, the inherited mergeinfo subtree targets is not shown. For example: Given this repository structure with the explicit properties noted:

/                  svn:inheritable:foo=bar
branches/          svn:inheritable:baz=qux
branches/src/
branches/src/foo.c
branches/inc
branches/inc/bar.h
trunk/
trunk/src/
trunk/src/foo.c
trunk/inc
trunk/inc/bar.h

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

>svn pl -vR ^/branches
Properties on '%ROOT_URL%/branches':
  svn:inheritable:baz
    qux

>svn pg -R svn:inheritable:baz ^/
%ROOT_URL%/branches - qux

>svn pg -v svn:inheritable:baz ^/trunk
Properties on '%ROOT_URL%/branches':
  svn:inheritable:foo
    bar

>svn pg svn:inheritable:baz ^/trunk

>

With the --show-inherited-props option the inherited properties on the target are also shown:

>svn pl -R --show-inherited-props ^/branches
Properties on '%ROOT_URL%/branches':
  svn:inheritable:baz
  svn:inheritable:foo

>svn pg -v --show-inherited-props svn:inheritable:baz ^/trunk
Properties on '%ROOT_URL%/branches':
  svn:inheritable:foo
    bar

API Changes

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

/**
 * The callback invoked by svn_client_proplist4().  Each invocation
 * provides the regular and/or inherited properties of @a path, which is
 * either a working copy path or a URL.  If @a prop_hash is not @c NULL, then
 * it maps explicit <tt>char *</tt> property names to <tt>svn_string_t *</tt>
 * explicit property values.  If @a inherited_prop_hash is not @c NULL,
 * then it maps <tt>const char *</tt> paths (either working copy paths or
 * URLs), which are parents of @a path, to a hash mapping <tt>char *</tt>
 * inherited property names to the <tt>svn_string_t *</tt> explicit property
 * values found on these parents.  Use @a pool for all temporary allocations.
 *
 * @since New in 1.8.
 */
typedef svn_error_t *(*svn_proplist_receiver2_t)(
  void *baton,
  const char *path,
  apr_hash_t *prop_hash,
  apr_hash_t *inherited_prop_hash,
  apr_pool_t *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).

Rev svn_client_proplist3 to svn_client_proplist4, adding the argument svn_boolean_t get_target_inherited_props:

/**
 * Invoke @a receiver with @a receiver_baton to return the regular explicit, and
 * possibly the inherited, properties of @a target, a URL or working copy path.
 * @a receiver will be called for each path encountered.
 *
 * @a target is a WC path or a URL.
 *
 * If @a revision->kind is #svn_opt_revision_unspecified, then get the
 * explicit (and possibly the inherited) properties from the working copy,
 * if @a target is a working copy path, or from the repository head if
 * @a target is a URL.  Else get the properties as of @a revision.
 * The actual node revision selected is determined by the path as it exists
 * in @a peg_revision.  If @a peg_revision->kind is
 * #svn_opt_revision_unspecified, then it defaults to #svn_opt_revision_head
 * for URLs or #svn_opt_revision_working for WC targets.  Use the
 * authentication baton cached in @a ctx for authentication if contacting
 * the repository.
 *
 * If @a depth is #svn_depth_empty, list only the properties of
 * @a target itself.  If @a depth is #svn_depth_files, and
 * @a target is a directory, list the properties of @a target
 * and its file entries.  If #svn_depth_immediates, list the properties
 * of its immediate file and directory entries.  If #svn_depth_infinity,
 * list the properties of its file entries and recurse (with
 * #svn_depth_infinity) on directory entries.  #svn_depth_unknown is
 * equivalent to #svn_depth_empty.  All other values produce undefined
 * results.
 *
 * @a changelists is an array of <tt>const char *</tt> changelist
 * names, used as a restrictive filter on items whose properties are
 * listed; that is, don't list properties on any item unless it's a member
 * of one of those changelists.  If @a changelists is empty (or
 * altogether @c NULL), no changelist filtering occurs.
 *
 * If @a get_target_inherited_props is true, then also return any inherited
 * properties when @a receiver is called for @a target.  If @a target is a
 * working copy path, then properties inherited by @a target as far as the
 * root of the working copy are obtained from the working copy's actual
 * property values.  Properties inherited from above the working copy
 * root come from the inherited properties cache.  If @a target is a URL,
 * then the inherited properties come from the repository.
 * If @a get_target_inherited_props is false, then no inherited properties
 * are returned to @a receiver.
 *
 * If @a target is not found, return the error #SVN_ERR_ENTRY_NOT_FOUND.
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_client_proplist4(const char *target,
                     const svn_opt_revision_t *peg_revision,
                     const svn_opt_revision_t *revision,
                     svn_depth_t depth,
                     const apr_array_header_t *changelists,
                     svn_boolean_t get_target_inherited_props,
                     svn_proplist_receiver2_t receiver,
                     void *receiver_baton,
                     svn_client_ctx_t *ctx,
                     apr_pool_t *pool);

Rev svn_client_propget4 to svn_client_propget5, adding the argument apr_hash_t **inherited_prop_hash:

/**
 * Set @a *props to a hash table whose keys are absolute paths or URLs
 * of items on which property @a propname is explicitly set, and whose
 * values are <tt>svn_string_t *</tt> representing the property value for
 * @a propname at that path.
 *
 * If @a inherited_prop_hash is not @c NULL, then set @a *inherited_prop_hash
 * to a mapping of <tt>const char *</tt> paths (either absolute working copy
 * paths or URLs) which are parents of @a target, to <tt>svn_string_t *</tt>
 * inheritable property values which are set on that parent.  If @a target
 * is a working copy path, then properties inherited by @a target as far as the
 * root of the working copy are obtained from the working copy's actual
 * property values.  Properties inherited from above the working copy
 * root come from the inherited properties cache.  If @a target is a URL,
 * then the inherited properties come from the repository.
 * If @a inherited_prop_hash is not @c NULL and no inheritable properties
 * are found, then set @a *inherited_prop_hash to an empty hash.
 *
 * Allocate @a *props, its keys, and its values in @a pool, use
 * @a scratch_pool for temporary allocations.
 *
 * @a target is a WC absolute path or a URL.
 *
 * Don't store any path, not even @a target, if it does not have a
 * property named @a propname.
 *
 * If @a revision->kind is #svn_opt_revision_unspecified, then: get
 * properties from the working copy if @a target is a working copy
 * path, or from the repository head if @a target is a URL.  Else get
 * the properties as of @a revision.  The actual node revision
 * selected is determined by the path as it exists in @a peg_revision.
 * If @a peg_revision->kind is #svn_opt_revision_unspecified, then
 * it defaults to #svn_opt_revision_head for URLs or
 * #svn_opt_revision_working for WC targets.  Use the authentication
 * baton in @a ctx for authentication if contacting the repository.
 * If @a actual_revnum is not @c NULL, the actual revision number used
 * for the fetch is stored in @a *actual_revnum.
 *
 * If @a depth is #svn_depth_empty, fetch the property from
 * @a target only; if #svn_depth_files, fetch from @a target and its
 * file children (if any); if #svn_depth_immediates, from @a target
 * and all of its immediate children (both files and directories); if
 * #svn_depth_infinity, from @a target and everything beneath it.
 *
 * @a changelists is an array of <tt>const char *</tt> changelist
 * names, used as a restrictive filter on items whose properties are
 * gotten; that is, don't get @a propname on any item unless it's a member
 * of one of those changelists.  If @a changelists is empty (or
 * altogether @c NULL), no changelist filtering occurs.
 *
 * If error, don't touch @a *props, otherwise @a *props is a hash table
 * even if empty.
 *
 * This function returns SVN_ERR_UNVERSIONED_RESOURCE when it is called on
 * unversioned nodes.
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_client_propget5(apr_hash_t **props,
                    apr_hash_t **inherited_prop_hash,
                    const char *propname,
                    const char *target,  /* abspath or URL */
                    const svn_opt_revision_t *peg_revision,
                    const svn_opt_revision_t *revision,
                    svn_revnum_t *actual_revnum,
                    svn_depth_t depth,
                    const apr_array_header_t *changelists,
                    svn_client_ctx_t *ctx,
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool);

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

/**
 * If @a dirents is non @c NULL, set @a *dirents to contain all the entries
 * of directory @a path at @a revision.  The keys of @a dirents will be
 * entry names (<tt>const char *</tt>), and the values dirents
 * (<tt>@c svn_dirent_t *</tt>).  Use @a pool for all allocations.
 *
 * @a dirent_fields controls which portions of the <tt>@c svn_dirent_t</tt>
 * objects are filled in.  To have them completely filled in just pass
 * @c SVN_DIRENT_ALL, otherwise pass the bitwise OR of all the @c SVN_DIRENT_
 * fields you would like to have returned to you.
 *
 * @a path is interpreted relative to the URL in @a session.
 *
 * If @a revision is @c SVN_INVALID_REVNUM (meaning 'head') and
 * @a *fetched_rev is not @c NULL, then this function will set
 * @a *fetched_rev to the actual revision that was retrieved.  (Some
 * callers want to know, and some don't.)
 *
 * If @a props is non @c NULL, set @a *props to contain the properties of
 * the directory.  This means @em all properties: not just ones controlled by
 * the user and stored in the repository fs, but non-tweakable ones
 * generated by the SCM system itself (e.g. 'wcprops', 'entryprops',
 * etc.)  The keys are <tt>const char *</tt>, values are
 * <tt>@c svn_string_t *</tt>.
 *
 * If @a inherited_props is not @c NULL, then set @a *inherited_props
 * to a mapping of <tt>const char *</tt> URLS which are parents of @a path,
 * to a hash mapping <tt>const char *</tt> property names to
 * <tt>svn_string_t *</tt> property values for each inheritable property set
 * on the parent URL.  If @a inherited_props is not @c NULL no inheritable
 * properties are found, then set @a *inherited_props to an empty hash.
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_ra_get_dir3(svn_ra_session_t *session,
                apr_hash_t **dirents,
                svn_revnum_t *fetched_rev,
                apr_hash_t **props,
                apr_hash_t **inherited_props,
                const char *path,
                svn_revnum_t revision,
                apr_uint32_t dirent_fields,
                apr_pool_t *pool);

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

/** Fetch the contents and properties of file @a path at @a revision.
 * @a revision may be SVN_INVALID_REVNUM, indicating that the HEAD
 * revision should be used.  Interpret @a path relative to the URL in
 * @a session.  Use @a pool for all allocations.
 *
 * If @a revision is @c SVN_INVALID_REVNUM and @a fetched_rev is not
 * @c NULL, then set @a *fetched_rev to the actual revision that was
 * retrieved.
 *
 * If @a stream is non @c NULL, push the contents of the file at @a
 * stream, do not call svn_stream_close() when finished.
 *
 * If @a props is non @c NULL, set @a *props to contain the properties of
 * the file.  This means @em all properties: not just ones controlled by
 * the user and stored in the repository fs, but non-tweakable ones
 * generated by the SCM system itself (e.g. 'wcprops', 'entryprops',
 * etc.)  The keys are <tt>const char *</tt>, values are
 * <tt>@c svn_string_t *</tt>.
 *
 * If @a inherited_props is non @c NULL, set @a *inherited_props to contain
 * any properties inherited by the directory @a path.  The keys are
 * <tt>const char *</tt>, values are <tt>@c svn_string_t *</tt>.
 *
 * If @a inherited_props is not @c NULL, then set @a *inherited_props
 * to a mapping of <tt>const char *</tt> URLS which are parents of @a path,
 * to a hash mapping <tt>const char *</tt> property names to
 * <tt>svn_string_t *</tt> property values for each inheritable property set
 * on the parent URL.  If @a inherited_props is not @c NULL no inheritable
 * properties are found, then set @a *inherited_props to an empty hash.
 *
 * The stream handlers for @a stream may not perform any RA
 * operations using @a session.
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_ra_get_file2(svn_ra_session_t *session,
                 const char *path,
                 svn_revnum_t revision,
                 svn_stream_t *stream,
                 svn_revnum_t *fetched_rev,
                 apr_hash_t **props,
                 apr_hash_t **inherited_props,
                 apr_pool_t *pool);

Rev svn_wc_prop_get2 to svn_wc_prop_get3, adding the argument apr_hash_t **inherited_values. Also allow the possibility that the existing svn_string_t **value argument may be null:

/** If @a value is not null, then set @a *value to the value of property
 * @a name for @a local_abspath, allocating @a *value in @a result_pool.
 * If no such prop, set @a *value to @c NULL. @a name may be a regular or
 * wc property; if it is an entry property, return the error
 * #SVN_ERR_BAD_PROP_KIND.
 *
 * If @a inherited_values is not @c NULL and @a name is a regular property,
 * then set @a *inherited_values to a mapping of <tt>const char *</tt> paths
 * (either working copy paths or URLs), which are parents of @a local_abspath,
 * to the <tt>svn_string_t *</tt> explicit property found on these parents.
 * If @a inherited_values is not @c NULL and no properties are inherited, then
 * set @a *inherited_values to an empty hash.  If @a inherited_values is not
 * @c NULL and @a name is a wc or entry property, then return the error
 * #SVN_ERR_BAD_PROP_KIND.
 *
 * @a wc_ctx is used to access the working copy.
 *
 * If @a local_abspath is not a versioned path, return
 * #SVN_ERR_WC_PATH_NOT_FOUND
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_wc_prop_get3(const svn_string_t **value,
                 apr_hash_t **inherited_values,
                 svn_wc_context_t *wc_ctx,
                 const char *local_abspath,
                 const char *name,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool);

Rev svn_fs_node_prop to svn_fs_node_prop2, adding the argument apr_hash_t **inherited_values. Also allow the possibility that the existing svn_string_t **value_p argument may be null:

/** If @a value_p is not null, then set @a *value_p to the value of the
 * property named @a propname of @a path in @a root.  If the node has no
 * property by that name, set @a *value_p to null.
 *
 * If @a inherited_values is not @c NULL then set @a *inherited_values to
 * a mapping of <tt>const char *</tt> absolute filesystem paths, which are
 * parents of @a path, to the <tt>svn_string_t *</tt> explicit inheritable
 * property @a propname found on these parents.  If @a inherited_values is
 * not @c NULL and no inherited properties are found, then set
 * @a *inherited_values to an empty hash.
 *
 * Allocate the results in @a pool.
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_fs_node_prop2(svn_string_t **value_p,
                  apr_hash_t **inherited_values,
                  svn_fs_root_t *root,
                  const char *path,
                  const char *propname,
                  svn_boolean_t get_inherited_prop,
                  apr_pool_t *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:

/** If @a table_p is not null, then set @a *table_p to the entire property
 * list of @a path in @a root.
 *
 * If @a inherited_table_p is not null, then set @a *inherited_table_p to
 * a mapping of <tt>const char *</tt> absolute filesystem paths, which are
 * parents of @a path, to a hash mapping <tt>const char *</tt> property names
 * to <tt>svn_string_t *</tt> property values for each inheritable property
 * set on the parent paths.  If @a inherited_table_p is not @c NULL and no
 * inherited properties are found, then set @a *inherited_table_p to an empty
 * hash.
 *
 * Both hashes map property names to pointers to #svn_string_t objects
 * containing the property value.  Both hashes are allocated in @a pool.
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_fs_node_proplist2(apr_hash_t **table_p,
                      apr_hash_t **inherited_table_p,
                      svn_fs_root_t *root,
                      const char *path,
                      apr_pool_t *pool);

New APIs

/** Return @c TRUE iff @a prop_name represents the name of a inheritable
 * property.
 *
 * @since New in 1.8.
 */
svn_boolean_t
svn_prop_is_inheritable_prop(const char *prop_name);

Drawbacks

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

  1. [danielsh] Using the 'svn:inheritable:' namespace precludes a property from being both inheritable and ${some_future_semantics}, since a property name cannot be simultaneously in the 'svn:inheritable:' namespace and in the 'svn:{some_future_semantics}:' namespace.
  2. [JAF] I note that there is no ability to specify that an inheritable property should be deleted rather than overridden with a new value. Thus we can't use absent/present semantics similar to 'svn:needs-lock'. If we set an inheritable equivalent of 'svn:needs-lock = *' on a parent dir, there is no way to designate a particular child node as explicitly removing that inherited property. That's probably fine – we can just avoid using the absent/present semantics for that kind of purpose – but it deserves to be mentioned.
  • No labels