Author: breser
Date: Tue Apr 29 23:49:19 2014
New Revision: 1591150

URL: http://svn.apache.org/r1591150
Log:
Merge the 1.8.x-r1544597 branch:

 * r1544597, r1544600, r1544688, r1544690, r1545111, r1545122
   Fix a specific case where the api behind status would report a different
   move status for A depending on whether the user called 'svn status A' or
   'svn status wc' and then looked at 'wc/A'.
   Justification:
     Api users such as AnkhSVN rely on the result of svn status to match and
     this breaks their assumptions.
     This change has the nice side effect that it resolves the most common
     user triggerable assertion in AnkhSVN, which is really caused by a
     broken move. (See other nomination for a fix). But even then it shouldn't
     matter how status is called: on the node or an ancestor.
     (It also improves status performance a tiny bit, by reducing the number
      of sqlite transactions used)
   Notes:
     r1564587 fixes a function reference on the branch in some code that
     is disabled by the macro processor on Windows.
   Branch:
     ^/subversion/branches/1.8.x-r1544597
   Votes:
     +1: philip, rhuijben, breser

Modified:
    subversion/branches/1.8.x/   (props changed)
    subversion/branches/1.8.x/STATUS
    subversion/branches/1.8.x/subversion/libsvn_wc/status.c
    subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql
    subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c
    subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h
    subversion/branches/1.8.x/subversion/tests/cmdline/stat_tests.py

Propchange: subversion/branches/1.8.x/
------------------------------------------------------------------------------
  Merged /subversion/trunk:r1544597,1544600,1544688,1544690,1545111,1545122
  Merged /subversion/branches/1.8.x-r1544597:r1564547-1591149

Modified: subversion/branches/1.8.x/STATUS
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x/STATUS?rev=1591150&r1=1591149&r2=1591150&view=diff
==============================================================================
--- subversion/branches/1.8.x/STATUS (original)
+++ subversion/branches/1.8.x/STATUS Tue Apr 29 23:49:19 2014
@@ -181,23 +181,3 @@ Veto-blocked changes:
 Approved changes:
 =================
 
- * r1544597, r1544600, r1544688, r1544690, r1545111, r1545122
-   Fix a specific case where the api behind status would report a different
-   move status for A depending on whether the user called 'svn status A' or
-   'svn status wc' and then looked at 'wc/A'.
-   Justification:
-     Api users such as AnkhSVN rely on the result of svn status to match and
-     this breaks their assumptions.
-     This change has the nice side effect that it resolves the most common
-     user triggerable assertion in AnkhSVN, which is really caused by a
-     broken move. (See other nomination for a fix). But even then it shouldn't
-     matter how status is called: on the node or an ancestor.
-     (It also improves status performance a tiny bit, by reducing the number
-      of sqlite transactions used)
-   Notes:
-     r1564587 fixes a function reference on the branch in some code that
-     is disabled by the macro processor on Windows.
-   Branch:
-     ^/subversion/branches/1.8.x-r1544597
-   Votes:
-     +1: philip, rhuijben, breser

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/status.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/status.c?rev=1591150&r1=1591149&r2=1591150&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/status.c (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/status.c Tue Apr 29 23:49:19 
2014
@@ -242,144 +242,7 @@ struct file_baton
 
 /** Code **/
 
-/* Fill in *INFO with the information it would contain if it were
-   obtained from svn_wc__db_read_children_info. */
-static svn_error_t *
-read_info(const struct svn_wc__db_info_t **info,
-          const char *local_abspath,
-          svn_wc__db_t *db,
-          apr_pool_t *result_pool,
-          apr_pool_t *scratch_pool)
-{
-  struct svn_wc__db_info_t *mtb = apr_pcalloc(result_pool, sizeof(*mtb));
-  const svn_checksum_t *checksum;
-  const char *original_repos_relpath;
-
-  SVN_ERR(svn_wc__db_read_info(&mtb->status, &mtb->kind,
-                               &mtb->revnum, &mtb->repos_relpath,
-                               &mtb->repos_root_url, &mtb->repos_uuid,
-                               &mtb->changed_rev, &mtb->changed_date,
-                               &mtb->changed_author, &mtb->depth,
-                               &checksum, NULL, &original_repos_relpath, NULL,
-                               NULL, NULL, &mtb->lock, &mtb->recorded_size,
-                               &mtb->recorded_time, &mtb->changelist,
-                               &mtb->conflicted, &mtb->op_root,
-                               &mtb->had_props, &mtb->props_mod,
-                               &mtb->have_base, &mtb->have_more_work, NULL,
-                               db, local_abspath,
-                               result_pool, scratch_pool));
-
-  SVN_ERR(svn_wc__db_wclocked(&mtb->locked, db, local_abspath, scratch_pool));
-
-  /* Maybe we have to get some shadowed lock from BASE to make our test suite
-     happy... (It might be completely unrelated, but...) */
-  if (mtb->have_base
-      && (mtb->status == svn_wc__db_status_added
-          || mtb->status == svn_wc__db_status_deleted
-          || mtb->kind == svn_node_file))
-    {
-      svn_boolean_t update_root;
-      svn_wc__db_lock_t **lock_arg = NULL;
-
-      if (mtb->status == svn_wc__db_status_added
-          || mtb->status == svn_wc__db_status_deleted)
-        lock_arg = &mtb->lock;
-
-      SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
-                                       NULL, NULL, NULL, NULL, NULL, NULL,
-                                       lock_arg, NULL, NULL, &update_root,
-                                       db, local_abspath,
-                                       result_pool, scratch_pool));
 
-      mtb->file_external = (update_root && mtb->kind == svn_node_file);
-
-      if (mtb->status == svn_wc__db_status_deleted)
-        {
-          const char *moved_to_abspath;
-          const char *moved_to_op_root_abspath;
-
-          /* NOTE: we can't use op-root-ness as a condition here since a base
-           * node can be the root of a move and still not be an explicit
-           * op-root (having a working node with op_depth == pathelements).
-           *
-           * Both these (almost identical) situations showcase this:
-           *   svn mv a/b bb
-           *   svn del a
-           * and
-           *   svn mv a aa
-           *   svn mv aa/b bb
-           * In both, 'bb' is moved from 'a/b', but 'a/b' has no op_depth>0
-           * node at all, as its parent 'a' is locally deleted. */
-
-          SVN_ERR(svn_wc__db_scan_deletion(NULL,
-                                           &moved_to_abspath,
-                                           NULL,
-                                           &moved_to_op_root_abspath,
-                                           db, local_abspath,
-                                           scratch_pool, scratch_pool));
-          if (moved_to_abspath != NULL
-              && moved_to_op_root_abspath != NULL
-              && strcmp(moved_to_abspath, moved_to_op_root_abspath) == 0)
-            {
-              mtb->moved_to_abspath = apr_pstrdup(result_pool,
-                                                  moved_to_abspath);
-            }
-          /* ### ^^^ THIS SUCKS. For at least two reasons:
-           * 1) We scan the node deletion and that's technically not necessary.
-           *    We'd be fine to know if this is an actual root of a move.
-           * 2) From the elaborately calculated results, we backwards-guess
-           *    whether this is a root.
-           * It works ok, and this code only gets called when a node is an
-           * explicit target of a 'status'. But it would be better to do this
-           * differently.
-           * We could return moved-to via svn_wc__db_base_get_info() (called
-           * just above), but as moved-to is only intended to be returned for
-           * roots of a move, that doesn't fit too well. */
-        }
-    }
-
-  /* ### svn_wc__db_read_info() could easily return the moved-here flag. But
-   * for now... (The per-dir query for recursive status is far more optimal.)
-   * Note that this actually scans around to get the full path, for a bool.
-   * This bool then gets returned, later is evaluated, and if true leads to
-   * the same paths being scanned again. We'd want to obtain this bool here as
-   * cheaply as svn_wc__db_read_children_info() does. */
-  if (mtb->status == svn_wc__db_status_added)
-    {
-      svn_wc__db_status_t status;
-
-      SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL,
-                                       NULL, NULL, NULL, NULL,
-                                       db, local_abspath,
-                                       result_pool, scratch_pool));
-
-      mtb->moved_here = (status == svn_wc__db_status_moved_here);
-      mtb->incomplete = (status == svn_wc__db_status_incomplete);
-    }
-
-  mtb->has_checksum = (checksum != NULL);
-  mtb->copied = (original_repos_relpath != NULL);
-
-#ifdef HAVE_SYMLINK
-  if (mtb->kind == svn_node_file
-      && (mtb->had_props || mtb->props_mod))
-    {
-      apr_hash_t *properties;
-
-      if (mtb->props_mod)
-        SVN_ERR(svn_wc__db_read_props(&properties, db, local_abspath,
-                                      scratch_pool, scratch_pool));
-      else
-        SVN_ERR(svn_wc__db_read_pristine_props(&properties, db, local_abspath,
-                                               scratch_pool, scratch_pool));
-
-      mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
-    }
-#endif
-  *info = mtb;
-
-  return SVN_NO_ERROR;
-}
 
 /* Return *REPOS_RELPATH and *REPOS_ROOT_URL for LOCAL_ABSPATH using
    information in INFO if available, falling back on
@@ -522,7 +385,8 @@ assemble_status(svn_wc_status3_t **statu
 
 
   if (!info)
-    SVN_ERR(read_info(&info, local_abspath, db, result_pool, scratch_pool));
+    SVN_ERR(svn_wc__db_read_single_info(&info, db, local_abspath,
+                                        result_pool, scratch_pool));
 
   if (!info->repos_relpath || !parent_repos_relpath)
     switched_p = FALSE;
@@ -828,8 +692,11 @@ assemble_status(svn_wc_status3_t **statu
     stat->changelist = apr_pstrdup(result_pool, info->changelist);
 
   stat->moved_from_abspath = moved_from_abspath;
-  if (info->moved_to_abspath)
-    stat->moved_to_abspath = apr_pstrdup(result_pool, info->moved_to_abspath);
+
+  /* ### TODO: Handle multiple moved_to values properly */
+  if (info->moved_to)
+    stat->moved_to_abspath = apr_pstrdup(result_pool,
+                                         info->moved_to->moved_to_abspath);
 
   stat->file_external = info->file_external;
 
@@ -1374,8 +1241,8 @@ get_dir_status(const struct walk_status_
     SVN_ERR(err);
 
   if (!dir_info)
-    SVN_ERR(read_info(&dir_info, local_abspath, wb->db,
-                      scratch_pool, iterpool));
+      SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, local_abspath,
+                                          scratch_pool, iterpool));
 
   SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url,
                                      &dir_repos_uuid, dir_info,
@@ -1535,8 +1402,9 @@ get_child_status(const struct walk_statu
   if (dirent->kind == svn_node_none)
     dirent = NULL;
 
-  SVN_ERR(read_info(&dir_info, parent_abspath, wb->db,
-                    scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__db_read_single_info(&dir_info,
+                                      wb->db, parent_abspath,
+                                      scratch_pool, scratch_pool));
 
   SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url,
                                      &dir_repos_uuid, dir_info,
@@ -2739,7 +2607,8 @@ svn_wc__internal_walk_status(svn_wc__db_
       ignore_patterns = ignores;
     }
 
-  err = read_info(&info, local_abspath, db, scratch_pool, scratch_pool);
+  err = svn_wc__db_read_single_info(&info, db, local_abspath,
+                                    scratch_pool, scratch_pool);
 
   if (err)
     {

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql?rev=1591150&r1=1591149&r2=1591150&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql Tue Apr 29 
23:49:19 2014
@@ -417,6 +417,12 @@ LEFT OUTER JOIN nodes AS moved 
 WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0
 LIMIT 1
 
+-- STMT_SELECT_MOVED_TO_NODE
+SELECT op_depth, moved_to
+FROM nodes
+WHERE wc_id = ?1 AND local_relpath = ?2 AND moved_to IS NOT NULL
+ORDER BY op_depth DESC
+
 -- STMT_SELECT_OP_DEPTH_MOVED_TO
 SELECT op_depth, moved_to, repos_path, revision
 FROM nodes

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c?rev=1591150&r1=1591149&r2=1591150&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c Tue Apr 29 23:49:19 
2014
@@ -8903,7 +8903,11 @@ read_children_info(svn_wc__db_wcroot_t *
           else
             child->op_root = (op_depth == relpath_depth(child_relpath));
 
-          svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
+          if (op_depth && child->op_root)
+            child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
+
+          if (new_child)
+            svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
         }
 
       if (op_depth == 0)
@@ -8925,18 +8929,48 @@ read_children_info(svn_wc__db_wcroot_t *
           child_item->nr_layers++;
           child_item->info.have_more_work = (child_item->nr_layers > 1);
 
-          /* Moved-to can only exist at op_depth > 0. */
-          /* ### Should we really do this for every layer where op_depth > 0
-                 in undefined order? */
+
+          /* A local_relpath can be moved multiple times at different op
+             depths and it really depends on the caller what is interesting.
+             We provide a simple linked list with the moved_from information */
+
           moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
           if (moved_to_relpath)
-            child_item->info.moved_to_abspath =
-              svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool);
+            {
+              struct svn_wc__db_moved_to_info_t *moved_to;
+              struct svn_wc__db_moved_to_info_t **next;
+              const char *shadow_op_relpath;
+              int cur_op_depth;
+
+              moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
+              moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
+                                                           moved_to_relpath,
+                                                           result_pool);
+
+              cur_op_depth = relpath_depth(child_relpath);
+              shadow_op_relpath = child_relpath;
 
-          /* Moved-here can only exist at op_depth > 0. */
-          /* ### Should we really do this for every layer where op_depth > 0
-                 in undefined order? */
-          child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
+              while (cur_op_depth > op_depth)
+                {
+                  shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath,
+                                                          scratch_pool);
+                  cur_op_depth--;
+                }
+
+              moved_to->shadow_op_root_abspath =
+                        svn_dirent_join(wcroot->abspath, shadow_op_relpath,
+                                        result_pool);
+
+              next = &child_item->info.moved_to;
+
+              while (*next &&
+                     0 < strcmp((*next)->shadow_op_root_abspath,
+                                moved_to->shadow_op_root_abspath))
+                next = &((*next)->next);
+
+              moved_to->next = *next;
+              *next = moved_to;
+            }
         }
 
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
@@ -9024,6 +9058,178 @@ svn_wc__db_read_children_info(apr_hash_t
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+db_read_props(apr_hash_t **props,
+              svn_wc__db_wcroot_t *wcroot,
+              const char *local_relpath,
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool);
+
+static svn_error_t *
+read_single_info(const struct svn_wc__db_info_t **info,
+                 svn_wc__db_wcroot_t *wcroot,
+                 const char *local_relpath,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
+{
+  struct svn_wc__db_info_t *mtb;
+  apr_int64_t repos_id;
+  const svn_checksum_t *checksum;
+  const char *original_repos_relpath;
+  svn_boolean_t have_work;
+
+  mtb = apr_pcalloc(result_pool, sizeof(*mtb));
+
+  SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
+                    &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
+                    &mtb->changed_date, &mtb->changed_author, &mtb->depth,
+                    &checksum, NULL, &original_repos_relpath, NULL, NULL,
+                    &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
+                    &mtb->changelist, &mtb->conflicted, &mtb->op_root,
+                    &mtb->had_props, &mtb->props_mod, &mtb->have_base,
+                    &mtb->have_more_work, &have_work,
+                    wcroot, local_relpath,
+                    result_pool, scratch_pool));
+
+  /* Query the same rows in the database again for move information */
+  if (have_work && (mtb->have_base || mtb->have_more_work))
+    {
+      svn_sqlite__stmt_t *stmt;
+      svn_boolean_t have_row;
+      const char *cur_relpath = NULL;
+      int cur_op_depth;
+
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_SELECT_MOVED_TO_NODE));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+      while (have_row)
+        {
+          struct svn_wc__db_moved_to_info_t *move;
+          int op_depth = svn_sqlite__column_int(stmt, 0);
+          const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, 
NULL);
+
+          move = apr_pcalloc(result_pool, sizeof(*move));
+          move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
+                                                   moved_to_relpath,
+                                                   result_pool);
+
+          if (!cur_relpath)
+            {
+              cur_relpath = local_relpath;
+              cur_op_depth = relpath_depth(cur_relpath);
+            }
+          while (cur_op_depth > op_depth)
+            {
+              cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool);
+              cur_op_depth--;
+            }
+          move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
+                                                         cur_relpath,
+                                                         result_pool);
+
+          move->next = mtb->moved_to;
+          mtb->moved_to = move;
+
+          SVN_ERR(svn_sqlite__step(&have_row, stmt));
+        }
+
+      SVN_ERR(svn_sqlite__reset(stmt));
+    }
+
+  /* Maybe we have to get some shadowed lock from BASE to make our test suite
+     happy... (It might be completely unrelated, but...)
+     This queries the same BASE row again, joined to the lock table */
+  if (mtb->have_base && (have_work || mtb->kind == svn_node_file))
+    {
+      svn_boolean_t update_root;
+      svn_wc__db_lock_t **lock_arg = NULL;
+
+      if (have_work)
+        lock_arg = &mtb->lock;
+
+      SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
+                                                NULL, NULL, NULL, NULL, NULL,
+                                                NULL, lock_arg, NULL, NULL,
+                                                &update_root,
+                                                wcroot, local_relpath,
+                                                result_pool, scratch_pool));
+
+      mtb->file_external = (update_root && mtb->kind == svn_node_file);
+    }
+
+  if (mtb->status == svn_wc__db_status_added)
+    {
+      svn_wc__db_status_t status;
+
+      SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                            NULL, NULL,
+                            wcroot, local_relpath,
+                            result_pool, scratch_pool));
+
+      mtb->moved_here = (status == svn_wc__db_status_moved_here);
+      mtb->incomplete = (status == svn_wc__db_status_incomplete);
+    }
+
+#ifdef HAVE_SYMLINK
+  if (mtb->kind == svn_node_file
+      && (mtb->had_props || mtb->props_mod))
+    {
+      apr_hash_t *properties;
+
+      if (mtb->props_mod)
+        SVN_ERR(db_read_props(&properties,
+                              wcroot, local_relpath,
+                              scratch_pool, scratch_pool));
+      else
+        SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
+                                       TRUE /* deleted_ok */,
+                                       scratch_pool, scratch_pool));
+
+      mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
+    }
+#endif
+
+  mtb->has_checksum = (checksum != NULL);
+  mtb->copied = (original_repos_relpath != NULL);
+
+  SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
+                                      wcroot->sdb, repos_id, result_pool));
+
+  if (mtb->kind == svn_node_dir)
+    SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
+
+  *info = mtb;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
+                            svn_wc__db_t *db,
+                            const char *local_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(local_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                                                local_abspath,
+                                                scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
+                                       result_pool, scratch_pool),
+                      wcroot);
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
                               svn_node_kind_t *kind,

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h?rev=1591150&r1=1591149&r2=1591150&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h Tue Apr 29 23:49:19 
2014
@@ -1909,6 +1909,16 @@ svn_wc__db_read_info(svn_wc__db_status_t
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool);
 
+/* Structure used as linked list in svn_wc__db_info_t to describe all nodes
+   in this location that were moved to another location */
+struct svn_wc__db_moved_to_info_t
+{
+  const char *moved_to_abspath;
+  const char *shadow_op_root_abspath;
+
+  struct svn_wc__db_moved_to_info_t *next;
+};
+
 /* Structure returned by svn_wc__db_read_children_info.  Only has the
    fields needed by status. */
 struct svn_wc__db_info_t {
@@ -1945,7 +1955,10 @@ struct svn_wc__db_info_t {
   svn_wc__db_lock_t *lock;  /* Repository file lock */
   svn_boolean_t incomplete; /* TRUE if a working node is incomplete */
 
-  const char *moved_to_abspath; /* Only on op-roots. See svn_wc_status3_t. */
+  struct svn_wc__db_moved_to_info_t *moved_to; /* A linked list of locations
+                                                 where nodes at this path
+                                                 are moved to. Highest layers
+                                                 first */
   svn_boolean_t moved_here;     /* Only on op-roots. */
 
   svn_boolean_t file_external;
@@ -1967,6 +1980,14 @@ svn_wc__db_read_children_info(apr_hash_t
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool);
 
+/* Like svn_wc__db_read_children_info, but only gets an info node for the root
+   element. */
+svn_error_t *
+svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
+                            svn_wc__db_t *db,
+                            const char *local_abspath,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool);
 
 /* Structure returned by svn_wc__db_read_walker_info.  Only has the
    fields needed by svn_wc__internal_walk_children(). */

Modified: subversion/branches/1.8.x/subversion/tests/cmdline/stat_tests.py
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/tests/cmdline/stat_tests.py?rev=1591150&r1=1591149&r2=1591150&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/tests/cmdline/stat_tests.py (original)
+++ subversion/branches/1.8.x/subversion/tests/cmdline/stat_tests.py Tue Apr 29 
23:49:19 2014
@@ -2119,6 +2119,95 @@ def status_path_handling(sbox):
   expected_status = svntest.actions.get_virginal_state(rel_wc_dir, 1)
   svntest.actions.run_and_verify_status(rel_wc_dir, expected_status)
 
+def status_move_missing_direct(sbox):
+  "move information when status is called directly"
+  
+  sbox.build()
+  sbox.simple_copy('A', 'Z')
+  sbox.simple_commit('')
+  sbox.simple_update('')
+  
+  sbox.simple_move('Z', 'ZZ')
+  sbox.simple_move('A', 'Z')
+  sbox.simple_move('Z/B', 'ZB')
+  sbox.simple_mkdir('Z/B')
+  sbox.simple_move('ZB/E', 'Z/B/E')
+
+  # Somehow 'svn status' now shows different output for 'ZB/E'
+  # when called directly and via an ancestor, as this handles
+  # multi-layer in a different way
+  
+  # Note that the status output may change over different Subversion revisions,
+  # but the status on a node should be identical anyway 'svn status' is called
+  # on it.
+  
+  expected_output = [
+    'A  +    %s\n' % sbox.ospath('ZB'),
+    '        > moved from %s\n' % os.path.join('..', 'Z', 'B'),    
+    'D  +    %s\n' % sbox.ospath('ZB/E'),
+    '        > moved to %s\n' % os.path.join('..', 'Z', 'B', 'E'),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'status',
+                                     sbox.ospath('ZB'), '--depth', 
'immediates')
+
+  # And calling svn status on just 'ZB/E' should have the same result for this 
node
+  # except that we calculate the relative path from a different base
+  expected_output = [
+    'D  +    %s\n' % sbox.ospath('ZB/E'),
+    '        > moved to %s\n' % os.path.join('..', '..', 'Z', 'B', 'E'),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'status',
+                                     sbox.ospath('ZB/E'), '--depth', 'empty')
+
+def status_move_missing_direct_base(sbox):
+  "move when status is called directly with base"
+  
+  sbox.build()
+  sbox.simple_copy('A', 'Z')
+  sbox.simple_mkdir('Q')
+  sbox.simple_mkdir('Q/ZB')
+  sbox.simple_mkdir('Q/ZB/E')
+  sbox.simple_commit('')
+  sbox.simple_update('')
+  
+  sbox.simple_rm('Q')
+  sbox.simple_mkdir('Q')
+  
+  sbox.simple_move('Z', 'ZZ')
+  sbox.simple_move('A', 'Z')
+  sbox.simple_move('Z/B', 'Q/ZB')
+  sbox.simple_mkdir('Z/B')
+  sbox.simple_move('Q/ZB/E', 'Z/B/E')
+
+  # Somehow 'svn status' now shows different output for 'Q/ZB/E'
+  # when called directly and via an ancestor, as this handles
+  # multi-layer in a different way
+  
+  # Note that the status output may change over different Subversion revisions,
+  # but the status on a node should be identical anyway 'svn status' is called
+  # on it.
+  
+  # This test had a different result as status_move_missing_direct at the time 
of
+  # writing this test.
+  
+  expected_output = [
+    'A  +    %s\n' % sbox.ospath('Q/ZB'),
+    '        > moved from %s\n' % os.path.join('..', '..', 'Z', 'B'),
+    'D  +    %s\n' % sbox.ospath('Q/ZB/E'),
+    '        > moved to %s\n' % os.path.join('..', '..', 'Z', 'B', 'E'),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'status',
+                                     sbox.ospath('Q/ZB'), '--depth', 
'immediates')
+
+  # And calling svn status on just 'ZB/E' should have the same result for this 
node,
+  # except that the moved_to information is calculated from the node itself
+  expected_output = [
+    'D  +    %s\n' % sbox.ospath('Q/ZB/E'),
+    '        > moved to %s\n' % os.path.join('..', '..', '..', 'Z', 'B', 'E'),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'status',
+                                     sbox.ospath('Q/ZB/E'), '--depth', 'empty')
+
 ########################################################################
 # Run the tests
 
@@ -2167,6 +2256,8 @@ test_list = [ None,
               status_case_changed,
               move_update_timestamps,
               status_path_handling,
+              status_move_missing_direct,
+              status_move_missing_direct_base,
              ]
 
 if __name__ == '__main__':


Reply via email to