Using the Subversion Client API, Part 2
by Garrett Rooney05/15/2003
In the first
article of this series, we learned how to make basic use of the Subversion client libraries.
We covered why one would want to use the Subversion client libraries
directly instead of simply calling the svn
binary
directly, the basic use of the Apache
Portable Runtime, which Subversion uses as its portability
library, and a few low-level Subversion constructs, including
svn_error_t
and svn_client_ctx_t
. Finally,
we went over the first few functions necessary for making a minimal
Subversion client.
Let's continue with the example begun in the first article, expanding our basic client. The examples in this article will be written for version 0.20.0 of the Subversion API. If you are using an earlier version, you should upgrade. If you are using a later version, you should be aware that there may be some differences in the API, but the concepts should still apply.
Now that you've provided your company's web developers with a
minimalistic Subversion client application, which allows them to
deploy sites directly from your repository onto the web server,
they've become a bit more used to the idea of version control. Some
of them are even using the regular svn
command line
client. The rest, though, are a bit stuck. They still need a way to
interact with the repository to make changes to the sites that are
stored there, but they uncertain about using command line tools.
Fortunately, your newfound knowledge of the Subversion client APIs
will save the day. All you have to do is extend your client to
support a few more features. You've already got
checkout
, status
, and
update
. There are just a few more you'll need to provide
to give them enough functionality to make changes to the sites they're
working on and commit them back into the repository.
First, a note about function targets. Many subversion commands
(and the underlying libsvn_client
functions which
implement them) can target either a working copy or a repository. For
example, you can use svn copy
to copy a file in your
local working copy and then commit that change back to the repository
later, or you can use it to copy a file or directory in the repository
directly. You would do this if you were tagging a particular release
of your software, for example. Generally, the effect you get from a
command that targets the repository directly can be achieved within
the working copy, as long as you follow it up with a commit.
The reason the functions can work on both the repository and the
working copy is efficiency. If you tag a new release of your software
by performing the copy in your checked out working copy, you have to
check out the directory that holds your tags as well as the one that
holds the version you are tagging, which could take up a lot of disk
space. Then when you do the actual copy, the client will need to
write many files out to disk as part of maintaining the working copy
(the contents of all the .svn
directories in the new
directory, as well as the actual files you copy). When you finally
commit, all the changes need to be communicated to the server. Doing
the copy directly in the server saves all this trouble, so it's the
usual way of working with large copies like tags and branches.
For the examples in this article, we use the
libsvn_client
functions on the working copy, but using
them on the repository is essentially the same. It only requires the
addition of a new parameter which Subversion uses to hold the results
of the commit and a callback function inside the client context that
Subversion uses to get the log message for the commit. We'll cover
both when we talk about svn_client_commit
.
So What Did I Change Anyway?
To get your web developers started, all you need to do is give them
the ability to edit a file, verify that the change is what they want,
and commit it back to the repository. The editing part is
easy. Subversion doesn't actually require you to do anything before
making a change, so users can make changes in their favorite editor.
Once they've made a change, use svn_client_diff
to show
them exactly what they are going to be committing to the
repository.
svn_client_diff
takes a number of arguments, but it's really
not that complicated. Here's the function prototype.
svn_error_t *svn_client_diff (
const apr_array_header_t *diff_options,
const char *path1,
const svn_opt_revision_t *revision1,
const char *path2,
const svn_opt_revision_t *revision2,
svn_boolean_t recurse,
svn_boolean_t no_diff_deleted,
apr_file_t *outfile,
apr_file_t *errfile,
svn_client_ctx_t *ctx,
apr_pool_t *pool);
The diff_options
argument is an
apr_array_header_t *
of const char *
command
line arguments to be passed to an external diff command such as GNU
diff
. We can just pass an empty array, since we'll be
using Subversion's internal diff library to produce our diffs. It
doesn't understand any options yet. path1
is the path
(or URL in the repository) for the source file and
revision1
determines which revision of that file to read.
path2
and revision2
determine the
destination file. recurse
determines if the diff should
recurse into the target (if the target is a directory) and
no_diff_deleted
indicates that there should not be any
diff for deleted files. outfile
and errfile
are apr_file_t *
s that will hold the output of the diff
and any errors that occur. The client context is used for
authentication when diffing against a repository.
Here's an example of how to use svn_client_diff
to
find the difference between the version you have in your working copy
and the version you started with:
void
diff_wc_to_working(const char *filename,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_array_header_t *diff_opts = apr_array_make(pool, 0, sizeof (char *));
svn_opt_revision_t rev1 = { 0 }, rev2 = { 0 };
apr_file_t *outfile, *errfile;
svn_error_t *err;
/* the revision we started with. */
rev1.kind = svn_opt_revision_head;
/* to the revision we've got here. */
rev2.kind = svn_opt_revision_working;
/* for your client, you'd probably want to open temp files for this, but for
* our purposes we'll just use stdout and stderr. */
apr_file_open_stdout (&outfile, pool);
apr_file_open_stderr (&errfile, pool);
err = svn_client_diff (diff_opts,
filename, &rev1,
filename, &rev2,
TRUE,
FALSE,
outfile,
errfile,
ctx,
pool);
if (err)
handle_error (err);
}
Oops, I Didn't Mean To Do That
Once the developer can see what changes have been made to the
working copy, a reversion back to unmodified files may be required.
Subversion provides svn_client_revert
to do just that.
svn_client_revert
is pretty simple: you give it the path
to the file or directory in your working copy that you want to revert
and a flag to determine if the revert should recurse into
subdirectories. Then it will revert the current changes. As with
svn_client_status
, you can include a notification
callback in the client context structure to be called for each file
that is reverted. Here's an example:
void
revert_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 ("reverting %s\n", path);
}
void
revert_wc_file (const char *path,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
ctx->notify_func = revert_notification_callback;
svn_error_t *err = svn_client_revert (path, FALSE, ctx, pool);
if (err)
handle_error (err);
}
Pages: 1, 2 |
![](/web/20090216162458im_/http://www.linuxdevcenter.com/images/trans.gif)