Author: stsp Date: Fri Jul 29 14:48:18 2011 New Revision: 1152245 URL: http://svn.apache.org/viewvc?rev=1152245&view=rev Log: Update moved-to information on the BASE path of a node which is being moved out of, or within, an existing moved subtree. Also, make 'svn commit' deal with the add-half of such moves correctly.
* subversion/libsvn_wc/wc_db.c (op_delete_txn): If the node was moved-here and we are moving it away, we always want to update moved-to in BASE, regardless of whether the node itself was the op-root of the move that moved it here. Prior to this commit, we only updated moved-to if the node itself was the op-root. * subversion/libsvn_client/commit.c (svn_client_commit5): If the delete-half of a move is not in the commit target list, look up the op-root of the delete and check if the op-root is among the commit targets. If it is, the delete-half of the move will be committed along with it, and we can allow the commit. Modified: subversion/trunk/subversion/libsvn_client/commit.c subversion/trunk/subversion/libsvn_wc/wc_db.c Modified: subversion/trunk/subversion/libsvn_client/commit.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/commit.c?rev=1152245&r1=1152244&r2=1152245&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_client/commit.c (original) +++ subversion/trunk/subversion/libsvn_client/commit.c Fri Jul 29 14:48:18 2011 @@ -1379,19 +1379,72 @@ svn_client_commit5(const apr_array_heade goto cleanup; if (moved_from_abspath && delete_op_root_abspath && - strcmp(moved_from_abspath, delete_op_root_abspath) == 0 && - apr_hash_get(committables->by_path, delete_op_root_abspath, - APR_HASH_KEY_STRING) == NULL) + strcmp(moved_from_abspath, delete_op_root_abspath) == 0) + { - cmt_err = svn_error_createf( - SVN_ERR_ILLEGAL_TARGET, NULL, - _("Cannot commit '%s' because it was moved from " - "'%s' which is not part of the commit; both " - "sides of the move must be committed together"), - svn_dirent_local_style(item->path, iterpool), - svn_dirent_local_style(delete_op_root_abspath, - iterpool)); - goto cleanup; + svn_boolean_t found_delete_half = + (apr_hash_get(committables->by_path, delete_op_root_abspath, + APR_HASH_KEY_STRING) != NULL); + + if (!found_delete_half) + { + const char *delete_half_parent_abspath; + + /* The delete-half isn't in the commit target list. + * However, it might itself be the child of a deleted node, + * either because of another move or a deletion. + * + * For example, consider: mv A/B B; mv B/C C; commit; + * C's moved-from A/B/C is a child of the deleted A/B. + * A/B/C does not appear in the commit target list, but + * A/B does appear. + * (Note that moved-from information is always stored + * relative to the BASE tree, so we have 'C moved-from + * A/B/C', not 'C moved-from B/C'.) + * + * An example involving a move and a delete would be: + * mv A/B C; rm A; commit; + * Now C is moved-from A/B which does not appear in the + * commit target list, but A does appear. + */ + + /* Scan upwards for a deletion op-root from the + * delete-half's parent directory. */ + delete_half_parent_abspath = + svn_dirent_dirname(delete_op_root_abspath, iterpool); + if (strcmp(delete_op_root_abspath, + delete_half_parent_abspath) != 0) + { + const char *parent_delete_op_root_abspath; + + cmt_err = svn_error_trace( + svn_wc__node_get_deleted_ancestor( + &parent_delete_op_root_abspath, + ctx->wc_ctx, delete_half_parent_abspath, + iterpool, iterpool)); + if (cmt_err) + goto cleanup; + + if (parent_delete_op_root_abspath) + found_delete_half = + (apr_hash_get(committables->by_path, + parent_delete_op_root_abspath, + APR_HASH_KEY_STRING) != NULL); + } + } + + if (!found_delete_half) + { + cmt_err = svn_error_createf( + SVN_ERR_ILLEGAL_TARGET, NULL, + _("Cannot commit '%s' because it was moved from " + "'%s' which is not part of the commit; both " + "sides of the move must be committed together"), + svn_dirent_local_style(item->path, iterpool), + svn_dirent_local_style(delete_op_root_abspath, + iterpool)); + goto cleanup; + } } } /* ### TODO: check the delete-half, too */ Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1152245&r1=1152244&r2=1152245&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db.c (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db.c Fri Jul 29 14:48:18 2011 @@ -6070,21 +6070,19 @@ op_delete_txn(void *baton, if (b->moved_to_relpath) { const char *moved_from_relpath; - const char *delete_op_root_relpath; /* ### call scan_addition_txn() directly? */ if (status == svn_wc__db_status_added) SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, - &moved_from_relpath, - &delete_op_root_relpath, + &moved_from_relpath, NULL, wcroot, local_relpath, scratch_pool, scratch_pool)); - if (status == svn_wc__db_status_moved_here && - strcmp(moved_from_relpath, delete_op_root_relpath) == 0) + if (status == svn_wc__db_status_moved_here) { - /* The node has already been moved and is being moved again. + /* The node has already been moved, possibly along with a parent, + * and is being moved again. * Update the existing moved_to path at the delete-half of * the prior move. The source of a move is in the BASE tree * so it remains constant if a node is moved around multiple