| <html> |
| <head> |
| <title>Subversion Best Practices</title> |
| </head> |
| |
| <body> |
| |
| <center> |
| <h1>Subversion Best Practices</h1> |
| </center> |
| |
| <p>This is a quick set of guidelines for making the best use of |
| Subversion in your day-to-day software development work.</p> |
| |
| |
| <h2>Use a sane repository layout</h2> |
| |
| <p>There are many ways to lay out your repository. Because branches |
| and tags are ordinary directories, you'll need to account for them in |
| your repository structure.</p> |
| |
| <p>The Subversion project officially recommends the idea of a "project |
| root", which represents an anchoring point for a project. A "project |
| root" contains exactly three subdirectories: <tt>/trunk</tt>, |
| <tt>/branches</tt>, and <tt>/tags</tt>. A repository may contain |
| only one project root, or it may contain a number of them.</p> |
| |
| <p><em>Book reference:</em> <a |
| href="http://svnbook.red-bean.com/svnbook/ch05s04.html#svn-ch-5-sect-6.1">Choosing |
| a Repository Layout</a>.</p> |
| |
| |
| |
| <!-- =================================================== --> |
| |
| <h2>Commit logical changesets</h2> |
| |
| <p>When you commit a change to the repository, make sure your change |
| reflects a single purpose: the fixing of a specific bug, the addition |
| of a new feature, or some particular task. Your commit will create a |
| new revision number which can forever be used as a "name" for the |
| change. You can mention this revision number in bug databases, or use |
| it as an argument to <tt>svn merge</tt> should you want to undo the |
| change or port it to another branch.</p> |
| |
| <p><em>Book reference:</em> "Subversion and Changesets" sidebar, |
| within <a |
| href="http://svnbook.red-bean.com/svnbook/ch04s03.html">chapter |
| 4</a>.</p> |
| |
| <!-- =================================================== --> |
| |
| <h2>Use the issue-tracker wisely</h2> |
| |
| <p>Try to create as many two-way links between Subversion changesets |
| and your issue-tracking database as possible:</p> |
| |
| <ul> |
| <li>If possible, refer to a specific issue ID in every commit log message.</li> |
| <li>When appending information to an issue (to describe progress, or |
| to close the issue) name the revision number(s) responsible |
| for the change.</li> |
| </ul> |
| |
| <!-- =================================================== --> |
| |
| <h2>Track merges manually</h2> |
| |
| <p>When committing the result of a merge, be sure to write a |
| descriptive log message that explains what was merged, something |
| like:</p> |
| |
| <pre>Merged revisions 3490:4120 of /branches/foobranch to /trunk.</pre> |
| |
| <p><em>Book reference:</em> <a |
| href="http://svnbook.red-bean.com/svnbook/ch04s03.html#svn-ch-4-sect-3.2">Tracking |
| merges manually</a>, and <a |
| href="http://svnbook.red-bean.com/svnbook/ch04s04.html#svn-ch-4-sect-4.1">Merging a whole branch to another</a>.</p> |
| |
| <!-- =================================================== --> |
| |
| <h2>Understand mixed-revision working copies</h2> |
| |
| <p>Your working copy's directories and files can be at different |
| "working" revisions: this is a deliberate feature which allows you to |
| mix and match older versions of things with newer ones. But there are |
| few facts you must be aware of:</p> |
| |
| <ol> |
| |
| <li>After every <tt>svn commit</tt>, your working copy has mixed |
| revisions. The things you just committed are now at the HEAD |
| revision, and everything else is at an older revision.</li> |
| |
| <li>Certain commits are disallowed: |
| <ul> |
| <li>You cannot commit the deletion of a file or directory which |
| doesn't have a working revision of HEAD.</li> |
| <li>You cannot commit a property change to a directory which |
| doesn't have a working revision of HEAD.</li> |
| </ul> |
| </li> |
| |
| <li><tt>svn update</tt> will bring your entire working copy to one |
| working revision, and is the typical solution to the |
| problems mentioned in point #2.</li> |
| </ol> |
| |
| <p><em>Book reference:</em> <a |
| href="http://svnbook.red-bean.com/svnbook/ch02s03.html#svn-ch-2-sect-3.4">The |
| limitation of mixed revisions</a>.</p> |
| |
| |
| <!-- =================================================== --> |
| |
| <h2>Be patient with large files</h2> |
| |
| <p>A nice feature of Subversion is that by design, there is no limit |
| to the size of files it can handle. Files are sent "streamily" in |
| both directions between Subversion client and server, using a small, |
| constant amount of memory on each side of the network.</p> |
| |
| <p>Of course, there are a number of practical issues to consider. |
| While there's no need to worry about files in the kilobyte-sized range |
| (e.g. typical source-code files), committing larger files can take a |
| tremendous amount of both time and space (e.g. files that are dozens |
| or hundreds of megabytes large.)</p> |
| |
| <p>To begin with, remember that your Subversion working copy stores |
| pristine copies of all version-controlled files in the |
| <tt>.svn/text-base/</tt> area. This means that your working copy |
| takes up at least twice as much disk space as the original dataset. |
| Beyond that, the Subversion client follows a (currently unadjustable) |
| algorithm for committing files:</p> |
| |
| <ul> |
| <li>Copies the file to <tt>.svn/tmp/</tt> <em>(can take a while, |
| and temporarily uses extra disk space)</em>)</li> |
| |
| <li>Performs a binary diff between the tmpfile and the pristine |
| copy, or between the tmpfile and an empty-file if newly |
| added. <em>(can take a very long time to compute, even |
| though only a small amount of data might ultimately be sent |
| over the network)</em></li> |
| |
| <li>Sends the diff to the server, then moves the tmpfile into |
| <tt>.svn/text-base/</tt></li> |
| </ul> |
| |
| <p>So while there's no theoretical limit to the size of your files, |
| you'll need to be aware that very large files may require quite a bit |
| of patient waiting while your client chugs away. You can rest |
| assured, however, that unlike CVS, your large files won't incapacitate |
| the server or affect other users.</p> |
| |
| <!-- =================================================== --> |
| |
| <h2>Work around commands that don't understand copies/renames</h2> |
| |
| <p>When a file or directory is copied or renamed, the Subversion repository |
| tracks that history. Unfortunately in Subversion 1.0, the only client |
| subcommand which actually takes advantage of this feature is <tt>svn |
| log</tt>. A number of other commands (such as <tt>svn diff</tt> and |
| <tt>svn cat</tt>) ought to be automatically following rename-history, |
| but aren't doing so yet.</p> |
| |
| <p>In all of these cases, a basic workaround is to use <tt>'svn log |
| -v'</tt> to discover the proper path within the older revision.</p> |
| |
| <p>For example, suppose you copied <tt>/trunk</tt> to |
| <tt>/branches/mybranch</tt> in revision 200, and then committed some |
| changes to <tt>/branches/mybranch/foo.c</tt> in subsequent revisions. |
| Now you'd like to compare revisions 80 and 250 of the file.</p> |
| |
| <p>If you have a working copy of the branch and run <tt>svn diff |
| -r80:250 foo.c</tt>, you'll see an error about |
| <tt>/branches/mybranch/foo.c</tt> not existing in revision 80. To |
| remedy, you would run <tt>svn log -v</tt> on your branch or file to |
| discover that it was named <tt>/trunk/foo.c</tt> prior to revision 200, |
| and then compare the two URLs directly:</p> |
| |
| <pre> |
| $ svn diff http://.../trunk/foo.c@80 \ |
| http://.../branches/mybranch/foo.c@200 |
| </pre> |
| |
| |
| |
| <!-- =================================================== --> |
| |
| <h2>Know when to create branches</h2> |
| |
| <p>This is a hotly debated question, and it really depends on the |
| culture of your software project. Rather than prescribe a universal |
| policy, we'll describe three common ones here.</p> |
| |
| <h3>The Never-Branch system</h3> |
| |
| <p>(Often used by nascent projects that don't yet have runnable code.)</p> |
| |
| <ul> |
| <li>Users commit their day-to-day work on <tt>/trunk</tt>.</li> |
| <li>Occasionally <tt>/trunk</tt> "breaks" (doesn't compile, or fails |
| functional tests) when a user begins to commit a series of complicated |
| changes.</li> |
| </ul> |
| |
| <p><em>Pros:</em> Very easy policy to follow. New developers have low |
| barrier to entry. Nobody needs to learn how to branch or merge.</p> |
| |
| <p><em>Cons:</em> Chaotic development, code could be unstable at any |
| time.</p> |
| |
| <p>A side note: this sort of development is a bit less risky in |
| Subversion than in CVS. Because Subversion commits are atomic, it's |
| not possible for a checkout or update to receive a "partial" commit |
| while somebody else is in the process of committing.</p> |
| |
| |
| <h3>The Always-Branch system</h3> |
| |
| <p>(Often used by projects that favor heavy management and supervision.)</p> |
| |
| <ul> |
| <li>Each user creates/works on a private branch for <em>every</em> coding task. |
| </li> |
| <li>When coding is complete, someone (original coder, peer, or |
| manager) reviews all private branch changes and merges them to |
| <tt>/trunk</tt>.</li> |
| </ul> |
| |
| <p><em>Pros:</em> <tt>/trunk</tt> is guaranteed to be |
| <em>extremely</em> stable at all times. </p> |
| |
| <p><em>Cons:</em> Coders are artificially isolated from each other, |
| possibly creating more merge conflicts than necessary. |
| Requires users to do lots of extra merging.</p> |
| |
| |
| <h3>The Branch-When-Needed system</h3> |
| |
| <p>(This is the system used by the Subversion project.) |
| |
| <ul> |
| <li>Users commit their day-to-day work on <tt>/trunk</tt>.</li> |
| |
| <li>Rule #1: <tt>/trunk</tt> must compile and pass regression tests at |
| all times. Committers who violate this rule are publically |
| humiliated.</li> |
| |
| <li>Rule #2: a single commit (changeset) must not be so large |
| so as to discourage peer-review.</li> |
| |
| <li>Rule #3: if rules #1 and #2 come into conflict (i.e. it's |
| impossible to make a series of small commits without disrupting the |
| trunk), then the user should create a branch and commit a series of |
| smaller changesets there. This allows peer-review without disrupting |
| the stability of <tt>/trunk</tt>.</li> |
| |
| </ul> |
| |
| <p><em>Pros:</em> <tt>/trunk</tt> is guaranteed to be stable at all |
| times. The hassle of branching/merging is somewhat rare.</p> |
| |
| <p><em>Cons:</em> Adds a bit of burden to users' daily work: |
| they must compile and test before every commit.</p> |
| |
| |
| <!-- |
| |
| |
| Mapping CVS tasks to SVN tasks |
| ============================== |
| |
| This section is just a re-indexing of topics covered in the book, |
| for people who prefer to learn from the "bottom up" rather than "top down". |
| It shows some common CVS operations, and then the equivalent SVN operation, |
| followed by a link to the book which explains more. |
| |
| |
| * Importing data. |
| |
| * Checking out a working copy. |
| |
| * Seeing your changes. |
| |
| * Undoing your changes. |
| |
| * Resolving a conflict. |
| |
| * Adding binary files. |
| |
| * Activating keyword expansion and/or EOL translation. |
| |
| |
| TAGS: |
| |
| * Creating a tag from a working copy |
| |
| * Creating a remote tag |
| |
| * Seeing all of a project's tags |
| |
| * Comparing two tags |
| |
| * Seeing the logs between two tags |
| |
| * Tweaking a tag |
| |
| |
| BRANCHES: |
| |
| * Creating a branch and switching to it |
| |
| * Finding the beginning of a branch |
| |
| * Merging a branch to trunk, or vice versa |
| |
| --> |
| |
| |
| </body> |
| </html> |