You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2011/12/19 22:10:15 UTC
svn commit: r1220946 - in
/subversion/branches/fs-py/subversion/libsvn_fs_py: ./ dag.c fs.c fs_fs.c
fs_fs.h key-gen.c lock.c rep-cache-db.sql rep-cache.c rep-cache.h
temp_serializer.c temp_serializer.h tree.c tree.h
Author: hwright
Date: Mon Dec 19 21:10:15 2011
New Revision: 1220946
URL: http://svn.apache.org/viewvc?rev=1220946&view=rev
Log:
On the fs-py branch:
Bring libsvn_fs_py up-to-date with libsvn_fs_fs. This resolves conflicts, but
still leaves a number of build errors, which will be resolved shortly.
Modified:
subversion/branches/fs-py/subversion/libsvn_fs_py/ (props changed)
subversion/branches/fs-py/subversion/libsvn_fs_py/dag.c
subversion/branches/fs-py/subversion/libsvn_fs_py/fs.c
subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.c
subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.h
subversion/branches/fs-py/subversion/libsvn_fs_py/key-gen.c
subversion/branches/fs-py/subversion/libsvn_fs_py/lock.c
subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache-db.sql
subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.c
subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.h
subversion/branches/fs-py/subversion/libsvn_fs_py/temp_serializer.c (props changed)
subversion/branches/fs-py/subversion/libsvn_fs_py/temp_serializer.h (props changed)
subversion/branches/fs-py/subversion/libsvn_fs_py/tree.c
subversion/branches/fs-py/subversion/libsvn_fs_py/tree.h
Propchange: subversion/branches/fs-py/subversion/libsvn_fs_py/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Dec 19 21:10:15 2011
@@ -8,7 +8,7 @@
/subversion/branches/double-delete/subversion/libsvn_fs_py:870511-872970
/subversion/branches/explore-wc/subversion/libsvn_fs_py:875486,875493,875497,875507,875511,875514,875559,875580-875581,875584,875587,875611,875627,875647,875667-875668,875711-875712,875733-875734,875736,875744-875748,875751,875758,875782,875795-875796,875830,875836,875838,875842,875852,875855,875864,875870,875873,875880,875885-875888,875890,875897-875898,875905,875907-875909,875935,875943-875944,875946,875979,875982-875983,875985-875986,875990,875997
/subversion/branches/file-externals/subversion/libsvn_fs_py:871779-873302
-/subversion/branches/fs-py/subversion/libsvn_fs_fs:1154225-1182878
+/subversion/branches/fs-py/subversion/libsvn_fs_fs:1154225-1220896
/subversion/branches/fs-rep-sharing/subversion/libsvn_fs_py:869036-873803
/subversion/branches/fsfs-pack/subversion/libsvn_fs_py:873717-874575
/subversion/branches/gnome-keyring/subversion/libsvn_fs_py:870558-871410
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/dag.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/dag.c?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/dag.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/dag.c Mon Dec 19 21:10:15 2011
@@ -449,7 +449,7 @@ svn_fs_py__dag_dir_entry(svn_fs_dirent_t
/* Get a dirent hash for this directory. */
return svn_fs_py__rep_contents_dir_entry(dirent, node->fs,
- noderev, name, pool);
+ noderev, name, pool, pool);
}
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/fs.c?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/fs.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/fs.c Mon Dec 19 21:10:15 2011
@@ -280,16 +280,28 @@ fs_pack(svn_fs_t *fs,
/* This implements the fs_library_vtable_t.hotcopy() API. Copy a
- possibly live Subversion filesystem from SRC_PATH to DEST_PATH.
+ possibly live Subversion filesystem SRC_FS from SRC_PATH to a
+ DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
+ re-copy data which already exists in DST_FS.
The CLEAN_LOGS argument is ignored and included for Subversion
1.0.x compatibility. Perform all temporary allocations in POOL. */
static svn_error_t *
-fs_hotcopy(const char *src_path,
- const char *dest_path,
+fs_hotcopy(svn_fs_t *src_fs,
+ svn_fs_t *dst_fs,
+ const char *src_path,
+ const char *dst_path,
svn_boolean_t clean_logs,
+ svn_boolean_t incremental,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
apr_pool_t *pool)
{
- return svn_fs_py__hotcopy(src_path, dest_path, pool);
+ SVN_ERR(initialize_fs_struct(src_fs));
+ SVN_ERR(fs_serialized_init(src_fs, pool, pool));
+ SVN_ERR(initialize_fs_struct(dst_fs));
+ SVN_ERR(fs_serialized_init(dst_fs, pool, pool));
+ return svn_fs_py__hotcopy(src_fs, dst_fs, src_path, dst_path,
+ incremental, cancel_func, cancel_baton, pool);
}
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.c?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.c Mon Dec 19 21:10:15 2011
@@ -1412,238 +1412,6 @@ get_youngest(svn_revnum_t *youngest_p,
return SVN_NO_ERROR;
}
-svn_error_t *
-svn_fs_py__hotcopy(const char *src_path,
- const char *dst_path,
- apr_pool_t *pool)
-{
- const char *src_subdir, *dst_subdir;
- svn_revnum_t youngest, rev, min_unpacked_rev;
- apr_pool_t *iterpool;
- svn_node_kind_t kind;
- int format, max_files_per_dir;
-
- /* Check format to be sure we know how to hotcopy this FS. */
- SVN_ERR(read_format(&format, &max_files_per_dir,
- svn_dirent_join(src_path, PATH_FORMAT, pool),
- pool));
- SVN_ERR(check_format(format));
-
- /* Try to copy the config.
- *
- * ### We try copying the config file before doing anything else,
- * ### because higher layers will abort the hotcopy if we throw
- * ### an error from this function, and that renders the hotcopy
- * ### unusable anyway. */
- if (format >= SVN_FS_FS__MIN_CONFIG_FILE)
- {
- svn_error_t *err;
-
- err = svn_io_dir_file_copy(src_path, dst_path, PATH_CONFIG, pool);
- if (err)
- {
- if (APR_STATUS_IS_ENOENT(err->apr_err))
- {
- /* 1.6.0 to 1.6.11 did not copy the configuration file during
- * hotcopy. So if we're hotcopying a repository which has been
- * created as a hotcopy itself, it's possible that fsfs.conf
- * does not exist. Ask the user to re-create it.
- *
- * ### It would be nice to make this a non-fatal error,
- * ### but this function does not get an svn_fs_t object
- * ### so we have no way of just printing a warning via
- * ### the fs->warning() callback. */
-
- const char *msg;
- const char *src_abspath;
- const char *dst_abspath;
- const char *config_relpath;
- svn_error_t *err2;
-
- config_relpath = svn_dirent_join(src_path, PATH_CONFIG, pool);
- err2 = svn_dirent_get_absolute(&src_abspath, src_path, pool);
- if (err2)
- return svn_error_trace(svn_error_compose_create(err, err2));
- err2 = svn_dirent_get_absolute(&dst_abspath, dst_path, pool);
- if (err2)
- return svn_error_trace(svn_error_compose_create(err, err2));
-
- /* ### hack: strip off the 'db/' directory from paths so
- * ### they make sense to the user */
- src_abspath = svn_dirent_dirname(src_abspath, pool);
- dst_abspath = svn_dirent_dirname(dst_abspath, pool);
-
- msg = apr_psprintf(pool,
- _("Failed to create hotcopy at '%s'. "
- "The file '%s' is missing from the source "
- "repository. Please create this file, for "
- "instance by running 'svnadmin upgrade %s'"),
- dst_abspath, config_relpath, src_abspath);
- return svn_error_quick_wrap(err, msg);
- }
- else
- return svn_error_trace(err);
- }
- }
-
- /* Copy the 'current' file. */
- SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_CURRENT, pool));
-
- /* Copy the uuid. */
- SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_UUID, pool));
-
- /* Copy the rep cache before copying the rev files to make sure all
- cached references will be present in the copy. */
- src_subdir = svn_dirent_join(src_path, REP_CACHE_DB_NAME, pool);
- dst_subdir = svn_dirent_join(dst_path, REP_CACHE_DB_NAME, pool);
- SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
- if (kind == svn_node_file)
- SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));
-
- /* Copy the min unpacked rev, and read its value. */
- if (format >= SVN_FS_FS__MIN_PACKED_FORMAT)
- {
- const char *min_unpacked_rev_path;
- min_unpacked_rev_path = svn_dirent_join(src_path, PATH_MIN_UNPACKED_REV,
- pool);
-
- SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_MIN_UNPACKED_REV,
- pool));
- SVN_ERR(read_min_unpacked_rev(&min_unpacked_rev, min_unpacked_rev_path,
- pool));
- }
- else
- {
- min_unpacked_rev = 0;
- }
-
- /* Find the youngest revision from this 'current' file. */
- SVN_ERR(get_youngest(&youngest, dst_path, pool));
-
- /* Copy the necessary rev files. */
- src_subdir = svn_dirent_join(src_path, PATH_REVS_DIR, pool);
- dst_subdir = svn_dirent_join(dst_path, PATH_REVS_DIR, pool);
-
- SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
-
- iterpool = svn_pool_create(pool);
- /* First, copy packed shards. */
- for (rev = 0; rev < min_unpacked_rev; rev += max_files_per_dir)
- {
- const char *packed_shard = apr_psprintf(iterpool, "%ld.pack",
- rev / max_files_per_dir);
- const char *src_subdir_packed_shard;
- src_subdir_packed_shard = svn_dirent_join(src_subdir, packed_shard,
- iterpool);
-
- SVN_ERR(svn_io_copy_dir_recursively(src_subdir_packed_shard,
- dst_subdir, packed_shard,
- TRUE /* copy_perms */,
- NULL /* cancel_func */, NULL,
- iterpool));
- svn_pool_clear(iterpool);
- }
-
- /* Then, copy non-packed shards. */
- SVN_ERR_ASSERT(rev == min_unpacked_rev);
- for (; rev <= youngest; rev++)
- {
- const char *src_subdir_shard = src_subdir,
- *dst_subdir_shard = dst_subdir;
-
- if (max_files_per_dir)
- {
- const char *shard = apr_psprintf(iterpool, "%ld",
- rev / max_files_per_dir);
- src_subdir_shard = svn_dirent_join(src_subdir, shard, iterpool);
- dst_subdir_shard = svn_dirent_join(dst_subdir, shard, iterpool);
-
- if (rev % max_files_per_dir == 0)
- {
- SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
- iterpool));
- SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
- iterpool));
- }
- }
-
- SVN_ERR(svn_io_dir_file_copy(src_subdir_shard, dst_subdir_shard,
- apr_psprintf(iterpool, "%ld", rev),
- iterpool));
- svn_pool_clear(iterpool);
- }
-
- /* Copy the necessary revprop files. */
- src_subdir = svn_dirent_join(src_path, PATH_REVPROPS_DIR, pool);
- dst_subdir = svn_dirent_join(dst_path, PATH_REVPROPS_DIR, pool);
-
- SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
-
- for (rev = 0; rev <= youngest; rev++)
- {
- const char *src_subdir_shard = src_subdir,
- *dst_subdir_shard = dst_subdir;
-
- svn_pool_clear(iterpool);
-
- if (max_files_per_dir)
- {
- const char *shard = apr_psprintf(iterpool, "%ld",
- rev / max_files_per_dir);
- src_subdir_shard = svn_dirent_join(src_subdir, shard, iterpool);
- dst_subdir_shard = svn_dirent_join(dst_subdir, shard, iterpool);
-
- if (rev % max_files_per_dir == 0)
- {
- SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
- iterpool));
- SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
- iterpool));
- }
- }
-
- SVN_ERR(svn_io_dir_file_copy(src_subdir_shard, dst_subdir_shard,
- apr_psprintf(iterpool, "%ld", rev),
- iterpool));
- }
-
- svn_pool_destroy(iterpool);
-
- /* Make an empty transactions directory for now. Eventually some
- method of copying in progress transactions will need to be
- developed.*/
- dst_subdir = svn_dirent_join(dst_path, PATH_TXNS_DIR, pool);
- SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
- if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
- {
- dst_subdir = svn_dirent_join(dst_path, PATH_TXN_PROTOS_DIR, pool);
- SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
- }
-
- /* Now copy the locks tree. */
- src_subdir = svn_dirent_join(src_path, PATH_LOCKS_DIR, pool);
- SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
- if (kind == svn_node_dir)
- SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path,
- PATH_LOCKS_DIR, TRUE, NULL,
- NULL, pool));
-
- /* Now copy the node-origins cache tree. */
- src_subdir = svn_dirent_join(src_path, PATH_NODE_ORIGINS_DIR, pool);
- SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
- if (kind == svn_node_dir)
- SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path,
- PATH_NODE_ORIGINS_DIR, TRUE, NULL,
- NULL, pool));
-
- /* Copy the txn-current file. */
- if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
- SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_TXN_CURRENT, pool));
-
- /* Hotcopied FS is complete. Stamp it with a format file. */
- return write_format(svn_dirent_join(dst_path, PATH_FORMAT, pool),
- format, max_files_per_dir, FALSE, pool);
-}
svn_error_t *
svn_fs_py__youngest_rev(svn_revnum_t *youngest_p,
@@ -3651,7 +3419,8 @@ svn_fs_py__rep_contents_dir_entry(svn_fs
svn_fs_t *fs,
node_revision_t *noderev,
const char *name,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_boolean_t found = FALSE;
@@ -3662,29 +3431,22 @@ svn_fs_py__rep_contents_dir_entry(svn_fs
svn_fs_dirent_t *entry;
svn_fs_dirent_t *entry_copy = NULL;
- /* since we don't need the directory content later on, put it into
- some sub-pool that will be reclaimed immedeately after exiting
- this function successfully. Opon failure, it will live as long
- as pool.
- */
- apr_pool_t *sub_pool = svn_pool_create(pool);
-
/* read the dir from the file system. It will probably be put it
into the cache for faster lookup in future calls. */
- SVN_ERR(svn_fs_py__rep_contents_dir(&entries, fs, noderev, sub_pool));
+ SVN_ERR(svn_fs_py__rep_contents_dir(&entries, fs, noderev,
+ scratch_pool));
/* find desired entry and return a copy in POOL, if found */
entry = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
if (entry != NULL)
{
- entry_copy = apr_palloc(pool, sizeof(*entry_copy));
- entry_copy->name = apr_pstrdup(pool, entry->name);
- entry_copy->id = svn_fs_py__id_copy(entry->id, pool);
+ entry_copy = apr_palloc(result_pool, sizeof(*entry_copy));
+ entry_copy->name = apr_pstrdup(result_pool, entry->name);
+ entry_copy->id = svn_fs_py__id_copy(entry->id, result_pool);
entry_copy->kind = entry->kind;
}
*dirent = entry_copy;
- apr_pool_destroy(sub_pool);
}
return SVN_NO_ERROR;
@@ -5457,17 +5219,50 @@ write_hash_rep(svn_filesize_t *size,
}
/* Sanity check ROOT_NODEREV, a candidate for being the root node-revision
- of (not yet committed) revision REV. Use OCEAN for temporary allocations.
+ of (not yet committed) revision REV in FS. Use POOL for temporary
+ allocations.
*/
-static APR_INLINE svn_error_t *
-validate_root_noderev(node_revision_t *root_noderev,
- svn_revnum_t rev)
+static svn_error_t *
+validate_root_noderev(svn_fs_t *fs,
+ node_revision_t *root_noderev,
+ svn_revnum_t rev,
+ apr_pool_t *pool)
{
- /* Bogosity seen on svn.apache.org; see
+ svn_revnum_t head_revnum = rev-1;
+ int head_predecessor_count;
+
+ SVN_ERR_ASSERT(rev > 0);
+
+ /* Compute HEAD_PREDECESSOR_COUNT. */
+ {
+ svn_fs_root_t *head_revision;
+ const svn_fs_id_t *head_root_id;
+ node_revision_t *head_root_noderev;
+
+ /* Get /@HEAD's noderev. */
+ SVN_ERR(svn_fs_py__revision_root(&head_revision, fs, head_revnum, pool));
+ SVN_ERR(svn_fs_py__node_id(&head_root_id, head_revision, "/", pool));
+ SVN_ERR(svn_fs_py__get_node_revision(&head_root_noderev, fs, head_root_id,
+ pool));
+
+ head_predecessor_count = head_root_noderev->predecessor_count;
+ }
+
+ /* Check that the root noderev's predecessor count equals REV.
+
+ This kind of corruption was seen on svn.apache.org (both on
+ the root noderev and on other fspaths' noderevs); see
http://mid.gmane.org/20111002202833.GA12373@daniel3.local
+
+ Normally (rev == root_noderev->predecessor_count), but here we
+ use a more roundabout check that should only trigger on new instances
+ of the corruption, rather then trigger on each and every new commit
+ to a repository that has triggered the bug somewhere in its root
+ noderev's history.
*/
if (root_noderev->predecessor_count != -1
- && root_noderev->predecessor_count != rev)
+ && (root_noderev->predecessor_count - head_predecessor_count)
+ != (rev - head_revnum))
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("predecessor count for "
@@ -5651,7 +5446,7 @@ write_final_rev(const svn_fs_id_t **new_
/* Write out our new node-revision. */
if (at_root)
- SVN_ERR(validate_root_noderev(noderev, rev));
+ SVN_ERR(validate_root_noderev(fs, noderev, rev, pool));
SVN_ERR(svn_fs_py__write_noderev(svn_stream_from_aprfile2(file, TRUE, pool),
noderev, format,
svn_fs_py__fs_supports_mergeinfo(fs),
@@ -5992,7 +5787,7 @@ commit_body(void *baton, apr_pool_t *poo
fails because the shard already existed for some reason. */
if (max_files_per_dir && new_rev % max_files_per_dir == 0)
{
- if (1)
+ /* if (1); null condition for easier merging to revprop-packing */
{
const char *new_dir = path_rev_shard(cb->fs, new_rev, pool);
svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
@@ -6548,7 +6343,7 @@ recover_body(void *baton, apr_pool_t *po
&youngest_revprops_kind, pool));
if (youngest_revprops_kind == svn_node_none)
{
- if (1)
+ /* if (1); null condition for easier merging to revprop-packing */
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Revision %ld has a revs file but no "
@@ -6564,6 +6359,10 @@ recover_body(void *baton, apr_pool_t *po
max_rev);
}
+ /* Prune younger-than-(newfound-youngest) revisions from the rep cache. */
+ if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
+ SVN_ERR(svn_fs_fs__del_rep_reference(fs, max_rev, pool));
+
/* Now store the discovered youngest revision, and the next IDs if
relevant, in a new 'current' file. */
return write_current(fs, max_rev, next_node_id, next_copy_id, pool);
@@ -7112,3 +6911,934 @@ svn_fs_py__verify(svn_fs_t *fs,
return SVN_NO_ERROR;
}
+
+
+
+/** Hotcopy. **/
+
+/* Like svn_io_dir_file_copy(), but doesn't copy files that exist at
+ * the destination and do not differ in terms of kind, size, and mtime. */
+static svn_error_t *
+hotcopy_io_dir_file_copy(const char *src_path,
+ const char *dst_path,
+ const char *file,
+ apr_pool_t *scratch_pool)
+{
+ const svn_io_dirent2_t *src_dirent;
+ const svn_io_dirent2_t *dst_dirent;
+ const char *src_target;
+ const char *dst_target;
+
+ /* Does the destination already exist? If not, we must copy it. */
+ dst_target = svn_dirent_join(dst_path, file, scratch_pool);
+ SVN_ERR(svn_io_stat_dirent(&dst_dirent, dst_target, TRUE,
+ scratch_pool, scratch_pool));
+ if (dst_dirent->kind != svn_node_none)
+ {
+ /* If the destination's stat information indicates that the file
+ * is equal to the source, don't bother copying the file again. */
+ src_target = svn_dirent_join(src_path, file, scratch_pool);
+ SVN_ERR(svn_io_stat_dirent(&src_dirent, src_target, FALSE,
+ scratch_pool, scratch_pool));
+ if (src_dirent->kind == dst_dirent->kind &&
+ src_dirent->special == dst_dirent->special &&
+ src_dirent->filesize == dst_dirent->filesize &&
+ src_dirent->mtime <= dst_dirent->mtime)
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(svn_io_dir_file_copy(src_path, dst_path, file,
+ scratch_pool));
+}
+
+/* Set *NAME_P to the UTF-8 representation of directory entry NAME.
+ * NAME is in the the internal encoding used by APR; PARENT is in
+ * UTF-8 and in internal (not local) style.
+ *
+ * Use PARENT only for generating an error string if the conversion
+ * fails because NAME could not be represented in UTF-8. In that
+ * case, return a two-level error in which the outer error's message
+ * mentions PARENT, but the inner error's message does not mention
+ * NAME (except possibly in hex) since NAME may not be printable.
+ * Such a compound error at least allows the user to go looking in the
+ * right directory for the problem.
+ *
+ * If there is any other error, just return that error directly.
+ *
+ * If there is any error, the effect on *NAME_P is undefined.
+ *
+ * *NAME_P and NAME may refer to the same storage.
+ */
+static svn_error_t *
+entry_name_to_utf8(const char **name_p,
+ const char *name,
+ const char *parent,
+ apr_pool_t *pool)
+{
+ svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
+ if (err && err->apr_err == APR_EINVAL)
+ {
+ return svn_error_createf(err->apr_err, err,
+ _("Error converting entry "
+ "in directory '%s' to UTF-8"),
+ svn_dirent_local_style(parent, pool));
+ }
+ return err;
+}
+
+/* Like svn_io_copy_dir_recursively() but doesn't copy regular files that
+ * exist in the destination and do not differ from the source in terms of
+ * kind, size, and mtime. */
+static svn_error_t *
+hotcopy_io_copy_dir_recursively(const char *src,
+ const char *dst_parent,
+ const char *dst_basename,
+ svn_boolean_t copy_perms,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool)
+{
+ svn_node_kind_t kind;
+ apr_status_t status;
+ const char *dst_path;
+ apr_dir_t *this_dir;
+ apr_finfo_t this_entry;
+ apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
+
+ /* Make a subpool for recursion */
+ apr_pool_t *subpool = svn_pool_create(pool);
+
+ /* The 'dst_path' is simply dst_parent/dst_basename */
+ dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
+
+ /* Sanity checks: SRC and DST_PARENT are directories, and
+ DST_BASENAME doesn't already exist in DST_PARENT. */
+ SVN_ERR(svn_io_check_path(src, &kind, subpool));
+ if (kind != svn_node_dir)
+ return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Source '%s' is not a directory"),
+ svn_dirent_local_style(src, pool));
+
+ SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
+ if (kind != svn_node_dir)
+ return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Destination '%s' is not a directory"),
+ svn_dirent_local_style(dst_parent, pool));
+
+ SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
+
+ /* Create the new directory. */
+ /* ### TODO: copy permissions (needs apr_file_attrs_get()) */
+ SVN_ERR(svn_io_make_dir_recursively(dst_path, pool));
+
+ /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
+ SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
+
+ for (status = apr_dir_read(&this_entry, flags, this_dir);
+ status == APR_SUCCESS;
+ status = apr_dir_read(&this_entry, flags, this_dir))
+ {
+ if ((this_entry.name[0] == '.')
+ && ((this_entry.name[1] == '\0')
+ || ((this_entry.name[1] == '.')
+ && (this_entry.name[2] == '\0'))))
+ {
+ continue;
+ }
+ else
+ {
+ const char *entryname_utf8;
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
+ src, subpool));
+ if (this_entry.filetype == APR_REG) /* regular file */
+ {
+ SVN_ERR(hotcopy_io_dir_file_copy(src, dst_path, entryname_utf8,
+ subpool));
+ }
+ else if (this_entry.filetype == APR_LNK) /* symlink */
+ {
+ const char *src_target = svn_dirent_join(src, entryname_utf8,
+ subpool);
+ const char *dst_target = svn_dirent_join(dst_path,
+ entryname_utf8,
+ subpool);
+ SVN_ERR(svn_io_copy_link(src_target, dst_target,
+ subpool));
+ }
+ else if (this_entry.filetype == APR_DIR) /* recurse */
+ {
+ const char *src_target;
+
+ /* Prevent infinite recursion by filtering off our
+ newly created destination path. */
+ if (strcmp(src, dst_parent) == 0
+ && strcmp(entryname_utf8, dst_basename) == 0)
+ continue;
+
+ src_target = svn_dirent_join(src, entryname_utf8, subpool);
+ SVN_ERR(hotcopy_io_copy_dir_recursively(src_target,
+ dst_path,
+ entryname_utf8,
+ copy_perms,
+ cancel_func,
+ cancel_baton,
+ subpool));
+ }
+ /* ### support other APR node types someday?? */
+
+ }
+ }
+
+ if (! (APR_STATUS_IS_ENOENT(status)))
+ return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
+ svn_dirent_local_style(src, pool));
+
+ status = apr_dir_close(this_dir);
+ if (status)
+ return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
+ svn_dirent_local_style(src, pool));
+
+ /* Free any memory used by recursion */
+ svn_pool_destroy(subpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Copy an un-packed revision or revprop file for revision REV from SRC_SUBDIR
+ * to DST_SUBDIR. Assume a sharding layout based on MAX_FILES_PER_DIR.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+hotcopy_copy_shard_file(const char *src_subdir,
+ const char *dst_subdir,
+ svn_revnum_t rev,
+ int max_files_per_dir,
+ apr_pool_t *scratch_pool)
+{
+ const char *src_subdir_shard = src_subdir,
+ *dst_subdir_shard = dst_subdir;
+
+ if (max_files_per_dir)
+ {
+ const char *shard = apr_psprintf(scratch_pool, "%ld",
+ rev / max_files_per_dir);
+ src_subdir_shard = svn_dirent_join(src_subdir, shard, scratch_pool);
+ dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
+
+ if (rev % max_files_per_dir == 0)
+ {
+ SVN_ERR(svn_io_make_dir_recursively(dst_subdir_shard, scratch_pool));
+ SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
+ scratch_pool));
+ }
+ }
+
+ SVN_ERR(hotcopy_io_dir_file_copy(src_subdir_shard, dst_subdir_shard,
+ apr_psprintf(scratch_pool, "%ld", rev),
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+
+/* Copy a packed shard containing revision REV, and which contains
+ * MAX_FILES_PER_DIR revisions, from SRC_FS to DST_FS.
+ * Update *DST_MIN_UNPACKED_REV in case the shard is new in DST_FS.
+ * Do not re-copy data which already exists in DST_FS.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+hotcopy_copy_packed_shard(svn_revnum_t *dst_min_unpacked_rev,
+ svn_fs_t *src_fs,
+ svn_fs_t *dst_fs,
+ svn_revnum_t rev,
+ int max_files_per_dir,
+ apr_pool_t *scratch_pool)
+{
+ const char *src_subdir;
+ const char *dst_subdir;
+ const char *packed_shard;
+ const char *src_subdir_packed_shard;
+ svn_revnum_t shard_rev;
+ svn_revnum_t revprop_rev;
+ apr_pool_t *iterpool;
+
+ /* Copy the pack file. */
+ src_subdir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, scratch_pool);
+ dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, scratch_pool);
+ shard_rev = rev / max_files_per_dir;
+ packed_shard = apr_psprintf(scratch_pool, "%ld.pack", shard_rev);
+ src_subdir_packed_shard = svn_dirent_join(src_subdir, packed_shard,
+ scratch_pool);
+ SVN_ERR(hotcopy_io_copy_dir_recursively(src_subdir_packed_shard,
+ dst_subdir, packed_shard,
+ TRUE /* copy_perms */,
+ NULL /* cancel_func */, NULL,
+ scratch_pool));
+
+ /* Copy revprops belonging to revisions in this pack. */
+ src_subdir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, scratch_pool);
+ dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, scratch_pool);
+ iterpool = svn_pool_create(scratch_pool);
+ for (revprop_rev = shard_rev;
+ revprop_rev < shard_rev + max_files_per_dir;
+ revprop_rev++)
+ {
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(hotcopy_copy_shard_file(src_subdir, dst_subdir,
+ rev, max_files_per_dir,
+ iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ /* If necessary, update the min-unpacked rev file in the hotcopy. */
+ if (*dst_min_unpacked_rev < shard_rev + max_files_per_dir)
+ {
+ *dst_min_unpacked_rev = shard_rev + max_files_per_dir;
+ SVN_ERR(write_revnum_file(dst_fs->path, PATH_MIN_UNPACKED_REV,
+ *dst_min_unpacked_rev,
+ scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* If NEW_YOUNGEST is younger than *DST_YOUNGEST, update the 'current'
+ * file in DST_FS and set *DST_YOUNGEST to NEW_YOUNGEST.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+hotcopy_update_current(svn_revnum_t *dst_youngest,
+ svn_fs_t *dst_fs,
+ svn_revnum_t new_youngest,
+ apr_pool_t *scratch_pool)
+{
+ char next_node_id[MAX_KEY_SIZE] = "0";
+ char next_copy_id[MAX_KEY_SIZE] = "0";
+ fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
+
+ if (*dst_youngest >= new_youngest)
+ return SVN_NO_ERROR;
+
+ /* If necessary, get new current next_node and next_copy IDs. */
+ if (dst_ffd->format < SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
+ {
+ apr_off_t root_offset;
+ apr_file_t *rev_file;
+
+ if (dst_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
+ SVN_ERR(update_min_unpacked_rev(dst_fs, scratch_pool));
+
+ SVN_ERR(open_pack_or_rev_file(&rev_file, dst_fs, new_youngest,
+ scratch_pool));
+ SVN_ERR(get_root_changes_offset(&root_offset, NULL, rev_file,
+ dst_fs, new_youngest, scratch_pool));
+ SVN_ERR(recover_find_max_ids(dst_fs, new_youngest, rev_file,
+ root_offset, next_node_id, next_copy_id,
+ scratch_pool));
+ SVN_ERR(svn_io_file_close(rev_file, scratch_pool));
+ }
+
+ /* Update 'current'. */
+ SVN_ERR(write_current(dst_fs, new_youngest, next_node_id, next_copy_id,
+ scratch_pool));
+
+ *dst_youngest = new_youngest;
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Remove revisions between START_REV (inclusive) and END_REV (non-inclusive)
+ * from DST_FS. Assume sharding as per MAX_FILES_PER_DIR.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+hotcopy_remove_rev_files(svn_fs_t *dst_fs,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ int max_files_per_dir,
+ apr_pool_t *scratch_pool)
+{
+ const char *dst_subdir;
+ const char *shard;
+ const char *dst_subdir_shard;
+ svn_revnum_t rev;
+ apr_pool_t *iterpool;
+
+ SVN_ERR_ASSERT(start_rev <= end_rev);
+
+ dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, scratch_pool);
+
+ /* Pre-compute paths for initial shard. */
+ shard = apr_psprintf(scratch_pool, "%ld", start_rev / max_files_per_dir);
+ dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (rev = start_rev; rev < end_rev; rev++)
+ {
+ const char *rev_path;
+
+ svn_pool_clear(iterpool);
+
+ /* If necessary, update paths for shard. */
+ if (rev != start_rev && rev % max_files_per_dir == 0)
+ {
+ shard = apr_psprintf(iterpool, "%ld", rev / max_files_per_dir);
+ dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
+ }
+
+ rev_path = svn_dirent_join(dst_subdir_shard,
+ apr_psprintf(iterpool, "%ld", rev),
+ iterpool);
+
+ /* Make the rev file writable and remove it. */
+ SVN_ERR(svn_io_set_file_read_write(rev_path, TRUE, iterpool));
+ SVN_ERR(svn_io_remove_file2(rev_path, TRUE, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Verify that DST_FS is a suitable destination for an incremental
+ * hotcopy from SRC_FS. */
+static svn_error_t *
+hotcopy_incremental_check_preconditions(svn_fs_t *src_fs,
+ svn_fs_t *dst_fs,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *src_ffd = src_fs->fsap_data;
+ fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
+
+ /* We only support incremental hotcopy between the same format. */
+ if (src_ffd->format != dst_ffd->format)
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("The FSFS format (%d) of the hotcopy source does not match the "
+ "FSFS format (%d) of the hotcopy destination; please upgrade "
+ "both repositories to the same format"),
+ src_ffd->format, dst_ffd->format);
+
+ /* Make sure the UUID of source and destination match up.
+ * We don't want to copy over a different repository. */
+ if (strcmp(src_ffd->uuid, dst_ffd->uuid) != 0)
+ return svn_error_create(SVN_ERR_RA_UUID_MISMATCH, NULL,
+ _("The UUID of the hotcopy source does "
+ "not match the UUID of the hotcopy "
+ "destination"));
+
+ /* Also require same shard size. */
+ if (src_ffd->max_files_per_dir != dst_ffd->max_files_per_dir)
+ return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("The sharding layout configuration "
+ "of the hotcopy source does not match "
+ "the sharding layout configuration of "
+ "the hotcopy destination"));
+ return SVN_NO_ERROR;
+}
+
+
+/* Baton for hotcopy_body(). */
+struct hotcopy_body_baton {
+ svn_fs_t *src_fs;
+ svn_fs_t *dst_fs;
+ svn_boolean_t incremental;
+ svn_cancel_func_t cancel_func;
+ void *cancel_baton;
+} hotcopy_body_baton;
+
+/* Perform a hotcopy, either normal or incremental.
+ *
+ * Normal hotcopy assumes that the destination exists as an empty
+ * directory. It behaves like an incremental hotcopy except that
+ * none of the copied files already exist in the destination.
+ *
+ * An incremental hotcopy copies only changed or new files to the destination,
+ * and removes files from the destination no longer present in the source.
+ * While the incremental hotcopy is running, readers should still be able
+ * to access the destintation repository without error and should not see
+ * revisions currently in progress of being copied. Readers are able to see
+ * new fully copied revisions even if the entire incremental hotcopy procedure
+ * has not yet completed.
+ *
+ * Writers are blocked out completely during the entire incremental hotcopy
+ * process to ensure consistency. This function assumes that the repository
+ * write-lock is held.
+ */
+static svn_error_t *
+hotcopy_body(void *baton, apr_pool_t *pool)
+{
+ struct hotcopy_body_baton *hbb = baton;
+ svn_fs_t *src_fs = hbb->src_fs;
+ fs_fs_data_t *src_ffd = src_fs->fsap_data;
+ svn_fs_t *dst_fs = hbb->dst_fs;
+ fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
+ int max_files_per_dir = src_ffd->max_files_per_dir;
+ svn_boolean_t incremental = hbb->incremental;
+ svn_cancel_func_t cancel_func = hbb->cancel_func;
+ void* cancel_baton = hbb->cancel_baton;
+ svn_revnum_t src_youngest;
+ svn_revnum_t dst_youngest;
+ svn_revnum_t rev;
+ svn_revnum_t src_min_unpacked_rev;
+ svn_revnum_t dst_min_unpacked_rev;
+ const char *src_subdir;
+ const char *dst_subdir;
+ const char *revprop_src_subdir;
+ const char *revprop_dst_subdir;
+ apr_pool_t *iterpool;
+ svn_node_kind_t kind;
+
+ /* Try to copy the config.
+ *
+ * ### We try copying the config file before doing anything else,
+ * ### because higher layers will abort the hotcopy if we throw
+ * ### an error from this function, and that renders the hotcopy
+ * ### unusable anyway. */
+ if (src_ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE)
+ {
+ svn_error_t *err;
+
+ err = svn_io_dir_file_copy(src_fs->path, dst_fs->path, PATH_CONFIG,
+ pool);
+ if (err)
+ {
+ if (APR_STATUS_IS_ENOENT(err->apr_err))
+ {
+ /* 1.6.0 to 1.6.11 did not copy the configuration file during
+ * hotcopy. So if we're hotcopying a repository which has been
+ * created as a hotcopy itself, it's possible that fsfs.conf
+ * does not exist. Ask the user to re-create it.
+ *
+ * ### It would be nice to make this a non-fatal error,
+ * ### but this function does not get an svn_fs_t object
+ * ### so we have no way of just printing a warning via
+ * ### the fs->warning() callback. */
+
+ const char *msg;
+ const char *src_abspath;
+ const char *dst_abspath;
+ const char *config_relpath;
+ svn_error_t *err2;
+
+ config_relpath = svn_dirent_join(src_fs->path, PATH_CONFIG, pool);
+ err2 = svn_dirent_get_absolute(&src_abspath, src_fs->path, pool);
+ if (err2)
+ return svn_error_trace(svn_error_compose_create(err, err2));
+ err2 = svn_dirent_get_absolute(&dst_abspath, dst_fs->path, pool);
+ if (err2)
+ return svn_error_trace(svn_error_compose_create(err, err2));
+
+ /* ### hack: strip off the 'db/' directory from paths so
+ * ### they make sense to the user */
+ src_abspath = svn_dirent_dirname(src_abspath, pool);
+ dst_abspath = svn_dirent_dirname(dst_abspath, pool);
+
+ msg = apr_psprintf(pool,
+ _("Failed to create hotcopy at '%s'. "
+ "The file '%s' is missing from the source "
+ "repository. Please create this file, for "
+ "instance by running 'svnadmin upgrade %s'"),
+ dst_abspath, config_relpath, src_abspath);
+ return svn_error_quick_wrap(err, msg);
+ }
+ else
+ return svn_error_trace(err);
+ }
+ }
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ /* Find the youngest revision in the source and destination.
+ * We only support hotcopies from sources with an equal or greater amount
+ * of revisions than the destination.
+ * This also catches the case where users accidentally swap the
+ * source and destination arguments. */
+ SVN_ERR(get_youngest(&src_youngest, src_fs->path, pool));
+ if (incremental)
+ {
+ SVN_ERR(get_youngest(&dst_youngest, dst_fs->path, pool));
+ if (src_youngest < dst_youngest)
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("The hotcopy destination already contains more revisions "
+ "(%lu) than the hotcopy source contains (%lu); are source "
+ "and destination swapped?"),
+ dst_youngest, src_youngest);
+ }
+ else
+ dst_youngest = 0;
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ /* Copy the min unpacked rev, and read its value. */
+ if (src_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
+ {
+ const char *min_unpacked_rev_path;
+
+ min_unpacked_rev_path = svn_dirent_join(src_fs->path,
+ PATH_MIN_UNPACKED_REV,
+ pool);
+ SVN_ERR(read_min_unpacked_rev(&src_min_unpacked_rev,
+ min_unpacked_rev_path,
+ pool));
+
+ min_unpacked_rev_path = svn_dirent_join(dst_fs->path,
+ PATH_MIN_UNPACKED_REV,
+ pool);
+ SVN_ERR(read_min_unpacked_rev(&dst_min_unpacked_rev,
+ min_unpacked_rev_path,
+ pool));
+
+ /* We only support packs coming from the hotcopy source.
+ * The destination should not be packed independently from
+ * the source. This also catches the case where users accidentally
+ * swap the source and destination arguments. */
+ if (src_min_unpacked_rev < dst_min_unpacked_rev)
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("The hotcopy destination already contains "
+ "more packed revisions (%lu) than the "
+ "hotcopy source contains (%lu)"),
+ dst_min_unpacked_rev - 1,
+ src_min_unpacked_rev - 1);
+
+ SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path,
+ PATH_MIN_UNPACKED_REV, pool));
+ }
+ else
+ src_min_unpacked_rev = 0;
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ /*
+ * Copy the necessary rev files.
+ */
+
+ src_subdir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, pool);
+ dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, pool);
+ SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
+
+ iterpool = svn_pool_create(pool);
+ /* First, copy packed shards. */
+ for (rev = 0; rev < src_min_unpacked_rev; rev += max_files_per_dir)
+ {
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ /* Copy the packed shard. */
+ SVN_ERR(hotcopy_copy_packed_shard(&dst_min_unpacked_rev,
+ src_fs, dst_fs,
+ rev, max_files_per_dir,
+ iterpool));
+
+ /* If necessary, update 'current' to the most recent packed rev,
+ * so readers can see new revisions which arrived in this pack. */
+ SVN_ERR(hotcopy_update_current(&dst_youngest, dst_fs,
+ rev + max_files_per_dir - 1,
+ iterpool));
+
+ /* Remove revision files which are now packed. */
+ if (incremental)
+ SVN_ERR(hotcopy_remove_rev_files(dst_fs, rev, rev + max_files_per_dir,
+ max_files_per_dir, iterpool));
+
+ /* Now that all revisions have moved into the pack, the original
+ * rev dir can be removed. */
+ err = svn_io_remove_dir2(path_rev_shard(dst_fs, rev, iterpool),
+ TRUE, cancel_func, cancel_baton, iterpool);
+ if (err)
+ {
+ if (APR_STATUS_IS_ENOTEMPTY(err->apr_err))
+ svn_error_clear(err);
+ else
+ return svn_error_trace(err);
+ }
+ }
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ /* Now, copy pairs of non-packed revisions and revprop files.
+ * If necessary, update 'current' after copying all files from a shard. */
+ SVN_ERR_ASSERT(rev == src_min_unpacked_rev);
+ revprop_src_subdir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, pool);
+ revprop_dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, pool);
+ SVN_ERR(svn_io_make_dir_recursively(revprop_dst_subdir, pool));
+ for (; rev <= src_youngest; rev++)
+ {
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ /* Copy the rev file. */
+ err = hotcopy_copy_shard_file(src_subdir, dst_subdir,
+ rev, max_files_per_dir,
+ iterpool);
+ if (err)
+ {
+ if (APR_STATUS_IS_ENOENT(err->apr_err) &&
+ src_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
+ {
+ svn_error_clear(err);
+
+ /* The source rev file does not exist. This can happen if the
+ * source repository is being packed concurrently with this
+ * hotcopy operation.
+ *
+ * If the new revision is now packed, and the youngest revision
+ * we're interested in is not inside this pack, try to copy the
+ * pack instead.
+ *
+ * If the youngest revision ended up being packed, don't try
+ * to be smart and work around this. Just abort the hotcopy. */
+ SVN_ERR(update_min_unpacked_rev(src_fs, pool));
+ if (is_packed_rev(src_fs, rev))
+ {
+ if (is_packed_rev(src_fs, src_youngest))
+ return svn_error_createf(
+ SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+ _("The assumed HEAD revision (%lu) of the "
+ "hotcopy source has been packed while the "
+ "hotcopy was in progress; please restart "
+ "the hotcopy operation"),
+ src_youngest);
+
+ SVN_ERR(hotcopy_copy_packed_shard(&dst_min_unpacked_rev,
+ src_fs, dst_fs,
+ rev, max_files_per_dir,
+ iterpool));
+ rev = dst_min_unpacked_rev;
+ continue;
+ }
+ else
+ return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+ _("Revision %lu disappeared from the "
+ "hotcopy source while hotcopy was "
+ "in progress"), rev);
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ /* Copy the revprop file. */
+ SVN_ERR(hotcopy_copy_shard_file(revprop_src_subdir,
+ revprop_dst_subdir,
+ rev, max_files_per_dir,
+ iterpool));
+
+ /* After completing a full shard, update 'current'. */
+ if (rev % max_files_per_dir == 0)
+ SVN_ERR(hotcopy_update_current(&dst_youngest, dst_fs, rev, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ /* We assume that all revisions were copied now, i.e. we didn't exit the
+ * above loop early. 'rev' was last incremented during exit of the loop. */
+ SVN_ERR_ASSERT(rev == src_youngest + 1);
+
+ /* All revisions were copied. Update 'current'. */
+ SVN_ERR(hotcopy_update_current(&dst_youngest, dst_fs, src_youngest, pool));
+
+ /* Replace the locks tree.
+ * This is racy in case readers are currently trying to list locks in
+ * the destination. However, we need to get rid of stale locks.
+ * This is the simplest way of doing this, so we accept this small race. */
+ dst_subdir = svn_dirent_join(dst_fs->path, PATH_LOCKS_DIR, pool);
+ SVN_ERR(svn_io_remove_dir2(dst_subdir, TRUE, cancel_func, cancel_baton,
+ pool));
+ src_subdir = svn_dirent_join(src_fs->path, PATH_LOCKS_DIR, pool);
+ SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
+ if (kind == svn_node_dir)
+ SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_fs->path,
+ PATH_LOCKS_DIR, TRUE,
+ cancel_func, cancel_baton, pool));
+
+ /* Now copy the node-origins cache tree. */
+ src_subdir = svn_dirent_join(src_fs->path, PATH_NODE_ORIGINS_DIR, pool);
+ SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
+ if (kind == svn_node_dir)
+ SVN_ERR(hotcopy_io_copy_dir_recursively(src_subdir, dst_fs->path,
+ PATH_NODE_ORIGINS_DIR, TRUE,
+ cancel_func, cancel_baton, pool));
+
+ /*
+ * NB: Data copied below is only read by writers, not readers.
+ * Writers are still locked out at this point.
+ */
+
+ if (dst_ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
+ {
+ /* Copy the rep cache and then remove entries for revisions
+ * younger than the destination's youngest revision. */
+ src_subdir = svn_dirent_join(src_fs->path, REP_CACHE_DB_NAME, pool);
+ dst_subdir = svn_dirent_join(dst_fs->path, REP_CACHE_DB_NAME, pool);
+ SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
+ if (kind == svn_node_file)
+ {
+ SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));
+ SVN_ERR(svn_fs_fs__del_rep_reference(dst_fs, dst_youngest, pool));
+ }
+ }
+
+ /* Copy the txn-current file. */
+ if (dst_ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
+ SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path,
+ PATH_TXN_CURRENT, pool));
+
+ /* Hotcopied FS is complete. Stamp it with a format file. */
+ SVN_ERR(write_format(svn_dirent_join(dst_fs->path, PATH_FORMAT, pool),
+ dst_ffd->format, max_files_per_dir, TRUE, pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Create an empty filesystem at DST_FS at DST_PATH with the same
+ * configuration as SRC_FS (uuid, format, and other parameters).
+ * After creation DST_FS has no revisions, not even revision zero. */
+static svn_error_t *
+hotcopy_create_empty_dest(svn_fs_t *src_fs,
+ svn_fs_t *dst_fs,
+ const char *dst_path,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *src_ffd = src_fs->fsap_data;
+ fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
+
+ dst_fs->path = apr_pstrdup(pool, dst_path);
+
+ dst_ffd->max_files_per_dir = src_ffd->max_files_per_dir;
+ dst_ffd->config = src_ffd->config;
+ dst_ffd->format = src_ffd->format;
+
+ /* Create the revision data directories. */
+ if (dst_ffd->max_files_per_dir)
+ SVN_ERR(svn_io_make_dir_recursively(path_rev_shard(dst_fs, 0, pool),
+ pool));
+ else
+ SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path,
+ PATH_REVS_DIR, pool),
+ pool));
+
+ /* Create the revprops directory. */
+ if (src_ffd->max_files_per_dir)
+ SVN_ERR(svn_io_make_dir_recursively(path_revprops_shard(dst_fs, 0, pool),
+ pool));
+ else
+ SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path,
+ PATH_REVPROPS_DIR,
+ pool),
+ pool));
+
+ /* Create the transaction directory. */
+ SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path, PATH_TXNS_DIR,
+ pool),
+ pool));
+
+ /* Create the protorevs directory. */
+ if (dst_ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
+ SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path,
+ PATH_TXN_PROTOS_DIR,
+ pool),
+ pool));
+
+ /* Create the 'current' file. */
+ SVN_ERR(svn_io_file_create(svn_fs_py__path_current(dst_fs, pool),
+ (dst_ffd->format >=
+ SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT
+ ? "0\n" : "0 1 1\n"),
+ pool));
+
+ /* Create lock file and UUID. */
+ SVN_ERR(svn_io_file_create(path_lock(dst_fs, pool), "", pool));
+ SVN_ERR(svn_fs_py__set_uuid(dst_fs, src_ffd->uuid, pool));
+
+ /* Create the min unpacked rev file. */
+ if (dst_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
+ SVN_ERR(svn_io_file_create(path_min_unpacked_rev(dst_fs, pool),
+ "0\n", pool));
+ /* Create the txn-current file if the repository supports
+ the transaction sequence file. */
+ if (dst_ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
+ {
+ SVN_ERR(svn_io_file_create(path_txn_current(dst_fs, pool),
+ "0\n", pool));
+ SVN_ERR(svn_io_file_create(path_txn_current_lock(dst_fs, pool),
+ "", pool));
+ }
+
+ dst_ffd->youngest_rev_cache = 0;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_py__hotcopy(svn_fs_t *src_fs,
+ svn_fs_t *dst_fs,
+ const char *src_path,
+ const char *dst_path,
+ svn_boolean_t incremental,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool)
+{
+ struct hotcopy_body_baton hbb;
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ SVN_ERR(svn_fs_py__open(src_fs, src_path, pool));
+
+ if (incremental)
+ {
+ const char *dst_format_abspath;
+ svn_node_kind_t dst_format_kind;
+
+ /* Check destination format to be sure we know how to incrementally
+ * hotcopy to the destination FS. */
+ dst_format_abspath = svn_dirent_join(dst_path, PATH_FORMAT, pool);
+ SVN_ERR(svn_io_check_path(dst_format_abspath, &dst_format_kind, pool));
+ if (dst_format_kind == svn_node_none)
+ {
+ /* Destination doesn't exist yet. Perform a normal hotcopy to a
+ * empty destination using the same configuration as the source. */
+ SVN_ERR(hotcopy_create_empty_dest(src_fs, dst_fs, dst_path, pool));
+ }
+ else
+ {
+ /* Check the existing repository. */
+ SVN_ERR(svn_fs_py__open(dst_fs, dst_path, pool));
+ SVN_ERR(hotcopy_incremental_check_preconditions(src_fs, dst_fs,
+ pool));
+ }
+ }
+ else
+ {
+ /* Start out with an empty destination using the same configuration
+ * as the source. */
+ SVN_ERR(hotcopy_create_empty_dest(src_fs, dst_fs, dst_path, pool));
+ }
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ hbb.src_fs = src_fs;
+ hbb.dst_fs = dst_fs;
+ hbb.incremental = incremental;
+ hbb.cancel_func = cancel_func;
+ hbb.cancel_baton = cancel_baton;
+ SVN_ERR(svn_fs_py__with_write_lock(dst_fs, hotcopy_body, &hbb, pool));
+
+ return SVN_NO_ERROR;
+}
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.h?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.h (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/fs_fs.h Mon Dec 19 21:10:15 2011
@@ -44,11 +44,17 @@ svn_error_t *svn_fs_py__verify(svn_fs_t
void *cancel_baton,
apr_pool_t *pool);
-/* Copy the fsfs filesystem at SRC_PATH into a new copy at DST_PATH.
- Use POOL for temporary allocations. */
-svn_error_t *svn_fs_py__hotcopy(const char *src_path,
- const char *dst_path,
- apr_pool_t *pool);
+/* Copy the fsfs filesystem SRC_FS at SRC_PATH into a new copy DST_FS at
+ * DST_PATH. If INCREMENTAL is TRUE, do not re-copy data which already
+ * exists in DST_FS. Use POOL for temporary allocations. */
+svn_error_t * svn_fs_py__hotcopy(svn_fs_t *src_fs,
+ svn_fs_t *dst_fs,
+ const char *src_path,
+ const char *dst_path,
+ svn_boolean_t incremental,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool);
/* Recover the fsfs associated with filesystem FS.
Use optional CANCEL_FUNC/CANCEL_BATON for cancellation support.
@@ -117,14 +123,15 @@ svn_error_t *svn_fs_py__rep_contents_dir
apr_pool_t *pool);
/* Set *DIRENT to the entry identified by NAME in the directory given
- by NODEREV in filesystem FS. The returned object is allocated in POOL,
- which is also used for temporary allocations. */
+ by NODEREV in filesystem FS. The returned object is allocated in
+ RESULT_POOL; SCRATCH_POOL used for temporary allocations. */
svn_error_t *
svn_fs_py__rep_contents_dir_entry(svn_fs_dirent_t **dirent,
svn_fs_t *fs,
node_revision_t *noderev,
const char *name,
- apr_pool_t *pool);
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* Set *CONTENTS to be a readable svn_stream_t that receives the text
representation of node-revision NODEREV as seen in filesystem FS.
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/key-gen.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/key-gen.c?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/key-gen.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/key-gen.c Mon Dec 19 21:10:15 2011
@@ -41,8 +41,8 @@
void
svn_fs_py__add_keys(const char *key1, const char *key2, char *result)
{
- apr_size_t i1 = strlen(key1) - 1;
- apr_size_t i2 = strlen(key2) - 1;
+ apr_ssize_t i1 = strlen(key1) - 1;
+ apr_ssize_t i2 = strlen(key2) - 1;
int i3 = 0;
int val;
int carry = 0;
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/lock.c?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/lock.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/lock.c Mon Dec 19 21:10:15 2011
@@ -205,7 +205,7 @@ write_digest_file(apr_hash_t *children,
}
if (apr_hash_count(children))
{
- svn_stringbuf_t *children_list = svn_stringbuf_create("", pool);
+ svn_stringbuf_t *children_list = svn_stringbuf_create_empty(pool);
for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi))
{
svn_stringbuf_appendbytes(children_list,
@@ -1036,7 +1036,7 @@ get_locks_filter_func(void *baton,
else if ((b->requested_depth == svn_depth_files) ||
(b->requested_depth == svn_depth_immediates))
{
- const char *rel_uri = svn_fspath__is_child(b->path, lock->path, pool);
+ const char *rel_uri = svn_fspath__skip_ancestor(b->path, lock->path);
if (rel_uri && (svn_path_component_count(rel_uri) == 1))
SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool));
}
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache-db.sql
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache-db.sql?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache-db.sql (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache-db.sql Mon Dec 19 21:10:15 2011
@@ -52,3 +52,8 @@ SELECT hash, revision, offset, size, exp
FROM rep_cache
+-- STMT_DEL_REPS_YOUNGER_THAN_REV
+DELETE FROM rep_cache
+WHERE revision > ?1
+
+
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.c?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.c Mon Dec 19 21:10:15 2011
@@ -335,3 +335,26 @@ svn_fs_py__set_rep_reference(svn_fs_t *f
return SVN_NO_ERROR;
}
+
+
+svn_error_t *
+svn_fs_fs__del_rep_reference(svn_fs_t *fs,
+ svn_revnum_t youngest,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ svn_sqlite__stmt_t *stmt;
+ int format;
+
+ SVN_ERR(svn_fs_py__get_int_attr(&format, ffd->p_fs, "format"));
+ SVN_ERR_ASSERT(format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT);
+ if (! ffd->rep_cache_db)
+ SVN_ERR(svn_fs_py__open_rep_cache(fs, pool));
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
+ STMT_DEL_REPS_YOUNGER_THAN_REV));
+ SVN_ERR(svn_sqlite__bindf(stmt, "r", youngest));
+ SVN_ERR(svn_sqlite__step_done(stmt));
+
+ return SVN_NO_ERROR;
+}
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.h
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.h?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.h (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/rep-cache.h Mon Dec 19 21:10:15 2011
@@ -79,6 +79,13 @@ svn_fs_py__set_rep_reference(svn_fs_t *f
svn_boolean_t reject_dup,
apr_pool_t *pool);
+/* Delete from the cache all reps corresponding to revisions younger
+ than YOUNGEST. */
+svn_error_t *
+svn_fs_fs__del_rep_reference(svn_fs_t *fs,
+ svn_revnum_t youngest,
+ apr_pool_t *pool);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
Propchange: subversion/branches/fs-py/subversion/libsvn_fs_py/temp_serializer.c
('svn:mergeinfo' removed)
Propchange: subversion/branches/fs-py/subversion/libsvn_fs_py/temp_serializer.h
('svn:mergeinfo' removed)
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/tree.c?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/tree.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/tree.c Mon Dec 19 21:10:15 2011
@@ -712,11 +712,11 @@ add_change(svn_fs_t *fs,
/* Get the id of a node referenced by path PATH in ROOT. Return the
id in *ID_P allocated in POOL. */
-static svn_error_t *
-fs_node_id(const svn_fs_id_t **id_p,
- svn_fs_root_t *root,
- const char *path,
- apr_pool_t *pool)
+svn_error_t *
+svn_fs_fs__node_id(const svn_fs_id_t **id_p,
+ svn_fs_root_t *root,
+ const char *path,
+ apr_pool_t *pool)
{
if ((! root->is_txn_root)
&& (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
@@ -781,7 +781,7 @@ node_kind(svn_node_kind_t *kind_p,
dag_node_t *node;
/* Get the node id. */
- SVN_ERR(fs_node_id(&node_id, root, path, pool));
+ SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool));
/* Use the node id to get the real kind. */
SVN_ERR(svn_fs_py__dag_get_node(&node, root->fs, node_id, pool));
@@ -1490,7 +1490,7 @@ svn_fs_py__commit_txn(const char **confl
*/
svn_error_t *err = SVN_NO_ERROR;
- svn_stringbuf_t *conflict = svn_stringbuf_create("", pool);
+ svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
svn_fs_t *fs = txn->fs;
/* Limit memory usage when the repository has a high commit rate and
@@ -1596,7 +1596,7 @@ fs_merge(const char **conflict_p,
dag_node_t *source, *ancestor;
svn_fs_txn_t *txn;
svn_error_t *err;
- svn_stringbuf_t *conflict = svn_stringbuf_create("", pool);
+ svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
if (! target_root->is_txn_root)
return SVN_FS__NOT_TXN(target_root);
@@ -2278,7 +2278,7 @@ apply_textdelta(void *baton, apr_pool_t
/* Make a writable "string" stream which writes data to
tb->target_string. */
- tb->target_string = svn_stringbuf_create("", tb->pool);
+ tb->target_string = svn_stringbuf_create_empty(tb->pool);
tb->string_stream = svn_stream_create(tb, tb->pool);
svn_stream_set_write(tb->string_stream, write_to_string);
@@ -2748,7 +2748,7 @@ prev_location(const char **prev_path,
const char *path,
apr_pool_t *pool)
{
- const char *copy_path, *copy_src_path, *remainder_path = "";
+ const char *copy_path, *copy_src_path, *remainder_path;
svn_fs_root_t *copy_root;
svn_revnum_t copy_src_rev;
@@ -2777,8 +2777,7 @@ prev_location(const char **prev_path,
*/
SVN_ERR(fs_copied_from(©_src_rev, ©_src_path,
copy_root, copy_path, pool));
- if (strcmp(copy_path, path) != 0)
- remainder_path = svn_relpath__is_child(copy_path, path, pool);
+ remainder_path = svn_relpath_skip_ancestor(copy_path, path);
*prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
*prev_rev = copy_src_rev;
return SVN_NO_ERROR;
@@ -2798,7 +2797,7 @@ fs_node_origin_rev(svn_revnum_t *revisio
path = svn_fs__canonicalize_abspath(path, pool);
/* Check the cache first. */
- SVN_ERR(fs_node_id(&given_noderev_id, root, path, pool));
+ SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
node_id = svn_fs_py__id_node_id(given_noderev_id);
/* Is it a brand new uncommitted node? */
@@ -2870,7 +2869,7 @@ fs_node_origin_rev(svn_revnum_t *revisio
}
/* Walk the predecessor links back to origin. */
- SVN_ERR(fs_node_id(&pred_id, curroot, lastpath->data, predidpool));
+ SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
do
{
svn_pool_clear(subpool);
@@ -3025,10 +3024,7 @@ history_prev(void *baton, apr_pool_t *po
the copy source. Finally, if our current path doesn't meet
one of these other criteria ... ### for now just fallback to
the old copy hunt algorithm. */
- if (strcmp(path, copy_dst) == 0)
- remainder_path = "";
- else
- remainder_path = svn_relpath__is_child(copy_dst, path, pool);
+ remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
if (remainder_path)
{
@@ -3286,6 +3282,7 @@ get_mergeinfo_for_path(svn_mergeinfo_t *
svn_fs_root_t *rev_root,
const char *path,
svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -3372,7 +3369,7 @@ get_mergeinfo_for_path(svn_mergeinfo_t *
can return the mergeinfo results directly. Otherwise, we're
inheriting the mergeinfo, so we need to a) remove non-inheritable
ranges and b) telescope the merged-from paths. */
- if (nearest_ancestor != parent_path)
+ if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
{
svn_mergeinfo_t tmp_mergeinfo;
@@ -3427,10 +3424,12 @@ get_mergeinfos_for_paths(svn_fs_root_t *
const apr_array_header_t *paths,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t include_descendants,
- apr_pool_t *pool)
+ svn_boolean_t adjust_inherited_mergeinfo,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- svn_mergeinfo_catalog_t result_catalog = apr_hash_make(pool);
- apr_pool_t *iterpool = svn_pool_create(pool);
+ svn_mergeinfo_catalog_t result_catalog = apr_hash_make(result_pool);
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
for (i = 0; i < paths->nelts; i++)
@@ -3442,7 +3441,8 @@ get_mergeinfos_for_paths(svn_fs_root_t *
svn_pool_clear(iterpool);
err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
- inherit, pool, iterpool);
+ inherit, adjust_inherited_mergeinfo,
+ result_pool, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -3461,8 +3461,8 @@ get_mergeinfos_for_paths(svn_fs_root_t *
apr_hash_set(result_catalog, path, APR_HASH_KEY_STRING,
path_mergeinfo);
if (include_descendants)
- SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path, pool,
- iterpool));
+ SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
+ result_pool, scratch_pool));
}
svn_pool_destroy(iterpool);
@@ -3478,7 +3478,9 @@ fs_get_mergeinfo(svn_mergeinfo_catalog_t
const apr_array_header_t *paths,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t include_descendants,
- apr_pool_t *pool)
+ svn_boolean_t adjust_inherited_mergeinfo,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = root->fs->fsap_data;
int format;
@@ -3500,7 +3502,9 @@ fs_get_mergeinfo(svn_mergeinfo_catalog_t
/* Retrieve a path -> mergeinfo hash mapping. */
return get_mergeinfos_for_paths(root, catalog, paths,
inherit,
- include_descendants, pool);
+ include_descendants,
+ adjust_inherited_mergeinfo,
+ result_pool, scratch_pool);
}
@@ -3509,7 +3513,7 @@ static root_vtable_t root_vtable = {
fs_paths_changed,
svn_fs_py__check_path,
fs_node_history,
- fs_node_id,
+ svn_fs_fs__node_id,
svn_fs_py__node_created_rev,
fs_node_origin_rev,
fs_node_created_path,
Modified: subversion/branches/fs-py/subversion/libsvn_fs_py/tree.h
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_fs_py/tree.h?rev=1220946&r1=1220945&r2=1220946&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_fs_py/tree.h (original)
+++ subversion/branches/fs-py/subversion/libsvn_fs_py/tree.h Mon Dec 19 21:10:15 2011
@@ -61,6 +61,13 @@ svn_fs_py__check_path(svn_node_kind_t *k
const char *path,
apr_pool_t *pool);
+/* Implement root_vtable_t.node_id(). */
+svn_error_t *
+svn_fs_fs__node_id(const svn_fs_id_t **id_p,
+ svn_fs_root_t *root,
+ const char *path,
+ apr_pool_t *pool);
+
/* Set *REVISION to the revision in which PATH under ROOT was created.
Use POOL for any temporary allocations. If PATH is in an
uncommitted transaction, *REVISION will be set to