You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pb...@apache.org on 2010/03/16 19:03:41 UTC

svn commit: r923910 [4/9] - in /subversion/branches/1.6.x-issue3432: ./ build/ build/ac-macros/ build/generator/ contrib/cgi/ contrib/client-side/emacs/ contrib/client-side/svn_load_dirs/ contrib/hook-scripts/ contrib/server-side/ doc/user/ notes/ pack...

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/mergeinfo.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/mergeinfo.c Tue Mar 16 18:03:37 2010
@@ -45,7 +45,8 @@
    http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
 */
 static svn_boolean_t
-combine_ranges(svn_merge_range_t **output, svn_merge_range_t *in1,
+combine_ranges(svn_merge_range_t *output,
+               svn_merge_range_t *in1,
                svn_merge_range_t *in2,
                svn_boolean_t consider_inheritance)
 {
@@ -55,9 +56,9 @@ combine_ranges(svn_merge_range_t **outpu
           || (consider_inheritance
               && (in1->inheritable == in2->inheritable)))
         {
-          (*output)->start = MIN(in1->start, in2->start);
-          (*output)->end = MAX(in1->end, in2->end);
-          (*output)->inheritable = (in1->inheritable || in2->inheritable);
+          output->start = MIN(in1->start, in2->start);
+          output->end = MAX(in1->end, in2->end);
+          output->inheritable = (in1->inheritable || in2->inheritable);
           return TRUE;
         }
     }
@@ -66,8 +67,10 @@ combine_ranges(svn_merge_range_t **outpu
 
 /* pathname -> PATHNAME */
 static svn_error_t *
-parse_pathname(const char **input, const char *end,
-               svn_stringbuf_t **pathname, apr_pool_t *pool)
+parse_pathname(const char **input,
+               const char *end,
+               svn_stringbuf_t **pathname,
+               apr_pool_t *pool)
 {
   const char *curr = *input;
   const char *last_colon = NULL;
@@ -89,257 +92,341 @@ parse_pathname(const char **input, const
     return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
                             _("No pathname preceding ':'"));
 
-  *pathname = svn_stringbuf_ncreate(*input, last_colon - *input, pool);
+  /* Tolerate relative repository paths, but convert them to absolute. */
+  if (**input == '/')
+    {
+      *pathname = svn_stringbuf_ncreate(*input, last_colon - *input, pool);
+    }
+  else
+    {
+      const char *repos_rel_path = apr_pstrndup(pool, *input,
+                                                last_colon - *input);
+      *pathname = svn_stringbuf_createf(pool, "/%s",  repos_rel_path);
+    }
+
   *input = last_colon;
 
   return SVN_NO_ERROR;
 }
 
+/* Ways in which two svn_merge_range_t can intersect, if at all. */
+typedef enum
+{
+  /* Ranges don't intersect. */
+  svn__no_intersection,
+
+  /* Ranges are equal. */
+  svn__equal_intersection,
+
+  /* Ranges adjoin but don't overlap. */
+  svn__adjoining_intersection,
+
+  /* Ranges overalp but neither is a subset of the other. */
+  svn__overlapping_intersection,
+
+  /* One range is a proper subset of the other. */
+  svn__proper_subset_intersection
+} intersection_type_t;
+
+/* Given ranges R1 and R2, both of which must be forward merge ranges,
+   set *INTERSECTION_TYPE to describe how the ranges intersect, if they
+   do at all.  The inheritance type of the ranges is not considered. */
+static svn_error_t *
+get_type_of_intersection(svn_merge_range_t *r1,
+                         svn_merge_range_t *r2,
+                         intersection_type_t *intersection_type)
+{
+  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);
+
+  if (!(r1->start <= r2->end && r2->start <= r1->end))
+    *intersection_type = svn__no_intersection;
+  else if (r1->start == r2->start && r1->end == r2->end)
+    *intersection_type = svn__equal_intersection;
+  else if (r1->end == r2->start || r2->end == r1->start)
+    *intersection_type = svn__adjoining_intersection;
+  else if (r1->start <= r2->start && r1->end >= r2->end)
+    *intersection_type = svn__proper_subset_intersection;
+  else if (r2->start <= r1->start && r2->end >= r1->end)
+    *intersection_type = svn__proper_subset_intersection;
+  else
+    *intersection_type = svn__overlapping_intersection;
+
+  return SVN_NO_ERROR;
+}
+
 /* Helper for svn_rangelist_merge() and rangelist_intersect_or_remove().
 
    If *LASTRANGE is not NULL it should point to the last element in REVLIST.
    REVLIST must be sorted from lowest to highest revision and contain no
-   overlapping revision ranges.  Any changes made to REVLIST will maintain
-   this guarantee.
-
-   If *LASTRANGE is NULL then push MRANGE to REVLIST.
-
-   If *LASTRANGE and MRANGE don't intersect then push MRANGE to REVLIST.
-   If they do intersect and have the same inheritability then combine the
-   ranges, updating *LASTRANGE to reflect the new combined range.  If the
-   ranges intersect but differ in inheritability, then merge the ranges - see
-   the doc string for svn_mergeinfo_merge.  This may result in a change to
-   *LASTRANGE's end field and the pushing of up to two new ranges on REVLIST.
-
-     e.g.  *LASTRANGE: '4-10*' merged with MRANGE: '6'________
-                  |                           |               |
-             Update end field               Push       Account for trimmed
-                  |                           |        range from *LASTRANGE.
-                  |                           |        Push it last to
-                  |                           |        maintain sort order.
-                  |                           |               |
-                  V                           V               V
-           *LASTRANGE: '4-5*'              MRANGE: '6'   NEWRANGE: '6-10*'
+   overlapping revision ranges.  All ranges in REVLIST must describe forward
+   merges. Any changes made to REVLIST will maintain these guarantees.
 
-   Upon return, if any new ranges were pushed onto REVLIST, then set
-   *LASTRANGE to the last range pushed.
+   Make a copy of NEW_RANGE allocated in RESULT_POOL.  In some cases
+   *LASTRANGE may be popped from REVLIST, a copy made (allocated in
+   RESULT_POOL), the copy modified and then pushed back onto REVLIST.
+
+   If *LASTRANGE is NULL then push the copy of NEW_RANGE onto REVLIST.
+
+   If *LASTRANGE and NEW_RANGE don't intersect then push the copy of
+   NEW_RANGE onto REVLIST.
+
+   If the ranges do intersect and have the same inheritability then combine
+   the ranges.
+   
+   If the ranges intersect but differ in inheritability, then merge the
+   ranges as dictated below by CONSIDER_INHERITANCE.
 
    CONSIDER_INHERITANCE determines how to account for the inheritability of
-   MRANGE and *LASTRANGE when determining if they intersect.  If
-   CONSIDER_INHERITANCE is TRUE, then only ranges with the same
-   inheritability can intersect and therefore be combined.
+   NEW_RANGE and *LASTRANGE when determining if they intersect.
+   
+   If CONSIDER_INHERITANCE is false then any intersection between *LASTRANGE
+   and NEW_RANGE is determined strictly on the ranges start and end revisions.
+   If the ranges intersect then they are joined.  The inheritability of the
+   resulting range is non-inheritable *only* if both ranges were
+   non-inheritable, otherwise the combined range is inheritable, e.g.:
+     
+     *LASTRANGE        NEW_RANGE        RESULTING RANGES
+     ----------        ---------        ----------------
+     4-10*             6-13             4-13
+     4-10              6-13*            4-13
+     4-10*             6-13*            4-13*
+
+   If CONSIDER_INHERITANCE is true, then only the intersection between two
+   ranges with differing inheritance can be combined.  If one range has
+   non-inheritable ranges unique to it and the other range is inheritable,
+   then the unique non-inheritable ranges are pushed onto REVLIST as separate
+   ranges.  Adjoining ranges of the same inheritance are joined to make a
+   single range, e.g.:
+
+     *LASTRANGE        NEW_RANGE        RESULTING RANGES
+     ----------        ---------        ----------------
+     4-10*             6                4-5*, 6, 7-10*
+     4-10              6*               4-10
+     4-10*             6-12             4-5*, 6-12
 
-   If DUP_MRANGE is TRUE then allocate a copy of MRANGE before pushing it
-   onto REVLIST.
+   SCRATCH_POOL is used for any temporary allocations.  RESULT_POOL is used
+   to allocate any svn_merge_range_t added to REVLIST.
 */
-static APR_INLINE void
+static svn_error_t *
 combine_with_lastrange(svn_merge_range_t** lastrange,
-                       svn_merge_range_t *mrange, svn_boolean_t dup_mrange,
+                       svn_merge_range_t *new_range,
                        apr_array_header_t *revlist,
                        svn_boolean_t consider_inheritance,
-                       apr_pool_t *pool)
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
 {
-  svn_merge_range_t *pushed_mrange_1 = NULL;
-  svn_merge_range_t *pushed_mrange_2 = NULL;
-  svn_boolean_t ranges_intersect = FALSE;
-  svn_boolean_t ranges_have_same_inheritance = FALSE;
+  svn_merge_range_t combined_range;
+
+  /* We don't accept a NULL REVLIST. */
+  SVN_ERR_ASSERT(revlist);
 
+  /* Our contract requires that *LASTRANGE is the "last" range
+     if it isn't NULL. */
   if (*lastrange)
-    {
-      if ((*lastrange)->start <= mrange->end
-          && mrange->start <= (*lastrange)->end)
-        ranges_intersect = TRUE;
-      if ((*lastrange)->inheritable == mrange->inheritable)
-        ranges_have_same_inheritance = TRUE;
-    }
-
-  if (!(*lastrange)
-      || (!ranges_intersect || (!ranges_have_same_inheritance
-                                && consider_inheritance)))
-
-    {
-      /* No *LASTRANGE
-           or
-         LASTRANGE and MRANGE don't intersect
-           or
-         LASTRANGE and MRANGE "intersect" but have different
-         inheritability and we are considering inheritance so
-         can't combined them...
-
-         ...In all these cases just push MRANGE onto *LASTRANGE. */
-      if (dup_mrange)
-        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
+    SVN_ERR_ASSERT(*lastrange == APR_ARRAY_IDX(revlist,
+                                               revlist->nelts - 1,
+                                               svn_merge_range_t *));
+
+  if (!*lastrange)
+    {
+      /* No *LASTRANGE so push NEW_RANGE onto REVLIST and we are done. */
+      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) =
+        svn_merge_range_dup(new_range, result_pool);
+    }
+  else if (!consider_inheritance)
+    {
+      /* We are not considering inheritance so we can merge intersecting
+         ranges of different inheritability.  Of course if the ranges
+         don't intersect at all we simply push NEW_RANGE only REVLIST. */
+      if (combine_ranges(&combined_range, *lastrange, new_range, FALSE))
+        {
+          (*lastrange)->start = combined_range.start;
+          (*lastrange)->end = combined_range.end;
+          (*lastrange)->inheritable = combined_range.inheritable;
+        }
       else
-        pushed_mrange_1 = mrange;
+        {
+          APR_ARRAY_PUSH(revlist, svn_merge_range_t *) =
+            svn_merge_range_dup(new_range, result_pool);
+        }
     }
-  else /* MRANGE and *LASTRANGE intersect */
+  else /* Considering inheritance */
     {
-      if (ranges_have_same_inheritance)
+      if (combine_ranges(&combined_range, *lastrange, new_range, TRUE))
         {
-          /* Intersecting ranges have the same inheritability
-             so just combine them. */
-          (*lastrange)->start = MIN((*lastrange)->start, mrange->start);
-          (*lastrange)->end = MAX((*lastrange)->end, mrange->end);
-          (*lastrange)->inheritable =
-            ((*lastrange)->inheritable || mrange->inheritable);
+          /* Even when considering inheritance two intersection ranges
+             of the same inheritability can simply be combined. */
+          (*lastrange)->start = combined_range.start;
+          (*lastrange)->end = combined_range.end;
+          (*lastrange)->inheritable = combined_range.inheritable;
         }
-      else /* Ranges intersect but have different
-              inheritability so merge the ranges. */
+      else
         {
-          svn_revnum_t tmp_revnum;
-
-          /* Ranges have same starting revision. */
-          if ((*lastrange)->start == mrange->start)
-            {
-              if ((*lastrange)->end == mrange->end)
-                {
-                  (*lastrange)->inheritable = TRUE;
-                }
-              else if ((*lastrange)->end > mrange->end)
-                {
-                  if (!(*lastrange)->inheritable)
-                    {
-                      tmp_revnum = (*lastrange)->end;
-                      (*lastrange)->end = mrange->end;
-                      (*lastrange)->inheritable = TRUE;
-                      if (dup_mrange)
-                        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
-                      else
-                        pushed_mrange_1 = mrange;
-                      pushed_mrange_1->start = pushed_mrange_1->start;
-                      pushed_mrange_1->end = tmp_revnum;
-                      *lastrange = pushed_mrange_1;
-                    }
-                }
-              else /* (*lastrange)->end < mrange->end) */
-                {
-                  if (mrange->inheritable)
-                    {
-                      (*lastrange)->inheritable = TRUE;
-                      (*lastrange)->end = mrange->end;
-                    }
-                  else
-                    {
-                      if (dup_mrange)
-                        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
-                      else
-                        pushed_mrange_1 = mrange;
-                      pushed_mrange_1->start = (*lastrange)->end;
-                    }
-                }
-            }
-          /* Ranges have same ending revision. (Same starting
-             and ending revisions already handled above.) */
-          else if ((*lastrange)->end == mrange->end)
-            {
-              if ((*lastrange)->start < mrange->start)
+          /* If we are here then the ranges either don't intersect or do
+             intersect but have differing inheritability.  Check for the
+             first case as that is easy to handle. */
+          intersection_type_t intersection_type;
+          
+          SVN_ERR(get_type_of_intersection(new_range, *lastrange,
+                                           &intersection_type));
+              
+              switch (intersection_type)
                 {
-                  if (!(*lastrange)->inheritable)
+                  case svn__no_intersection:
+                    /* NEW_RANGE and *LASTRANGE *really* don't intersect so
+                       just push NEW_RANGE only REVLIST. */
+                    APR_ARRAY_PUSH(revlist, svn_merge_range_t *) =
+                      svn_merge_range_dup(new_range, result_pool);
+                    break;
+
+                  case svn__equal_intersection:
+                    /* They range are equal so all we do is force the
+                       inheritability of lastrange to true. */
+                    (*lastrange)->inheritable = TRUE;
+                    break;
+
+                  case svn__adjoining_intersection:
+                    /* They adjoin but don't overlap so just push NEW_RANGE
+                       onto REVLIST. */
+                    APR_ARRAY_PUSH(revlist, svn_merge_range_t *) =
+                      svn_merge_range_dup(new_range, result_pool);
+                    break;
+
+                  case svn__overlapping_intersection:
+                    /* They ranges overlap but neither is a proper subset of
+                       the other.  We'll end up pusing two new ranges onto
+                       REVLIST, the intersecting part and the part unique to
+                       NEW_RANGE.*/
                     {
-                      (*lastrange)->end = mrange->start;
-                      if (dup_mrange)
-                        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
+                      svn_merge_range_t *r1 = svn_merge_range_dup(*lastrange,
+                                                                  result_pool);
+                      svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
+                                                                  result_pool);
+
+                      /* Pop off *LASTRANGE to make our manipulations
+                         easier. */
+                      apr_array_pop(revlist);
+
+                      /* Ensure R1 is the older range. */
+                      if (r2->start < r1->start)
+                        {
+                          /* Swap R1 and R2. */
+                          r2->start = r1->start;
+                          r2->end = r1->end;
+                          r2->inheritable = r1->inheritable;
+                          r1->start = new_range->start;
+                          r1->end = new_range->end;
+                          r1->inheritable = new_range->inheritable;
+                        }
+
+                      /* Absorb the intersecting ranges into the
+                         inheritable range. */
+                      if (r1->inheritable)
+                        r2->start = r1->end;
                       else
-                        pushed_mrange_1 = mrange;
-                      *lastrange = pushed_mrange_1;
+                        r1->end = r2->start;
+                      
+                      /* Push everything back onto REVLIST. */
+                      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = r1;
+                      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = r2;
+
+                      break;
                     }
-                }
-              else /* (*lastrange)->start > mrange->start */
-                {
-                  (*lastrange)->start = mrange->start;
-                  (*lastrange)->end = mrange->end;
-                  (*lastrange)->inheritable = mrange->inheritable;
-                  if (dup_mrange)
-                    pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
-                  else
-                    pushed_mrange_1 = mrange;
-                  pushed_mrange_1->start = (*lastrange)->end;
-                  pushed_mrange_1->inheritable = TRUE;
 
-                }
-            }
-          else /* Ranges have different starting and ending revisions. */
-            {
-              if ((*lastrange)->start < mrange->start)
-                {
-                  /* If MRANGE is a proper subset of *LASTRANGE and
-                     *LASTRANGE is inheritable there is nothing more
-                     to do. */
-                  if (!((*lastrange)->end > mrange->end
-                        && (*lastrange)->inheritable))
+                  default: /* svn__proper_subset_intersection */
                     {
-                      tmp_revnum = (*lastrange)->end;
-                      if (!(*lastrange)->inheritable)
-                        (*lastrange)->end = mrange->start;
-                      else
-                        mrange->start = (*lastrange)->end;
-                      if (dup_mrange)
-                        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
-                      else
-                        pushed_mrange_1 = mrange;
+                      /* One range is a proper subset of the other. */
+                      svn_merge_range_t *r1 = svn_merge_range_dup(*lastrange,
+                                                                  result_pool);
+                      svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
+                                                                  result_pool);
+                      svn_merge_range_t *r3 = NULL;
+                      svn_revnum_t tmp_revnum;
+
+                      /* Pop off *LASTRANGE to make our manipulations
+                         easier. */
+                      apr_array_pop(revlist);
 
-                      if (tmp_revnum > mrange->end)
+                      /* Ensure R1 is the superset. */
+                      if (r2->start < r1->start || r2->end > r1->end)
                         {
-                          pushed_mrange_2 =
-                            apr_palloc(pool, sizeof(*pushed_mrange_2));
-                          pushed_mrange_2->start = mrange->end;
-                          pushed_mrange_2->end = tmp_revnum;
-                          pushed_mrange_2->inheritable =
-                            (*lastrange)->inheritable;
+                          /* Swap R1 and R2. */
+                          r2->start = r1->start;
+                          r2->end = r1->end;
+                          r2->inheritable = r1->inheritable;
+                          r1->start = new_range->start;
+                          r1->end = new_range->end;
+                          r1->inheritable = new_range->inheritable;
                         }
-                      mrange->inheritable = TRUE;
-                    }
-                }
-              else /* ((*lastrange)->start > mrange->start) */
-                {
-                  pushed_mrange_2 =
-                    apr_palloc(pool, sizeof(*pushed_mrange_2));
 
-                  if ((*lastrange)->end < mrange->end)
-                    {
-                      pushed_mrange_2->start = (*lastrange)->end;
-                      pushed_mrange_2->end = mrange->end;
-                      pushed_mrange_2->inheritable = mrange->inheritable;
-
-                      tmp_revnum = (*lastrange)->start;
-                      (*lastrange)->start = mrange->start;
-                      (*lastrange)->end = tmp_revnum;
-                      (*lastrange)->inheritable = mrange->inheritable;
-
-                      mrange->start = tmp_revnum;
-                      mrange->end = pushed_mrange_2->start;
-                      mrange->inheritable = TRUE;
-                    }
-                  else /* (*lastrange)->end > mrange->end */
-                    {
-                      pushed_mrange_2->start = mrange->end;
-                      pushed_mrange_2->end = (*lastrange)->end;
-                      pushed_mrange_2->inheritable =
-                        (*lastrange)->inheritable;
-
-                      tmp_revnum = (*lastrange)->start;
-                      (*lastrange)->start = mrange->start;
-                      (*lastrange)->end = tmp_revnum;
-                      (*lastrange)->inheritable = mrange->inheritable;
-
-                      mrange->start = tmp_revnum;
-                      mrange->end = pushed_mrange_2->start;
-                      mrange->inheritable = TRUE;
+                      if (r1->inheritable)
+                        {
+                          /* The simple case: The superset is inheritable, so
+                             just combine r1 and r2. */
+                          r1->start = MIN(r1->start, r2->start);
+                          r1->end = MAX(r1->end, r2->end);
+                          r2 = NULL;
+                        }
+                      else if (r1->start == r2->start)
+                        {
+                          /* *LASTRANGE and NEW_RANGE share an end point. */
+                          tmp_revnum = r1->end;
+                          r1->end = r2->end;
+                          r2->inheritable = r1->inheritable;
+                          r1->inheritable = TRUE;
+                          r2->start = r1->end;
+                          r2->end = tmp_revnum;
+                        }
+                      else if (r1->end == r2->end)
+                        {
+                          /* *LASTRANGE and NEW_RANGE share an end point. */
+                          r1->end = r2->start;
+                          r2->inheritable = TRUE;
+                        }
+                      else
+                        {
+                          /* NEW_RANGE and *LASTRANGE share neither start
+                             nor end points. */
+                          r3 = apr_pcalloc(result_pool, sizeof(*r3));
+                          r3->start = r2->end;
+                          r3->end = r1->end;
+                          r3->inheritable = r1->inheritable;
+                          r2->inheritable = TRUE;
+                          r1->end = r2->start;
+                        }
+
+                      /* Push everything back onto REVLIST. */
+                      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = r1;
+                      if (r2)
+                        APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = r2;
+                      if (r3)
+                        APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = r3;
+
+                      break;
                     }
                 }
-            }
+
+              /* Some of the above cases might have put *REVLIST out of
+                 order, so re-sort.*/
+              qsort(revlist->elts, revlist->nelts, revlist->elt_size,
+                    svn_sort_compare_ranges);
         }
     }
-  if (pushed_mrange_1)
-    {
-      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = pushed_mrange_1;
-      *lastrange = pushed_mrange_1;
-    }
-  if (pushed_mrange_2)
-    {
-      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = pushed_mrange_2;
-      *lastrange = pushed_mrange_2;
-    }
+
+  /* Make sure *LASTRANGE points at the "last" range. */
+ *lastrange = APR_ARRAY_IDX(revlist, revlist->nelts - 1, svn_merge_range_t *);
+  return SVN_NO_ERROR;
 }
 
 /* Convert a single svn_merge_range_t * back into an svn_string_t *.  */
@@ -487,6 +574,7 @@ parse_revision_line(const char **input, 
                     apr_pool_t *pool)
 {
   svn_stringbuf_t *pathname;
+  apr_array_header_t *existing_rangelist;
   apr_array_header_t *revlist = apr_array_make(pool, 1,
                                                sizeof(svn_merge_range_t *));
 
@@ -559,6 +647,17 @@ parse_revision_line(const char **input, 
           lastrange = APR_ARRAY_IDX(revlist, i, svn_merge_range_t *);
         }
     }
+
+  /* Handle any funky mergeinfo with relative merge source paths that
+     might exist due to issue #3547.  It's possible that this issue allowed
+     the creation of mergeinfo with path keys that differ only by a
+     leading slash, e.g. "trunk:4033\n/trunk:4039-4995".  In the event
+     we encounter this we merge the rangelists together under a single
+     absolute path key. */
+  if (existing_rangelist = apr_hash_get(hash, pathname->data,
+                                        APR_HASH_KEY_STRING))
+    svn_rangelist_merge(&revlist, existing_rangelist, pool);
+
   apr_hash_set(hash, pathname->data, APR_HASH_KEY_STRING, revlist);
 
   return SVN_NO_ERROR;
@@ -621,21 +720,21 @@ svn_rangelist_merge(apr_array_header_t *
              result. */
           if (elt1->inheritable || elt2->inheritable)
             elt1->inheritable = TRUE;
-          combine_with_lastrange(&lastrange, elt1, TRUE, output,
-                                 FALSE, pool);
+          SVN_ERR(combine_with_lastrange(&lastrange, elt1, output,
+                                         TRUE, pool, pool));
           i++;
           j++;
         }
       else if (res < 0)
         {
-          combine_with_lastrange(&lastrange, elt1, TRUE, output,
-                                 FALSE, pool);
+          SVN_ERR(combine_with_lastrange(&lastrange, elt1, output,
+                                         TRUE, pool, pool));
           i++;
         }
       else
         {
-          combine_with_lastrange(&lastrange, elt2, TRUE, output,
-                                 FALSE, pool);
+          SVN_ERR(combine_with_lastrange(&lastrange, elt2, output,
+                                         TRUE, pool, pool));
           j++;
         }
     }
@@ -648,16 +747,16 @@ svn_rangelist_merge(apr_array_header_t *
     {
       svn_merge_range_t *elt = APR_ARRAY_IDX(*rangelist, i,
                                              svn_merge_range_t *);
-      combine_with_lastrange(&lastrange, elt, TRUE, output,
-                             FALSE, pool);
+      SVN_ERR(combine_with_lastrange(&lastrange, elt, output,
+                                     TRUE, pool, pool));
     }
 
 
   for (; j < changes->nelts; j++)
     {
       svn_merge_range_t *elt = APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
-      combine_with_lastrange(&lastrange, elt, TRUE, output,
-                             FALSE, pool);
+      SVN_ERR(combine_with_lastrange(&lastrange, elt, output,
+                                     TRUE, pool, pool));
     }
 
   *rangelist = output;
@@ -787,8 +886,9 @@ rangelist_intersect_or_remove(apr_array_
       if (range_contains(elt2, elt1, consider_inheritance))
         {
           if (!do_remove)
-              combine_with_lastrange(&lastrange, elt1, TRUE, *output,
-                                     consider_inheritance, pool);
+            SVN_ERR(combine_with_lastrange(&lastrange, elt1, *output,
+                                           consider_inheritance, pool,
+                                           pool));
 
           i++;
 
@@ -816,8 +916,9 @@ rangelist_intersect_or_remove(apr_array_
                   tmp_range.end = MIN(elt1->end, elt2->end);
                 }
 
-              combine_with_lastrange(&lastrange, &tmp_range, TRUE,
-                                     *output, consider_inheritance, pool);
+              SVN_ERR(combine_with_lastrange(&lastrange, &tmp_range,
+                                             *output, consider_inheritance,
+                                             pool, pool));
             }
 
           /* Set up the rest of the whiteboard range for further
@@ -832,8 +933,10 @@ rangelist_intersect_or_remove(apr_array_
                   tmp_range.start = MAX(elt1->start, elt2->start);
                   tmp_range.end = elt2->end;
                   tmp_range.inheritable = elt1->inheritable;
-                  combine_with_lastrange(&lastrange, &tmp_range, TRUE,
-                                         *output, consider_inheritance, pool);
+                  SVN_ERR(combine_with_lastrange(&lastrange, &tmp_range,
+                                                 *output,
+                                                 consider_inheritance,
+                                                 pool, pool));
                 }
 
               wboardelt.start = elt2->end;
@@ -855,7 +958,7 @@ rangelist_intersect_or_remove(apr_array_
           else
             {
               if (do_remove && !(lastrange &&
-                                 combine_ranges(&lastrange, lastrange, elt1,
+                                 combine_ranges(lastrange, lastrange, elt1,
                                                 consider_inheritance)))
                 {
                   lastrange = svn_merge_range_dup(elt1, pool);
@@ -876,8 +979,8 @@ rangelist_intersect_or_remove(apr_array_
          the whiteboard element. */
       if (i == lasti && i < whiteboard->nelts)
         {
-          combine_with_lastrange(&lastrange, &wboardelt, TRUE, *output,
-                                 consider_inheritance, pool);
+          SVN_ERR(combine_with_lastrange(&lastrange, &wboardelt, *output,
+                                         consider_inheritance, pool, pool));
           i++;
         }
 
@@ -887,8 +990,8 @@ rangelist_intersect_or_remove(apr_array_
           svn_merge_range_t *elt = APR_ARRAY_IDX(whiteboard, i,
                                                  svn_merge_range_t *);
 
-          combine_with_lastrange(&lastrange, elt, TRUE, *output,
-                                 consider_inheritance, pool);
+          SVN_ERR(combine_with_lastrange(&lastrange, elt, *output,
+                                         consider_inheritance, pool, pool));
         }
     }
 
@@ -1253,7 +1356,9 @@ svn_rangelist_to_string(svn_string_t **o
 
 /* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT.  If PREFIX
    is not NULL then prepend PREFIX to each line in OUTPUT.  If INPUT contains
-   no elements, return the empty string.
+   no elements, return the empty string.  If INPUT contains any merge source
+   path keys that are relative then convert these to absolute paths in
+   *OUTPUT.
  */
 static svn_error_t *
 mergeinfo_to_stringbuf(svn_stringbuf_t **output,
@@ -1275,11 +1380,13 @@ mergeinfo_to_stringbuf(svn_stringbuf_t *
           svn_string_t *revlist;
 
           SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool));
-          svn_stringbuf_appendcstr(*output,
-                                   apr_psprintf(pool, "%s%s:%s",
-                                                prefix ? prefix : "",
-                                                (const char *) elt.key,
-                                                revlist->data));
+          svn_stringbuf_appendcstr(
+            *output,
+            apr_psprintf(pool, "%s%s%s:%s",
+                         prefix ? prefix : "",
+                         *((const char *) elt.key) == '/' ? "" : "/",
+                         (const char *) elt.key,
+                         revlist->data));
           if (i < sorted->nelts - 1)
             svn_stringbuf_appendcstr(*output, "\n");
         }

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/opt.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/opt.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/opt.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/opt.c Tue Mar 16 18:03:37 2010
@@ -872,13 +872,22 @@ svn_opt__split_arg_at_peg_revision(const
 
   if (peg_start)
     {
+      /* Error out if target is the empty string. */
+      if (j == 0)
+        return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
+                                 _("'%s' is just a peg revision. "
+                                   "Maybe try '%s@' instead?"),
+                                 utf8_target, utf8_target);
+
       *true_target = apr_pstrmemdup(pool, utf8_target, j);
-      *peg_revision = apr_pstrdup(pool, peg_start);
+      if (peg_revision)
+        *peg_revision = apr_pstrdup(pool, peg_start);
     }
   else
     {
       *true_target = utf8_target;
-      *peg_revision = "";
+      if (peg_revision)
+        *peg_revision = "";
     }
 
   return SVN_NO_ERROR;
@@ -1017,3 +1026,29 @@ svn_opt_print_help3(apr_getopt_t *os,
 
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_opt__eat_peg_revisions(apr_array_header_t **true_targets_p,
+                           apr_array_header_t *targets,
+                           apr_pool_t *pool)
+{
+  unsigned int i;
+  apr_array_header_t *true_targets;
+
+  true_targets = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
+
+  for (i = 0; i < targets->nelts; i++)
+    {
+      const char *target = APR_ARRAY_IDX(targets, i, const char *);
+      const char *true_target;
+
+      SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, NULL,
+                                                 target, pool));
+      APR_ARRAY_PUSH(true_targets, const char *) = true_target;
+    }
+
+  SVN_ERR_ASSERT(true_targets_p);
+  *true_targets_p = true_targets;
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/prompt.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/prompt.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/prompt.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/prompt.c Tue Mar 16 18:03:37 2010
@@ -438,8 +438,8 @@ svn_cmdline_auth_plaintext_prompt(svn_bo
 {
   const char *prompt_string = _("Store password unencrypted (yes/no)? ");
   const char *prompt_text =
-  _("-----------------------------------------------------------------------\n"
-    "ATTENTION!  Your password for authentication realm:\n"
+  _("\n-----------------------------------------------------------------------"
+    "\nATTENTION!  Your password for authentication realm:\n"
     "\n"
     "   %s\n"
     "\n"
@@ -467,7 +467,7 @@ svn_cmdline_auth_plaintext_passphrase_pr
 {
   const char *prompt_string = _("Store passphrase unencrypted (yes/no)? ");
   const char *prompt_text =
-  _("-----------------------------------------------------------------------\n"
+  _("\n-----------------------------------------------------------------------\n"
     "ATTENTION!  Your passphrase for client certificate:\n"
     "\n"
     "   %s\n"

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/sqlite.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/sqlite.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/sqlite.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_subr/sqlite.c Tue Mar 16 18:03:37 2010
@@ -492,6 +492,7 @@ init_sqlite(apr_pool_t *pool)
                              SQLITE_VERSION, sqlite3_libversion());
   }
 
+#if APR_HAS_THREADS
 #if SQLITE_VERSION_AT_LEAST(3,5,0)
   /* SQLite 3.5 allows verification of its thread-safety at runtime.
      Older versions are simply expected to have been configured with
@@ -513,6 +514,7 @@ init_sqlite(apr_pool_t *pool)
   }
   SQLITE_ERR_MSG(sqlite3_initialize(), "Could not initialize SQLite");
 #endif
+#endif /* APR_HAS_THRADS */
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/adm_ops.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/adm_ops.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/adm_ops.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/adm_ops.c Tue Mar 16 18:03:37 2010
@@ -36,6 +36,7 @@
 #include "svn_pools.h"
 #include "svn_string.h"
 #include "svn_error.h"
+#include "svn_dirent_uri.h"
 #include "svn_path.h"
 #include "svn_hash.h"
 #include "svn_wc.h"
@@ -2631,7 +2632,8 @@ svn_wc_remove_from_revision_control(svn_
                function is called by svn_wc_crop_tree(). */
             dir_entry = apr_hash_get(parent_entries, base_name,
                                      APR_HASH_KEY_STRING);
-            if (dir_entry->depth != svn_depth_exclude)
+            if (dir_entry
+                && dir_entry->depth != svn_depth_exclude)
               {
                 svn_wc__entry_remove(parent_entries, base_name);
                 SVN_ERR(svn_wc__entries_write(parent_entries, parent_access, pool));
@@ -2739,23 +2741,31 @@ resolve_conflict_on_entry(const char *pa
 
   if (resolve_text)
     {
-      const char *auto_resolve_src;
+      const char *auto_resolve_src = NULL;
 
       /* Handle automatic conflict resolution before the temporary files are
        * deleted, if necessary. */
       switch (conflict_choice)
         {
         case svn_wc_conflict_choose_base:
-          auto_resolve_src = entry->conflict_old;
+          if (entry->conflict_old)
+            auto_resolve_src = svn_path_join(
+              svn_wc_adm_access_path(conflict_dir),
+              entry->conflict_old, pool);
           break;
         case svn_wc_conflict_choose_mine_full:
-          auto_resolve_src = entry->conflict_wrk;
+          if (entry->conflict_wrk)
+            auto_resolve_src = svn_path_join(
+              svn_wc_adm_access_path(conflict_dir),
+              entry->conflict_wrk, pool);
           break;
         case svn_wc_conflict_choose_theirs_full:
-          auto_resolve_src = entry->conflict_new;
+          if (entry->conflict_new)
+            auto_resolve_src = svn_path_join(
+              svn_wc_adm_access_path(conflict_dir),
+              entry->conflict_new, pool);
           break;
         case svn_wc_conflict_choose_merged:
-          auto_resolve_src = NULL;
           break;
         case svn_wc_conflict_choose_theirs_conflict:
         case svn_wc_conflict_choose_mine_conflict:
@@ -2766,6 +2776,9 @@ resolve_conflict_on_entry(const char *pa
                 apr_file_t *tmp_f;
                 svn_stream_t *tmp_stream;
                 svn_diff_t *diff;
+                const char *conflict_old;
+                const char *conflict_wrk;
+                const char *conflict_new;
                 svn_diff_conflict_display_style_t style =
                   conflict_choice == svn_wc_conflict_choose_theirs_conflict
                   ? svn_diff_conflict_display_latest
@@ -2777,24 +2790,46 @@ resolve_conflict_on_entry(const char *pa
                                                 svn_io_file_del_none,
                                                 pool));
                 tmp_stream = svn_stream_from_aprfile2(tmp_f, FALSE, pool);
+
+                /* ### If any of these paths isn't absolute, treat it
+                 * ### as relative to CONFLICT_DIR_ABSPATH.
+                 * ### Else we end up erroring out here, e.g. if the file
+                 * ### is just a basename, and does not live in the current
+                 * ### working directory.
+                 * ### The API docs are unclear about whether these paths
+                 * ### must be absolute or not. */
+                conflict_old = entry->conflict_old;
+                conflict_wrk = entry->conflict_wrk;
+                conflict_new = entry->conflict_new;
+                if (! svn_dirent_is_absolute(conflict_old))
+                  conflict_old = svn_dirent_join(
+                                   svn_wc_adm_access_path(conflict_dir),
+                                   entry->conflict_old, pool);
+                if (! svn_dirent_is_absolute(conflict_wrk))
+                  conflict_wrk = svn_dirent_join(
+                                   svn_wc_adm_access_path(conflict_dir),
+                                   entry->conflict_wrk, pool);
+                if (! svn_dirent_is_absolute(conflict_new))
+                  conflict_new = svn_dirent_join(
+                                   svn_wc_adm_access_path(conflict_dir),
+                                   entry->conflict_new, pool);
+
                 SVN_ERR(svn_diff_file_diff3_2(&diff,
-                                              entry->conflict_old,
-                                              entry->conflict_wrk,
-                                              entry->conflict_new,
+                                              conflict_old,
+                                              conflict_wrk,
+                                              conflict_new,
                                               svn_diff_file_options_create(pool),
                                               pool));
                 SVN_ERR(svn_diff_file_output_merge2(tmp_stream, diff,
-                                                    entry->conflict_old,
-                                                    entry->conflict_wrk,
-                                                    entry->conflict_new,
+                                                    conflict_old,
+                                                    conflict_wrk,
+                                                    conflict_new,
                                                     /* markers ignored */
                                                     NULL, NULL, NULL, NULL,
                                                     style,
                                                     pool));
                 SVN_ERR(svn_stream_close(tmp_stream));
               }
-            else
-              auto_resolve_src = NULL;
             break;
           }
         default:
@@ -2803,10 +2838,7 @@ resolve_conflict_on_entry(const char *pa
         }
 
       if (auto_resolve_src)
-        SVN_ERR(svn_io_copy_file(
-          svn_path_join(svn_wc_adm_access_path(conflict_dir), auto_resolve_src,
-                        pool),
-          path, TRUE, pool));
+        SVN_ERR(svn_io_copy_file(auto_resolve_src, path, TRUE, pool));
     }
 
   /* Yes indeed, being able to map a function over a list would be nice. */
@@ -2954,6 +2986,20 @@ resolve_found_entry_callback(const char 
                                         pool));
       if (conflict)
         {
+          /* For now, we only clear tree conflict information and resolve
+           * to the working state. There is no way to pick theirs-full
+           * or mine-full, etc. Throw an error if the user expects us
+           * to be smarter than we really are. */
+          if (baton->conflict_choice != svn_wc_conflict_choose_merged)
+            {
+              return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
+                                       NULL,
+                                       _("Tree conflicts can only be resolved "
+                                         "to 'working' state; "
+                                         "'%s' not resolved"),
+                                       svn_dirent_local_style(path, pool));
+            }
+
           SVN_ERR(svn_wc__del_tree_conflict(path, parent_adm_access, pool));
 
           /* Sanity check:  see if libsvn_wc *still* thinks this item is in a

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/props.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/props.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/props.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/props.c Tue Mar 16 18:03:37 2010
@@ -926,7 +926,7 @@ diff_mergeinfo_props(svn_mergeinfo_t *de
       SVN_ERR(svn_mergeinfo_parse(&from, from_prop_val->data, pool));
       SVN_ERR(svn_mergeinfo_parse(&to, to_prop_val->data, pool));
       SVN_ERR(svn_mergeinfo_diff(deleted, added, from, to,
-                                 FALSE, pool));
+                                 TRUE, pool));
     }
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/questions.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/questions.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/questions.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/questions.c Tue Mar 16 18:03:37 2010
@@ -41,8 +41,52 @@
 
 #include "svn_private_config.h"
 #include "private/svn_wc_private.h"
+#include "private/svn_sqlite.h"
 
+#define SVN_WC_NG_CHECK_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_CHECK_FOR_WC_NG"
 
+static svn_error_t *
+is_inside_wc_ng(const char *abspath,
+                const char *target_path,
+                int *wc_format,
+                apr_pool_t *pool)
+{
+  svn_node_kind_t kind;
+  const char *wc_db_path;
+  char *wc_ng_check_env_var;
+
+  wc_ng_check_env_var = getenv(SVN_WC_NG_CHECK_ENV_VAR);
+  if (wc_ng_check_env_var &&
+      apr_strnatcasecmp(wc_ng_check_env_var, "yes") == 0)
+    return SVN_NO_ERROR; /* Allow skipping for testing */
+
+  wc_db_path = svn_path_join_many(pool, abspath, SVN_WC_ADM_DIR_NAME,
+                                  "wc.db", NULL);
+  SVN_ERR(svn_io_check_path(wc_db_path, &kind, pool));
+
+  if (kind == svn_node_file)
+    {
+      /* This value is completely bogus, but it is much higher than 1.6 will
+         have any prayer of reading. */
+      *wc_format = 9999;
+
+      return svn_error_createf(SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
+         _("The path '%s' appears to be part of a Subversion 1.7 or greater\n"
+           "working copy rooted at '%s'.\n"
+           "Please upgrade your Subversion client to use this working copy."
+           ),
+         svn_path_local_style(target_path, pool),
+         svn_path_local_style(abspath, pool));
+    }
+
+  if (svn_dirent_is_root(abspath, strlen(abspath)))
+    return SVN_NO_ERROR;
+  else
+    return is_inside_wc_ng(svn_path_dirname(abspath, pool), target_path,
+                           wc_format, pool);
+}
+
+
 /* ### todo: make this compare repository too?  Or do so in parallel
    code.  */
 svn_error_t *
@@ -95,7 +139,17 @@ svn_wc_check_wc(const char *path,
     }
   else if (err)
     return err;
-  else
+
+  /* Let's check for the future. */
+  if (*wc_format == 0)
+    {
+      const char *abspath;
+
+      SVN_ERR(svn_path_get_absolute(&abspath, path, pool));
+      SVN_ERR(is_inside_wc_ng(abspath, path, wc_format, pool));
+    }
+
+  if (*wc_format > 0)
     {
       /* If we managed to read the format file we assume that we
           are dealing with a real wc so we can return a nice
@@ -125,12 +179,9 @@ svn_wc__check_format(int wc_format, cons
          least post-1.5 crossgrades will be somewhat less painful. */
       return svn_error_createf
         (SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
-         _("This client is too old to work with working copy '%s'.  You need\n"
-           "to get a newer Subversion client, or to downgrade this working "
-           "copy.\n"
-           "See "
-           "http://subversion.tigris.org/faq.html#working-copy-format-change\n"
-           "for details."
+         _("The path '%s' appears to be part of a Subversion 1.7 or greater\n"
+           "working copy.  Please upgrade your Subversion client to use this\n"
+           "working copy."
            ),
          svn_path_local_style(path, pool));
     }

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/tree_conflicts.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/tree_conflicts.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/tree_conflicts.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/tree_conflicts.c Tue Mar 16 18:03:37 2010
@@ -512,8 +512,10 @@ svn_wc__add_tree_conflict(const svn_wc_c
   SVN_ERR(svn_wc__get_tree_conflict(&existing_conflict, conflict->path,
                                     adm_access, pool));
   if (existing_conflict != NULL)
-    return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
-                         _("Attempt to add tree conflict that already exists"));
+    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+                             _("Attempt to add tree conflict that already "
+                               "exists at '%s'"),
+                             svn_path_local_style(conflict->path, pool));
 
   SVN_ERR(svn_wc__loggy_add_tree_conflict(&log_accum, conflict, adm_access,
                                           pool));

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/update_editor.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/update_editor.c Tue Mar 16 18:03:37 2010
@@ -1851,8 +1851,9 @@ set_copied_handle_error(const char *path
   return err;
 }
 
-/* Schedule the WC item PATH, whose entry is ENTRY, for re-addition as a copy
- * with history of (ENTRY->url)@(ENTRY->rev). PATH's parent is PARENT_PATH.
+/* Schedule the WC item PATH, whose entry is ENTRY, for re-addition.
+ * If MODIFY_COPYFROM is TRUE, re-add the item as a copy with history
+ * of (ENTRY->url)@(ENTRY->rev). PATH's parent is PARENT_PATH.
  * PATH and PARENT_PATH are relative to the current working directory.
  * Assume that the item exists locally and is scheduled as still existing with
  * some local modifications relative to its (old) base, but does not exist in
@@ -1876,6 +1877,7 @@ schedule_existing_item_for_re_add(const 
                                   const char *parent_path,
                                   const char *path,
                                   const char *their_url,
+                                  svn_boolean_t modify_copyfrom,
                                   apr_pool_t *pool)
 {
   const char *base_name = svn_path_basename(path, pool);
@@ -1894,12 +1896,15 @@ schedule_existing_item_for_re_add(const 
   flags |= SVN_WC__ENTRY_MODIFY_SCHEDULE;
   flags |= SVN_WC__ENTRY_MODIFY_FORCE;
 
-  tmp_entry.copyfrom_url = entry->url;
-  flags |= SVN_WC__ENTRY_MODIFY_COPYFROM_URL;
-  tmp_entry.copyfrom_rev = entry->revision;
-  flags |= SVN_WC__ENTRY_MODIFY_COPYFROM_REV;
-  tmp_entry.copied = TRUE;
-  flags |= SVN_WC__ENTRY_MODIFY_COPIED;
+  if (modify_copyfrom)
+    {
+      tmp_entry.copyfrom_url = entry->url;
+      flags |= SVN_WC__ENTRY_MODIFY_COPYFROM_URL;
+      tmp_entry.copyfrom_rev = entry->revision;
+      flags |= SVN_WC__ENTRY_MODIFY_COPYFROM_REV;
+      tmp_entry.copied = TRUE;
+      flags |= SVN_WC__ENTRY_MODIFY_COPIED;
+    }
 
   /* ### Need to change the "base" into a "revert-base" ? */
 
@@ -2062,17 +2067,42 @@ do_entry_deletion(struct edit_baton *eb,
 
           SVN_ERR(schedule_existing_item_for_re_add(entry, eb, parent_path,
                                                     full_path, their_url,
-                                                    pool));
+                                                    TRUE, pool));
           return SVN_NO_ERROR;
         }
       else if (tree_conflict->reason == svn_wc_conflict_reason_deleted)
         {
-          /* The item does not exist locally (except perhaps as a skeleton
-           * directory tree) because it was already scheduled for delete.
-           * We must complete the deletion, leaving the tree conflict info
-           * as the only difference from a normal deletion. */
+          if (entry->schedule == svn_wc_schedule_replace)
+            {
+              /* The item was locally replaced with something else. We should
+               * keep the existing item schedule-replace, but we also need to
+               * update the BASE rev of the item to the revision we are
+               * updating to. Otherwise, the replace cannot be committed
+               * because the item is considered out-of-date, and it cannot
+               * be updated either because we're here to do just that. */
+
+              /* Run the log in the parent dir, to record the tree conflict.
+               * Do this before schedule_existing_item_for_re_add(), in case
+               * that needs to modify the same entries. */
+              SVN_ERR(svn_wc__write_log(parent_adm_access, *log_number,
+                                        log_item, pool));
+              SVN_ERR(svn_wc__run_log(parent_adm_access, NULL, pool));
+              *log_number = 0;
+
+              SVN_ERR(schedule_existing_item_for_re_add(entry, eb, parent_path,
+                                                        full_path, their_url,
+                                                        FALSE, pool));
+              return SVN_NO_ERROR;
+            }
+          else
+            {
+              /* The item does not exist locally (except perhaps as a skeleton
+               * directory tree) because it was already scheduled for delete.
+               * We must complete the deletion, leaving the tree conflict info
+               * as the only difference from a normal deletion. */
 
-          /* Fall through to the normal "delete" code path. */
+              /* Fall through to the normal "delete" code path. */
+            }
         }
       else
         SVN_ERR_MALFUNCTION();  /* other reasons are not expected here */
@@ -3518,7 +3548,7 @@ add_file(const char *path,
              "from a different repository"),
            svn_path_local_style(full_path, pool));
 
-      if (!eb->switch_url
+      if (!eb->switch_url && fb->new_URL && entry->url
           && strcmp(fb->new_URL, entry->url) != 0)
         return svn_error_createf(
            SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
@@ -4130,9 +4160,17 @@ merge_file(svn_wc_notify_state_t *conten
      different from fb->old_text_base_path if we have a replaced-with-history
      file.  However, in the case we had an obstruction, we check against the
      new text base. (And if we're doing an add-with-history and we've already
-     saved a copy of a locally-modified file, then there certainly are mods.) */
+     saved a copy of a locally-modified file, then there certainly are mods.)
+
+     Special case: The working file is referring to a file external? If so
+                   then we must mark it as unmodified in order to avoid bogus
+                   conflicts, since this file was added as a place holder to
+                   merge externals item from the repository. */
   if (fb->copied_working_text)
     is_locally_modified = TRUE;
+  else if (entry && entry->file_external_path
+           && entry->schedule == svn_wc_schedule_add)
+    is_locally_modified = FALSE;
   else if (! fb->existed)
     SVN_ERR(svn_wc__text_modified_internal_p(&is_locally_modified, fb->path,
                                              FALSE, adm_access, FALSE, pool));

Modified: subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/wc_db.h?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/1.6.x-issue3432/subversion/libsvn_wc/wc_db.h Tue Mar 16 18:03:37 2010
@@ -238,7 +238,7 @@ svn_wc__db_close(svn_wc__db_t *db,
  * Different kind of trees
  *
  * The design doc mentions three different kinds of trees, BASE, WORKING and
- * ACTUAL: http://svn.collab.net/repos/svn/trunk/notes/wc-ng-design
+ * ACTUAL: http://svn.apache.org/repos/asf/subversion/trunk/notes/wc-ng-design
  * We have different APIs to handle each tree, enumerated below, along with
  * a blurb to explain what that tree represents.
  */

Modified: subversion/branches/1.6.x-issue3432/subversion/mod_dav_svn/liveprops.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/mod_dav_svn/liveprops.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/mod_dav_svn/liveprops.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/mod_dav_svn/liveprops.c Tue Mar 16 18:03:37 2010
@@ -466,7 +466,7 @@ insert_prop(const dav_resource *resource
                      && resource->info->r->content_type)
               mime_type = resource->info->r->content_type;
             else
-              mime_type = ap_default_type(resource->info->r);
+              mime_type = "text/plain";
 
             if ((serr = svn_mime_type_validate(mime_type, p)))
               {

Modified: subversion/branches/1.6.x-issue3432/subversion/mod_dav_svn/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x-issue3432/subversion/mod_dav_svn/repos.c?rev=923910&r1=923909&r2=923910&view=diff
==============================================================================
--- subversion/branches/1.6.x-issue3432/subversion/mod_dav_svn/repos.c (original)
+++ subversion/branches/1.6.x-issue3432/subversion/mod_dav_svn/repos.c Tue Mar 16 18:03:37 2010
@@ -2721,21 +2721,32 @@ set_headers(request_rec *r, const dav_re
       else if ((! resource->info->repos->is_svn_client)
                && r->content_type)
         mimetype = r->content_type;
-      else
-        mimetype = ap_default_type(r);
 
-      serr = svn_mime_type_validate(mimetype, resource->pool);
-      if (serr)
+      /* If we found a MIME type, we'll make sure it's Subversion-friendly. */
+      if (mimetype)
         {
-          /* Probably serr->apr == SVN_ERR_BAD_MIME_TYPE, but
-             there's no point even checking.  No matter what the
-             error is, we can't derive the mime type from the
-             svn:mime-type property.  So we resort to the infamous
-             "mime type of last resort." */
-          svn_error_clear(serr);
-          mimetype = "application/octet-stream";
+          if ((serr = svn_mime_type_validate(mimetype, resource->pool)))
+            {
+              /* Probably serr->apr == SVN_ERR_BAD_MIME_TYPE, but there's
+                 no point even checking.  No matter what the error is, we
+                 can't use this MIME type.  */
+              svn_error_clear(serr);
+              mimetype = NULL;
+            }
         }
 
+      /* We've found/calculated/validated no usable MIME type.  We
+         could fall back to "application/octet-stream" (aka "bag o'
+         bytes"), but many browsers have grown to expect "text/plain"
+         to mean "*shrug*", and kick off their own MIME type detection
+         routines when they see it.  So we'll use "text/plain".
+      
+         ### Why not just avoid sending a Content-type at all?  Is
+         ### that just bad form for HTTP?  */
+      if (! mimetype)
+        mimetype = "text/plain";
+
+
       /* if we aren't sending a diff, then we know the length of the file,
          so set up the Content-Length header */
       serr = svn_fs_file_length(&length,
@@ -2978,9 +2989,23 @@ deliver(const dav_resource *resource, ap
           && (resource->info->restype != DAV_SVN_RESTYPE_PARENTPATH_COLLECTION))
         {
           if (gen_html)
-            ap_fprintf(output, bb, "  <li><a href=\"../\">..</a></li>\n");
+            {
+              if (resource->info->pegged)
+                {
+                  ap_fprintf(output, bb,
+                             "  <li><a href=\"../?p=%ld\">..</a></li>\n",
+                             resource->info->root.rev);
+                }
+              else
+                {
+                  ap_fprintf(output, bb,
+                             "  <li><a href=\"../\">..</a></li>\n");
+                }
+            }
           else
-            ap_fprintf(output, bb, "    <updir />\n");
+            {
+              ap_fprintf(output, bb, "    <updir />\n");
+            }
         }
 
       /* get a sorted list of the entries */
@@ -3069,11 +3094,27 @@ deliver(const dav_resource *resource, ap
       svn_pool_destroy(entry_pool);
 
       if (gen_html)
-        ap_fputs(output, bb,
-                 " </ul>\n <hr noshade><em>Powered by "
-                 "<a href=\"http://subversion.tigris.org/\">Subversion</a> "
-                 "version " SVN_VERSION "."
-                 "</em>\n</body></html>");
+        {
+          if (strcmp(ap_psignature("FOO", resource->info->r), "") != 0)
+            {
+              /* Apache's signature generation code didn't eat our prefix.
+                 ServerSignature must be enabled.  Print our version info.
+
+                 WARNING: This is a kludge!! ap_psignature() doesn't promise
+                 to return the empty string when ServerSignature is off.  We
+                 know it does by code inspection, but this behavior is subject
+                 to change. (Perhaps we should try to get the Apache folks to
+                 make this promise, though.  Seems harmless/useful enough...)
+              */
+              ap_fputs(output, bb,
+                       " </ul>\n <hr noshade><em>Powered by "
+                       "<a href=\"http://subversion.tigris.org/\">Subversion"
+                       "</a> version " SVN_VERSION "."
+                       "</em>\n</body></html>");
+            }
+          else
+            ap_fputs(output, bb, " </ul>\n</body></html>");
+        }
       else
         ap_fputs(output, bb, "  </index>\n</svn>\n");