summaryrefslogtreecommitdiff
path: root/subversion/libsvn_repos/reporter.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_repos/reporter.c')
-rw-r--r--subversion/libsvn_repos/reporter.c412
1 files changed, 268 insertions, 144 deletions
diff --git a/subversion/libsvn_repos/reporter.c b/subversion/libsvn_repos/reporter.c
index bcee14e..de46858 100644
--- a/subversion/libsvn_repos/reporter.c
+++ b/subversion/libsvn_repos/reporter.c
@@ -22,6 +22,7 @@
*/
#include "svn_dirent_uri.h"
+#include "svn_hash.h"
#include "svn_path.h"
#include "svn_types.h"
#include "svn_error.h"
@@ -32,17 +33,20 @@
#include "svn_props.h"
#include "repos.h"
#include "svn_private_config.h"
+
#include "private/svn_dep_compat.h"
#include "private/svn_fspath.h"
+#include "private/svn_subr_private.h"
+#include "private/svn_string_private.h"
#define NUM_CACHED_SOURCE_ROOTS 4
-/* Theory of operation: we write report operations out to a temporary
- file as we receive them. When the report is finished, we read the
+/* Theory of operation: we write report operations out to a spill-buffer
+ as we receive them. When the report is finished, we read the
operations back out again, using them to guide the progression of
the delta between the source and target revs.
- Temporary file format: we use a simple ad-hoc format to store the
+ Spill-buffer content format: we use a simple ad-hoc format to store the
report operations. Each report operation is the concatention of
the following ("+/-" indicates the single character '+' or '-';
<length> and <revnum> are written out as decimal strings):
@@ -100,13 +104,15 @@ typedef struct revision_info_t
driven by the client as it describes its working copy revisions. */
typedef struct report_baton_t
{
- /* Parameters remembered from svn_repos_begin_report2 */
+ /* Parameters remembered from svn_repos_begin_report3 */
svn_repos_t *repos;
const char *fs_base; /* fspath corresponding to wc anchor */
const char *s_operand; /* anchor-relative wc target (may be empty) */
svn_revnum_t t_rev; /* Revnum which the edit will bring the wc to */
const char *t_path; /* FS path the edit will bring the wc to */
svn_boolean_t text_deltas; /* Whether to report text deltas */
+ apr_size_t zero_copy_limit; /* Max item size that will be sent using
+ the zero-copy code path. */
/* If the client requested a specific depth, record it here; if the
client did not, then this is svn_depth_unknown, and the depth of
@@ -122,8 +128,8 @@ typedef struct report_baton_t
svn_repos_authz_func_t authz_read_func;
void *authz_read_baton;
- /* The temporary file in which we are stashing the report. */
- apr_file_t *tempfile;
+ /* The spill-buffer holding the report. */
+ svn_spillbuf_reader_t *reader;
/* For the actual editor drive, we'll need a lookahead path info
entry, a cache of FS roots, and a pool to store them. */
@@ -133,8 +139,10 @@ typedef struct report_baton_t
/* Cache for revision properties. This is used to eliminate redundant
revprop fetching. */
- apr_hash_t* revision_infos;
+ apr_hash_t *revision_infos;
+ /* This will not change. So, fetch it once and reuse it. */
+ svn_string_t *repos_uuid;
apr_pool_t *pool;
} report_baton_t;
@@ -158,14 +166,14 @@ static svn_error_t *delta_dirs(report_baton_t *b, svn_revnum_t s_rev,
/* --- READING PREVIOUSLY STORED REPORT INFORMATION --- */
static svn_error_t *
-read_number(apr_uint64_t *num, apr_file_t *temp, apr_pool_t *pool)
+read_number(apr_uint64_t *num, svn_spillbuf_reader_t *reader, apr_pool_t *pool)
{
char c;
*num = 0;
while (1)
{
- SVN_ERR(svn_io_file_getc(&c, temp, pool));
+ SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
if (c == ':')
break;
*num = *num * 10 + (c - '0');
@@ -174,13 +182,14 @@ read_number(apr_uint64_t *num, apr_file_t *temp, apr_pool_t *pool)
}
static svn_error_t *
-read_string(const char **str, apr_file_t *temp, apr_pool_t *pool)
+read_string(const char **str, svn_spillbuf_reader_t *reader, apr_pool_t *pool)
{
apr_uint64_t len;
apr_size_t size;
+ apr_size_t amt;
char *buf;
- SVN_ERR(read_number(&len, temp, pool));
+ SVN_ERR(read_number(&len, reader, pool));
/* Len can never be less than zero. But could len be so large that
len + 1 wraps around and we end up passing 0 to apr_palloc(),
@@ -201,22 +210,26 @@ read_string(const char **str, apr_file_t *temp, apr_pool_t *pool)
size = (apr_size_t)len;
buf = apr_palloc(pool, size+1);
- SVN_ERR(svn_io_file_read_full2(temp, buf, size, NULL, NULL, pool));
+ if (size > 0)
+ {
+ SVN_ERR(svn_spillbuf__reader_read(&amt, reader, buf, size, pool));
+ SVN_ERR_ASSERT(amt == size);
+ }
buf[len] = 0;
*str = buf;
return SVN_NO_ERROR;
}
static svn_error_t *
-read_rev(svn_revnum_t *rev, apr_file_t *temp, apr_pool_t *pool)
+read_rev(svn_revnum_t *rev, svn_spillbuf_reader_t *reader, apr_pool_t *pool)
{
char c;
apr_uint64_t num;
- SVN_ERR(svn_io_file_getc(&c, temp, pool));
+ SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
if (c == '+')
{
- SVN_ERR(read_number(&num, temp, pool));
+ SVN_ERR(read_number(&num, reader, pool));
*rev = (svn_revnum_t) num;
}
else
@@ -225,15 +238,15 @@ read_rev(svn_revnum_t *rev, apr_file_t *temp, apr_pool_t *pool)
}
/* Read a single character to set *DEPTH (having already read '+')
- from TEMP. PATH is the path to which the depth applies, and is
+ from READER. PATH is the path to which the depth applies, and is
used for error reporting only. */
static svn_error_t *
-read_depth(svn_depth_t *depth, apr_file_t *temp, const char *path,
+read_depth(svn_depth_t *depth, svn_spillbuf_reader_t *reader, const char *path,
apr_pool_t *pool)
{
char c;
- SVN_ERR(svn_io_file_getc(&c, temp, pool));
+ SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
switch (c)
{
case 'X':
@@ -260,14 +273,16 @@ read_depth(svn_depth_t *depth, apr_file_t *temp, const char *path,
return SVN_NO_ERROR;
}
-/* Read a report operation *PI out of TEMP. Set *PI to NULL if we
+/* Read a report operation *PI out of READER. Set *PI to NULL if we
have reached the end of the report. */
static svn_error_t *
-read_path_info(path_info_t **pi, apr_file_t *temp, apr_pool_t *pool)
+read_path_info(path_info_t **pi,
+ svn_spillbuf_reader_t *reader,
+ apr_pool_t *pool)
{
char c;
- SVN_ERR(svn_io_file_getc(&c, temp, pool));
+ SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
if (c == '-')
{
*pi = NULL;
@@ -275,23 +290,23 @@ read_path_info(path_info_t **pi, apr_file_t *temp, apr_pool_t *pool)
}
*pi = apr_palloc(pool, sizeof(**pi));
- SVN_ERR(read_string(&(*pi)->path, temp, pool));
- SVN_ERR(svn_io_file_getc(&c, temp, pool));
+ SVN_ERR(read_string(&(*pi)->path, reader, pool));
+ SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
if (c == '+')
- SVN_ERR(read_string(&(*pi)->link_path, temp, pool));
+ SVN_ERR(read_string(&(*pi)->link_path, reader, pool));
else
(*pi)->link_path = NULL;
- SVN_ERR(read_rev(&(*pi)->rev, temp, pool));
- SVN_ERR(svn_io_file_getc(&c, temp, pool));
+ SVN_ERR(read_rev(&(*pi)->rev, reader, pool));
+ SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
if (c == '+')
- SVN_ERR(read_depth(&((*pi)->depth), temp, (*pi)->path, pool));
+ SVN_ERR(read_depth(&((*pi)->depth), reader, (*pi)->path, pool));
else
(*pi)->depth = svn_depth_infinity;
- SVN_ERR(svn_io_file_getc(&c, temp, pool));
+ SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
(*pi)->start_empty = (c == '+');
- SVN_ERR(svn_io_file_getc(&c, temp, pool));
+ SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
if (c == '+')
- SVN_ERR(read_string(&(*pi)->lock_token, temp, pool));
+ SVN_ERR(read_string(&(*pi)->lock_token, reader, pool));
else
(*pi)->lock_token = NULL;
(*pi)->pool = pool;
@@ -306,7 +321,7 @@ relevant(path_info_t *pi, const char *prefix, apr_size_t plen)
(!*prefix || pi->path[plen] == '/'));
}
-/* Fetch the next pathinfo from B->tempfile for a descendant of
+/* Fetch the next pathinfo from B->reader for a descendant of
PREFIX. If the next pathinfo is for an immediate child of PREFIX,
set *ENTRY to the path component of the report information and
*INFO to the path information for that entry. If the next pathinfo
@@ -353,7 +368,7 @@ fetch_path_info(report_baton_t *b, const char **entry, path_info_t **info,
*entry = relpath;
*info = b->lookahead;
subpool = svn_pool_create(b->pool);
- SVN_ERR(read_path_info(&b->lookahead, b->tempfile, subpool));
+ SVN_ERR(read_path_info(&b->lookahead, b->reader, subpool));
}
}
return SVN_NO_ERROR;
@@ -371,7 +386,7 @@ skip_path_info(report_baton_t *b, const char *prefix)
{
svn_pool_destroy(b->lookahead->pool);
subpool = svn_pool_create(b->pool);
- SVN_ERR(read_path_info(&b->lookahead, b->tempfile, subpool));
+ SVN_ERR(read_path_info(&b->lookahead, b->reader, subpool));
}
return SVN_NO_ERROR;
}
@@ -433,7 +448,8 @@ static svn_error_t *
change_dir_prop(report_baton_t *b, void *dir_baton, const char *name,
const svn_string_t *value, apr_pool_t *pool)
{
- return b->editor->change_dir_prop(dir_baton, name, value, pool);
+ return svn_error_trace(b->editor->change_dir_prop(dir_baton, name, value,
+ pool));
}
/* Call the file property-setting function of B->editor to set the
@@ -442,7 +458,8 @@ static svn_error_t *
change_file_prop(report_baton_t *b, void *file_baton, const char *name,
const svn_string_t *value, apr_pool_t *pool)
{
- return b->editor->change_file_prop(file_baton, name, value, pool);
+ return svn_error_trace(b->editor->change_file_prop(file_baton, name, value,
+ pool));
}
/* For the report B, return the relevant revprop data of revision REV in
@@ -470,12 +487,10 @@ get_revision_info(report_baton_t *b,
scratch_pool));
/* Extract the committed-date. */
- cdate = apr_hash_get(r_props, SVN_PROP_REVISION_DATE,
- APR_HASH_KEY_STRING);
+ cdate = svn_hash_gets(r_props, SVN_PROP_REVISION_DATE);
/* Extract the last-author. */
- author = apr_hash_get(r_props, SVN_PROP_REVISION_AUTHOR,
- APR_HASH_KEY_STRING);
+ author = svn_hash_gets(r_props, SVN_PROP_REVISION_AUTHOR);
/* Create a result object */
info = apr_palloc(b->pool, sizeof(*info));
@@ -504,25 +519,29 @@ delta_proplists(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
void *object, apr_pool_t *pool)
{
svn_fs_root_t *s_root;
- apr_hash_t *s_props, *t_props;
+ apr_hash_t *s_props = NULL, *t_props;
apr_array_header_t *prop_diffs;
int i;
svn_revnum_t crev;
- const char *uuid;
- svn_string_t *cr_str;
- revision_info_t* revision_info;
+ revision_info_t *revision_info;
svn_boolean_t changed;
const svn_prop_t *pc;
svn_lock_t *lock;
+ apr_hash_index_t *hi;
/* 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))
{
+ /* convert committed-rev to string */
+ char buf[SVN_INT64_BUFFER_SIZE];
+ svn_string_t cr_str;
+ cr_str.data = buf;
+ cr_str.len = svn__i64toa(buf, 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));
+ SVN_PROP_ENTRY_COMMITTED_REV, &cr_str, pool));
SVN_ERR(get_revision_info(b, crev, &revision_info, pool));
@@ -537,9 +556,8 @@ delta_proplists(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
revision_info->author, pool));
/* Transmit the UUID. */
- SVN_ERR(svn_fs_get_uuid(b->repos->fs, &uuid, pool));
SVN_ERR(change_fn(b, object, SVN_PROP_ENTRY_UUID,
- svn_string_create(uuid, pool), pool));
+ b->repos_uuid, pool));
}
/* Update lock properties. */
@@ -566,23 +584,83 @@ delta_proplists(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
/* 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++)
+ if (s_props && apr_hash_count(s_props))
+ {
+ /* 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));
+ }
+ }
+ else if (apr_hash_count(t_props))
+ {
+ /* So source, i.e. all new. Transmit all target props. */
+ for (hi = apr_hash_first(pool, t_props); hi; hi = apr_hash_next(hi))
+ {
+ const void *key;
+ void *val;
+
+ apr_hash_this(hi, &key, NULL, &val);
+ SVN_ERR(change_fn(b, object, key, val, pool));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Baton type to be passed into send_zero_copy_delta.
+ */
+typedef struct zero_copy_baton_t
+{
+ /* don't process data larger than this limit */
+ apr_size_t zero_copy_limit;
+
+ /* window handler and baton to send the data to */
+ svn_txdelta_window_handler_t dhandler;
+ void *dbaton;
+
+ /* return value: will be set to TRUE, if the data was processed. */
+ svn_boolean_t zero_copy_succeeded;
+} zero_copy_baton_t;
+
+/* Implement svn_fs_process_contents_func_t. If LEN is smaller than the
+ * limit given in *BATON, send the CONTENTS as an delta windows to the
+ * handler given in BATON and set the ZERO_COPY_SUCCEEDED flag in that
+ * BATON. Otherwise, reset it to FALSE.
+ * Use POOL for temporary allocations.
+ */
+static svn_error_t *
+send_zero_copy_delta(const unsigned char *contents,
+ apr_size_t len,
+ void *baton,
+ apr_pool_t *pool)
+{
+ zero_copy_baton_t *zero_copy_baton = baton;
+
+ /* if the item is too large, the caller must revert to traditional
+ streaming code. */
+ if (len > zero_copy_baton->zero_copy_limit)
{
- pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t);
- SVN_ERR(change_fn(b, object, pc->name, pc->value, pool));
+ zero_copy_baton->zero_copy_succeeded = FALSE;
+ return SVN_NO_ERROR;
}
+ SVN_ERR(svn_txdelta_send_contents(contents, len,
+ zero_copy_baton->dhandler,
+ zero_copy_baton->dbaton, pool));
+
+ /* all fine now */
+ zero_copy_baton->zero_copy_succeeded = TRUE;
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,
possibly using LOCK_TOKEN to determine if the client's lock on the file
@@ -608,18 +686,13 @@ delta_files(report_baton_t *b, void *file_baton, svn_revnum_t s_rev,
{
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(svn_repos__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));
+ /* We're not interested in the theoretical difference between "has
+ contents which have not changed with respect to" and "has the same
+ actual contents as" when sending text-deltas. If we know the
+ delta is an empty one, we avoiding sending it in either case. */
+ SVN_ERR(svn_repos__compare_files(&changed, b->t_root, t_path,
+ s_root, s_path, pool));
+
if (!changed)
return SVN_NO_ERROR;
@@ -631,14 +704,42 @@ delta_files(report_baton_t *b, void *file_baton, svn_revnum_t s_rev,
/* 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)
+
+ if (dhandler != svn_delta_noop_window_handler)
{
- 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);
+ if (b->text_deltas)
+ {
+ /* if we send deltas against empty streams, we may use our
+ zero-copy code. */
+ if (b->zero_copy_limit > 0 && s_path == NULL)
+ {
+ zero_copy_baton_t baton;
+ svn_boolean_t called = FALSE;
+
+ baton.zero_copy_limit = b->zero_copy_limit;
+ baton.dhandler = dhandler;
+ baton.dbaton = dbaton;
+ baton.zero_copy_succeeded = FALSE;
+ SVN_ERR(svn_fs_try_process_file_contents(&called,
+ b->t_root, t_path,
+ send_zero_copy_delta,
+ &baton, pool));
+
+ /* data has been available and small enough,
+ i.e. been processed? */
+ if (called && baton.zero_copy_succeeded)
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(svn_fs_get_file_delta_stream(&dstream, s_root, s_path,
+ b->t_root, t_path, pool));
+ SVN_ERR(svn_txdelta_send_txstream(dstream, dhandler, dbaton, pool));
+ }
+ else
+ SVN_ERR(dhandler(NULL, dbaton));
}
- else
- return dhandler(NULL, dbaton);
+
+ return SVN_NO_ERROR;
}
/* Determine if the user is authorized to view B->t_root/PATH. */
@@ -647,8 +748,8 @@ 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);
+ return svn_error_trace(b->authz_read_func(allowed, b->t_root, path,
+ b->authz_read_baton, pool));
*allowed = TRUE;
return SVN_NO_ERROR;
}
@@ -774,9 +875,9 @@ add_file_smartly(report_baton_t *b,
}
}
- return b->editor->add_file(path, parent_baton,
- *copyfrom_path, *copyfrom_rev,
- pool, new_file_baton);
+ return svn_error_trace(b->editor->add_file(path, parent_baton,
+ *copyfrom_path, *copyfrom_rev,
+ pool, new_file_baton));
}
@@ -889,6 +990,21 @@ update_entry(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
SVN_ERR(svn_repos_deleted_rev(svn_fs_root_fs(b->t_root), t_path,
s_rev, b->t_rev, &deleted_rev,
pool));
+
+ if (!SVN_IS_VALID_REVNUM(deleted_rev))
+ {
+ /* Two possibilities: either the thing doesn't exist in S_REV; or
+ it wasn't deleted between S_REV and B->T_REV. In the first case,
+ I think we should leave DELETED_REV as SVN_INVALID_REVNUM, but
+ in the second, it should be set to B->T_REV-1 for the call to
+ delete_entry() below. */
+ svn_node_kind_t kind;
+
+ SVN_ERR(svn_fs_check_path(&kind, b->t_root, t_path, pool));
+ if (kind != svn_node_none)
+ deleted_rev = b->t_rev - 1;
+ }
+
SVN_ERR(b->editor->delete_entry(e_path, deleted_rev, dir_baton,
pool));
s_path = NULL;
@@ -896,7 +1012,7 @@ update_entry(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
/* If there's no target, we have nothing more to do. */
if (!t_entry)
- return skip_path_info(b, e_path);
+ return svn_error_trace(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));
@@ -906,7 +1022,7 @@ update_entry(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
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);
+ return svn_error_trace(skip_path_info(b, e_path));
}
if (t_entry->kind == svn_node_dir)
@@ -922,7 +1038,7 @@ update_entry(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
SVN_ERR(delta_dirs(b, s_rev, s_path, t_path, new_baton, e_path,
info ? info->start_empty : FALSE,
wc_depth, requested_depth, pool));
- return b->editor->close_directory(new_baton, pool);
+ return svn_error_trace(b->editor->close_directory(new_baton, pool));
}
else
{
@@ -954,7 +1070,8 @@ update_entry(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, b->t_root,
t_path, TRUE, pool));
hex_digest = svn_checksum_to_cstring(checksum, pool);
- return b->editor->close_file(new_baton, hex_digest, pool);
+ return svn_error_trace(b->editor->close_file(new_baton, hex_digest,
+ pool));
}
}
@@ -1026,7 +1143,8 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
svn_fs_root_t *s_root;
apr_hash_t *s_entries = NULL, *t_entries;
apr_hash_index_t *hi;
- apr_pool_t *subpool;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ apr_pool_t *iterpool;
const char *name, *s_fullpath, *t_fullpath, *e_fullpath;
path_info_t *info;
@@ -1035,7 +1153,8 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
When we support directory locks, we must pass the lock token here. */
SVN_ERR(delta_proplists(b, s_rev, start_empty ? NULL : s_path, t_path,
- NULL, change_dir_prop, dir_baton, pool));
+ NULL, change_dir_prop, dir_baton, subpool));
+ svn_pool_clear(subpool);
if (requested_depth > svn_depth_empty
|| requested_depth == svn_depth_unknown)
@@ -1044,19 +1163,19 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
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(&s_entries, s_root, s_path, subpool));
}
- SVN_ERR(svn_fs_dir_entries(&t_entries, b->t_root, t_path, pool));
+ SVN_ERR(svn_fs_dir_entries(&t_entries, b->t_root, t_path, subpool));
/* Iterate over the report information for this directory. */
- subpool = svn_pool_create(pool);
+ iterpool = svn_pool_create(pool);
while (1)
{
const svn_fs_dirent_t *s_entry, *t_entry;
- svn_pool_clear(subpool);
- SVN_ERR(fetch_path_info(b, &name, &info, e_path, subpool));
+ svn_pool_clear(iterpool);
+ SVN_ERR(fetch_path_info(b, &name, &info, e_path, iterpool));
if (!name)
break;
@@ -1072,16 +1191,16 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
item is a delete, remove the entry from the source hash,
but don't update the entry yet. */
if (s_entries)
- apr_hash_set(s_entries, name, APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(s_entries, name, NULL);
continue;
}
- e_fullpath = svn_relpath_join(e_path, name, subpool);
- t_fullpath = svn_fspath__join(t_path, name, subpool);
- t_entry = apr_hash_get(t_entries, name, APR_HASH_KEY_STRING);
- s_fullpath = s_path ? svn_fspath__join(s_path, name, subpool) : NULL;
+ e_fullpath = svn_relpath_join(e_path, name, iterpool);
+ t_fullpath = svn_fspath__join(t_path, name, iterpool);
+ t_entry = svn_hash_gets(t_entries, name);
+ s_fullpath = s_path ? svn_fspath__join(s_path, name, iterpool) : NULL;
s_entry = s_entries ?
- apr_hash_get(s_entries, name, APR_HASH_KEY_STRING) : NULL;
+ svn_hash_gets(s_entries, name) : NULL;
/* The only special cases here are
@@ -1099,15 +1218,15 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
t_entry, dir_baton, e_fullpath, info,
info ? info->depth
: DEPTH_BELOW_HERE(wc_depth),
- DEPTH_BELOW_HERE(requested_depth), subpool));
+ DEPTH_BELOW_HERE(requested_depth), iterpool));
/* Don't revisit this name in the target or source entries. */
- apr_hash_set(t_entries, name, APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(t_entries, name, NULL);
if (s_entries
/* Keep the entry for later process if it is reported as
excluded and got deleted in repos. */
&& (! info || info->depth != svn_depth_exclude || t_entry))
- apr_hash_set(s_entries, name, APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(s_entries, name, NULL);
/* pathinfo entries live in their own subpools due to lookahead,
so we need to clear each one out as we finish with it. */
@@ -1119,17 +1238,16 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
target, for graceful handling of case-only renames. */
if (s_entries)
{
- for (hi = apr_hash_first(pool, s_entries);
+ for (hi = apr_hash_first(subpool, s_entries);
hi;
hi = apr_hash_next(hi))
{
const svn_fs_dirent_t *s_entry;
- svn_pool_clear(subpool);
+ svn_pool_clear(iterpool);
s_entry = svn__apr_hash_index_val(hi);
- if (apr_hash_get(t_entries, s_entry->name,
- APR_HASH_KEY_STRING) == NULL)
+ if (svn_hash_gets(t_entries, s_entry->name) == NULL)
{
svn_revnum_t deleted_rev;
@@ -1143,27 +1261,29 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
continue;
/* There is no corresponding target entry, so delete. */
- e_fullpath = svn_relpath_join(e_path, s_entry->name, subpool);
+ e_fullpath = svn_relpath_join(e_path, s_entry->name, iterpool);
SVN_ERR(svn_repos_deleted_rev(svn_fs_root_fs(b->t_root),
svn_fspath__join(t_path,
s_entry->name,
- subpool),
+ iterpool),
s_rev, b->t_rev,
- &deleted_rev, subpool));
+ &deleted_rev, iterpool));
SVN_ERR(b->editor->delete_entry(e_fullpath,
deleted_rev,
- dir_baton, subpool));
+ dir_baton, iterpool));
}
}
}
/* Loop over the dirents in the target. */
- for (hi = apr_hash_first(pool, t_entries); hi; hi = apr_hash_next(hi))
+ for (hi = apr_hash_first(subpool, t_entries);
+ hi;
+ hi = apr_hash_next(hi))
{
const svn_fs_dirent_t *s_entry, *t_entry;
- svn_pool_clear(subpool);
+ svn_pool_clear(iterpool);
t_entry = svn__apr_hash_index_val(hi);
if (is_depth_upgrade(wc_depth, requested_depth, t_entry->kind))
@@ -1188,27 +1308,30 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path,
/* 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)
+ svn_hash_gets(s_entries, t_entry->name)
: NULL;
s_fullpath = s_entry ?
- svn_fspath__join(s_path, t_entry->name, subpool) : NULL;
+ svn_fspath__join(s_path, t_entry->name, iterpool) : NULL;
}
/* Compose the report, editor, and target paths for this entry. */
- e_fullpath = svn_relpath_join(e_path, t_entry->name, subpool);
- t_fullpath = svn_fspath__join(t_path, t_entry->name, subpool);
+ e_fullpath = svn_relpath_join(e_path, t_entry->name, iterpool);
+ t_fullpath = svn_fspath__join(t_path, t_entry->name, iterpool);
SVN_ERR(update_entry(b, s_rev, s_fullpath, s_entry, t_fullpath,
t_entry, dir_baton, e_fullpath, NULL,
DEPTH_BELOW_HERE(wc_depth),
DEPTH_BELOW_HERE(requested_depth),
- subpool));
+ iterpool));
}
/* Destroy iteration subpool. */
- svn_pool_destroy(subpool);
+ svn_pool_destroy(iterpool);
}
+
+ svn_pool_destroy(subpool);
+
return SVN_NO_ERROR;
}
@@ -1270,14 +1393,13 @@ drive(report_baton_t *b, svn_revnum_t s_rev, path_info_t *info,
t_entry, root_baton, b->s_operand, info,
info->depth, b->requested_depth, pool));
- return b->editor->close_directory(root_baton, pool);
+ return svn_error_trace(b->editor->close_directory(root_baton, pool));
}
/* 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;
@@ -1286,14 +1408,12 @@ finish_report(report_baton_t *b, apr_pool_t *pool)
/* 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));
+ /* Add the end marker. */
+ SVN_ERR(svn_spillbuf__reader_write(b->reader, "-", 1, 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));
+ SVN_ERR(read_path_info(&info, b->reader, 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,
@@ -1302,7 +1422,7 @@ finish_report(report_baton_t *b, apr_pool_t *pool)
/* Initialize the lookahead pathinfo. */
subpool = svn_pool_create(pool);
- SVN_ERR(read_path_info(&b->lookahead, b->tempfile, subpool));
+ SVN_ERR(read_path_info(&b->lookahead, b->reader, subpool));
if (b->lookahead && strcmp(b->lookahead->path, b->s_operand) == 0)
{
@@ -1320,7 +1440,7 @@ finish_report(report_baton_t *b, apr_pool_t *pool)
b->lookahead->depth = info->depth;
}
info = b->lookahead;
- SVN_ERR(read_path_info(&b->lookahead, b->tempfile, subpool));
+ SVN_ERR(read_path_info(&b->lookahead, b->reader, subpool));
}
/* Open the target root and initialize the source root cache. */
@@ -1329,7 +1449,8 @@ finish_report(report_baton_t *b, apr_pool_t *pool)
b->s_roots[i] = NULL;
{
- svn_error_t *err = drive(b, s_rev, info, pool);
+ svn_error_t *err = svn_error_trace(drive(b, s_rev, info, pool));
+
if (err == SVN_NO_ERROR)
return svn_error_trace(b->editor->close_edit(b->edit_baton, pool));
@@ -1342,7 +1463,7 @@ finish_report(report_baton_t *b, apr_pool_t *pool)
/* --- COLLECTING THE REPORT INFORMATION --- */
-/* Record a report operation into the temporary file. Return an error
+/* Record a report operation into the spill buffer. Return an error
if DEPTH is svn_depth_unknown. */
static svn_error_t *
write_path_info(report_baton_t *b, const char *path, const char *lpath,
@@ -1381,7 +1502,8 @@ write_path_info(report_baton_t *b, const char *path, const char *lpath,
rep = apr_psprintf(pool, "+%" APR_SIZE_T_FMT ":%s%s%s%s%c%s",
strlen(path), path, lrep, rrep, drep,
start_empty ? '+' : '-', ltrep);
- return svn_io_file_write_full(b->tempfile, rep, strlen(rep), NULL, pool);
+ return svn_error_trace(
+ svn_spillbuf__reader_write(b->reader, rep, strlen(rep), pool));
}
svn_error_t *
@@ -1389,8 +1511,9 @@ svn_repos_set_path3(void *baton, const char *path, svn_revnum_t rev,
svn_depth_t depth, svn_boolean_t start_empty,
const char *lock_token, apr_pool_t *pool)
{
- return write_path_info(baton, path, NULL, rev, depth, start_empty,
- lock_token, pool);
+ return svn_error_trace(
+ write_path_info(baton, path, NULL, rev, depth, start_empty,
+ lock_token, pool));
}
svn_error_t *
@@ -1403,8 +1526,9 @@ svn_repos_link_path3(void *baton, const char *path, const char *link_path,
return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL,
_("Depth 'exclude' not supported for link"));
- return write_path_info(baton, path, link_path, rev, depth,
- start_empty, lock_token, pool);
+ return svn_error_trace(
+ write_path_info(baton, path, link_path, rev, depth,
+ start_empty, lock_token, pool));
}
svn_error_t *
@@ -1412,35 +1536,30 @@ svn_repos_delete_path(void *baton, const char *path, apr_pool_t *pool)
{
/* We pass svn_depth_infinity because deletion of a path always
deletes everything underneath it. */
- return write_path_info(baton, path, NULL, SVN_INVALID_REVNUM,
- svn_depth_infinity, FALSE, NULL, pool);
+ return svn_error_trace(
+ write_path_info(baton, path, NULL, SVN_INVALID_REVNUM,
+ svn_depth_infinity, FALSE, NULL, 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);
-
- return svn_error_trace(svn_error_compose_create(finish_err, close_err));
+ return svn_error_trace(finish_report(b, pool));
}
svn_error_t *
svn_repos_abort_report(void *baton, apr_pool_t *pool)
{
- report_baton_t *b = baton;
-
- return svn_error_trace(svn_io_file_close(b->tempfile, pool));
+ return SVN_NO_ERROR;
}
/* --- BEGINNING THE REPORT --- */
svn_error_t *
-svn_repos_begin_report2(void **report_baton,
+svn_repos_begin_report3(void **report_baton,
svn_revnum_t revnum,
svn_repos_t *repos,
const char *fs_base,
@@ -1454,14 +1573,18 @@ svn_repos_begin_report2(void **report_baton,
void *edit_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
+ apr_size_t zero_copy_limit,
apr_pool_t *pool)
{
report_baton_t *b;
+ const char *uuid;
if (depth == svn_depth_exclude)
return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL,
_("Request depth 'exclude' not supported"));
+ SVN_ERR(svn_fs_get_uuid(repos->fs, &uuid, pool));
+
/* Build a reporter baton. Copy strings in case the caller doesn't
keep track of them. */
b = apr_palloc(pool, sizeof(*b));
@@ -1472,6 +1595,7 @@ svn_repos_begin_report2(void **report_baton,
b->t_path = switch_path ? svn_fspath__canonicalize(switch_path, pool)
: svn_fspath__join(b->fs_base, s_operand, pool);
b->text_deltas = text_deltas;
+ b->zero_copy_limit = zero_copy_limit;
b->requested_depth = depth;
b->ignore_ancestry = ignore_ancestry;
b->send_copyfrom_args = send_copyfrom_args;
@@ -1482,10 +1606,10 @@ svn_repos_begin_report2(void **report_baton,
b->authz_read_baton = authz_read_baton;
b->revision_infos = apr_hash_make(pool);
b->pool = pool;
-
- SVN_ERR(svn_io_open_unique_file3(&b->tempfile, NULL, NULL,
- svn_io_file_del_on_pool_cleanup,
- pool, pool));
+ b->reader = svn_spillbuf__reader_create(1000 /* blocksize */,
+ 1000000 /* maxsize */,
+ pool);
+ b->repos_uuid = svn_string_create(uuid, pool);
/* Hand reporter back to client. */
*report_baton = b;