svn-populate-node-origins-index.c [plain text]
#include "svn_cmdline.h"
#include "svn_error.h"
#include "svn_fs.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "svn_repos.h"
#include "svn_utf.h"
#define NL APR_EOL_STR
static const char *usage_summary =
"Crawl the Subversion repository located at REPOS-PATH in an attempt to" NL
"populate that repository's index of node origins. " NL
"" NL
"The node origins index is new as of Subversion 1.5, and behaves as a" NL
"cache to vastly speed up certain history-querying operations. For" NL
"compatibility with repositories created with pre-1.5 versions of" NL
"Subversion, Subversion will gracefully handle cache misses by doing a" NL
"brute-force calculation of the query answer and lazily populating the" NL
"index with answers it calculates. Unfortunately, calculating that" NL
"information using the brute-force method (instead of having the" NL
"information appear in the index organically) can be very costly." NL
"" NL
"This tool triggers the lazy index population logic built into" NL
"Subversion in a fashion far more efficient than is likely to happen" NL
"during typical repository usage. It can be run while the repository" NL
"is online, too, without interrupting normal Subversion activities." NL;
static void
usage_maybe_with_err(const char *progname, const char *err_msg)
{
FILE *out;
out = err_msg ? stderr : stdout;
fprintf(out, "Usage: %s REPOS-PATH\n\n%s", progname, usage_summary);
if (err_msg)
fprintf(out, "\nERROR: %s\n", err_msg);
}
static svn_error_t *
index_revision_adds(int *count, svn_fs_t *fs,
svn_revnum_t revision, apr_pool_t *pool)
{
svn_fs_root_t *root;
apr_pool_t *subpool = svn_pool_create(pool);
svn_fs_path_change_iterator_t *iterator;
svn_fs_path_change3_t *change;
*count = 0;
SVN_ERR(svn_fs_revision_root(&root, fs, revision, pool));
SVN_ERR(svn_fs_paths_changed3(&iterator, root, pool, subpool));
SVN_ERR(svn_fs_path_change_get(&change, iterator));
while (change)
{
svn_pool_clear(subpool);
if ((change->change_kind == svn_fs_path_change_add)
|| (change->change_kind == svn_fs_path_change_replace))
{
if (! (change->copyfrom_path
&& SVN_IS_VALID_REVNUM(change->copyfrom_rev)))
{
svn_revnum_t origin;
SVN_ERR(svn_fs_node_origin_rev(&origin, root,
change->path.data, subpool));
(*count)++;
}
}
SVN_ERR(svn_fs_path_change_get(&change, iterator));
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
build_index(const char *repos_path, apr_pool_t *pool)
{
svn_repos_t *repos;
svn_fs_t *fs;
svn_revnum_t youngest_rev, i;
size_t slotsize;
const char *progress_fmt;
apr_pool_t *subpool;
SVN_ERR(svn_repos_open3(&repos, repos_path, NULL, pool, pool));
fs = svn_repos_fs(repos);
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
slotsize = strlen(apr_ltoa(pool, youngest_rev));
progress_fmt = apr_psprintf
(pool,
"[%%%" APR_SIZE_T_FMT "ld"
"/%%%" APR_SIZE_T_FMT "ld] "
"Found %%d new lines of history."
"\n", slotsize, slotsize);
subpool = svn_pool_create(pool);
for (i = 0; i < youngest_rev; i++)
{
int count;
svn_pool_clear(subpool);
SVN_ERR(index_revision_adds(&count, fs, i + 1, subpool));
printf(progress_fmt, i + 1, youngest_rev, count);
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
int
main(int argc, const char **argv)
{
apr_pool_t *pool;
svn_error_t *err = SVN_NO_ERROR;
const char *repos_path;
if (svn_cmdline_init(argv[0], stderr) == EXIT_FAILURE)
return EXIT_FAILURE;
pool = svn_pool_create(NULL);
if (argc <= 1)
{
usage_maybe_with_err(argv[0], "Not enough arguments.");
goto cleanup;
}
if ((err = svn_utf_cstring_to_utf8(&repos_path, argv[1], pool)))
goto cleanup;
repos_path = svn_dirent_internal_style(repos_path, pool);
repos_path = svn_dirent_canonicalize(repos_path, pool);
if ((err = build_index(repos_path, pool)))
goto cleanup;
cleanup:
svn_pool_destroy(pool);
if (err)
{
svn_handle_error2(err, stderr, FALSE,
"svn-populate-node-origins-index: ");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}