Author: stsp
Date: Tue Aug 22 19:45:43 2017
New Revision: 1805813
URL: http://svn.apache.org/viewvc?rev=1805813&view=rev
Log:
On the addremove branch, introduce new a API for creating local moves.
This new API works on deleted and added nodes which are present in the
working copy. It makes it possible to change copyfrom info of added or
copied nodes to point at a deleted node, so they become part of a local move.
The heuristic which matches up deletes and adds is not documented.
For now, it only works for files and relies on the pristine SHA1 checksum
to determine identify. In the future, we may add additional heuristics,
including repository log scanning as implemented in the conflict resolver.
While this feature was invented for addremove, it could eventually be useful
outside the context of addremove. For instance, merges currently produce
copyfrom which points to a different branch if a move is merged without
a tree conflict (see issue #2685). Perhaps, in the future, a merge could
use this new API to create moves in the working copy as a post-processing
step, fixing up copyfrom info such that it points to valid move source
within the working copy (and thus hopefully the same branch) instead.
* subversion/include/private/svn_wc_private.h
(svn_wc__move_fixup): Declare.
* subversion/include/svn_client.h
(svn_client_addremove): Fix name of first argument (cosmetic change).
(svn_client_match_up_local_deletes_and_adds): Declare the new API.
* subversion/libsvn_client/addremove.c
(addremove_status_baton): Add tables for added and deleted nodes.
(addremove_status_func): Collect added and deleted nodles in addition to,
or instead of, missing and unversioned ones.
(suggest_moves): Rename parameters from unversioned/missing to added/deleted.
(addremove): Stop moving items, just add or delete them.
(match_up_local_deletes_and_adds,
svn_client_match_up_local_deletes_and_adds): New. Implement the new API.
* subversion/libsvn_wc/copy.c
(svn_wc__move_fixup): Implement new private API at the libsvn_wc layer.
* subversion/libsvn_wc/wc-queries.sql
(STMT_SET_MOVED_HERE_RECURSIVE): New query which sets 'moved_here'.
* subversion/libsvn_wc/wc_db.c
(get_origin_of_delete_op_root): New helper function, factored out of ...
(get_info_for_copy): ... this one.
(set_moved_here_recursive, update_origin): New helper functions.
(move_fixup, svn_wc__db_move_fixup): Implement wc_db support for new API.
* subversion/libsvn_wc/wc_db.h
(svn_wc__db_move_fixup): Declare.
* subversion/svn/addremove-cmd.c
(wrap_illegal_target_error): New helper, factored out of ...
(svn_cl__addremove): ... this function. After adding unversioned and deleting
missing nodes, call the new API to create local moves.
Modified:
subversion/branches/addremove/subversion/include/private/svn_wc_private.h
subversion/branches/addremove/subversion/include/svn_client.h
subversion/branches/addremove/subversion/libsvn_client/addremove.c
subversion/branches/addremove/subversion/libsvn_wc/copy.c
subversion/branches/addremove/subversion/libsvn_wc/wc-queries.sql
subversion/branches/addremove/subversion/libsvn_wc/wc_db.c
subversion/branches/addremove/subversion/libsvn_wc/wc_db.h
subversion/branches/addremove/subversion/svn/addremove-cmd.c
Modified:
subversion/branches/addremove/subversion/include/private/svn_wc_private.h
URL:
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/include/private/svn_wc_private.h?rev=1805813&r1=1805812&r2=1805813&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/include/private/svn_wc_private.h
(original)
+++ subversion/branches/addremove/subversion/include/private/svn_wc_private.h
Tue Aug 22 19:45:43 2017
@@ -1998,6 +1998,40 @@ svn_wc__move2(svn_wc_context_t *wc_ctx,
apr_pool_t *scratch_pool);
+/**
+ * Move @a src_abspath to @a dst_abspath in working copy meta data.
+ * This is a database-only operation and the working directories and files
+ * are not changed.
+ *
+ * @a src_abspath must be a deleted file or directory under version control;
+ * the parent of @a dst_abspath must be a directory under version control
+ * in the same working copy; @a dst_abspath must already exist as an added
+ * or copied item of the same node kind as the source. Note that when
+ * @a src points to a deleted file, the working file is not assumed to exist,
+ * so its text-base is used instead.
+ *
+ * If @a cancel_func is non-NULL, call it with @a cancel_baton at
+ * various points during the operation. If it returns an error
+ * (typically #SVN_ERR_CANCELLED), return that error immediately.
+ *
+ * If @a notify_func is non-NULL, call it with @a notify_baton and the path
+ * of the root node (only) of the destination.
+ *
+ * Use @a scratch_pool for temporary allocations.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_wc__move_fixup(svn_wc_context_t *wc_ctx,
+ const char *src_abspath,
+ const char *dst_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
+
+
/* During merge when we encounter added directories, we add them using
svn_wc_add4(), recording its original location, etc. But at that time
we don't have its original properties. This function allows updating the
Modified: subversion/branches/addremove/subversion/include/svn_client.h
URL:
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/include/svn_client.h?rev=1805813&r1=1805812&r2=1805813&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/include/svn_client.h (original)
+++ subversion/branches/addremove/subversion/include/svn_client.h Tue Aug 22
19:45:43 2017
@@ -1684,12 +1684,27 @@ svn_client_add(const char *path,
* @since New in 1.10.
*/
svn_error_t *
-svn_client_addremove(const char *path,
+svn_client_addremove(const char *local_path,
svn_depth_t depth,
svn_boolean_t no_autoprops,
svn_boolean_t no_ignore,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
+
+/**
+ * Recurse into the versioned directory @a local_path, and attempt to match
+ * up versioned deleted nodes with versioned added (or copied) nodes.
+ * Any matches found will be transformed into a move.
+ *
+ * The level of recursion is specified by @a depth.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_match_up_local_deletes_and_adds(const char *local_path,
+ svn_depth_t depth,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
/** @} */
/**
Modified: subversion/branches/addremove/subversion/libsvn_client/addremove.c
URL:
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/addremove.c?rev=1805813&r1=1805812&r2=1805813&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_client/addremove.c
(original)
+++ subversion/branches/addremove/subversion/libsvn_client/addremove.c Tue Aug
22 19:45:43 2017
@@ -52,6 +52,12 @@ struct addremove_status_baton {
/* Status info for unversioned paths. */
apr_hash_t *unversioned;
+
+ /* Status info for added paths. */
+ apr_hash_t *added;
+
+ /* Status info for deleted paths. */
+ apr_hash_t *deleted;
};
/* Implements svn_wc_status_func4_t. */
@@ -61,41 +67,45 @@ addremove_status_func(void *baton, const
apr_pool_t *scratch_pool)
{
struct addremove_status_baton *b = baton;
+ apr_hash_t *hash = NULL;
switch (status->node_status)
{
case svn_wc_status_unversioned:
- {
- apr_hash_t *hash = b->unversioned;
- apr_pool_t *result_pool = apr_hash_pool_get(hash);
-
- svn_hash_sets(hash, apr_pstrdup(result_pool, local_abspath),
- svn_wc_dup_status3(status, result_pool));
- break;
- }
+ hash = b->unversioned;
+ break;
case svn_wc_status_missing:
- {
+ hash = b->missing;
+ break;
- apr_hash_t *hash = b->missing;
- apr_pool_t *result_pool = apr_hash_pool_get(hash);
+ case svn_wc_status_added:
+ hash = b->added;
+ break;
- svn_hash_sets(hash, apr_pstrdup(result_pool, local_abspath),
- svn_wc_dup_status3(status, result_pool));
- break;
- }
+ case svn_wc_status_deleted:
+ hash = b->deleted;
+ break;
default:
break;
}
+ if (hash)
+ {
+ apr_pool_t *result_pool = apr_hash_pool_get(hash);
+
+ svn_hash_sets(hash, apr_pstrdup(result_pool, local_abspath),
+ svn_wc_dup_status3(status, result_pool));
+ }
+
return SVN_NO_ERROR;
}
static svn_error_t *
suggest_moves(apr_hash_t **moves,
- apr_hash_t *unversioned,
- apr_hash_t *missing,
+ apr_hash_t *deleted,
+ apr_hash_t *added,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -106,10 +116,10 @@ suggest_moves(apr_hash_t **moves,
*moves = apr_hash_make(result_pool);
iterpool = svn_pool_create(scratch_pool);
- for (hi = apr_hash_first(scratch_pool, unversioned); hi;
+ for (hi = apr_hash_first(scratch_pool, added); hi;
hi = apr_hash_next(hi))
{
- const char *unversioned_abspath = apr_hash_this_key(hi);
+ const char *added_abspath = apr_hash_this_key(hi);
const svn_wc_status3_t *status = apr_hash_this_val(hi);
apr_array_header_t *similar_abspaths;
int i;
@@ -120,7 +130,7 @@ suggest_moves(apr_hash_t **moves,
svn_pool_clear(iterpool);
SVN_ERR(svn_wc__find_similar_files(&similar_abspaths, ctx->wc_ctx,
- unversioned_abspath,
+ added_abspath,
ctx->cancel_func, ctx->cancel_baton,
result_pool, iterpool));
@@ -129,13 +139,13 @@ suggest_moves(apr_hash_t **moves,
const char *similar_abspath = APR_ARRAY_IDX(similar_abspaths, i,
const char *);
- if (svn_hash_gets(missing, similar_abspath) == NULL)
+ if (svn_hash_gets(deleted, similar_abspath) == NULL)
continue; /* ### TODO treat as a copy? */
/* ### TODO deal with ambiguous moves */
svn_hash_sets(*moves,
similar_abspath,
- apr_pstrdup(result_pool, unversioned_abspath));
+ apr_pstrdup(result_pool, added_abspath));
}
}
@@ -151,7 +161,6 @@ addremove(const char *local_abspath, svn
{
svn_magic__cookie_t *magic_cookie;
struct addremove_status_baton b;
- apr_hash_t *moves;
apr_hash_index_t *hi;
apr_pool_t *iterpool;
@@ -159,6 +168,8 @@ addremove(const char *local_abspath, svn
b.missing = apr_hash_make(scratch_pool);
b.unversioned = apr_hash_make(scratch_pool);
+ b.added = NULL;
+ b.deleted = NULL;
SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, local_abspath, depth,
TRUE, FALSE, FALSE, NULL,
@@ -166,12 +177,7 @@ addremove(const char *local_abspath, svn
ctx->cancel_func, ctx->cancel_baton,
scratch_pool));
-
- SVN_ERR(suggest_moves(&moves, b.unversioned, b.missing,
- ctx, scratch_pool, scratch_pool));
-
iterpool = svn_pool_create(scratch_pool);
-
for (hi = apr_hash_first(scratch_pool, b.unversioned); hi;
hi = apr_hash_next(hi))
{
@@ -210,24 +216,6 @@ addremove(const char *local_abspath, svn
}
}
- for (hi = apr_hash_first(scratch_pool, moves); hi;
- hi = apr_hash_next(hi))
- {
- const char *src_abspath = apr_hash_this_key(hi);
- const char *dst_abspath = apr_hash_this_val(hi);
-
- svn_pool_clear(iterpool);
-
- SVN_ERR(svn_wc__move2(ctx->wc_ctx, src_abspath, dst_abspath,
- TRUE, /* metadata_only */
- FALSE, /* allow_mixed_revisions */
- ctx->cancel_func, ctx->cancel_baton,
- ctx->notify_func2, ctx->notify_baton2,
- iterpool));
-
- svn_hash_sets(b.missing, src_abspath, NULL);
- }
-
for (hi = apr_hash_first(scratch_pool, b.missing); hi;
hi = apr_hash_next(hi))
{
@@ -242,7 +230,6 @@ addremove(const char *local_abspath, svn
ctx->notify_func2, ctx->notify_baton2,
iterpool));
}
-
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
@@ -266,3 +253,65 @@ svn_client_addremove(const char *local_p
return SVN_NO_ERROR;
}
+
+static svn_error_t *
+match_up_local_deletes_and_adds(const char *local_abspath,
+ svn_depth_t depth,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ struct addremove_status_baton b;
+ apr_hash_t *moves;
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+
+ b.missing = NULL;
+ b.unversioned = NULL;
+ b.added = apr_hash_make(scratch_pool);
+ b.deleted = apr_hash_make(scratch_pool);
+
+ SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, local_abspath, depth,
+ TRUE, FALSE, FALSE, NULL,
+ addremove_status_func, &b,
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool));
+
+ SVN_ERR(suggest_moves(&moves, b.deleted, b.added,
+ ctx, scratch_pool, scratch_pool));
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (hi = apr_hash_first(scratch_pool, moves); hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *src_abspath = apr_hash_this_key(hi);
+ const char *dst_abspath = apr_hash_this_val(hi);
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_wc__move_fixup(ctx->wc_ctx, src_abspath, dst_abspath,
+ ctx->cancel_func, ctx->cancel_baton,
+ ctx->notify_func2, ctx->notify_baton2,
+ iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_match_up_local_deletes_and_adds(const char *local_path,
+ svn_depth_t depth,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_abspath;
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, local_path, scratch_pool));
+
+ SVN_WC__CALL_WITH_WRITE_LOCK(
+ match_up_local_deletes_and_adds(local_abspath, depth, ctx, scratch_pool),
+ ctx->wc_ctx, local_abspath, TRUE, scratch_pool);
+
+ return SVN_NO_ERROR;
+}
+
Modified: subversion/branches/addremove/subversion/libsvn_wc/copy.c
URL:
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_wc/copy.c?rev=1805813&r1=1805812&r2=1805813&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_wc/copy.c (original)
+++ subversion/branches/addremove/subversion/libsvn_wc/copy.c Tue Aug 22
19:45:43 2017
@@ -1157,3 +1157,79 @@ svn_wc__move2(svn_wc_context_t *wc_ctx,
return SVN_NO_ERROR;
}
+
+svn_error_t *
+svn_wc__move_fixup(svn_wc_context_t *wc_ctx,
+ const char *src_abspath,
+ const char *dst_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc__db_t *db = wc_ctx->db;
+ svn_node_kind_t src_kind;
+ svn_node_kind_t dst_kind;
+ svn_boolean_t conflicted;
+ svn_wc__db_status_t src_status;
+ svn_wc__db_status_t dst_status;
+
+ /* Verify that we have the required write locks. */
+ SVN_ERR(svn_wc__write_check(wc_ctx->db,
+ svn_dirent_dirname(src_abspath, scratch_pool),
+ scratch_pool));
+ SVN_ERR(svn_wc__write_check(wc_ctx->db,
+ svn_dirent_dirname(dst_abspath, scratch_pool),
+ scratch_pool));
+
+ /* Validate source and destination. */
+ SVN_ERR(svn_wc__db_read_info(&src_status, &src_kind, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ &conflicted, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ db, src_abspath,
+ scratch_pool, scratch_pool));
+ if (src_status != svn_wc__db_status_deleted)
+ return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Source '%s' is not a deleted path"),
+ svn_dirent_local_style(src_abspath,
+ scratch_pool));
+ if (conflicted)
+ return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
+ _("Source '%s' is in conflicted status"),
+ svn_dirent_local_style(src_abspath,
+ scratch_pool));
+ SVN_ERR(svn_wc__db_read_info(&dst_status, &dst_kind, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ &conflicted, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ db, dst_abspath,
+ scratch_pool, scratch_pool));
+ if (dst_status != svn_wc__db_status_added)
+ return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Destination '%s' is not an added path"),
+ svn_dirent_local_style(dst_abspath,
+ scratch_pool));
+ if (conflicted)
+ return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
+ _("Destination '%s' is in conflicted status"),
+ svn_dirent_local_style(dst_abspath,
+ scratch_pool));
+ if (src_kind != dst_kind)
+ return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Source '%s' and destination '%s' do not "
+ "have the same node kind"),
+ svn_dirent_local_style(src_abspath,
+ scratch_pool),
+ svn_dirent_local_style(dst_abspath,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__db_move_fixup(wc_ctx->db, src_abspath, dst_abspath,
+ cancel_func, cancel_baton,
+ notify_func, notify_baton,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
Modified: subversion/branches/addremove/subversion/libsvn_wc/wc-queries.sql
URL:
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_wc/wc-queries.sql?rev=1805813&r1=1805812&r2=1805813&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/addremove/subversion/libsvn_wc/wc-queries.sql Tue Aug
22 19:45:43 2017
@@ -1615,6 +1615,12 @@ WHERE wc_id = ?1 AND local_relpath = ?2
UPDATE nodes SET moved_to = NULL
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
+-- STMT_SET_MOVED_HERE_RECURSIVE
+UPDATE nodes SET moved_here = 1
+WHERE wc_id = ?1
+ AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
+ AND op_depth = ?3
+
-- STMT_CLEAR_MOVED_HERE_RECURSIVE
UPDATE nodes SET moved_here = NULL
WHERE wc_id = ?1
Modified: subversion/branches/addremove/subversion/libsvn_wc/wc_db.c
URL:
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_wc/wc_db.c?rev=1805813&r1=1805812&r2=1805813&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/addremove/subversion/libsvn_wc/wc_db.c Tue Aug 22
19:45:43 2017
@@ -4329,6 +4329,58 @@ svn_wc__db_scan_deletion(const char **ba
return SVN_NO_ERROR;
}
+/* Similar to svn_wc__internal_get_origin() but for deletion op-roots only. */
+static svn_error_t *
+get_origin_of_delete_op_root(const char **repos_relpath,
+ svn_revnum_t *revision,
+ apr_int64_t *repos_id,
+ const char *local_relpath,
+ svn_wc__db_wcroot_t *wcroot,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *base_del_relpath, *work_del_relpath;
+
+ SVN_ERR(scan_deletion(&base_del_relpath, NULL,
+ &work_del_relpath,
+ NULL, wcroot, local_relpath,
+ scratch_pool, scratch_pool));
+ if (work_del_relpath)
+ {
+ const char *op_root_relpath;
+ const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
+ scratch_pool);
+
+ /* Similar to, but not the same as, the _scan_addition and
+ _join in get_info_for_copy(). Can we use get_copyfrom here? */
+ SVN_ERR(scan_addition(NULL, &op_root_relpath,
+ NULL, NULL, /* repos_* */
+ repos_relpath, repos_id, revision,
+ NULL, NULL, NULL,
+ wcroot, parent_del_relpath,
+ scratch_pool, scratch_pool));
+ *repos_relpath
+ = svn_relpath_join(*repos_relpath,
+ svn_relpath_skip_ancestor(op_root_relpath,
+ local_relpath),
+ result_pool);
+ }
+ else if (base_del_relpath)
+ {
+ SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision,
+ repos_relpath,
+ repos_id, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ wcroot, local_relpath,
+ result_pool,
+ scratch_pool));
+ }
+ else
+ SVN_ERR_MALFUNCTION();
+
+ return SVN_NO_ERROR;
+}
/* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK,
*OP_ROOT
@@ -4389,45 +4441,10 @@ get_info_for_copy(apr_int64_t *copyfrom_
}
else if (node_status == svn_wc__db_status_deleted && is_op_root)
{
- const char *base_del_relpath, *work_del_relpath;
-
- SVN_ERR(scan_deletion(&base_del_relpath, NULL,
- &work_del_relpath,
- NULL, src_wcroot, local_relpath,
- scratch_pool, scratch_pool));
- if (work_del_relpath)
- {
- const char *op_root_relpath;
- const char *parent_del_relpath =
svn_relpath_dirname(work_del_relpath,
- scratch_pool);
-
- /* Similar to, but not the same as, the _scan_addition and
- _join above. Can we use get_copyfrom here? */
- SVN_ERR(scan_addition(NULL, &op_root_relpath,
- NULL, NULL, /* repos_* */
- copyfrom_relpath, copyfrom_id, copyfrom_rev,
- NULL, NULL, NULL,
- src_wcroot, parent_del_relpath,
- scratch_pool, scratch_pool));
- *copyfrom_relpath
- = svn_relpath_join(*copyfrom_relpath,
- svn_relpath_skip_ancestor(op_root_relpath,
- local_relpath),
- result_pool);
- }
- else if (base_del_relpath)
- {
- SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
- copyfrom_relpath,
- copyfrom_id, NULL, NULL,
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
- src_wcroot, local_relpath,
- result_pool,
- scratch_pool));
- }
- else
- SVN_ERR_MALFUNCTION();
+ SVN_ERR(get_origin_of_delete_op_root(copyfrom_relpath, copyfrom_rev,
+ copyfrom_id, local_relpath,
+ src_wcroot, result_pool,
+ scratch_pool));
}
else if (node_status == svn_wc__db_status_deleted)
{
@@ -16713,3 +16730,194 @@ svn_wc__find_repos_node_in_wc(apr_array_
return svn_error_trace(svn_sqlite__reset(stmt));
}
+static svn_error_t *
+set_moved_here_recursive(svn_wc__db_wcroot_t *wcroot,
+ const char *local_relpath,
+ apr_pool_t *scratch_pool)
+{
+ svn_sqlite__stmt_t *stmt;
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+ STMT_SET_MOVED_HERE_RECURSIVE));
+ SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+ relpath_depth(local_relpath)));
+ SVN_ERR(svn_sqlite__update(NULL, stmt));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+update_origin(svn_wc__db_wcroot_t *wcroot,
+ const char *local_relpath,
+ const char *new_repos_relpath,
+ svn_revnum_t new_revision,
+ apr_int64_t repos_id,
+ apr_pool_t *scratch_pool)
+{
+ svn_sqlite__stmt_t *stmt;
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+ STMT_COMMIT_UPDATE_ORIGIN));
+ SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
+ local_relpath,
+ relpath_depth(local_relpath),
+ repos_id,
+ new_repos_relpath,
+ new_revision));
+ SVN_ERR(svn_sqlite__update(NULL, stmt));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+move_fixup(svn_wc__db_wcroot_t *wcroot,
+ const char *src_relpath,
+ const char *dst_relpath,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc__db_status_t src_status;
+ svn_wc__db_status_t dst_status;
+ svn_node_kind_t src_kind;
+ svn_node_kind_t dst_kind;
+ const char *src_repos_relpath;
+ const char *dst_repos_relpath;
+ svn_revnum_t src_revision;
+ apr_int64_t src_repos_id;
+ apr_int64_t dst_repos_id;
+ svn_boolean_t src_is_op_root;
+ svn_boolean_t dst_is_op_root;
+ const char *moved_to_relpath;
+ const char *moved_from_relpath;
+
+ SVN_ERR(read_info(&src_status, &src_kind, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, &src_is_op_root,
+ NULL, NULL, NULL, NULL, NULL,
+ wcroot, src_relpath, scratch_pool, scratch_pool));
+ SVN_ERR(read_info(&dst_status, &dst_kind, NULL, &dst_repos_relpath,
+ &dst_repos_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dst_is_op_root,
+ NULL, NULL, NULL, NULL, NULL,
+ wcroot, dst_relpath, scratch_pool, scratch_pool));
+
+ /* Destination must be the op-root of a deletion. */
+ if (src_status != svn_wc__db_status_deleted || !src_is_op_root)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not the root of a deletion"),
+ path_for_error_message(wcroot, src_relpath,
+ scratch_pool));
+ /* Source must not already be part of a move. */
+ SVN_ERR(scan_deletion(NULL, &moved_to_relpath, NULL, NULL,
+ wcroot, src_relpath, scratch_pool, scratch_pool));
+ if (moved_to_relpath != NULL)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is already moved to '%s'"),
+ path_for_error_message(wcroot, src_relpath,
+ scratch_pool),
+ path_for_error_message(wcroot, moved_to_relpath,
+ scratch_pool));
+
+ SVN_ERR(get_origin_of_delete_op_root(&src_repos_relpath, &src_revision,
+ &src_repos_id, src_relpath, wcroot,
+ scratch_pool, scratch_pool));
+
+ /* Destination must be the op-root of an addition. */
+ if (dst_status != svn_wc__db_status_added || !dst_is_op_root)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not the root of an addition"),
+ path_for_error_message(wcroot, dst_relpath,
+ scratch_pool));
+
+ /* Destination must not already be part of a move. */
+ SVN_ERR(scan_addition(NULL, NULL, NULL, &dst_repos_id, NULL, NULL, NULL,
+ &moved_from_relpath, NULL, NULL,
+ wcroot, dst_relpath, scratch_pool, scratch_pool));
+ if (moved_from_relpath != NULL)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' was already moved here from '%s'"),
+ path_for_error_message(wcroot, dst_relpath,
+ scratch_pool),
+ path_for_error_message(wcroot, moved_from_relpath,
+ scratch_pool));
+
+ /* Source and destination must be from the same repository. */
+ if (src_repos_id != dst_repos_id)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Source '%s' and destination '%s' are not "
+ "from the same repository"),
+ path_for_error_message(wcroot, src_relpath,
+ scratch_pool),
+ path_for_error_message(wcroot, dst_relpath,
+ scratch_pool));
+
+ /* Set the moved-here flag on the destination and children, if any. */
+ SVN_ERR(set_moved_here_recursive(wcroot, dst_relpath, scratch_pool));
+
+ /* Rewrite copyfrom on the destination. The repository location of the
+ * source will become the destination's new copyfrom url@revision. */
+ SVN_ERR(update_origin(wcroot, dst_relpath, src_repos_relpath, src_revision,
+ src_repos_id, scratch_pool));
+ if (dst_kind == svn_node_dir)
+ {
+ apr_hash_t *moves = NULL;
+
+ /* Rewrite copyfrom on children as well, if any.
+ * ### Re-uses code which was originally written for commit processing.
*/
+ SVN_ERR(moved_descendant_collect(&moves, wcroot, dst_relpath,
+ relpath_depth(dst_relpath),
+ scratch_pool, scratch_pool));
+ SVN_ERR(moved_descendant_commit(wcroot, dst_relpath, src_repos_id,
+ src_repos_relpath, src_revision,
+ moves, scratch_pool));
+ }
+
+ /* Rewrite the moved-to path on the source. */
+ SVN_ERR(delete_update_movedto(wcroot,
+ src_relpath, relpath_depth(src_relpath),
+ dst_relpath, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_move_fixup(svn_wc__db_t *db,
+ const char *src_abspath,
+ const char *dst_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc__db_wcroot_t *src_wcroot;
+ svn_wc__db_wcroot_t *dst_wcroot;
+ const char *src_relpath;
+ const char *dst_relpath;
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
+
+ SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&src_wcroot, &src_relpath, db,
+ src_abspath, scratch_pool,
+ scratch_pool));
+ VERIFY_USABLE_WCROOT(src_wcroot);
+ SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&dst_wcroot, &dst_relpath, db,
+ dst_abspath, scratch_pool,
+ scratch_pool));
+ VERIFY_USABLE_WCROOT(dst_wcroot);
+
+ if (strcmp(src_wcroot->abspath, dst_wcroot->abspath) != 0)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Source '%s' and destination '%s' are not "
+ "in the same working copy"),
+ path_for_error_message(src_wcroot, src_relpath,
+ scratch_pool),
+ path_for_error_message(dst_wcroot, dst_relpath,
+ scratch_pool));
+
+ SVN_WC__DB_WITH_TXN(
+ move_fixup(src_wcroot, src_relpath, dst_relpath, scratch_pool),
+ src_wcroot);
+
+ return SVN_NO_ERROR;
+}
Modified: subversion/branches/addremove/subversion/libsvn_wc/wc_db.h
URL:
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_wc/wc_db.h?rev=1805813&r1=1805812&r2=1805813&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/addremove/subversion/libsvn_wc/wc_db.h Tue Aug 22
19:45:43 2017
@@ -3537,6 +3537,16 @@ svn_wc__db_verify_db_full(svn_wc__db_t *
void *baton,
apr_pool_t *scratch_pool);
+/* Internal implementation of svn_wc__move_fixup(). */
+svn_error_t *
+svn_wc__db_move_fixup(svn_wc__db_t *db,
+ const char *src_abspath,
+ const char *dst_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
Modified: subversion/branches/addremove/subversion/svn/addremove-cmd.c
URL:
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/svn/addremove-cmd.c?rev=1805813&r1=1805812&r2=1805813&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/svn/addremove-cmd.c (original)
+++ subversion/branches/addremove/subversion/svn/addremove-cmd.c Tue Aug 22
19:45:43 2017
@@ -39,6 +39,29 @@
/*** Code. ***/
+static svn_error_t *
+wrap_illegal_target_error(apr_array_header_t *errors)
+{
+ svn_error_t *err;
+ int i;
+
+ err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, NULL);
+ for (i = 0; i < errors->nelts; i++)
+ {
+ apr_status_t status = APR_ARRAY_IDX(errors, i, apr_status_t);
+ if (status == SVN_ERR_WC_PATH_NOT_FOUND)
+ err = svn_error_quick_wrap(err,
+ _("Could not add all targets because "
+ "some targets don't exist"));
+ else if (status == SVN_ERR_ENTRY_EXISTS)
+ err = svn_error_quick_wrap(err,
+ _("Could not add all targets because "
+ "some targets are already versioned"));
+ }
+
+ return err;
+}
+
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__addremove(apr_getopt_t *os,
@@ -82,27 +105,33 @@ svn_cl__addremove(apr_getopt_t *os,
0));
}
- svn_pool_destroy(iterpool);
-
if (errors->nelts > 0)
{
- svn_error_t *err;
+ svn_error_t *err = wrap_illegal_target_error(errors);
+ svn_handle_warning2(stderr, err, "svn: ");
+ }
+
+ /* Now ask for magic to be performed which will detect moves. */
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
- err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, NULL);
- for (i = 0; i < errors->nelts; i++)
- {
- apr_status_t status = APR_ARRAY_IDX(errors, i, apr_status_t);
- if (status == SVN_ERR_WC_PATH_NOT_FOUND)
- err = svn_error_quick_wrap(err,
- _("Could not add all targets because "
- "some targets don't exist"));
- else if (status == SVN_ERR_ENTRY_EXISTS)
- err = svn_error_quick_wrap(err,
- _("Could not add all targets because "
- "some targets are already
versioned"));
- }
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+ SVN_ERR(svn_cl__try(svn_client_match_up_local_deletes_and_adds(
+ target, opt_state->depth,
+ ctx, iterpool),
+ errors, opt_state->quiet,
+ SVN_ERR_ENTRY_EXISTS,
+ SVN_ERR_WC_PATH_NOT_FOUND,
+ 0));
+ }
+ svn_pool_destroy(iterpool);
- return svn_error_trace(err);
+ if (errors->nelts > 0)
+ {
+ svn_error_t *err = wrap_illegal_target_error(errors);
+ svn_handle_warning2(stderr, err, "svn: ");
}
return SVN_NO_ERROR;