📄 reporter.c
字号:
{
svn_fs_root_t *s_root;
apr_hash_t *s_props, *t_props;
apr_array_header_t *prop_diffs;
int i;
svn_revnum_t crev;
const char *uuid;
svn_string_t *cr_str, *cdate, *last_author;
svn_boolean_t changed;
const svn_prop_t *pc;
/* Fetch the created-rev and send entry props. */
SVN_ERR (svn_fs_node_created_rev (&crev, b->t_root, t_path, pool));
if (SVN_IS_VALID_REVNUM (crev))
{
/* Transmit the committed-rev. */
cr_str = svn_string_createf (pool, "%ld", crev);
SVN_ERR (change_fn (b, object,
SVN_PROP_ENTRY_COMMITTED_REV, cr_str, pool));
/* Transmit the committed-date. */
SVN_ERR (svn_fs_revision_prop (&cdate, b->repos->fs, crev,
SVN_PROP_REVISION_DATE, pool));
if (cdate || s_path)
SVN_ERR (change_fn (b, object, SVN_PROP_ENTRY_COMMITTED_DATE,
cdate, pool));
/* Transmit the last-author. */
SVN_ERR (svn_fs_revision_prop (&last_author, b->repos->fs, crev,
SVN_PROP_REVISION_AUTHOR, pool));
if (last_author || s_path)
SVN_ERR (change_fn (b, object, SVN_PROP_ENTRY_LAST_AUTHOR,
last_author, pool));
/* Transmit the UUID. */
SVN_ERR (svn_fs_get_uuid (b->repos->fs, &uuid, pool));
if (uuid || s_path)
SVN_ERR (change_fn (b, object, SVN_PROP_ENTRY_UUID,
svn_string_create (uuid, pool), pool));
}
if (s_path)
{
SVN_ERR (get_source_root (b, &s_root, s_rev));
/* Is this deltification worth our time? */
SVN_ERR (svn_fs_props_changed (&changed, b->t_root, t_path, s_root,
s_path, pool));
if (! changed)
return SVN_NO_ERROR;
/* If so, go ahead and get the source path's properties. */
SVN_ERR (svn_fs_node_proplist (&s_props, s_root, s_path, pool));
}
else
s_props = apr_hash_make (pool);
/* Get the target path's properties */
SVN_ERR (svn_fs_node_proplist (&t_props, b->t_root, t_path, pool));
/* Now transmit the differences. */
SVN_ERR (svn_prop_diffs (&prop_diffs, t_props, s_props, pool));
for (i = 0; i < prop_diffs->nelts; i++)
{
pc = &APR_ARRAY_IDX (prop_diffs, i, svn_prop_t);
SVN_ERR (change_fn (b, object, pc->name, pc->value, pool));
}
return SVN_NO_ERROR;
}
/* Set *CHANGED_P to TRUE if ROOT1/PATH1 and ROOT2/PATH2 have
different contents, FALSE if they have the same contents. */
static svn_error_t *
compare_files (svn_boolean_t *changed_p, svn_fs_root_t *root1,
const char *path1, svn_fs_root_t *root2, const char *path2,
apr_pool_t *pool)
{
svn_filesize_t size1, size2;
unsigned char digest1[APR_MD5_DIGESTSIZE], digest2[APR_MD5_DIGESTSIZE];
svn_stream_t *stream1, *stream2;
char buf1[SVN_STREAM_CHUNK_SIZE], buf2[SVN_STREAM_CHUNK_SIZE];
apr_size_t len1, len2;
/* If the filesystem claims the things haven't changed, then they
haven't changed. */
SVN_ERR (svn_fs_contents_changed (changed_p, root1, path1,
root2, path2, pool));
if (!*changed_p)
return SVN_NO_ERROR;
/* From this point on, assume things haven't changed. */
*changed_p = FALSE;
/* So, things have changed. But we need to know if the two sets of
file contents are actually different. If they have differing
sizes, then we know they differ. */
SVN_ERR (svn_fs_file_length (&size1, root1, path1, pool));
SVN_ERR (svn_fs_file_length (&size2, root2, path2, pool));
if (size1 != size2)
{
*changed_p = TRUE;
return SVN_NO_ERROR;
}
/* Same sizes, huh? Well, if their checksums differ, we know they
differ. */
SVN_ERR (svn_fs_file_md5_checksum (digest1, root1, path1, pool));
SVN_ERR (svn_fs_file_md5_checksum (digest2, root2, path2, pool));
if (!svn_md5_digests_match (digest1, digest2))
{
*changed_p = TRUE;
return SVN_NO_ERROR;
}
/* Same sizes, same checksums. Chances are reallllly good that they
don't differ, but to be absolute sure, we need to compare bytes. */
SVN_ERR (svn_fs_file_contents (&stream1, root1, path1, pool));
SVN_ERR (svn_fs_file_contents (&stream2, root2, path2, pool));
do
{
len1 = len2 = SVN_STREAM_CHUNK_SIZE;
SVN_ERR (svn_stream_read (stream1, buf1, &len1));
SVN_ERR (svn_stream_read (stream2, buf2, &len2));
if (len1 != len2 || memcmp (buf1, buf2, len1))
{
*changed_p = TRUE;
return SVN_NO_ERROR;
}
}
while (len1 > 0);
return SVN_NO_ERROR;
}
/* Make the appropriate edits on FILE_BATON to change its contents and
properties from those in S_REV/S_PATH to those in B->t_root/T_PATH. */
static svn_error_t *
delta_files (report_baton_t *b, void *file_baton, svn_revnum_t s_rev,
const char *s_path, const char *t_path, apr_pool_t *pool)
{
svn_boolean_t changed;
svn_fs_root_t *s_root = NULL;
svn_txdelta_stream_t *dstream = NULL;
unsigned char s_digest[APR_MD5_DIGESTSIZE];
const char *s_hex_digest = NULL;
svn_txdelta_window_handler_t dhandler;
void *dbaton;
/* Compare the files' property lists. */
SVN_ERR (delta_proplists (b, s_rev, s_path, t_path, change_file_prop,
file_baton, pool));
if (s_path)
{
SVN_ERR (get_source_root (b, &s_root, s_rev));
/* Is this delta calculation worth our time? If we are ignoring
ancestry, then our editor implementor isn't concerned by the
theoretical differences between "has contents which have not
changed with respect to" and "has the same actual contents
as". We'll do everything we can to avoid transmitting even
an empty text-delta in that case. */
if (b->ignore_ancestry)
SVN_ERR (compare_files (&changed, b->t_root, t_path, s_root, s_path,
pool));
else
SVN_ERR (svn_fs_contents_changed (&changed, b->t_root, t_path, s_root,
s_path, pool));
if (!changed)
return SVN_NO_ERROR;
SVN_ERR (svn_fs_file_md5_checksum (s_digest, s_root, s_path, pool));
s_hex_digest = svn_md5_digest_to_cstring (s_digest, pool);
}
/* Send the delta stream if desired, or just a NULL window if not. */
SVN_ERR (b->editor->apply_textdelta (file_baton, s_hex_digest, pool,
&dhandler, &dbaton));
if (b->text_deltas)
{
SVN_ERR (svn_fs_get_file_delta_stream (&dstream, s_root, s_path,
b->t_root, t_path, pool));
return svn_txdelta_send_txstream (dstream, dhandler, dbaton, pool);
}
else
return dhandler (NULL, dbaton);
}
/* Determine if the user is authorized to view B->t_root/PATH. */
static svn_error_t *
check_auth (report_baton_t *b, svn_boolean_t *allowed, const char *path,
apr_pool_t *pool)
{
if (b->authz_read_func)
return b->authz_read_func (allowed, b->t_root, path,
b->authz_read_baton, pool);
*allowed = TRUE;
return SVN_NO_ERROR;
}
/* Create a dirent in *ENTRY for the given ROOT and PATH. We use this to
replace the source or target dirent when a report pathinfo tells us to
change paths or revisions. */
static svn_error_t *
fake_dirent (const svn_fs_dirent_t **entry, svn_fs_root_t *root,
const char *path, apr_pool_t *pool)
{
svn_node_kind_t kind;
svn_fs_dirent_t *ent;
SVN_ERR (svn_fs_check_path (&kind, root, path, pool));
if (kind == svn_node_none)
*entry = NULL;
else
{
ent = apr_palloc (pool, sizeof (**entry));
ent->name = svn_path_basename (path, pool);
SVN_ERR (svn_fs_node_id (&ent->id, root, path, pool));
ent->kind = kind;
*entry = ent;
}
return SVN_NO_ERROR;
}
/* Emit an appropriate edit to transform the entry E_PATH under
DIR_BATON with the changes between S_REV/S_PATH (with contents
S_ENTRY) and B->t_root/T_PATH (with contents T_ENTRY).
S_PATH/S_ENTRY or T_PATH/T_ENTRY may be NULL if the entry does not
exist in the source or target. The source and to some extent the
target may be modified by INFO. S_PATH may be set and S_ENTRY may
be NULL if the caller expects report information to modify the
source to an existing location. If RECURSE is not set, avoid
operating on directories. (Normally RECURSE is simply taken from
B->recurse, but drive() needs to force us to recurse into the
target even if that flag is not set.) */
static svn_error_t *
update_entry (report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
const svn_fs_dirent_t *s_entry, const char *t_path,
const svn_fs_dirent_t *t_entry, void *dir_baton,
const char *e_path, path_info_t *info, svn_boolean_t recurse,
apr_pool_t *pool)
{
svn_fs_root_t *s_root;
svn_boolean_t allowed, related;
void *new_baton;
unsigned char digest[APR_MD5_DIGESTSIZE];
const char *hex_digest;
int distance;
/* For non-switch operations, follow link_path in the target. */
if (info && info->link_path && !b->is_switch)
{
t_path = info->link_path;
SVN_ERR (fake_dirent (&t_entry, b->t_root, t_path, pool));
}
if (info && !SVN_IS_VALID_REVNUM (info->rev))
{
/* Delete this entry in the source. */
s_path = NULL;
s_entry = NULL;
}
else if (info && s_path)
{
/* Follow the rev and possibly path in this entry. */
s_path = (info->link_path) ? info->link_path : s_path;
s_rev = info->rev;
SVN_ERR (get_source_root (b, &s_root, s_rev));
SVN_ERR (fake_dirent (&s_entry, s_root, s_path, pool));
}
/* Don't let the report carry us somewhere nonexistent. */
if (s_path && !s_entry)
return svn_error_createf (SVN_ERR_FS_NOT_FOUND, NULL,
"Working copy path '%s' does not exist in "
"repository", e_path);
if (!recurse && ((s_entry && s_entry->kind == svn_node_dir)
|| (t_entry && t_entry->kind == svn_node_dir)))
return skip_path_info (b, e_path);
/* If the source and target both exist and are of the same kind,
then find out whether they're related. If they're exactly the
same, then we don't have to do anything (unless the report has
changes to the source). If we're ignoring ancestry, then any two
nodes of the same type are related enough for us. */
related = FALSE;
if (s_entry && t_entry && s_entry->kind == t_entry->kind)
{
distance = svn_fs_compare_ids (s_entry->id, t_entry->id);
if (distance == 0 && !any_path_info (b, e_path)
&& (!info || !info->start_empty))
return SVN_NO_ERROR;
else if (distance != -1 || b->ignore_ancestry)
related = TRUE;
}
/* If there's a source and it's not related to the target, nuke it. */
if (s_entry && !related)
{
SVN_ERR (b->editor->delete_entry (e_path, SVN_INVALID_REVNUM, dir_baton,
pool));
s_path = NULL;
}
/* If there's no target, we have nothing more to do. */
if (!t_entry)
return skip_path_info (b, e_path);
/* Check if the user is authorized to find out about the target. */
SVN_ERR (check_auth (b, &allowed, t_path, pool));
if (!allowed)
{
if (t_entry->kind == svn_node_dir)
SVN_ERR (b->editor->absent_directory (e_path, dir_baton, pool));
else
SVN_ERR (b->editor->absent_file (e_path, dir_baton, pool));
return skip_path_info (b, e_path);
}
if (t_entry->kind == svn_node_dir)
{
if (related)
SVN_ERR (b->editor->open_directory (e_path, dir_baton, s_rev, pool,
&new_baton));
else
SVN_ERR (b->editor->add_directory (e_path, dir_baton, NULL,
SVN_INVALID_REVNUM, pool,
&new_baton));
SVN_ERR (delta_dirs (b, s_rev, s_path, t_path, new_baton, e_path,
info ? info->start_empty : FALSE, pool));
return b->editor->close_directory (new_baton, pool);
}
else
{
if (related)
SVN_ERR (b->editor->open_file (e_path, dir_baton, s_rev, pool,
&new_baton));
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -