You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by da...@apache.org on 2010/03/30 22:58:01 UTC

svn commit: r929279 [11/20] - in /subversion/branches/svn-patch-improvements: ./ build/ac-macros/ build/generator/ build/generator/templates/ contrib/client-side/emacs/ notes/feedback/ notes/meetings/ notes/wc-ng/ subversion/ subversion/bindings/javahl...

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/mergeinfo.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/mergeinfo.c Tue Mar 30 20:57:53 2010
@@ -115,10 +115,20 @@ parse_pathname(const char **input,
   return SVN_NO_ERROR;
 }
 
-/* Ways in which two svn_merge_range_t can intersect, if at all. */
+/* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward
+ * revision range.
+ *
+ * Note: The smallest valid value of RANGE->start is 0 because it is an
+ * exclusive endpoint, being one less than the revision number of the first
+ * change described by the range, and the oldest possible change is "r1" as
+ * there cannot be a change "r0". */
+#define IS_VALID_FORWARD_RANGE(range) \
+  (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
+
+/* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */
 typedef enum
 {
-  /* Ranges don't intersect. */
+  /* Ranges don't intersect and don't adjoin. */
   svn__no_intersection,
 
   /* Ranges are equal. */
@@ -127,7 +137,7 @@ typedef enum
   /* Ranges adjoin but don't overlap. */
   svn__adjoining_intersection,
 
-  /* Ranges overalp but neither is a subset of the other. */
+  /* Ranges overlap but neither is a subset of the other. */
   svn__overlapping_intersection,
 
   /* One range is a proper subset of the other. */
@@ -144,16 +154,8 @@ get_type_of_intersection(const svn_merge
 {
   SVN_ERR_ASSERT(r1);
   SVN_ERR_ASSERT(r2);
-
-  /* Why not use SVN_IS_VALID_REVNUM here?  Because revision 0
-     is described START = -1, END = 0.  See svn_merge_range_t. */
-  SVN_ERR_ASSERT(r1->start >= -1);
-  SVN_ERR_ASSERT(r2->start >= -1);
-
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(r1->end));
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(r2->end));
-  SVN_ERR_ASSERT(r1->start < r1->end);
-  SVN_ERR_ASSERT(r2->start < r2->end);
+  SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1));
+  SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2));
 
   if (!(r1->start <= r2->end && r2->start <= r1->end))
     *intersection_type = svn__no_intersection;
@@ -457,6 +459,10 @@ range_to_string(const svn_merge_range_t 
 }
 
 /* Helper for svn_mergeinfo_parse()
+   Append revision ranges onto the array RANGELIST to represent the range
+   descriptions found in the string *INPUT.  Read only as far as a newline
+   or the position END, whichever comes first.  Set *INPUT to the position
+   after the last character of INPUT that was used.
 
    revisionlist -> (revisionelement)(COMMA revisionelement)*
    revisionrange -> REVISION "-" REVISION("*")
@@ -501,6 +507,11 @@ parse_rangelist(const char **input, cons
       mrange->end = firstrev;
       mrange->inheritable = TRUE;
 
+      if (firstrev == 0)
+        return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
+                                 _("Invalid revision number '0' found in "
+                                   "range list"));
+
       if (*curr == '-')
         {
           svn_revnum_t secondrev;
@@ -696,7 +707,7 @@ svn_mergeinfo_parse(svn_mergeinfo_t *mer
 
 svn_error_t *
 svn_rangelist_merge(apr_array_header_t **rangelist,
-                    apr_array_header_t *changes,
+                    const apr_array_header_t *changes,
                     apr_pool_t *pool)
 {
   int i, j;
@@ -763,20 +774,31 @@ svn_rangelist_merge(apr_array_header_t *
   return SVN_NO_ERROR;
 }
 
+/* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and
+ * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */
 static svn_boolean_t
 range_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second,
                 svn_boolean_t consider_inheritance)
 {
+  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
+  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
+
   return (first->start + 1 <= second->end)
     && (second->start + 1 <= first->end)
     && (!consider_inheritance
         || (!(first->inheritable) == !(second->inheritable)));
 }
 
+/* Return TRUE iff the forward revision range FIRST wholly contains the
+ * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has
+ * the same inheritability. */
 static svn_boolean_t
 range_contains(const svn_merge_range_t *first, const svn_merge_range_t *second,
                svn_boolean_t consider_inheritance)
 {
+  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
+  SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
+
   return (first->start <= second->start) && (second->end <= first->end)
     && (!consider_inheritance
         || (!(first->inheritable) == !(second->inheritable)));
@@ -848,12 +870,7 @@ svn_mergeinfo__set_inheritance(svn_merge
            hi;
            hi = apr_hash_next(hi))
         {
-          apr_array_header_t *rangelist;
-          const void *path;
-          void *val;
-          apr_hash_this(hi, &path, NULL, &val);
-
-          rangelist = apr_hash_get(mergeinfo, path, APR_HASH_KEY_STRING);
+          apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
 
           if (rangelist)
             svn_rangelist__set_inheritance(rangelist, inheritable);
@@ -862,84 +879,126 @@ svn_mergeinfo__set_inheritance(svn_merge
   return;
 }
 
-/* Either remove any overlapping ranges described by ERASER from
-   WHITEBOARD (when DO_REMOVE is TRUE), or capture the overlap, and
-   place the remaining or overlapping ranges in OUTPUT. */
-/*  ### FIXME: Some variables names and inline comments for this method
-    ### are legacy from when it was solely the remove() impl. */
+/* If DO_REMOVE is true, then remove any overlapping ranges described by
+   RANGELIST1 from RANGELIST2 and place the results in *OUTPUT.  When
+   DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2
+   the "whiteboard".
+
+   If DO_REMOVE is false, then capture the intersection between RANGELIST1
+   and RANGELIST2 and place the results in *OUTPUT.  The ordering of
+   RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false.
+
+   If CONSIDER_INHERITANCE is true, then take the inheritance of the
+   ranges in RANGELIST1 and RANGELIST2 into account when comparing them
+   for intersection, see the doc string for svn_rangelist_intersection().
+
+   If CONSIDER_INHERITANCE is false, then ranges with differing inheritance
+   may intersect, but the resulting intersection is non-inheritable only
+   if both ranges were non-inheritable, e.g.:
+
+   RANGELIST1  RANGELIST2  CONSIDER     DO_REMOVE  *OUTPUT
+                           INHERITANCE
+   ----------  ------      -----------  ---------  -------
+
+   90-420*     1-100       TRUE         FALSE      Empty Rangelist
+   90-420      1-100*      TRUE         FALSE      Empty Rangelist
+   90-420      1-100       TRUE         FALSE      90-100
+   90-420*     1-100*      TRUE         FALSE      90-100*
+
+   90-420*     1-100       FALSE        FALSE      90-100
+   90-420      1-100*      FALSE        FALSE      90-100
+   90-420      1-100       FALSE        FALSE      90-100
+   90-420*     1-100*      FALSE        FALSE      90-100*
+
+   Allocate the contents of *OUTPUT in POOL. */
 static svn_error_t *
 rangelist_intersect_or_remove(apr_array_header_t **output,
-                              apr_array_header_t *eraser,
-                              apr_array_header_t *whiteboard,
+                              const apr_array_header_t *rangelist1,
+                              const apr_array_header_t *rangelist2,
                               svn_boolean_t do_remove,
                               svn_boolean_t consider_inheritance,
                               apr_pool_t *pool)
 {
-  int i, j, lasti;
-  svn_merge_range_t wboardelt;
+  int i1, i2, lasti2;
+  svn_merge_range_t working_elt2;
 
   *output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
 
-  i = 0;
-  j = 0;
-  lasti = -1;  /* Initialized to a value that "i" will never be. */
+  i1 = 0;
+  i2 = 0;
+  lasti2 = -1;  /* Initialized to a value that "i2" will never be. */
 
-  while (i < whiteboard->nelts && j < eraser->nelts)
+  while (i1 < rangelist1->nelts && i2 < rangelist2->nelts)
     {
       svn_merge_range_t *elt1, *elt2;
 
-      elt2 = APR_ARRAY_IDX(eraser, j, svn_merge_range_t *);
+      elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *);
 
-      /* Instead of making a copy of the entire array of whiteboard
-         elements, we just keep a copy of the current whiteboard element
+      /* Instead of making a copy of the entire array of rangelist2
+         elements, we just keep a copy of the current rangelist2 element
          that needs to be used, and modify our copy if necessary. */
-      if (i != lasti)
+      if (i2 != lasti2)
         {
-          wboardelt = *(APR_ARRAY_IDX(whiteboard, i, svn_merge_range_t *));
-          lasti = i;
+          working_elt2 =
+            *(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *));
+          lasti2 = i2;
         }
 
-      elt1 = &wboardelt;
+      elt2 = &working_elt2;
 
-      /* If the whiteboard range is contained completely in the
-         eraser, we increment the whiteboard.
+      /* If the rangelist2 range is contained completely in the
+         rangelist1, we increment the rangelist2.
          If the ranges intersect, and match exactly, we increment both
-         eraser and whiteboard.
+         rangelist1 and rangelist2.
          Otherwise, we have to generate a range for the left part of
-         the removal of eraser from whiteboard, and possibly change
-         the whiteboard to the remaining portion of the right part of
+         the removal of rangelist1 from rangelist2, and possibly change
+         the rangelist2 to the remaining portion of the right part of
          the removal, to test against. */
-      if (range_contains(elt2, elt1, consider_inheritance))
+      if (range_contains(elt1, elt2, consider_inheritance))
         {
           if (!do_remove)
-            SVN_ERR(combine_with_lastrange(elt1, *output,
-                                           consider_inheritance, pool,
-                                           pool));
+            {
+              svn_merge_range_t tmp_range;
+              tmp_range.start = elt2->start;
+              tmp_range.end = elt2->end;
+              /* The intersection of two ranges is non-inheritable only
+                 if both ranges are non-inheritable. */
+              tmp_range.inheritable =
+                (elt2->inheritable || elt1->inheritable);
+              SVN_ERR(combine_with_lastrange(&tmp_range, *output,
+                                             consider_inheritance, pool,
+                                             pool));
+            }
 
-          i++;
+          i2++;
 
-          if (elt1->start == elt2->start && elt1->end == elt2->end)
-            j++;
+          if (elt2->start == elt1->start && elt2->end == elt1->end)
+            i1++;
         }
-      else if (range_intersect(elt2, elt1, consider_inheritance))
+      else if (range_intersect(elt1, elt2, consider_inheritance))
         {
-          if (elt1->start < elt2->start)
+          if (elt2->start < elt1->start)
             {
-              /* The whiteboard range starts before the eraser range. */
+              /* The rangelist2 range starts before the rangelist1 range. */
               svn_merge_range_t tmp_range;
-              tmp_range.inheritable = elt1->inheritable;
               if (do_remove)
                 {
-                  /* Retain the range that falls before the eraser start. */
-                  tmp_range.start = elt1->start;
-                  tmp_range.end = elt2->start;
+                  /* Retain the range that falls before the rangelist1
+                     start. */
+                  tmp_range.start = elt2->start;
+                  tmp_range.end = elt1->start;
+                  tmp_range.inheritable = elt2->inheritable;
                 }
               else
                 {
-                  /* Retain the range that falls between the eraser
-                     start and whiteboard end. */
-                  tmp_range.start = elt2->start;
-                  tmp_range.end = MIN(elt1->end, elt2->end);
+                  /* Retain the range that falls between the rangelist1
+                     start and rangelist2 end. */
+                  tmp_range.start = elt1->start;
+                  tmp_range.end = MIN(elt2->end, elt1->end);
+                  /* The intersection of two ranges is non-inheritable only
+                     if both ranges are non-inheritable. */
+                  tmp_range.inheritable =
+                    (elt2->inheritable || elt1->inheritable);
                 }
 
               SVN_ERR(combine_with_lastrange(&tmp_range,
@@ -947,40 +1006,43 @@ rangelist_intersect_or_remove(apr_array_
                                              pool, pool));
             }
 
-          /* Set up the rest of the whiteboard range for further
+          /* Set up the rest of the rangelist2 range for further
              processing.  */
-          if (elt1->end > elt2->end)
+          if (elt2->end > elt1->end)
             {
-              /* The whiteboard range ends after the eraser range. */
+              /* The rangelist2 range ends after the rangelist1 range. */
               if (!do_remove)
                 {
                   /* Partial overlap. */
                   svn_merge_range_t tmp_range;
-                  tmp_range.start = MAX(elt1->start, elt2->start);
-                  tmp_range.end = elt2->end;
-                  tmp_range.inheritable = elt1->inheritable;
+                  tmp_range.start = MAX(elt2->start, elt1->start);
+                  tmp_range.end = elt1->end;
+                  /* The intersection of two ranges is non-inheritable only
+                     if both ranges are non-inheritable. */
+                  tmp_range.inheritable =
+                    (elt2->inheritable || elt1->inheritable);
                   SVN_ERR(combine_with_lastrange(&tmp_range,
                                                  *output,
                                                  consider_inheritance,
                                                  pool, pool));
                 }
 
-              wboardelt.start = elt2->end;
-              wboardelt.end = elt1->end;
+              working_elt2.start = elt1->end;
+              working_elt2.end = elt2->end;
             }
           else
-            i++;
+            i2++;
         }
       else  /* ranges don't intersect */
         {
-          /* See which side of the whiteboard the eraser is on.  If it
-             is on the left side, we need to move the eraser.
+          /* See which side of the rangelist2 the rangelist1 is on.  If it
+             is on the left side, we need to move the rangelist1.
 
-             If it is on past the whiteboard on the right side, we
-             need to output the whiteboard and increment the
-             whiteboard.  */
-          if (svn_sort_compare_ranges(&elt2, &elt1) < 0)
-            j++;
+             If it is on past the rangelist2 on the right side, we
+             need to output the rangelist2 and increment the
+             rangelist2.  */
+          if (svn_sort_compare_ranges(&elt1, &elt2) < 0)
+            i1++;
           else
             {
               svn_merge_range_t *lastrange;
@@ -992,36 +1054,36 @@ rangelist_intersect_or_remove(apr_array_
                 lastrange = NULL;
 
               if (do_remove && !(lastrange &&
-                                 combine_ranges(lastrange, lastrange, elt1,
+                                 combine_ranges(lastrange, lastrange, elt2,
                                                 consider_inheritance)))
                 {
-                  lastrange = svn_merge_range_dup(elt1, pool);
+                  lastrange = svn_merge_range_dup(elt2, pool);
                   APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange;
                 }
-              i++;
+              i2++;
             }
         }
     }
 
   if (do_remove)
     {
-      /* Copy the current whiteboard element if we didn't hit the end
-         of the whiteboard, and we still had it around.  This element
-         may have been touched, so we can't just walk the whiteboard
+      /* Copy the current rangelist2 element if we didn't hit the end
+         of the rangelist2, and we still had it around.  This element
+         may have been touched, so we can't just walk the rangelist2
          array, we have to use our copy.  This case only happens when
-         we ran out of eraser before whiteboard, *and* we had changed
-         the whiteboard element. */
-      if (i == lasti && i < whiteboard->nelts)
+         we ran out of rangelist1 before rangelist2, *and* we had changed
+         the rangelist2 element. */
+      if (i2 == lasti2 && i2 < rangelist2->nelts)
         {
-          SVN_ERR(combine_with_lastrange(&wboardelt, *output,
+          SVN_ERR(combine_with_lastrange(&working_elt2, *output,
                                          consider_inheritance, pool, pool));
-          i++;
+          i2++;
         }
 
-      /* Copy any other remaining untouched whiteboard elements.  */
-      for (; i < whiteboard->nelts; i++)
+      /* Copy any other remaining untouched rangelist2 elements.  */
+      for (; i2 < rangelist2->nelts; i2++)
         {
-          svn_merge_range_t *elt = APR_ARRAY_IDX(whiteboard, i,
+          svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2,
                                                  svn_merge_range_t *);
 
           SVN_ERR(combine_with_lastrange(elt, *output,
@@ -1035,8 +1097,8 @@ rangelist_intersect_or_remove(apr_array_
 
 svn_error_t *
 svn_rangelist_intersect(apr_array_header_t **output,
-                        apr_array_header_t *rangelist1,
-                        apr_array_header_t *rangelist2,
+                        const apr_array_header_t *rangelist1,
+                        const apr_array_header_t *rangelist2,
                         svn_boolean_t consider_inheritance,
                         apr_pool_t *pool)
 {
@@ -1046,8 +1108,8 @@ svn_rangelist_intersect(apr_array_header
 
 svn_error_t *
 svn_rangelist_remove(apr_array_header_t **output,
-                     apr_array_header_t *eraser,
-                     apr_array_header_t *whiteboard,
+                     const apr_array_header_t *eraser,
+                     const apr_array_header_t *whiteboard,
                      svn_boolean_t consider_inheritance,
                      apr_pool_t *pool)
 {
@@ -1057,7 +1119,7 @@ svn_rangelist_remove(apr_array_header_t 
 
 svn_error_t *
 svn_rangelist_diff(apr_array_header_t **deleted, apr_array_header_t **added,
-                   apr_array_header_t *from, apr_array_header_t *to,
+                   const apr_array_header_t *from, const apr_array_header_t *to,
                    svn_boolean_t consider_inheritance,
                    apr_pool_t *pool)
 {
@@ -1295,7 +1357,7 @@ svn_error_t *
 svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo,
                          svn_mergeinfo_t mergeinfo1,
                          svn_mergeinfo_t mergeinfo2,
-                         svn_boolean_t consider_ineheritance,
+                         svn_boolean_t consider_inheritance,
                          apr_pool_t *result_pool,
                          apr_pool_t *scratch_pool)
 {
@@ -1311,23 +1373,20 @@ svn_mergeinfo_intersect2(svn_mergeinfo_t
   for (hi = apr_hash_first(apr_hash_pool_get(mergeinfo1), mergeinfo1);
        hi; hi = apr_hash_next(hi))
     {
-      apr_array_header_t *rangelist;
-      const void *path;
-      void *val;
-      apr_hash_this(hi, &path, NULL, &val);
-
-      rangelist = apr_hash_get(mergeinfo2, path, APR_HASH_KEY_STRING);
-      if (rangelist)
-        {
-          SVN_ERR(svn_rangelist_intersect(&rangelist,
-                                          (apr_array_header_t *) val,
-                                          rangelist, consider_ineheritance,
-                                          scratch_pool));
-          if (rangelist->nelts > 0)
+      const char *path = svn__apr_hash_index_key(hi);
+      apr_array_header_t *rangelist1 = svn__apr_hash_index_val(hi);
+      apr_array_header_t *rangelist2;
+
+      rangelist2 = apr_hash_get(mergeinfo2, path, APR_HASH_KEY_STRING);
+      if (rangelist2)
+        {
+          SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2,
+                                          consider_inheritance, scratch_pool));
+          if (rangelist2->nelts > 0)
             apr_hash_set(*mergeinfo,
                          apr_pstrdup(result_pool, path),
                          APR_HASH_KEY_STRING,
-                         svn_rangelist_dup(rangelist, result_pool));
+                         svn_rangelist_dup(rangelist2, result_pool));
         }
     }
   return SVN_NO_ERROR;
@@ -1345,13 +1404,13 @@ svn_error_t *
 svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo,
                       svn_mergeinfo_t eraser,
                       svn_mergeinfo_t whiteboard,
-                      svn_boolean_t consider_ineritance,
+                      svn_boolean_t consider_inheritance,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool)
 {
   *mergeinfo = apr_hash_make(result_pool);
   return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL,
-                                      consider_ineritance, result_pool,
+                                      consider_inheritance, result_pool,
                                       scratch_pool);
 }
 
@@ -1447,14 +1506,11 @@ svn_error_t *
 svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool)
 {
   apr_hash_index_t *hi;
-  void *val;
 
   for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
     {
-      apr_array_header_t *rl;
-      apr_hash_this(hi, NULL, NULL, &val);
+      apr_array_header_t *rl = svn__apr_hash_index_val(hi);
 
-      rl = val;
       qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges);
     }
   return SVN_NO_ERROR;
@@ -1471,9 +1527,9 @@ svn_mergeinfo_catalog_dup(svn_mergeinfo_
        hi;
        hi = apr_hash_next(hi))
     {
-      const void *key;
-      void *val;
-      apr_hash_this(hi, &key, NULL, &val);
+      const char *key = svn__apr_hash_index_key(hi);
+      svn_mergeinfo_t val = svn__apr_hash_index_val(hi);
+
       apr_hash_set(new_mergeinfo_catalog,
                    apr_pstrdup(pool, key),
                    APR_HASH_KEY_STRING,
@@ -1488,15 +1544,15 @@ svn_mergeinfo_dup(svn_mergeinfo_t mergei
 {
   svn_mergeinfo_t new_mergeinfo = apr_hash_make(pool);
   apr_hash_index_t *hi;
-  const void *path;
-  apr_ssize_t pathlen;
-  void *rangelist;
 
   for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
     {
-      apr_hash_this(hi, &path, &pathlen, &rangelist);
+      const char *path = svn__apr_hash_index_key(hi);
+      apr_ssize_t pathlen = svn__apr_hash_index_klen(hi);
+      apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
+
       apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen,
-                   svn_rangelist_dup((apr_array_header_t *) rangelist, pool));
+                   svn_rangelist_dup(rangelist, pool));
     }
 
   return new_mergeinfo;
@@ -1513,25 +1569,23 @@ svn_mergeinfo_inheritable2(svn_mergeinfo
                            apr_pool_t *scratch_pool)
 {
   apr_hash_index_t *hi;
-  const void *key;
-  apr_ssize_t keylen;
-  void *rangelist;
-
   svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool);
+
   for (hi = apr_hash_first(scratch_pool, mergeinfo);
        hi;
        hi = apr_hash_next(hi))
     {
+      const char *key = svn__apr_hash_index_key(hi);
+      apr_ssize_t keylen = svn__apr_hash_index_klen(hi);
+      apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
       apr_array_header_t *inheritable_rangelist;
-      apr_hash_this(hi, &key, &keylen, &rangelist);
-      if (!path || svn_path_compare_paths(path, (const char *)key) == 0)
-        SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist,
-                                           (apr_array_header_t *) rangelist,
+
+      if (!path || svn_path_compare_paths(path, key) == 0)
+        SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist, rangelist,
                                            start, end, inheritable,
                                            result_pool, scratch_pool));
       else
-        inheritable_rangelist =
-          svn_rangelist_dup((apr_array_header_t *)rangelist, result_pool);
+        inheritable_rangelist = svn_rangelist_dup(rangelist, result_pool);
 
       /* Only add this rangelist if some ranges remain.  A rangelist with
          a path mapped to an empty rangelist is not syntactically valid */
@@ -1544,47 +1598,10 @@ svn_mergeinfo_inheritable2(svn_mergeinfo
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_mergeinfo_inheritable(svn_mergeinfo_t *output,
-                          svn_mergeinfo_t mergeinfo,
-                          const char *path,
-                          svn_revnum_t start,
-                          svn_revnum_t end,
-                          apr_pool_t *pool)
-{
-  apr_hash_index_t *hi;
-  const void *key;
-  apr_ssize_t keylen;
-  void *rangelist;
-
-  svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(pool);
-  for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
-    {
-      apr_array_header_t *inheritable_rangelist;
-      apr_hash_this(hi, &key, &keylen, &rangelist);
-      if (!path || svn_path_compare_paths(path, (const char *)key) == 0)
-        SVN_ERR(svn_rangelist_inheritable(&inheritable_rangelist,
-                                          (apr_array_header_t *) rangelist,
-                                          start, end, pool));
-      else
-        inheritable_rangelist =
-          svn_rangelist_dup((apr_array_header_t *)rangelist, pool);
-
-      /* Only add this rangelist if some ranges remain.  A rangelist with
-         a path mapped to an empty rangelist is not syntactically valid */
-      if (inheritable_rangelist->nelts)
-        apr_hash_set(inheritable_mergeinfo,
-                     apr_pstrmemdup(pool, key, keylen), keylen,
-                     inheritable_rangelist);
-    }
-  *output = inheritable_mergeinfo;
-  return SVN_NO_ERROR;
-}
-
 
 svn_error_t *
 svn_rangelist_inheritable2(apr_array_header_t **inheritable_rangelist,
-                           apr_array_header_t *rangelist,
+                           const apr_array_header_t *rangelist,
                            svn_revnum_t start,
                            svn_revnum_t end,
                            svn_boolean_t inheritable,
@@ -1635,17 +1652,6 @@ svn_rangelist_inheritable2(apr_array_hea
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_rangelist_inheritable(apr_array_header_t **inheritable_rangelist,
-                          apr_array_header_t *rangelist,
-                          svn_revnum_t start,
-                          svn_revnum_t end,
-                          apr_pool_t *pool)
-{
-  return svn_rangelist_inheritable2(inheritable_rangelist, rangelist,
-                                    start, end, TRUE, pool, pool);
-}
-
 svn_boolean_t
 svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo,
                                        apr_pool_t *pool)
@@ -1657,14 +1663,8 @@ svn_mergeinfo__remove_empty_rangelists(s
     {
       for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
         {
-          const void *key;
-          void *value;
-          const char *path;
-          apr_array_header_t *rangelist;
-
-          apr_hash_this(hi, &key, NULL, &value);
-          path = key;
-          rangelist = value;
+          const char *path = svn__apr_hash_index_key(hi);
+          apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
 
           if (rangelist->nelts == 0)
             {
@@ -1689,15 +1689,12 @@ svn_mergeinfo__remove_prefix_from_catalo
 
   for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi))
     {
-      const void *key;
-      const char *original_path;
-      void *value;
-      apr_ssize_t klen;
+      const char *original_path = svn__apr_hash_index_key(hi);
+      apr_ssize_t klen = svn__apr_hash_index_klen(hi);
+      svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
 
-      apr_hash_this(hi, &key, &klen, &value);
-      original_path = key;
       SVN_ERR_ASSERT(klen >= prefix_len);
-      SVN_ERR_ASSERT(strncmp(key, prefix, prefix_len) == 0);
+      SVN_ERR_ASSERT(strncmp(original_path, prefix, prefix_len) == 0);
 
       apr_hash_set(*out_catalog, original_path + prefix_len,
                    klen-prefix_len, value);
@@ -1728,8 +1725,8 @@ svn_mergeinfo__add_suffix_to_mergeinfo(s
            hi;
            hi = apr_hash_next(hi))
         {
-          const char *path = svn_apr_hash_index_key(hi);
-          apr_array_header_t *rangelist = svn_apr_hash_index_val(hi);
+          const char *path = svn__apr_hash_index_key(hi);
+          apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
 
           apr_hash_set(*out_mergeinfo,
                        svn_dirent_join(path, canonical_suffix, result_pool),
@@ -1742,7 +1739,7 @@ svn_mergeinfo__add_suffix_to_mergeinfo(s
 }
 
 apr_array_header_t *
-svn_rangelist_dup(apr_array_header_t *rangelist, apr_pool_t *pool)
+svn_rangelist_dup(const apr_array_header_t *rangelist, apr_pool_t *pool)
 {
   apr_array_header_t *new_rl = apr_array_make(pool, rangelist->nelts,
                                               sizeof(svn_merge_range_t *));
@@ -1884,14 +1881,7 @@ svn_mergeinfo__get_range_endpoints(svn_r
 
       for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
         {
-          const void *key;
-          void *value;
-          const char *path;
-          apr_array_header_t *rangelist;
-
-          apr_hash_this(hi, &key, NULL, &value);
-          path = key;
-          rangelist = value;
+          apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
 
           if (rangelist->nelts)
             {
@@ -1928,14 +1918,10 @@ svn_mergeinfo__filter_catalog_by_ranges(
        hi;
        hi = apr_hash_next(hi))
     {
-      const void *key;
-      void *val;
-      const char *path;
-
-      svn_mergeinfo_t mergeinfo, filtered_mergeinfo;
-      apr_hash_this(hi, &key, NULL, &val);
-      path = key;
-      mergeinfo = val;
+      const char *path = svn__apr_hash_index_key(hi);
+      svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
+      svn_mergeinfo_t filtered_mergeinfo;
+
       SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo,
                                                         mergeinfo,
                                                         youngest_rev,
@@ -1975,26 +1961,16 @@ svn_mergeinfo__filter_mergeinfo_by_range
            hi;
            hi = apr_hash_next(hi))
         {
-          const void *key;
-          void *value;
-          const char *path;
-          apr_array_header_t *rangelist;
-
-          apr_hash_this(hi, &key, NULL, &value);
-          path = key;
-          rangelist = value;
+          const char *path = svn__apr_hash_index_key(hi);
+          apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
 
           if (rangelist->nelts)
             {
               apr_array_header_t *new_rangelist;
 
-              if (include_range)
-                SVN_ERR(svn_rangelist_intersect(&new_rangelist, rangelist,
-                                                filter_rangelist, FALSE,
-                                                result_pool));
-              else
-                SVN_ERR(svn_rangelist_remove(&new_rangelist, filter_rangelist,
-                                             rangelist, FALSE, result_pool));
+              SVN_ERR(rangelist_intersect_or_remove(
+                        &new_rangelist, filter_rangelist, rangelist,
+                        ! include_range, FALSE, result_pool));
 
               if (new_rangelist->nelts)
                 apr_hash_set(*filtered_mergeinfo,
@@ -2019,7 +1995,7 @@ svn_mergeinfo__is_noninheritable(svn_mer
            hi;
            hi = apr_hash_next(hi))
         {
-          apr_array_header_t *rangelist = svn_apr_hash_index_val(hi);
+          apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
           int i;
 
           for (i = 0; i < rangelist->nelts; i++)

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/opt.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/opt.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/opt.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/opt.c Tue Mar 30 20:57:53 2010
@@ -739,7 +739,7 @@ svn_opt_parse_path(svn_opt_revision_t *r
 svn_error_t *
 svn_opt__args_to_target_array(apr_array_header_t **targets_p,
                               apr_getopt_t *os,
-                              apr_array_header_t *known_targets,
+                              const apr_array_header_t *known_targets,
                               apr_pool_t *pool)
 {
   int i;
@@ -1067,7 +1067,7 @@ svn_opt_print_help3(apr_getopt_t *os,
 
 svn_error_t *
 svn_opt_eat_peg_revisions(apr_array_header_t **true_targets_p,
-                          apr_array_header_t *targets,
+                          const apr_array_header_t *targets,
                           apr_pool_t *pool)
 {
   int i;

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/prompt.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/prompt.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/prompt.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/prompt.c Tue Mar 30 20:57:53 2010
@@ -71,7 +71,7 @@ static apr_status_t wait_for_input(apr_f
    * ourselves here.
    */
   return APR_ENOTIMPL;
-#endif  
+#endif
 }
 
 /* Set @a *result to the result of prompting the user with @a

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/quoprint.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/quoprint.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/quoprint.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/quoprint.c Tue Mar 30 20:57:53 2010
@@ -37,24 +37,24 @@
 
 /* Caveats:
 
-	(1) This code is for the encoding and decoding of binary data
-	    only.  Thus, CRLF sequences are encoded as =0D=0A, and we
-	    don't have to worry about tabs and spaces coming before
-	    hard newlines, since there aren't any.
-
-	(2) The decoder does no error reporting, and instead throws
-	    away invalid sequences.  It also discards CRLF sequences,
-	    since those can only appear in the encoding of text data.
-
-	(3) The decoder does not strip whitespace at the end of a
-	    line, so it is not actually compliant with RFC 2045.
-	    (Such whitespace should never occur, even in the encoding
-	    of text data, but RFC 2045 requires a decoder to detect
-	    that a transport agent has added trailing whitespace).
-
-	(4) The encoder is tailored to make output embeddable in XML,
-	    which means it quotes <>'"& as well as the characters
-	    required by RFC 2045.  */
+        (1) This code is for the encoding and decoding of binary data
+            only.  Thus, CRLF sequences are encoded as =0D=0A, and we
+            don't have to worry about tabs and spaces coming before
+            hard newlines, since there aren't any.
+
+        (2) The decoder does no error reporting, and instead throws
+            away invalid sequences.  It also discards CRLF sequences,
+            since those can only appear in the encoding of text data.
+
+        (3) The decoder does not strip whitespace at the end of a
+            line, so it is not actually compliant with RFC 2045.
+            (Such whitespace should never occur, even in the encoding
+            of text data, but RFC 2045 requires a decoder to detect
+            that a transport agent has added trailing whitespace).
+
+        (4) The encoder is tailored to make output embeddable in XML,
+            which means it quotes <>'"& as well as the characters
+            required by RFC 2045.  */
 
 #define QUOPRINT_LINELEN 76
 #define VALID_LITERAL(c) ((c) == '\t' || ((c) >= ' ' && (c) <= '~' \

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/simple_providers.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/simple_providers.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/simple_providers.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/simple_providers.c Tue Mar 30 20:57:53 2010
@@ -202,7 +202,7 @@ svn_auth__simple_first_creds_helper(void
               else
                 need_to_save = TRUE;
             }
-	}
+        }
 
       /* See if we need to save this password if it is not present in
          auth cache. */

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/skel.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/skel.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/skel.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/skel.c Tue Mar 30 20:57:53 2010
@@ -609,6 +609,24 @@ void svn_skel__prepend_str(const char *v
   svn_skel__prepend(atom, skel);
 }
 
+
+void svn_skel__append(svn_skel_t *list_skel, const svn_skel_t *skel)
+{
+  SVN_ERR_ASSERT_NO_RETURN(list_skel != NULL && !list_skel->is_atom);
+
+  if (list_skel->children == NULL)
+    {
+      list_skel->children = (svn_skel_t *)skel;
+    }
+  else
+    {
+      list_skel = list_skel->children;
+      while (list_skel->next != NULL)
+        list_skel = list_skel->next;
+      list_skel->next = (svn_skel_t *)skel;
+    }
+}
+
 
 /* Examining skels.  */
 

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/sorts.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/sorts.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/sorts.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/sorts.c Tue Mar 30 20:57:53 2010
@@ -196,7 +196,7 @@ bsearch_lower_bound(const void *key,
 
 int
 svn_sort__bsearch_lower_bound(const void *key,
-                              apr_array_header_t *array,
+                              const apr_array_header_t *array,
                               int (*compare_func)(const void *, const void *))
 {
   return bsearch_lower_bound(key,

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/stream.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/stream.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/stream.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/stream.c Tue Mar 30 20:57:53 2010
@@ -456,7 +456,7 @@ svn_error_t *svn_stream_copy3(svn_stream
 
       err = svn_stream_read(from, buf, &len);
       if (err)
-      	 break;
+         break;
 
       if (len > 0)
         err = svn_stream_write(to, buf, &len);
@@ -676,35 +676,6 @@ read_handler_apr(void *baton, char *buff
   struct baton_apr *btn = baton;
   svn_error_t *err;
 
-  /* Check for range restriction. */
-  if (btn->start >= 0 && btn->end > 0)
-    {
-      /* Get the current file position and make sure it is in range. */
-      apr_off_t pos;
-
-      pos = 0;
-      SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &pos, btn->pool));
-      if (pos < btn->start)
-        {
-          /* We're before the range, so forward the file cursor to
-           * the start of the range. */
-          pos = btn->start;
-          SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &pos, btn->pool));
-        }
-      else if (pos >= btn->end)
-        {
-          /* We're past the range, indicate that no bytes can be read. */
-          *len = 0;
-          return SVN_NO_ERROR;
-        }
-      else
-        {
-          /* We're in range, but don't read over the end of the range. */
-          if (pos + *len > btn->end)
-              *len = btn->end - pos;
-        }
-    }
-
   err = svn_io_file_read_full(btn->file, buffer, *len, len, btn->pool);
   if (err && APR_STATUS_IS_EOF(err->apr_err))
     {
@@ -846,12 +817,47 @@ svn_stream_from_aprfile2(apr_file_t *fil
   return stream;
 }
 
-svn_stream_t *
-svn_stream_from_aprfile(apr_file_t *file, apr_pool_t *pool)
+/* A read handler (#svn_read_fn_t) that forwards to read_handler_apr()
+   but only allows reading between BATON->start and BATON->end. */
+static svn_error_t *
+read_range_handler_apr(void *baton, char *buffer, apr_size_t *len)
 {
-  return svn_stream_from_aprfile2(file, TRUE, pool);
+  struct baton_apr *btn = baton;
+
+  /* ### BH: I think this can be simplified/optimized by just keeping
+             track of the current position. */
+
+  /* Check for range restriction. */
+  if (btn->start >= 0 && btn->end > 0)
+    {
+      /* Get the current file position and make sure it is in range. */
+      apr_off_t pos;
+
+      pos = 0;
+      SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &pos, btn->pool));
+      if (pos < btn->start)
+        {
+          /* We're before the range, so forward the file cursor to
+           * the start of the range. */
+          pos = btn->start;
+          SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &pos, btn->pool));
+        }
+      else if (pos >= btn->end)
+        {
+          /* We're past the range, indicate that no bytes can be read. */
+          *len = 0;
+          return SVN_NO_ERROR;
+        }
+
+      /* We're in range, but don't read over the end of the range. */
+      if (pos + *len > btn->end)
+        *len = (apr_size_t)(btn->end - pos);
+    }
+
+  return read_handler_apr(baton, buffer, len);
 }
 
+
 svn_stream_t *
 svn_stream_from_aprfile_range_readonly(apr_file_t *file,
                                        svn_boolean_t disown,
@@ -863,6 +869,7 @@ svn_stream_from_aprfile_range_readonly(a
   svn_stream_t *stream;
   apr_off_t pos;
 
+  /* ### HACK: These shortcuts don't handle disown FALSE properly */
   if (file == NULL || start < 0 || end <= 0 || start >= end)
     return svn_stream_empty(pool);
 
@@ -877,7 +884,7 @@ svn_stream_from_aprfile_range_readonly(a
   baton->start = start;
   baton->end = end;
   stream = svn_stream_create(baton, pool);
-  svn_stream_set_read(stream, read_handler_apr);
+  svn_stream_set_read(stream, read_range_handler_apr);
   svn_stream_set_reset(stream, reset_handler_apr);
   svn_stream_set_mark(stream, mark_handler_apr);
   svn_stream_set_seek(stream, seek_handler_apr);

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/subst.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/subst.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/subst.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/subst.c Tue Mar 30 20:57:53 2010
@@ -769,9 +769,9 @@ struct translation_baton
   apr_hash_t *keywords;
   svn_boolean_t expand;
 
-  /* Characters (excluding the terminating NUL character) which
+  /* 'short boolean' array that encodes what character values
      may trigger a translation action, hence are 'interesting' */
-  const char *interesting;
+  char interesting[256];
 
   /* Length of the string EOL_STR points to. */
   apr_size_t eol_str_len;
@@ -821,11 +821,21 @@ create_translation_baton(const char *eol
   b->repair = repair;
   b->keywords = keywords;
   b->expand = expand;
-  b->interesting = (eol_str && keywords) ? "$\r\n" : eol_str ? "\r\n" : "$";
   b->newline_off = 0;
   b->keyword_off = 0;
   b->src_format_len = 0;
 
+  /* Most characters don't start translation actions.
+   * Mark those that do depending on the parameters we got. */
+  memset(b->interesting, FALSE, sizeof(b->interesting));
+  if (keywords)
+    b->interesting['$'] = TRUE;
+  if (eol_str)
+    {
+      b->interesting['\r'] = TRUE;
+      b->interesting['\n'] = TRUE;
+    }
+
   return b;
 }
 
@@ -938,14 +948,9 @@ translate_chunk(svn_stream_t *dst,
           len = 0;
 
           /* We wanted memcspn(), but lacking that, the loop below has
-             the same effect.
-
-             Also, skip NUL characters explicitly, since strchr()
-             considers them part of the string argument,
-             but we don't consider them interesting
+             the same effect. Also, skip NUL characters.
           */
-          while ((p + len) < end
-                 && (! p[len] || ! strchr(interesting, p[len])))
+          while ((p + len) < end && !interesting[(unsigned char)p[len]])
             len++;
 
           if (len)
@@ -1033,6 +1038,7 @@ struct translated_stream_baton
 };
 
 
+/* Implements svn_read_fn_t. */
 static svn_error_t *
 translated_stream_read(void *baton,
                        char *buffer,
@@ -1044,6 +1050,25 @@ translated_stream_read(void *baton,
   apr_size_t off = 0;
   apr_pool_t *iterpool;
 
+  /* Optimization for a frequent special case. The configuration parser (and
+     a few others) reads the stream one byte at a time. All the memcpy, pool
+     clearing etc. imposes a huge overhead in that case. In most cases, we
+     can just take that single byte directly from the read buffer.
+
+     Since *len > 1 requires lots of code to be run anyways, we can afford
+     the extra overhead of checking for *len == 1.
+
+     See <http://mail-archives.apache.org/mod_mbox/subversion-dev/201003.mbox/%3C4B94011E.1070207@alice-dsl.de%3E>.
+  */
+  if (unsatisfied == 1 && b->readbuf_off < b->readbuf->len)
+    {
+      /* Just take it from the read buffer */
+      *buffer = b->readbuf->data[b->readbuf_off++];
+
+      return SVN_NO_ERROR;
+    }
+
+  /* Standard code path. */
   iterpool = b->iterpool;
   while (readlen == SVN__STREAM_CHUNK_SIZE && unsatisfied > 0)
     {
@@ -1086,6 +1111,7 @@ translated_stream_read(void *baton,
   return SVN_NO_ERROR;
 }
 
+/* Implements svn_write_fn_t. */
 static svn_error_t *
 translated_stream_write(void *baton,
                         const char *buffer,
@@ -1098,6 +1124,7 @@ translated_stream_write(void *baton,
   return translate_chunk(b->stream, b->out_baton, buffer, *len, b->iterpool);
 }
 
+/* Implements svn_close_fn_t. */
 static svn_error_t *
 translated_stream_close(void *baton)
 {
@@ -1112,6 +1139,7 @@ translated_stream_close(void *baton)
   return SVN_NO_ERROR;
 }
 
+/* Implements svn_io_reset_fn_t. */
 static svn_error_t *
 translated_stream_reset(void *baton)
 {
@@ -1136,6 +1164,7 @@ translated_stream_reset(void *baton)
   return svn_error_return(err);
 }
 
+/* Implements svn_io_mark_fn_t. */
 static svn_error_t *
 translated_stream_mark(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
 {
@@ -1144,6 +1173,7 @@ translated_stream_mark(void *baton, svn_
   return svn_error_return(svn_stream_mark(b->stream, mark, pool));
 }
 
+/* Implements svn_io_seek_fn_t. */
 static svn_error_t *
 translated_stream_seek(void *baton, svn_stream_mark_t *mark)
 {

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_subr/svn_string.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_subr/svn_string.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_subr/svn_string.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_subr/svn_string.c Tue Mar 30 20:57:53 2010
@@ -536,7 +536,7 @@ svn_cstring_split(const char *input,
 
 
 svn_boolean_t svn_cstring_match_glob_list(const char *str,
-                                          apr_array_header_t *list)
+                                          const apr_array_header_t *list)
 {
   int i;
 

Modified: subversion/branches/svn-patch-improvements/subversion/libsvn_wc/adm_crawler.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-patch-improvements/subversion/libsvn_wc/adm_crawler.c?rev=929279&r1=929278&r2=929279&view=diff
==============================================================================
--- subversion/branches/svn-patch-improvements/subversion/libsvn_wc/adm_crawler.c (original)
+++ subversion/branches/svn-patch-improvements/subversion/libsvn_wc/adm_crawler.c Tue Mar 30 20:57:53 2010
@@ -74,6 +74,9 @@ restore_file(svn_wc__db_t *db,
 
   SVN_ERR(svn_wc__get_pristine_contents(&src_stream, db, local_abspath, pool,
                                         pool));
+  if (src_stream == NULL)
+    /* Nothing to restore. */
+    return SVN_NO_ERROR;
 
   SVN_ERR(svn_wc__get_special(&special, db, local_abspath, pool));
   if (special)
@@ -310,12 +313,12 @@ report_revisions_and_depths(svn_wc__db_t
                             svn_boolean_t depth_compatibility_trick,
                             svn_boolean_t report_everything,
                             svn_boolean_t use_commit_times,
-                            apr_pool_t *pool)
+                            apr_pool_t *scratch_pool)
 {
   const char *dir_abspath;
   const apr_array_header_t *children;
   apr_hash_t *dirents;
-  apr_pool_t *subpool = svn_pool_create(pool), *iterpool;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   int i;
   const char *dir_repos_root, *dir_repos_relpath;
   svn_depth_t dir_depth;
@@ -324,10 +327,10 @@ report_revisions_and_depths(svn_wc__db_t
   /* Get both the SVN Entries and the actual on-disk entries.   Also
      notice that we're picking up hidden entries too (read_children never
      hides children). */
-  dir_abspath = svn_dirent_join(anchor_abspath, dir_path, subpool);
-  SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath,
-                                   subpool, subpool));
-  SVN_ERR(svn_io_get_dir_filenames(&dirents, dir_abspath, subpool));
+  dir_abspath = svn_dirent_join(anchor_abspath, dir_path, scratch_pool);
+  SVN_ERR(svn_wc__db_base_get_children(&children, db, dir_abspath,
+                                       scratch_pool, iterpool));
+  SVN_ERR(svn_io_get_dir_filenames(&dirents, dir_abspath, scratch_pool));
 
   /*** Do the real reporting and recursing. ***/
 
@@ -338,38 +341,35 @@ report_revisions_and_depths(svn_wc__db_t
                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                                NULL,
                                db, dir_abspath,
-                               subpool, subpool));
+                               scratch_pool, iterpool));
 
   /* If the directory has no url, search its parents */
   if (dir_repos_relpath == NULL)
     SVN_ERR(svn_wc__db_scan_base_repos(&dir_repos_relpath, &dir_repos_root,
                                        NULL, db, dir_abspath,
-                                       subpool, subpool));
+                                       scratch_pool, iterpool));
 
   /* If "this dir" has "svn:externals" property set on it, store its name
      and depth in traversal_info. */
   if (external_func)
     {
       SVN_ERR(read_traversal_info(db, dir_abspath, external_func,
-                                  external_baton, dir_depth, subpool));
+                                  external_baton, dir_depth, iterpool));
     }
 
   /* Looping over current directory's SVN entries: */
-  iterpool = svn_pool_create(subpool);
-
   for (i = 0; i < children->nelts; ++i)
     {
       const char *child = APR_ARRAY_IDX(children, i, const char *);
       const char *this_path, *this_abspath;
       const char *this_repos_root_url, *this_repos_relpath;
-      const char *this_original_repos_relpath;
       svn_wc__db_status_t this_status;
       svn_wc__db_kind_t this_kind;
-      svn_revnum_t this_rev, this_base_rev;
+      svn_revnum_t this_rev;
       svn_depth_t this_depth;
       svn_wc__db_lock_t *this_lock;
-      svn_boolean_t this_shadows_base, this_switched, replaced = FALSE;
-
+      svn_boolean_t this_switched;
+      svn_error_t *err;
 
       /* Clear the iteration subpool here because the loop has a bunch
          of 'continue' jump statements. */
@@ -379,15 +379,36 @@ report_revisions_and_depths(svn_wc__db_t
       this_path = svn_dirent_join(dir_path, child, iterpool);
       this_abspath = svn_dirent_join(dir_abspath, child, iterpool);
 
-      SVN_ERR(svn_wc__db_read_info(&this_status, &this_kind, &this_rev,
-                                   &this_repos_relpath, &this_repos_root_url,
-                                   NULL, NULL, NULL, NULL, NULL, &this_depth,
-                                   NULL, NULL, NULL, NULL,
-                                   &this_original_repos_relpath,
-                                   NULL, NULL, NULL, NULL, NULL,
-                                   &this_shadows_base, NULL,
-                                   &this_lock,
-                                   db, this_abspath, iterpool, iterpool));
+      err = svn_wc__db_base_get_info(&this_status, &this_kind, &this_rev,
+                                     &this_repos_relpath, &this_repos_root_url,
+                                     NULL, NULL, NULL, NULL, NULL, &this_depth,
+                                     NULL, NULL, NULL, &this_lock,
+                                     db, this_abspath, iterpool, iterpool);
+
+      if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+        svn_error_clear(err);
+      else
+        SVN_ERR(err);
+
+      /* ### While still using parent stubs: Fetch info from the stub.
+             Handles the error case above */
+      {
+        svn_boolean_t not_present;
+
+        SVN_ERR(svn_wc__db_temp_is_dir_deleted(&not_present, &this_rev, db,
+                                               this_abspath, iterpool));
+
+        if (not_present || err)
+          this_status = svn_wc__db_status_not_present;
+
+        if (!SVN_IS_VALID_REVNUM(this_rev))
+          this_rev = dir_rev; /* Obstructed node */
+      } /* /Stub handling */
+
+      if (this_depth == svn_depth_unknown)
+        this_depth = svn_depth_infinity;
+
+      SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(this_rev));
 
       /* First check for exclusion */
       if (this_status == svn_wc__db_status_excluded)
@@ -422,75 +443,38 @@ report_revisions_and_depths(svn_wc__db_t
           continue;
         }
 
-      if (this_kind == svn_wc__db_kind_dir)
-        {
-          svn_revnum_t del_rev;
-          SVN_ERR(svn_wc__db_temp_is_dir_deleted(&replaced, &del_rev,
-                                                 db, this_abspath,
-                                                 iterpool));
-        }
-
       /*** The Big Tests: ***/
-
-      if (this_shadows_base)
+      if (this_status == svn_wc__db_status_absent ||
+          this_status == svn_wc__db_status_excluded ||
+          this_status == svn_wc__db_status_not_present)
         {
-          svn_wc__db_status_t this_base_status;
-          SVN_ERR(svn_wc__db_base_get_info(&this_base_status, NULL,
-                                           &this_base_rev,
-                                           NULL, NULL, NULL, NULL, NULL,
-                                           NULL, NULL, NULL, NULL, NULL,
-                                           NULL, NULL,
-                                           db, this_abspath,
-                                           iterpool, iterpool));
-
-          if (!replaced)
-            replaced = (this_base_status == svn_wc__db_status_not_present);
-        }
-
-      {
-        svn_boolean_t this_absent;
-
-        if (replaced ||
-            this_status == svn_wc__db_status_absent ||
-            this_status == svn_wc__db_status_excluded ||
-            this_status == svn_wc__db_status_not_present)
-          {
-            this_absent = TRUE;
-          }
-        else if (this_status == svn_wc__db_status_deleted && !this_shadows_base)
-          this_absent = TRUE;
-        else
-          this_absent = FALSE;
-
-        /* If the entry is 'deleted' or 'absent', make sure the server
-           knows it's gone... */
-        if (this_absent)
-          {
-            /* ...unless we're reporting everything, in which case we're
-               going to report it missing later anyway. */
-            if (! report_everything)
-              SVN_ERR(reporter->delete_path(report_baton, this_path, iterpool));
-            continue;
-          }
-      }
-
-      /* From here on out, ignore any entry scheduled for addition */
-      if ((this_status == svn_wc__db_status_added) ||
-          (this_status == svn_wc__db_status_obstructed_add))
-      {
-        if (!replaced)
+          /* If the entry is 'deleted' or 'absent', make sure the server
+             knows it's gone...
+             ...unless we're reporting everything, in which case we're
+             going to report it missing later anyway. */
+          if (! report_everything)
+            SVN_ERR(reporter->delete_path(report_baton, this_path, iterpool));
           continue;
-
-        if (!this_shadows_base && this_original_repos_relpath)
-          continue; /* Skip copy roots (and all children) */
-      }
+        }
 
       /* Is the entry on disk? */
       if (apr_hash_get(dirents, child, APR_HASH_KEY_STRING) == NULL)
         {
           svn_boolean_t missing = FALSE;
-          if (restore_files && this_status != svn_wc__db_status_deleted
-                            && !replaced)
+          svn_wc__db_status_t wrk_status;
+          svn_wc__db_kind_t wrk_kind;
+
+          SVN_ERR(svn_wc__db_read_info(&wrk_status, &wrk_kind, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL,
+                                       db, this_abspath, iterpool, iterpool));
+
+          if (restore_files && wrk_status != svn_wc__db_status_added
+              && wrk_status != svn_wc__db_status_deleted
+              && wrk_status != svn_wc__db_status_obstructed_add
+              && wrk_status != svn_wc__db_status_obstructed_delete)
             {
               svn_node_kind_t dirent_kind;
 
@@ -503,7 +487,7 @@ report_revisions_and_depths(svn_wc__db_t
               if (dirent_kind == svn_node_none)
                 {
                   svn_boolean_t restored;
-                  SVN_ERR(restore_node(&restored, db, this_abspath, this_kind,
+                  SVN_ERR(restore_node(&restored, db, this_abspath, wrk_kind,
                                        use_commit_times, notify_func,
                                        notify_baton, iterpool));
 
@@ -514,11 +498,11 @@ report_revisions_and_depths(svn_wc__db_t
           else
             missing = TRUE;
 
-          /* If a directory is missing from disk, we have no way to
-             recreate it locally, so report as missing and move
-             along.  Again, don't bother if we're reporting
-             everything, because the dir is already missing on the server. */
-          if (missing && this_kind == svn_wc__db_kind_dir
+          /* If a node is still missing from disk here, we have no way to recreate
+             it locally, so report as missing and move along.  Again, don't bother
+             if we're reporting everything, because the dir is already missing on
+             the server. */
+          if (missing && wrk_kind == svn_wc__db_kind_dir
                && (depth > svn_depth_files || depth == svn_depth_unknown))
             {
               if (! report_everything)
@@ -546,32 +530,19 @@ report_revisions_and_depths(svn_wc__db_t
             this_switched = FALSE;
         }
 
-      if (this_depth == svn_depth_unknown)
-        this_depth = svn_depth_infinity;
-
-      if (this_rev == SVN_INVALID_REVNUM)
-        {
-          /* For added and replaced nodes use their base revision
-             in reports */
-          this_rev = this_shadows_base ? this_base_rev : dir_rev;
-        }
-
       /*** Files ***/
       if (this_kind == svn_wc__db_kind_file ||
           this_kind == svn_wc__db_kind_symlink)
         {
-          const char *url = NULL;
-
-          if (this_switched)
-            url = svn_path_url_add_component2(dir_repos_root, this_repos_relpath, iterpool);
-
           if (report_everything)
             {
               /* Report the file unconditionally, one way or another. */
               if (this_switched)
                 SVN_ERR(reporter->link_path(report_baton,
                                             this_path,
-                                            url,
+                                            svn_path_url_add_component2(
+                                                dir_repos_root,
+                                                this_repos_relpath, iterpool),
                                             this_rev,
                                             this_depth,
                                             FALSE,
@@ -588,10 +559,12 @@ report_revisions_and_depths(svn_wc__db_t
             }
 
           /* Possibly report a disjoint URL ... */
-          else if (this_switched && !this_shadows_base)
+          else if (this_switched)
             SVN_ERR(reporter->link_path(report_baton,
                                         this_path,
-                                        url,
+                                        svn_path_url_add_component2(
+                                                dir_repos_root,
+                                                this_repos_relpath, iterpool),
                                         this_rev,
                                         this_depth,
                                         FALSE,
@@ -616,14 +589,8 @@ report_revisions_and_depths(svn_wc__db_t
                && (depth > svn_depth_files
                    || depth == svn_depth_unknown))
         {
-          const char *url = NULL;
-          svn_boolean_t start_empty;
           svn_boolean_t is_incomplete = (this_status == svn_wc__db_status_incomplete);
-
-          if (this_switched)
-            url = svn_path_url_add_component2(dir_repos_root, this_repos_relpath, iterpool);
-
-          start_empty = is_incomplete;
+          svn_boolean_t start_empty = is_incomplete;
 
           if (depth_compatibility_trick
               && this_depth <= svn_depth_files
@@ -638,11 +605,14 @@ report_revisions_and_depths(svn_wc__db_t
               if (this_switched)
                 SVN_ERR(reporter->link_path(report_baton,
                                             this_path,
-                                            url,
+                                            svn_path_url_add_component2(
+                                                dir_repos_root,
+                                                this_repos_relpath, iterpool),
                                             this_rev,
                                             this_depth,
                                             start_empty,
-                                            this_lock ? this_lock->token : NULL,
+                                            this_lock ? this_lock->token
+                                                      : NULL,
                                             iterpool));
               else
                 SVN_ERR(reporter->set_path(report_baton,
@@ -658,7 +628,9 @@ report_revisions_and_depths(svn_wc__db_t
           else if (this_switched)
             SVN_ERR(reporter->link_path(report_baton,
                                         this_path,
-                                        url,
+                                        svn_path_url_add_component2(
+                                                dir_repos_root,
+                                                this_repos_relpath, iterpool),
                                         this_rev,
                                         this_depth,
                                         start_empty,
@@ -704,7 +676,7 @@ report_revisions_and_depths(svn_wc__db_t
     } /* end main entries loop */
 
   /* We're done examining this dir's entries, so free everything. */
-  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
@@ -788,106 +760,133 @@ svn_wc_crawl_revisions5(svn_wc_context_t
                         void *external_baton,
                         svn_wc_notify_func2_t notify_func,
                         void *notify_baton,
-                        apr_pool_t *pool)
+                        apr_pool_t *scratch_pool)
 {
   svn_wc__db_t *db = wc_ctx->db;
   svn_error_t *fserr, *err;
-  svn_revnum_t base_rev = SVN_INVALID_REVNUM, target_rev;
+  svn_revnum_t target_rev = SVN_INVALID_REVNUM;
   svn_boolean_t missing = FALSE;
   svn_boolean_t start_empty;
   svn_wc__db_status_t status;
-  svn_wc__db_kind_t target_kind;
-  const char *repos_relpath, *repos_root_url;
-  svn_depth_t target_depth;
-  svn_boolean_t replaced = FALSE;
-  svn_wc__db_lock_t *target_lock;
-  svn_boolean_t target_exists = TRUE;
+  svn_wc__db_kind_t target_kind = svn_wc__db_kind_unknown;
+  const char *repos_relpath=NULL, *repos_root=NULL;
+  svn_depth_t target_depth = svn_depth_unknown;
+  svn_wc__db_lock_t *target_lock = NULL;
+  svn_boolean_t explicit_rev;
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
   /* The first thing we do is get the base_rev from the working copy's
      ROOT_DIRECTORY.  This is the first revnum that entries will be
      compared to. */
-  err = svn_wc__db_read_info(&status, &target_kind, &target_rev,
-                             &repos_relpath, &repos_root_url,
-                             NULL, NULL, NULL, NULL, NULL,
-                             &target_depth, NULL, NULL, NULL, NULL, NULL,
-                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                             &target_lock,
-                             db, local_abspath, pool, pool);
+  err = svn_wc__db_base_get_info(&status, &target_kind, &target_rev,
+                                 &repos_relpath, &repos_root,
+                                 NULL, NULL, NULL, NULL, NULL,
+                                 &target_depth, NULL, NULL, NULL,
+                                 &target_lock,
+                                 db, local_abspath, scratch_pool,
+                                 scratch_pool);
 
-  if (err)
-    {
-      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
-        return svn_error_return(err);
+  {
+    svn_boolean_t has_base = TRUE;
 
-      svn_error_clear(err);
-      target_exists = FALSE;
+    if (err)
+      {
+        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+          return svn_error_return(err);
 
-      /* Set output values of svn_wc__db_read_info to their empty values */
-      status = svn_wc__db_status_not_present;
-      target_kind = svn_wc__db_kind_unknown;
-      target_rev = -1;
-      repos_relpath = NULL;
-      repos_root_url = NULL;
-      target_depth = svn_depth_unknown;
-      target_lock = NULL;
-    }
+        svn_error_clear(err);
+        has_base = FALSE;
+        SVN_ERR(svn_wc__db_read_kind(&target_kind, db, local_abspath, TRUE,
+                                     scratch_pool));
 
-  if (status == svn_wc__db_status_added)
-    SVN_ERR(svn_wc__internal_is_replaced(&replaced, db, local_abspath, pool));
+        if (target_kind == svn_wc__db_kind_file
+            || target_kind == svn_wc__db_kind_symlink)
+          status = svn_wc__db_status_absent; /* Crawl via parent dir */
+        else
+          status = svn_wc__db_status_not_present; /* As checkout */
+      }
 
-  if (!target_exists ||
-      (status == svn_wc__db_status_not_present) ||
-      (target_kind == svn_wc__db_kind_dir && !replaced &&
-           (status == svn_wc__db_status_added ||
-            status == svn_wc__db_status_absent ||
-            status == svn_wc__db_status_excluded ||
-            status == svn_wc__db_status_obstructed ||
-            status == svn_wc__db_status_obstructed_add ||
-            status == svn_wc__db_status_obstructed_delete)))
-    {
-      /* Don't check the exclude flag for the target.
-
-       If we report the target itself as excluded, the server will
-       send us nothing about the target -- but we want to permit
-       targets to be explicitly pulled in.  For example, 'svn up A'
-       should always work, even if its parent is svn_depth_empty or
-       svn_depth_files, or even if A was explicitly excluded from a
-       parent at svn_depth_immediates or svn_depth_infinity.
-       Whatever the case, we want A back now. */
+    /* ### Check the parentstub if we don't find a BASE. But don't
+           do this if we already have the info we want or we break
+           some copy scenarios. */
+    if (!has_base && target_kind == svn_wc__db_kind_dir)
+      {
+        svn_boolean_t not_present;
+        svn_revnum_t rev = SVN_INVALID_REVNUM;
+        err = svn_wc__db_temp_is_dir_deleted(&not_present, &rev,
+                                             db, local_abspath, scratch_pool);
 
-      if (!SVN_IS_VALID_REVNUM(base_rev))
-        {
-          const char *dir_abspath = svn_dirent_dirname(local_abspath, pool);
+        if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
+                    || err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
+          {
+            svn_error_clear(err);
+            not_present = FALSE;
+          }
+        else
+          SVN_ERR(err);
 
-          SVN_ERR(find_base_rev(&base_rev, db, dir_abspath, dir_abspath,
-                                pool));
-        }
+        if (not_present)
+          status = svn_wc__db_status_not_present;
+
+        if (!SVN_IS_VALID_REVNUM(target_rev))
+          target_rev = rev;
+      }
+  }
+
+  if ((status == svn_wc__db_status_not_present)
+      || (target_kind == svn_wc__db_kind_dir
+          && status != svn_wc__db_status_normal
+          && status != svn_wc__db_status_incomplete))
+    {
+      /* The target does not exist or is a local addition */
 
-      /* There aren't any versioned paths to crawl which are known to
-         the repository. */
+      if (!SVN_IS_VALID_REVNUM(target_rev))
+        target_rev = 0;
 
-      /* If no versioned path exists, we use the requested depth, which
-         is the depth at which the new path should be brought in.  Default
-         to infinity if no explicit depth was given. */
       if (depth == svn_depth_unknown)
         depth = svn_depth_infinity;
 
-      SVN_ERR(reporter->set_path(report_baton, "", base_rev, depth,
-                                 status == svn_wc__db_status_incomplete,
-                                 target_lock ? target_lock->token : NULL,
-                                 pool));
-      SVN_ERR(reporter->delete_path(report_baton, "", pool));
+      SVN_ERR(reporter->set_path(report_baton, "", target_rev, depth,
+                                 FALSE,
+                                 NULL,
+                                 scratch_pool));
+      SVN_ERR(reporter->delete_path(report_baton, "", scratch_pool));
 
       /* Finish the report, which causes the update editor to be
          driven. */
-      return reporter->finish_report(report_baton, pool);
+      SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
+
+      return SVN_NO_ERROR;
     }
 
-  base_rev = target_rev;
+  if (!repos_root || !repos_relpath)
+    {
+      err = svn_wc__db_scan_base_repos(&repos_relpath, &repos_root, NULL,
+                                      db, local_abspath,
+                                      scratch_pool, scratch_pool);
+
+      if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+        svn_error_clear(err);
+      else
+        SVN_ERR(err);
 
-  if (!SVN_IS_VALID_REVNUM(base_rev))
-    SVN_ERR(find_base_rev(&base_rev, db, local_abspath, local_abspath, pool));
+      /* Ok, that leaves a local addition. Deleted and not existing nodes
+         are already handled. */
+      if (!repos_root || !repos_relpath)
+        SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &repos_relpath,
+                                         &repos_root, NULL, NULL, NULL, NULL,
+                                         NULL, db, local_abspath,
+                                         scratch_pool, scratch_pool));
+    }
+
+  if (!SVN_IS_VALID_REVNUM(target_rev))
+    {
+      SVN_ERR(find_base_rev(&target_rev, db, local_abspath, local_abspath,
+                            scratch_pool));
+      explicit_rev = TRUE;
+    }
+  else
+    explicit_rev = FALSE;
 
   start_empty = (status == svn_wc__db_status_incomplete);
   if (depth_compatibility_trick
@@ -903,13 +902,13 @@ svn_wc_crawl_revisions5(svn_wc_context_t
   /* The first call to the reporter merely informs it that the
      top-level directory being updated is at BASE_REV.  Its PATH
      argument is ignored. */
-  SVN_ERR(reporter->set_path(report_baton, "", base_rev, target_depth,
-                             start_empty, NULL, pool));
+  SVN_ERR(reporter->set_path(report_baton, "", target_rev, target_depth,
+                             start_empty, NULL, scratch_pool));
 
   if (status != svn_wc__db_status_deleted)
     {
       apr_finfo_t info;
-      err = svn_io_stat(&info, local_abspath, APR_FINFO_MIN, pool);
+      err = svn_io_stat(&info, local_abspath, APR_FINFO_MIN, scratch_pool);
       if (err)
         {
           if (APR_STATUS_IS_ENOENT(err->apr_err))
@@ -926,7 +925,7 @@ svn_wc_crawl_revisions5(svn_wc_context_t
       err = restore_node(&restored, wc_ctx->db, local_abspath,
                          target_kind, use_commit_times,
                          notify_func, notify_baton,
-                         pool);
+                         scratch_pool);
 
       if (err)
           goto abort_report;
@@ -941,7 +940,7 @@ svn_wc_crawl_revisions5(svn_wc_context_t
         {
           /* Report missing directories as deleted to retrieve them
              from the repository. */
-          err = reporter->delete_path(report_baton, "", pool);
+          err = reporter->delete_path(report_baton, "", scratch_pool);
           if (err)
             goto abort_report;
         }
@@ -952,7 +951,7 @@ svn_wc_crawl_revisions5(svn_wc_context_t
           err = report_revisions_and_depths(wc_ctx->db,
                                             local_abspath,
                                             "",
-                                            base_rev,
+                                            target_rev,
                                             reporter, report_baton,
                                             external_func, external_baton,
                                             notify_func, notify_baton,
@@ -961,7 +960,7 @@ svn_wc_crawl_revisions5(svn_wc_context_t
                                             depth_compatibility_trick,
                                             start_empty,
                                             use_commit_times,
-                                            pool);
+                                            scratch_pool);
           if (err)
             goto abort_report;
         }
@@ -971,93 +970,82 @@ svn_wc_crawl_revisions5(svn_wc_context_t
            target_kind == svn_wc__db_kind_symlink)
     {
       svn_boolean_t skip_set_path  = FALSE;
-
-      if (repos_relpath)
-        {
-          const char *parent_abspath, *base;
-          svn_wc__db_status_t parent_status;
-          const char *parent_repos_relpath;
-
-          svn_dirent_split(local_abspath, &parent_abspath, &base, pool);
-
-          /* We can assume a file is in the same repository as its parent
-             directory, so we only look at the relpath. */
-          err = svn_wc__db_read_info(&parent_status, NULL, NULL,
+      const char *parent_abspath, *base;
+      svn_wc__db_status_t parent_status;
+      const char *parent_repos_relpath;
+
+      svn_dirent_split(local_abspath, &parent_abspath, &base,
+                       scratch_pool);
+
+      /* We can assume a file is in the same repository as its parent
+         directory, so we only look at the relpath. */
+      err = svn_wc__db_base_get_info(&parent_status, NULL, NULL,
                                      &parent_repos_relpath, NULL, NULL, NULL,
                                      NULL, NULL, NULL, NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL, NULL,
-                                     db, parent_abspath, pool, pool);
+                                     NULL, NULL,
+                                     db, parent_abspath,
+                                     scratch_pool, scratch_pool);
 
-          if (err)
-            goto abort_report;
+      if (err)
+        goto abort_report;
 
-          if (!parent_repos_relpath &&
-              parent_status == svn_wc__db_status_added)
-            {
-              err = svn_wc__db_scan_addition(NULL, NULL,
-                                             &parent_repos_relpath, NULL,
-                                             NULL, NULL, NULL, NULL, NULL,
-                                             db, parent_abspath,
-                                             pool, pool);
-            }
-          else if (!parent_repos_relpath)
-            err = svn_wc__db_scan_base_repos(&parent_repos_relpath, NULL,
-                                             NULL,
-                                             db, parent_abspath,
-                                             pool, pool);
+      if (!parent_repos_relpath)
+        err = svn_wc__db_scan_base_repos(&parent_repos_relpath, NULL,
+                                         NULL,
+                                         db, parent_abspath,
+                                         scratch_pool, scratch_pool);
+
+      if (err)
+        goto abort_report;
 
+      if (strcmp(repos_relpath,
+                 svn_relpath_join(parent_repos_relpath, base,
+                                  scratch_pool)) != 0)
+        {
+          /* This file is disjoint with respect to its parent
+             directory.  Since we are looking at the actual target of
+             the report (not some file in a subdirectory of a target
+             directory), and that target is a file, we need to pass an
+             empty string to link_path. */
+          err = reporter->link_path(report_baton,
+                                    "",
+                                    svn_path_url_add_component2(
+                                                    repos_root,
+                                                    repos_relpath,
+                                                    scratch_pool),
+                                    target_rev,
+                                    target_depth,
+                                    FALSE,
+                                    target_lock ? target_lock->token : NULL,
+                                    scratch_pool);
           if (err)
             goto abort_report;
-
-          if (strcmp(repos_relpath,
-                     svn_relpath_join(parent_repos_relpath, base, pool)) != 0)
-            {
-              /* This file is disjoint with respect to its parent
-                 directory.  Since we are looking at the actual target of
-                 the report (not some file in a subdirectory of a target
-                 directory), and that target is a file, we need to pass an
-                 empty string to link_path. */
-              err = reporter->link_path(report_baton,
-                                        "",
-                                        svn_path_url_add_component2(
-                                                        repos_root_url,
-                                                        repos_relpath,
-                                                        pool),
-                                        base_rev,
-                                        target_depth,
-                                        FALSE,
-                                        target_lock ? target_lock->token : NULL,
-                                        pool);
-              if (err)
-                goto abort_report;
-              skip_set_path = TRUE;
-            }
+          skip_set_path = TRUE;
         }
 
-      if (!skip_set_path && (target_rev != base_rev || target_lock))
+      if (!skip_set_path && (explicit_rev || target_lock))
         {
           /* If this entry is a file node, we just want to report that
              node's revision.  Since we are looking at the actual target
              of the report (not some file in a subdirectory of a target
              directory), and that target is a file, we need to pass an
              empty string to set_path. */
-          err = reporter->set_path(report_baton, "", base_rev,
+          err = reporter->set_path(report_baton, "", target_rev,
                                    target_depth,
                                    FALSE,
                                    target_lock ? target_lock->token : NULL,
-                                   pool);
+                                   scratch_pool);
           if (err)
             goto abort_report;
         }
     }
 
   /* Finish the report, which causes the update editor to be driven. */
-  return reporter->finish_report(report_baton, pool);
+  return reporter->finish_report(report_baton, scratch_pool);
 
  abort_report:
   /* Clean up the fs transaction. */
-  if ((fserr = reporter->abort_report(report_baton, pool)))
+  if ((fserr = reporter->abort_report(report_baton, scratch_pool)))
     {
       fserr = svn_error_quick_wrap(fserr, _("Error aborting report"));
       svn_error_compose(err, fserr);
@@ -1082,6 +1070,7 @@ struct copying_stream_baton
 };
 
 
+/* */
 static svn_error_t *
 read_handler_copy(void *baton, char *buffer, apr_size_t *len)
 {
@@ -1092,6 +1081,7 @@ read_handler_copy(void *baton, char *buf
   return svn_stream_write(btn->target, buffer, len);
 }
 
+/* */
 static svn_error_t *
 close_handler_copy(void *baton)
 {
@@ -1152,8 +1142,9 @@ svn_wc__internal_transmit_text_deltas(co
                                              SVN_WC_TRANSLATE_TO_NF,
                                              scratch_pool, scratch_pool));
 
-  /* Alert the caller that we have created a temporary file that might
-     need to be cleaned up, if he asked for one. */
+  /* If the caller wants a copy of the working file translated to
+   * repository-normal form, make the copy by tee-ing the stream and set
+   * *TEMPFILE to the path to it. */
   if (tempfile)
     {
       const char *tmp_base;
@@ -1188,6 +1179,8 @@ svn_wc__internal_transmit_text_deltas(co
       /* Compute delta against the pristine contents */
       SVN_ERR(svn_wc__get_pristine_contents(&base_stream, db, local_abspath,
                                             scratch_pool, scratch_pool));
+      if (base_stream == NULL)
+        base_stream = svn_stream_empty(scratch_pool);
 
       SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL,
                                    NULL, NULL, NULL,
@@ -1218,7 +1211,10 @@ svn_wc__internal_transmit_text_deltas(co
 
           /* ### we should ALREADY have the checksum for pristine. */
           SVN_ERR(svn_wc__get_pristine_contents(&p_stream, db, local_abspath,
-                                               scratch_pool, scratch_pool));
+                                                scratch_pool, scratch_pool));
+          if (p_stream == NULL)
+            p_stream = svn_stream_empty(scratch_pool);
+
           p_stream = svn_stream_checksummed2(p_stream, &p_checksum,
                                              NULL, svn_checksum_md5, TRUE,
                                              scratch_pool);