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.