blob: 95f3b32652b8fffef6c1de5f5541be22e5d47f94 [file] [log] [blame]
/*
* ====================================================================
* Copyright (c) 2003 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include "svn_pools.h"
#include "svn_client.h"
#include "svn_utf.h"
#include "svn_path.h"
#include <apr_tables.h>
/* The svnversion program uses svn_client_status to produce a compact
* "version number" for the Subversion working copy. The program takes one
* or two arguments, the first is the path to the working copy, the second
* is the trailing portion of the trunk URL. The version number is written
* to standard output. Here is an example
*
* $ svnversion . /repos/svn/trunk
* 4168
*
* The version number will be a single number if the working copy is single
* revision, unmodified, not switched and with an URL that matches the
* trunk URL argument. If the working copy is unusual the version number
* will be more coplex:
*
* 4123:4168 a mixed revision working copy
* 4168M a modified working copy
* 4123S a switched working copy
* 4123:4168MS a mixed revision, modified, switched working copy
*
* If invoked on a directory that is not a working copy, an exported
* directory say, the program will output "exported".
*
* Why is this not an svn subcommand? I have this vague idea that it could
* be run as part of the build process, with the output embedded in the svn
* program. Obviously we don't want to have to run svn when building svn.
* We could always put this into libsvn_client and share it between
* svnversion and svn.
*/
int
main(int argc, char *argv[])
{
const char *wc_path;
apr_pool_t *pool;
apr_hash_t *status_hash;
apr_hash_index_t *hi;
svn_revnum_t youngest;
svn_boolean_t switched = FALSE, modified = FALSE;
svn_revnum_t min_revnum = SVN_INVALID_REVNUM, max_revnum = SVN_INVALID_REVNUM;
const svn_wc_status_t *status;
int wc_format;
svn_client_ctx_t ctx = { 0 };
if (argc != 2 && argc != 3)
{
fprintf(stderr, "usage: svnversion wc_path [trail_url]\n");
return EXIT_FAILURE;
}
/* Initialize the app. */
if (svn_cmdline_init ("svnversion", stderr) != EXIT_SUCCESS)
return EXIT_FAILURE;
/* Create our top-level pool. */
pool = svn_pool_create (NULL);
ctx.config = apr_hash_make (pool);
SVN_INT_ERR (svn_utf_cstring_to_utf8 (&wc_path, argv[1], NULL, pool));
wc_path = svn_path_internal_style (wc_path, pool);
SVN_INT_ERR (svn_wc_check_wc (wc_path, &wc_format, pool));
if (! wc_format)
{
svn_node_kind_t kind;
SVN_INT_ERR(svn_io_check_path (wc_path, &kind, pool));
if (kind == svn_node_dir)
{
printf ("exported\n");
svn_pool_destroy (pool);
return EXIT_SUCCESS;
}
else
{
fprintf (stderr, "'%s' not versioned, and not exported\n", wc_path);
svn_pool_destroy (pool);
return EXIT_FAILURE;
}
}
SVN_INT_ERR (svn_client_status (&status_hash, &youngest, wc_path, TRUE, TRUE,
FALSE, FALSE, &ctx, pool));
for (hi = apr_hash_first (pool, status_hash); hi; hi = apr_hash_next (hi))
{
void *val;
apr_hash_this (hi, NULL, NULL, &val);
status = val;
if (!status->entry)
continue;
/* Added files have a revision of no interest */
if (status->text_status != svn_wc_status_added)
{
if (min_revnum == SVN_INVALID_REVNUM
|| status->entry->revision < min_revnum)
min_revnum = status->entry->revision;
if (max_revnum == SVN_INVALID_REVNUM
|| status->entry->revision > max_revnum)
max_revnum = status->entry->revision;
}
switched |= status->switched;
modified |= (status->text_status != svn_wc_status_normal);
modified |= (status->prop_status != svn_wc_status_normal
&& status->prop_status != svn_wc_status_none);
}
/* If the trailing part of the URL of the working copy directory does not
match the given trailing URL then the whole working copy is
switched. */
if (! switched && argc == 3)
{
const char *trail_url;
SVN_INT_ERR (svn_utf_cstring_to_utf8 (&trail_url, argv[2], NULL, pool));
status = apr_hash_get (status_hash, wc_path, APR_HASH_KEY_STRING);
if (! status)
switched = TRUE;
else if (status->entry)
{
apr_size_t len1 = strlen (trail_url);
apr_size_t len2 = strlen (status->entry->url);
if (len1 > len2
|| strcmp (status->entry->url + len2 - len1, trail_url))
switched = TRUE;
}
}
printf ("%" SVN_REVNUM_T_FMT, min_revnum);
if (min_revnum != max_revnum)
printf (":%" SVN_REVNUM_T_FMT, max_revnum);
if (modified)
fputs ("M", stdout);
if (switched)
fputs ("S", stdout);
fputs ("\n", stdout);
svn_pool_destroy (pool);
return EXIT_SUCCESS;
}