📄 main.c
字号:
/*
* main.c: Subversion server inspection tool.
*
* ====================================================================
* Copyright (c) 2000-2004 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 <assert.h>
#include <apr_general.h>
#include <apr_pools.h>
#include <apr_time.h>
#include <apr_thread_proc.h>
#include <apr_file_io.h>
#include <apr_signal.h>
#define APR_WANT_STDIO
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include "svn_cmdline.h"
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_repos.h"
#include "svn_fs.h"
#include "svn_repos.h"
#include "svn_time.h"
#include "svn_utf.h"
#include "svn_subst.h"
#include "svn_opt.h"
#include "svn_props.h"
#include "svn_diff.h"
#include "svn_private_config.h"
/*** Some convenience macros and types. ***/
/* Option handling. */
static svn_opt_subcommand_t
subcommand_author,
subcommand_cat,
subcommand_changed,
subcommand_date,
subcommand_diff,
subcommand_dirschanged,
subcommand_help,
subcommand_history,
subcommand_info,
subcommand_log,
subcommand_pget,
subcommand_plist,
subcommand_tree,
subcommand_uuid,
subcommand_youngest;
/* Option codes and descriptions. */
enum
{
svnlook__version = SVN_OPT_FIRST_LONGOPT_ID,
svnlook__show_ids,
svnlook__no_diff_deleted
};
/*
* This must not have more than SVN_OPT_MAX_OPTIONS entries; if you
* need more, increase that limit first.
*
* The entire list must be terminated with an entry of nulls.
*/
static const apr_getopt_option_t options_table[] =
{
{"help", 'h', 0,
N_("show help on a subcommand")},
{NULL, '?', 0,
N_("show help on a subcommand")},
{"version", svnlook__version, 0,
N_("show version information")},
{"revision", 'r', 1,
N_("specify revision number ARG")},
{"transaction", 't', 1,
N_("specify transaction name ARG")},
{"verbose", 'v', 0,
N_("be verbose")},
{"show-ids", svnlook__show_ids, 0,
N_("show node revision ids for each path")},
{"no-diff-deleted", svnlook__no_diff_deleted, 0,
N_("do not print differences for deleted files")},
{0, 0, 0, 0}
};
/* Array of available subcommands.
* The entire list must be terminated with an entry of nulls.
*/
static const svn_opt_subcommand_desc_t cmd_table[] =
{
{"author", subcommand_author, {0},
N_("usage: svnlook author REPOS_PATH\n\n"
"Print the author.\n"),
{'r', 't'} },
{"cat", subcommand_cat, {0},
N_("usage: svnlook cat REPOS_PATH FILE_PATH\n\n"
"Print the contents of a file. Leading '/' on FILE_PATH is "
"optional.\n"),
{'r', 't'} },
{"changed", subcommand_changed, {0},
N_("usage: svnlook changed REPOS_PATH\n\n"
"Print the paths that were changed.\n"),
{'r', 't'} },
{"date", subcommand_date, {0},
N_("usage: svnlook date REPOS_PATH\n\n"
"Print the datestamp.\n"),
{'r', 't'} },
{"diff", subcommand_diff, {0},
N_("usage: svnlook diff REPOS_PATH\n\n"
"Print GNU-style diffs of changed files and properties.\n"),
{'r', 't', svnlook__no_diff_deleted} },
{"dirs-changed", subcommand_dirschanged, {0},
N_("usage: svnlook dirs-changed REPOS_PATH\n\n"
"Print the directories that were themselves changed (property edits)\n"
"or whose file children were changed.\n"),
{'r', 't'} },
{"help", subcommand_help, {"?", "h"},
N_("usage: svnlook help [SUBCOMMAND...]\n\n"
"Describe the usage of this program or its subcommands.\n"),
{svnlook__version} },
{"history", subcommand_history, {0},
N_("usage: svnlook history REPOS_PATH [PATH_IN_REPOS]\n\n"
"Print information about the history of a path in the repository (or\n"
"the root directory if no path is supplied).\n"),
{'r', svnlook__show_ids} },
{"info", subcommand_info, {0},
N_("usage: svnlook info REPOS_PATH\n\n"
"Print the author, datestamp, log message size, and log message.\n"),
{'r', 't'} },
{"log", subcommand_log, {0},
N_("usage: svnlook log REPOS_PATH\n\n"
"Print the log message.\n"),
{'r', 't'} },
{"propget", subcommand_pget, {"pget", "pg"},
N_("usage: svnlook propget REPOS_PATH PROPNAME PATH_IN_REPOS\n\n"
"Print the raw value of a property on a path in the repository.\n"),
{'r', 't'} },
{"proplist", subcommand_plist, {"plist", "pl"},
N_("usage: svnlook proplist REPOS_PATH PATH_IN_REPOS\n\n"
"List the properties of a path in the repository.\n"
"With -v, show the property values too.\n"),
{'r', 't', 'v'} },
{"tree", subcommand_tree, {0},
N_("usage: svnlook tree REPOS_PATH [PATH_IN_REPOS]\n\n"
"Print the tree, starting at PATH_IN_REPOS (if supplied, at the root\n"
"of the tree otherwise), optionally showing node revision ids.\n"),
{'r', 't', svnlook__show_ids} },
{"uuid", subcommand_uuid, {0},
N_("usage: svnlook uuid REPOS_PATH\n\n"
"Print the repository's UUID.\n"),
{0} },
{"youngest", subcommand_youngest, {0},
N_("usage: svnlook youngest REPOS_PATH\n\n"
"Print the youngest revision number.\n"),
{0} },
{ NULL, NULL, {0}, NULL, {0} }
};
/* Baton for passing option/argument state to a subcommand function. */
struct svnlook_opt_state
{
const char *repos_path; /* 'arg0' is always the path to the repository. */
const char *arg1; /* Usually an fs path, a propname, or NULL. */
const char *arg2; /* Usually an fs path or NULL. */
svn_revnum_t rev;
const char *txn;
svn_boolean_t version; /* --version */
svn_boolean_t show_ids; /* --show-ids */
svn_boolean_t help; /* --help */
svn_boolean_t no_diff_deleted; /* --no-diff-deleted */
svn_boolean_t verbose; /* --verbose */
};
typedef struct svnlook_ctxt_t
{
svn_repos_t *repos;
svn_fs_t *fs;
svn_boolean_t is_revision;
svn_boolean_t show_ids;
svn_boolean_t no_diff_deleted;
svn_revnum_t rev_id;
svn_fs_txn_t *txn;
const char *txn_name /* UTF-8! */;
} svnlook_ctxt_t;
/* A flag to see if we've been cancelled by the client or not. */
static volatile sig_atomic_t cancelled = FALSE;
/*** Helper functions. ***/
/* A signal handler to support cancellation. */
static void
signal_handler (int signum)
{
apr_signal (signum, SIG_IGN);
cancelled = TRUE;
}
/* Our cancellation callback. */
static svn_error_t *
check_cancel (void *baton)
{
if (cancelled)
return svn_error_create (SVN_ERR_CANCELLED, NULL, "Caught signal");
else
return SVN_NO_ERROR;
}
/* Version compatibility check */
static svn_error_t *
check_lib_versions (void)
{
static const svn_version_checklist_t checklist[] =
{
{ "svn_subr", svn_subr_version },
{ "svn_repos", svn_repos_version },
{ "svn_fs", svn_fs_version },
{ "svn_delta", svn_delta_version },
{ "svn_diff", svn_diff_version },
{ NULL, NULL }
};
SVN_VERSION_DEFINE (my_version);
return svn_ver_check_list (&my_version, checklist);
}
/* Get revision or transaction property PROP_NAME for the revision or
transaction specified in C, allocating in in POOL and placing it in
*PROP_VALUE. */
static svn_error_t *
get_property (svn_string_t **prop_value,
svnlook_ctxt_t *c,
const char *prop_name,
apr_pool_t *pool)
{
svn_string_t *raw_value;
/* Fetch transaction property... */
if (! c->is_revision)
SVN_ERR (svn_fs_txn_prop (&raw_value, c->txn, prop_name, pool));
/* ...or revision property -- it's your call. */
else
SVN_ERR (svn_fs_revision_prop (&raw_value, c->fs, c->rev_id,
prop_name, pool));
*prop_value = raw_value;
return SVN_NO_ERROR;
}
static svn_error_t *
get_root (svn_fs_root_t **root,
svnlook_ctxt_t *c,
apr_pool_t *pool)
{
/* Open up the appropriate root (revision or transaction). */
if (c->is_revision)
{
/* If we didn't get a valid revision number, we'll look at the
youngest revision. */
if (! SVN_IS_VALID_REVNUM (c->rev_id))
SVN_ERR (svn_fs_youngest_rev (&(c->rev_id), c->fs, pool));
SVN_ERR (svn_fs_revision_root (root, c->fs, c->rev_id, pool));
}
else
{
SVN_ERR (svn_fs_txn_root (root, c->txn, pool));
}
return SVN_NO_ERROR;
}
/*** Tree Routines ***/
/* Generate a generic delta tree. */
static svn_error_t *
generate_delta_tree (svn_repos_node_t **tree,
svn_repos_t *repos,
svn_fs_root_t *root,
svn_revnum_t base_rev,
svn_boolean_t use_copy_history,
apr_pool_t *pool)
{
svn_fs_root_t *base_root;
const svn_delta_editor_t *editor;
void *edit_baton;
apr_pool_t *edit_pool = svn_pool_create (pool);
svn_fs_t *fs = svn_repos_fs (repos);
/* Get the base root. */
SVN_ERR (svn_fs_revision_root (&base_root, fs, base_rev, pool));
/* Request our editor. */
SVN_ERR (svn_repos_node_editor (&editor, &edit_baton, repos,
base_root, root, pool, edit_pool));
/* Drive our editor. */
SVN_ERR (svn_repos_replay (root, editor, edit_baton, edit_pool));
/* Return the tree we just built. */
*tree = svn_repos_node_from_baton (edit_baton);
svn_pool_destroy (edit_pool);
return SVN_NO_ERROR;
}
/*** Tree Printing Routines ***/
/* Recursively print only directory nodes that either a) have property
mods, or b) contains files that have changed. */
static svn_error_t *
print_dirs_changed_tree (svn_repos_node_t *node,
const char *path /* UTF-8! */,
apr_pool_t *pool)
{
svn_repos_node_t *tmp_node;
int print_me = 0;
const char *full_path;
apr_pool_t *subpool;
SVN_ERR (check_cancel (NULL));
if (! node)
return SVN_NO_ERROR;
/* Not a directory? We're not interested. */
if (node->kind != svn_node_dir)
return SVN_NO_ERROR;
/* Got prop mods? Excellent. */
if (node->prop_mod)
print_me = 1;
if (! print_me)
{
/* Fly through the list of children, checking for modified files. */
tmp_node = node->child;
if (tmp_node)
{
if ((tmp_node->kind == svn_node_file)
|| (tmp_node->text_mod)
|| (tmp_node->action == 'A')
|| (tmp_node->action == 'D'))
{
print_me = 1;
}
while (tmp_node->sibling && (! print_me ))
{
tmp_node = tmp_node->sibling;
if ((tmp_node->kind == svn_node_file)
|| (tmp_node->text_mod)
|| (tmp_node->action == 'A')
|| (tmp_node->action == 'D'))
{
print_me = 1;
}
}
}
}
/* Print the node if it qualifies. */
if (print_me)
{
SVN_ERR (svn_cmdline_printf (pool, "%s/\n", path));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -