LinuxDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


Using the Subversion Client API, Part 2
Pages: 1, 2

So And So Did What?

Now that we've moved into the realm of editing files in the working copy, we'll have to account for how that will interact with svn_client_update. If you have uncommitted changes in the tree and you update, conflicts can occur. When this happens, Subversion will leave three extra versions of the file in your working copy: the base version from which you started, your modified version, and the new version from the repository. The file you had edited will also have conflict markers inserted into it showing where the conflict occurred. Once you have resolved the conflict manually--by removing the conflict markers and leaving the file in its final state--call svn_client_resolve to tell Subversion that the conflict has been resolved. This will remove the other three versions of the file and Subversion will then allow you to commit your changes. svn_client_resolve is quite simple, so let's look at an example.



void
resolve_notification_callback (void *baton,
                               const char *path,
                               svn_wc_notify_action_t action,
                               svn_node_kind_t kind,
                               const char *mime_type,
                               svn_wc_notify_state_t content_state,
                               svn_wc_notify_state_t prop_state,
                               svn_revnum_t revision)
{
  printf ("resolving %s\n", path);
}

void
resolve_conflict (const char *path,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool)
{
  ctx->notify_func = resolve_notification_callback;

  svn_error_t *err = svn_client_resolve (path, FALSE, ctx, pool);
  if (err)
    handle_error (err);
}

And You Thought You Could Never Commit To Anything

Now that you can edit files in your working copy, view diffs, and revert unwanted changes, you'll need to commit the changes to the repository for safekeeping. To do this, call svn_client_commit. As you'd expect, svn_client_commit uses some callback functions and batons from the client context. In addition to the standard notification callback, it uses a log message callback which fetches a log message for the commit from the client application. In the svn command line client, this function starts your $EDITOR and returns what you write there.

The next example assumes you have the log entry before you call svn_client_commit. Pass in your log entry as log_msg_baton and have the callback just return it. To make things fancier, use the tmp_file or commit_items parameters. tmp_file holds the name of a file that contains the log message. This file will be deleted when the commit completes but will remain if the commit fails. The user will not lose the log message. commit_items parameter holds information about each item that is being committed. It's useful for composing a default form for your log message.

svn_error_t *
commit_log_callback (const char **log_msg,
                     const char **tmp_file, 
                     apr_array_header_t *commit_items,
                     void *baton,
                     apr_pool_t *pool)
{
  *tmp_file = NULL;
  *log_msg = baton;

  return SVN_NO_ERROR;
}

With that callback you can now commit a change to the repository.

svn_client_commit introduces a few new concepts. Besides returning a svn_error_t to indicate an error, it also takes a svn_client_commit_info_t ** which it will fill in with the results of the commit. Since this function can take multiple different targets, we pass in an apr_array_header_t * that holds an array of const char * paths to items to commit. The rest of the arguments are typical of libsvn_client: a boolean that controls recursing into directories, a client context, and a pool for memory allocation. Here's an example of how this all works.

void
commit_item (const char *item,
             const char *log_entry,
             svn_client_ctx_t *ctx,
             apr_pool_t *pool)
{
  apr_array_header_t *targets = apr_array_make (pool, 1, sizeof (char *));
  svn_client_commit_info_t *commit_info;
  svn_error_t *err;

  /* yeah, i think this looks kind of nasty too... */
  (*((const char **) apr_array_push (targets))) = item;

  ctx->log_msg_func = commit_log_callback;

  /* this cast is just because log_entry is const and the baton isn't. */
  ctx->log_msg_baton = (void *) log_entry;

  err = svn_client_commit (&commit_info,
                           targets,
                           TRUE,
                           ctx,
                           pool);
  if (err)
    handle_error (err);

  printf ("revision %" SVN_REVNUM_T_FMT " committed at %s by %s\n",
          commit_info->revision,
          commit_info->date,
          commit_info->author);
}

Hey, New Stuff!

This gives your client the ability to make changes to an existing file and commit them to the repository. Eventually they'll want to add new items, so you'll need to use svn_client_add. This is another stereotypical libsvn_client function. It takes a path to an item (file or directory) in the working copy, a flag to indicate if it should recurse, a client context, and a pool. When it succeeds, the item is scheduled for addition during the next commit. Showing an example for this function is pointless, so just look at the one for svn_client_revert and replace revert with add. It's really that simple.

svn_client_add does have a quirk, though. Subversion tries to guess at the MIME type of the file as you add it. While it does a pretty good job of figuring out when something is a text file and when it isn't, it doesn't yet try to determine anything else. This means that if you add a PNG image, the svn:mime-type property will be set to application/octet-stream, which is all well and good for Subversion, but probably isn't what you need. With this MIME type, mod_dav_svn won't know enough to serve the file, so you won't be able to easily view it in a web browser. To make that work, you need to use svn_client_propset to set the svn:mime-type to something more appropriate (image/png in this case). Here's some example code that shows how to do that:

void
set_mime_type_to_png (const char *target,
                      apr_pool_t *pool)
{
  static const svn_string_t propval = { "image/png", 10 };

  svn_error_t *err = svn_client_propset ("svn:mime-type",
                                         &propval,
                                         target,
                                         FALSE,
                                         pool);
  if (err)
    handle_error (err);
}

Just Get Rid Of It!

Eventually your user is going to want to remove a file from the repository, so you'll need to use svn_client_delete. Again, this function can work either on a local working copy or a (possibly remote) repository. Its signature resembles that of svn_client_commit. If you're using it on a repository directly, pass it a svn_client_commit_info_t **, to get back information about the commit it performs and the url of the item in the repository. The log message callback and baton in the client context will be used to get the log message for the commit, and the context's authentication baton will be used to authenticate.

If you're deleting an item from a working copy, pass the path to the item on disk. You can also pass an svn_wc_adm_access_t *, in which case Subversion will use its existing directory lock, or NULL to open a new lock. force is a flag to indicate that Subversion should delete the item even if it is locally modified or unversioned, which normally results in an error. Let's take a look at how you would use svn_client_delete to schedule a file for deletion.

void
delete_item (const char *target,
             svn_client_ctx_t *ctx,
             apr_pool_t *pool)
{
  svn_error_t *err = svn_client_delete (NULL,   /* this isn't a commit */
                                        target,
                                        NULL,   /* let svn open a new lock */
                                        FALSE,  /* don't force it */
                                        ctx,
                                        pool);
  if (err)
    handle_error (err);
}

No, Put It Over There!

The remaining, relevant libsvn_client functions are svn_client_copy and svn_client_move. Since the ability to rename a file while keeping its revision history intact is one of Subversion's selling points over CVS, a good client needs this feature. Both functions have the same signature. They take as arguments a svn_client_commit_info_t ** (used to get information about the commit that is performed if you use them on the repository directly, just like in svn_client_commit); a src_path and src_revision, which identify the path (or url) for the source file and its revision; a dst_path, which indicates the destination path or url; an svn_wc_adm_access_t * (which can be NULL, like in svn_client_delete); and, finally, a client context and a pool. Let's see one last example, which copies a file within a working copy. This could just as easily move a file using svn_client_move, since they are the same from the point of view of the calling code.

void
copy_file (const char *source,
           const char *dest,
           svn_client_ctx_t *ctx,
           apr_pool_t *pool)
{
  svn_opt_revision_t source_rev = { 0 };
  svn_error_t *err;

  err = svn_client_copy (NULL,
                         source,
                         &source_rev,
                         dest,
                         NULL,
                         ctx,
                         pool);
  if (err)
    handle_error (err);
}

Conclusion

You're probably starting to notice that all the libsvn_client functions feel pretty similar. That's intentional. They reuse the same patterns. Once you've mastered one function that commits a change to the repository, you'll be able to use the rest with little trouble.

Even though we've gone over most of the functions in the libsvn_client API, your client isn't perfectly complete. There are still a number of interesting functions left, and your users will eventually clamor for them. The source code to the existing clients (especially the command line client distributed with the Subversion source tree) is your the best guide, along with the svn_client.h header file. Once you figure out what else you'd like your client to do, dig in and get to hacking.

Garrett Rooney is a software developer at FactSet Research Systems, where he works on real-time market data.


Return to ONLamp.com.




Linux Online Certification

Linux/Unix System Administration Certificate Series
Linux/Unix System Administration Certificate Series — This course series targets both beginning and intermediate Linux/Unix users who want to acquire advanced system administration skills, and to back those skills up with a Certificate from the University of Illinois Office of Continuing Education.

Enroll today!


Linux Resources
  • Linux Online
  • The Linux FAQ
  • linux.java.net
  • Linux Kernel Archives
  • Kernel Traffic
  • DistroWatch.com


  • Sponsored by: