You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2017/07/31 22:04:30 UTC
svn commit: r1803585 - in /subversion/branches/addremove/subversion:
include/private/svn_wc_private.h libsvn_client/addremove.c
libsvn_wc/questions.c libsvn_wc/wc-queries.sql libsvn_wc/wc_db.c
libsvn_wc/wc_db.h
Author: stsp
Date: Mon Jul 31 22:04:29 2017
New Revision: 1803585
URL: http://svn.apache.org/viewvc?rev=1803585&view=rev
Log:
On the addremove branch, start finding similar files to detect unversioned
mov operations.
This initial proof-of-concept implementation relies on the SHA1 checksum
recorded in the pristine store and requires a 100% match between an
unversioned file and a missing BASE file.
It does not yet handle ambiguous moves of unversioned files.
A possible future expansion would be the detection of copies, where an
unversioned files matches some other versioned file in the working copy.
* subversion/include/private/svn_wc_private.h
(svn_wc__find_similar_files): Declare.
* subversion/libsvn_client/addremove.c
(suggest_moves): New helper function driving svn_wc__find_similar_files().
(addremove): If a move was suggested, record it in WC meta data.
* subversion/libsvn_wc/questions.c
(svn_wc__find_similar_files): New.
* subversion/libsvn_wc/wc-queries.sql
(STMT_SELECT_BASE_NODES_BY_CHECKSUM): New query which finds all base nodes
that have a particular checksum.
* subversion/libsvn_wc/wc_db.c
(base_info_from_stmt): New helper function, factored out of ...
(base_get_children_info): ... this function.
(base_get_nodes_by_checksum): New function which runs above new query.
(svn_wc__db_base_get_nodes_by_checksum): New function which finds all base
nodes that have a particular checksum.
* subversion/libsvn_wc/wc_db.h
(svn_wc__db_base_get_nodes_by_checksum): Declare.
Modified:
subversion/branches/addremove/subversion/include/private/svn_wc_private.h
subversion/branches/addremove/subversion/libsvn_client/addremove.c
subversion/branches/addremove/subversion/libsvn_wc/questions.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
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=1803585&r1=1803584&r2=1803585&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/include/private/svn_wc_private.h (original)
+++ subversion/branches/addremove/subversion/include/private/svn_wc_private.h Mon Jul 31 22:04:29 2017
@@ -2062,6 +2062,19 @@ svn_wc__read_conflict_descriptions2_t(co
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/**
+ * Return a list of absolute paths of files which are similar to the file
+ * at LOCAL_ABSPATH, which may be an unversioned file.
+ */
+svn_error_t *
+svn_wc__find_similar_files(apr_array_header_t **similar_abspaths,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
Modified: subversion/branches/addremove/subversion/libsvn_client/addremove.c
URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/addremove.c?rev=1803585&r1=1803584&r2=1803585&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_client/addremove.c (original)
+++ subversion/branches/addremove/subversion/libsvn_client/addremove.c Mon Jul 31 22:04:29 2017
@@ -93,12 +93,65 @@ addremove_status_func(void *baton, const
}
static svn_error_t *
+suggest_moves(apr_hash_t **moves,
+ apr_hash_t *unversioned,
+ apr_hash_t *missing,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+
+ *moves = apr_hash_make(result_pool);
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (hi = apr_hash_first(scratch_pool, unversioned); hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *unversioned_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;
+
+ if (status->actual_kind != svn_node_file)
+ continue;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_wc__find_similar_files(&similar_abspaths, ctx->wc_ctx,
+ unversioned_abspath,
+ ctx->cancel_func, ctx->cancel_baton,
+ result_pool, iterpool));
+
+ for (i = 0; i < similar_abspaths->nelts; i++)
+ {
+ const char *similar_abspath = APR_ARRAY_IDX(similar_abspaths, i,
+ const char *);
+
+ if (svn_hash_gets(missing, 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));
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
addremove(const char *local_abspath, svn_depth_t depth,
svn_boolean_t no_autoprops, svn_boolean_t no_ignore,
svn_client_ctx_t *ctx, apr_pool_t *scratch_pool)
{
svn_magic__cookie_t *magic_cookie;
struct addremove_status_baton b;
+ apr_hash_t *moves;
apr_hash_index_t *hi;
apr_pool_t *iterpool;
@@ -113,7 +166,12 @@ 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))
{
@@ -152,6 +210,24 @@ 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))
{
Modified: subversion/branches/addremove/subversion/libsvn_wc/questions.c
URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_wc/questions.c?rev=1803585&r1=1803584&r2=1803585&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_wc/questions.c (original)
+++ subversion/branches/addremove/subversion/libsvn_wc/questions.c Mon Jul 31 22:04:29 2017
@@ -38,6 +38,7 @@
#include "svn_time.h"
#include "svn_io.h"
#include "svn_props.h"
+#include "svn_hash.h"
#include "wc.h"
#include "conflicts.h"
@@ -744,3 +745,30 @@ svn_wc__has_local_mods(svn_boolean_t *is
*is_modified = modified;
return SVN_NO_ERROR;
}
+
+svn_error_t *
+svn_wc__find_similar_files(apr_array_header_t **similar_abspaths,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_checksum_t *checksum;
+ apr_hash_t *nodes;
+
+ *similar_abspaths = apr_array_make(result_pool, 0, sizeof(const char *));
+
+ /* Identify files with the same SHA1 checksum in the pristine store. */
+ /* ### Detranslate the file? */
+ SVN_ERR(svn_io_file_checksum2(&checksum, local_abspath, svn_checksum_sha1,
+ scratch_pool));
+ SVN_ERR(svn_wc__db_base_get_nodes_by_checksum(&nodes, wc_ctx->db,
+ checksum, local_abspath,
+ result_pool, scratch_pool));
+ /* ### discards additional base info returned by wc_db code */
+ SVN_ERR(svn_hash_keys(similar_abspaths, nodes, result_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=1803585&r1=1803584&r2=1803585&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/addremove/subversion/libsvn_wc/wc-queries.sql Mon Jul 31 22:04:29 2017
@@ -85,6 +85,11 @@ LEFT OUTER JOIN lock ON nodes.repos_id =
AND nodes.repos_path = lock.repos_relpath
WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0
+-- STMT_SELECT_BASE_NODES_BY_CHECKSUM
+SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind,
+ revision, depth, file_external
+FROM nodes
+WHERE wc_id = ?1 AND checksum = ?2 AND op_depth = 0
-- STMT_SELECT_WORKING_NODE
SELECT op_depth, presence, kind, checksum, translated_size,
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=1803585&r1=1803584&r2=1803585&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/addremove/subversion/libsvn_wc/wc_db.c Mon Jul 31 22:04:29 2017
@@ -2665,6 +2665,60 @@ svn_wc__db_base_get_info(svn_wc__db_stat
return SVN_NO_ERROR;
}
+
+static svn_error_t *
+base_info_from_stmt(struct svn_wc__db_base_info_t **infop,
+ svn_boolean_t obtain_locks,
+ svn_sqlite__stmt_t *stmt,
+ svn_wc__db_wcroot_t *wcroot,
+ apr_pool_t *result_pool)
+{
+ apr_int64_t repos_id;
+ const char *last_repos_root_url = NULL;
+ apr_int64_t last_repos_id = INVALID_REPOS_ID;
+ struct svn_wc__db_base_info_t *info;
+
+ *infop = NULL;
+ info = apr_pcalloc(result_pool, sizeof(*info));
+
+ repos_id = svn_sqlite__column_int64(stmt, 1);
+ info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
+ info->status = svn_sqlite__column_token(stmt, 3, presence_map);
+ info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
+ info->revnum = svn_sqlite__column_revnum(stmt, 5);
+
+ info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
+ svn_depth_unknown);
+
+ info->update_root = svn_sqlite__column_boolean(stmt, 7);
+
+ if (obtain_locks)
+ info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
+
+ if (repos_id != last_repos_id)
+ {
+ svn_error_t *err;
+
+ err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL,
+ wcroot, repos_id,
+ result_pool);
+
+ if (err)
+ return svn_error_trace(
+ svn_error_compose_create(err,
+ svn_sqlite__reset(stmt)));
+
+ last_repos_id = repos_id;
+ }
+
+ info->repos_root_url = last_repos_root_url;
+
+ *infop = info;
+
+ return SVN_NO_ERROR;
+}
+
+
/* The implementation of svn_wc__db_base_get_children_info */
static svn_error_t *
base_get_children_info(apr_hash_t **nodes,
@@ -2676,8 +2730,6 @@ base_get_children_info(apr_hash_t **node
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
- apr_int64_t last_repos_id = INVALID_REPOS_ID;
- const char *last_repos_root_url = NULL;
*nodes = apr_hash_make(result_pool);
@@ -2692,44 +2744,11 @@ base_get_children_info(apr_hash_t **node
while (have_row)
{
struct svn_wc__db_base_info_t *info;
- apr_int64_t repos_id;
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
const char *name = svn_relpath_basename(child_relpath, result_pool);
- info = apr_pcalloc(result_pool, sizeof(*info));
-
- repos_id = svn_sqlite__column_int64(stmt, 1);
- info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
- info->status = svn_sqlite__column_token(stmt, 3, presence_map);
- info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
- info->revnum = svn_sqlite__column_revnum(stmt, 5);
-
- info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
- svn_depth_unknown);
-
- info->update_root = svn_sqlite__column_boolean(stmt, 7);
-
- if (obtain_locks)
- info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
-
- if (repos_id != last_repos_id)
- {
- svn_error_t *err;
-
- err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL,
- wcroot, repos_id,
- result_pool);
-
- if (err)
- return svn_error_trace(
- svn_error_compose_create(err,
- svn_sqlite__reset(stmt)));
-
- last_repos_id = repos_id;
- }
-
- info->repos_root_url = last_repos_root_url;
-
+ SVN_ERR(base_info_from_stmt(&info, obtain_locks, stmt, wcroot,
+ result_pool));
svn_hash_sets(*nodes, name, info);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
@@ -2740,6 +2759,7 @@ base_get_children_info(apr_hash_t **node
return SVN_NO_ERROR;
}
+
svn_error_t *
svn_wc__db_base_get_children_info(apr_hash_t **nodes,
svn_wc__db_t *db,
@@ -2765,6 +2785,75 @@ svn_wc__db_base_get_children_info(apr_ha
}
+static svn_error_t *
+base_get_nodes_by_checksum(apr_hash_t **nodes,
+ svn_wc__db_wcroot_t *wcroot,
+ svn_checksum_t *checksum,
+ svn_boolean_t obtain_locks,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_sqlite__stmt_t *stmt;
+ svn_boolean_t have_row;
+
+ *nodes = apr_hash_make(result_pool);
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+ STMT_SELECT_BASE_NODES_BY_CHECKSUM));
+ SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
+ SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, checksum, scratch_pool));
+
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+ while (have_row)
+ {
+ struct svn_wc__db_base_info_t *info;
+ const char *local_relpath;
+ const char *local_abspath;
+
+ local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+ local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
+ result_pool);
+ SVN_ERR(base_info_from_stmt(&info, obtain_locks, stmt, wcroot,
+ result_pool));
+ svn_hash_sets(*nodes, local_abspath, info);
+
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+ }
+
+ SVN_ERR(svn_sqlite__reset(stmt));
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__db_base_get_nodes_by_checksum(apr_hash_t **nodes,
+ svn_wc__db_t *db,
+ svn_checksum_t *checksum,
+ const char *wri_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc__db_wcroot_t *wcroot;
+ const char *local_relpath;
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
+
+ SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+ wri_abspath, scratch_pool,
+ scratch_pool));
+ VERIFY_USABLE_WCROOT(wcroot);
+
+ return svn_error_trace(base_get_nodes_by_checksum(nodes,
+ wcroot,
+ checksum,
+ TRUE /* obtain_locks */,
+ result_pool,
+ scratch_pool));
+}
+
+
svn_error_t *
svn_wc__db_base_get_props(apr_hash_t **props,
svn_wc__db_t *db,
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=1803585&r1=1803584&r2=1803585&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/addremove/subversion/libsvn_wc/wc_db.h Mon Jul 31 22:04:29 2017
@@ -814,6 +814,15 @@ svn_wc__db_base_get_children_info(apr_ha
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/* Return in *NODES a hash mapping local_abspath->struct svn_wc__db_base_info_t
+ * for all files at op_depth 0 with the specified CHECKSUM. */
+svn_error_t *
+svn_wc__db_base_get_nodes_by_checksum(apr_hash_t **nodes,
+ svn_wc__db_t *db,
+ svn_checksum_t *checksum,
+ const char *wri_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* Set *PROPS to the properties of the node LOCAL_ABSPATH in the BASE tree.