You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2012/01/16 13:32:48 UTC

svn commit: r1231975 [3/11] - in /subversion/branches/showing-merge-info: ./ notes/ notes/http-and-webdav/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/bindings/swig/ruby/test/ subversion/include/ subversion/include/private/ ...

Modified: subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.c?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.c Mon Jan 16 12:32:43 2012
@@ -322,7 +322,7 @@ svn_client__get_wc_mergeinfo(svn_mergein
                                                 scratch_pool));
 
           /* Look in LOCAL_ABSPATH's parent for inherited mergeinfo if
-             LOCAL_ABSPATH's has no base revision because it is an uncommitted
+             LOCAL_ABSPATH has no base revision because it is an uncommitted
              addition, or if its base revision falls within the inclusive
              range of its parent's last changed revision to the parent's base
              revision; otherwise stop looking for inherited mergeinfo. */
@@ -493,7 +493,7 @@ svn_client__get_wc_mergeinfo_catalog(svn
 svn_error_t *
 svn_client__get_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
                                 svn_ra_session_t *ra_session,
-                                const char *rel_path,
+                                const char *url,
                                 svn_revnum_t rev,
                                 svn_mergeinfo_inheritance_t inherit,
                                 svn_boolean_t squelch_incapable,
@@ -505,7 +505,7 @@ svn_client__get_repos_mergeinfo(svn_merg
 
   SVN_ERR(svn_client__get_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
                                                   ra_session,
-                                                  rel_path, rev, inherit,
+                                                  url, rev, inherit,
                                                   squelch_incapable, FALSE,
                                                   pool, pool));
 
@@ -525,7 +525,7 @@ svn_client__get_repos_mergeinfo(svn_merg
 svn_error_t *
 svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
                                         svn_ra_session_t *ra_session,
-                                        const char *rel_path,
+                                        const char *url,
                                         svn_revnum_t rev,
                                         svn_mergeinfo_inheritance_t inherit,
                                         svn_boolean_t squelch_incapable,
@@ -536,13 +536,18 @@ svn_client__get_repos_mergeinfo_catalog(
   svn_error_t *err;
   svn_mergeinfo_t repos_mergeinfo_cat;
   apr_array_header_t *rel_paths = apr_array_make(scratch_pool, 1,
-                                                 sizeof(rel_path));
+                                                 sizeof(const char *));
+  const char *old_session_url;
 
-  APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
+  APR_ARRAY_PUSH(rel_paths, const char *) = "";
 
   /* Fetch the mergeinfo. */
+  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
+                                            ra_session, url, scratch_pool));
   err = svn_ra_get_mergeinfo(ra_session, &repos_mergeinfo_cat, rel_paths,
                              rev, inherit, include_descendants, result_pool);
+  err = svn_error_compose_create(
+          err, svn_ra_reparent(ra_session, old_session_url, scratch_pool));
   if (err)
     {
       if (squelch_incapable && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
@@ -562,11 +567,9 @@ svn_client__get_repos_mergeinfo_catalog(
   else
     {
       const char *session_relpath;
-      const char *session_url;
 
-      SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
       SVN_ERR(svn_ra_get_path_relative_to_root(ra_session, &session_relpath,
-                                               session_url, scratch_pool));
+                                               url, scratch_pool));
 
       if (session_relpath[0] == '\0')
         *mergeinfo_cat = repos_mergeinfo_cat;
@@ -653,7 +656,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
      a URL and without that we cannot get accurate mergeinfo for
      TARGET_WCPATH. */
   SVN_ERR(svn_wc__node_get_origin(NULL, &target_rev, &repos_relpath,
-                                  &repos_root, NULL,
+                                  &repos_root, NULL, NULL,
                                   ctx->wc_ctx, local_abspath, FALSE,
                                   scratch_pool, scratch_pool));
 
@@ -711,16 +714,9 @@ svn_client__get_wc_or_repos_mergeinfo_ca
           if (!apr_hash_get(original_props, SVN_PROP_MERGEINFO,
                             APR_HASH_KEY_STRING))
             {
-              const char *session_url = NULL;
               apr_pool_t *sesspool = NULL;
 
-              if (ra_session)
-                {
-                  SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
-                                                            ra_session,
-                                                            url, result_pool));
-                }
-              else
+              if (! ra_session)
                 {
                   sesspool = svn_pool_create(scratch_pool);
                   SVN_ERR(svn_client__open_ra_session_internal(
@@ -730,7 +726,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
 
               SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
                         &target_mergeinfo_cat_repos, ra_session,
-                        "", target_rev, inherit,
+                        url, target_rev, inherit,
                         TRUE, include_descendants,
                         result_pool, scratch_pool));
 
@@ -752,11 +748,6 @@ svn_client__get_wc_or_repos_mergeinfo_ca
                 {
                   svn_pool_destroy(sesspool);
                 }
-              else if (session_url)
-                {
-                  SVN_ERR(svn_ra_reparent(ra_session, session_url,
-                                          result_pool));
-                }
             }
         }
     }
@@ -893,6 +884,7 @@ svn_client__get_branch_to_branch_mergein
 svn_error_t *
 svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p,
                                      svn_boolean_t *has_rev_zero_history,
+                                     const char *url,
                                      svn_revnum_t peg_revnum,
                                      svn_revnum_t range_youngest,
                                      svn_revnum_t range_oldest,
@@ -907,7 +899,8 @@ svn_client__get_history_as_mergeinfo(svn
     range_youngest = peg_revnum;
   if (! SVN_IS_VALID_REVNUM(range_oldest))
     range_oldest = 0;
-  SVN_ERR(svn_client__repos_location_segments(&segments, ra_session, "",
+
+  SVN_ERR(svn_client__repos_location_segments(&segments, ra_session, url,
                                               peg_revnum, range_youngest,
                                               range_oldest, ctx, pool));
 
@@ -1213,7 +1206,7 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
                                       scratch_pool));
 
       SVN_ERR(svn_wc__node_get_origin(NULL, &rev, &repos_relpath,
-                                      &repos_root_url, NULL,
+                                      &repos_root_url, NULL, NULL,
                                       ctx->wc_ctx, local_abspath, FALSE,
                                       scratch_pool, scratch_pool));
 
@@ -1225,7 +1218,7 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
           || strcmp(origin_url, url) != 0
           || peg_rev != rev)
       {
-        use_url = TRUE; /* Don't rely on local merginfo */
+        use_url = TRUE; /* Don't rely on local mergeinfo */
       }
     }
 
@@ -1239,7 +1232,7 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
     {
       rev = peg_rev;
       SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
-        mergeinfo_catalog, ra_session, "", rev, svn_mergeinfo_inherited,
+        mergeinfo_catalog, ra_session, url, rev, svn_mergeinfo_inherited,
         FALSE, include_descendants,
         result_pool, scratch_pool));
     }
@@ -1610,7 +1603,7 @@ filter_log_entry_with_rangelist(void *ba
                           else
                             {
                               /* TARGET_PATH_AFFECTED inherited its mergeinfo,
-                                 se we have to ignore non-inheritable
+                                 so we have to ignore non-inheritable
                                  ranges. */
                               SVN_ERR(svn_rangelist_intersect(
                                 &intersection,
@@ -1874,9 +1867,12 @@ mergeinfo_log(svn_boolean_t finding_merg
   /* Open RA sessions to the repository for the source and target.
    * ### TODO: As the source and target must be in the same repository, we
    * should share a single session, tracking the two URLs separately. */
+#define TARGET_URL(target,pool) svn_path_url_add_component2((target)->repos_root_url, (target)->repos_relpath, pool)
   if (!finding_merged)
     {
       SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history, NULL,
+                                                   TARGET_URL(target,
+                                                              scratch_pool),
                                                    target->repos_revnum,
                                                    SVN_INVALID_REVNUM,
                                                    SVN_INVALID_REVNUM,
@@ -1890,6 +1886,8 @@ mergeinfo_log(svn_boolean_t finding_merg
     SVN_ERR(svn_client__peg_resolve(&source, &source_session, source_peg,
                                     ctx, scratch_pool, scratch_pool));
     SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
+                                                 TARGET_URL(target,
+                                                            scratch_pool),
                                                  source->repos_revnum,
                                                  SVN_INVALID_REVNUM,
                                                  SVN_INVALID_REVNUM,
@@ -2022,7 +2020,7 @@ mergeinfo_log(svn_boolean_t finding_merg
       else
         {
           /* Map SUBTREE_PATH to an empty rangelist if there was nothing
-             fully merged. e.g. Only empty or non-inheritable mergienfo
+             fully merged. e.g. Only empty or non-inheritable mergeinfo
              on the subtree or mergeinfo unrelated to the source. */
           apr_hash_set(inheritable_subtree_merges, subtree_path,
                        APR_HASH_KEY_STRING,

Modified: subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.h?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.h Mon Jan 16 12:32:43 2012
@@ -170,37 +170,40 @@ svn_client__get_wc_mergeinfo_catalog(svn
                                      apr_pool_t *result_pool,
                                      apr_pool_t *scratch_pool);
 
-/* Obtain any mergeinfo for repository filesystem path REL_PATH
-   (relative to RA_SESSION's session URL) from the repository, and set
+/* Obtain any mergeinfo for URL from the repository, and set
    it in *TARGET_MERGEINFO.
 
    INHERIT indicates whether explicit, explicit or inherited, or only
-   inherited mergeinfo for REL_PATH is obtained.
+   inherited mergeinfo for URL is obtained.
 
-   If REL_PATH does not exist at REV, SVN_ERR_FS_NOT_FOUND or
+   If URL does not exist at REV, SVN_ERR_FS_NOT_FOUND or
    SVN_ERR_RA_DAV_REQUEST_FAILED is returned and *TARGET_MERGEINFO
    is untouched.
 
-   If there is no mergeinfo available for REL_PATH, or if the server
+   If there is no mergeinfo available for URL, or if the server
    doesn't support a mergeinfo capability and SQUELCH_INCAPABLE is
    TRUE, set *TARGET_MERGEINFO to NULL. If the server doesn't support
    a mergeinfo capability and SQUELCH_INCAPABLE is FALSE, return an
-   SVN_ERR_UNSUPPORTED_FEATURE error. */
+   SVN_ERR_UNSUPPORTED_FEATURE error.
+
+   RA_SESSION is an open RA session to the repository in which URL lives;
+   it may be temporarily reparented by this function.
+*/
 svn_error_t *
 svn_client__get_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
                                 svn_ra_session_t *ra_session,
-                                const char *rel_path,
+                                const char *url,
                                 svn_revnum_t rev,
                                 svn_mergeinfo_inheritance_t inherit,
                                 svn_boolean_t squelch_incapable,
                                 apr_pool_t *pool);
 
 /* If INCLUDE_DESCENDANTS is FALSE, behave exactly like
-   svn_client__get_repos_mergeinfo() except the mergeinfo for REL_PATH
+   svn_client__get_repos_mergeinfo() except the mergeinfo for URL
    is put in the mergeinfo catalog MERGEINFO_CAT, with the key being
-   the repository root-relative path of REL_PATH.
+   the repository root-relative path of URL.
 
-   If INCLUDE_DESCENDANTS is true, then any subtrees under REL_PATH
+   If INCLUDE_DESCENDANTS is true, then any subtrees under URL
    with explicit mergeinfo are also included in MERGEINFO_CAT.  The
    keys for the subtree mergeinfo are the repository root-relative
    paths of the subtrees.  If no mergeinfo is found, then
@@ -208,7 +211,7 @@ svn_client__get_repos_mergeinfo(svn_merg
 svn_error_t *
 svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
                                         svn_ra_session_t *ra_session,
-                                        const char *rel_path,
+                                        const char *url,
                                         svn_revnum_t rev,
                                         svn_mergeinfo_inheritance_t inherit,
                                         svn_boolean_t squelch_incapable,
@@ -286,7 +289,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
   apr_pool_t *scratch_pool);
 
 /* Set *MERGEINFO_P to a mergeinfo constructed solely from the
-   natural history of RA_SESSION's session URL at PEG_REVNUM.
+   natural history of URL at PEG_REVNUM.
 
    If RANGE_YOUNGEST and RANGE_OLDEST are valid, use them to bound the
    revision ranges of returned mergeinfo.  They are governed by the same
@@ -294,10 +297,15 @@ svn_client__get_wc_or_repos_mergeinfo_ca
    svn_ra_get_location_segments().
 
    If HAS_REV_ZERO_HISTORY is not NULL, then set *HAS_REV_ZERO_HISTORY to
-   TRUE if the natural history includes revision 0, else to FALSE. */
+   TRUE if the natural history includes revision 0, else to FALSE.
+
+   RA_SESSION is an open RA session to the repository in which URL lives;
+   it may be temporarily reparented by this function.
+*/
 svn_error_t *
 svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p,
                                      svn_boolean_t *has_rev_zero_history,
+                                     const char *url,
                                      svn_revnum_t peg_revnum,
                                      svn_revnum_t range_youngest,
                                      svn_revnum_t range_oldest,

Modified: subversion/branches/showing-merge-info/subversion/libsvn_client/prop_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_client/prop_commands.c?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_client/prop_commands.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_client/prop_commands.c Mon Jan 16 12:32:43 2012
@@ -117,7 +117,8 @@ get_file_for_validation(const svn_string
 
 static
 svn_error_t *
-do_url_propset(const char *propname,
+do_url_propset(const char *url,
+               const char *propname,
                const svn_string_t *propval,
                const svn_node_kind_t kind,
                const svn_revnum_t base_revision_for_url,
@@ -133,8 +134,10 @@ do_url_propset(const char *propname,
   if (kind == svn_node_file)
     {
       void *file_baton;
-      SVN_ERR(editor->open_file("", root_baton, base_revision_for_url,
-                                pool, &file_baton));
+      const char *uri_basename = svn_uri_basename(url, pool);
+
+      SVN_ERR(editor->open_file(uri_basename, root_baton,
+                                base_revision_for_url, pool, &file_baton));
       SVN_ERR(editor->change_file_prop(file_baton, propname, propval, pool));
       SVN_ERR(editor->close_file(file_baton, NULL, pool));
     }
@@ -186,6 +189,18 @@ propset_on_url(const char *propname,
        _("Path '%s' does not exist in revision %ld"),
        target, base_revision_for_url);
 
+  if (node_kind == svn_node_file)
+    {
+      /* We need to reparent our session one directory up, since editor
+         semantics require the root is a directory.
+
+         ### How does this interact with authz? */
+      const char *parent_url;
+      parent_url = svn_uri_dirname(target, pool);
+
+      SVN_ERR(svn_ra_reparent(ra_session, parent_url, pool));
+    }
+
   /* Setting an inappropriate property is not allowed (unless
      overridden by 'skip_checks', in some circumstances).  Deleting an
      inappropriate property is allowed, however, since older clients
@@ -234,8 +249,8 @@ propset_on_url(const char *propname,
                                     NULL, TRUE, /* No lock tokens */
                                     pool));
 
-  err = do_url_propset(propname, propval, node_kind, base_revision_for_url,
-                       editor, edit_baton, pool);
+  err = do_url_propset(target, propname, propval, node_kind,
+                       base_revision_for_url, editor, edit_baton, pool);
 
   if (err)
     {

Modified: subversion/branches/showing-merge-info/subversion/libsvn_client/ra.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_client/ra.c?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_client/ra.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_client/ra.c Mon Jan 16 12:32:43 2012
@@ -404,6 +404,10 @@ svn_client_open_ra_session(svn_ra_sessio
    final resulting URL in *URL_P. REV_P and/or URL_P may be NULL if not
    wanted.
 
+   RA_SESSION should be an open RA session pointing at the URL of
+   PATH_OR_URL, or NULL, in which case this function will open its own
+   temporary session.
+
    Use authentication baton cached in CTX to authenticate against the
    repository.
 
@@ -423,9 +427,10 @@ resolve_rev_and_url(svn_revnum_t *rev_p,
   const char *url;
   svn_revnum_t rev;
 
+  /* Default revisions: peg -> working or head; operative -> peg. */
   SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev,
                                     svn_path_is_url(path_or_url),
-                                    TRUE,
+                                    TRUE /* notice_local_mods */,
                                     pool));
 
   /* Run the history function to get the object's (possibly
@@ -511,7 +516,6 @@ svn_client__ensure_ra_session_url(const 
                                   const char *session_url,
                                   apr_pool_t *pool)
 {
-  *old_session_url = NULL;
   SVN_ERR(svn_ra_get_session_url(ra_session, old_session_url, pool));
   if (! session_url)
     SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_url, pool));
@@ -562,7 +566,7 @@ compare_segments(const void *a, const vo
 svn_error_t *
 svn_client__repos_location_segments(apr_array_header_t **segments,
                                     svn_ra_session_t *ra_session,
-                                    const char *path,
+                                    const char *url,
                                     svn_revnum_t peg_revision,
                                     svn_revnum_t start_revision,
                                     svn_revnum_t end_revision,
@@ -570,14 +574,19 @@ svn_client__repos_location_segments(apr_
                                     apr_pool_t *pool)
 {
   struct gls_receiver_baton_t gls_receiver_baton;
+  const char *old_session_url;
+
   *segments = apr_array_make(pool, 8, sizeof(svn_location_segment_t *));
   gls_receiver_baton.segments = *segments;
   gls_receiver_baton.ctx = ctx;
   gls_receiver_baton.pool = pool;
-  SVN_ERR(svn_ra_get_location_segments(ra_session, path, peg_revision,
+  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+                                            url, pool));
+  SVN_ERR(svn_ra_get_location_segments(ra_session, "", peg_revision,
                                        start_revision, end_revision,
                                        gls_receiver, &gls_receiver_baton,
                                        pool));
+  SVN_ERR(svn_ra_reparent(ra_session, old_session_url, pool));
   qsort((*segments)->elts, (*segments)->nelts,
         (*segments)->elt_size, compare_segments);
   return SVN_NO_ERROR;
@@ -589,7 +598,9 @@ svn_client__repos_location_segments(apr_
  *
  * START_URL and/or END_URL may be NULL if not wanted.  START_REVNUM and
  * END_REVNUM must be valid revision numbers except that END_REVNUM may
- * be SVN_INVALID_REVNUM if END_URL is NULL.  RA_SESSION is required.
+ * be SVN_INVALID_REVNUM if END_URL is NULL.
+ *
+ * RA_SESSION is an open RA session parented at URL.
  */
 static svn_error_t *
 repos_locations(const char **start_url,
@@ -615,9 +626,9 @@ repos_locations(const char **start_url,
       && (end_revnum == peg_revnum || end_revnum == SVN_INVALID_REVNUM))
     {
       if (start_url)
-        *start_url = url;
+        *start_url = apr_pstrdup(result_pool, url);
       if (end_url)
-        *end_url = url;
+        *end_url = apr_pstrdup(result_pool, url);
       return SVN_NO_ERROR;
     }
 
@@ -671,10 +682,15 @@ svn_client__repos_location(const char **
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool)
 {
+  const char *old_session_url;
+
+  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+                                            peg_url, scratch_pool));
   SVN_ERR(repos_locations(op_url, NULL,
                           ra_session, peg_url, peg_revnum,
                           op_revnum, SVN_INVALID_REVNUM,
                           result_pool, scratch_pool));
+  SVN_ERR(svn_ra_reparent(ra_session, old_session_url, scratch_pool));
   return SVN_NO_ERROR;
 }
 
@@ -756,7 +772,7 @@ svn_client__repos_locations(const char *
           svn_boolean_t is_copy;
 
           SVN_ERR(svn_wc__node_get_origin(&is_copy, &peg_revnum, &repos_relpath,
-                                          &repos_root_url, NULL,
+                                          &repos_root_url, NULL, NULL,
                                           ctx->wc_ctx, local_abspath_or_url,
                                           FALSE, subpool, subpool));
 
@@ -846,55 +862,43 @@ svn_client__repos_locations(const char *
 
 svn_error_t *
 svn_client__get_youngest_common_ancestor(const char **ancestor_relpath,
+                                         const char **ancestor_url,
                                          svn_revnum_t *ancestor_revision,
-                                         const char *path_or_url1,
+                                         const char *url1,
                                          svn_revnum_t rev1,
-                                         const char *path_or_url2,
+                                         const char *url2,
                                          svn_revnum_t rev2,
                                          svn_client_ctx_t *ctx,
                                          apr_pool_t *pool)
 {
   apr_pool_t *sesspool = svn_pool_create(pool);
-  svn_ra_session_t *session1, *session2;
+  svn_ra_session_t *session;
+  const char *repos_root_url;
   apr_hash_t *history1, *history2;
   apr_hash_index_t *hi;
   svn_revnum_t yc_revision = SVN_INVALID_REVNUM;
   const char *yc_relpath = NULL;
-  svn_opt_revision_t revision1, revision2;
   svn_boolean_t has_rev_zero_history1;
   svn_boolean_t has_rev_zero_history2;
 
-  revision1.kind = revision2.kind = svn_opt_revision_number;
-  revision1.value.number = rev1;
-  revision2.value.number = rev2;
-
-  /* Open RA sessions for the two locations.
-   * ### TODO: As they are assumed to be in the same repository, we
-   * should share a single session, tracking the two URLs separately. */
-  {
-    SVN_ERR(svn_client__ra_session_from_path(&session1, NULL, NULL,
-                                             path_or_url1, NULL,
-                                             &revision1, &revision1,
-                                             ctx, sesspool));
-    SVN_ERR(svn_client__ra_session_from_path(&session2, NULL, NULL,
-                                             path_or_url2, NULL,
-                                             &revision2, &revision2,
-                                             ctx, sesspool));
-  }
+  /* Open an RA session for the two locations. */
+  SVN_ERR(svn_client_open_ra_session(&session, url1, ctx, sesspool));
+  SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, pool));
+
   /* We're going to cheat and use history-as-mergeinfo because it
      saves us a bunch of annoying custom data comparisons and such. */
   SVN_ERR(svn_client__get_history_as_mergeinfo(&history1,
                                                &has_rev_zero_history1,
-                                               rev1,
+                                               url1, rev1,
                                                SVN_INVALID_REVNUM,
                                                SVN_INVALID_REVNUM,
-                                               session1, ctx, pool));
+                                               session, ctx, pool));
   SVN_ERR(svn_client__get_history_as_mergeinfo(&history2,
                                                &has_rev_zero_history2,
-                                               rev2,
+                                               url2, rev2,
                                                SVN_INVALID_REVNUM,
                                                SVN_INVALID_REVNUM,
-                                               session2, ctx, pool));
+                                               session, ctx, pool));
   /* Close the source and target sessions. */
   svn_pool_destroy(sesspool);
 
@@ -937,7 +941,46 @@ svn_client__get_youngest_common_ancestor
       yc_revision = 0;
     }
 
-  *ancestor_relpath = yc_relpath;
-  *ancestor_revision = yc_revision;
+  if (ancestor_relpath)
+    *ancestor_relpath = yc_relpath;
+  if (ancestor_url)
+    *ancestor_url
+      = yc_relpath ? svn_path_url_add_component2(repos_root_url, yc_relpath,
+                                                 pool) : NULL;
+  if (ancestor_revision)
+    *ancestor_revision = yc_revision;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__youngest_common_ancestor(const char **ancestor_url,
+                                     svn_revnum_t *ancestor_rev,
+                                     const char *path_or_url1,
+                                     const svn_opt_revision_t *revision1,
+                                     const char *path_or_url2,
+                                     const svn_opt_revision_t *revision2,
+                                     svn_client_ctx_t *ctx,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool)
+{
+  apr_pool_t *sesspool = svn_pool_create(scratch_pool);
+  svn_ra_session_t *session;
+  const char *url1, *url2;
+  svn_revnum_t rev1, rev2;
+
+  /* Resolve the two locations */
+  SVN_ERR(svn_client__ra_session_from_path(&session, &rev1, &url1,
+                                           path_or_url1, NULL,
+                                           revision1, revision1,
+                                           ctx, sesspool));
+  SVN_ERR(resolve_rev_and_url(&rev2, &url2, session,
+                              path_or_url2, revision2, revision2,
+                              ctx, scratch_pool));
+
+  SVN_ERR(svn_client__get_youngest_common_ancestor(
+            NULL, ancestor_url, ancestor_rev,
+            url1, rev1, url2, rev2, ctx, result_pool));
+
+  svn_pool_destroy(sesspool);
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/showing-merge-info/subversion/libsvn_client/repos_diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_client/repos_diff.c?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_client/repos_diff.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_client/repos_diff.c Mon Jan 16 12:32:43 2012
@@ -478,38 +478,84 @@ open_root(void *edit_baton,
   return SVN_NO_ERROR;
 }
 
-/* Recursively walk tree rooted at DIR (at REVISION) in the repository,
+/* Compare a file being deleted against an empty file.
+ */
+static svn_error_t *
+diff_deleted_file(svn_wc_notify_state_t *state_p,
+                  svn_boolean_t *tree_conflicted_p,
+                  const char *path,
+                  struct edit_baton *eb,
+                  apr_pool_t *scratch_pool)
+{
+  struct file_baton *b = make_file_baton(path, FALSE, eb, scratch_pool);
+/*  struct edit_baton *eb = b->edit_baton;*/
+  const char *mimetype1, *mimetype2;
+
+  if (eb->cancel_func)
+    SVN_ERR(eb->cancel_func(eb->cancel_baton));
+
+  if (eb->text_deltas)
+    SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool));
+  else
+    SVN_ERR(get_empty_file(eb, &b->path_start_revision));
+  SVN_ERR(get_empty_file(eb, &b->path_end_revision));
+  get_file_mime_types(&mimetype1, &mimetype2, b);
+
+  SVN_ERR(eb->diff_callbacks->file_deleted(state_p, tree_conflicted_p,
+                                           b->path,
+                                           b->path_start_revision,
+                                           b->path_end_revision,
+                                           mimetype1, mimetype2,
+                                           b->pristine_props,
+                                           eb->diff_cmd_baton,
+                                           scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+/* Recursively walk tree rooted at DIR (at EB->revision) in the repository,
  * reporting all children as deleted.  Part of a workaround for issue 2333.
  *
- * DIR is a repository path relative to the URL in RA_SESSION.  REVISION
- * must be a valid revision number, not SVN_INVALID_REVNUM.  EB is the
- * overall crawler editor baton.  If CANCEL_FUNC is not NULL, then it
- * should refer to a cancellation function (along with CANCEL_BATON).
+ * DIR is a repository path relative to the URL in EB->ra_session.  EB is
+ * the overall crawler editor baton.  EB->revision must be a valid revision
+ * number, not SVN_INVALID_REVNUM.  Use EB->cancel_func (if not null) with
+ * EB->cancel_baton for cancellation.
  */
 /* ### TODO: Handle depth. */
 static svn_error_t *
-diff_deleted_dir(const char *dir,
-                 svn_revnum_t revision,
-                 svn_ra_session_t *ra_session,
+diff_deleted_dir(svn_wc_notify_state_t *state_p,
+                 svn_boolean_t *tree_conflicted_p,
+                 const char *dir,
                  struct edit_baton *eb,
-                 svn_cancel_func_t cancel_func,
-                 void *cancel_baton,
                  apr_pool_t *pool)
 {
   apr_hash_t *dirents;
   apr_pool_t *iterpool = svn_pool_create(pool);
   apr_hash_index_t *hi;
 
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
+  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(eb->revision));
 
-  if (cancel_func)
-    SVN_ERR(cancel_func(cancel_baton));
+  if (eb->cancel_func)
+    SVN_ERR(eb->cancel_func(eb->cancel_baton));
 
-  SVN_ERR(svn_ra_get_dir2(ra_session,
+  SVN_ERR(eb->diff_callbacks->dir_deleted(
+                        state_p, tree_conflicted_p, dir,
+                        eb->diff_cmd_baton, pool));
+
+  /* The "old" dir will be skipped by the repository report.  If required,
+   * crawl it recursively, diffing each file against the empty file.  This
+   * is a workaround for issue 2333 "'svn diff URL1 URL2' not reverse of
+   * 'svn diff URL2 URL1'". */
+  if (! eb->walk_deleted_repos_dirs)
+    {
+      svn_pool_destroy(iterpool);
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_ra_get_dir2(eb->ra_session,
                           &dirents,
                           NULL, NULL,
                           dir,
-                          revision,
+                          eb->revision,
                           SVN_DIRENT_KIND,
                           pool));
 
@@ -526,42 +572,12 @@ diff_deleted_dir(const char *dir,
 
       if (dirent->kind == svn_node_file)
         {
-          struct file_baton *b;
-          const char *mimetype1, *mimetype2;
-
-          /* Compare a file being deleted against an empty file */
-          b = make_file_baton(path, FALSE, eb, iterpool);
-          if (eb->text_deltas)
-            SVN_ERR(get_file_from_ra(b, FALSE, iterpool));
-          else
-            SVN_ERR(get_empty_file(eb, &b->path_start_revision));
-
-          SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
-
-          get_file_mime_types(&mimetype1, &mimetype2, b);
-
-          SVN_ERR(eb->diff_callbacks->file_deleted(
-                                NULL, NULL, b->path,
-                                b->path_start_revision,
-                                b->path_end_revision,
-                                mimetype1, mimetype2,
-                                b->pristine_props,
-                                b->edit_baton->diff_cmd_baton,
-                                iterpool));
+          SVN_ERR(diff_deleted_file(NULL, NULL, path, eb, iterpool));
         }
 
       if (dirent->kind == svn_node_dir)
         {
-          SVN_ERR(eb->diff_callbacks->dir_deleted(
-                                NULL, NULL, path,
-                                eb->diff_cmd_baton, iterpool));
-          SVN_ERR(diff_deleted_dir(path,
-                                   revision,
-                                   ra_session,
-                                   eb,
-                                   cancel_func,
-                                   cancel_baton,
-                                   iterpool));
+          SVN_ERR(diff_deleted_dir(NULL, NULL, path, eb, iterpool));
         }
     }
 
@@ -599,51 +615,14 @@ delete_entry(const char *path,
     {
     case svn_node_file:
       {
-        const char *mimetype1, *mimetype2;
-        struct file_baton *b;
-
-        /* Compare a file being deleted against an empty file */
-        b = make_file_baton(path, FALSE, eb, scratch_pool);
-        if (eb->text_deltas)
-          SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool));
-        else
-          SVN_ERR(get_empty_file(eb, &b->path_start_revision));
-
-        SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
-
-        get_file_mime_types(&mimetype1, &mimetype2, b);
-
-        SVN_ERR(eb->diff_callbacks->file_deleted(
-                     &state, &tree_conflicted, b->path,
-                     b->path_start_revision,
-                     b->path_end_revision,
-                     mimetype1, mimetype2,
-                     b->pristine_props,
-                     b->edit_baton->diff_cmd_baton,
-                     scratch_pool));
-
+        SVN_ERR(diff_deleted_file(&state, &tree_conflicted, path, eb,
+                                  scratch_pool));
         break;
       }
     case svn_node_dir:
       {
-        SVN_ERR(eb->diff_callbacks->dir_deleted(
-                     &state, &tree_conflicted,
-                     path,
-                     eb->diff_cmd_baton, scratch_pool));
-
-        if (eb->walk_deleted_repos_dirs)
-          {
-            /* A workaround for issue 2333.  The "old" dir will be
-            skipped by the repository report.  Crawl it recursively,
-            diffing each file against the empty file. */
-            SVN_ERR(diff_deleted_dir(path,
-                                     eb->revision,
-                                     eb->ra_session,
-                                     eb,
-                                     eb->cancel_func,
-                                     eb->cancel_baton,
-                                     scratch_pool));
-          }
+        SVN_ERR(diff_deleted_dir(&state, &tree_conflicted, path, eb,
+                                 scratch_pool));
         break;
       }
     default:
@@ -1328,6 +1307,96 @@ absent_file(const char *path,
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+fetch_kind_func(svn_kind_t *kind,
+                void *baton,
+                const char *path,
+                svn_revnum_t base_revision,
+                apr_pool_t *scratch_pool)
+{
+  struct edit_baton *eb = baton;
+  svn_node_kind_t node_kind;
+
+  SVN_ERR(svn_ra_check_path(eb->ra_session, path, eb->revision, &node_kind,
+                            scratch_pool));
+
+  *kind = svn__kind_from_node_kind(node_kind, FALSE);
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_props_func(apr_hash_t **props,
+                 void *baton,
+                 const char *path,
+                 svn_revnum_t base_revision,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
+{
+  struct edit_baton *eb = baton;
+  svn_node_kind_t node_kind;
+
+  SVN_ERR(svn_ra_check_path(eb->ra_session, path, eb->revision, &node_kind,
+                            scratch_pool));
+
+  if (node_kind == svn_node_file)
+    {
+      SVN_ERR(svn_ra_get_file(eb->ra_session, path, eb->revision,
+                              NULL, NULL, props, result_pool));
+    }
+  else if (node_kind == svn_node_dir)
+    {
+      apr_array_header_t *tmp_props;
+
+      SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
+                              eb->revision, 0 /* Dirent fields */,
+                              result_pool));
+      tmp_props = svn_prop_hash_to_array(*props, result_pool);
+      SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
+                                   result_pool));
+      *props = svn_prop_array_to_hash(tmp_props, result_pool);
+    }
+  else
+    {
+      *props = apr_hash_make(result_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_base_func(const char **filename,
+                void *baton,
+                const char *path,
+                svn_revnum_t base_revision,
+                apr_pool_t *result_pool,
+                apr_pool_t *scratch_pool)
+{
+  struct edit_baton *eb = baton;
+  svn_stream_t *fstream;
+  svn_error_t *err;
+
+  SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
+                                 svn_io_file_del_on_pool_cleanup,
+                                 result_pool, scratch_pool));
+
+  err = svn_ra_get_file(eb->ra_session, path, eb->revision,
+                        fstream, NULL, NULL, scratch_pool);
+  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+    {
+      svn_error_clear(err);
+      SVN_ERR(svn_stream_close(fstream));
+
+      *filename = NULL;
+      return SVN_NO_ERROR;
+    }
+  else if (err)
+    return svn_error_trace(err);
+  
+  SVN_ERR(svn_stream_close(fstream));
+
+  return SVN_NO_ERROR;
+}
+
 /* Create a repository diff editor and baton.  */
 svn_error_t *
 svn_client__get_diff_editor(const svn_delta_editor_t **editor,
@@ -1389,6 +1458,11 @@ svn_client__get_diff_editor(const svn_de
                                             editor, edit_baton,
                                             eb->pool));
 
+  shim_callbacks->fetch_kind_func = fetch_kind_func;
+  shim_callbacks->fetch_props_func = fetch_props_func;
+  shim_callbacks->fetch_base_func = fetch_base_func;
+  shim_callbacks->fetch_baton = eb;
+
   SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
                                    shim_callbacks,
                                    result_pool, result_pool));

Modified: subversion/branches/showing-merge-info/subversion/libsvn_client/switch.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_client/switch.c?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_client/switch.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_client/switch.c Mon Jan 16 12:32:43 2012
@@ -197,7 +197,7 @@ switch_internal(svn_revnum_t *result_rev
      ### okay? */
   if (! ignore_ancestry)
     {
-      const char *target_url, *yc_relpath;
+      const char *target_url, *yc_url;
       svn_revnum_t target_rev, yc_rev;
 
       SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, local_abspath,
@@ -206,11 +206,11 @@ switch_internal(svn_revnum_t *result_rev
                                         local_abspath, pool));
       /* ### It would be nice if this function could reuse the existing
              ra session instead of opening two for its own use. */
-      SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_relpath, &yc_rev,
+      SVN_ERR(svn_client__get_youngest_common_ancestor(NULL, &yc_url, &yc_rev,
                                                        switch_rev_url, revnum,
                                                        target_url, target_rev,
                                                        ctx, pool));
-      if (! (yc_relpath && SVN_IS_VALID_REVNUM(yc_rev)))
+      if (! (yc_url && SVN_IS_VALID_REVNUM(yc_rev)))
         return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
                                  _("'%s' shares no common ancestry with '%s'"),
                                  switch_url, local_abspath);

Modified: subversion/branches/showing-merge-info/subversion/libsvn_delta/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_delta/compat.c?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_delta/compat.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_delta/compat.c Mon Jan 16 12:32:43 2012
@@ -71,7 +71,7 @@ svn_compat_wrap_file_rev_handler(svn_fil
                                  void *handler_baton,
                                  apr_pool_t *pool)
 {
-  struct file_rev_handler_wrapper_baton *fwb = apr_palloc(pool, sizeof(*fwb));
+  struct file_rev_handler_wrapper_baton *fwb = apr_pcalloc(pool, sizeof(*fwb));
 
   /* Set the user provided old format callback in the baton. */
   fwb->baton = handler_baton;
@@ -95,35 +95,34 @@ svn_compat_wrap_file_rev_handler(svn_fil
 
 typedef svn_error_t *(*start_edit_func_t)(
     void *baton,
-    svn_revnum_t base_revision,
-    apr_pool_t *result_pool,
-    void **root_baton);
+    svn_revnum_t base_revision);
 
 typedef svn_error_t *(*target_revision_func_t)(
     void *baton,
     svn_revnum_t target_revision,
     apr_pool_t *scratch_pool);
 
+/* svn_editor__See insert_shims() for more information. */
+struct extra_baton
+{
+  start_edit_func_t start_edit;
+  target_revision_func_t target_revision;
+  void *baton;
+};
+
 struct ev2_edit_baton
 {
   svn_editor_t *editor;
   apr_hash_t *paths;
   apr_pool_t *edit_pool;
+  struct extra_baton *exb;
 
   svn_boolean_t *found_abs_paths; /* Did we strip an incoming '/' from the
                                      paths?  */
 
-  void *root_baton;
-
-  start_edit_func_t start_edit;
-  void *start_edit_baton;
-
   svn_delta_fetch_props_func_t fetch_props_func;
   void *fetch_props_baton;
 
-  target_revision_func_t target_revision_func;
-  void *target_revision_baton;
-
   svn_delta_fetch_base_func_t fetch_base_func;
   void *fetch_base_baton;
 };
@@ -132,12 +131,17 @@ struct ev2_dir_baton
 {
   struct ev2_edit_baton *eb;
   const char *path;
+  svn_revnum_t base_revision;
+
+  const char *copyfrom_path;
+  svn_revnum_t copyfrom_rev;
 };
 
 struct ev2_file_baton
 {
   struct ev2_edit_baton *eb;
   const char *path;
+  svn_revnum_t base_revision;
   const char *delta_base;
 };
 
@@ -163,6 +167,7 @@ struct path_action
 struct prop_args
 {
   const char *name;
+  svn_revnum_t base_revision;
   const svn_string_t *value;
 };
 
@@ -175,6 +180,7 @@ struct copy_args
 struct path_checksum_args
 {
   const char *path;
+  svn_revnum_t base_revision;
   svn_checksum_t *checksum;
 };
 
@@ -214,9 +220,16 @@ process_actions(void *edit_baton,
   struct ev2_edit_baton *eb = edit_baton;
   apr_hash_t *props = NULL;
   svn_boolean_t need_add = FALSE;
+  svn_boolean_t need_delete = FALSE;
+  svn_boolean_t need_copy = FALSE;
+  const char *copyfrom_path;
+  svn_revnum_t copyfrom_rev;
   apr_array_header_t *children;
   svn_stream_t *contents = NULL;
   svn_checksum_t *checksum = NULL;
+  svn_revnum_t delete_revnum = SVN_INVALID_REVNUM;
+  svn_revnum_t props_base_revision = SVN_INVALID_REVNUM;
+  svn_revnum_t text_base_revision = SVN_INVALID_REVNUM;
   svn_kind_t kind;
   int i;
 
@@ -239,13 +252,18 @@ process_actions(void *edit_baton,
             {
               const struct prop_args *p_args = action->args;
 
+              if (!SVN_IS_VALID_REVNUM(props_base_revision))
+                props_base_revision = p_args->base_revision;
+              else
+                SVN_ERR_ASSERT(p_args->base_revision == props_base_revision);
+
               if (!props)
                 {
                   /* Fetch the original props. We can then apply each of
                      the modifications to it.  */
                   SVN_ERR(eb->fetch_props_func(&props,
                                                eb->fetch_props_baton,
-                                               path,
+                                               path, props_base_revision,
                                                scratch_pool, scratch_pool));
                 }
 
@@ -257,12 +275,8 @@ process_actions(void *edit_baton,
 
           case ACTION_DELETE:
             {
-              svn_revnum_t *revnum = action->args;
-
-              /* If we get a delete, we'd better not have gotten any
-                 other actions for this path later, so we can go ahead
-                 and call our handler. */
-              SVN_ERR(svn_editor_delete(eb->editor, path, *revnum));
+              delete_revnum = *((svn_revnum_t *) action->args);
+              need_delete = TRUE;
               break;
             }
 
@@ -293,6 +307,12 @@ process_actions(void *edit_baton,
               SVN_ERR(svn_stream_open_readonly(&contents, pca->path,
                                                scratch_pool, scratch_pool));
               checksum = pca->checksum;
+
+              if (!SVN_IS_VALID_REVNUM(text_base_revision))
+                text_base_revision = pca->base_revision;
+              else
+                SVN_ERR_ASSERT(pca->base_revision == text_base_revision);
+
               break;
             }
 
@@ -300,9 +320,9 @@ process_actions(void *edit_baton,
             {
               struct copy_args *c_args = action->args;
 
-              SVN_ERR(svn_editor_copy(eb->editor, c_args->copyfrom_path,
-                                      c_args->copyfrom_rev, path,
-                                      SVN_INVALID_REVNUM));
+              copyfrom_path = c_args->copyfrom_path;
+              copyfrom_rev = c_args->copyfrom_rev;
+              need_copy = TRUE;
               break;
             }
 
@@ -322,35 +342,47 @@ process_actions(void *edit_baton,
   /* We've now got a wholistic view of what has happened to this node,
    * so we can call our own editor APIs on it. */
 
+  if (need_delete && !need_add && !need_copy)
+    {
+      /* If we're only doing a delete, do it here. */
+      SVN_ERR(svn_editor_delete(eb->editor, path, delete_revnum));
+    }
+
   if (need_add)
     {
       if (kind == svn_kind_dir)
         {
           SVN_ERR(svn_editor_add_directory(eb->editor, path, children,
-                                           props, SVN_INVALID_REVNUM));
+                                           props, delete_revnum));
         }
       else
         {
           SVN_ERR(svn_editor_add_file(eb->editor, path, checksum, contents,
-                                      props, SVN_INVALID_REVNUM));
+                                      props, delete_revnum));
         }
+
+      return SVN_NO_ERROR;
     }
-  else
+
+  if (need_copy)
     {
-      if (props)
-        {
-          /* We fetched and modified the props in some way. Apply 'em now that
-             we have the new set.  */
-          SVN_ERR(svn_editor_set_props(eb->editor, path, SVN_INVALID_REVNUM,
-                                       props, contents == NULL));
-        }
+      SVN_ERR(svn_editor_copy(eb->editor, copyfrom_path, copyfrom_rev, path,
+                              delete_revnum));
+    }
 
-      if (contents)
-        {
-          /* If we have an content for this node, set it now. */
-          SVN_ERR(svn_editor_set_text(eb->editor, path, SVN_INVALID_REVNUM,
-                                      checksum, contents));
-        }
+  if (props)
+    {
+      /* We fetched and modified the props in some way. Apply 'em now that
+         we have the new set.  */
+      SVN_ERR(svn_editor_set_props(eb->editor, path, props_base_revision,
+                                   props, contents == NULL));
+    }
+
+  if (contents)
+    {
+      /* If we have an content for this node, set it now. */
+      SVN_ERR(svn_editor_set_text(eb->editor, path, text_base_revision,
+                                  checksum, contents));
     }
 
   return SVN_NO_ERROR;
@@ -381,6 +413,9 @@ run_ev2_actions(void *edit_baton,
 
       svn_pool_clear(iterpool);
       SVN_ERR(process_actions(edit_baton, path, actions, iterpool));
+
+      /* Remove this item from the hash. */
+      apr_hash_set(eb->paths, path, APR_HASH_KEY_STRING, NULL);
     }
   svn_pool_destroy(iterpool);
 
@@ -394,8 +429,10 @@ ev2_set_target_revision(void *edit_baton
 {
   struct ev2_edit_baton *eb = edit_baton;
 
-  SVN_ERR(eb->target_revision_func(eb->target_revision_baton, target_revision,
-                                   scratch_pool));
+  if (eb->exb->target_revision)
+    SVN_ERR(eb->exb->target_revision(eb->exb->baton, target_revision,
+                                     scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -405,17 +442,17 @@ ev2_open_root(void *edit_baton,
               apr_pool_t *result_pool,
               void **root_baton)
 {
-  struct ev2_dir_baton *db = apr_palloc(result_pool, sizeof(*db));
+  struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db));
   struct ev2_edit_baton *eb = edit_baton;
 
   db->eb = eb;
   db->path = "";
+  db->base_revision = base_revision;
 
   *root_baton = db;
 
-  if (eb->start_edit)
-    SVN_ERR(eb->start_edit(eb->start_edit_baton, base_revision, result_pool,
-                           &eb->root_baton));
+  if (eb->exb->start_edit)
+    SVN_ERR(eb->exb->start_edit(eb->exb->baton, base_revision));
 
   return SVN_NO_ERROR;
 }
@@ -444,10 +481,11 @@ ev2_add_directory(const char *path,
                   void **child_baton)
 {
   struct ev2_dir_baton *pb = parent_baton;
-  struct ev2_dir_baton *cb = apr_palloc(result_pool, sizeof(*cb));
+  struct ev2_dir_baton *cb = apr_pcalloc(result_pool, sizeof(*cb));
 
   cb->eb = pb->eb;
   cb->path = apr_pstrdup(result_pool, path);
+  cb->base_revision = pb->base_revision;
   *child_baton = cb;
 
   if (!copyfrom_path)
@@ -457,6 +495,14 @@ ev2_add_directory(const char *path,
 
       *kind = svn_kind_dir;
       SVN_ERR(add_action(pb->eb, path, ACTION_ADD, kind));
+
+      if (pb->copyfrom_path)
+        {
+          const char *name = svn_relpath_basename(path, result_pool);
+          cb->copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
+                                          "/", name, NULL);
+          cb->copyfrom_rev = pb->copyfrom_rev;
+        }
     }
   else
     {
@@ -466,6 +512,9 @@ ev2_add_directory(const char *path,
       args->copyfrom_path = apr_pstrdup(pb->eb->edit_pool, copyfrom_path);
       args->copyfrom_rev = copyfrom_revision;
       SVN_ERR(add_action(pb->eb, path, ACTION_COPY, args));
+
+      cb->copyfrom_path = args->copyfrom_path;
+      cb->copyfrom_rev = args->copyfrom_rev;
     }
 
   return SVN_NO_ERROR;
@@ -479,10 +528,21 @@ ev2_open_directory(const char *path,
                    void **child_baton)
 {
   struct ev2_dir_baton *pb = parent_baton;
-  struct ev2_dir_baton *db = apr_palloc(result_pool, sizeof(*db));
+  struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db));
 
   db->eb = pb->eb;
   db->path = apr_pstrdup(result_pool, path);
+  db->base_revision = base_revision;
+
+  if (pb->copyfrom_path)
+    {
+      /* We are inside a copy. */
+      const char *name = svn_relpath_basename(path, result_pool);
+
+      db->copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
+                                      "/", name, NULL);
+      db->copyfrom_rev = pb->copyfrom_rev;
+    }
 
   *child_baton = db;
   return SVN_NO_ERROR;
@@ -499,6 +559,7 @@ ev2_change_dir_prop(void *dir_baton,
 
   p_args->name = apr_pstrdup(db->eb->edit_pool, name);
   p_args->value = value ? svn_string_dup(value, db->eb->edit_pool) : NULL;
+  p_args->base_revision = db->base_revision;
 
   SVN_ERR(add_action(db->eb, db->path, ACTION_PROPSET, p_args));
 
@@ -534,22 +595,22 @@ ev2_add_file(const char *path,
              apr_pool_t *result_pool,
              void **file_baton)
 {
-  struct ev2_file_baton *fb = apr_palloc(result_pool, sizeof(*fb));
+  struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
   struct ev2_dir_baton *pb = parent_baton;
 
   fb->eb = pb->eb;
   fb->path = apr_pstrdup(result_pool, path);
+  fb->base_revision = pb->base_revision;
   *file_baton = fb;
 
-  SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
-                                  fb->eb->fetch_base_baton,
-                                  path, result_pool, result_pool));
-
   if (!copyfrom_path)
     {
       /* A simple add. */
       svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
 
+      /* Don't bother fetching the base, as in an add we don't have a base. */
+      fb->delta_base = NULL;
+
       *kind = svn_kind_file;
       SVN_ERR(add_action(pb->eb, path, ACTION_ADD, kind));
     }
@@ -558,6 +619,11 @@ ev2_add_file(const char *path,
       /* A copy */
       struct copy_args *args = apr_palloc(pb->eb->edit_pool, sizeof(*args));
 
+      SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
+                                      fb->eb->fetch_base_baton,
+                                      copyfrom_path, copyfrom_revision,
+                                      result_pool, result_pool));
+
       args->copyfrom_path = apr_pstrdup(pb->eb->edit_pool, copyfrom_path);
       args->copyfrom_rev = copyfrom_revision;
       SVN_ERR(add_action(pb->eb, path, ACTION_COPY, args));
@@ -573,15 +639,33 @@ ev2_open_file(const char *path,
               apr_pool_t *result_pool,
               void **file_baton)
 {
-  struct ev2_file_baton *fb = apr_palloc(result_pool, sizeof(*fb));
+  struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
   struct ev2_dir_baton *pb = parent_baton;
 
   fb->eb = pb->eb;
   fb->path = apr_pstrdup(result_pool, path);
+  fb->base_revision = base_revision;
 
-  SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
-                                  fb->eb->fetch_base_baton,
-                                  path, result_pool, result_pool));
+  if (pb->copyfrom_path)
+    {
+      /* We're in a copied directory, so the delta base is going to be
+         based up on the copy source. */
+      const char *name = svn_relpath_basename(path, result_pool);
+      const char *copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
+                                              "/", name, NULL);
+
+      SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
+                                      fb->eb->fetch_base_baton,
+                                      copyfrom_path, pb->copyfrom_rev,
+                                      result_pool, result_pool));
+    }
+  else
+    {
+      SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
+                                      fb->eb->fetch_base_baton,
+                                      path, base_revision,
+                                      result_pool, result_pool));
+    }
 
   *file_baton = fb;
   return SVN_NO_ERROR;
@@ -626,6 +710,8 @@ ev2_apply_textdelta(void *file_baton,
   struct path_checksum_args *pca = apr_pcalloc(fb->eb->edit_pool,
                                                sizeof(*pca));
 
+  pca->base_revision = fb->base_revision;
+
   if (! fb->delta_base)
     source = svn_stream_empty(handler_pool);
   else
@@ -667,6 +753,7 @@ ev2_change_file_prop(void *file_baton,
 
   p_args->name = apr_pstrdup(fb->eb->edit_pool, name);
   p_args->value = value ? svn_string_dup(value, fb->eb->edit_pool) : NULL;
+  p_args->base_revision = fb->base_revision;
 
   SVN_ERR(add_action(fb->eb, fb->path, ACTION_PROPSET, p_args));
 
@@ -715,39 +802,6 @@ ev2_abort_edit(void *edit_baton,
   return svn_error_trace(svn_editor_abort(eb->editor));
 }
 
-struct start_edit_baton
-{
-  const svn_delta_editor_t *deditor;
-  void *dedit_baton;
-};
-
-static svn_error_t *
-start_edit_func(void *baton,
-                svn_revnum_t base_revision,
-                apr_pool_t *result_pool,
-                void **root_baton)
-{
-  struct start_edit_baton *seb = baton;
-
-  SVN_ERR(seb->deditor->open_root(seb->dedit_baton, base_revision, result_pool,
-                                  root_baton));
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-target_revision_func(void *baton,
-                     svn_revnum_t target_revision,
-                     apr_pool_t *scratch_pool)
-{
-  struct start_edit_baton *seb = baton;
-
-  SVN_ERR(seb->deditor->set_target_revision(seb->dedit_baton, target_revision,
-                                            scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
 static svn_error_t *
 delta_from_editor(const svn_delta_editor_t **deditor,
                   void **dedit_baton,
@@ -757,10 +811,7 @@ delta_from_editor(const svn_delta_editor
                   void *fetch_props_baton,
                   svn_delta_fetch_base_func_t fetch_base_func,
                   void *fetch_base_baton,
-                  start_edit_func_t start_edit,
-                  void *start_edit_baton,
-                  target_revision_func_t target_revision,
-                  void *target_revision_baton,
+                  struct extra_baton *exb,
                   apr_pool_t *pool)
 {
   /* Static 'cause we don't want it to be on the stack. */
@@ -789,19 +840,14 @@ delta_from_editor(const svn_delta_editor
   eb->edit_pool = pool;
   eb->found_abs_paths = found_abs_paths;
   *eb->found_abs_paths = FALSE;
+  eb->exb = exb;
 
   eb->fetch_props_func = fetch_props_func;
   eb->fetch_props_baton = fetch_props_baton;
 
-  eb->start_edit = start_edit;
-  eb->start_edit_baton = start_edit_baton;
-
   eb->fetch_base_func = fetch_base_func;
   eb->fetch_base_baton = fetch_base_baton;
 
-  eb->target_revision_func = target_revision;
-  eb->target_revision_baton = target_revision_baton;
-
   *dedit_baton = eb;
   *deditor = &delta_editor;
 
@@ -823,7 +869,10 @@ struct operation {
                             occuring; directories are OP_OPEN with non-empty
                             props */
   } operation;
+
+  const char *path;
   svn_kind_t kind;  /* to copy, mkdir, put or set revprops */
+  svn_revnum_t base_revision;       /* When committing, the base revision */
   svn_revnum_t copyfrom_revision;      /* to copy, valid for add and replace */
   const char *copyfrom_url;       /* to copy, valid for add and replace */
   const char *src_file;  /* for put, the source file for contents */
@@ -861,6 +910,7 @@ struct editor_baton
 static struct operation *
 get_operation(const char *path,
               struct operation *operation,
+              svn_revnum_t base_revision,
               apr_pool_t *result_pool)
 {
   struct operation *child = apr_hash_get(operation->children, path,
@@ -869,9 +919,11 @@ get_operation(const char *path,
     {
       child = apr_pcalloc(result_pool, sizeof(*child));
       child->children = apr_hash_make(result_pool);
+      child->path = apr_pstrdup(result_pool, path);
       child->operation = OP_OPEN;
       child->copyfrom_revision = SVN_INVALID_REVNUM;
       child->kind = svn_kind_dir;
+      child->base_revision = base_revision;
       child->prop_mods = apr_hash_make(result_pool);
       child->prop_dels = apr_array_make(result_pool, 1, sizeof(const char *));
       apr_hash_set(operation->children, apr_pstrdup(result_pool, path),
@@ -925,7 +977,7 @@ build(struct editor_baton *eb,
     {
       const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
       path_so_far = svn_relpath_join(path_so_far, path_bit, scratch_pool);
-      operation = get_operation(path_so_far, operation, eb->edit_pool);
+      operation = get_operation(path_so_far, operation, head, eb->edit_pool);
     }
 
   /* Handle property changes. */
@@ -936,16 +988,14 @@ build(struct editor_baton *eb,
 
       if (kind == svn_kind_unknown)
         SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
-                                    relpath, scratch_pool));
+                                    relpath, rev, scratch_pool));
       else
         operation->kind = kind;
 
       SVN_ERR(eb->fetch_props_func(&current_props, eb->fetch_props_baton,
-                                   relpath, scratch_pool, scratch_pool));
+                                   relpath, rev, scratch_pool, scratch_pool));
 
-      /* Use the edit pool, since most of the results will need to be
-         persisted. */
-      SVN_ERR(svn_prop_diffs(&propdiffs, props, current_props, eb->edit_pool));
+      SVN_ERR(svn_prop_diffs(&propdiffs, props, current_props, scratch_pool));
 
       for (i = 0; i < propdiffs->nelts; i++)
         {
@@ -953,10 +1003,11 @@ build(struct editor_baton *eb,
              actual structures, not pointers to them. */
           svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
           if (!prop->value)
-            APR_ARRAY_PUSH(operation->prop_dels, const char *) = prop->name;
+            APR_ARRAY_PUSH(operation->prop_dels, const char *) = 
+                                        apr_pstrdup(eb->edit_pool, prop->name);
           else
             apr_hash_set(operation->prop_mods, prop->name, APR_HASH_KEY_STRING,
-                         prop->value);
+                         svn_string_dup(prop->value, eb->edit_pool));
         }
 
       /* If we're not adding this thing ourselves, check for existence.  */
@@ -984,9 +1035,9 @@ build(struct editor_baton *eb,
       operation->operation =
         operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
 
-      if (kind == svn_kind_none)
+      if (kind == svn_kind_none || kind == svn_kind_unknown)
         SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
-                                    relpath, scratch_pool));
+                                    url, rev, scratch_pool));
       else
         operation->kind = kind;
       operation->copyfrom_url = url;
@@ -1006,7 +1057,7 @@ build(struct editor_baton *eb,
         {
           operation->operation = OP_REPLACE;
         }
-      else
+      else if (operation->operation == OP_OPEN)
         {
           if (kind == svn_kind_file)
             operation->operation = OP_OPEN;
@@ -1033,7 +1084,7 @@ ensure_root_opened(struct editor_baton *
 {
   if (!eb->root_opened)
     {
-      SVN_ERR(eb->deditor->open_root(eb->dedit_baton, SVN_INVALID_REVNUM,
+      SVN_ERR(eb->deditor->open_root(eb->dedit_baton, eb->root.base_revision,
                                      eb->edit_pool, &eb->root.baton));
       eb->root_opened = TRUE;
     }
@@ -1054,6 +1105,15 @@ add_directory_cb(void *baton,
 
   SVN_ERR(ensure_root_opened(eb));
 
+  if (SVN_IS_VALID_REVNUM(replaces_rev))
+    {
+      /* We need to add the delete action. */
+
+      SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
+                    NULL, SVN_INVALID_REVNUM,
+                    NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+    }
+
   SVN_ERR(build(eb, ACTION_MKDIR, relpath, svn_kind_dir,
                 NULL, SVN_INVALID_REVNUM,
                 NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
@@ -1082,6 +1142,15 @@ add_file_cb(void *baton,
 
   SVN_ERR(ensure_root_opened(eb));
 
+  if (SVN_IS_VALID_REVNUM(replaces_rev))
+    {
+      /* We need to add the delete action. */
+
+      SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
+                    NULL, SVN_INVALID_REVNUM,
+                    NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+    }
+
   /* Spool the contents to a tempfile, and provide that to the driver. */
   SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL,
                                  svn_io_file_del_on_pool_cleanup,
@@ -1114,6 +1183,15 @@ add_symlink_cb(void *baton,
 
   SVN_ERR(ensure_root_opened(eb));
 
+  if (SVN_IS_VALID_REVNUM(replaces_rev))
+    {
+      /* We need to add the delete action. */
+
+      SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
+                    NULL, SVN_INVALID_REVNUM,
+                    NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -1151,7 +1229,7 @@ set_props_cb(void *baton,
 
   SVN_ERR(build(eb, ACTION_PROPSET, relpath, svn_kind_unknown,
                 NULL, SVN_INVALID_REVNUM,
-                props, NULL, SVN_INVALID_REVNUM, scratch_pool));
+                props, NULL, revision, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -1180,7 +1258,7 @@ set_text_cb(void *baton,
 
   SVN_ERR(build(eb, ACTION_PUT, relpath, svn_kind_file,
                 NULL, SVN_INVALID_REVNUM,
-                NULL, tmp_filename, SVN_INVALID_REVNUM, scratch_pool));
+                NULL, tmp_filename, revision, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -1231,6 +1309,15 @@ copy_cb(void *baton,
 
   SVN_ERR(ensure_root_opened(eb));
 
+  if (SVN_IS_VALID_REVNUM(replaces_rev))
+    {
+      /* We need to add the delete action. */
+
+      SVN_ERR(build(eb, ACTION_DELETE, dst_relpath, svn_kind_unknown,
+                    NULL, SVN_INVALID_REVNUM,
+                    NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+    }
+
   SVN_ERR(build(eb, ACTION_COPY, dst_relpath, svn_kind_unknown,
                 src_relpath, src_revision, NULL, NULL, SVN_INVALID_REVNUM,
                 scratch_pool));
@@ -1302,117 +1389,145 @@ change_props(const svn_delta_editor_t *e
 }
 
 static svn_error_t *
-drive_tree(const struct operation *operation,
+drive_tree(struct operation *op,
+           const struct operation *parent_op,
            const svn_delta_editor_t *editor,
-           svn_boolean_t *make_abs_paths,
+           svn_boolean_t make_abs_paths,
            apr_pool_t *scratch_pool)
 {
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-  apr_hash_index_t *hi;
+  const char *path = op->path;
 
-  for (hi = apr_hash_first(scratch_pool, operation->children);
-       hi; hi = apr_hash_next(hi))
+  if (path[0] != '/' && make_abs_paths)
+    path = apr_pstrcat(scratch_pool, "/", path, NULL);
+
+  /* Deletes and replacements are simple -- just delete the thing. */
+  if (op->operation == OP_DELETE || op->operation == OP_REPLACE)
     {
-      struct operation *child;
-      const char *path;
-      void *file_baton = NULL;
+      SVN_ERR(editor->delete_entry(path, SVN_INVALID_REVNUM,
+                                   parent_op->baton, scratch_pool));
+    }
 
-      svn_pool_clear(iterpool);
-      child = svn__apr_hash_index_val(hi);
-      path = svn__apr_hash_index_key(hi);
+  if (op->kind == svn_kind_dir)
+    {
+      /* Open or create our baton. */
+      if (op->operation == OP_OPEN || op->operation == OP_PROPSET)
+        SVN_ERR(editor->open_directory(path, parent_op->baton,
+                                       parent_op->base_revision,
+                                       scratch_pool, &op->baton));
 
-      if (path[0] != '/' && *make_abs_paths)
-        path = apr_pstrcat(iterpool, "/", path, NULL);
+      else if (op->operation == OP_ADD || op->operation == OP_REPLACE)
+        SVN_ERR(editor->add_directory(path, parent_op->baton,
+                                      op->copyfrom_url, op->copyfrom_revision,
+                                      scratch_pool, &op->baton));
 
-      /* Deletes are simple -- just delete the thing. */
-      if (child->operation == OP_DELETE)
-        {
-          SVN_ERR(editor->delete_entry(path, SVN_INVALID_REVNUM,
-                                       operation->baton, iterpool));
-        }
+      else if (op->operation == OP_ADD_ABSENT)
+        SVN_ERR(editor->absent_directory(path, parent_op->baton,
+                                         scratch_pool));
 
-      if (child->operation == OP_OPEN || child->operation == OP_PROPSET)
+      if (op->baton)
         {
-          if (child->kind == svn_kind_dir)
-            SVN_ERR(editor->open_directory(path, operation->baton,
-                                           SVN_INVALID_REVNUM,
-                                           iterpool, &child->baton));
-          else
-            SVN_ERR(editor->open_file(path, operation->baton,
-                                      SVN_INVALID_REVNUM,
-                                      iterpool, &file_baton));
-        }
+          apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+          apr_hash_index_t *hi;
 
-      if (child->operation == OP_ADD)
-        {
-          if (child->kind == svn_kind_dir)
-            SVN_ERR(editor->add_directory(path, operation->baton,
-                                          child->copyfrom_url,
-                                          child->copyfrom_revision,
-                                          iterpool, &child->baton));
-          else
-            SVN_ERR(editor->add_file(path, operation->baton,
-                                     child->copyfrom_url,
-                                     child->copyfrom_revision, iterpool,
-                                     &file_baton));
-        }
+          /* Do any prop mods we may have. */
+          SVN_ERR(change_props(editor, op->baton, op, scratch_pool));
 
-      if (child->operation == OP_ADD_ABSENT)
-        {
-          if (child->kind == svn_kind_dir)
-            SVN_ERR(editor->absent_directory(path, operation->baton,
-                                             iterpool));
-          else
-            SVN_ERR(editor->absent_file(path, operation->baton, iterpool));
-        }
+          for (hi = apr_hash_first(scratch_pool, op->children);
+               hi; hi = apr_hash_next(hi))
+            {
+              struct operation *child = svn__apr_hash_index_val(hi);
 
-      if (child->src_file && file_baton)
-        {
-          /* We need to change textual contents. */
-          svn_txdelta_window_handler_t handler;
-          void *handler_baton;
-          svn_stream_t *contents;
-
-          SVN_ERR(editor->apply_textdelta(file_baton, NULL, iterpool,
-                                          &handler, &handler_baton));
-          SVN_ERR(svn_stream_open_readonly(&contents, child->src_file,
-                                           iterpool, iterpool));
-          SVN_ERR(svn_txdelta_send_stream(contents, handler, handler_baton,
-                                          NULL, iterpool));
-          SVN_ERR(svn_stream_close(contents));
+              svn_pool_clear(iterpool);
+              SVN_ERR(drive_tree(child, op, editor, make_abs_paths, iterpool));
+            }
+          svn_pool_destroy(iterpool);
+
+          /* We're done, close the directory. */
+          SVN_ERR(editor->close_directory(op->baton, scratch_pool));
         }
+    }
+  else
+    {
+      /* This currently treats anything that isn't a directory as a file.
+         I don't know that that's a valid assumption... */
+
+      void *file_baton = NULL;
+      
+      /* Open or create our baton. */
+      if (op->operation == OP_OPEN || op->operation == OP_PROPSET)
+        SVN_ERR(editor->open_file(path, parent_op->baton, op->base_revision,
+                                  scratch_pool, &file_baton));
+
+      else if (op->operation == OP_ADD || op->operation == OP_REPLACE)
+        SVN_ERR(editor->add_file(path, parent_op->baton, op->copyfrom_url,
+                                 op->copyfrom_revision, scratch_pool,
+                                 &file_baton));
+
+      else if (op->operation == OP_ADD_ABSENT)
+        SVN_ERR(editor->absent_file(path, parent_op->baton, scratch_pool));
 
-      /* Only worry about properties and closing the file baton if we've
-         previously opened it. */
       if (file_baton)
         {
-          if (child->kind == svn_kind_file)
-            SVN_ERR(change_props(editor, file_baton, child, iterpool));
-          SVN_ERR(editor->close_file(file_baton, NULL, iterpool));
-        }
+          /* Do we need to change text contents? */
+          if (op->src_file)
+            {
+              svn_txdelta_window_handler_t handler;
+              void *handler_baton;
+              svn_stream_t *contents;
+
+              SVN_ERR(editor->apply_textdelta(file_baton, NULL, scratch_pool,
+                                              &handler, &handler_baton));
+              SVN_ERR(svn_stream_open_readonly(&contents, op->src_file,
+                                               scratch_pool, scratch_pool));
+              SVN_ERR(svn_txdelta_send_stream(contents, handler, handler_baton,
+                                              NULL, scratch_pool));
+              SVN_ERR(svn_stream_close(contents));
+            }
 
-      /* We *always* open the child directory, so drive the child, change any
-         props, and then close the directory. */
-      if (child->kind == svn_kind_dir
-                   && (child->operation == OP_OPEN
-                    || child->operation == OP_PROPSET
-                    || child->operation == OP_ADD))
-        {
-          SVN_ERR(drive_tree(child, editor, make_abs_paths, iterpool));
-          SVN_ERR(editor->close_directory(child->baton, iterpool));
+          /* Do any prop mods we may have. */
+          SVN_ERR(change_props(editor, file_baton, op, scratch_pool));
+
+          /* Close the file. */
+          SVN_ERR(editor->close_file(file_baton, NULL, scratch_pool));
         }
+
     }
-  svn_pool_destroy(iterpool);
 
-  /* Finally, for this node, if it's a directory, change any props before
-     returning (our caller will close the directory. */
-  if (operation->kind == svn_kind_dir
-                   && (operation->operation == OP_OPEN
-                    || operation->operation == OP_PROPSET
-                    || operation->operation == OP_ADD))
+  return SVN_NO_ERROR;
+}
+
+/* This is a special case of drive_tree(), meant to handle the root, which
+   doesn't have a parent and should already be open. */
+static svn_error_t *
+drive_root(struct operation *root,
+           const svn_delta_editor_t *editor,
+           svn_boolean_t make_abs_paths,
+           apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  /* Early out: if we haven't opened the root yet (which would usually only
+     be the case in an abort), there isn't much we can do here. */
+  if (!root->baton)
+    return SVN_NO_ERROR;
+
+  /* Do any prop mods we may have. */
+  SVN_ERR(change_props(editor, root->baton, root, scratch_pool));
+
+  /* Now iterate over our children. */
+  for (hi = apr_hash_first(scratch_pool, root->children);
+       hi; hi = apr_hash_next(hi))
     {
-      SVN_ERR(change_props(editor, operation->baton, operation, scratch_pool));
+      struct operation *child = svn__apr_hash_index_val(hi);
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(drive_tree(child, root, editor, make_abs_paths, iterpool));
     }
+  
+  /* We need to close the root directory, but leave it to our caller to call
+     close_ or abort_edit(). */
+  SVN_ERR(editor->close_directory(root->baton, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -1428,10 +1543,9 @@ complete_cb(void *baton,
   SVN_ERR(ensure_root_opened(eb));
 
   /* Drive the tree we've created. */
-  err = drive_tree(&eb->root, eb->deditor, eb->make_abs_paths, scratch_pool);
+  err = drive_root(&eb->root, eb->deditor, *eb->make_abs_paths, scratch_pool);
   if (!err)
      {
-       err = eb->deditor->close_directory(eb->root.baton, scratch_pool);
        err = svn_error_compose_create(err, eb->deditor->close_edit(
                                                             eb->dedit_baton,
                                                             scratch_pool));
@@ -1456,7 +1570,7 @@ abort_cb(void *baton,
      point. */
 
   /* Drive the tree we've created. */
-  err = drive_tree(&eb->root, eb->deditor, eb->make_abs_paths, scratch_pool);
+  err = drive_root(&eb->root, eb->deditor, *eb->make_abs_paths, scratch_pool);
 
   err2 = eb->deditor->abort_edit(eb->dedit_baton, scratch_pool);
 
@@ -1472,7 +1586,33 @@ abort_cb(void *baton,
 }
 
 static svn_error_t *
+start_edit_func(void *baton,
+                svn_revnum_t base_revision)
+{
+  struct editor_baton *eb = baton;
+
+  eb->root.base_revision = base_revision;
+  SVN_ERR(ensure_root_opened(eb));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+target_revision_func(void *baton,
+                     svn_revnum_t target_revision,
+                     apr_pool_t *scratch_pool)
+{
+  struct editor_baton *eb = baton;
+
+  SVN_ERR(eb->deditor->set_target_revision(eb->dedit_baton, target_revision,
+                                           scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 editor_from_delta(svn_editor_t **editor_p,
+                  struct extra_baton **exb,
                   const svn_delta_editor_t *deditor,
                   void *dedit_baton,
                   svn_boolean_t *send_abs_paths,
@@ -1500,7 +1640,9 @@ editor_from_delta(svn_editor_t **editor_
       complete_cb,
       abort_cb
     };
-  struct editor_baton *eb = apr_palloc(result_pool, sizeof(*eb));
+  struct editor_baton *eb = apr_pcalloc(result_pool, sizeof(*eb));
+  struct extra_baton *extra_baton = apr_pcalloc(result_pool,
+                                                sizeof(*extra_baton));
 
   eb->deditor = deditor;
   eb->dedit_baton = dedit_baton;
@@ -1512,6 +1654,7 @@ editor_from_delta(svn_editor_t **editor_
   eb->fetch_props_func = fetch_props_func;
   eb->fetch_props_baton = fetch_props_baton;
 
+  eb->root.path = NULL;
   eb->root.children = apr_hash_make(result_pool);
   eb->root.kind = svn_kind_dir;
   eb->root.operation = OP_OPEN;
@@ -1528,6 +1671,12 @@ editor_from_delta(svn_editor_t **editor_
 
   *editor_p = editor;
 
+  extra_baton->start_edit = start_edit_func;
+  extra_baton->target_revision = target_revision_func;
+  extra_baton->baton = eb;
+
+  *exb = extra_baton;
+
   return SVN_NO_ERROR;
 }
 
@@ -1561,7 +1710,12 @@ svn_editor__insert_shims(const svn_delta
      wrap that again back into a svn_delta_editor_t.  This introduces
      a lot of overhead. */
   svn_editor_t *editor;
-  struct start_edit_baton *seb = apr_palloc(result_pool, sizeof(*seb));
+
+  /* The "extra baton" is a set of functions and a baton which allows the
+     shims to communicate additional events to each other.
+     editor_from_delta() returns a pointer to this baton, which
+     delta_from_editor() should then store. */
+  struct extra_baton *exb;
 
   /* The reason this is a pointer is that we don't know the appropriate
      value until we start receiving paths.  So process_actions() sets the
@@ -1569,25 +1723,20 @@ svn_editor__insert_shims(const svn_delta
   svn_boolean_t *found_abs_paths = apr_palloc(result_pool,
                                               sizeof(*found_abs_paths));
 
-  seb->deditor = deditor_in;
-  seb->dedit_baton = dedit_baton_in;
-
-  SVN_ERR(editor_from_delta(&editor, deditor_in, dedit_baton_in,
+  SVN_ERR(editor_from_delta(&editor, &exb, deditor_in, dedit_baton_in,
                             found_abs_paths, NULL, NULL,
                             shim_callbacks->fetch_kind_func,
-                            shim_callbacks->fetch_kind_baton,
+                            shim_callbacks->fetch_baton,
                             shim_callbacks->fetch_props_func,
-                            shim_callbacks->fetch_props_baton,
+                            shim_callbacks->fetch_baton,
                             result_pool, scratch_pool));
   SVN_ERR(delta_from_editor(deditor_out, dedit_baton_out, editor,
                             found_abs_paths,
                             shim_callbacks->fetch_props_func,
-                            shim_callbacks->fetch_props_baton,
+                            shim_callbacks->fetch_baton,
                             shim_callbacks->fetch_base_func,
-                            shim_callbacks->fetch_base_baton,
-                            start_edit_func, seb,
-                            target_revision_func, seb,
-                            result_pool));
+                            shim_callbacks->fetch_baton,
+                            exb, result_pool));
 
 #endif
   return SVN_NO_ERROR;

Modified: subversion/branches/showing-merge-info/subversion/libsvn_delta/delta.h
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_delta/delta.h?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_delta/delta.h (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_delta/delta.h Mon Jan 16 12:32:43 2012
@@ -68,6 +68,12 @@ void svn_txdelta__insert_op(svn_txdelta_
                             const char *new_data,
                             apr_pool_t *pool);
 
+/* Remove / truncate the last delta ops spanning the last MAX_LEN bytes
+   from the delta window being built via BUILD_BATON starting.  Return the
+   number of bytes that were actually removed. */
+apr_size_t
+svn_txdelta__remove_copy(svn_txdelta__ops_baton_t *build_baton,
+                         apr_size_t max_len);
 
 /* Allocate a delta window from POOL. */
 svn_txdelta_window_t *

Modified: subversion/branches/showing-merge-info/subversion/libsvn_delta/svndiff.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_delta/svndiff.c?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_delta/svndiff.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_delta/svndiff.c Mon Jan 16 12:32:43 2012
@@ -31,6 +31,8 @@
 #include "svn_private_config.h"
 #include <zlib.h>
 
+#include "private/svn_error_private.h"
+
 /* The zlib compressBound function was not exported until 1.2.0. */
 #if ZLIB_VERNUM >= 0x1200
 #define svnCompressBound(LEN) compressBound(LEN)
@@ -156,15 +158,18 @@ zlib_encode(const char *data,
     }
   else
     {
+      int zerr;
+
       svn_stringbuf_ensure(out, svnCompressBound(len) + intlen);
       endlen = out->blocksize;
 
-      if (compress2((unsigned char *)out->data + intlen, &endlen,
-                    (const unsigned char *)data, len,
-                    compression_level) != Z_OK)
-        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
-                                NULL,
-                                _("Compression of svndiff data failed"));
+      zerr = compress2((unsigned char *)out->data + intlen, &endlen,
+                       (const unsigned char *)data, len,
+                       compression_level);
+      if (zerr != Z_OK)
+        return svn_error_trace(svn_error__wrap_zlib(
+                                 zerr, "compress2",
+                                 _("Compression of svndiff data failed")));
 
       /* Compression didn't help :(, just append the original text */
       if (endlen >= len)
@@ -472,12 +477,14 @@ zlib_decode(const unsigned char *in, apr
   else
     {
       unsigned long zlen = len;
+      int zerr;
 
       svn_stringbuf_ensure(out, len);
-      if (uncompress((unsigned char *)out->data, &zlen, in, inLen) != Z_OK)
-        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
-                                NULL,
-                                _("Decompression of svndiff data failed"));
+      zerr = uncompress((unsigned char *)out->data, &zlen, in, inLen);
+      if (zerr != Z_OK)
+        return svn_error_trace(svn_error__wrap_zlib(
+                                 zerr, "uncompress",
+                                 _("Decompression of svndiff data failed")));
 
       /* Zlib should not produce something that has a different size than the
          original length we stored. */

Modified: subversion/branches/showing-merge-info/subversion/libsvn_delta/text_delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_delta/text_delta.c?rev=1231975&r1=1231974&r2=1231975&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_delta/text_delta.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_delta/text_delta.c Mon Jan 16 12:32:43 2012
@@ -276,6 +276,48 @@ svn_txdelta__insert_op(svn_txdelta__ops_
   ++build_baton->num_ops;
 }
 
+apr_size_t
+svn_txdelta__remove_copy(svn_txdelta__ops_baton_t *build_baton,
+                         apr_size_t max_len)
+{
+  svn_txdelta_op_t *op;
+  apr_size_t len = 0;
+
+  /* remove ops back to front */
+  while (build_baton->num_ops > 0)
+    {
+      op = &build_baton->ops[build_baton->num_ops-1];
+
+      /*  we can't modify svn_txdelta_target ops -> stop there */
+      if (op->action_code == svn_txdelta_target)
+        break;
+      
+      /*  handle the case that we cannot remove the op entirely */
+      if (op->length + len > max_len)
+        {
+          /* truncate only insertions. Copies don't benefit
+             from being truncated. */
+          if (op->action_code == svn_txdelta_new)
+            {
+               build_baton->new_data->len -= max_len - len;
+               op->length -= max_len - len;
+               len = max_len;
+            }
+          
+          break;
+        }
+        
+      /* drop the op entirely */
+      if (op->action_code == svn_txdelta_new)
+        build_baton->new_data->len -= op->length;
+      
+      len += op->length;
+      --build_baton->num_ops;
+    }
+    
+  return len;
+}
+
 
 
 /* Generic delta stream functions. */