You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2012/11/11 21:24:21 UTC
svn commit: r1408109 [2/2] - in /subversion/branches/tree-read-api: ./
subversion/bindings/javahl/native/ subversion/bindings/swig/perl/native/
subversion/bindings/swig/ruby/svn/ subversion/bindings/swig/ruby/test/
subversion/bindings/swig/ruby/test/te...
Modified: subversion/branches/tree-read-api/subversion/libsvn_wc/wc_db_wcroot.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_wc/wc_db_wcroot.c?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_wc/wc_db_wcroot.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_wc/wc_db_wcroot.c Sun Nov 11 20:24:17 2012
@@ -419,7 +419,7 @@ svn_wc__db_wcroot_parse_local_abspath(sv
svn_wc__db_wcroot_t *probe_wcroot;
svn_wc__db_wcroot_t *found_wcroot = NULL;
const char *scan_abspath;
- svn_sqlite__db_t *sdb;
+ svn_sqlite__db_t *sdb = NULL;
svn_boolean_t moved_upwards = FALSE;
svn_boolean_t always_check = FALSE;
int wc_format = 0;
@@ -763,7 +763,8 @@ try_symlink_as_dir:
scratch_pool));
/* This handle was opened in this function but is not going
to be used further so close it. */
- SVN_ERR(svn_sqlite__close(sdb));
+ if (sdb)
+ SVN_ERR(svn_sqlite__close(sdb));
goto try_symlink_as_dir;
}
}
Modified: subversion/branches/tree-read-api/subversion/svn/mergeinfo-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svn/mergeinfo-cmd.c?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svn/mergeinfo-cmd.c (original)
+++ subversion/branches/tree-read-api/subversion/svn/mergeinfo-cmd.c Sun Nov 11 20:24:17 2012
@@ -37,7 +37,6 @@
#include "cl.h"
#include "svn_private_config.h"
-#include "private/svn_client_private.h"
/*** Code. ***/
@@ -60,10 +59,15 @@ print_log_rev(void *baton,
* of merging between two branches, given the merge description
* indicated by YCA, BASE, RIGHT, TARGET, REINTEGRATE_LIKE. */
static svn_error_t *
-mergeinfo_diagram(svn_client__pathrev_t *yca,
- svn_client__pathrev_t *base,
- svn_client__pathrev_t *right,
- svn_client__pathrev_t *target,
+mergeinfo_diagram(const char *yca_url,
+ const char *base_url,
+ const char *right_url,
+ const char *target_url,
+ svn_revnum_t yca_rev,
+ svn_revnum_t base_rev,
+ svn_revnum_t right_rev,
+ svn_revnum_t target_rev,
+ const char *repos_root_url,
svn_boolean_t target_is_wc,
svn_boolean_t reintegrate_like,
apr_pool_t *pool)
@@ -82,16 +86,16 @@ mergeinfo_diagram(svn_client__pathrev_t
/* The YCA (that is, the branching point). And an ellipsis, because we
* don't show information about earlier merges */
- g[0][0] = apr_psprintf(pool, " %-8ld ", yca->rev);
+ g[0][0] = apr_psprintf(pool, " %-8ld ", yca_rev);
g[1][0] = " | ";
- if (strcmp(yca->url, right->url) == 0)
+ if (strcmp(yca_url, right_url) == 0)
{
g[2][0] = "-------| |--";
g[3][0] = " \\ ";
g[4][0] = " \\ ";
g[5][0] = " --| |--";
}
- else if (strcmp(yca->url, target->url) == 0)
+ else if (strcmp(yca_url, target_url) == 0)
{
g[2][0] = " --| |--";
g[3][0] = " / ";
@@ -107,18 +111,18 @@ mergeinfo_diagram(svn_client__pathrev_t
}
/* The last full merge */
- if ((base->rev > yca->rev) && reintegrate_like)
+ if ((base_rev > yca_rev) && reintegrate_like)
{
g[2][2] = "---------";
g[3][2] = " / ";
g[4][2] = " / ";
g[5][2] = "---------";
g[6][2] = "| ";
- g[7][2] = apr_psprintf(pool, "%-8ld ", base->rev);
+ g[7][2] = apr_psprintf(pool, "%-8ld ", base_rev);
}
- else if (base->rev > yca->rev)
+ else if (base_rev > yca_rev)
{
- g[0][2] = apr_psprintf(pool, "%-8ld ", base->rev);
+ g[0][2] = apr_psprintf(pool, "%-8ld ", base_rev);
g[1][2] = "| ";
g[2][2] = "---------";
g[3][2] = " \\ ";
@@ -135,7 +139,7 @@ mergeinfo_diagram(svn_client__pathrev_t
/* The tips of the branches */
{
- g[0][3] = apr_psprintf(pool, "%-8ld", right->rev);
+ g[0][3] = apr_psprintf(pool, "%-8ld", right_rev);
g[1][3] = "| ";
g[2][3] = "- ";
g[3][3] = " ";
@@ -143,7 +147,7 @@ mergeinfo_diagram(svn_client__pathrev_t
g[5][3] = "- ";
g[6][3] = "| ";
g[7][3] = target_is_wc ? "WC "
- : apr_psprintf(pool, "%-8ld", target->rev);
+ : apr_psprintf(pool, "%-8ld", target_rev);
}
/* Find the width of each column, so we know how to print blank cells */
@@ -183,10 +187,10 @@ mergeinfo_diagram(svn_client__pathrev_t
}
if (row == 2)
SVN_ERR(svn_cmdline_printf(pool, " %s",
- svn_client__pathrev_relpath(right, pool)));
+ svn_uri_skip_ancestor(repos_root_url, right_url, pool)));
if (row == 5)
SVN_ERR(svn_cmdline_printf(pool, " %s",
- svn_client__pathrev_relpath(target, pool)));
+ svn_uri_skip_ancestor(repos_root_url, target_url, pool)));
SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
}
@@ -206,7 +210,9 @@ mergeinfo_summary(
apr_pool_t *pool)
{
svn_client_automatic_merge_t *the_merge;
- svn_client__pathrev_t *yca, *base, *right, *target;
+ const char *yca_url, *base_url, *right_url, *target_url;
+ svn_revnum_t yca_rev, base_rev, right_rev, target_rev;
+ const char *repos_root_url;
svn_boolean_t target_is_wc, reintegrate_like;
target_is_wc = (! svn_path_is_url(target_path_or_url))
@@ -226,12 +232,18 @@ mergeinfo_summary(
target_path_or_url, target_revision,
ctx, pool, pool));
- SVN_ERR(svn_client__automatic_merge_get_locations(
- &yca, &base, &right, &target, the_merge, pool));
+ SVN_ERR(svn_client_automatic_merge_get_locations(
+ &yca_url, &yca_rev,
+ &base_url, &base_rev,
+ &right_url, &right_rev,
+ &target_url, &target_rev,
+ &repos_root_url,
+ the_merge, pool));
reintegrate_like = svn_client_automatic_merge_is_reintegrate_like(the_merge);
- SVN_ERR(mergeinfo_diagram(yca, base, right, target,
- target_is_wc, reintegrate_like,
+ SVN_ERR(mergeinfo_diagram(yca_url, base_url, right_url, target_url,
+ yca_rev, base_rev, right_rev, target_rev,
+ repos_root_url, target_is_wc, reintegrate_like,
pool));
return SVN_NO_ERROR;
Modified: subversion/branches/tree-read-api/subversion/svnadmin/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnadmin/main.c?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnadmin/main.c (original)
+++ subversion/branches/tree-read-api/subversion/svnadmin/main.c Sun Nov 11 20:24:17 2012
@@ -43,6 +43,7 @@
#include "svn_xml.h"
#include "private/svn_opt_private.h"
+#include "private/svn_subr_private.h"
#include "svn_private_config.h"
@@ -193,7 +194,7 @@ enum svnadmin__cmdline_options_t
svnadmin__pre_1_4_compatible,
svnadmin__pre_1_5_compatible,
svnadmin__pre_1_6_compatible,
- svnadmin__pre_1_8_compatible
+ svnadmin__compatible_version
};
/* Option codes and descriptions.
@@ -271,26 +272,23 @@ static const apr_getopt_option_t options
" use by another process")},
{"pre-1.4-compatible", svnadmin__pre_1_4_compatible, 0,
- N_("use format compatible with Subversion versions\n"
- " earlier than 1.4")},
+ N_("deprecated; see --compatible-version")},
{"pre-1.5-compatible", svnadmin__pre_1_5_compatible, 0,
- N_("use format compatible with Subversion versions\n"
- " earlier than 1.5")},
+ N_("deprecated; see --compatible-version")},
{"pre-1.6-compatible", svnadmin__pre_1_6_compatible, 0,
- N_("use format compatible with Subversion versions\n"
- " earlier than 1.6")},
-
- {"pre-1.8-compatible", svnadmin__pre_1_8_compatible, 0,
- N_("use format compatible with Subversion versions\n"
- " earlier than 1.8")},
+ N_("deprecated; see --compatible-version")},
{"memory-cache-size", 'M', 1,
N_("size of the extra in-memory cache in MB used to\n"
" minimize redundant operations. Default: 16.\n"
" [used for FSFS repositories only]")},
+ {"compatible-version", svnadmin__compatible_version, 1,
+ N_("use repository format compatible with Subversion\n"
+ " version ARG (\"1.5.5\", \"1.7\", etc.)")},
+
{NULL}
};
@@ -310,9 +308,9 @@ static const svn_opt_subcommand_desc2_t
("usage: svnadmin create REPOS_PATH\n\n"
"Create a new, empty repository at REPOS_PATH.\n"),
{svnadmin__bdb_txn_nosync, svnadmin__bdb_log_keep,
- svnadmin__config_dir, svnadmin__fs_type, svnadmin__pre_1_4_compatible,
- svnadmin__pre_1_5_compatible, svnadmin__pre_1_6_compatible,
- svnadmin__pre_1_8_compatible
+ svnadmin__config_dir, svnadmin__fs_type, svnadmin__compatible_version,
+ svnadmin__pre_1_4_compatible, svnadmin__pre_1_5_compatible,
+ svnadmin__pre_1_6_compatible
} },
{"deltify", subcommand_deltify, {0}, N_
@@ -487,7 +485,7 @@ struct svnadmin_opt_state
svn_boolean_t pre_1_4_compatible; /* --pre-1.4-compatible */
svn_boolean_t pre_1_5_compatible; /* --pre-1.5-compatible */
svn_boolean_t pre_1_6_compatible; /* --pre-1.6-compatible */
- svn_boolean_t pre_1_8_compatible; /* --pre-1.8-compatible */
+ svn_version_t *compatible_version; /* --compatible-version */
svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */
svn_boolean_t help; /* --help or -? */
svn_boolean_t version; /* --version */
@@ -638,25 +636,35 @@ subcommand_create(apr_getopt_t *os, void
APR_HASH_KEY_STRING,
opt_state->fs_type);
+ /* Prior to 1.8, we had explicit options to specify compatibility
+ with a handful of prior Subversion releases. */
if (opt_state->pre_1_4_compatible)
apr_hash_set(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE,
- APR_HASH_KEY_STRING,
- "1");
-
+ APR_HASH_KEY_STRING, "1");
if (opt_state->pre_1_5_compatible)
apr_hash_set(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE,
- APR_HASH_KEY_STRING,
- "1");
-
+ APR_HASH_KEY_STRING, "1");
if (opt_state->pre_1_6_compatible)
apr_hash_set(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE,
- APR_HASH_KEY_STRING,
- "1");
+ APR_HASH_KEY_STRING, "1");
- if (opt_state->pre_1_8_compatible)
- apr_hash_set(fs_config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE,
- APR_HASH_KEY_STRING,
- "1");
+ /* In 1.8, we figured out that we didn't have to keep extending this
+ madness indefinitely. */
+ if (opt_state->compatible_version)
+ {
+ if (! svn_version__at_least(opt_state->compatible_version, 1, 4, 0))
+ apr_hash_set(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE,
+ APR_HASH_KEY_STRING, "1");
+ if (! svn_version__at_least(opt_state->compatible_version, 1, 5, 0))
+ apr_hash_set(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE,
+ APR_HASH_KEY_STRING, "1");
+ if (! svn_version__at_least(opt_state->compatible_version, 1, 6, 0))
+ apr_hash_set(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE,
+ APR_HASH_KEY_STRING, "1");
+ if (! svn_version__at_least(opt_state->compatible_version, 1, 8, 0))
+ apr_hash_set(fs_config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE,
+ APR_HASH_KEY_STRING, "1");
+ }
SVN_ERR(svn_repos_create(&repos, opt_state->repository_path,
NULL, NULL, NULL, fs_config, pool));
@@ -1987,8 +1995,42 @@ sub_main(int argc, const char *argv[], a
case svnadmin__pre_1_6_compatible:
opt_state.pre_1_6_compatible = TRUE;
break;
- case svnadmin__pre_1_8_compatible:
- opt_state.pre_1_8_compatible = TRUE;
+ case svnadmin__compatible_version:
+ {
+ svn_version_t latest = { SVN_VER_MAJOR, SVN_VER_MINOR,
+ SVN_VER_PATCH, NULL };
+ svn_version_t *compatible_version;
+
+ /* Parse the version string which carries our target
+ compatibility. */
+ SVN_INT_ERR(svn_version__parse_version_string(&compatible_version,
+ opt_arg, pool));
+
+ /* We can't create repository with a version older than 1.0.0. */
+ if (! svn_version__at_least(compatible_version, 1, 0, 0))
+ {
+ err = svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("Cannot create pre-1.0-compatible "
+ "repositories"));
+ return EXIT_ERROR(err);
+ }
+
+ /* We can't create repository with a version newer than what
+ the running version of Subversion supports. */
+ if (! svn_version__at_least(&latest,
+ compatible_version->major,
+ compatible_version->minor,
+ compatible_version->patch))
+ {
+ err = svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("Cannot guaranteed compatibility "
+ "beyond the current running version "
+ "(" SVN_VER_NUM ")"));
+ return EXIT_ERROR(err);
+ }
+
+ opt_state.compatible_version = compatible_version;
+ }
break;
case svnadmin__fs_type:
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool));
Modified: subversion/branches/tree-read-api/subversion/svnlook/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnlook/main.c?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnlook/main.c (original)
+++ subversion/branches/tree-read-api/subversion/svnlook/main.c Sun Nov 11 20:24:17 2012
@@ -93,7 +93,9 @@ enum
svnlook__revprop_opt,
svnlook__full_paths,
svnlook__copy_info,
- svnlook__xml_opt
+ svnlook__xml_opt,
+ svnlook__ignore_properties,
+ svnlook__properties_only
};
/*
@@ -125,6 +127,12 @@ static const apr_getopt_option_t options
{"no-diff-deleted", svnlook__no_diff_deleted, 0,
N_("do not print differences for deleted files")},
+ {"ignore-properties", svnlook__ignore_properties, 0,
+ N_("ignore properties during the operation")},
+
+ {"properties-only", svnlook__properties_only, 0,
+ N_("show only properties during the operation")},
+
{"non-recursive", 'N', 0,
N_("operate on single directory only")},
@@ -218,7 +226,8 @@ static const svn_opt_subcommand_desc2_t
N_("usage: svnlook diff REPOS_PATH\n\n"
"Print GNU-style diffs of changed files and properties.\n"),
{'r', 't', svnlook__no_diff_deleted, svnlook__no_diff_added,
- svnlook__diff_copy_from, 'x'} },
+ svnlook__diff_copy_from, 'x', svnlook__ignore_properties,
+ svnlook__properties_only} },
{"dirs-changed", subcommand_dirschanged, {0},
N_("usage: svnlook dirs-changed REPOS_PATH\n\n"
@@ -320,6 +329,8 @@ struct svnlook_opt_state
svn_boolean_t xml; /* --xml */
const char *extensions; /* diff extension args (UTF-8!) */
svn_boolean_t quiet; /* --quiet */
+ svn_boolean_t ignore_properties; /* --ignore_properties */
+ svn_boolean_t properties_only; /* --properties-only */
};
@@ -339,6 +350,8 @@ typedef struct svnlook_ctxt_t
svn_fs_txn_t *txn;
const char *txn_name /* UTF-8! */;
const apr_array_header_t *diff_options;
+ svn_boolean_t ignore_properties;
+ svn_boolean_t properties_only;
} svnlook_ctxt_t;
@@ -1031,7 +1044,7 @@ print_diff_tree(svn_fs_root_t *root,
}
}
- if (do_diff)
+ if (do_diff && (! c->properties_only))
{
svn_stringbuf_appendcstr(header, equal_string);
svn_stringbuf_appendcstr(header, "\n");
@@ -1122,7 +1135,7 @@ print_diff_tree(svn_fs_root_t *root,
SVN_ERR(svn_io_remove_file2(new_path, FALSE, pool));
/*** Now handle property diffs ***/
- if ((node->prop_mod) && (node->action != 'D'))
+ if ((node->prop_mod) && (node->action != 'D') && (! c->ignore_properties))
{
apr_hash_t *local_proptable;
apr_hash_t *base_proptable;
@@ -1913,6 +1926,8 @@ get_ctxt_baton(svnlook_ctxt_t **baton_p,
baton->diff_options = svn_cstring_split(opt_state->extensions
? opt_state->extensions : "",
" \t\n\r", TRUE, pool);
+ baton->ignore_properties = opt_state->ignore_properties;
+ baton->properties_only = opt_state->properties_only;
if (baton->txn_name)
SVN_ERR(svn_fs_open_txn(&(baton->txn), baton->fs,
@@ -2414,6 +2429,14 @@ main(int argc, const char *argv[])
opt_state.extensions = opt_arg;
break;
+ case svnlook__ignore_properties:
+ opt_state.ignore_properties = TRUE;
+ break;
+
+ case svnlook__properties_only:
+ opt_state.properties_only = TRUE;
+ break;
+
default:
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
svn_pool_destroy(pool);
Modified: subversion/branches/tree-read-api/subversion/svnmucc/svnmucc.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnmucc/svnmucc.c?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnmucc/svnmucc.c (original)
+++ subversion/branches/tree-read-api/subversion/svnmucc/svnmucc.c Sun Nov 11 20:24:17 2012
@@ -1300,7 +1300,7 @@ main(int argc, const char **argv)
else
anchor = svn_uri_get_longest_ancestor(anchor, url, pool);
- if ((++i == action_args->nelts) && (j >= num_url_args))
+ if ((++i == action_args->nelts) && (j + 1 < num_url_args))
insufficient(pool);
}
APR_ARRAY_PUSH(actions, struct action *) = action;
Modified: subversion/branches/tree-read-api/subversion/tests/cmdline/copy_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/cmdline/copy_tests.py?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/cmdline/copy_tests.py (original)
+++ subversion/branches/tree-read-api/subversion/tests/cmdline/copy_tests.py Sun Nov 11 20:24:17 2012
@@ -4361,7 +4361,7 @@ def nonrecursive_commit_of_copy(sbox):
def copy_added_dir_with_copy(sbox):
"""copy of new dir with copied file keeps history"""
- sbox.build()
+ sbox.build(read_only=True)
wc_dir = sbox.wc_dir
new_dir = sbox.ospath('NewDir');
@@ -4670,7 +4670,7 @@ def changed_dir_data_should_match_checko
def move_added_nodes(sbox):
"""move added nodes"""
- sbox.build()
+ sbox.build(read_only=True)
svntest.actions.run_and_verify_svn(None, None, [], 'mkdir',
sbox.ospath('X'),
@@ -4784,7 +4784,7 @@ def mixed_rev_copy_del(sbox):
def copy_delete_undo(sbox, use_revert):
"copy, delete child, undo"
- sbox.build()
+ sbox.build(read_only=True)
wc_dir = sbox.wc_dir
# Copy directory with children
@@ -4839,7 +4839,7 @@ def copy_delete_revert(sbox):
def delete_replace_delete(sbox):
"delete a directory scheduled for replacement"
- sbox.build()
+ sbox.build(read_only=True)
wc_dir = sbox.wc_dir
# Delete directory with children
Modified: subversion/branches/tree-read-api/subversion/tests/cmdline/merge_automatic_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/cmdline/merge_automatic_tests.py?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/cmdline/merge_automatic_tests.py (original)
+++ subversion/branches/tree-read-api/subversion/tests/cmdline/merge_automatic_tests.py Sun Nov 11 20:24:17 2012
@@ -83,6 +83,8 @@ from merge_tests import set_up_branch
# B (--o--x--o-----?--x
#
# Merge with cherry-picks
+# (This set of six cases represents all of the topologically distinct
+# scenarios involving one cherry-pick between two automatic merges.)
#
# Cherry1, fwd
# A (--o-----o-[o]----o---
@@ -701,6 +703,7 @@ def cherry1_fwd(sbox):
@SkipUnless(server_has_mergeinfo)
@XFail()
+@Issue(4255)
def cherry2_fwd(sbox):
"""cherry2_fwd"""
@@ -721,6 +724,7 @@ def cherry2_fwd(sbox):
@SkipUnless(server_has_mergeinfo)
@XFail()
+@Issue(4255)
def cherry3_fwd(sbox):
"""cherry3_fwd"""
@@ -728,7 +732,7 @@ def cherry3_fwd(sbox):
# ( \ / \
# ( \ / \
# B (---o--o-[o]-x-/---------x
- # \__/
+ # \__/
# 2 34 5 6 7 8 9 0
make_branches(sbox)
Modified: subversion/branches/tree-read-api/subversion/tests/cmdline/special_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/cmdline/special_tests.py?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/cmdline/special_tests.py (original)
+++ subversion/branches/tree-read-api/subversion/tests/cmdline/special_tests.py Sun Nov 11 20:24:17 2012
@@ -25,7 +25,7 @@
######################################################################
# General modules
-import sys, os, re
+import sys, os, re, copy
# Our testing module
import svntest
@@ -49,7 +49,6 @@ Item = svntest.wc.StateItem
#----------------------------------------------------------------------
-@SkipUnless(svntest.main.is_posix_os)
def general_symlink(sbox):
"general symlink handling"
@@ -57,11 +56,11 @@ def general_symlink(sbox):
wc_dir = sbox.wc_dir
# First try to just commit a symlink
- newfile_path = os.path.join(wc_dir, 'newfile')
- linktarget_path = os.path.join(wc_dir, 'linktarget')
- svntest.main.file_append(linktarget_path, 'this is just a link target')
- os.symlink('linktarget', newfile_path)
- svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
+ newfile_path = sbox.ospath('newfile')
+
+ sbox.simple_append('linktarget', 'this is just a link target')
+ sbox.simple_add('linktarget')
+ sbox.simple_add_symlink('linktarget', 'newfile')
expected_output = svntest.wc.State(wc_dir, {
'newfile' : Item(verb='Adding'),
@@ -104,14 +103,18 @@ def general_symlink(sbox):
'up', '-r', '2', wc_dir)
# Is the symlink back?
- new_target = os.readlink(newfile_path)
- if new_target != 'linktarget':
- raise svntest.Failure
+ if svntest.main.is_posix_os():
+ new_target = os.readlink(newfile_path)
+ if new_target != 'linktarget':
+ raise svntest.Failure
## Now change the target of the symlink, verify that it is shown as
## modified and that a commit succeeds.
os.remove(newfile_path)
- os.symlink('A', newfile_path)
+ if svntest.main.is_posix_os():
+ os.symlink('A', newfile_path)
+ else:
+ sbox.simple_append('newfile', 'link A', truncate = True)
was_cwd = os.getcwd()
os.chdir(wc_dir)
@@ -223,7 +226,6 @@ def import_export_symlink(sbox):
#----------------------------------------------------------------------
# Regression test for issue 1986
@Issue(1986)
-@SkipUnless(svntest.main.is_posix_os)
def copy_tree_with_symlink(sbox):
"'svn cp dir1 dir2' which contains a symlink"
@@ -231,11 +233,10 @@ def copy_tree_with_symlink(sbox):
wc_dir = sbox.wc_dir
# Create a versioned symlink within directory 'A/D/H'.
- newfile_path = os.path.join(wc_dir, 'A', 'D', 'H', 'newfile')
- linktarget_path = os.path.join(wc_dir, 'A', 'D', 'H', 'linktarget')
- svntest.main.file_append(linktarget_path, 'this is just a link target')
- os.symlink('linktarget', newfile_path)
- svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
+ newfile_path = sbox.ospath('A/D/H/newfile')
+ sbox.simple_append('A/D/H/linktarget', 'this is just a link target')
+ sbox.simple_add('A/D/H/linktarget')
+ sbox.simple_add_symlink('linktarget', 'A/D/H/newfile')
expected_output = svntest.wc.State(wc_dir, {
'A/D/H/newfile' : Item(verb='Adding'),
@@ -323,7 +324,7 @@ def replace_symlink_with_file(sbox):
raise svntest.Failure
-@SkipUnless(svntest.main.is_posix_os)
+#----------------------------------------------------------------------
def remove_symlink(sbox):
"remove a symlink"
@@ -334,8 +335,8 @@ def remove_symlink(sbox):
newfile_path = os.path.join(wc_dir, 'newfile')
linktarget_path = os.path.join(wc_dir, 'linktarget')
svntest.main.file_append(linktarget_path, 'this is just a link target')
- os.symlink('linktarget', newfile_path)
- svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
+ sbox.simple_add_symlink('linktarget', 'newfile')
+ sbox.simple_add('linktarget')
expected_output = svntest.wc.State(wc_dir, {
'newfile' : Item(verb='Adding'),
@@ -367,7 +368,7 @@ def remove_symlink(sbox):
svntest.actions.run_and_verify_commit(wc_dir, expected_output,
expected_status, None, wc_dir)
-@SkipUnless(svntest.main.is_posix_os)
+#----------------------------------------------------------------------
@SkipUnless(server_has_mergeinfo)
@Issue(2530)
def merge_symlink_into_file(sbox):
@@ -378,8 +379,8 @@ def merge_symlink_into_file(sbox):
d_url = sbox.repo_url + '/A/D'
dprime_url = sbox.repo_url + '/A/Dprime'
- gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
- gamma_prime_path = os.path.join(wc_dir, 'A', 'Dprime', 'gamma')
+ gamma_path = sbox.ospath('A/D/gamma')
+ gamma_prime_path = sbox.ospath('A/Dprime/gamma')
# create a copy of the D directory to play with
svntest.main.run_svn(None,
@@ -400,8 +401,7 @@ def merge_symlink_into_file(sbox):
# Commit a symlink in its place
linktarget_path = os.path.join(wc_dir, 'linktarget')
svntest.main.file_append(linktarget_path, 'this is just a link target')
- os.symlink('linktarget', gamma_prime_path)
- svntest.main.run_svn(None, 'add', gamma_prime_path)
+ sbox.simple_add_symlink('linktarget', 'A/Dprime/gamma')
expected_output = svntest.wc.State(wc_dir, {
'A/Dprime/gamma' : Item(verb='Adding'),
@@ -435,7 +435,7 @@ def merge_symlink_into_file(sbox):
-@SkipUnless(svntest.main.is_posix_os)
+#----------------------------------------------------------------------
def merge_file_into_symlink(sbox):
"merge file into symlink"
@@ -466,8 +466,7 @@ def merge_file_into_symlink(sbox):
# Commit a symlink in its place
linktarget_path = os.path.join(wc_dir, 'linktarget')
svntest.main.file_append(linktarget_path, 'this is just a link target')
- os.symlink('linktarget', gamma_prime_path)
- svntest.main.run_svn(None, 'add', gamma_prime_path)
+ sbox.simple_add_symlink('linktarget', 'A/Dprime/gamma')
expected_output = svntest.wc.State(wc_dir, {
'A/Dprime/gamma' : Item(verb='Adding'),
@@ -520,22 +519,19 @@ def checkout_repo_with_symlinks(sbox):
expected_output,
expected_wc)
+#----------------------------------------------------------------------
# Issue 2716: 'svn diff' against a symlink to a directory within the wc
@Issue(2716)
-@SkipUnless(svntest.main.is_posix_os)
def diff_symlink_to_dir(sbox):
"diff a symlink to a directory"
sbox.build(read_only = True)
- os.chdir(sbox.wc_dir)
- # Create a symlink to A/D/.
+ # Create a symlink to A/D as link.
d_path = os.path.join('A', 'D')
- link_path = 'link'
- os.symlink(d_path, link_path)
+ sbox.simple_add_symlink('A/D', 'link')
- # Add the symlink.
- svntest.main.run_svn(None, 'add', link_path)
+ os.chdir(sbox.wc_dir)
# Now diff the wc itself and check the results.
expected_output = [
@@ -544,7 +540,7 @@ def diff_symlink_to_dir(sbox):
"--- link\t(revision 0)\n",
"+++ link\t(working copy)\n",
"@@ -0,0 +1 @@\n",
- "+link " + d_path + "\n",
+ "+link A/D\n",
"\ No newline at end of file\n",
"\n",
"Property changes on: link\n",
@@ -557,9 +553,9 @@ def diff_symlink_to_dir(sbox):
svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
'.')
# We should get the same output if we the diff the symlink itself.
- svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
- link_path)
+ svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff', 'link')
+#----------------------------------------------------------------------
# Issue 2692 (part of): Check that the client can check out a repository
# that contains an unknown special file type.
@Issue(2692)
@@ -710,9 +706,8 @@ def unrelated_changed_special_status(sbo
'--changelist', 'chi cl',
'-m', 'psi changed special status')
-
+#----------------------------------------------------------------------
@Issue(3972)
-@SkipUnless(svntest.main.is_posix_os)
def symlink_destination_change(sbox):
"revert a symlink destination change"
@@ -721,8 +716,7 @@ def symlink_destination_change(sbox):
# Create a new symlink and commit it.
newfile_path = os.path.join(wc_dir, 'newfile')
- os.symlink('linktarget', newfile_path)
- svntest.main.run_svn(None, 'add', newfile_path)
+ sbox.simple_add_symlink('linktarget', 'newfile')
expected_output = svntest.wc.State(wc_dir, {
'newfile' : Item(verb='Adding'),
@@ -738,7 +732,10 @@ def symlink_destination_change(sbox):
# Modify the symlink to point somewhere else
os.remove(newfile_path)
- os.symlink('linktarget2', newfile_path)
+ if svntest.main.is_posix_os():
+ os.symlink('linktarget2', newfile_path)
+ else:
+ sbox.simple_append('newfile', 'link linktarget2', truncate = True)
expected_status.tweak('newfile', status='M ')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
@@ -758,7 +755,6 @@ def symlink_destination_change(sbox):
# This used to lose the special status in the target working copy
# (disk and metadata).
@Issue(3884)
-@SkipUnless(svntest.main.is_posix_os)
def merge_foreign_symlink(sbox):
"merge symlink-add from foreign repos"
@@ -777,8 +773,7 @@ def merge_foreign_symlink(sbox):
zeta2_path = sbox2.ospath('A/zeta')
# sbox2 r2: create zeta2 in sbox2
- os.symlink('target', zeta2_path)
- sbox2.simple_add('A/zeta')
+ sbox2.simple_add_symlink('target', 'A/zeta')
sbox2.simple_commit('A/zeta')
@@ -859,8 +854,8 @@ def symlink_to_wc_svnversion(sbox):
symlink_path, sbox.repo_url,
[ "1\n" ], [])
+#----------------------------------------------------------------------
# Regression in 1.7.0: Update fails to change a symlink
-@SkipUnless(svntest.main.is_posix_os)
def update_symlink(sbox):
"update a symlink"
@@ -873,13 +868,15 @@ def update_symlink(sbox):
symlink_path = sbox.ospath('symlink')
# create a symlink to /A/mu
- os.symlink("A/mu", symlink_path)
- sbox.simple_add('symlink')
+ sbox.simple_add_symlink("A/mu", 'symlink')
sbox.simple_commit()
# change the symlink to /iota
os.remove(symlink_path)
- os.symlink("iota", symlink_path)
+ if svntest.main.is_posix_os():
+ os.symlink("iota", symlink_path)
+ else:
+ file_write(symlink_path, 'link iota')
sbox.simple_commit()
# update back to r2
@@ -896,6 +893,10 @@ def update_symlink(sbox):
expected_status.add({
'symlink' : Item(status=' ', wc_rev='3'),
})
+
+ if not svntest.main.is_posix_os():
+ expected_disk = None
+
svntest.actions.run_and_verify_update(wc_dir,
expected_output,
expected_disk,
@@ -903,9 +904,8 @@ def update_symlink(sbox):
None, None, None,
None, None, 1)
-@XFail()
+#----------------------------------------------------------------------
@Issue(4091)
-@SkipUnless(svntest.main.is_posix_os)
def replace_symlinks(sbox):
"replace symlinks"
sbox.build()
@@ -922,10 +922,8 @@ def replace_symlinks(sbox):
sbox.simple_mkdir('A/D/Y')
sbox.simple_mkdir('Ax')
- os.symlink('../Y', wc('A/D/H/Z'))
- os.symlink('../Y', wc('A/D/Hx/Z'))
- sbox.simple_add('A/D/H/Z',
- 'A/D/Hx/Z')
+ sbox.simple_add_symlink('../Y', 'A/D/H/Z')
+ sbox.simple_add_symlink('../Y', 'A/D/Hx/Z')
for p in ['Ax/mu',
'A/D/Gx/pi',
@@ -946,6 +944,8 @@ def replace_symlinks(sbox):
file_write(wc(p), '#!/bin/sh\necho "hello, svn!"\n')
os.chmod(wc(p), 0775)
sbox.simple_add(p)
+ if not svntest.main.is_posix_os():
+ sbox.simple_propset('svn:executable', 'X', p)
sbox.simple_commit() # r2
sbox.simple_update()
expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 2)
@@ -971,19 +971,17 @@ def replace_symlinks(sbox):
'A/mu.sh' : Item(status=' ', wc_rev=2),
'iota.sh' : Item(status=' ', wc_rev=2),
})
- expected_status_r2 = expected_status
+ expected_status_r2 = copy.deepcopy(expected_status)
svntest.actions.run_and_verify_status(sbox.wc_dir, expected_status_r2)
# Failing git-svn test: 'new symlink is added to a file that was
# also just made executable', i.e., in the same revision.
- sbox.simple_propset("svn:executable", "*", 'A/B/E/alpha')
- os.symlink('alpha', wc('A/B/E/sym-alpha'))
- sbox.simple_add('A/B/E/sym-alpha')
+ sbox.simple_propset("svn:executable", "X", 'A/B/E/alpha')
+ sbox.simple_add_symlink('alpha', 'A/B/E/sym-alpha')
# Add a symlink to a file made non-executable in the same revision.
sbox.simple_propdel("svn:executable", 'A/B/E/beta.sh')
- os.symlink('beta.sh', wc('A/B/E/sym-beta.sh'))
- sbox.simple_add('A/B/E/sym-beta.sh')
+ sbox.simple_add_symlink('beta.sh', 'A/B/E/sym-beta.sh')
# Replace a normal {file, exec, dir} with a symlink to the same kind
# via Subversion replacement.
@@ -991,13 +989,9 @@ def replace_symlinks(sbox):
'A/D/G/rho.sh',
#'A/D/G/Z', # Ooops, not compatible with --bin=svn1.6.
)
- os.symlink(wc('../gamma'), wc('A/D/G/pi'))
- os.symlink(wc('../gamma.sh'), wc('A/D/G/rho.sh'))
- #os.symlink(wc('../Y'), wc('A/D/G/Z'))
- sbox.simple_add('A/D/G/pi',
- 'A/D/G/rho.sh',
- #'A/D/G/Z',
- )
+ sbox.simple_add_symlink('../gamma', 'A/D/G/pi')
+ sbox.simple_add_symlink('../gamma.sh', 'A/D/G/rho.sh')
+ #sbox.simple_add_symlink('../Y', 'A/D/G/Z')
# Replace a symlink to {file, exec, dir} with a normal item of the
# same kind via Subversion replacement.
@@ -1005,30 +999,34 @@ def replace_symlinks(sbox):
'A/D/H/psi.sh',
#'A/D/H/Z',
)
- os.symlink(wc('../gamma'), wc('A/D/H/chi'))
- os.symlink(wc('../gamma.sh'), wc('A/D/H/psi.sh'))
- #os.symlink(wc('../Y'), wc('A/D/H/Z'))
- sbox.simple_add('A/D/H/chi',
- 'A/D/H/psi.sh',
- #'A/D/H/Z',
- )
+ sbox.simple_add_symlink('../gamma', 'A/D/H/chi')
+ sbox.simple_add_symlink('../gamma.sh', 'A/D/H/psi.sh')
+ #sbox.simple_add_symlink('../Y', 'A/D/H/Z')
# Replace a normal {file, exec} with a symlink to {exec, file} via
# Subversion replacement.
sbox.simple_rm('A/mu',
'A/mu.sh')
- os.symlink('../iota2', wc('A/mu'))
- os.symlink('../iota', wc('A/mu.sh'))
- sbox.simple_add('A/mu',
- 'A/mu.sh')
+ sbox.simple_add_symlink('../iota2', 'A/mu')
+ sbox.simple_add_symlink('../iota', 'A/mu.sh')
# Ditto, without the Subversion replacement. Failing git-svn test
# 'executable file becomes a symlink to bar/zzz (file)'.
- os.remove(wc('Ax/mu'))
- os.remove(wc('Ax/mu.sh'))
- os.symlink('../iota2', wc('Ax/mu'))
- os.symlink('../iota', wc('Ax/mu.sh'))
- sbox.simple_propset('svn:special', '*',
+ if svntest.main.is_posix_os():
+ os.remove(wc('Ax/mu'))
+ os.remove(wc('Ax/mu.sh'))
+ os.symlink('../iota2', wc('Ax/mu'))
+ os.symlink('../iota', wc('Ax/mu.sh'))
+ else:
+ # At least modify the file a bit
+
+ # ### Somehow this breaks the test when using multiline data?
+ # ### Is that intended behavior?
+
+ file_write(sbox.ospath('Ax/mu'), 'Link to iota2')
+ file_write(sbox.ospath('Ax/mu.sh'), 'Link to iota')
+
+ sbox.simple_propset('svn:special', 'X',
'Ax/mu',
'Ax/mu.sh')
sbox.simple_propdel('svn:executable', 'Ax/mu.sh')
@@ -1100,20 +1098,121 @@ def externals_as_symlink_targets(sbox):
sbox.simple_commit()
+#----------------------------------------------------------------------
@XFail()
@Issue(4119)
-@SkipUnless(svntest.main.is_posix_os)
def cat_added_symlink(sbox):
"cat added symlink"
sbox.build(read_only = True)
kappa_path = sbox.ospath('kappa')
- os.symlink('iota', kappa_path)
- sbox.simple_add('kappa')
+ sbox.simple_add_symlink('iota', 'kappa')
svntest.actions.run_and_verify_svn(None, "link iota", [],
"cat", kappa_path)
+#----------------------------------------------------------------------
+def incoming_symlink_changes(sbox):
+ "verify incoming symlink change behavior"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ sbox.simple_add_symlink('iota', 's-replace')
+ sbox.simple_add_symlink('iota', 's-in-place')
+ sbox.simple_add_symlink('iota', 's-type')
+ sbox.simple_append('s-reverse', 'link iota')
+ sbox.simple_add('s-reverse')
+ # Sleep here so that the subsequent s-in-place change is detected.
+ svntest.actions.do_sleep_for_timestamps()
+ sbox.simple_commit() # r2
+ svntest.actions.no_sleep_for_timestamps()
+
+ # Replace s-replace
+ sbox.simple_rm('s-replace')
+ sbox.simple_add_symlink('A/mu', 's-replace')
+
+ # Change target of s-in-place
+ if svntest.main.is_posix_os():
+ os.remove(sbox.ospath('s-in-place'))
+ os.symlink('A/mu', sbox.ospath('s-in-place'))
+ else:
+ sbox.simple_append('s-in-place', 'link A/mu', truncate = True)
+ sbox.simple_commit() # r3
+
+ # r4
+ svntest.main.run_svnmucc('propdel', 'svn:special',
+ sbox.repo_url + '/s-type',
+ '-m', 'Turn s-type into a file')
+
+ # r5
+ svntest.main.run_svnmucc('propset', 'svn:special', 'X',
+ sbox.repo_url + '/s-reverse',
+ '-m', 'Turn s-reverse into a symlink')
+
+ # Currently we expect to see 'U'pdates, but we would like to see
+ # replacements
+ expected_output = svntest.wc.State(wc_dir, {
+ 's-reverse' : Item(status=' U'),
+ 's-type' : Item(status=' U'),
+ })
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 5)
+ expected_status.add({
+ 's-type' : Item(status=' ', wc_rev='5'),
+ 's-replace' : Item(status=' ', wc_rev='5'),
+ 's-reverse' : Item(status=' ', wc_rev='5'),
+ 's-in-place' : Item(status=' ', wc_rev='5'),
+ })
+
+ # Update to HEAD/r5 to fetch the r4 and r5 symlink changes
+ svntest.actions.run_and_verify_update(wc_dir,
+ expected_output,
+ None,
+ expected_status,
+ None, None, None, None, None,
+ check_props=True)
+
+ # Update back to r2, to prepare some local changes
+ expected_output = svntest.wc.State(wc_dir, {
+ # s-replace is D + A
+ 's-replace' : Item(status='A '),
+ 's-in-place' : Item(status='U '),
+ 's-reverse' : Item(status=' U'),
+ 's-type' : Item(status=' U'),
+ })
+ expected_status.tweak(wc_rev=2)
+
+ svntest.actions.run_and_verify_update(wc_dir,
+ expected_output,
+ None,
+ expected_status,
+ None, None, None, None, None,
+ True,
+ wc_dir, '-r', '2')
+
+ # Ok, now add a property on all of them to make future symlinkness changes
+ # a tree conflict
+ # ### We should also try this with a 'textual change'
+ sbox.simple_propset('x', 'y', 's-replace', 's-in-place', 's-reverse', 's-type')
+
+ expected_output = svntest.wc.State(wc_dir, {
+ 's-replace' : Item(status=' ', treeconflict='A'),
+ 's-in-place' : Item(status='U '),
+ 's-reverse' : Item(status=' ', treeconflict='C'),
+ 's-type' : Item(status=' ', treeconflict='C'),
+ })
+ expected_status.tweak(wc_rev=5)
+ expected_status.tweak('s-replace', 's-reverse', 's-type', status='RM',
+ copied='+', treeconflict='C', wc_rev='-')
+ expected_status.tweak('s-in-place', status=' M')
+
+ svntest.actions.run_and_verify_update(wc_dir,
+ expected_output,
+ None,
+ expected_status,
+ None, None, None, None, None,
+ True)
+
########################################################################
# Run the tests
@@ -1144,6 +1243,7 @@ test_list = [ None,
replace_symlinks,
externals_as_symlink_targets,
cat_added_symlink,
+ incoming_symlink_changes,
]
if __name__ == '__main__':
Modified: subversion/branches/tree-read-api/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/cmdline/svntest/main.py?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/branches/tree-read-api/subversion/tests/cmdline/svntest/main.py Sun Nov 11 20:24:17 2012
@@ -829,12 +829,7 @@ def create_repos(path, minor_version = N
opts = ("--bdb-txn-nosync",)
if not minor_version or minor_version > options.server_minor_version:
minor_version = options.server_minor_version
- if minor_version < 4:
- opts += ("--pre-1.4-compatible",)
- elif minor_version < 5:
- opts += ("--pre-1.5-compatible",)
- elif minor_version < 6:
- opts += ("--pre-1.6-compatible",)
+ opts += ("--compatible-version=1.%d" % (minor_version),)
if options.fs_type is not None:
opts += ("--fs-type=" + options.fs_type,)
exit_code, stdout, stderr = run_command(svnadmin_binary, 1, 0, "create",
Modified: subversion/branches/tree-read-api/subversion/tests/cmdline/svntest/sandbox.py
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/cmdline/svntest/sandbox.py?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/cmdline/svntest/sandbox.py (original)
+++ subversion/branches/tree-read-api/subversion/tests/cmdline/svntest/sandbox.py Sun Nov 11 20:24:17 2012
@@ -307,6 +307,17 @@ class Sandbox:
raise Exception("Unexpected line '" + line + "' in proplist output" + str(out))
return props
+ def simple_add_symlink(self, dest, target):
+ """Create a symlink TARGET pointing to DEST and add it to subversion"""
+ if svntest.main.is_posix_os():
+ os.symlink(dest, self.ospath(target))
+ else:
+ svntest.main.file_write(self.ospath(target), "link %s" % dest)
+ self.simple_add(target)
+ if not svntest.main.is_posix_os():
+ # '*' is evaluated on Windows
+ self.simple_propset('svn:special', 'X', target)
+
def simple_copy(self, source, dest):
"""Copy SOURCE to DEST in the WC.
SOURCE and DEST are relpaths relative to the WC."""
Modified: subversion/branches/tree-read-api/subversion/tests/libsvn_wc/op-depth-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/libsvn_wc/op-depth-test.c?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/branches/tree-read-api/subversion/tests/libsvn_wc/op-depth-test.c Sun Nov 11 20:24:17 2012
@@ -1628,7 +1628,7 @@ temp_op_make_copy(svn_test__sandbox_t *b
SVN_ERR(insert_dirs(b, before));
- SVN_ERR(svn_wc__db_temp_op_make_copy(b->wc_ctx->db, dir_abspath, b->pool));
+ SVN_ERR(svn_wc__db_op_make_copy(b->wc_ctx->db, dir_abspath, NULL, NULL, b->pool));
SVN_ERR(check_db_rows(b, "", after));
Modified: subversion/branches/tree-read-api/tools/server-side/fsfs-reorg.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/tools/server-side/fsfs-reorg.c?rev=1408109&r1=1408108&r2=1408109&view=diff
==============================================================================
--- subversion/branches/tree-read-api/tools/server-side/fsfs-reorg.c (original)
+++ subversion/branches/tree-read-api/tools/server-side/fsfs-reorg.c Sun Nov 11 20:24:17 2012
@@ -47,165 +47,376 @@
#define ERROR_TAG "diff: "
+/* forward declarations */
typedef struct noderev_t noderev_t;
typedef struct revision_info_t revision_info_t;
+/* A FSFS rev file is sequence of fragments and unused space (the latter
+ * only being inserted by this tool and not during ordinary SVN operation).
+ *
+ * This type defines the type of any fragment.
+ *
+ * Please note that the classification as "property", "dir" or "file"
+ * fragments is only to be used while determining the future placement
+ * of a representation. If the rep is shared, the same rep may be used
+ * as *any* of the 3 kinds.
+ */
enum fragment_kind_t
{
+ /* the 2 number line containing changes and root node offsets */
header_fragment,
+
+ /* list of all changes in a revision */
changes_fragment,
- noderep_fragment,
+
+ /* (the textual representation of) a noderev */
+ noderev_fragment,
+
+ /* a property rep (including PLAIN / DELTA header) */
property_fragment,
+
+ /* a directory rep (including PLAIN / DELTA header) */
dir_fragment,
+
+ /* a file rep (including PLAIN / DELTA header) */
file_fragment
};
+/* A fragment. This is used to represent the final ordering, i.e. there
+ * will be an array containing elements of this type that basically put
+ * a fragment at some location in the target file.
+ */
typedef struct fragment_t
{
+ /* position in the target file */
apr_size_t position;
- void *data;
+
+ /* kind of fragment */
enum fragment_kind_t kind;
+
+ /* pointer to the fragment struct; type depends on KIND */
+ void *data;
} fragment_t;
+/* Location info for a single revision.
+ */
typedef struct revision_location_t
{
+ /* pack file offset (manifest value), 0 for non-packed files */
apr_size_t offset;
+
+ /* offset of the changes list relative to OFFSET */
apr_size_t changes;
+
+ /* length of the changes list on bytes */
apr_size_t changes_len;
+
+ /* first offset behind the revision data in the pack file (file length
+ * for non-packed revs) */
apr_size_t end;
} revision_location_t;
+/* Absolute position and size of some item.
+ */
typedef struct location_t
{
+ /* absolute offset in the file */
apr_size_t offset;
+
+ /* item length in bytes */
apr_size_t size;
} location_t;
+/* A parsed directory entry. Note that instances of this struct may be
+ * shared between different DIRECTORY_T containers.
+ */
typedef struct direntry_t
{
+ /* (local) entry / path name */
const char *name;
+
+ /* strlen (name) */
apr_size_t name_len;
+
+ /* node rev providing ID and representation(s) */
noderev_t *node;
} direntry_t;
+/* Representation of a parsed directory content.
+ */
typedef struct directory_t
{
+ /* array of pointers to DIRENTRY_T */
apr_array_header_t *entries;
+
+ /* MD5 of the textual representation. Will be set lazily as a side-effect
+ * of determining the length of this dir's textual representation. */
unsigned char target_md5[16];
+
+ /* (expanded) length of the textual representation.
+ * Determined lazily during the write process. */
apr_size_t size;
} directory_t;
+/* A representation fragment.
+ */
typedef struct representation_t
{
+ /* location in the source file */
location_t original;
+
+ /* location in the reordered target file */
location_t target;
+
+ /* length of the PLAIN / DELTA line in the source file in bytes */
apr_size_t header_size;
+
+ /* deltification base, or NULL if there is none */
struct representation_t *delta_base;
+
+ /* revision that contains this representation
+ * (may be referenced by other revisions, though) */
revision_info_t *revision;
+
+ /* representation content parsed as a directory. This will be NULL, if
+ * *no* directory noderev uses this representation. */
directory_t *dir;
+
+ /* the source content has a PLAIN header, so we may simply copy the
+ * source content into the target */
svn_boolean_t is_plain;
+
+ /* coloring flag used in the reordering algorithm to keep track of
+ * representations that still need to be placed. */
svn_boolean_t covered;
} representation_t;
+/* A node rev.
+ */
struct noderev_t
{
+ /* location within the source file */
location_t original;
+
+ /* location within the reorganized target file. */
location_t target;
+
+ /* predecessor node, or NULL if there is none */
noderev_t *predecessor;
+
+ /* content representation; may be NULL if there is none */
representation_t *text;
+
+ /* properties representation; may be NULL if there is none */
representation_t *props;
+
+ /* revision that this noderev belongs to */
revision_info_t *revision;
+
+ /* coloring flag used in the reordering algorithm to keep track of
+ * representations that still need to be placed. */
svn_boolean_t covered;
};
+/* Represents a single revision.
+ * There will be only one instance per revision. */
struct revision_info_t
{
+ /* number of this revision */
svn_revnum_t revision;
+
+ /* position in the source file */
revision_location_t original;
+
+ /* position in the reorganized target file */
revision_location_t target;
+
+ /* noderev of the root directory */
noderev_t *root_noderev;
+
+ /* all noderevs_t of this revision (in no particular order),
+ * i.e. those that point back to this struct */
apr_array_header_t *node_revs;
+
+ /* all representation_t of this revision (in no particular order),
+ * i.e. those that point back to this struct */
apr_array_header_t *representations;
};
+/* Represents a packed revision file.
+ */
typedef struct revision_pack_t
{
+ /* first revision in the pack file */
svn_revnum_t base;
+
+ /* revision_info_t* of all revisions in the pack file; in revision order. */
apr_array_header_t *info;
+
+ /* list of fragments to place in the target pack file; in target order. */
apr_array_header_t *fragments;
+
+ /* source pack file length */
apr_size_t filesize;
+
+ /* temporary value. Equal to the number of bytes in the target pack file
+ * already allocated to fragments. */
apr_size_t target_offset;
} revision_pack_t;
+/* Cache for revision source content. All content is stored in DATA and
+ * the HASH maps revision number to an svn_string_t instance whose data
+ * member points into DATA.
+ *
+ * Once TOTAL_SIZE exceeds LIMIT, all content will be discarded. Similarly,
+ * the hash gets cleared every 10000 insertions to keep the HASH_POOL
+ * memory usage in check.
+ */
typedef struct content_cache_t
{
- apr_pool_t *pool;
+ /* pool used for HASH */
apr_pool_t *hash_pool;
+ /* svn_revnum_t -> svn_string_t.
+ * The strings become (potentially) invalid when adding new cache entries. */
apr_hash_t *hash;
+ /* data buffer. the first TOTAL_SIZE bytes are actually being used. */
char *data;
+
+ /* DATA capacity */
apr_size_t limit;
+ /* number of bytes used in DATA */
apr_size_t total_size;
+
+ /* number of insertions since the last hash cleanup */
apr_size_t insert_count;
} content_cache_t;
+/* A cached directory. In contrast to directory_t, this stored the data as
+ * the plain hash that the normal FSFS will use to serialize & diff dirs.
+ */
typedef struct dir_cache_entry_t
{
+ /* revision containing the representation */
svn_revnum_t revision;
+
+ /* offset of the representation within that revision */
apr_size_t offset;
-
+
+ /* key-value representation of the directory entries */
apr_hash_t *hash;
} dir_cache_entry_t;
+/* Directory cache. (revision, offset) will be mapped directly into the
+ * ENTRIES array of ENTRY_COUNT buckets (many entries will be NULL).
+ * Two alternating pools will be used to allocate dir content.
+ *
+ * If the INSERT_COUNT exceeds a given limit, the pools get exchanged and
+ * the older of the two will be cleared. This is to keep dir objects valid
+ * for at least one insertion.
+ */
typedef struct dir_cache_t
{
+ /* fixed-size array of ENTRY_COUNT elements */
dir_cache_entry_t *entries;
+ /* currently used for entry allocations */
apr_pool_t *pool1;
+
+ /* previously used for entry allocations */
apr_pool_t *pool2;
+
+ /* size of ENTRIES in elements */
apr_size_t entry_count;
+
+ /* number of directory elements added. I.e. usually >> #cached dirs */
apr_size_t insert_count;
} dir_cache_t;
+/* A cached, undeltified txdelta window.
+ */
typedef struct window_cache_entry_t
{
+ /* revision containing the window */
svn_revnum_t revision;
+
+ /* offset of the deltified window within that revision */
apr_size_t offset;
+ /* window content */
svn_stringbuf_t *window;
} window_cache_entry_t;
+/* Cache for undeltified txdelta windows. (revision, offset) will be mapped
+ * directly into the ENTRIES array of INSERT_COUNT buckets (most entries
+ * will be NULL).
+ *
+ * The cache will be cleared when USED exceeds CAPACITY.
+ */
typedef struct window_cache_t
{
+ /* fixed-size array of ENTRY_COUNT elements */
window_cache_entry_t *entries;
+ /* used to allocate windows */
apr_pool_t *pool;
+
+ /* size of ENTRIES in elements */
apr_size_t entry_count;
+
+ /* maximum combined size of all cached windows */
apr_size_t capacity;
+
+ /* current combined size of all cached windows */
apr_size_t used;
} window_cache_t;
+/* Root data structure containing all information about a given repository.
+ */
typedef struct fs_fs_t
{
+ /* repository to reorg */
const char *path;
+
+ /* revision to start at (must be 0, ATM) */
svn_revnum_t start_revision;
+
+ /* FSFS format number */
int format;
+ /* highest revision number in the repo */
svn_revnum_t max_revision;
+
+ /* first non-packed revision */
svn_revnum_t min_unpacked_rev;
+
+ /* sharing size*/
int max_files_per_dir;
+ /* all revisions */
apr_array_header_t *revisions;
+
+ /* all packed files */
apr_array_header_t *packs;
+ /* empty representation.
+ * Used as a dummy base for DELTA reps without base. */
representation_t *null_base;
+
+ /* revision content cache */
content_cache_t *cache;
+
+ /* directory hash cache */
dir_cache_t *dir_cache;
+
+ /* undeltified txdelta window cache */
window_cache_t *window_cache;
} fs_fs_t;
+/* Return the rev pack folder for revision REV in FS.
+ */
static const char *
get_pack_folder(fs_fs_t *fs,
svn_revnum_t rev,
@@ -215,6 +426,8 @@ get_pack_folder(fs_fs_t *fs,
fs->path, rev / fs->max_files_per_dir);
}
+/* Return the path of the file containing revision REV in FS.
+ */
static const char *
rev_or_pack_file_name(fs_fs_t *fs,
svn_revnum_t rev,
@@ -226,6 +439,8 @@ rev_or_pack_file_name(fs_fs_t *fs,
rev / fs->max_files_per_dir, rev);
}
+/* Open the file containing revision REV in FS and return it in *FILE.
+ */
static svn_error_t *
open_rev_or_pack_file(apr_file_t **file,
fs_fs_t *fs,
@@ -239,6 +454,9 @@ open_rev_or_pack_file(apr_file_t **file,
pool);
}
+/* Read the whole content of the file containing REV in FS and return that
+ * in *CONTENT.
+ */
static svn_error_t *
read_rev_or_pack_file(svn_stringbuf_t **content,
fs_fs_t *fs,
@@ -250,13 +468,15 @@ read_rev_or_pack_file(svn_stringbuf_t **
pool);
}
+/* Return a new content cache with the given size LIMIT. Use POOL for
+ * all cache-related allocations.
+ */
static content_cache_t *
create_content_cache(apr_pool_t *pool,
apr_size_t limit)
{
content_cache_t *result = apr_pcalloc(pool, sizeof(*result));
- result->pool = pool;
result->hash_pool = svn_pool_create(pool);
result->hash = svn_hash__make(result->hash_pool);
result->limit = limit;
@@ -267,6 +487,9 @@ create_content_cache(apr_pool_t *pool,
return result;
}
+/* Return the content of revision REVISION from CACHE. Return NULL upon a
+ * cache miss. This is a cache-internal function.
+ */
static svn_string_t *
get_cached_content(content_cache_t *cache,
svn_revnum_t revision)
@@ -274,6 +497,9 @@ get_cached_content(content_cache_t *cach
return apr_hash_get(cache->hash, &revision, sizeof(revision));
}
+/* Take the content in DATA and store it under REVISION in CACHE.
+ * This is a cache-internal function.
+ */
static void
set_cached_content(content_cache_t *cache,
svn_revnum_t revision,
@@ -281,11 +507,14 @@ set_cached_content(content_cache_t *cach
{
svn_string_t *content;
svn_revnum_t *key;
-
+
+ /* double insertion? -> broken cache logic */
assert(get_cached_content(cache, revision) == NULL);
+ /* purge the cache upon overflow */
if (cache->total_size + data->len > cache->limit)
{
+ /* the hash pool grows slowly over time; clear it once in a while */
if (cache->insert_count > 10000)
{
svn_pool_clear(cache->hash_pool);
@@ -296,8 +525,13 @@ set_cached_content(content_cache_t *cach
cache->hash = svn_hash__make(cache->hash_pool);
cache->total_size = 0;
+
+ /* buffer overflow / revision too large */
+ if (data->len > cache->limit)
+ SVN_ERR_MALFUNCTION_NO_RETURN();
}
+ /* copy data to cache and update he index (hash) */
content = apr_palloc(cache->hash_pool, sizeof(*content));
content->data = cache->data + cache->total_size;
content->len = data->len;
@@ -312,6 +546,9 @@ set_cached_content(content_cache_t *cach
++cache->insert_count;
}
+/* Get the file content of revision REVISION in FS and return it in *DATA.
+ * Use SCRATCH_POOL for temporary allocations.
+ */
static svn_error_t *
get_content(svn_string_t **data,
fs_fs_t *fs,
@@ -322,7 +559,8 @@ get_content(svn_string_t **data,
revision_info_t *revision_info;
svn_stringbuf_t *temp;
apr_off_t temp_offset;
-
+
+ /* try getting the data from our cache */
svn_string_t *result = get_cached_content(fs->cache, revision);
if (result)
{
@@ -330,6 +568,7 @@ get_content(svn_string_t **data,
return SVN_NO_ERROR;
}
+ /* not in cache. Is the revision valid at all? */
if (revision - fs->start_revision > fs->revisions->nelts)
return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
_("Unknown revision %ld"), revision);
@@ -337,6 +576,8 @@ get_content(svn_string_t **data,
revision - fs->start_revision,
revision_info_t*);
+ /* read the revision content. Assume that the file has *not* been
+ * reorg'ed, yet, i.e. all data is in one place. */
temp = svn_stringbuf_create_ensure( revision_info->original.end
- revision_info->original.offset,
scratch_pool);
@@ -350,6 +591,7 @@ get_content(svn_string_t **data,
revision_info->original.offset = (apr_size_t)temp_offset;
SVN_ERR(svn_io_file_read(file, temp->data, &temp->len, scratch_pool));
+ /* cache the result and return it */
set_cached_content(fs->cache, revision,
svn_stringbuf__morph_into_string(temp));
*data = get_cached_content(fs->cache, revision);
@@ -357,6 +599,9 @@ get_content(svn_string_t **data,
return SVN_NO_ERROR;
}
+/* Return a new directory cache with ENTRY_COUNT buckets in its index.
+ * Use POOL for all cache-related allocations.
+ */
static dir_cache_t *
create_dir_cache(apr_pool_t *pool,
apr_size_t entry_count)
@@ -372,6 +617,9 @@ create_dir_cache(apr_pool_t *pool,
return result;
}
+/* Return the position within FS' dir cache ENTRIES index for the given
+ * (REVISION, OFFSET) pair. This is a cache-internal function.
+ */
static apr_size_t
get_dir_cache_index(fs_fs_t *fs,
svn_revnum_t revision,
@@ -380,12 +628,18 @@ get_dir_cache_index(fs_fs_t *fs,
return (revision + offset * 0xd1f3da69) % fs->dir_cache->entry_count;
}
+/* Return the currently active pool of FS' dir cache. Note that it may be
+ * cleared after *2* insertions.
+ */
static apr_pool_t *
get_cached_dir_pool(fs_fs_t *fs)
{
return fs->dir_cache->pool1;
}
+/* Return the cached directory content stored in REPRESENTAION within FS.
+ * If that has not been found in cache, return NULL.
+ */
static apr_hash_t *
get_cached_dir(fs_fs_t *fs,
representation_t *representation)
@@ -401,17 +655,21 @@ get_cached_dir(fs_fs_t *fs,
: NULL;
}
+/* Cache the directory HASH for REPRESENTAION within FS.
+ */
static void
set_cached_dir(fs_fs_t *fs,
representation_t *representation,
apr_hash_t *hash)
{
+ /* select the entry to use */
svn_revnum_t revision = representation->revision->revision;
apr_size_t offset = representation->original.offset;
apr_size_t i = get_dir_cache_index(fs, revision, offset);
dir_cache_entry_t *entry = &fs->dir_cache->entries[i];
+ /* clean the cache and rotate pools at regular intervals */
fs->dir_cache->insert_count += apr_hash_count(hash);
if (fs->dir_cache->insert_count >= fs->dir_cache->entry_count * 100)
{
@@ -428,11 +686,16 @@ set_cached_dir(fs_fs_t *fs,
fs->dir_cache->pool1 = pool;
}
+ /* write data to cache */
entry->hash = hash;
entry->offset = offset;
entry->revision = revision;
}
+/* Return a new txdelta window cache with ENTRY_COUNT buckets in its index
+ * and a the total CAPACITY given in bytes.
+ * Use POOL for all cache-related allocations.
+ */
static window_cache_t *
create_window_cache(apr_pool_t *pool,
apr_size_t entry_count,
@@ -449,6 +712,9 @@ create_window_cache(apr_pool_t *pool,
return result;
}
+/* Return the position within FS' window cache ENTRIES index for the given
+ * (REVISION, OFFSET) pair. This is a cache-internal function.
+ */
static apr_size_t
get_window_cache_index(fs_fs_t *fs,
svn_revnum_t revision,
@@ -457,6 +723,9 @@ get_window_cache_index(fs_fs_t *fs,
return (revision + offset * 0xd1f3da69) % fs->window_cache->entry_count;
}
+/* Return the cached txdelta window stored in REPRESENTAION within FS.
+ * If that has not been found in cache, return NULL.
+ */
static svn_stringbuf_t *
get_cached_window(fs_fs_t *fs,
representation_t *representation,
@@ -473,17 +742,21 @@ get_cached_window(fs_fs_t *fs,
: NULL;
}
+/* Cache the undeltified txdelta WINDOW for REPRESENTAION within FS.
+ */
static void
set_cached_window(fs_fs_t *fs,
representation_t *representation,
svn_stringbuf_t *window)
{
+ /* select entry */
svn_revnum_t revision = representation->revision->revision;
apr_size_t offset = representation->original.offset;
apr_size_t i = get_window_cache_index(fs, revision, offset);
window_cache_entry_t *entry = &fs->window_cache->entries[i];
+ /* if the capacity is exceeded, clear the cache */
fs->window_cache->used += window->len;
if (fs->window_cache->used >= fs->window_cache->capacity)
{
@@ -494,6 +767,7 @@ set_cached_window(fs_fs_t *fs,
fs->window_cache->used = window->len;
}
+ /* set the entry to a copy of the window data */
entry->window = svn_stringbuf_dup(window, fs->window_cache->pool);
entry->offset = offset;
entry->revision = revision;
@@ -1672,7 +1946,7 @@ add_noderev_recursively(fs_fs_t *fs,
node->target.offset = *current_pos;
fragment.data = node;
- fragment.kind = noderep_fragment;
+ fragment.kind = noderev_fragment;
fragment.position = *current_pos;
APR_ARRAY_PUSH(fragments, fragment_t) = fragment;
@@ -1804,7 +2078,7 @@ get_content_length(apr_size_t *length,
case dir_fragment:
*length = content->len + 16;
break;
- case noderep_fragment:
+ case noderev_fragment:
*length = content->len + 3;
break;
default:
@@ -1846,7 +2120,7 @@ move_fragment(fragment_t *fragment,
representation->target.offset = new_position;
break;
- case noderep_fragment:
+ case noderev_fragment:
node = fragment->data;
node->target.offset = new_position;
break;
@@ -2352,7 +2626,7 @@ get_fragment_content(svn_string_t **cont
return SVN_NO_ERROR;
- case noderep_fragment:
+ case noderev_fragment:
node = fragment->data;
SVN_ERR(get_content(&revision_content, fs,
node->revision->revision, pool));