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:
- A way to get a path's inherited properties from the repository.
- A way to cache a working copy's inherited properties locally so that disconnected WC operations remain disconnected.
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 inherited. 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.
Repository Inheritance Rules
Inheritance of properties within the repository is pretty straightforward, for a given property 'PropName':
- A repository child_path@REV may inherit one or more values of the 'PropName' property from any of the child's parents (i.e. its path-wise ancestors @REV) which have the 'PropName' property explicitly set on them.
Inheritance Rules
- For a given property 'PropName':
- A working copy child path may inherit the 'PropName' 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 'PropName' 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 occurs separately within each revision, and the WC 'actual' tree is treated as an unnumbered prototype for a revision that could potentially be committed[1]. Thus, a WC base node inherits from its repository parent node in its own revision in the repository, no matter whether that is the same as its WC parent node; and an 'actual' node[2] inherits from the parent node that it would have in the repository if the current WC were committed in full, no matter whether that is the same as its WC parent node."
I think that principle would match all your rules if the 'mixed-revision' rule applies to actual nodes but not base nodes.
[1] This particular principle for how we treat the 'actual' tree 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] By 'actual' version I mean the 'topmost' (i.e. MAX(op_depth)) 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
. I thought we differentiated actual vs. working such that the former included property and text changes? ([JAF] Yes, sorry for confusing terminology; I've adjusted my note to use 'actual'.)
[JAF] At each WC path there is potentially a base node, potentially some intermediate layers, and potentially an actual node. Each of the nodes has properties, which may include inherited properties. When you say "Inheritance within the WC is within the actual tree", precisely what do you mean? It seems clear you mean each actual node should inherit from its (WC path parent) actual node, but do you mean each base node should inherit from its (WC parent path) actual node as well? The latter sounds terribly wrong because a base node is supposed to represent a repository node, but now it would have a different set of inherited properties depending on the state of the WC.
In what I suggest, a child path base node would inherit from its repository parent, no matter whether that is the same as its its WC parent base node, because that maintains consistency, guaranteeing that particular node has the same set of properties no matter when and where you find it. As for the WC actual tree, our ideas are different but similar, but never mind that until we're clear on the basics.
- Note an important implication of #2 and #3: Unlike svn:mergeinfo, a child path with the 'PropName' property explicitly set on it can also inherit the 'PropName' 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.
- 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.
- 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.
### 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
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:
- Properties can only be inherited from paths which the user has read access to.
- Inheritance from a grandparent can occur across an intermediate parent the user has no read access to, as long as the user can read the grandparent.
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 proposal 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 suggested 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:
- '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 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 inheritable 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 custom user inheritable properties. 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, svn pg svn:mergeinfo --show-inherited-props -v ^/branches/projX/src
would show either the explicit mergeinfo on ^/branches/projX/src
or the mergeinfo existing on src's
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_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>const 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>const 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:
- [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.
- [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.