📄 log.c
字号:
{
svn_string_t value;
const char *propname, *propval, *path;
if (strcmp (name, SVN_WC_ENTRY_THIS_DIR) == 0)
path = svn_wc_adm_access_path (loggy->adm_access);
else
path = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
name, loggy->pool);
propname = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_PROPNAME, atts);
propval = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_PROPVAL, atts);
if (propval)
{
value.data = propval;
value.len = strlen (propval);
}
return svn_wc__wcprop_set (propname, propval ? &value : NULL,
path, loggy->adm_access, loggy->pool);
}
static void
start_handler (void *userData, const char *eltname, const char **atts)
{
svn_error_t *err = SVN_NO_ERROR;
struct log_runner *loggy = userData;
/* All elements use the `name' attribute, so grab it now. */
const char *name = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_NAME, atts);
/* Clear the per-log-item pool. */
svn_pool_clear (loggy->pool);
if (strcmp (eltname, "wc-log") == 0) /* ignore expat pacifier */
return;
else if (! name)
{
signal_error
(loggy, svn_error_createf
(pick_error_code (loggy), NULL,
_("Log entry missing 'name' attribute (entry '%s' "
"for directory '%s')"),
eltname, svn_wc_adm_access_path (loggy->adm_access)));
return;
}
/* Increment the top-level element count before processing any commands. */
loggy->count += 1;
/* Dispatch. */
if (strcmp (eltname, SVN_WC__LOG_MODIFY_ENTRY) == 0) {
err = log_do_modify_entry (loggy, name, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_DELETE_ENTRY) == 0) {
err = log_do_delete_entry (loggy, name);
}
else if (strcmp (eltname, SVN_WC__LOG_COMMITTED) == 0) {
err = log_do_committed (loggy, name, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_MODIFY_WCPROP) == 0) {
err = log_do_modify_wcprop (loggy, name, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_RM) == 0) {
err = log_do_rm (loggy, name);
}
else if (strcmp (eltname, SVN_WC__LOG_MERGE) == 0) {
err = log_do_merge (loggy, name, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_MV) == 0) {
err = log_do_file_xfer (loggy, name, svn_wc__xfer_mv, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_CP) == 0) {
err = log_do_file_xfer (loggy, name, svn_wc__xfer_cp, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_CP_AND_TRANSLATE) == 0) {
err = log_do_file_xfer (loggy, name,svn_wc__xfer_cp_and_translate, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_CP_AND_DETRANSLATE) == 0) {
err = log_do_file_xfer (loggy, name,svn_wc__xfer_cp_and_detranslate, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_APPEND) == 0) {
err = log_do_file_xfer (loggy, name, svn_wc__xfer_append, atts);
}
else if (strcmp (eltname, SVN_WC__LOG_READONLY) == 0) {
err = log_do_file_readonly (loggy, name);
}
else if (strcmp (eltname, SVN_WC__LOG_SET_TIMESTAMP) == 0) {
err = log_do_file_timestamp (loggy, name, atts);
}
else
{
signal_error
(loggy, svn_error_createf
(pick_error_code (loggy), NULL,
_("Unrecognized logfile element '%s' in '%s'"),
eltname, svn_wc_adm_access_path (loggy->adm_access)));
return;
}
if (err)
signal_error
(loggy, svn_error_createf
(pick_error_code (loggy), err,
_("Error processing command '%s' in '%s'"),
eltname, svn_wc_adm_access_path (loggy->adm_access)));
return;
}
/* Process the "KILLME" file in ADM_ACCESS
*/
static svn_error_t *
handle_killme (svn_wc_adm_access_t *adm_access,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const svn_wc_entry_t *thisdir_entry, *parent_entry;
svn_wc_entry_t tmp_entry;
svn_error_t *err;
SVN_ERR (svn_wc_entry (&thisdir_entry,
svn_wc_adm_access_path (adm_access), adm_access,
FALSE, pool));
/* Blow away the entire directory, and all those below it too. */
err = svn_wc_remove_from_revision_control (adm_access,
SVN_WC_ENTRY_THIS_DIR,
TRUE, /* destroy */
FALSE, /* no instant err */
cancel_func, cancel_baton,
pool);
if (err && err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
return err;
svn_error_clear (err);
/* If revnum of this dir is greater than parent's revnum, then
recreate 'deleted' entry in parent. */
{
const char *parent, *bname;
svn_wc_adm_access_t *parent_access;
svn_path_split (svn_wc_adm_access_path (adm_access), &parent, &bname, pool);
SVN_ERR (svn_wc_adm_retrieve (&parent_access, adm_access, parent, pool));
SVN_ERR (svn_wc_entry (&parent_entry, parent, parent_access, FALSE, pool));
if (thisdir_entry->revision > parent_entry->revision)
{
tmp_entry.kind = svn_node_dir;
tmp_entry.deleted = TRUE;
tmp_entry.revision = thisdir_entry->revision;
SVN_ERR (svn_wc__entry_modify (parent_access, bname, &tmp_entry,
SVN_WC__ENTRY_MODIFY_REVISION
| SVN_WC__ENTRY_MODIFY_KIND
| SVN_WC__ENTRY_MODIFY_DELETED,
TRUE, pool));
}
}
return SVN_NO_ERROR;
}
/*** Using the parser to run the log file. ***/
/* Determine the log file that should be used for a given number. */
const char *
svn_wc__logfile_path (int log_number,
apr_pool_t *pool)
{
return apr_psprintf (pool, SVN_WC__ADM_LOG "%s",
(log_number == 0) ? ""
: apr_psprintf (pool, ".%d", log_number));
}
/* Run a sequence of log files. */
svn_error_t *
svn_wc__run_log (svn_wc_adm_access_t *adm_access,
const char *diff3_cmd,
apr_pool_t *pool)
{
svn_error_t *err, *err2;
svn_xml_parser_t *parser;
struct log_runner *loggy = apr_pcalloc (pool, sizeof (*loggy));
char buf[BUFSIZ];
apr_size_t buf_len;
apr_file_t *f = NULL;
const char *logfile_path;
int log_number;
apr_pool_t *iterpool = svn_pool_create (pool);
/* kff todo: use the tag-making functions here, now. */
const char *log_start
= "<wc-log xmlns=\"http://subversion.tigris.org/xmlns\">\n";
const char *log_end
= "</wc-log>\n";
parser = svn_xml_make_parser (loggy, start_handler, NULL, NULL, pool);
loggy->adm_access = adm_access;
loggy->pool = svn_pool_create (pool);
loggy->parser = parser;
loggy->entries_modified = FALSE;
loggy->diff3_cmd = diff3_cmd;
loggy->count = 0;
/* Expat wants everything wrapped in a top-level form, so start with
a ghost open tag. */
SVN_ERR (svn_xml_parse (parser, log_start, strlen (log_start), 0));
for (log_number = 0; ; log_number++)
{
svn_pool_clear (iterpool);
logfile_path = svn_wc__logfile_path (log_number, iterpool);
/* Parse the log file's contents. */
err = svn_wc__open_adm_file (&f, svn_wc_adm_access_path (adm_access),
logfile_path, APR_READ, iterpool);
if (err)
{
if (APR_STATUS_IS_ENOENT (err->apr_err))
{
svn_error_clear (err);
break;
}
else
{
SVN_ERR_W (err, _("Couldn't open log"));
}
}
do {
buf_len = sizeof (buf);
err = svn_io_file_read (f, buf, &buf_len, iterpool);
if (err && !APR_STATUS_IS_EOF(err->apr_err))
return svn_error_createf
(err->apr_err, err,
_("Error reading administrative log file in '%s'"),
svn_wc_adm_access_path (adm_access));
err2 = svn_xml_parse (parser, buf, buf_len, 0);
if (err2)
{
if (err)
svn_error_clear (err);
SVN_ERR (err2);
}
} while (! err);
svn_error_clear (err);
SVN_ERR (svn_io_file_close (f, iterpool));
}
/* Pacify Expat with a pointless closing element tag. */
SVN_ERR (svn_xml_parse (parser, log_end, strlen (log_end), 1));
svn_xml_free_parser (parser);
if (loggy->entries_modified == TRUE)
{
apr_hash_t *entries;
SVN_ERR (svn_wc_entries_read (&entries, loggy->adm_access, TRUE, pool));
SVN_ERR (svn_wc__entries_write (entries, loggy->adm_access, pool));
}
/* Check for a 'killme' file in the administrative area. */
if (svn_wc__adm_path_exists (svn_wc_adm_access_path (adm_access), 0, pool,
SVN_WC__ADM_KILLME, NULL))
{
SVN_ERR (handle_killme (adm_access, NULL, NULL, pool));
}
else
{
for (log_number--; log_number >= 0; log_number--)
{
svn_pool_clear (iterpool);
logfile_path = svn_wc__logfile_path (log_number, iterpool);
/* No 'killme'? Remove the logfile; its commands have been executed. */
SVN_ERR (svn_wc__remove_adm_file (svn_wc_adm_access_path (adm_access),
iterpool, logfile_path, NULL));
}
}
return SVN_NO_ERROR;
}
/*** Recursively do log things. ***/
svn_error_t *
svn_wc_cleanup (const char *path,
svn_wc_adm_access_t *optional_adm_access,
const char *diff3_cmd,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
apr_hash_t *entries = NULL;
apr_hash_index_t *hi;
svn_node_kind_t kind;
svn_wc_adm_access_t *adm_access;
svn_boolean_t cleanup;
int wc_format_version;
/* Check cancellation; note that this catches recursive calls too. */
if (cancel_func)
SVN_ERR (cancel_func (cancel_baton));
SVN_ERR (svn_wc_check_wc (path, &wc_format_version, pool));
/* a "version" of 0 means a non-wc directory */
if (wc_format_version == 0)
return svn_error_createf
(SVN_ERR_WC_NOT_DIRECTORY, NULL,
_("'%s' is not a working copy directory"), path);
/* Lock this working copy directory, or steal an existing lock */
SVN_ERR (svn_wc__adm_steal_write_lock (&adm_access, optional_adm_access,
path, pool));
/* Recurse on versioned subdirs first, oddly enough. */
SVN_ERR (svn_wc_entries_read (&entries, adm_access, FALSE, pool));
for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
{
const void *key;
void *val;
const svn_wc_entry_t *entry;
apr_hash_this (hi, &key, NULL, &val);
entry = val;
if ((entry->kind == svn_node_dir)
&& (strcmp (key, SVN_WC_ENTRY_THIS_DIR) != 0))
{
/* Recurse */
const char *subdir = svn_path_join (path, key, pool);
SVN_ERR (svn_io_check_path (subdir, &kind, pool));
if (kind == svn_node_dir)
SVN_ERR (svn_wc_cleanup (subdir, adm_access, diff3_cmd,
cancel_func, cancel_baton, pool));
}
}
if (svn_wc__adm_path_exists (svn_wc_adm_access_path (adm_access), 0, pool,
SVN_WC__ADM_KILLME, NULL))
{
/* A KILLME indicates that the log has already been run */
SVN_ERR (handle_killme (adm_access, cancel_func, cancel_baton, pool));
}
else
{
/* In an attempt to maintain consitency between the decisions made in
this function, and those made in the access baton lock-removal code,
we use the same test as the lock-removal code. */
SVN_ERR (svn_wc__adm_is_cleanup_required (&cleanup, adm_access, pool));
if (cleanup)
SVN_ERR (svn_wc__run_log (adm_access, diff3_cmd, pool));
}
/* Cleanup the tmp area of the admin subdir, if running the log has not
removed it! The logs have been run, so anything left here has no hope
of being useful. */
if (svn_wc__adm_path_exists (path, 0, pool, NULL))
SVN_ERR (svn_wc__adm_cleanup_tmp_area (adm_access, pool));
if (! optional_adm_access)
SVN_ERR (svn_wc_adm_close (adm_access));
return SVN_NO_ERROR;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -