Return-Path: X-Original-To: apmail-subversion-commits-archive@minotaur.apache.org Delivered-To: apmail-subversion-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id BC7E910DDA for ; Sat, 14 Dec 2013 20:36:17 +0000 (UTC) Received: (qmail 32522 invoked by uid 500); 14 Dec 2013 20:36:17 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 32505 invoked by uid 500); 14 Dec 2013 20:36:16 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 32498 invoked by uid 99); 14 Dec 2013 20:36:16 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 14 Dec 2013 20:36:16 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 14 Dec 2013 20:36:14 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 6F32623888E4; Sat, 14 Dec 2013 20:35:09 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1550967 - in /subversion/trunk/subversion: libsvn_client/mtcc.c libsvn_client/mtcc.h tests/libsvn_client/mtcc-test.c Date: Sat, 14 Dec 2013 20:35:09 -0000 To: commits@subversion.apache.org From: rhuijben@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20131214203509.6F32623888E4@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 +#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 };