Login | Register
My pages Projects Community openCollabNet

Hacker's Guide to SCPlugin

If you are contributing code to the SCPlugin project, please read this first.

(Shamelessly and lavishly plagiarized from the Hacker's Guide to Subversion)

$LastChangedDate: 2008-04-04 18:12:51 -0700 (Fri, 04 Apr 2008) $

Participating in the community

SCPlugin a true open-source project under a X/MIT-style license. Some developers work for CollabNet, some work for other large companies, and others are simply excellent volunteers who are interested in building an OS X UI to Subversion.

The community exists mainly through mailing lists, and IRC channel, and a Subversion repository. To participate:

Go to http://scplugin.tigris.org/ and

  • Join the "dev", "svn", and "announce" mailing lists. The dev list, dev@scplugin.tigris.org, is where almost all discussion takes place. All development questions should go there, though you might want to check the list archives first. The "svn" list receives automated commit emails.

  • Get a copy of the latest development sources from http://subversion.tigris.org/svn/scplugin/trunk/.
    New development always takes place on trunk. At this time, we do not maintain active maintenance branches for older releases, only tags defining what went into each. All branch work is temporary, experimental work.

  • Hang out with us in the IRC channel #scplugin, on irc.freenode.net.

There are many ways to contribute to the project, either by writing code, or by testing and/or helping to manage the bug database. If you'd like to contribute, then look at:

To submit code, simply send your patches to dev@scplugin.tigris.org. No, wait, first read the rest of this file, then start sending patches to dev@scplugin.tigris.org. :-)

To help manage the issues database, read over the issue summaries, looking and testing for issues that are either invalid, or are duplicates of other issues. Both kinds are very common, the first because bugs often get unknowingly fixed as side effects of other changes in the code, and the second because people sometimes file an issue without noticing that it has already been reported. If you are not sure about an issue, post a question to dev@scplugin.tigris.org.

Another way to help would be to write tests for our existing automated test framework. We use the Xcode standard unit testing framework; see http://scplugin.tigris.org/svn/scplugin/trunk/Documentation/UnitTests.html.

Theory and documentation

  1. Design

    A brief design sketch was written in September 2005, and is a bit out of date. But it still gives a good introduction to SCPlugin's various components.

  2. Build notes

    Directions for building SCPlugin may be found in http://scplugin.tigris.org/svn/scplugin/trunk/INSTALLATION.txt

Implementation language

SCPlugin is written in Objective-C. Mostly.

Most of the SCPlugin code is resident in the SCPluginUIDaemon, which is a Cocoa application, and therefore must be in Objective-C.

The parts that call into Subversion have to deal with the Subversion API, which is a straight C API with some rather elaborate gyrations that really ought to have been done in an object-oriented language, but weren't for portability reasons. Dealing with Subversion also brings us into fleeting contact with APR, which is another C API that wishes it were in C++.

Finally, the stuff in SCFinderPlugin (the part that actually plugs into the Finder) has to talk to Carbon and CoreFoundation, which actually are in C++.

It's fun!

If you're not familiar with Objective-C, here are two good references that compare it to C and C++. They cover pretty much the same ground, but have very different styles, so start with whichever one you like, and if you find its style doesn't suit you, try the other.

From C++ to Objective-C, by Pierre Chatelier
Kind of reference-manual structured. Translated from French, but quite well, and only occasionally awkward.
Objective-C in Wikipedia.
A good aritcle, and like all Wikipedia articles, sometimes made even better because you have access to the authorial debates.

Code to read

Before you can contribute code, you'll need to familiarize yourself with the existing code base and interfaces.

Check out a copy of SCPlugin (anonymously, if you don't yet have an account with commit access) — so you can look at the code.

If you're just browsing around, you can start with the documentation files mentioned above. But if you expect to build the product, then your first stop should be SCPlugin.xcodeproj. Launch that, select the target "Subversion Latest," build, and wait for the dust to settle. This will build a complete copy of Subversion, which is used later in the build of SCPlugin itself. You don't need to rebuild Subversion very often — basically, only when Subversion itself has a new release — but you do need to do it once. Watch dev@scplugin.tigris.org for discussion about upgrading and rebuilding this directory.

Your most important landmark in the code is the Xcode project file, SCPlugin.xcodeproj. You should start your browsing by double-clicking this in Finder. You'll easily spot, in the Groups & Files area, the primary bits described in the DESIGN document:

  • SCFinderPlugin
  • SCPluginUIDaemon

Some others you'll see include:

SCPluginCmd
A debugging aid: accepts a command line argument set much like the standard Subversion command-line tool svn, but passes the request to SCPluginUIDaemon to process.
Source Documentation
Contains the documents described here.
Project Website
The HTML documents published as the web content of http://scplugin.tigris.org/

In the Targets group, get friendly with:

All
The only target you'll ever have to build! Note in particular that this not only builds, but runs the unit tests.
scplugin_svnversion
This little target builds the version string you can see in the About box. One reason to get friendly with it: it's not just totally bullet-proof, depending as it does on svn-mac-build and a lot of details of the file system. Watch for build failures here. Hopefully, the error messages will be clear (if it says it can't find something in svn-mac-build, for example, that probably means you forgot to build in there!).
SCPluginUIDaemonUnitTests
Use this target to run the unit tests on their own, if you need to. Most useful while you're writing tests, since building All also runs the tests.
Subversion Latest
Use this target to build Subversion (in svn-mac-build). You must do this before building SCPlugin itself, but you only need to do it once, and it takes quite a while, so it's not wired up to happen automatically. Just light the blue flash paper and step away.
Doxygen
We haven't built any Doxygen documentation, so you can ignore this one.

Down inside the SCFinderPlugin group, where you might not notice it at first, is

Subversion Wrapper
Code that actually makes calls to the Subversion API. When working in the Subversion Wrapper group, you're working in code very similar to code contained within Subversion itself. At that point, you should take a look over at the Subversion Hacker's Guide, as well as this one, for pointers about dealing with APR and other libraries.

Directory layout

A rough guide to the source tree, as you'll see it in Finder (rather than Xcode):

  • Documentation
    User and Developer documentation.

  • SCFinderPlugin
    The code for the Contextual Menu Plugin

  • SCPluginCmd
    The code for a debugging aid

  • SCPluginUIDaemon
    The code for the daemon that does the real work. Also contains the wrappers for Subversion.

  • build
    Directory created and used by the Xcode build process. The most interesting thing here is build/Development/SCFinderPlugin.plugin, the plugin built by your build (drag this to Contextual Menu Items to install)

  • support
    Contains the wherewithal to build the installer package, notably SCPlugin.pmproj, the Package Maker project file.

  • svn-mac-build
    Contains the Subversion build (contents later copied into the SCPlugin build)

  • tools
    Scripts used during the build process, and sometimes useful at other times.

  • www
    Files automatically published by CEE as the web pages of this project

Coding style

Most of SCPlugin is in Objective-C (not 2.0 — we're not ready to make that transition yet, most devs are still on Tiger). SCFinderPlugin is in plain C, because Finder is (mostly) Carbon. For both languages, we use the default indentation style of Xcode.

Documentation

This section is lifted pretty much intact from the Subversion Hacker's Guide. It's all pretty good advice, but I confess we're not nearly this careful.

One Objective-C note: follow good Objective-C naming styles, so that your method signatures constitute a major part of the documentation. This can cover much of the ground of the "internal" whole-method documentation enjoined below. It's vastly easier to figure out the purpose of a routine named initWindow:withDimensions:fromParentWindow:atLocationX:andLocationY: that a mere "initWindow" with parameters where you have to read the documentation just to learn their names!

Document Everything

Every function, whether public or internal, must start out with a documentation comment that describes what the function does. The documentation should mention every parameter received by the function, every possible return value, and (if not obvious) the conditions under which the function could return an error.

For internal documentation put the parameter names in upper case in the doc string, even when they are not upper case in the actual declaration, so that they stand out to human readers.

For public or semi-public API functions, the doc string should go above the function in the .h file where it is declared; otherwise, it goes above the function definition in the .c file.

For structure types, document each individual member of the structure as well as the structure itself.

For actual source code, internally document chunks of each function, so that someone familiar with SCPlugin can understand the algorithm being implemented. Do not include obvious or overly verbose documentation; the comments should help understanding of the code, not hinder it.

For example:

  /*** How not to document.  Don't do this. ***/

  // Make a foo object. 
  -(Foo*)make_foo_object:arg1, arg2:arg2, (NSAutoreleasePool *)pool:pool)
  {
     /* Create a subpool. */
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

     /* Allocate a foo object from the pool */
     Foo *foo = [[[Foo alloc] autorelease] init];
     ...
  }

Instead, document decent sized chunks of code, like this:

      /* Trasmit the segment (if its within the scope of our concern). */
      SVN_ERR(maybe_crop_and_send_segment(segment, start_rev, end_rev,
                                          receiver, receiver_baton, subpool));

      /* If we've set CURRENT_REV to SVN_INVALID_REVNUM, we're done
         (and didn't ever reach END_REV).  */
      if (! SVN_IS_VALID_REVNUM(current_rev))
        break;

      /* If there's a gap in the history, we need to report as much
         (if the gap is within the scope of our concern). */
      if (segment->range_start - current_rev < 1)
        {
          svn_location_segment_t *gap_segment;
          gap_segment = apr_pcalloc(subpool, sizeof(*gap_segment));
          gap_segment->range_end = segment->range_start - 1;
          gap_segment->range_start = current_rev + 1;
          gap_segment->path = NULL;
          SVN_ERR(maybe_crop_and_send_segment(gap_segment, start_rev, end_rev,
                                              receiver, receiver_baton,
                                              subpool));
        }

Read over the SCPlugin code to get an overview of how documentation looks in practice.

Automated tests

For a description of how to use and add tests to SCPlugin's automated test framework, please read Documentation/UnitTests.html.

Writing test cases before code

Wise words from the Subversion archives:

From: Karl Fogel <kfogel@collab.net>
Subject: writing test cases
To: dev@subversion.tigris.org
Date: Mon, 5 Mar 2001 15:58:46 -0600

Many of us implementing the filesystem interface have now gotten into
the habit of writing the test cases (see fs-test.c) *before* writing
the actual code.  It's really helping us out a lot -- for one thing,
it forces one to define the task precisely in advance, and also it
speedily reveals the bugs in one's first try (and second, and
third...).

I'd like to recommend this practice to everyone.  If you're
implementing an interface, or adding an entirely new feature, or even
just fixing a bug, a test for it is a good idea.  And if you're going
to write the test anyway, you might as well write it first. :-)

Yoshiki Hayashi's been sending test cases with all his patches lately,
which is what inspired me to write this mail to encourage everyone to
do the same.  Having those test cases makes patches easier to examine,
because they show the patch's purpose very clearly.  It's like having
a second log message, one whose accuracy is verified at run-time.

That said, I don't think we want a rigid policy about this, at least
not yet.  If you encounter a bug somewhere in the code, but you only
have time to write a patch with no test case, that's okay -- having
the patch is still useful; someone else can write the test case.

As Subversion gets more complex, though, the automated test suite gets
more crucial, so let's all get in the habit of using it early.

-K

Writing log messages

Every commit needs a log message.

The intended audience for a log message is a developer who is already familiar with SCPlugin, but not necessarily familiar with this particular commit. Usually when someone goes back and reads a change, he no longer has in his head all the context around that change. This is true even if he is the author of the change! All the discussions and mailing list threads and everything else may be forgotten; the only clue to what the change is about comes from the log message and the diff itself. People revisit changes with surprising frequency, too: for example, it might be months after the original commit and now the change is being ported to a maintenance branch.

The log message is the introduction to the change. Start it off with one line indicating the general nature of the change, and follow that with a descriptive paragraph if necessary. However, if the commit is just one simple change to one file, then you can dispense with the general description and simply go straight to the detailed description, in the standard filename-then-symbol format shown below.

Throughout the log message, use full sentences, not sentence fragments. Fragments are more often ambiguous, and it takes only a few more seconds to write out what you mean. Certain fragments like "Doc fix", "New file", or "New function" are acceptable because they are standard idioms, and all further details should appear in the source code.

The log message should name every affected function, variable, macro, makefile target, grammar rule, etc, including the names of symbols that are being removed in this commit. This helps people searching through the logs later. Don't hide names in wildcards, because the globbed portion may be what someone searches for later. For example, this is bad:

   * SCPluginUIDaemon/UserInterface/NewGenericUI.m
     (twirling_baton_*): Removed these obsolete structures.
     (-handle_parser_warning:urgency:): Pass data directly to callees, instead
      of storing in twirling_baton_*.

   * SCPluginUIDaemon/UserInterface/NewGenericUI.h: Fix indentation.

Later on, when someone is trying to figure out what happened to `twirling_baton_fast', they may not find it if they just search for "_fast". A better entry would be:

   * SCPluginUIDaemon/UserInterface/NewGenericUI.m
     (twirling_baton_fast, twirling_baton_slow): Removed these
      obsolete structures. 
     (-handle_parser_warning:urgency:): Pass data directly to callees, instead
      of storing in twirling_baton_*. 

   * SCPluginUIDaemon/UserInterface/NewGenericUI.h: Fix indentation.

The wildcard is okay in the description for '-handle_parser_warning:urgency:', but only because the two structures were mentioned by full name elsewhere in the log entry.

Note how each file gets its own entry prefixed with an "*", and the changes within a file are grouped by symbol, with the symbols listed in parentheses followed by a colon, followed by text describing the change. Please adhere to this format, even when only one file is changed — not only does consistency aid readability, it also allows software to colorize log entries automatically.

Note also that Objective-C methods should have their complete names specified, including the '+/-" class/instance marker and the named parameters. Remember that in Objective-C, it's not at all uncommon to have multiple routines only distinguished by these.

As an exception to the above, if you make exactly the same change in several files, list all the changed files in one entry. For example:

   * SCPluginUIDaemon/UserInterface/NewGenericUI.m,
     SCPluginUIDaemon/UserInterface/NewCommitUI.m:
     Include SCPlugin_private_config.h.

If all the changed files are deep inside the source tree, you can shorten the file name entries by noting the common prefix before the change entries:

   [in SCPluginUIDaemon/UserInterface]

   * dialects/nightingale.c (get_base_pitch): Allow 3/4-tone
     pitch variation to account for trait variability amongst
     isolated populations Erithacus megarhynchos.

   * dialects/gallus_domesticus.c: Remove. Unreliable due to
     extremely low brain-to-body mass ratio.

If your change is related to a specific issue in the issue tracker, then include a string like "issue #N" in the log message, but make sure you still summarize what the change is about. For example, if a patch resolves issue #1729, then the log message might be:

   Fix issue #1729: Don't crash because of a missing file.

   * SCPluginUIDaemon/SCUIDaemonController.m
     (frobnicate_file): Check that file exists before frobnicating.

Try to put related changes together. For example, if you create deletePaths(), deprecating deletePath(), then those two things should be near each other in the log message:

   * SCPluginUIDaemon/SCUIDaemonController+SubversionSupport.m
     (-deletePaths:): New prototype, obsoletes -deletePath:.
     (deletePath:): Deprecate.

For large changes or change groups, group the log entry into paragraphs separated by blank lines. Each paragraph should be a set of changes that accomplishes a single goal, and each group should start with a sentence or two summarizing the change. Truly independent changes should be made in separate commits, of course.

See Crediting for how to give credit to someone else if you are committing their patch, or committing a change they suggested.

One should never need the log entries to understand the current code. If you find yourself writing a significant explanation in the log, you should consider carefully whether your text doesn't actually belong in a comment, alongside the code it explains. Here's an example of doing it right:

   (consume_count): If `count' is unreasonable, return 0 and don't
    advance input pointer.

And then, in `consume_count' in `cplus-dem.c':

   while (isdigit((unsigned char)**type))
     {
       count *= 10;
       count += **type - '0';
       /* A sanity check.  Otherwise a symbol like
         `_Utf390_1__1_9223372036854775807__9223372036854775'
         can cause this function to return a negative value.
         In this case we just consume until the end of the string.  */
      if (count > strlen(*type))
        {
          *type = save;
          return 0;
        }

This is why a new function, for example, needs only a log entry saying "New Function" --- all the details should be in the source.

You can make common-sense exceptions to the need to name everything that was changed. For example, if you have made a change which requires trivial changes throughout the rest of the program (e.g., renaming a popular function), you needn't name all the calling functions affected, you can just say "All callers changed". However, please mention every renamed symbol, both old and new — this is the only way the rename of a particular symbol would be traceable later. See Subversion r20946 for an example of this.

In general, there is a tension between making entries easy to find by searching for identifiers, and wasting time or producing unreadable entries by being exhaustive. Use the above guidelines and your best judgment, and be considerate of your fellow developers. (Also, check the Subversion log to see how others have been writing their log entries.)

Log messages for documentation or translation have somewhat looser guidelines. The requirement to name every symbol obviously does not apply, and if the change is just one more increment in a continuous process such as translation, it's not even necessary to name every file. Just briefly summarize the change, for example: "More work on Malagasy translation." Please write your log messages in English, so everybody involved in the project can understand the changes you made.

Crediting

It is very important to record code contributions in a consistent and parseable way. This allows us to write scripts to figure out who has been actively contributing — and what they have contributed — so we can spot potential new committers quickly.

When committing a patch written by someone else, use "Patch by: " at the beginning of a line to indicate the author:

   Fix issue #1729: Don't crash because of a missing file.

   * SCPluginUIDaemon/SubversionWrapper/SVNWorkingCopy.m
     (-frobnicate_file:): Check that file exists before frobnicating.

   Patch by: J. Random <jrandom@example.com>

If multiple individuals wrote the patch, list them each on a separate line — making sure to start each continuation line with whitespace. Non-committers should be listed by name, if known, and e-mail. Committers may be listed similarly, or by their canonical usernames from COMMITTERS (the leftmost column). Additionally, "me" is an acceptable shorthand for the person actually committing the change.

   Fix issue #1729: Don't crash because of a missing file.

   * SCPluginUIDaemon/SubversionWrapper/SVNWorkingCopy.m
     (-frobnicate_file:): Check that file exists before frobnicating.

   Patch by: J. Random <jrandom@example.com>
             Enrico Caruso <codingtenor@codingtenor.com>
             jcommitter
             me

If someone found the bug or pointed out the problem, but didn't write the patch, indicate their contribution with "Found by: ":

   Fix issue #1729: Don't crash because of a missing file.

   * SCPluginUIDaemon/SubversionWrapper/SVNWorkingCopy.m
     (-frobnicate_file:): Check that file exists before frobnicating.

   Found by: J. Random <jrandom@example.com>

If someone suggested something useful, but didn't write the patch, indicate their contribution with "Suggested by: ":

   Extend the Contribulyzer syntax to distinguish finds from ideas.

   * www/hacking.html (crediting): Adjust accordingly.

   Suggested by: dlr

If someone reviewed the change, use "Review by: " (or "Reviewed by: " if you prefer):

   Fix issue #1729: Don't crash because of a missing file.

   * SCPluginUIDaemon/SubversionWrapper/SVNWorkingCopy.m
     (-frobnicate_file:): Check that file exists before frobnicating.

   Review by: Eagle Eyes <eeyes@example.com>

A field may have multiple lines, and a log message may contain any combination of fields:

   Fix issue #1729: Don't crash because of a missing file.

   * SCPluginUIDaemon/SubversionWrapper/SVNWorkingCopy.m
     (-frobnicate_file:): Check that file exists before frobnicating.

   Patch by: J. Random <jrandom@example.com>
             Enrico Caruso <codingtenor@codingtenor.com>
             me
   Found by: J. Random <jrandom@example.com>
   Review by: Eagle Eyes <eeyes@example.com>
              jcommitter

Further details about a contribution should be listed in a parenthetical aside immediately after the corresponding field. Such an aside always applies to the field right above it; in the following example, the fields have been spaced out for readability, but note that the spacing is optional and not necessary for parseability:

   Fix issue #1729: Don't crash because of a missing file.

   * SCPluginUIDaemon/SubversionWrapper/SVNWorkingCopy.m
     (-frobnicate_file:): Check that file exists before frobnicating.

   Patch by: J. Random <jrandom@example.com>
   (Tweaked by me.)

   Review by: Eagle Eyes <eeyes@example.com>
              jcommitter
   (Eagle Eyes caught an off-by-one-error in the basename extraction.)

Currently, these fields

   Patch by:
   Suggested by:
   Found by:
   Review by:

are the only officially-supported crediting fields (where "supported" means scripts know to look for them). Future fields will probably be of the form "VERB by: ", and from time to time someone may use a field that sounds official but really is not — for example, there are a few instances of "Reported by: ". These are okay, but try to use an official field, or a parenthetical aside, in preference to creating your own. Also, don't use "Reported by: " when the reporter is already recorded in an issue; instead, simply refer to the issue.

Look over SCPlugin's existing log messages to see how to use these fields in practice. This command from the top of a trunk SCPlugin working copy will help:

svn log | svn-mac-build/subversion-1.*/contrib/client-side/search-svnlog.pl "(Patch|Review|Suggested) by: "

Note: The "Approved by: " field seen in some commit messages is totally unrelated to these crediting fields, and is generally not parsed by scripts. It is simply the standard syntax for indicating either who approved a partial committer's commit outside their usual area, or (in the case of merges to release branches) who voted for the change to be merged.

Patch submission guidelines

Mail patches to dev@scplugin.tigris.org, starting the subject line with [PATCH]. This helps our patch manager spot patches right away. For example:

   Subject: [PATCH] fix for rev printing bug in svn status

If the patch addresses a particular issue, include the issue number as well: "[PATCH] issue #1729: ...". Developers who are interested in that particular issue will know to read the mail.

A patch submission should contain one logical change; please don't mix unrelated changes in one submission — send separate emails instead.

Generate the patch using svn diff from the top of a SCPlugin trunk working copy. If the file you're diffing is not under revision control, you can achieve the same effect by using diff -u.

Please include a log message with your patch. A good log message helps potential reviewers understand the changes in your patch, and increases the likelihood that it will be applied. You can put the log message in the body of the email, or at the top of the patch attachment (see below). Either way, it should follow the guidelines given in Writing log messages, and be enclosed in triple square brackets, like so:

   [[[
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.
   ]]]

(The brackets are not actually part of the log message, they're just a way to clearly mark off the log message from its surrounding context.)

If possible, send the patch as an attachment with a mime-type of text/x-diff, text/x-patch, or text/plain. Most people's mailreaders can display those inline, and having the patch as an attachment allows them to extract the patch from the message conveniently. Never send patches in archived or compressed form (e.g., tar, gzip, zip, bzip2), because that prevents people from reviewing the patch directly in their mailreaders (and these days, may even get your patch tossed away by some mail systems!).

If you can't attach the patch with one of these mime-types, or if the patch is very short, then it's okay to include it directly in the body of your message. But watch out: some mail editors munge inline patches by inserting unasked-for line breaks in the middle of long lines. If you think your mail software might do this, then please use an attachment instead.

If the patch implements a new feature, make sure to describe the feature completely in your mail; if the patch fixes a bug, describe the bug in detail and give a reproduction recipe. An exception to these guidelines is when the patch addresses a specific issue in the issues database — in that case, just refer to the issue number in your log message, as described in Writing log messages.

It is normal for patches to undergo several rounds of feedback and change before being applied. Don't be discouraged if your patch is not accepted immediately — it doesn't mean you goofed, it just means that it's a rare patch that doesn't have at least a little room for improvement. After reading people's responses to your patch, make the appropriate changes and resubmit, wait for the next round of feedback, and lather, rinse, repeat, until some committer applies it.

If you don't get a response for a while, and don't see the patch applied, it may just mean that people are really busy. Go ahead and repost, and don't hesitate to point out that you're still waiting for a response. One way to think of it is that patch management is highly parallizable, and we need you to shoulder your share of the management as well as the coding. Every patch needs someone to shepherd it through the process, and the person best qualified to do that is the original submitter.

Filing bugs / issues

This pretty much says it all:

   From: Karl Fogel <kfogel@collab.net>
   Subject: Please ask on the list before filing a new issue.
   To: dev@subversion.tigris.org
   Date: Tue, 30 Jul 2002 10:51:24 (CDT)
   
   Folks, we're getting tons of new issues, which is a Good Thing in
   general, but some of them don't really belong in the issue tracker.
   They're things that would be better solved by a quick conversation
   here on the dev list.  Compilation problems, behavior questions,
   feature ideas that have been discussed before, that sort of thing.
   
   *Please* be more conservative about filing issues.  The issues
   database is physically much more cumbersome than email.  It wastes
   people's time to have conversations in the issues database that should
   be had in email.  (This is not a libel against the issue tracker, it's
   just a result of the fact that the issues database is for permanent
   storage and flow annotation, not for real-time conversation.)
   
   If you encounter a situation where Subversion is clearly behaving
   wrongly, or behaving opposite to what the documentation says, then
   it's okay to file the issue right away (after searching to make sure
   it isn't already filed, of course!).  But if you're
   
      a) Requesting a new feature, or
      b) Having build problems, or
      c) Not sure what the behavior should be, or
      d) Disagreeing with current intended behavior, or
      e) Not TOTALLY sure that others would agree this is a bug, or
      f) For any reason at all not sure this should be filed,
   
   ...then please post to the dev list first.  You'll get a faster
   response, and others won't be forced to use the issues database to
   have the initial real-time conversations.
   
   Nothing is lost this way.  If we eventually conclude that it should be
   in the issue tracker, then we can still file it later, after the
   description and reproduction recipe have been honed on the dev list.
   
   Thank you,
   -Karl

Commit access

There are two types of commit access: full and partial. Full means anywhere in the tree, partial means only in that committer's specific area(s) of expertise. The COMMITTERS file lists all committers, both full and partial, and says the domains for each partial committer.

How full commit access is granted

After someone has successfully contributed a few non-trivial patches, some full committer, usually whoever has reviewed and applied the most patches from that contributor, proposes them for commit access. This proposal is sent only to the other full committers -- the ensuing discussion is private, so that everyone can feel comfortable speaking their minds. Assuming there are no objections, the contributor is granted commit access. The decision is made by consensus; there are no formal rules governing the procedure, though generally if someone strongly objects the access is not offered, or is offered on a provisional basis.

The primary criterion for full commit access is good judgment.

You do not have to be a technical wizard, or demonstrate deep knowledge of the entire codebase, to become a full committer. You just need to know what you don't know. If your patches adhere to the guidelines in this file, adhere to all the usual unquantifiable rules of coding (code should be readable, robust, maintainable, etc.), and respect the Hippocratic Principle of "first, do no harm", then you will probably get commit access pretty quickly. The size, complexity, and quantity of your patches do not matter as much as the degree of care you show in avoiding bugs and minimizing unnecessary impact on the rest of the code. Many full committers are people who have not made major code contributions, but rather lots of small, clean fixes, each of which was an unambiguous improvement to the code. (Of course, this does not mean the project needs a bunch of very trivial patches whose only purpose is to gain commit access; knowing what's worth a patch post and what's not is part of showing good judgement :-) .)

How partial commit access is granted

A full committer sponsors the partial committer. Usually this means the full committer has applied several patches to the same area from the proposed partial committer, and realizes things would be easier if the person were just committing directly. Approval is not required from the full committers; it is assumed that sponsors know what they're doing and will watch the partial committer's first few commits to make sure everything's going smoothly.

Patches submitted by a partial committer may be committed by that committer even if they are outside that person's domain. This requires approval (often expressed as a +1 vote) from at least one full committer. In such a case, the approval should be noted in the log message, like so:

   Approved by: lundblad

Any full committer may offer anyone commit access to an experimental branch at any time. It is not necessary that the experimental branch have a high likelihood of being merged to trunk (although that's always a good goal to aim for). It's just as imporant that the full committer — all the full committers, actually — view such branches as training grounds for new developers, by giving feedback on the commits. The goal of these branches is both to get new code into SCPlugin and to get new developers into the project. See also the section on lightweight branches, and this mail:

   http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=132746
   From: Karl Fogel <kfogel@red-bean.com>
   To: dev@subversion.tigris.org
   Subject: branch liberalization (was: Elego tree conflicts work)
   Date: Tue, 20 Nov 2007 10:49:38 -0800
   Message-Id: <87y7cswy4d.fsf@red-bean.com>

Use lightweight branches

If you're working on a feature or bugfix in stages involving multiple commits, and some of the intermediate stages aren't stable enough to go on trunk, then create a temporary branch in /branches. There's no need to ask — just do it. It's fine to try out experimental ideas in a temporary branch, too. And all the preceding applies to partial as well as full committers.

If you're just using the branch to "checkpoint" your code, and don't feel it's ready for review, please put some sort of notice at the top of the log message, such as:

   *** checkpoint commit -- please don't waste your time reviewing it ***

And if a later commit on that branch should be reviewed, then please supply, in the log message, the appropriate 'svn diff' command, since the diff would likely involve two non-adjacent commits on that branch, and reviewers shouldn't have to spend time figuring out which ones they are.

When you're done with the branch — when you've either merged it to trunk or given up on it — please remember to remove it.

See also the section on partial commit access for our policy on offering commit access to experimental branches.

How to release a distribution

See The SCPlugin Release Procedure.

Release numbering, compatibility, and deprecation

SCPlugin releases are always OS X disk images containing one or more Installer packages. The names of the disk images reflect the release:

SCPlugin uses "MAJOR.MINOR.PATCH" release numbers, with the same guidelines as APR (see http://apr.apache.org/versioning.html), plus a few extensions, described later.

SCPlugin does not use the "even==stable, odd==unstable" convention; any unqualified triplet indicates a stable release:

   1.0.1  -->  first stable patch release of 1.0
   1.1.0  -->  next stable minor release of 1.x after 1.0.x
   1.1.1  -->  first stable patch release of 1.1.x
   1.1.2  -->  second stable patch release of 1.1.x
   1.2.0  -->  next stable minor release after that

Non-stable releases are qualified with "alphaN" or "betaN" suffixes, and release candidates with "-rcN". For example, the prereleases leading to 1.3.7 might look like this:

   SCPlugin-1.3.7-alpha1.dmg
   SCPlugin-1.3.7-alpha2.dmg
   SCPlugin-1.3.7-beta1.dmg
   SCPlugin-1.3.7-beta2.dmg
   SCPlugin-1.3.7-rc1.dmg
   SCPlugin-1.3.7-rc2.dmg
   SCPlugin-1.3.7-rc3.dmg
   SCPlugin-1.3.7.dmg

The version string in the About box includes both the SCPlugin version and the version of Subversion it contains. Non-release versions contain the working copy revision number, to that make that clear:

   SCPlugin 0.7.1, SVN 1.4.6
   SCPlugin 0.7.1 r455M, SVN 1.4.6
   SCPlugin trunk r566M, SVN 1.4.6

When you install SCPlugin-1.3.7-rc1, it still installs as though it were "1.3.7", of course. The qualifiers are metadata on the release; we want each subsequent prerelease release to overwrite the previous one, and the final release to overwrite the last prerelease.

For working copy builds, there is no disk image name to worry about, but the About box still produces special output:

   SCPlugin 0.7.1 r455M, SVN 1.4.6

The version number (0.7.1) is the next version that the development was working towards. The important thing is the working-copy revision number (r455M). This indicates that the build came from a working copy, which is useful in bug reports.

Reuse of release names

If a release or candidate release needs to be quickly re-issued due to some non-code problem (say, a packaging glitch), it's okay to reuse the same name, as long as the disk image hasn't been blessed by signing yet. But if it has been uploaded to the standard distribution area with signatures, or if the re-issue was due to a change in code a user might run, then the old name must be tossed and the next name used.

Stabilizing and maintaining releases

Minor and major number releases go through a stabilization period before release. SCPlugin does not maintain old releases; fixes go into the next maintenance release. To start the release process, we create an "A.B.x" branch based on the latest trunk, for example:

   $ svn cp http://scplugin.tigris.org/svn/scplugin/trunk \
            http://scplugin.tigris.org/svn/scplugin/branches/A.B.x

The stabilization period for a new A.B.0 release allows us to make conservative bugfixes and discover showstopper issues. The stabilization period begins with a release candidate image with the version A.B.0-rc1. Further release candidate images may be made as blocking bugs are fixed.

At the beginning of the final week of the stabilization period, a new release candidate image should be made if there are any changes pending since the last one. The final week of the stabilization period is reserved for critical bugfixes; fixes for minor bugs should be deferred to the A.B.1 release. A critical bug is a non-edge-case crash, a data corruption problem, a major security hole, or something equally serious.

Under some circumstances, the stabilization period will be extended:

  • If a potentially destabilizing change must be made in order to fix a bug, the entire four-week stabilization period is restarted. A potentially destabilizing change is one which could affect many parts of SCPlugin in unpredictable ways, or which involves adding a substantial amount of new code.

  • If a critical bugfix is made during the final week of the stabilization period, the final week is restarted. The final A.B.0 release is always identical to the release candidate made one week before (with the exceptions discussed below).

If there are disagreements over whether a change is potentially destabilizing or over whether a bug is critical, they may be settled with a committer vote.

After the A.B.0 release is out, patch releases (A.B.1, A.B.2, etc.) follow when bugfixes warrant them. Patch releases do not require a four week soak, because only conservative changes go into the line.

Certain kinds of commits can go into A.B.0 without restarting the soak period, or into a later release without affecting the testing schedule or release date:

  • Without voting:

    • Changes to the STATUS file.

    • Documentation file changes, including to the book, README, INSTALL, www/, etc.

    • Changes that are a normal part of release bookkeeping, for example, the steps listed in notes/releases.txt.

    • Changes to dist.sh by, or approved by, the release manager.

    • Changes to message translations in .po files or additions of new .po files.

  • With voting:

    • Anything affecting only tools/, packages/, or bindings/.

    • Doc fixes in core code header or source files.

    • Changes to printed output, such as error and usage messages, as long as format string "%" codes and their args are not touched.

Core code changes, of course, require voting, and restart the soak or test period, since otherwise the change could be undertested.

The voting system for controversies during stabilization works like this:

A change needs at least one +1 vote from a full committer (or partial committer for the involved areas) besides the author, and no vetoes, to go into A.B.x.

If you cast a veto (i.e. -1), please state the reason in the concerns field, and include a url / message-id for the list discussion if any. You can go back and add the link later if the thread isn't available at the time you commit the veto.

Voting +1 on a change doesn't just mean you approve of it in principle. It means you have thoroughly reviewed the change, and find it correct and as nondisruptive as possible. When it is committed to the release branch, the log message will include the names of all who voted for it, as well as the original author and the person making the commit. All of these people are considered equally answerable for bugs.

If you've reviewed a patch, and like it but have some reservations, you can write "+1 (concept)" and then ask questions on the list about your concerns. You can write "+0" if you like the general idea but haven't reviewed the patch carefully. Neither of these votes counts toward the total, but they can be useful for tracking down people who are following the change and might be willing to spend more time on it.

Signing source distribution packages (a.k.a images)

Before a release or release candidate is officially made public, it is made available in a temporary location for committers to test and sign. The point is to have the images tested on more systems than just that of the person who rolled the release. When there are two signatures from full committers for the image file, the release (candidate) can go public.

Signing a image means that you assert certain things about it. When sending your signature (see below), indicate in the mail what steps you've taken to verify that the image is correct.

After having extracted and tested the image, you should sign it using gpg. To do so, use a command like:

    gpg -ba SCPlugin-1.3.0-rc4.dmg

This will result in a file with the same name as the signed file, but with a .asc extension, in the appropriate format for inclusion in the release announcement. Include this file in a mail, typically in reply to the announcement of the unofficial image.