You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2012/11/19 20:10:11 UTC

svn commit: r1411356 - in /subversion/branches/tree-read-api/subversion: include/svn_tree.h libsvn_subr/tree.c

Author: julianfoad
Date: Mon Nov 19 19:10:10 2012
New Revision: 1411356

URL: http://svn.apache.org/viewvc?rev=1411356&view=rev
Log:
On the 'tree-read-api' branch:
Add a 'walk_singleton_dirs' option to the two-trees walker.

* subversion/include/svn_tree.h
  (svn_tree_walk_two): Add a 'walk_singleton_dirs' option. Improve docs.

* subversion/libsvn_subr/tree.c
  (get_child): New function.
  (walk_two_trees, svn_tree_walk_two): Add and implement the
    'walk_singleton_dirs' option.

Modified:
    subversion/branches/tree-read-api/subversion/include/svn_tree.h
    subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c

Modified: subversion/branches/tree-read-api/subversion/include/svn_tree.h
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/include/svn_tree.h?rev=1411356&r1=1411355&r2=1411356&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/include/svn_tree.h (original)
+++ subversion/branches/tree-read-api/subversion/include/svn_tree.h Mon Nov 19 19:10:10 2012
@@ -146,23 +146,37 @@ typedef svn_error_t *(*svn_tree_walk_two
                                                  void *walk_baton,
                                                  apr_pool_t *scratch_pool);
 
-/** Walk the two generic trees @a tree1 and @a tree2, simultaneously,
- * recursing to @a depth.
+/** Walk the two generic trees @a tree1 and @a tree2, simultaneously.
+ * Recurse as far as @a depth in each tree.
  *
  * Call @a walk_func for each node, passing @a walk_baton and the tree
  * node object.
  *
+ * When a directory appears in just one of the trees, visit it, and if @a
+ * walk_singleton_dirs is TRUE, then also walk its contents, passing NULL
+ * as the node on the other side.  The walk recurses only as far as @a
+ * depth, interpreted relative to the root of @a tree1 and @a tree2.
+ *
  * If @a cancel_func is not null, call it with @a cancel_baton to check for
- * cancellation.
+ * cancellation, approximately once per directory.
+ *
+ * @note This function provides no information on the historical ancestry
+ * or versioning relationship between a pair of nodes.  Nodes at the same
+ * relative path are visited together regardless of whether they are, at one
+ * extreme, different kinds of node within entirely unrelated trees, or, at
+ * the other extreme, references to exactly the same node in two instances
+ * of the same tree.
  *
- * TODO: Visit nodes with the same id at the same time, thus tracking moves.
+ * TODO: Make another walker that visits nodes with the same id at the same
+ * time, regardless of their relative paths, thus tracking moves?
  * TODO: Let the callback determine the order of walking sub-nodes,
- * especially with respect to far-moves (moves into or out of a directory).
+ * especially with respect to far-moves (moves into or out of a directory)?
  */
 svn_error_t *
 svn_tree_walk_two(svn_tree_t *tree1,
                   svn_tree_t *tree2,
                   svn_depth_t depth,
+                  svn_boolean_t walk_singleton_dirs,
                   const svn_tree_walk_two_func_t walk_func,
                   void *walk_baton,
                   svn_cancel_func_t cancel_func,

Modified: subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c?rev=1411356&r1=1411355&r2=1411356&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c Mon Nov 19 19:10:10 2012
@@ -254,18 +254,53 @@ svn_tree_walk(svn_tree_t *tree,
   return SVN_NO_ERROR;
 }
 
-/* Walk two trees, rooted at NODE1 and NODE2, simultaneously, driving the
- * CALLBACKS.
+/* Get the child node named NAME from CHILDREN, if it falls within the
+ * requested DEPTH.
  *
- * Currently visits nodes with the same relpath at the same time; see TODO
- * notes on svn_tree_walk_two().
+ * CHILDREN maps (const char *) names to (svn_tree_node_t *) child nodes.
+ * DEPTH is relative to the parent node of CHILDREN and is at least 'files'.
  *
- * TODO: allow recursing into a singleton directory (that is, on one side)?
+ * If NAME is not present in CHILDREN, or DEPTH is 'files' and the child
+ * node is not a file, then set *CHILD to NULL.
+ */
+static svn_error_t *
+get_child(svn_tree_node_t **child,
+          apr_hash_t *children,
+          const char *name,
+          svn_depth_t depth,
+          apr_pool_t *scratch_pool)
+{
+  /* We shouldn't be called for depth less than 'files'. */
+  assert(depth >= svn_depth_files);
+
+  *child = apr_hash_get(children, name, APR_HASH_KEY_STRING);
+
+  /* If we want to omit directory children, do so now. */
+  if (*child && depth == svn_depth_files)
+    {
+      svn_node_kind_t kind;
+
+      SVN_ERR(tree_node_get_kind_or_unknown(&kind, *child, scratch_pool));
+      if (kind == svn_node_dir)
+        *child = NULL;
+    }
+  return SVN_NO_ERROR;
+}
+
+/* Walk two trees, rooted at NODE1 and NODE2, in parallel, visiting nodes
+ * with the same relpath at the same time.
+ *
+ * Call the WALK_FUNC with WALK_BATON for each visited pair of nodes.
+ * Recurse as far as DEPTH.  When a directory appears at a given path only
+ * in one of the trees, recurse into it only if WALK_SINGLETON_DIRS is true.
+ *
+ * Use CANCEL_FUNC with CANCEL_BATON for cancellation.
  */
 static svn_error_t *
 walk_two_trees(svn_tree_node_t *node1,
                svn_tree_node_t *node2,
                svn_depth_t depth,
+               svn_boolean_t walk_singleton_dirs,
                const svn_tree_walk_two_func_t walk_func,
                void *walk_baton,
                svn_cancel_func_t cancel_func,
@@ -299,27 +334,28 @@ walk_two_trees(svn_tree_node_t *node1,
 
   SVN_ERR(walk_func(node1, node2, walk_baton, scratch_pool));
 
-  SVN_DBG(("walk_two_trees: kind %d/%d, '%s'\n",
-           kind1, kind2, relpath1 ? relpath1 : relpath2));
-
-  /* Recurse, if it's a directory on BOTH sides.  (If it's a directory on
-   * just one side (it's a replacement), we want to treat that just the same
-   * as a deleted or added directory: it's up to the callback to traverse
-   * the singleton if it wants to.) */
-  if (node1 && node2
-      && kind1 == svn_node_dir
-      && kind2 == svn_node_dir
-      && depth >= svn_depth_files)
+  /* Recurse, if it's a directory on BOTH sides or if we're walking
+   * singleton directories. */
+  if (depth >= svn_depth_files
+      && ((kind1 == svn_node_dir && kind2 == svn_node_dir)
+          || ((kind1 == svn_node_dir || kind2 == svn_node_dir)
+              && walk_singleton_dirs)))
     {
       apr_hash_t *children1, *children2;
       apr_hash_t *all_children;
       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
       apr_hash_index_t *hi;
 
-      SVN_ERR(svn_tree_node_read_dir(node1, &children1, NULL,
-                                     scratch_pool, scratch_pool));
-      SVN_ERR(svn_tree_node_read_dir(node2, &children2, NULL,
-                                     scratch_pool, scratch_pool));
+      if (kind1 == svn_node_dir)
+        SVN_ERR(svn_tree_node_read_dir(node1, &children1, NULL,
+                                       scratch_pool, scratch_pool));
+      else
+        children1 = apr_hash_make(scratch_pool);
+      if (kind2 == svn_node_dir)
+        SVN_ERR(svn_tree_node_read_dir(node2, &children2, NULL,
+                                       scratch_pool, scratch_pool));
+      else
+        children2 = apr_hash_make(scratch_pool);
       all_children = apr_hash_overlay(scratch_pool, children1, children2);
 
       SVN_DBG(("Recursing (%d||%d=%d children) in '%s'\n",
@@ -327,26 +363,23 @@ walk_two_trees(svn_tree_node_t *node1,
                apr_hash_count(all_children),
                relpath1));
 
+      /* Iterate through the children in parallel, pairing them up by name. */
       for (hi = apr_hash_first(scratch_pool, all_children); hi;
            hi = apr_hash_next(hi))
         {
-          const char *relpath = svn__apr_hash_index_key(hi);
-          svn_tree_node_t *child1 = apr_hash_get(children1, relpath,
-                                                 APR_HASH_KEY_STRING);
-          svn_tree_node_t *child2 = apr_hash_get(children2, relpath,
-                                                 APR_HASH_KEY_STRING);
-          svn_node_kind_t child_kind;
+          const char *name = svn__apr_hash_index_key(hi);
+          svn_tree_node_t *child1, *child2;
 
           svn_pool_clear(iterpool);
 
-          SVN_ERR(tree_node_get_kind_or_unknown(&child_kind,
-                                                child1 ? child1 : child2,
-                                                iterpool));
-          if (depth >= svn_depth_immediates || child_kind == svn_node_file)
+          SVN_ERR(get_child(&child1, children1, name, depth, iterpool));
+          SVN_ERR(get_child(&child2, children2, name, depth, iterpool));
+
+          if (child1 || child2)
             {
               SVN_ERR(walk_two_trees(child1, child2,
                                      depth == svn_depth_infinity ? depth
-                                       : svn_depth_empty,
+                                       : svn_depth_empty, walk_singleton_dirs,
                                      walk_func, walk_baton,
                                      cancel_func, cancel_baton,
                                      iterpool));
@@ -362,6 +395,7 @@ svn_error_t *
 svn_tree_walk_two(svn_tree_t *tree1,
                   svn_tree_t *tree2,
                   svn_depth_t depth,
+                  svn_boolean_t walk_singleton_dirs,
                   const svn_tree_walk_two_func_t walk_func,
                   void *walk_baton,
                   svn_cancel_func_t cancel_func,
@@ -374,7 +408,7 @@ svn_tree_walk_two(svn_tree_t *tree1,
   SVN_ERR(svn_tree_get_root_node(&node2, tree2, scratch_pool, scratch_pool));
 
   SVN_ERR(walk_two_trees(node1, node2,
-                         depth,
+                         depth, walk_singleton_dirs,
                          walk_func, walk_baton,
                          cancel_func, cancel_baton,
                          scratch_pool));