📄 reporter.c
字号:
SVN_ERR (b->editor->add_file (e_path, dir_baton, NULL,
SVN_INVALID_REVNUM, pool, &new_baton));
SVN_ERR (delta_files (b, new_baton, s_rev, s_path, t_path, pool));
SVN_ERR (svn_fs_file_md5_checksum (digest, b->t_root, t_path, pool));
hex_digest = svn_md5_digest_to_cstring (digest, pool);
return b->editor->close_file (new_baton, hex_digest, pool);
}
}
/* Emit edits within directory DIR_BATON (with corresponding path
E_PATH) with the changes from the directory S_REV/S_PATH to the
directory B->t_rev/T_PATH. S_PATH may be NULL if the entry does
not exist in the source. */
static svn_error_t *
delta_dirs (report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
const char *t_path, void *dir_baton, const char *e_path,
svn_boolean_t start_empty, apr_pool_t *pool)
{
svn_fs_root_t *s_root;
apr_hash_t *s_entries = NULL, *t_entries;
apr_hash_index_t *hi;
apr_pool_t *subpool;
const svn_fs_dirent_t *s_entry, *t_entry;
void *val;
const char *name, *s_fullpath, *t_fullpath, *e_fullpath;
path_info_t *info;
/* Compare the property lists. If we're starting empty, pass a NULL
source path so that we add all the properties. */
SVN_ERR (delta_proplists (b, s_rev, start_empty ? NULL : s_path, t_path,
change_dir_prop, dir_baton, pool));
/* Get the list of entries in each of source and target. */
if (s_path && !start_empty)
{
SVN_ERR (get_source_root (b, &s_root, s_rev));
SVN_ERR (svn_fs_dir_entries (&s_entries, s_root, s_path, pool));
}
SVN_ERR (svn_fs_dir_entries (&t_entries, b->t_root, t_path, pool));
/* Iterate over the report information for this directory. */
subpool = svn_pool_create (pool);
while (1)
{
svn_pool_clear (subpool);
SVN_ERR (fetch_path_info (b, &name, &info, e_path, subpool));
if (!name)
break;
e_fullpath = svn_path_join (e_path, name, subpool);
t_fullpath = svn_path_join (t_path, name, subpool);
t_entry = apr_hash_get (t_entries, name, APR_HASH_KEY_STRING);
s_fullpath = s_path ? svn_path_join (s_path, name, subpool) : NULL;
s_entry = s_entries ?
apr_hash_get (s_entries, name, APR_HASH_KEY_STRING) : NULL;
SVN_ERR (update_entry (b, s_rev, s_fullpath, s_entry, t_fullpath,
t_entry, dir_baton, e_fullpath, info,
b->recurse, subpool));
/* Don't revisit this name in the target or source entries. */
apr_hash_set (t_entries, name, APR_HASH_KEY_STRING, NULL);
if (s_entries)
apr_hash_set (s_entries, name, APR_HASH_KEY_STRING, NULL);
/* pathinfo entries live in their own subpools due to lookahead,
so we need to clear each one out as we finish with it. */
if (info)
svn_pool_destroy (info->pool);
}
/* Loop over the remaining dirents in the target. */
for (hi = apr_hash_first (pool, t_entries); hi; hi = apr_hash_next (hi))
{
svn_pool_clear (subpool);
apr_hash_this (hi, NULL, NULL, &val);
t_entry = val;
/* Compose the report, editor, and target paths for this entry. */
e_fullpath = svn_path_join (e_path, t_entry->name, subpool);
t_fullpath = svn_path_join (t_path, t_entry->name, subpool);
/* Look for an entry with the same name in the source dirents. */
s_entry = s_entries ?
apr_hash_get (s_entries, t_entry->name, APR_HASH_KEY_STRING) : NULL;
s_fullpath = s_entry ? svn_path_join (s_path, t_entry->name, subpool)
: NULL;
SVN_ERR (update_entry (b, s_rev, s_fullpath, s_entry, t_fullpath,
t_entry, dir_baton, e_fullpath, NULL,
b->recurse, subpool));
/* Don't revisit this name in the source entries. */
if (s_entries)
apr_hash_set (s_entries, t_entry->name, APR_HASH_KEY_STRING, NULL);
}
/* Loop over the remaining dirents in the source. */
if (s_entries)
{
for (hi = apr_hash_first (pool, s_entries); hi; hi = apr_hash_next (hi))
{
svn_pool_clear (subpool);
apr_hash_this (hi, NULL, NULL, &val);
s_entry = val;
/* We know there is no corresponding target entry, so just delete. */
e_fullpath = svn_path_join (e_path, s_entry->name, subpool);
if (b->recurse || s_entry->kind != svn_node_dir)
SVN_ERR (b->editor->delete_entry (e_fullpath, SVN_INVALID_REVNUM,
dir_baton, subpool));
}
}
/* Destroy iteration subpool. */
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
drive (report_baton_t *b, svn_revnum_t s_rev, path_info_t *info,
apr_pool_t *pool)
{
const char *t_anchor, *s_fullpath;
svn_boolean_t allowed, info_is_set_path;
svn_fs_root_t *s_root;
const svn_fs_dirent_t *s_entry, *t_entry;
void *root_baton;
/* Compute the target path corresponding to the working copy anchor,
and check its authorization. */
t_anchor = *b->s_operand ? svn_path_dirname (b->t_path, pool) : b->t_path;
SVN_ERR (check_auth (b, &allowed, t_anchor, pool));
if (!allowed)
return svn_error_create (SVN_ERR_AUTHZ_ROOT_UNREADABLE, NULL,
"Not authorized to open root of edit operation.");
SVN_ERR (b->editor->set_target_revision (b->edit_baton, b->t_rev, pool));
/* Collect information about the source and target nodes. */
s_fullpath = svn_path_join (b->fs_base, b->s_operand, pool);
SVN_ERR (get_source_root (b, &s_root, s_rev));
SVN_ERR (fake_dirent (&s_entry, s_root, s_fullpath, pool));
SVN_ERR (fake_dirent (&t_entry, b->t_root, b->t_path, pool));
/* If the operand is a locally added file or directory, it won't
exist in the source, so accept that. */
info_is_set_path = (SVN_IS_VALID_REVNUM (info->rev) && !info->link_path);
if (info_is_set_path && !s_entry)
s_fullpath = NULL;
/* If the anchor is the operand, the source and target must be dirs.
Check this before opening the root to avoid modifying the wc. */
if (!*b->s_operand && (!s_entry || s_entry->kind != svn_node_dir
|| !t_entry || t_entry->kind != svn_node_dir))
return svn_error_create (SVN_ERR_FS_PATH_SYNTAX, NULL,
"Cannot replace a directory from within");
SVN_ERR (b->editor->open_root (b->edit_baton, s_rev, pool, &root_baton));
/* If the anchor is the operand, diff the two directories; otherwise
update the operand within the anchor directory. */
if (!*b->s_operand)
SVN_ERR (delta_dirs (b, s_rev, s_fullpath, b->t_path, root_baton,
"", info->start_empty, pool));
else
SVN_ERR (update_entry (b, s_rev, s_fullpath, s_entry, b->t_path,
t_entry, root_baton, b->s_operand, info,
TRUE, pool));
SVN_ERR (b->editor->close_directory (root_baton, pool));
SVN_ERR (b->editor->close_edit (b->edit_baton, pool));
return SVN_NO_ERROR;
}
/* Initialize the baton fields for editor-driving, and drive the editor. */
static svn_error_t *
finish_report (report_baton_t *b, apr_pool_t *pool)
{
apr_off_t offset;
path_info_t *info;
apr_pool_t *subpool;
svn_revnum_t s_rev;
int i;
/* Save our pool to manage the lookahead and fs_root cache with. */
b->pool = pool;
/* Add an end marker and rewind the temporary file. */
SVN_ERR (svn_io_file_write_full (b->tempfile, "-", 1, NULL, pool));
offset = 0;
SVN_ERR (svn_io_file_seek (b->tempfile, APR_SET, &offset, pool));
/* Read the first pathinfo from the report and verify that it is a top-level
set_path entry. */
SVN_ERR (read_path_info (&info, b->tempfile, pool));
if (!info || strcmp (info->path, b->s_operand) != 0
|| info->link_path || !SVN_IS_VALID_REVNUM(info->rev))
return svn_error_create (SVN_ERR_REPOS_BAD_REVISION_REPORT, NULL,
"Invalid report for top level of working copy");
s_rev = info->rev;
/* Initialize the lookahead pathinfo. */
subpool = svn_pool_create (pool);
SVN_ERR (read_path_info (&b->lookahead, b->tempfile, subpool));
if (b->lookahead && strcmp(b->lookahead->path, b->s_operand) == 0)
{
/* If the operand of the wc operation is switched or deleted,
then info above is just a place-holder, and the only thing we
have to do is pass the revision it contains to open_root.
The next pathinfo actually describes the target. */
if (!*b->s_operand)
return svn_error_create (SVN_ERR_REPOS_BAD_REVISION_REPORT, NULL,
"Two top-level reports with no target");
info = b->lookahead;
SVN_ERR (read_path_info (&b->lookahead, b->tempfile, subpool));
}
/* Open the target root and initialize the source root cache. */
SVN_ERR (svn_fs_revision_root (&b->t_root, b->repos->fs, b->t_rev, pool));
for (i = 0; i < NUM_CACHED_SOURCE_ROOTS; i++)
b->s_roots[i] = NULL;
return drive (b, s_rev, info, pool);
}
/* --- COLLECTING THE REPORT INFORMATION --- */
/* Record a report operation into the temporary file. */
static svn_error_t *
write_path_info (report_baton_t *b, const char *path, const char *lpath,
svn_revnum_t rev, svn_boolean_t start_empty, apr_pool_t *pool)
{
const char *lrep, *rrep, *rep;
/* Munge the path to be anchor-relative, so that we can use edit paths
as report paths. */
path = svn_path_join (b->s_operand, path, pool);
lrep = lpath ? apr_psprintf (pool, "+%" APR_SIZE_T_FMT ":%s",
strlen(lpath), lpath) : "-";
rrep = (SVN_IS_VALID_REVNUM (rev)) ?
apr_psprintf (pool, "+%ld:", rev) : "-";
rep = apr_psprintf (pool, "+%" APR_SIZE_T_FMT ":%s%s%s%c",
strlen(path), path, lrep, rrep, start_empty ? '+' : '-');
return svn_io_file_write_full (b->tempfile, rep, strlen(rep), NULL, pool);
}
svn_error_t *
svn_repos_set_path (void *baton, const char *path, svn_revnum_t rev,
svn_boolean_t start_empty, apr_pool_t *pool)
{
return write_path_info (baton, path, NULL, rev, start_empty, pool);
}
svn_error_t *
svn_repos_link_path (void *baton, const char *path, const char *link_path,
svn_revnum_t rev, svn_boolean_t start_empty,
apr_pool_t *pool)
{
return write_path_info (baton, path, link_path, rev, start_empty, pool);
}
svn_error_t *
svn_repos_delete_path (void *baton, const char *path, apr_pool_t *pool)
{
return write_path_info (baton, path, NULL, SVN_INVALID_REVNUM, FALSE, pool);
}
svn_error_t *
svn_repos_finish_report (void *baton, apr_pool_t *pool)
{
report_baton_t *b = baton;
svn_error_t *finish_err, *close_err;
finish_err = finish_report (b, pool);
close_err = svn_io_file_close (b->tempfile, pool);
if (finish_err)
svn_error_clear (close_err);
return finish_err ? finish_err : close_err;
}
svn_error_t *
svn_repos_abort_report (void *baton, apr_pool_t *pool)
{
report_baton_t *b = baton;
return svn_io_file_close (b->tempfile, pool);
}
/* --- BEGINNING THE REPORT --- */
svn_error_t *
svn_repos_begin_report (void **report_baton,
svn_revnum_t revnum,
const char *username,
svn_repos_t *repos,
const char *fs_base,
const char *s_operand,
const char *switch_path,
svn_boolean_t text_deltas,
svn_boolean_t recurse,
svn_boolean_t ignore_ancestry,
const svn_delta_editor_t *editor,
void *edit_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
report_baton_t *b;
const char *tempdir, *dummy;
/* Build a reporter baton. Copy strings in case the caller doesn't
keep track of them. */
b = apr_palloc (pool, sizeof (*b));
b->repos = repos;
b->fs_base = apr_pstrdup (pool, fs_base);
b->s_operand = apr_pstrdup (pool, s_operand);
b->t_rev = revnum;
b->t_path = switch_path ? switch_path
: svn_path_join (fs_base, s_operand, pool);
b->text_deltas = text_deltas;
b->recurse = recurse;
b->ignore_ancestry = ignore_ancestry;
b->is_switch = (switch_path != NULL);
b->editor = editor;
b->edit_baton = edit_baton;
b->authz_read_func = authz_read_func;
b->authz_read_baton = authz_read_baton;
SVN_ERR (svn_io_temp_dir (&tempdir, pool));
SVN_ERR (svn_io_open_unique_file (&b->tempfile, &dummy,
apr_psprintf (pool, "%s/report", tempdir),
".tmp", TRUE, pool));
/* Hand reporter back to client. */
*report_baton = b;
return SVN_NO_ERROR;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -