subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Apache subversion Wiki <comm...@subversion.apache.org>
Subject [Subversion Wiki] Update of "InheritedProperties" by pburba
Date Thu, 26 Jan 2012 21:53:40 GMT
Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Subversion Wiki" for change notification.

The "InheritedProperties" page has been changed by pburba:
http://wiki.apache.org/subversion/InheritedProperties

Comment:
Initial brain dump on simple inherited properties

New page:
= 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.
 1. Reserved Subversion inheritable properties will use the "svn:i:" prefix.

== Inheritance Rules ==
Inheritance will be very similar to the current svn:mergeinfo inheritance model.  For a given
inheritable property 'svn:i:X':

 1. A path '''''with''''' svn:i:X' explicitly set on it never inherits that property (i.e.
a path can only inherit a property it doesn't have set on itself).''''' '''''
 1. A repository path@REV '''''without''''' the 'svn:i:X' property explicitly set on it (we'll
refer to this as the 'child' path from here on) may inherit the property from the path's nearest
path-wise ancestor@REV with the 'svn:i:X' property explicitly set on it (we'll refer to this
as the 'parent' path).
 1. A '''''working copy''''' child path '''''without''''' the 'svn:i:X' property explicitly
set on it may inherit the 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:i: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).
 1. 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.  There are no plans to provide an API which reveals the
specific parent path a child inherits from.
 1. 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.

'''''### 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.

'''''### TBD: Specifics for the wcng storage of these caches.'''''

== 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.

== Copies and Inherited Properties ==
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.

== 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 ==
{{{
/**
 * Invoke @a receiver with @a receiver_baton to return the regular 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
 * 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 @a target may inherit properties up to the root
 * of the working copy.  If no inherited properties are found up to the
 * root of the working copy, then check the inherited properties cache.
 * If @a target is a URL, then @a target may inherit properties up to the
 * root of 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_receiver_t receiver,
                     void *receiver_baton,
                     svn_client_ctx_t *ctx,
                     apr_pool_t *pool);
}}}
'''''### TBD: Do we want to rev svn_proplist_receiver_t so it can differentiate between explicit
and inherited properties?'''''

{{{
/**
 * Set @a *props to a hash table whose keys are absolute paths or URLs
 * of items on which property @a propname is set, and whose values are
 * `#svn_string_t *' representing the property value for @a propname
 * at that path.
 *
 * 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 @a get_target_inherited_prop is true, @a propname is an inheritable
 * property, and @a propname is not explicitly set on @a target, then also
 * include the property value (if any) for @a propname which @a target
 * inherits from its nearest parent.  Regardless of whether @a target has
 * @a propname explicitly set on it or inherits that value, the absolute
 * path or URL key in @a *props still corresponds to @a target.
 * If @a target is a working copy path, then @a target may inherit
 * properties up to the root of the working copy.  If no inherited
 * properties are found up to the root of the working copy, then the
 * the inherited properties cache is checked.  If @a target is a URL,
 * then @a target may inherit properties up to the root of the repository.
 * If @a get_target_inherited_props is false, then no inherited properties
 * are included in @a *props.
 *
 * 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,
                    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_boolean_t get_target_inherited_prop,
                    svn_client_ctx_t *ctx,
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool);

/**
 * 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 get_inherited_props is true and @a props is non @c NULL, then
 * include any props which @a path inherits in @a *props.
 *
 * @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,
                const char *path,
                svn_revnum_t revision,
                apr_uint32_t dirent_fields,
                svn_boolean_t get_inherited_props,
                apr_pool_t *pool);

/**
 * 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 get_inherited_props is true and @a props is non @c NULL, then
 * include any props which @a path inherits in @a *props.
 *
 * 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_boolean_t get_inherited_props,
                 svn_stream_t *stream,
                 svn_revnum_t *fetched_rev,
                 apr_hash_t **props,
                 apr_pool_t *pool);

/** 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.  @a wc_ctx
 * is used to access the working copy.
 *
 * If @a get_inherited_prop is true, property @a name is not explicitly
 * set on @a local_abspath, and @a name is an inheritable property, then
 * check if property @a name is inherited from a parent path of @a path.
 * If this is the case, set @a *value to the value held by the nearest
 * parent of @a path.  If no parent is found with property @a name, then
 * check the inherited properties cache.
 *
 * 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,
                 svn_wc_context_t *wc_ctx,
                 const char *local_abspath,
                 const char *name,
                 svn_boolean_t get_inherited_prop,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool);

### TBD: Do we want another output argument to indicate if *VALUE inherited or not?

/** 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 and
 * @a get_inherited_prop is false, then set @a *value_p to zero.  If
 * the node has no property by that name, @a propname is an inheritable
 * property, and @a get_inherited_prop is true, then set @a *value_p to
 * the value (if any) which @a path inherits from its nearest parent.
 *
 * Allocate the result in @a pool.
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_fs_node_prop2(svn_string_t **value_p,
                  svn_fs_root_t *root,
                  const char *path,
                  const char *propname,
                  svn_boolean_t get_inherited_prop,
                  apr_pool_t *pool);


/** Set @a *table_p to the entire property list of @a path in @a root,
 * as an APR hash table allocated in @a pool.  The resulting table maps
 * property names to pointers to #svn_string_t objects containing the
 * property value.  If @a get_inherited_props is true, then any properties
 * inherited by @a path are also included in @a *table_p.
 *
 * @since New in 1.8.
 */
svn_error_t *
svn_fs_node_proplist2(apr_hash_t **table_p,
                      svn_fs_root_t *root,
                      const char *path,
                      svn_boolean_t get_inherited_props,
                      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);
}}}

Mime
View raw message