📄 dump.c
字号:
SVN_ERR (dump_node (eb, path,
svn_node_file,
val ? svn_node_action_replace : svn_node_action_add,
is_copy,
is_copy ? copyfrom_path : NULL,
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
pool));
if (val)
/* delete the path, it's now been dumped. */
apr_hash_set (pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
*file_baton = NULL; /* muhahahaha */
return SVN_NO_ERROR;
}
static svn_error_t *
open_file (const char *path,
void *parent_baton,
svn_revnum_t ancestor_revision,
apr_pool_t *pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
const char *cmp_path = NULL;
svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;
/* If the parent directory has explicit comparison path and rev,
record the same for this one. */
if (pb && ARE_VALID_COPY_ARGS (pb->cmp_path, pb->cmp_rev))
{
cmp_path = svn_path_join (pb->cmp_path,
svn_path_basename (path, pool), pool);
cmp_rev = pb->cmp_rev;
}
SVN_ERR (dump_node (eb, path,
svn_node_file, svn_node_action_change,
FALSE, cmp_path, cmp_rev, pool));
*file_baton = NULL; /* muhahahaha again */
return SVN_NO_ERROR;
}
static svn_error_t *
change_dir_prop (void *parent_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct dir_baton *db = parent_baton;
struct edit_baton *eb = db->edit_baton;
/* This function is what distinguishes between a directory that is
opened to merely get somewhere, vs. one that is opened because it
*actually* changed by itself. */
if (! db->written_out)
{
SVN_ERR (dump_node (eb, db->path,
svn_node_dir, svn_node_action_change,
FALSE, db->cmp_path, db->cmp_rev, pool));
db->written_out = TRUE;
}
return SVN_NO_ERROR;
}
static svn_error_t *
get_dump_editor (const svn_delta_editor_t **editor,
void **edit_baton,
svn_fs_t *fs,
svn_revnum_t to_rev,
const char *root_path,
svn_stream_t *stream,
svn_stream_t *feedback_stream,
svn_revnum_t oldest_dumped_rev,
svn_boolean_t use_deltas,
apr_pool_t *pool)
{
/* Allocate an edit baton to be stored in every directory baton.
Set it up for the directory baton we create here, which is the
root baton. */
struct edit_baton *eb = apr_pcalloc (pool, sizeof (*eb));
svn_delta_editor_t *dump_editor = svn_delta_default_editor (pool);
/* Set up the edit baton. */
eb->stream = stream;
eb->feedback_stream = feedback_stream;
eb->oldest_dumped_rev = oldest_dumped_rev;
eb->bufsize = sizeof(eb->buffer);
eb->path = apr_pstrdup (pool, root_path);
SVN_ERR (svn_fs_revision_root (&(eb->fs_root), fs, to_rev, pool));
eb->current_rev = to_rev;
eb->use_deltas = use_deltas;
/* Set up the editor. */
dump_editor->open_root = open_root;
dump_editor->delete_entry = delete_entry;
dump_editor->add_directory = add_directory;
dump_editor->open_directory = open_directory;
dump_editor->close_directory = close_directory;
dump_editor->change_dir_prop = change_dir_prop;
dump_editor->add_file = add_file;
dump_editor->open_file = open_file;
*edit_baton = eb;
*editor = dump_editor;
return SVN_NO_ERROR;
}
/*----------------------------------------------------------------------*/
/** The main dumping routine, svn_repos_dump_fs. **/
/* Helper for svn_repos_dump_fs.
Write a revision record of REV in FS to writable STREAM, using POOL.
*/
static svn_error_t *
write_revision_record (svn_stream_t *stream,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
apr_size_t len;
apr_hash_t *props;
svn_stringbuf_t *encoded_prophash;
apr_time_t timetemp;
svn_string_t *datevalue;
/* Read the revision props even if we're aren't going to dump
them for verification purposes */
SVN_ERR (svn_fs_revision_proplist (&props, fs, rev, pool));
/* If we have no stream, and we aren't validating anything, we might
as well just go home now. */
if (! stream)
return SVN_NO_ERROR;
/* Run revision date properties through the time conversion to
canonize them. */
/* ### Remove this when it is no longer needed for sure. */
datevalue = apr_hash_get (props, SVN_PROP_REVISION_DATE,
APR_HASH_KEY_STRING);
if (datevalue)
{
SVN_ERR (svn_time_from_cstring (&timetemp, datevalue->data, pool));
datevalue = svn_string_create (svn_time_to_cstring (timetemp, pool),
pool);
apr_hash_set (props, SVN_PROP_REVISION_DATE, APR_HASH_KEY_STRING,
datevalue);
}
write_hash_to_stringbuf (props, NULL, &encoded_prophash, pool);
/* ### someday write a revision-content-checksum */
SVN_ERR (svn_stream_printf (stream, pool,
SVN_REPOS_DUMPFILE_REVISION_NUMBER
": %ld\n", rev));
SVN_ERR (svn_stream_printf (stream, pool,
SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
": %" APR_SIZE_T_FMT "\n",
encoded_prophash->len));
/* Write out a regular Content-length header for the benefit of
non-Subversion RFC-822 parsers. */
SVN_ERR (svn_stream_printf (stream, pool,
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
": %" APR_SIZE_T_FMT "\n\n",
encoded_prophash->len));
len = encoded_prophash->len;
SVN_ERR (svn_stream_write (stream, encoded_prophash->data, &len));
len = 1;
SVN_ERR (svn_stream_write (stream, "\n", &len));
return SVN_NO_ERROR;
}
/* The main dumper. */
svn_error_t *
svn_repos_dump_fs2 (svn_repos_t *repos,
svn_stream_t *stream,
svn_stream_t *feedback_stream,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_boolean_t incremental,
svn_boolean_t use_deltas,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const svn_delta_editor_t *dump_editor;
void *dump_edit_baton;
svn_revnum_t i;
svn_fs_t *fs = svn_repos_fs (repos);
apr_pool_t *subpool = svn_pool_create (pool);
svn_revnum_t youngest;
const char *uuid;
svn_boolean_t dumping = (stream != NULL);
/* Determine the current youngest revision of the filesystem. */
SVN_ERR (svn_fs_youngest_rev (&youngest, fs, pool));
/* Use default vals if necessary. */
if (! SVN_IS_VALID_REVNUM(start_rev))
start_rev = 0;
if (! SVN_IS_VALID_REVNUM(end_rev))
end_rev = youngest;
if (! stream)
stream = svn_stream_empty (pool);
/* Validate the revisions. */
if (start_rev > end_rev)
return svn_error_createf (SVN_ERR_REPOS_BAD_ARGS, NULL,
_("Start revision %ld"
" is greater than end revision %ld"),
start_rev, end_rev);
if (end_rev > youngest)
return svn_error_createf (SVN_ERR_REPOS_BAD_ARGS, NULL,
_("End revision %ld is invalid "
"(youngest revision is %ld)"),
end_rev, youngest);
if ((start_rev == 0) && incremental)
incremental = FALSE; /* revision 0 looks the same regardless of
whether or not this is an incremental
dump, so just simplify things. */
/* Write out the UUID. */
SVN_ERR (svn_fs_get_uuid(fs, &uuid, pool));
/* Write out "general" metadata for the dumpfile. In this case, a
magic header followed by a dumpfile format version. */
if (stream)
{
int version = SVN_REPOS_DUMPFILE_FORMAT_VERSION;
/* If we're not using deltas, use the previous version, for
compatibility with svn 1.0.x. */
if (!use_deltas)
version--;
SVN_ERR (svn_stream_printf (stream, pool,
SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
version));
SVN_ERR (svn_stream_printf (stream, pool, SVN_REPOS_DUMPFILE_UUID
": %s\n\n", uuid));
}
/* Main loop: we're going to dump revision i. */
for (i = start_rev; i <= end_rev; i++)
{
svn_revnum_t from_rev, to_rev;
svn_fs_root_t *to_root;
svn_boolean_t use_deltas_for_rev;
/* Check for cancellation. */
if (cancel_func)
SVN_ERR (cancel_func (cancel_baton));
/* Special-case the initial revision dump: it needs to contain
*all* nodes, because it's the foundation of all future
revisions in the dumpfile. */
if ((i == start_rev) && (! incremental))
{
/* Special-special-case a dump of revision 0. */
if (i == 0)
{
/* Just write out the one revision 0 record and move on.
The parser might want to use its properties. */
SVN_ERR (write_revision_record (stream, fs, 0, subpool));
to_rev = 0;
goto loop_end;
}
/* Compare START_REV to revision 0, so that everything
appears to be added. */
from_rev = 0;
to_rev = i;
}
else
{
/* In the normal case, we want to compare consecutive revs. */
from_rev = i - 1;
to_rev = i;
}
/* Write the revision record. */
SVN_ERR (write_revision_record (stream, fs, to_rev, subpool));
/* Fetch the editor which dumps nodes to a file. Regardless of
what we've been told, don't use deltas for the first rev of a
non-incremental dump. */
use_deltas_for_rev = use_deltas && (incremental || i != start_rev);
SVN_ERR (get_dump_editor (&dump_editor, &dump_edit_baton, fs, to_rev,
"/", stream, feedback_stream, start_rev,
use_deltas_for_rev, subpool));
/* Drive the editor in one way or another. */
SVN_ERR (svn_fs_revision_root (&to_root, fs, to_rev, subpool));
/* If this is the first revision of a non-incremental dump,
we're in for a full tree dump. Othersise, we want to simply
replay the revision. */
if ((i == start_rev) && (! incremental))
{
svn_fs_root_t *from_root;
SVN_ERR (svn_fs_revision_root (&from_root, fs, from_rev, subpool));
SVN_ERR (svn_repos_dir_delta (from_root, "/", "",
to_root, "/",
dump_editor, dump_edit_baton,
NULL,
NULL,
FALSE, /* don't send text-deltas */
TRUE, /* recurse */
FALSE, /* don't send entry props */
FALSE, /* don't ignore ancestry */
subpool));
}
else
{
SVN_ERR (svn_repos_replay (to_root, dump_editor,
dump_edit_baton, subpool));
}
loop_end:
/* Reuse all memory consumed by the dump of this one revision. */
svn_pool_clear (subpool);
if (feedback_stream)
SVN_ERR (svn_stream_printf (feedback_stream, pool,
_("* %s revision %ld.\n"),
dumping ? "Dumped" : "Verified",
to_rev));
}
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_dump_fs (svn_repos_t *repos,
svn_stream_t *stream,
svn_stream_t *feedback_stream,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_boolean_t incremental,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
return svn_repos_dump_fs2 (repos, stream, feedback_stream, start_rev,
end_rev, incremental, FALSE, cancel_func,
cancel_baton, pool);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -