| <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> |
| <html> |
| <head> |
| <title>Use of WebDAV in Subversion</title> |
| <style type="text/css"> |
| .comment { |
| color: #900000; |
| background: #ffffc0; |
| } |
| .method { |
| font-family: courier; |
| font-weight: bold; |
| } |
| .term { |
| font-style: italic; |
| } |
| .prop { |
| font-family: courier; |
| font-weight: bold; |
| } |
| .url { |
| font-family: courier; |
| } |
| <!-- ### this dt thing isn't working... need to fix --> |
| dt { |
| font-weight: bold; |
| } |
| </style> |
| </head> |
| |
| <body bgcolor=white> |
| <h1>Use of WebDAV in Subversion</h1> |
| |
| <p> |
| This document details how WebDAV is used within the |
| <a href="http://subversion.tigris.org/">Subversion |
| product</a>. Specifically, how the client side interfaces with |
| <a href="http://www.webdav.org/neon/">Neon</a> to generate |
| WebDAV requests over the wire, and what the |
| server must do to map incoming WebDAV requests into operations |
| against the Subversion repository. Note that the server side is |
| implemented as an |
| <a href="http://httpd.apache.org/">Apache 2.0</a> module, |
| operating as a back-end for its mod_dav functionality. |
| </p> |
| <p> |
| This document heavily refers to the |
| <a href="http://subversion.tigris.org/svn-design.html">Subversion |
| design document</a> and the |
| <a href="http://www.webdav.org/deltav/">latest Delta-V protocol |
| draft</a>. Details of those documents will <em>not</em> be |
| replicated here. |
| </p> |
| |
| <!-- ### put some of this stuff into a stylesheet --> |
| <center> |
| <table width="70%" border="0" bgcolor="#ff0000" cellspacing="0" |
| cellpadding="10"> |
| <tr> |
| <td> |
| <font color="white"> |
| <strong>NOTE:</strong> Subversion uses DeltaV for its |
| communication, but the Subversion client is |
| <strong>not</strong> a general-purpose DeltaV client. In |
| fact, it expects some custom features from the |
| server. Further, the Subversion server is |
| <strong>not</strong> a general-purpose DeltaV server. It |
| implements a strict <strong>subset</strong> of the |
| DeltaV specification. A WebDAV or DeltaV client may very |
| well be able to interoperate with it, but only if that |
| client operates within the narrow confines of those |
| features the server has implemented. |
| </font> |
| </td> |
| </tr> |
| <tr> |
| <td> |
| <font color="white"> |
| Version 2.0 of Subversion will address WebDAV |
| interoperability (Class 1, Class 2, and DeltaV |
| features). Each of the custom features expected by the |
| client actually has an alternate mechanism available in |
| DeltaV, but in a much less efficient form. |
| </font> |
| </td> |
| </tr> |
| <tr> |
| <td> |
| <font color="white"> |
| It is expected that Version 1.0 will support read-only, |
| Class 1 WebDAV clients. Any "low-hanging fruit" to |
| increase DeltaV interoperability will be considered. |
| </font> |
| </td> |
| </tr> |
| </table> |
| </center> |
| |
| <p></p> |
| |
| <hr width="80%"> |
| <h2>Basic Concepts</h2> |
| <p> |
| Subversion uses a tree-based format to describe a change set |
| against the repository. This tree is constructed on the client |
| side (by "walking" over the working copy) to describe the |
| change. The tree is marshalled to the server as a linear |
| sequence of changes to be applied to the repository. The |
| repository can accept changes in a random-access manner, so the |
| mapping from a tree to a set of changes works very well for the |
| repository. |
| </p> |
| <p> |
| Subversion provides properties on files, directories, and even |
| the abstract concept of a revision. Each of the operations |
| involving properties are mapped directly to WebDAV properties, |
| which are manipulated with the <span |
| class="method">PROPFIND</span> and <span |
| class="method">PROPPATCH</span> HTTP methods. Revisions are |
| modeled as DeltaV <span class="term">baselines</span>, so |
| revision properties are available through a <span |
| class="method">PROPFIND</span> on the baseline. |
| </p> |
| <p> |
| The Subversion server can efficiently compute deltas between two |
| revisions (these deltas are complete <span class="term">tree |
| deltas</span>, not simple text deltas). DeltaV does not have a |
| direct analogue for the tree delta concept. A client could |
| discover changes by issuing a sequence of <span |
| class="method">PROPFIND</span> requests on the various WebDAV |
| resources, but this would be a time-consuming operation, |
| involving many requests. Instead, Subversion marshals this |
| concept as a custom WebDAV <span |
| class="term">report</span>. Using this report, the client learns |
| which items in the working copy are out of date and can issue |
| <span class="method">GET</span> and <span |
| class="method">PROPFIND</span> methods to fetch the new data. |
| </p> |
| <p> |
| Tags and branches are simple copies in Subversion, which are |
| handled with the WebDAV <span class="method">COPY</span>. |
| </p> |
| <blockquote class="comment"> |
| need to talk about copies somewhere. need to discuss how copy |
| history is retained (svn does it automatically, but interop with |
| other servers may require us to set a custom property on those |
| servers. |
| </blockquote> |
| |
| <hr width="80%"> |
| <h2>DeltaV Concepts Used by Subversion</h2> |
| <p> |
| Subversion uses many of the DeltaV concepts, as listed |
| below. Note that many of these concepts are not fully |
| implemented by Subversion; we have implemented enough to meet |
| our needs, but little more. |
| </p> |
| <dl> |
| <dt>Baseline</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Activity</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Version Resource</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Version-Controlled Configuration</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Baseline Collection</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Version-Controlled Resource</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Working Resource (Feature)</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Merge Feature</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Label Feature</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| <dt>Version-Controlled-Collection Feature</dt> |
| <dd> |
| <span class="comment">further info to come...</span><p></p> |
| </dd> |
| |
| </dl> |
| |
| <hr width="80%"> |
| <h2>Subversion Projects as URLs</h2> |
| <p> |
| The very first concept to define is how a project is exposed to |
| the client. Subversion will expose all projects as URLs on a |
| server. The files and subdirectories under this project will be |
| exposed through the URL namespace. |
| </p> |
| <p> |
| For example, let us assume that we have a project named |
| "example". And let us say that this project will be exposed at |
| the URL: |
| <span class="url">http://subversion.tigris.org/repos/example/</span>. |
| </p> |
| <p> |
| This mapping is set up through a set of configuration |
| parameters for the Apache HTTP Server (which is hosting the |
| Subversion code and the particular project in question). The |
| configuration could look like: |
| </p> |
| <blockquote> |
| <pre><Location /repos/example> |
| DAV svn |
| SVNPath /home/svn-projects/example |
| </Location></pre> |
| </blockquote> |
| <p> |
| Files and directories within the project will be directly mapped |
| into the URL namespace. For example, if the project contains a |
| file "file.c" in a subdirectory "sub", then the URL for that |
| file will be |
| <span class="url">http://subversion.tigris.org/repos/example/sub/file.c</span>. |
| </p> |
| |
| <hr width="80%"> |
| <h2>Initial Checkout</h2> |
| <p> |
| When the user performs the initial checkout of a Subversion |
| project, the client will issue a series of <span |
| class="method">PROPFIND</span> and <span |
| class="method">GET</span> requests. These requests will traverse |
| the repository, pick up some necessary metadata, and then fetch |
| the latest revision. |
| </p> |
| <p class="comment"> |
| describe the OPTIONS request for fetching the activity |
| collection set. describe the sequence of PROPFINDs to reach the |
| baseline collection. |
| </p> |
| <p class="comment"> |
| <i>(moved here from below; need to rewrite)</i><br> |
| When the initial checkout was performed, Subversion fetched |
| the <span class="prop">DAV:activity-collection-set</span> value |
| and stored it as a property on each directory in the working copy. |
| property for each |
| collection. This property lists all of the locations on the |
| server where an activity may be created. The first of these |
| locations will be stored on the client for use during the commit |
| process. |
| </p> |
| <p class="comment"> |
| Should probably describe the metadata we fetch, and how a |
| checkout of "not the latest" (e.g. by date or revision) will |
| work. |
| </p> |
| |
| <hr width="80%"> |
| <h2>Committing a Change</h2> |
| <p> |
| Subversion commits are modeled using the "activity" concept from |
| DeltaV. An activity can be viewed as a transaction for a set of |
| resources. |
| </p> |
| |
| <h3>Creating the activity</h3> |
| <p> |
| At commit time, the Subversion client will retrieve the stored |
| <span class="prop">DAV:activity-collection-set</span> value to |
| know where it should create the activity. Next, |
| the client will generate a UUID (a unique value) to use for the |
| activity's location. Finally, the client will issue a |
| <span class="method">MKACTIVITY</span> method request, where the Request-URL is |
| composed from the activity location and the UUID. This request |
| will construct an activity to hold all of the changes for the |
| commit. |
| </p> |
| <p>Abbreviated summary:</p> |
| <dl> |
| <dt>At checkout time:</dt> |
| <dd> |
| Request: <span class="method">OPTIONS</span> for |
| <span class="prop">DAV:activity-collection-set</span> |
| <br> |
| Response: <span class="url">http://www.example.com/repos/foo/$svn/act/</span> |
| </dd> |
| <dt>At commit time:</dt> |
| <dd> |
| Request: <span class="method">MKACTIVITY</span> |
| <span class="url">http://www.example.com/repos/foo/$svn/act/01234567-89ab-cdef-0123-456789abcdef</span> |
| <br> |
| Response: 201 (Created) |
| </dd> |
| </dl> |
| <p> |
| The <span class="method">CHECKOUT</span> method can specify an |
| activity to use upon checkout. This feature is used to associate |
| all items with the newly-created activity. |
| </p> |
| |
| <h3>Storing the commit message</h3> |
| <p class="comment"> |
| talk about checking out the baseline and applying a PROPPATCH to |
| the working baseline. |
| </p> |
| |
| <h3>Mapping changes to WebDAV</h3> |
| <p> |
| A change set in Subversion is specified with a "tree delta" (see |
| the SVN design for more details on the changes that can be |
| placed into a tree delta). The tree delta will be unravelled |
| into a set of requests. These requests will be one of the |
| following forms: |
| </p> |
| <dl> |
| <dt>Delete file or directory</dt> |
| <dd> |
| These changes are mapped onto a <span |
| class="method">DELETE</span> operation. The version resource |
| of the target's parent collection is checked out using the |
| <span class="method">CHECKOUT</span> method (into the current |
| activity). The target (name) is then deleted from the |
| resulting working collection using the <span |
| class="method">DELETE</span> method. |
| |
| <p></p> |
| </dd> |
| |
| <dt>Add file</dt> |
| <dd> |
| This is modeled by performing a <span |
| class="method">CHECKOUT</span> of the version resource of the |
| target's parent collection. The new file is created within the |
| resulting working collection using a <span |
| class="method">PUT</span> request. Properties are applied |
| using <span class="method">PROPPATCH</span>. |
| |
| <p></p> |
| </dd> |
| |
| <dt>Add directory</dt> |
| <dd> |
| This is modeled by performing a <span |
| class="method">CHECKOUT</span> on the version resource of the |
| target's parent collection. The new directory is created |
| within the resulting working collection with a <span |
| class="method">MKCOL</span> request. Properties are applied |
| using <span class="method">PROPPATCH</span>. |
| |
| <p></p> |
| </dd> |
| |
| <dt>Add file or directory, with previous ancestory (a copy)</dt> |
| <dd> |
| <span class="comment">need to fix this section</span> |
| |
| <p> |
| A tree delta can specify that a file/directory originates as |
| a copy of another file/dir. This copy may be further |
| modified by additional elements the tree delta. |
| </p> |
| <p> |
| This change will be modeled by performing a |
| <code>CHECKOUT</code> on the version resource of the parent |
| collection which will contain the new resource. The |
| <code>VERSION-CONTROL</code> method will create a new |
| version-controlled resource (VCR) within the working |
| collection, with the VCR's <code>DAV:checked-in</code> |
| property referring to the ancestor's version resource. |
| </p> |
| <blockquote> |
| <strong>Note:</strong> it appears that we will use |
| <code>COPY</code> to copy the appropriate resource into the |
| working collection. This will create a new version history |
| which is then placed into the working collection. The |
| version history will use the <code>DAV:precursor-set</code> |
| property to specify the version resource of the ancestor. |
| |
| <p> |
| Because a version resource does not specify the revision, |
| it will not be possible to <code>COPY</code> a version |
| resource into the working collection -- it will not tell |
| us what revision was copied. Instead, we will most likely |
| copy a version resource out of the appropriate |
| baseline. This implies the client must be able to map from |
| a URL/revision pair to a baselined version resource URL. |
| </p> |
| <p> |
| The second issue is whether/how we set the |
| <code>DAV:precursor-set</code> property of the version |
| history. Or, more precisely, how we synthesize the value |
| from information stored in the repository. This is still |
| under investigation. |
| </p> |
| </blockquote> |
| </dd> |
| |
| <dt>Replace file/dir by another file/dir</dt> |
| <dd> |
| This change does not have a WebDAV modeling because tree |
| deltas model it as two, sequential operations: a |
| <em>delete</em>, followed by an <em>add</em>. |
| |
| <p></p> |
| </dd> |
| |
| <dt>Moving a file or directory</dt> |
| <dd> |
| This change does not have a WebDAV modeling because tree |
| deltas model it as two, distinct operations: a |
| <em>delete</em>, and an <em>add</em> with previous ancestry. |
| |
| <p></p> |
| </dd> |
| |
| <dt>Replace file</dt> |
| <dd> |
| This is modeled with a <span class="method">CHECKOUT</span> on |
| the target's version resource, followed by a <span |
| class="method">PUT</span> to the resulting working resource. |
| |
| <p></p> |
| </dd> |
| |
| <dt>Replace directory</dt> |
| <dd> |
| In Subversion terms, "replace directory" means that additions, |
| deletions, and other changes will occur <em>within</em> the |
| directory. Each of these changes are modeled individually, and |
| the change to the directory is performed |
| implicitly. Therefore, this "change" has no particular mapping |
| into WebDAV. |
| |
| <p></p> |
| </dd> |
| |
| <dt>Property delta</dt> |
| <dd> |
| A property delta (against a file or directory) maps directly |
| to a <span class="method">PROPPATCH</span> in WebDAV |
| terms. The target's version resource will be checked out using |
| <span class="method">CHECKOUT</span> and the <span |
| class="method">PROPPATCH</span> will be applied to the |
| resulting working resource. |
| </dd> |
| </dl> |
| |
| <h3>Final Commit</h3> |
| <p> |
| The final action of the commit process is to issue a <span |
| class="method">MERGE</span> request to the Subversion server, |
| specifying that the activity (created earlier) be checked in and |
| the corresponding version-controlled resources be updated to |
| refer to the new version resources. |
| </p> |
| <p class="comment"> |
| the comment below is not quite right. talk about the working |
| baseline, and how that is used to create a new baseline (with |
| the commit message on it) |
| </p> |
| <p> |
| The version-controlled resources are also baseline-controlled, |
| which means that updates to them will automatically create a new |
| baseline. In essence, the commit will create a new baseline |
| corresponding to the new Subversion revision. |
| </p> |
| |
| <h3>Example</h3> |
| <p class="comment"> |
| <strong>Warning:</strong> this section has not been updated to |
| reflect some recent changes to the SVN-to-DAV mapping. Consider |
| it out of date until this warning is removed. |
| </p> |
| <p> |
| Consider the following set of operations and its corresponding |
| tree delta (taken from the SVN design document): |
| </p> |
| <ol> |
| <li>rename <code>/dir1/dir2</code> to <code>/dir1/dir4</code>,</li> |
| <li>rename <code>/dir1/dir3</code> to <code>/dir1/dir2</code>, and</li> |
| <li>move <code>file3</code> from <var>/dir1/dir4</var> to <var>/dir1/dir2</var>.</li> |
| </ol> |
| <pre><tree-delta> |
| <replace name='dir1'> |
| <directory> |
| <tree-delta> |
| <replace name='dir2'> |
| <directory ancestor='/dir1/dir3'> (1) |
| <tree-delta> |
| <new name='file3'> (2) |
| <file ancestor='/dir1/dir2/file3'/> |
| </new> |
| </tree-delta> |
| </directory> |
| </replace> |
| <delete name='dir3'/> (3) |
| <new name='dir4'> (4) |
| <directory ancestor='/dir1/dir2'> |
| <tree-delta> |
| <delete name='file3'/> (5) |
| </tree-delta> |
| </directory> |
| </new> |
| </tree-delta> |
| </directory> |
| </replace> |
| </tree-delta> |
| </pre> |
| |
| <p> |
| Walking through this delta, we map out the WebDAV requests |
| listed below. The numbers in the above delta roughly correspond |
| to the numbered entries below. The correspondence is not exact |
| because a specific, resulting behavior is typically based on a |
| combination of a few elements in the delta. |
| </p> |
| <ol> |
| <li> |
| The <code><directory ancestor="/dir1/dir3"></code> |
| specifies that we are overwriting <code>/dir1/dir2</code> with |
| <code>/dir1/dir3</code>. |
| <p> |
| <code>CHECKOUT /dir1/dir2/</code><br> |
| <i>(returns a working resource URL for the directory)</i> |
| </p> |
| <p> |
| <code>COPY /dir1/dir3/</code><br> |
| <code>Destination: http://www.example.com/$svn/wrk/.../</code><br> |
| <code>Overwrite: T</code> |
| </p> |
| </li> |
| <li> |
| <code>/dir1/dir2/file3</code> is new (since we just overwrote |
| the original <code>dir2</code> directory), and originates from |
| <code>/dir1/dir2/file3</code>. Thus, we simply |
| <code>COPY</code> the file into the target directory's working |
| resource: |
| <p> |
| <code>COPY /dir1/dir2/file3</code><br> |
| <code>Destination: http://www.example.com/$svn/wrk/.../file3</code> |
| </p> |
| </li> |
| <li> |
| <code>CHECKOUT /dir1/dir3/</code><br> |
| <i>(returns a working resource URL for the directory)</i> |
| <p> |
| <code>DELETE /$svn/wrk/.../</code> |
| </p> |
| </li> |
| <li> |
| We are going to creating a new subdirectory (<code>dir4</code>) in the |
| <code>/dir1</code> directory. Since we don't have |
| <code>/dir1</code> checked out yet, we do so: |
| <p> |
| <code>CHECKOUT /dir1/</code><br> |
| <i>(returns a working resource URL for the directory)</i> |
| </p> |
| <p> |
| And now we copy the right directory into the new working |
| resource: |
| </p> |
| <p> |
| <code>COPY /dir1/dir2/</code><br> |
| <code>Destination: http://www.example.com/$svn/wrk/.../dir4/</code> |
| </p> |
| </li> |
| <li> |
| The <code>COPY</code> created a complete set of working |
| resources on the server, so we simply delete the part that we |
| don't want: |
| <p> |
| <code>DELETE: /$svn/wrk/.../dir4/file3</code> |
| </p> |
| </li> |
| </ol> |
| |
| <hr width="80%"> |
| <h2>URL Layout</h2> |
| <p> |
| The Subversion server exposes repositories at user-defined |
| URLs. For example, the "foo" repository might be located at |
| <span |
| class="url">http://www.example.com/repos/foo/</span>. However, |
| the server also requires a number of other resources to be |
| exposed for proper operation. These additional resources will be |
| associated with each repository in a location under the main |
| repository URL. By default, this location is "<span |
| class="url">$svn</span>". It may be changed by using the |
| <code>SVNSpecialURI</code> directive: |
| </p> |
| <blockquote> |
| <pre><Location /repos/foo> |
| DAV svn |
| SVNPath /home/svn-projects/foo |
| SVNSpecialURI .special |
| </Location></pre> |
| </blockquote> |
| <p> |
| Underneath the location specified by <code>SVNSpecialURI</code>, |
| we will expose several collections. Assuming we use the default |
| of "<span class="url">$svn</span>", the collections are: |
| </p> |
| <dl> |
| <dt><span class="url">$svn/act/</span></dt> |
| <dd> |
| This area is where activity resources are created. The client |
| will pick a unique name within this collection and issue a |
| <span class="method">MKACTIVITY</span> for that URL. The client will then use |
| the activity in further interactions. |
| <p> |
| No methods are allowed on the <span class="url">$svn/act/</span> |
| resource. |
| </p> |
| <blockquote> |
| Note: actually, we may want to allow a <code>PROPFIND</code> |
| with a <code>Depth: 1</code> header to allow clients to |
| enumerate the current activities. |
| </blockquote> |
| <p> |
| Only a subset of methods are allowed on the activities |
| within the collection. They are: <span class="method">PROPFIND</span>, |
| <span class="method">MERGE</span> (commit the activity), and |
| <span class="method">DELETE</span> (abort the activity). |
| </p> |
| <p> |
| Per the Delta-V specification, all activity resources will |
| have a <span class="prop">DAV:resourcetype</span> of |
| <span class="prop">DAV:activity</span>. |
| </p> |
| </dd> |
| |
| <dt><span class="url">$svn/his/</span></dt> |
| <dd> |
| <span class="comment">do something with this section; we |
| actually don't use version history resources. in the future, |
| they might be modeled like this</span> |
| |
| <p> |
| This collection contains the version history resources for |
| files and directories in a project. Its internal layout is |
| completely server-defined. Clients will receive URLs into |
| this collection (or a subcollection) from various responses. |
| </p> |
| <p> |
| No methods are allowed on the <span class="url">$svn/his/</span> |
| resource. |
| </p> |
| <p> |
| Internally, the URL namespace is laid out with URLs of the |
| following form: |
| </p> |
| <blockquote class="url"> |
| $svn/his/<var>node-id</var> |
| </blockquote> |
| <p> |
| The <var>node-id</var> is an internal value |
| that Subversion uses to reference individual files and |
| directories. This <var>node-id</var> is a single integer |
| defined by the Subversion repository. Note that this is a an |
| undotted node id, which is the base for the entire history |
| of a given node in the repository. |
| </p> |
| <p> |
| The <span class="prop">DAV:resourcetype</span> of the <var>node-id</var> |
| collection is <span class="prop">DAV:version-history</span>. |
| </p> |
| <blockquote class="comment"> |
| <strong>Note:</strong> the above information is probably not |
| quite correct. The issue of linking one version history to |
| another is still open. Further, I think that node 73 and |
| node 73.4.1 are each version histories (where the latter is |
| linked to the former). 73.x and 73.4.1.x are the versions |
| within the version history. |
| </blockquote> |
| </dd> |
| |
| <dt><span class="url">$svn/ver/</span></dt> |
| <dd> |
| This collection contains the version resources for the |
| project. |
| |
| <p> |
| No methods are allowed on the <span class="url">$svn/ver/</span> |
| resource. |
| </p> |
| <p> |
| The layout of this collection is internal to the server. For |
| reference purposes here (and to describe the |
| implementation), it is laid out as: |
| </p> |
| <blockquote class="url"> |
| $svn/ver/<var>node-id</var>/<var>path</var> |
| </blockquote> |
| <p> |
| Only read-only methods are allowed against these resources |
| (e.g. <span class="method">GET</span>, <span class="method">PROPFIND</span>, |
| <span class="method">REPORT</span>); all other methods are illegal. |
| </p> |
| <p> |
| The <span class="prop">DAV:resourcetype</span> of a version resource is |
| simply the value of the resource at checkin time |
| (e.g. <code><D:resourcetype/></code> or |
| <code><D:resourcetype><D:collection/></D:resourcetype></code>). |
| </p> |
| </dd> |
| |
| <dt><span class="url">$svn/wrk/</span></dt> |
| <dd> |
| This collection contains working resources for the resources |
| that have been checked out with the <span class="method">CHECKOUT</span> |
| method. The form and construction of this collection is |
| server-defined, but is also well-defined so that clients may |
| interact properly with collection versions that have been |
| checked out. |
| <p> |
| No methods are allowed on the <span class="method">$svn/wrk/</span> |
| resource. |
| </p> |
| <p> |
| For reference purposes, the working resource URLs are |
| constructed as: |
| </p> |
| <blockquote class="url"> |
| $svn/wrk/<var>activity</var>/<var>path</var> |
| </blockquote> |
| <p> |
| Any method is allowed on the working resources, but no |
| methods are allowed on any of its parents. |
| </p> |
| <p> |
| The <span class="prop">DAV:resourcetype</span> of the working resources |
| follows normal resource typing: |
| <code><D:resourcetype/></code> for regular working |
| resources, and |
| <code><D:resourcetype><D:collection/></D:resourcetype></code> |
| for working collections. |
| </p> |
| </dd> |
| |
| <dt><span class="url">$svn/vcc/</span></dt> |
| <dd> |
| <span class="comment">This section is not yet complete.</span> |
| |
| <p> |
| version-controlled configuration... |
| </p> |
| |
| <p> |
| <code>$svn/vcc/root</code> as a singleton. |
| </p> |
| </dd> |
| |
| <dt><code>$svn/bln/</code></dt> |
| <dd> |
| <span class="comment">This section is not yet complete.</span> |
| |
| <p> |
| baselines... |
| </p> |
| |
| <p> |
| <code>$svn/bln/<var>rev</var>/</code> |
| </p> |
| </dd> |
| |
| <dt><code>$svn/wbl/</code></dt> |
| <dd> |
| <span class="comment">This section is not yet complete.</span> |
| |
| <p> |
| working baseline... |
| </p> |
| </dd> |
| |
| <dt><code>$svn/bc/</code></dt> |
| <dd> |
| <span class="comment">This section is not yet complete.</span> |
| |
| <p> |
| baseline collection... |
| </p> |
| </dd> |
| </dl> |
| |
| <hr width="80%"> |
| <h2>Property Management (and History/Log Reporting)</h2> |
| <p class="comment"> |
| this section needs to be reworked. the properties occur on the |
| FS revisions (and exposed via baselines). |
| </p> |
| <p> |
| As mentioned before, Subversion properties map onto WebDAV |
| properties. For history/log reporting, the following WebDAV |
| properties will be applied to each baseline (a Subversion |
| revision) and to each version resource created by the |
| revision. Since these resources are all version resources, the |
| properties below are read-only. |
| </p> |
| <dl> |
| <dt><code>DAV:comment</code></dt> |
| <dd> |
| This is the standard (dead) property for specifying a checkin |
| comment. |
| <p></p> |
| </dd> |
| <dt><code>DAV:creator-displayname</code></dt> |
| <dd> |
| This is a (dead) property that is generated from Subversion's |
| concept of the "user" who made a particular change. |
| <p></p> |
| </dd> |
| <dt><code>DAV:creationdate</code></dt> |
| <dd> |
| This is a read-only live property created by the server at |
| commit time. |
| </dd> |
| </dl> |
| <p> |
| The history for a specified file will be generated using the |
| <code>REPORT</code> method and a |
| <code>DAV:property-report</code> report. A typical history will |
| fetch the three properties mentioned above for each version of |
| the file/directory. |
| </p> |
| <p> |
| Based on the client design, it may be important to specify other |
| read-only live properties for information about versions. For |
| examply, how many lines were added/removed in a particular |
| checkin for a file? Creating these live properties will be quite |
| straight-forward, and driven by the client design over time. |
| </p> |
| <blockquote> |
| Note: if we do this, however, then we'd end up tying the client |
| to the server. Of course, if the client were run against another |
| DeltaV server which didn't report these properties, then we'd |
| simply not display them in the UI. (e.g. graceful degradation of |
| functionality) |
| </blockquote> |
| |
| <hr width="80%"> |
| <h2>Fetching Status and Updates</h2> |
| <p> |
| After the initial checkout, the client can request a status |
| report (what has been changed on the client, pending a commit; |
| what has been changed on the server, pending an update). The |
| update process is similar, except that we also fetch the changes |
| from the server. |
| </p> |
| <p> |
| The local changes can be handled entirely on the client |
| side. The Working Copy library can easily handle the detection |
| and reporting of these changes. We're concerned with efficiently |
| detecting what has changed on the server. |
| </p> |
| <p> |
| While it would be possible to traverse the repository, fetching |
| the current state, and comparing that to the client state, it |
| would not be efficient. The Subversion design enables the server |
| to easily compute what has changed (relative to the client), if |
| it is given a description of the client state. |
| </p> |
| <p> |
| The core of the <em>status</em> and <em>update</em> commands is |
| based on a custom Subversion-specific WebDAV report. This custom |
| report will transmit the state of the working copy to the |
| server, and the server response will specify which resources |
| will need to be updated (fetched). |
| </p> |
| <p> |
| The request is a standard <code>REPORT</code> request, with a |
| custom XML body. The body will use the standard Subversion |
| technique of reporting a top-level revision number, and then |
| only reporting children that have different revisions. The |
| result of the report will use the same technique of reporting |
| only the resources where a change is found. If a change is |
| found, the server will provide a URL to the version resource to |
| fetch for the changed resource. The server will also report the |
| current revision number. |
| </p> |
| <blockquote class="comment"> |
| The XML DTDs for the request and response are TBD. |
| </blockquote> |
| <blockquote> |
| <small> |
| The custom report will tie the client to only those servers |
| which support the report, but a future version of the software |
| will contain a fallback codepath, a graceful degradation, to |
| support other DeltaV servers. |
| </small> |
| </blockquote> |
| <p> |
| When an updated is performed, the client will fetch each of the |
| URLs (using <code>GET</code> requests) provided in the server |
| response. |
| </p> |
| <p> |
| <code>GET</code> (and <code>PUT</code>) operations will transfer |
| content in a "diff" format when possible. The mechanics of this |
| will follow the Internet Draft, titled |
| <a href="http://www.ietf.org/internet-drafts/draft-mogul-http-delta-10.txt">Delta Encoding in HTTP</a>. |
| </p> |
| |
| <h3>Entity Tags (etags)</h3> |
| <p> |
| Etags are required to be unique across all versions of a |
| resource. Luckily, this |
| is very easy for a version control system. Each etag will be |
| simply be the repository's <em>node-id</em> for the resource. |
| </p> |
| <p> |
| Etags are used to generate diffs, following the guidelines in |
| the aforementioned draft: |
| <a href="http://www.ietf.org/internet-drafts/draft-mogul-http-delta-10.txt">Delta Encoding in HTTP</a>. |
| The problem then becomes how to get the etag for each file |
| stored on the client (we don't need etags for directories since |
| we never fetch them). During a <em>checkout</em> or |
| <em>update</em> process, this is easy: the etag is provided in |
| the HTTP response headers for each file retrieved. |
| </p> |
| <p> |
| The other part of the problem is getting the etag after a |
| <em>commit</em> has occurred. The <code>MERGE</code> response |
| provides a way to request properties from the version resources |
| which are created as part of the checkin of the activity. The |
| etag (and other properties) can be fetched using that mechanism. |
| </p> |
| |
| <hr width="80%"> |
| <h2>Tags and Branches</h2> |
| <p> |
| Tags and branches within Subversion are performed by copying |
| from one area to another. For example: |
| </p> |
| <blockquote> |
| <pre> |
| [.../src/my-project]$ svn cp trunk tags/1.0.3-rc4 |
| [.../src/my-project]$ svn commit |
| </pre> |
| </blockquote> |
| <p> |
| In the above example, <code>tags/1.0.3-rc4</code> should now be |
| considered readonly and will always reflect the status of |
| <code>trunk</code>. |
| </p> |
| <p> |
| These copies are handled just like a regular commit. An activity |
| is created with <span class="method">MKACTIVITY</span>, a |
| working resource is created via <span |
| class="method">CHECKOUT</span> (for the target directory; |
| <code>tags/</code> in our example above), and then a <span |
| class="method">COPY</span> is performed. The activity is then |
| merged back into the repository with a <span |
| class="method">MERGE</span> request. |
| </p> |
| |
| <hr width="80%"> |
| <h2>Server Requirements</h2> |
| <p class="comment"> |
| <strong>Warning:</strong> this section is out of date. The |
| DeltaV draft has gone through a number of revisions, and our use |
| of DeltaV has changed some. |
| </p> |
| |
| <h3>DAV Methods</h3> |
| <p> |
| The server will need to implement the following WebDAV methods |
| for proper operation: |
| </p> |
| <ul> |
| <li><span class="method">OPTIONS</span></li> |
| <li><span class="method">GET</span></li> |
| <li><span class="method">DELETE</span></li> |
| <li><span class="method">COPY</span></li> |
| <li><span class="method">PROPPATCH</span></li> |
| <li><span class="method">PROPFIND</span></li> |
| |
| <li><span class="method">MKACTIVITY</span></li> |
| <li><span class="method">CHECKOUT</span></li> |
| <li><span class="method">MERGE</span></li> |
| <li><span class="method">REPORT</span></li> |
| </ul> |
| <p> |
| The following methods are not required by Subversion at this |
| time: |
| </p> |
| <ul> |
| <li><span class="method">CHECKIN</span></li> |
| <li><span class="method">UNCHECKOUT</span></li> |
| <li><span class="method">UPDATE</span></li> |
| <li><span class="method">LABEL</span></li> |
| <li><span class="method">VERSION-CONTROL</span></li> |
| <li><span class="method">BASELINE-CONTROL</span></li> |
| <li><span class="method">MKWORKSPACE</span></li> |
| </ul> |
| |
| <h3>DAV Properties</h3> |
| <p> |
| The following DeltaV properties will be implemented: |
| </p> |
| <ul> |
| <!-- resource properties --> |
| <li><span class="prop">DAV:comment</span></li> |
| <li><span class="prop">DAV:creator-displayname</span></li> |
| <li><span class="prop">DAV:supported-method-set</span></li> |
| <li><span class="prop">DAV:supported-live-property-set</span></li> |
| <li><span class="prop">DAV:supported-report-set</span></li> |
| <li><span class="prop">DAV:version-controlled-configuration</span></li> |
| |
| <!-- version-controlled resource properties --> |
| <li><span class="prop">DAV:checked-in</span></li> |
| <li> |
| <span class="prop">DAV:auto-version</span> is a readonly, |
| empty element (auto versioning not supported). |
| </li> |
| |
| <!-- checked-out resource properties --> |
| <li><span class="prop">DAV:checked-out</span></li> |
| <li> |
| <span class="prop">DAV:predecessor-set</span> |
| <br> |
| <i> |
| Note: the Subversion design document is not clear on the |
| mechanics of how multiple predecessors are merged to create |
| a single, new revision. When this clarifies, then |
| <code>DAV:predecessor-set</code> may end up containing more |
| than zero or one predecessor URLs |
| </i> |
| </li> |
| |
| <!-- version resource properties --> |
| <!-- predecessor-set --> |
| <li> |
| <span class="prop">DAV:version-name</span> is simply the |
| (global) revision number. |
| </li> |
| <li><span class="prop">DAV:checkout-fork</span></li> |
| <li><span class="prop">DAV:checkin-fork</span></li> |
| |
| <!-- checked-out (working resource) properties --> |
| <!-- checkout-fork, checkin-fork --> |
| <li><span class="prop">DAV:auto-update</span></li> |
| <li> |
| <span class="prop">DAV:subbaseline-set</span> is a readonly, |
| empty property (sub-baselines not supported). |
| </li> |
| <li> |
| <span class="prop">DAV:unreserved</span> is set to |
| <code>F</code>. |
| </li> |
| |
| <!-- vcc properties --> |
| <li><span class="prop">DAV:baseline-controlled-collection</span></li> |
| |
| <!-- baseline properties --> |
| <!-- subbaseline-set --> |
| <li><span class="prop">DAV:baseline-collection</span></li> |
| |
| <!-- activity properties --> |
| <li> |
| <span class="prop">DAV:subactivity-set</span> is a readonly, |
| empty property (sub-activities not supported). |
| </li> |
| |
| <!-- version-controlled collection properties --> |
| <li> |
| <span class="prop">DAV:eclipsed-set</span> is always empty |
| (internal members can never be eclipsed). |
| </li> |
| </ul> |
| |
| <p> |
| Contrary to the DeltaV specification, the following required |
| properties will not be implemented: |
| </p> |
| <ul> |
| <li> |
| <span class="prop">DAV:successor-set</span> - this may be an |
| expensive operation to synthesize this value. |
| </li> |
| <li> |
| <span class="prop">DAV:checkout-set</span> - we do not record |
| what has actually been checked out, but use the working |
| resource URL to provide the necessary information; thus we |
| have no record of the data to populate this property. |
| </li> |
| <li> |
| <span class="prop">DAV:merge-set</span> - our <span |
| class="method">MERGE</span> method is solely to support a |
| commit. It doesn't really support the arbitrary merging |
| defined within the spec. |
| </li> |
| <li> |
| <span class="prop">DAV:auto-merge-set</span> - same as for |
| <span class="prop">DAV:merge-set</span>. |
| </li> |
| <li> |
| <span class="prop">DAV:activity-version-set</span> - |
| activities are only used for working resources, so versions |
| cannot be part of an activity. |
| <br> |
| <i>maybe this should be defined as the empty set?</i> |
| </li> |
| <li> |
| <span class="prop">DAV:activity-checkout-set</span> |
| activities are only used for working resources, and we do not |
| record what working resources "exist". |
| </li> |
| <li> |
| <span class="prop">DAV:activity-set</span> - |
| activities are only used for working resources, so versions |
| cannot be part of an activity. |
| <br> |
| <i>maybe this should be defined as the empty set?</i> |
| </li> |
| <li> |
| <span class="prop">DAV:version-controlled-binding-set</span> - |
| we do not have version history resources to include in the |
| property. |
| </li> |
| </ul> |
| |
| <h3>OPTIONS</h3> |
| <p> |
| The <span class="method">OPTIONS</span> request will signal that |
| it supports the following DAV features: |
| </p> |
| <ul> |
| <li><code>1</code></li> |
| <li><code>2</code></li> |
| <li><code>version-control</code></li> |
| <li><code>checkout</code></li> |
| <li><code>working-resource</code></li> |
| <li><code>merge</code></li> |
| <li><code>baseline</code></li> |
| <li><code>activity</code></li> |
| <li><code>version-controlled-collection</code></li> |
| </ul> |
| |
| <h3>Reports</h3> |
| <p> |
| The <span class="prop">DAV:supported-report-set</code> property |
| will signal support for the following reports: |
| </p> |
| <ul> |
| <li>svn:update-report</li> |
| <li>svn:log-report</li> |
| </ul> |
| <p> |
| These reports are available only on the "public" resources (the |
| VCRs). They are not available on the resources within the |
| <span class="url">$svn/</span> area. |
| </p> |
| |
| <h3>Notes, reminders</h3> |
| <p> |
| Discuss timeouts and auto-purge of activities (and the related |
| working resources). |
| <br> |
| Discuss the activity database maintained by mod_dav_svn. |
| <br> |
| Discuss other implementation details of ra_dav and mod_dav_svn. |
| </p> |
| |
| <h2><a name="rationale">Appendix A: Rationale</a></h2> |
| <p> |
| Several times, people have asked, "Why choose |
| HTTP/WebDAV/DeltaV? That seems awfully bloated and |
| ill-suited. Why didn't you design a custom, well-tuned protocol? |
| Or maybe use the CVS protocol?" Listed below are a number of |
| reasons for our choice of WebDAV as our network protocol. |
| </p> |
| |
| <blockquote> |
| <small> |
| While this list could certainly be expanded with more reasons |
| (and to be fair, with a list of reasons why WebDAV was a poor |
| choice), it certainly demonstrates the basic reasons for our |
| choice. |
| </small> |
| </blockquote> |
| |
| <blockquote> |
| <small> |
| Note: this list came from an email note, so the tone and point |
| of view might be a bit off. Further word-smithing is |
| welcome... |
| </small> |
| </blockquote> |
| |
| <h3>Builtin web browsing of the repository</h3> |
| <blockquote> |
| For example, take a |
| look at: |
| <a href="http://svn.collab.net/repos/svn/trunk/HACKING">http://svn.collab.net/repos/svn/trunk/HACKING</a> |
| (that's the HEAD right there; we also have URLs for every |
| previous revision of every file) |
| </blockquote> |
| |
| <h3>DAV-based browsing</h3> |
| <blockquote> |
| Use Web Folders or WebDrive or somesuch on |
| your Windows box (or Windows XP's native DAV mounts) to browse |
| the SVN repository with Windows Explorer. Mac OS X has builtin |
| DAV server mounting. Nautilus has DAV capabilities. Then you |
| have your Open Source tools such as cadaver, Goliath, etc. |
| </blockquote> |
| |
| <h3>People can use existing libraries</h3> |
| <blockquote> |
| I couldn't even begin to count the number of HTTP tools and |
| libraries available. If we had designed our own protocol, then |
| we would have /none/ of those benefits. Heck, two HTTP library |
| implementors (Joe Orton of Neon, and Daniel Stenberg of CURL) |
| are regulars here. we wouldn't get that benefit. I've used |
| Python's httplib (and a davlib of my own) to do a lot of testing |
| of our server. No need to go and roll new protocol libraries. |
| </blockquote> |
| |
| <h3>Existing tools</h3> |
| <blockquote> |
| One word: Ethereal :-) When we capture network traces, Ethereal |
| already knows about HTTP. It's quite nice, but I know there are |
| even better ones out there. But we also have other tools like |
| squid and other (caching) proxies (see the next item). |
| </blockquote> |
| |
| <h3>Caching proxies</h3> |
| <blockquote> |
| Subversion will work great with caching proxies. There is no |
| longer a need for specialized tools like "cvsup". Just drop in a |
| caching proxy, and you've already got your distributed read-only |
| repository. That european dev team can just drop in the cache |
| between them and the US server and their checkouts/updates will |
| get cached for the benefit of the other team members. Commits |
| will flow through, back to the US-based server. |
| </blockquote> |
| |
| <h3>Sophisticated and broad-choice authentication</h3> |
| <blockquote> |
| We don't have to reimplement an authentication scheme for a new |
| protocol. We can use all of the various schemes that have been |
| defined for HTTP. Ever look at the CVS protocol? Ever see the "I |
| Love You" or "I Hate You" lines? :-) That is all part of |
| creating a new authentication scheme. But we get to use SSL and |
| certificate-based auth if we want. Kerberos. NTLM. or even just |
| simple Basic or Digest. And our users can come from text files, |
| database, LDAP, or PAM. We don't have to reinvent the wheel cuz |
| it is all available for Apache already. |
| </blockquote> |
| |
| <h3>Awesome network server</h3> |
| <blockquote> |
| We don't have to worry about how to portably set TCP_CORK for |
| optimal network packets. We don't have to worry about when |
| sendfile() makes sense, or if it is available. We don't have to |
| worry about dropped client connections, how to best use threads |
| and processes to scale, request management, monitoring, logging, |
| etc. Apache gives us all of that and a ton more. I *really* |
| would not want to do that through xinetd. I mean... setting |
| TCP_CORK on stdout? freaky :-) |
| </blockquote> |
| |
| <h3>Well-defined on-wire compression</h3> |
| <blockquote> |
| We already have on-wire compression, similar to CVS's "-z#" |
| switch. And we didn't do anything. The client library and server |
| that we use just support it automatically for us, according to |
| RFC 2616. |
| </blockquote> |
| |
| <h3>Future interoperability</h3> |
| <blockquote> |
| In the future, we'll be able to interoperate with a multitude of |
| IDEs and other WebDAV/DeltaV clients. As DeltaV becomes more |
| prevalent, IDEs could very well use it for source code |
| management, and we'll be right there without needing to write |
| some MS/SCC library to interface to the tool. |
| </blockquote> |
| |
| <hr> |
| <address><a href="mailto:gstein@lyra.org">Greg Stein</a></address> |
| <!-- Created: Thu Aug 10 19:14:20 PDT 2000 --> |
| <!-- hhmts start --> |
| Last modified: Fri Jan 25 12:54:20 PST 2002 |
| <!-- hhmts end --> |
| </body> |
| </html> |