Author: rhuijben
Date: Sat Dec 14 20:35:08 2013
New Revision: 1550967
URL: http://svn.apache.org/r1550967
Log:
Properly handle tree replacements in the existance checking of the 'mtcc' api.
* subversion/libsvn_client/mtcc.c
(SVN_PATH_IS_EMPTY): New define. Copied from path.c.
(mtcc_op_find): Search children backwards to find adds before deletes.
Properly bail on non directories.
(get_origin): Return doesn't exist information for new nodes.
(svn_client_mtcc_get_origin): Rename to...
(mtcc_get_origin): ... this. Add boolean to define no origin handling.
(svn_client_mtcc_create): Obtain and store head revision. Verify base
revision using head revision.
(mtcc_verify_create): Use standard error messages to ease translation.
(svn_client_mtcc_add_add_file): Use check in standard form.
(svn_client_mtcc_add_copy): Verify copy revision using head revision.
(svn_client_mtcc_add_delete): Use check in standard form, fixing bug.
(svn_client_mtcc_add_mkdir): Use check in standard form.
(svn_client_mtcc_add_move): Update caller, asking for error message.
(svn_client_mtcc_add_propset): Use check in standard form.
(svn_client_mtcc_check_path): Use check in standard form. Properly
check for replacements, etc.
* subversion/libsvn_client/mtcc.h
(MTCC_UNMODIFIED): Handle unmodified file open as unmodified.
(svn_client_mtcc_t): Add boolean.
* subversion/tests/libsvn_client/mtcc-test.c
(test_replace_tree): New test.
(test_funcs): Add test.
Modified:
subversion/trunk/subversion/libsvn_client/mtcc.c
subversion/trunk/subversion/libsvn_client/mtcc.h
subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c
Modified: subversion/trunk/subversion/libsvn_client/mtcc.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/mtcc.c?rev=1550967&r1=1550966&r2=1550967&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/mtcc.c (original)
+++ subversion/trunk/subversion/libsvn_client/mtcc.c Sat Dec 14 20:35:08 2013
@@ -36,6 +36,8 @@
#include <assert.h>
+#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0')
+
static svn_client_mtcc_op_t *
mtcc_op_create(const char *name,
svn_boolean_t add,
@@ -78,7 +80,7 @@ mtcc_op_find(svn_client_mtcc_op_t **op,
if (created)
*created = FALSE;
- if (!*relpath)
+ if (SVN_PATH_IS_EMPTY(relpath))
{
if (find_existing)
*op = base_op;
@@ -98,13 +100,21 @@ mtcc_op_find(svn_client_mtcc_op_t **op,
else
name = relpath;
- if (child && !base_op->children)
- return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL,
- _("Can't operate on '%s' because '%s' is not a "
- "directory"),
- child, base_op->name);
+ if (!base_op->children)
+ {
+ if (!created)
+ {
+ *op = NULL;
+ return SVN_NO_ERROR;
+ }
+ else
+ return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+ _("Can't operate on '%s' because '%s' is not
a "
+ "directory"),
+ name, base_op->name);
+ }
- for (i = 0; i < base_op->children->nelts; i++)
+ for (i = base_op->children->nelts-1; i >= 0 ; i--)
{
svn_client_mtcc_op_t *cop;
@@ -150,7 +160,8 @@ mtcc_op_find(svn_client_mtcc_op_t **op,
/* Gets the original repository location of RELPATH, checking things
like copies, moves, etc. */
static svn_error_t *
-get_origin(const char **origin_relpath,
+get_origin(svn_boolean_t *done,
+ const char **origin_relpath,
svn_revnum_t *rev,
svn_client_mtcc_op_t *op,
const char *relpath,
@@ -159,7 +170,7 @@ get_origin(const char **origin_relpath,
{
const char *child;
const char *name;
- if (!*relpath)
+ if (SVN_PATH_IS_EMPTY(relpath))
{
*origin_relpath = op->src_relpath
? apr_pstrdup(result_pool, op->src_relpath)
@@ -181,19 +192,25 @@ get_origin(const char **origin_relpath,
{
int i;
- for (i = 0; i < op->children->nelts; i++)
+ for (i = op->children->nelts-1; i >= 0; i--)
{
svn_client_mtcc_op_t *cop;
cop = APR_ARRAY_IDX(op->children, i, svn_client_mtcc_op_t *);
- if (! strcmp(cop->name, name) && cop->kind != OP_DELETE)
+ if (! strcmp(cop->name, name))
{
- SVN_ERR(get_origin(origin_relpath, rev,
+ if (cop->kind == OP_DELETE)
+ {
+ *done = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(get_origin(done, origin_relpath, rev,
cop, child ? child : "",
result_pool, scratch_pool));
- if (*origin_relpath)
+ if (*origin_relpath || *done)
return SVN_NO_ERROR;
break;
@@ -201,35 +218,48 @@ get_origin(const char **origin_relpath,
}
}
- if (op->src_relpath)
+ if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE)
{
- *origin_relpath = svn_relpath_join(op->src_relpath, relpath,
- result_pool);
- *rev = op->src_rev;
+ *done = TRUE;
+ if (op->src_relpath)
+ {
+ *origin_relpath = svn_relpath_join(op->src_relpath, relpath,
+ result_pool);
+ *rev = op->src_rev;
+ }
}
return SVN_NO_ERROR;
}
static svn_error_t * /* ### Make public? */
-svn_client_mtcc_get_origin(const char **origin_relpath,
- svn_revnum_t *rev,
- const char *relpath,
- svn_client_mtcc_t *mtcc,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+mtcc_get_origin(const char **origin_relpath,
+ svn_revnum_t *rev,
+ const char *relpath,
+ svn_boolean_t ignore_enoent,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
+ svn_boolean_t done = FALSE;
+
*origin_relpath = NULL;
*rev = SVN_INVALID_REVNUM;
- SVN_ERR(get_origin(origin_relpath, rev, mtcc->root_op, relpath,
+ SVN_ERR(get_origin(&done, origin_relpath, rev, mtcc->root_op, relpath,
result_pool, scratch_pool));
- if (!*origin_relpath)
+ if (!*origin_relpath && !done)
{
*origin_relpath = apr_pstrdup(result_pool, relpath);
*rev = mtcc->base_revision;
}
+ else if (!ignore_enoent)
+ {
+ return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+ _("No origin found for node at '%s'"),
+ relpath);
+ }
return SVN_NO_ERROR;
}
@@ -258,6 +288,16 @@ svn_client_mtcc_create(svn_client_mtcc_t
NULL /* wri_abspath */, ctx,
mtcc_pool, scratch_pool));
+ SVN_ERR(svn_ra_get_latest_revnum((*mtcc)->ra_session,
&(*mtcc)->head_revision,
+ scratch_pool));
+
+ if (! SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = (*mtcc)->head_revision;
+ else if (base_revision > (*mtcc)->head_revision)
+ return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+ _("No such revision %ld (HEAD is %ld)"),
+ base_revision, (*mtcc)->head_revision);
+
return SVN_NO_ERROR;
}
@@ -353,8 +393,7 @@ mtcc_verify_create(svn_client_mtcc_t *mt
if (op)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
- _("Can't create '%s': target already "
- "operated on"),
+ _("Path '%s' already exists"),
new_relpath);
SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, TRUE,
@@ -371,7 +410,7 @@ mtcc_verify_create(svn_client_mtcc_t *mt
if (kind != svn_node_none)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
- _("Can't copy to '%s': target already exists"),
+ _("Path '%s' already exists"),
new_relpath);
return SVN_NO_ERROR;
@@ -391,7 +430,7 @@ svn_client_mtcc_add_add_file(const char
SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool));
- if (!*relpath && MTCC_UNMODIFIED(mtcc))
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc))
{
/* Turn the root operation into a file addition */
op = mtcc->root_op;
@@ -429,8 +468,15 @@ svn_client_mtcc_add_copy(const char *src
svn_node_kind_t kind;
SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath)
- && svn_relpath_is_canonical(dst_relpath)
- && SVN_IS_VALID_REVNUM(revision));
+ && svn_relpath_is_canonical(dst_relpath));
+
+ if (! SVN_IS_VALID_REVNUM(revision))
+ revision = mtcc->head_revision;
+ else if (revision > mtcc->head_revision)
+ {
+ return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+ _("No such revision %ld"), revision);
+ }
SVN_ERR(mtcc_verify_create(mtcc, dst_relpath, scratch_pool));
@@ -440,9 +486,8 @@ svn_client_mtcc_add_copy(const char *src
if (kind != svn_node_dir && kind != svn_node_file)
{
- return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
- _("Can't create a copy of '%s' at revision %ld "
- "as it does not exist"),
+ return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+ _("Path '%s' not found in revision %ld"),
src_relpath, revision);
}
@@ -483,7 +528,7 @@ svn_client_mtcc_add_delete(const char *r
"does not exist"),
relpath);
- if (! *relpath || MTCC_UNMODIFIED(mtcc))
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc))
{
/* Turn root operation into delete */
op = mtcc->root_op;
@@ -519,7 +564,7 @@ svn_client_mtcc_add_mkdir(const char *re
SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool));
- if (! *relpath && MTCC_UNMODIFIED(mtcc))
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc))
{
/* Turn the root of the operation in an MKDIR */
mtcc->root_op->kind = OP_ADD_DIR;
@@ -551,9 +596,9 @@ svn_client_mtcc_add_move(const char *src
const char *origin_relpath;
svn_revnum_t origin_rev;
- SVN_ERR(svn_client_mtcc_get_origin(&origin_relpath, &origin_rev,
- src_relpath, mtcc,
- scratch_pool, scratch_pool));
+ SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev,
+ src_relpath, FALSE, mtcc,
+ scratch_pool, scratch_pool));
SVN_ERR(svn_client_mtcc_add_copy(src_relpath, mtcc->base_revision,
dst_relpath, mtcc, scratch_pool));
@@ -602,7 +647,7 @@ svn_client_mtcc_add_propset(const char *
/* ### TODO: Call svn_wc_canonicalize_svn_prop() */
}
- if (!*relpath && MTCC_UNMODIFIED(mtcc))
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc))
{
svn_node_kind_t kind;
@@ -724,9 +769,8 @@ svn_client_mtcc_check_path(svn_node_kind
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
- if (!*relpath
- && !mtcc->root_op->performed_stat
- && MTCC_UNMODIFIED(mtcc))
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)
+ && !mtcc->root_op->performed_stat)
{
/* We know nothing about the root. Perhaps it is a file? */
SVN_ERR(svn_ra_check_path(mtcc->ra_session, "", mtcc->base_revision,
@@ -746,12 +790,15 @@ svn_client_mtcc_check_path(svn_node_kind
if (!op || (check_repository && !op->performed_stat))
{
- SVN_ERR(svn_client_mtcc_get_origin(&origin_relpath, &origin_rev,
- relpath, mtcc,
- scratch_pool, scratch_pool));
+ SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev,
+ relpath, TRUE, mtcc,
+ scratch_pool, scratch_pool));
- SVN_ERR(svn_ra_check_path(mtcc->ra_session, origin_relpath,
- origin_rev, kind, scratch_pool));
+ if (!origin_relpath)
+ *kind = svn_node_none;
+ else
+ SVN_ERR(svn_ra_check_path(mtcc->ra_session, origin_relpath,
+ origin_rev, kind, scratch_pool));
if (op && *kind == svn_node_dir)
{
Modified: subversion/trunk/subversion/libsvn_client/mtcc.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/mtcc.h?rev=1550967&r1=1550966&r2=1550967&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/mtcc.h (original)
+++ subversion/trunk/subversion/libsvn_client/mtcc.h Sat Dec 14 20:35:08 2013
@@ -55,14 +55,18 @@ typedef struct svn_client_mtcc_op_t
} svn_client_mtcc_op_t;
/* Check if the mtcc doesn't contain any modifications yet */
-#define MTCC_UNMODIFIED(mtcc)
\
- ((mtcc->root_op->kind == OP_OPEN_DIR)
\
- && (mtcc->root_op->prop_mods == NULL || !mtcc->root_op->prop_mods->nelts)
\
- && (mtcc->root_op->children == NULL || !mtcc->root_op->children->nelts))
+#define MTCC_UNMODIFIED(mtcc) \
+ ((mtcc->root_op->kind == OP_OPEN_DIR \
+ || mtcc->root_op->kind == OP_OPEN_FILE) \
+ && (mtcc->root_op->prop_mods == NULL \
+ || !mtcc->root_op->prop_mods->nelts) \
+ && (mtcc->root_op->children == NULL \
+ || !mtcc->root_op->children->nelts))
struct svn_client_mtcc_t
{
apr_pool_t *pool;
+ svn_revnum_t head_revision;
svn_revnum_t base_revision;
svn_ra_session_t *ra_session;
Modified: subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c?rev=1550967&r1=1550966&r2=1550967&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_client/mtcc-test.c Sat Dec 14
20:35:08 2013
@@ -417,6 +417,42 @@ test_anchoring(const svn_test_opts_t *op
return SVN_NO_ERROR;
}
+static svn_error_t *
+test_replace_tree(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_client_mtcc_t *mtcc;
+ svn_client_ctx_t *ctx;
+ const char *repos_abspath;
+ const char *repos_url;
+ svn_repos_t* repos;
+
+ repos_abspath = svn_test_data_path("mtcc-replace_tree", pool);
+ SVN_ERR(svn_dirent_get_absolute(&repos_abspath, repos_abspath, pool));
+ SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_url, repos_abspath, pool));
+ SVN_ERR(svn_test__create_repos(&repos, repos_abspath, opts, pool));
+
+ SVN_ERR(make_greek_tree(repos_url, pool));
+
+ SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
+
+ /* Update a file as root operation */
+ SVN_ERR(svn_client_mtcc_create(&mtcc, repos_url, 1, ctx, pool, pool));
+
+ SVN_ERR(svn_client_mtcc_add_delete("A", mtcc, pool));
+ SVN_ERR(svn_client_mtcc_add_delete("iota", mtcc, pool));
+ SVN_ERR(svn_client_mtcc_add_mkdir("A", mtcc, pool));
+ SVN_ERR(svn_client_mtcc_add_mkdir("A/B", mtcc, pool));
+ SVN_ERR(svn_client_mtcc_add_mkdir("A/B/C", mtcc, pool));
+ SVN_ERR(svn_client_mtcc_add_mkdir("M", mtcc, pool));
+ SVN_ERR(svn_client_mtcc_add_mkdir("M/N", mtcc, pool));
+ SVN_ERR(svn_client_mtcc_add_mkdir("M/N/O", mtcc, pool));
+
+ SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
+
+ return SVN_NO_ERROR;
+}
+
/* ==========================================================================
*/
@@ -439,6 +475,8 @@ struct svn_test_descriptor_t test_funcs[
"test overwrite"),
SVN_TEST_OPTS_PASS(test_anchoring,
"test mtcc anchoring for root operations"),
+ SVN_TEST_OPTS_PASS(test_replace_tree,
+ "test mtcc replace tree"),
SVN_TEST_NULL
};