You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/08/10 20:06:33 UTC

svn commit: r984153 [17/39] - in /subversion/branches/ignore-mergeinfo: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/hudson/ build/hudson/jobs/subversion-1.6.x-solaris/ build/hudson/jobs/subversion-1.6.x-ubuntu/ build/hu...

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/revert.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/revert.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/revert.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/revert.c Tue Aug 10 18:06:17 2010
@@ -41,7 +41,17 @@
 
 /*** Code. ***/
 
-/* Attempt to revert PATH.
+struct revert_with_write_lock_baton {
+  const char *local_abspath;
+  svn_depth_t depth;
+  svn_boolean_t use_commit_times;
+  const apr_array_header_t *changelists;
+  svn_client_ctx_t *ctx;
+};
+
+/* (Note: All arguments are in the baton above.)
+
+   Attempt to revert LOCAL_ABSPATH.
 
    If DEPTH is svn_depth_empty, revert just the properties on the
    directory; else if svn_depth_files, revert the properties and any
@@ -56,34 +66,23 @@
    CHANGELISTS is empty (or altogether NULL), no changelist filtering occurs.
 
    Consult CTX to determine whether or not to revert timestamp to the
-   time of last commit ('use-commit-times = yes').  Use POOL for
-   temporary allocation.
+   time of last commit ('use-commit-times = yes').
 
    If PATH is unversioned, return SVN_ERR_UNVERSIONED_RESOURCE. */
 static svn_error_t *
-revert(const char *path,
-       svn_depth_t depth,
-       svn_boolean_t use_commit_times,
-       const apr_array_header_t *changelists,
-       svn_client_ctx_t *ctx,
-       apr_pool_t *pool)
+revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {
-  const char *local_abspath, *anchor_abspath;
+  struct revert_with_write_lock_baton *b = baton;
   svn_error_t *err;
 
-  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
-
-  SVN_ERR(svn_wc__acquire_write_lock(&anchor_abspath, ctx->wc_ctx,
-                                     local_abspath, pool, pool));
-
-  err = svn_wc_revert4(ctx->wc_ctx,
-                       local_abspath,
-                       depth,
-                       use_commit_times,
-                       changelists,
-                       ctx->cancel_func, ctx->cancel_baton,
-                       ctx->notify_func2, ctx->notify_baton2,
-                       pool);
+  err = svn_wc_revert4(b->ctx->wc_ctx,
+                       b->local_abspath,
+                       b->depth,
+                       b->use_commit_times,
+                       b->changelists,
+                       b->ctx->cancel_func, b->ctx->cancel_baton,
+                       b->ctx->notify_func2, b->ctx->notify_baton2,
+                       scratch_pool);
 
   if (err)
     {
@@ -92,19 +91,19 @@ revert(const char *path,
       if (err->apr_err == SVN_ERR_ENTRY_NOT_FOUND
           || err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE)
         {
-          if (ctx->notify_func2)
-            (*ctx->notify_func2)
-              (ctx->notify_baton2,
-               svn_wc_create_notify(path, svn_wc_notify_skip, pool),
-               pool);
+          if (b->ctx->notify_func2)
+            (*b->ctx->notify_func2)
+              (b->ctx->notify_baton2,
+               svn_wc_create_notify(b->local_abspath, svn_wc_notify_skip,
+                                    scratch_pool),
+               scratch_pool);
           svn_error_clear(err);
         }
       else
         return svn_error_return(err);
     }
 
-  return svn_error_return(
-    svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool));
+  return SVN_NO_ERROR;
 }
 
 
@@ -120,6 +119,7 @@ svn_client_revert2(const apr_array_heade
   int i;
   svn_config_t *cfg;
   svn_boolean_t use_commit_times;
+  struct revert_with_write_lock_baton baton;
 
   cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
                                    APR_HASH_KEY_STRING) : NULL;
@@ -134,6 +134,7 @@ svn_client_revert2(const apr_array_heade
   for (i = 0; i < paths->nelts; i++)
     {
       const char *path = APR_ARRAY_IDX(paths, i, const char *);
+      const char *local_abspath;
 
       svn_pool_clear(subpool);
 
@@ -142,7 +143,15 @@ svn_client_revert2(const apr_array_heade
           && ((err = ctx->cancel_func(ctx->cancel_baton))))
         goto errorful;
 
-      err = revert(path, depth, use_commit_times, changelists, ctx, subpool);
+      SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
+
+      baton.local_abspath = local_abspath;
+      baton.depth = depth;
+      baton.use_commit_times = use_commit_times;
+      baton.changelists = changelists;
+      baton.ctx = ctx;
+      err = svn_wc__call_with_write_lock(revert, &baton, ctx->wc_ctx,
+                                         local_abspath, pool, pool);
       if (err)
         goto errorful;
     }

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/switch.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/switch.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/switch.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/switch.c Tue Aug 10 18:06:17 2010
@@ -55,21 +55,22 @@
 */
 
 
-svn_error_t *
-svn_client__switch_internal(svn_revnum_t *result_rev,
-                            const char *path,
-                            const char *switch_url,
-                            const svn_opt_revision_t *peg_revision,
-                            const svn_opt_revision_t *revision,
-                            svn_wc_adm_access_t *adm_access,
-                            svn_depth_t depth,
-                            svn_boolean_t depth_is_sticky,
-                            svn_boolean_t *timestamp_sleep,
-                            svn_boolean_t ignore_externals,
-                            svn_boolean_t allow_unver_obstructions,
-                            svn_boolean_t innerswitch,
-                            svn_client_ctx_t *ctx,
-                            apr_pool_t *pool)
+static svn_error_t *
+switch_internal(svn_revnum_t *result_rev,
+                const char *path,
+                const char *local_abspath,
+                const char *anchor_abspath,
+                const char *switch_url,
+                const svn_opt_revision_t *peg_revision,
+                const svn_opt_revision_t *revision,
+                svn_depth_t depth,
+                svn_boolean_t depth_is_sticky,
+                svn_boolean_t *timestamp_sleep,
+                svn_boolean_t ignore_externals,
+                svn_boolean_t allow_unver_obstructions,
+                svn_boolean_t innerswitch,
+                svn_client_ctx_t *ctx,
+                apr_pool_t *pool)
 {
   const svn_ra_reporter3_t *reporter;
   void *report_baton;
@@ -77,8 +78,6 @@ svn_client__switch_internal(svn_revnum_t
   svn_ra_session_t *ra_session;
   svn_revnum_t revnum;
   svn_error_t *err = SVN_NO_ERROR;
-  svn_wc_adm_access_t *dir_access;
-  const svn_boolean_t close_adm_access = ! adm_access;
   const char *diff3_cmd;
   svn_boolean_t use_commit_times;
   svn_boolean_t sleep_here = FALSE;
@@ -86,18 +85,14 @@ svn_client__switch_internal(svn_revnum_t
   const svn_delta_editor_t *switch_editor;
   void *switch_edit_baton;
   const char *preserved_exts_str;
-  const char *anchor_abspath;
   apr_array_header_t *preserved_exts;
   svn_boolean_t server_supports_depth;
-  const char *local_abspath;
   svn_client__external_func_baton_t efb;
   svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config,
                                                  SVN_CONFIG_CATEGORY_CONFIG,
                                                  APR_HASH_KEY_STRING)
                                   : NULL;
 
-  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
-
   /* An unknown depth can't be sticky. */
   if (depth == svn_depth_unknown)
     depth_is_sticky = FALSE;
@@ -125,49 +120,16 @@ svn_client__switch_internal(svn_revnum_t
     : NULL;
 
   /* Sanity check.  Without these, the switch is meaningless. */
-  SVN_ERR_ASSERT(path);
   SVN_ERR_ASSERT(switch_url && (switch_url[0] != '\0'));
 
-  /* ### Need to lock the whole target tree to invalidate wcprops. Does
-     non-recursive switch really need to invalidate the whole tree? */
-  if (innerswitch)
-    {
-      SVN_ERR(svn_wc__adm_open_in_context(&adm_access, ctx->wc_ctx,
-                                          path, TRUE, -1, ctx->cancel_func,
-                                          ctx->cancel_baton, pool));
-      dir_access = adm_access;
-      target = "";
-      anchor = svn_wc_adm_access_path(adm_access);
-    }
-  else if (adm_access)
-    {
-      svn_wc_adm_access_t *a = adm_access;
-      const char *dir_access_path;
-
-      /* This is a little hacky, but open two new read-only access
-         baton's to get the anchor and target access batons that would
-         be used if a locked access baton was not available. */
-      SVN_ERR(svn_wc_adm_open_anchor(&adm_access, &dir_access, &target, path,
-                                     FALSE, -1, ctx->cancel_func,
-                                     ctx->cancel_baton, pool));
-      anchor = svn_wc_adm_access_path(adm_access);
-      dir_access_path = svn_wc_adm_access_path(dir_access);
-      SVN_ERR(svn_wc_adm_close2(adm_access, pool));
-
-      SVN_ERR(svn_wc_adm_retrieve(&adm_access, a, anchor, pool));
-      SVN_ERR(svn_wc_adm_retrieve(&dir_access, a, dir_access_path, pool));
-    }
+  if (strcmp(local_abspath, anchor_abspath))
+    svn_dirent_split(path, &anchor, &target, pool);
   else
     {
-      SVN_ERR(svn_wc__adm_open_anchor_in_context(&adm_access, &dir_access,
-                                                 &target, ctx->wc_ctx, path,
-                                                 TRUE, -1, ctx->cancel_func,
-                                                 ctx->cancel_baton, pool));
-
-      anchor = svn_wc_adm_access_path(adm_access);
+      target = "";
+      anchor = path;
     }
 
-  SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, pool));
   SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, anchor_abspath, pool, pool));
   if (! url)
     return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
@@ -188,10 +150,6 @@ svn_client__switch_internal(svn_revnum_t
                                  pool));
 
           /* Target excluded, we are done now */
-
-          if (close_adm_access)
-            SVN_ERR(svn_wc_adm_close2(adm_access, pool));
-
           return SVN_NO_ERROR;
         }
 
@@ -256,13 +214,15 @@ svn_client__switch_internal(svn_revnum_t
      PATH.  When we call reporter->finish_report, the update_editor
      will be driven by svn_repos_dir_delta2.
 
-     We pass NULL for traversal_info because this is a switch, not an
-     update, and therefore we don't want to handle any externals
-     except the ones directly affected by the switch. */
+     We pass in an external_func for recording all externals. It
+     shouldn't be needed for a switch if it wasn't for the relative
+     externals of type '../path'. All of those must be resolved to
+     the new location.  */
   err = svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter,
                                 report_baton, TRUE, depth, (! depth_is_sticky),
                                 (! server_supports_depth),
-                                use_commit_times, NULL, NULL,
+                                use_commit_times,
+                                svn_client__external_info_gatherer, &efb,
                                 ctx->notify_func2, ctx->notify_baton2, pool);
 
   if (err)
@@ -278,7 +238,7 @@ svn_client__switch_internal(svn_revnum_t
      handling external items (and any errors therefrom) doesn't delay
      the primary operation. */
   if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
-    err = svn_client__handle_externals(adm_access, efb.externals_old,
+    err = svn_client__handle_externals(efb.externals_old,
                                        efb.externals_new, efb.ambient_depths,
                                        switch_url, path, source_root, depth,
                                        use_sleep, ctx, pool);
@@ -292,9 +252,6 @@ svn_client__switch_internal(svn_revnum_t
   if (err)
     return svn_error_return(err);
 
-  if (close_adm_access)
-    SVN_ERR(svn_wc_adm_close2(adm_access, pool));
-
   /* Let everyone know we're finished here. */
   if (ctx->notify_func2)
     {
@@ -316,6 +273,57 @@ svn_client__switch_internal(svn_revnum_t
 }
 
 svn_error_t *
+svn_client__switch_internal(svn_revnum_t *result_rev,
+                            const char *path,
+                            const char *switch_url,
+                            const svn_opt_revision_t *peg_revision,
+                            const svn_opt_revision_t *revision,
+                            svn_depth_t depth,
+                            svn_boolean_t depth_is_sticky,
+                            svn_boolean_t *timestamp_sleep,
+                            svn_boolean_t ignore_externals,
+                            svn_boolean_t allow_unver_obstructions,
+                            svn_boolean_t innerswitch,
+                            svn_client_ctx_t *ctx,
+                            apr_pool_t *pool)
+{
+  const char *local_abspath, *anchor_abspath;
+  svn_boolean_t acquired_lock;
+  svn_error_t *err, *err1, *err2;
+
+  SVN_ERR_ASSERT(path);
+
+  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
+
+  /* Rely on svn_wc__acquire_write_lock setting ANCHOR_ABSPATH even
+     when it returns SVN_ERR_WC_LOCKED */
+  err = svn_wc__acquire_write_lock(&anchor_abspath, ctx->wc_ctx,
+                                   local_abspath, pool, pool);
+  if (err && err->apr_err != SVN_ERR_WC_LOCKED)
+    return svn_error_return(err);
+  else if (err)
+    {
+      svn_error_clear(err);
+      acquired_lock = FALSE;
+    }
+  else
+    acquired_lock = TRUE;
+
+  err1 = switch_internal(result_rev, path, local_abspath, anchor_abspath,
+                         switch_url, peg_revision, revision,
+                         depth, depth_is_sticky,
+                         timestamp_sleep, ignore_externals,
+                         allow_unver_obstructions, innerswitch, ctx, pool);
+
+  if (acquired_lock)
+    err2 = svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool);
+  else
+    err2 = SVN_NO_ERROR;
+
+  return svn_error_compose_create(err1, err2);
+}
+
+svn_error_t *
 svn_client_switch2(svn_revnum_t *result_rev,
                    const char *path,
                    const char *switch_url,
@@ -329,7 +337,7 @@ svn_client_switch2(svn_revnum_t *result_
                    apr_pool_t *pool)
 {
   return svn_client__switch_internal(result_rev, path, switch_url,
-                                     peg_revision, revision, NULL, depth,
+                                     peg_revision, revision, depth,
                                      depth_is_sticky, NULL, ignore_externals,
                                      allow_unver_obstructions, FALSE, ctx,
                                      pool);

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/update.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/update.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/update.c Tue Aug 10 18:06:17 2010
@@ -79,19 +79,21 @@ file_fetcher(void *baton,
 }
 
 
-svn_error_t *
-svn_client__update_internal(svn_revnum_t *result_rev,
-                            const char *path,
-                            const svn_opt_revision_t *revision,
-                            svn_depth_t depth,
-                            svn_boolean_t depth_is_sticky,
-                            svn_boolean_t ignore_externals,
-                            svn_boolean_t allow_unver_obstructions,
-                            svn_boolean_t *timestamp_sleep,
-                            svn_boolean_t send_copyfrom_args,
-                            svn_boolean_t innerupdate,
-                            svn_client_ctx_t *ctx,
-                            apr_pool_t *pool)
+static svn_error_t *
+update_internal(svn_revnum_t *result_rev,
+                const char *path,
+                const char *local_abspath,
+                const char *anchor_abspath,
+                const svn_opt_revision_t *revision,
+                svn_depth_t depth,
+                svn_boolean_t depth_is_sticky,
+                svn_boolean_t ignore_externals,
+                svn_boolean_t allow_unver_obstructions,
+                svn_boolean_t *timestamp_sleep,
+                svn_boolean_t send_copyfrom_args,
+                svn_boolean_t innerupdate,
+                svn_client_ctx_t *ctx,
+                apr_pool_t *pool)
 {
   const svn_delta_editor_t *update_editor;
   void *update_edit_baton;
@@ -102,19 +104,15 @@ svn_client__update_internal(svn_revnum_t
   const char *repos_root;
   svn_error_t *err;
   svn_revnum_t revnum;
-  svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info(pool);
-  svn_wc_adm_access_t *adm_access;
   svn_boolean_t use_commit_times;
   svn_boolean_t sleep_here = FALSE;
   svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here;
   const char *diff3_cmd;
   svn_ra_session_t *ra_session;
-  svn_wc_adm_access_t *dir_access;
   const char *preserved_exts_str;
   apr_array_header_t *preserved_exts;
   struct ff_baton *ffb;
-  const char *local_abspath;
-  const char *anchor_abspath;
+  svn_client__external_func_baton_t efb;
   svn_boolean_t server_supports_depth;
   svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config,
                                                  SVN_CONFIG_CATEGORY_CONFIG,
@@ -124,44 +122,18 @@ svn_client__update_internal(svn_revnum_t
   if (depth == svn_depth_unknown)
     depth_is_sticky = FALSE;
 
-  /* Sanity check.  Without this, the update is meaningless. */
-  SVN_ERR_ASSERT(path);
-
-  if (svn_path_is_url(path))
-    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
-                             _("Path '%s' is not a directory"),
-                             path);
-
-  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
-
-  if (!innerupdate)
+  if (strcmp(local_abspath, anchor_abspath))
     {
-      /* Use PATH to get the update's anchor and targets and get a write lock.
-       */
-      SVN_ERR(svn_wc__adm_open_anchor_in_context(&adm_access, &dir_access,
-                                                 &target, ctx->wc_ctx, path,
-                                                 TRUE,
-                                                 -1, /* recursive lock */
-                                                 ctx->cancel_func,
-                                                 ctx->cancel_baton, pool));
+      target = svn_dirent_basename(local_abspath, pool);
+      anchor = svn_dirent_basename(path, pool);
     }
   else
     {
-      /* Assume the exact root is specified (required for externals to work,
-         as these would otherwise try to open the parent working copy again) */
-      SVN_ERR(svn_wc__adm_open_in_context(&adm_access, ctx->wc_ctx, path, TRUE,
-                                          -1, /* recursive lock */
-                                          ctx->cancel_func, ctx->cancel_baton,
-                                          pool));
-      dir_access = adm_access;
       target = "";
+      anchor = path;
     }
 
-  anchor = svn_wc_adm_access_path(adm_access);
-  SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, pool));
-
   /* Get full URL from the ANCHOR. */
-
   SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath,
                                pool, pool));
   if (! anchor_url)
@@ -183,7 +155,8 @@ svn_client__update_internal(svn_revnum_t
                                  pool));
 
           /* Target excluded, we are done now */
-          SVN_ERR(svn_wc_adm_close2(adm_access, pool));
+          SVN_ERR(svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath,
+                                             pool));
 
           return SVN_NO_ERROR;
         }
@@ -239,19 +212,25 @@ svn_client__update_internal(svn_revnum_t
   ffb->repos_root = repos_root;
   ffb->pool = pool;
 
+  /* Build a baton for the externals-info-gatherer callback. */
+  efb.externals_new = apr_hash_make(pool);
+  efb.externals_old = apr_hash_make(pool);
+  efb.ambient_depths = apr_hash_make(pool);
+  efb.result_pool = pool;
+
   /* Fetch the update editor.  If REVISION is invalid, that's okay;
      the RA driver will call editor->set_target_revision later on. */
-  SVN_ERR(svn_wc_get_update_editor3(&revnum, adm_access, target,
-                                    use_commit_times, depth, depth_is_sticky,
-                                    allow_unver_obstructions,
-                                    ctx->notify_func2, ctx->notify_baton2,
-                                    ctx->cancel_func, ctx->cancel_baton,
-                                    ctx->conflict_func, ctx->conflict_baton,
-                                    file_fetcher, ffb,
+  SVN_ERR(svn_wc_get_update_editor4(&update_editor, &update_edit_baton,
+                                    &revnum, ctx->wc_ctx, anchor_abspath,
+                                    target, use_commit_times, depth,
+                                    depth_is_sticky, allow_unver_obstructions,
                                     diff3_cmd, preserved_exts,
-                                    &update_editor, &update_edit_baton,
-                                    traversal_info,
-                                    pool));
+                                    file_fetcher, ffb,
+                                    ctx->conflict_func, ctx->conflict_baton,
+                                    svn_client__external_info_gatherer, &efb,
+                                    ctx->cancel_func, ctx->cancel_baton,
+                                    ctx->notify_func2, ctx->notify_baton2,
+                                    pool, pool));
 
   /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an
      invalid revnum, that means RA will use the latest revision.  */
@@ -269,12 +248,13 @@ svn_client__update_internal(svn_revnum_t
   /* Drive the reporter structure, describing the revisions within
      PATH.  When we call reporter->finish_report, the
      update_editor will be driven by svn_repos_dir_delta2. */
-  err = svn_wc_crawl_revisions4(path, dir_access, reporter, report_baton,
-                                TRUE, depth, (! depth_is_sticky),
+  err = svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter,
+                                report_baton, TRUE, depth, (! depth_is_sticky),
                                 (! server_supports_depth),
                                 use_commit_times,
-                                ctx->notify_func2, ctx->notify_baton2,
-                                traversal_info, pool);
+                                svn_client__external_info_gatherer,
+                                &efb, ctx->notify_func2, ctx->notify_baton2,
+                                pool);
 
   if (err)
     {
@@ -290,21 +270,18 @@ svn_client__update_internal(svn_revnum_t
      the primary operation.  */
   if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
     {
-      apr_hash_t *externals_old, *externals_new, *ambient_depths;
-
-      svn_wc_edited_externals(&externals_old, &externals_new, traversal_info);
-      svn_wc_traversed_depths(&ambient_depths, traversal_info);
-
-      SVN_ERR(svn_client__handle_externals(adm_access, externals_old,
-                                           externals_new, ambient_depths,
-                                           anchor_url, anchor, repos_root,
+      SVN_ERR(svn_client__handle_externals(efb.externals_old,
+                                           efb.externals_new,
+                                           efb.ambient_depths,
+                                           anchor_url, anchor,
+                                           repos_root,
                                            depth, use_sleep, ctx, pool));
     }
 
   if (sleep_here)
     svn_io_sleep_for_timestamps(path, pool);
 
-  SVN_ERR(svn_wc_adm_close2(adm_access, pool));
+  SVN_ERR(svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool));
 
   /* Let everyone know we're finished here. */
   if (ctx->notify_func2)
@@ -327,6 +304,56 @@ svn_client__update_internal(svn_revnum_t
 }
 
 svn_error_t *
+svn_client__update_internal(svn_revnum_t *result_rev,
+                            const char *path,
+                            const svn_opt_revision_t *revision,
+                            svn_depth_t depth,
+                            svn_boolean_t depth_is_sticky,
+                            svn_boolean_t ignore_externals,
+                            svn_boolean_t allow_unver_obstructions,
+                            svn_boolean_t *timestamp_sleep,
+                            svn_boolean_t send_copyfrom_args,
+                            svn_boolean_t innerupdate,
+                            svn_client_ctx_t *ctx,
+                            apr_pool_t *pool)
+{
+  const char *local_abspath, *anchor_abspath;
+  svn_error_t *err1, *err2;
+
+  SVN_ERR_ASSERT(path);
+
+  if (svn_path_is_url(path))
+    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+                             _("Path '%s' is not a directory"),
+                             path);
+
+  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
+
+  if (!innerupdate)
+    {
+      SVN_ERR(svn_wc__acquire_write_lock(&anchor_abspath, ctx->wc_ctx,
+                                         local_abspath, pool, pool));
+    }
+  else
+    {
+      SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx,
+                                         local_abspath, pool, pool));
+      anchor_abspath = local_abspath;
+    }
+
+  err1 = update_internal(result_rev, path, local_abspath, anchor_abspath,
+                         revision, depth, depth_is_sticky,
+                         ignore_externals, allow_unver_obstructions,
+                         timestamp_sleep, send_copyfrom_args,
+                         innerupdate, ctx, pool);
+
+  err2 = svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool);
+
+  return svn_error_compose_create(err1, err2);
+}
+
+
+svn_error_t *
 svn_client_update3(apr_array_header_t **result_revs,
                    const apr_array_header_t *paths,
                    const svn_opt_revision_t *revision,
@@ -366,7 +393,7 @@ svn_client_update3(apr_array_header_t **
         }
       else if (err)
         {
-          /* SVN_ERR_WC_NOT_DIRECTORY: it's not versioned */
+          /* SVN_ERR_WC_NOT_WORKING_COPY: it's not versioned */
           svn_error_clear(err);
           err = SVN_NO_ERROR;
           result_rev = SVN_INVALID_REVNUM;

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/util.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/util.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/util.c Tue Aug 10 18:06:17 2010
@@ -50,9 +50,9 @@ string_hash_dup(apr_hash_t *hash, apr_po
 
   for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
     {
-      const char *key = apr_pstrdup(pool, svn_apr_hash_index_key(hi));
-      apr_ssize_t klen = svn_apr_hash_index_klen(hi);
-      svn_string_t *val = svn_string_dup(svn_apr_hash_index_val(hi), pool);
+      const char *key = apr_pstrdup(pool, svn__apr_hash_index_key(hi));
+      apr_ssize_t klen = svn__apr_hash_index_klen(hi);
+      svn_string_t *val = svn_string_dup(svn__apr_hash_index_val(hi), pool);
 
       apr_hash_set(new_hash, key, klen, val);
     }

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/compat.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/compat.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/compat.c Tue Aug 10 18:06:17 2010
@@ -33,7 +33,7 @@ struct file_rev_handler_wrapper_baton {
   svn_file_rev_handler_old_t handler;
 };
 
-/* This implements svn_repos_file_rev_handler2_t. */
+/* This implements svn_file_rev_handler_t. */
 static svn_error_t *
 file_rev_handler_wrapper(void *baton,
                          const char *path,

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/compose_delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/compose_delta.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/compose_delta.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/compose_delta.c Tue Aug 10 18:06:17 2010
@@ -160,36 +160,49 @@ create_offset_index(const svn_txdelta_wi
 }
 
 /* Find the index of the delta op thet defines that data at OFFSET in
-   NDX. */
+   NDX. HINT is an arbitrary positin within NDX and doesn't even need 
+   to be valid. To effectively speed up the search, use the last result
+   as hint because most lookups come as a sequence of decreasing values
+   for OFFSET and they concentrate on the lower end of the array. */
 
 static int
-search_offset_index(const offset_index_t *ndx, apr_size_t offset)
+search_offset_index(const offset_index_t *ndx, apr_size_t offset, int hint)
 {
   int lo, hi, op;
 
   assert(offset < ndx->offs[ndx->length]);
 
-  for (lo = 0, hi = ndx->length, op = (lo + hi)/2;
-       lo < hi;
-       op = (lo + hi)/2)
-    {
-      const apr_size_t this_offset = ndx->offs[op];
-      const apr_size_t next_offset = ndx->offs[op + 1];
-      if (offset < this_offset)
-        hi = op;
-      else if (offset > next_offset)
-        lo = op;
+  lo = 0;
+  hi = ndx->length;
+
+  /* If we got a valid hint, use it to reduce the range to cover.
+     Note that this will only be useful if either the hint is a
+     hit (i.e. equals the desired result) or narrows the range
+     length by a factor larger than 2. */
+
+  if (hint < hi)
+    {
+      if (offset < ndx->offs[hint])
+        hi = hint;
+      else if (offset < ndx->offs[hint+1])
+        return hint;
       else
-        {
-          /* this_offset <= offset <= next_offset */
-          if (offset == next_offset)
-            ++op;
-          break;
-        }
+        lo = hint+1;
     }
 
-  assert(ndx->offs[op] <= offset && offset < ndx->offs[op + 1]);
-  return op;
+  /* ordinary binary search */
+
+  for (op = (lo + hi)/2; lo != hi; op = (lo + hi)/2)
+    {
+      if (offset < ndx->offs[op])
+        hi = op;
+      else 
+        lo = ++op;
+    }
+
+  --lo;
+  assert(ndx->offs[lo] <= offset && offset < ndx->offs[lo + 1]);
+  return lo;
 }
 
 
@@ -614,28 +627,33 @@ build_range_list(apr_size_t offset, apr_
 
 /* Copy the instructions from WINDOW that define the range [OFFSET,
    LIMIT) in WINDOW's target stream to TARGET_OFFSET in the window
-   represented by BUILD_BATON. Use NDX to find the instructions in
-   WINDOW. Allocate space in BUILD_BATON from POOL. */
+   represented by BUILD_BATON. HINT is a position in the instructions
+   array that helps finding the position for OFFSET. A safe default 
+   is 0. Use NDX to find the instructions in WINDOW. Allocate space 
+   in BUILD_BATON from POOL. */
 
 static void
-copy_source_ops(apr_size_t offset, apr_size_t limit,
+copy_source_ops(apr_size_t offset, apr_size_t limit,  
                 apr_size_t target_offset,
+                int hint,
                 svn_txdelta__ops_baton_t *build_baton,
                 const svn_txdelta_window_t *window,
                 const offset_index_t *ndx,
                 apr_pool_t *pool)
 {
-  const int first_op = search_offset_index(ndx, offset);
-  const int last_op = search_offset_index(ndx, limit - 1);
-  int op_ndx;
-
-  for (op_ndx = first_op; op_ndx <= last_op; ++op_ndx)
+  int op_ndx = search_offset_index(ndx, offset, hint);
+  for (;; ++op_ndx)
     {
       const svn_txdelta_op_t *const op = &window->ops[op_ndx];
       const apr_size_t *const off = &ndx->offs[op_ndx];
+      apr_size_t fix_offset;
+      apr_size_t fix_limit;
+
+      if (off[0] >= limit)
+          break;
 
-      const apr_size_t fix_offset = (offset > off[0] ? offset - off[0] : 0);
-      const apr_size_t fix_limit = (off[1] > limit ? off[1] - limit : 0);
+      fix_offset = (offset > off[0] ? offset - off[0] : 0);
+      fix_limit = (off[1] > limit ? off[1] - limit : 0);
 
       /* It would be extremely weird if the fixed-up op had zero length. */
       assert(fix_offset + fix_limit < op->length);
@@ -667,6 +685,7 @@ copy_source_ops(apr_size_t offset, apr_s
               copy_source_ops(op->offset + fix_offset,
                               op->offset + op->length - fix_limit,
                               target_offset,
+                              op_ndx,
                               build_baton, window, ndx, pool);
             }
           else
@@ -692,6 +711,7 @@ copy_source_ops(apr_size_t offset, apr_s
                   copy_source_ops(op->offset + ptn_overlap,
                                   op->offset + ptn_overlap + length,
                                   tgt_off,
+                                  op_ndx,
                                   build_baton, window, ndx, pool);
                   fix_off += length;
                   tgt_off += length;
@@ -707,6 +727,7 @@ copy_source_ops(apr_size_t offset, apr_s
                   copy_source_ops(op->offset,
                                   op->offset + length,
                                   tgt_off,
+                                  op_ndx,
                                   build_baton, window, ndx, pool);
                   fix_off += length;
                   tgt_off += length;
@@ -788,7 +809,7 @@ svn_txdelta_compose_windows(const svn_tx
                                        range->limit - range->offset,
                                        NULL, pool);
               else
-                copy_source_ops(range->offset, range->limit, tgt_off,
+                copy_source_ops(range->offset, range->limit, tgt_off, 0,
                                 &build_baton, window_A, offset_index,
                                 pool);
 

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/path_driver.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/path_driver.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/path_driver.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_delta/path_driver.c Tue Aug 10 18:06:17 2010
@@ -133,7 +133,7 @@ svn_error_t *
 svn_delta_path_driver(const svn_delta_editor_t *editor,
                       void *edit_baton,
                       svn_revnum_t revision,
-                      apr_array_header_t *paths,
+                      const apr_array_header_t *paths,
                       svn_delta_path_driver_cb_func_t callback_func,
                       void *callback_baton,
                       apr_pool_t *pool)

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/diff.h
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/diff.h?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/diff.h (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/diff.h Tue Aug 10 18:06:17 2010
@@ -91,7 +91,7 @@ typedef enum svn_diff__normalize_state_t
 svn_diff__lcs_t *
 svn_diff__lcs(svn_diff__position_t *position_list1, /* pointer to tail (ring) */
               svn_diff__position_t *position_list2, /* pointer to tail (ring) */
-	      apr_pool_t *pool);
+              apr_pool_t *pool);
 
 
 /*

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/diff_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/diff_file.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/diff_file.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/diff_file.c Tue Aug 10 18:06:17 2010
@@ -302,10 +302,11 @@ datasource_get_next_token(apr_uint32_t *
           had_cr = (*eol == '\r');
           eol++;
           /* If we have the whole eol sequence in the chunk... */
-          if (!had_cr || eol != endp)
+          if (!(had_cr && eol == endp))
             {
+              /* Also skip past the '\n' in an '\r\n' sequence. */
               if (had_cr && *eol == '\n')
-                ++eol;
+                eol++;
               break;
             }
         }

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/lcs.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/lcs.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/lcs.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/lcs.c Tue Aug 10 18:06:17 2010
@@ -30,7 +30,7 @@
 
 
 /*
- * Calculate the Longest Common Subsequence between two datasources.
+ * Calculate the Longest Common Subsequence (LCS) between two datasources.
  * This function is what makes the diff code tick.
  *
  * The LCS algorithm implemented here is described by Sun Wu,

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/parse-diff.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_diff/parse-diff.c Tue Aug 10 18:06:17 2010
@@ -31,9 +31,7 @@
 #include "svn_pools.h"
 #include "svn_utf.h"
 #include "svn_dirent_uri.h"
-
-#include "private/svn_diff_private.h"
-
+#include "svn_diff.h"
 
 /* Helper macro for readability */
 #define starts_with(str, start)  \
@@ -47,7 +45,7 @@ parse_offset(svn_linenum_t *offset, cons
 {
   apr_int64_t parsed_offset;
 
-  errno = 0; /* clear errno for safety */
+  errno = 0; /* apr_atoi64() in APR-0.9 does not always set errno */
   parsed_offset = apr_atoi64(number);
   if (errno == ERANGE || parsed_offset < 0)
     return FALSE;
@@ -103,9 +101,11 @@ parse_range(svn_linenum_t *start, svn_li
 
 /* Try to parse a hunk header in string HEADER, putting parsed information
  * into HUNK. Return TRUE if the header parsed correctly.
+ * If REVERSE is TRUE, invert the hunk header while parsing it.
  * Do all allocations in POOL. */
 static svn_boolean_t
-parse_hunk_header(const char *header, svn_hunk_t *hunk, apr_pool_t *pool)
+parse_hunk_header(const char *header, svn_hunk_t *hunk,
+                  svn_boolean_t reverse, apr_pool_t *pool)
 {
   static const char * const atat = "@@";
   const char *p;
@@ -132,8 +132,16 @@ parse_hunk_header(const char *header, sv
     return FALSE;
 
   /* Try to parse the first range. */
-  if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
-    return FALSE;
+  if (reverse)
+    {
+      if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
+        return FALSE;
+    }
+  else
+    {
+      if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
+        return FALSE;
+    }
 
   /* Clear the stringbuf so we can reuse it for the second range. */
   svn_stringbuf_setempty(range);
@@ -161,8 +169,16 @@ parse_hunk_header(const char *header, sv
    * but we ignore that. */
 
   /* Try to parse the second range. */
-  if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
-    return FALSE;
+  if (reverse)
+    {
+      if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
+        return FALSE;
+    }
+  else
+    {
+      if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
+        return FALSE;
+    }
 
   /* Hunk header is good. */
   return TRUE;
@@ -204,19 +220,60 @@ remove_leading_char_transformer(svn_stri
   return SVN_NO_ERROR;
 }
 
+/** line-transformer callback to reverse a diff text. */
+static svn_error_t *
+reverse_diff_transformer(svn_stringbuf_t **buf,
+                         const char *line,
+                         void *baton,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
+{
+  svn_hunk_t hunk;
+
+  /* ### Pass the already parsed hunk via the baton?
+   * ### Maybe we should really make svn_stream_readline() a proper stream
+   * ### method and override it instead of adding special callbacks? */
+  if (parse_hunk_header(line, &hunk, FALSE, scratch_pool))
+    {
+      *buf = svn_stringbuf_createf(result_pool,
+                                   "@@ -%lu,%lu +%lu,%lu @@",
+                                   hunk.modified_start,
+                                   hunk.modified_length,
+                                   hunk.original_start,
+                                   hunk.original_length);
+    }
+  else if (line[0] == '+')
+    {
+      *buf = svn_stringbuf_create(line, result_pool);
+      (*buf)->data[0] = '-';
+    }
+  else if (line[0] == '-')
+    {
+      *buf = svn_stringbuf_create(line, result_pool);
+      (*buf)->data[0] = '+';
+    }
+  else
+    *buf = svn_stringbuf_create(line, result_pool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Return the next *HUNK from a PATCH, using STREAM to read data
  * from the patch file. If no hunk can be found, set *HUNK to NULL.
+ * If REVERSE is TRUE, invert the hunk while parsing it.
  * Allocate results in RESULT_POOL.
  * Use SCRATCH_POOL for all other allocations. */
 static svn_error_t *
 parse_next_hunk(svn_hunk_t **hunk,
                 svn_patch_t *patch,
                 svn_stream_t *stream,
+                svn_boolean_t reverse,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
 {
   static const char * const minus = "--- ";
   static const char * const atat = "@@";
+  svn_stringbuf_t *line;
   svn_boolean_t eof, in_hunk, hunk_seen;
   apr_off_t pos, last_line;
   apr_off_t start, end;
@@ -224,6 +281,9 @@ parse_next_hunk(svn_hunk_t **hunk,
   svn_stream_t *original_text;
   svn_stream_t *modified_text;
   svn_linenum_t original_lines;
+  svn_linenum_t leading_context;
+  svn_linenum_t trailing_context;
+  svn_boolean_t changed_line_seen;
   apr_pool_t *iterpool;
 
   if (apr_file_eof(patch->patch_file) == APR_EOF)
@@ -235,6 +295,9 @@ parse_next_hunk(svn_hunk_t **hunk,
 
   in_hunk = FALSE;
   hunk_seen = FALSE;
+  leading_context = 0;
+  trailing_context = 0;
+  changed_line_seen = FALSE;
   *hunk = apr_pcalloc(result_pool, sizeof(**hunk));
 
   /* Get current seek position -- APR has no ftell() :( */
@@ -244,7 +307,6 @@ parse_next_hunk(svn_hunk_t **hunk,
   iterpool = svn_pool_create(scratch_pool);
   do
     {
-      svn_stringbuf_t *line;
 
       svn_pool_clear(iterpool);
 
@@ -269,6 +331,19 @@ parse_next_hunk(svn_hunk_t **hunk,
       if (in_hunk)
         {
           char c;
+          char add;
+          char del;
+
+          if (reverse)
+            {
+              add = '-';
+              del = '+';
+            }
+          else
+            {
+              add = '+';
+              del = '-';
+            }
 
           if (! hunk_seen)
             {
@@ -278,20 +353,28 @@ parse_next_hunk(svn_hunk_t **hunk,
             }
 
           c = line->data[0];
-          if (original_lines > 0 && (c == ' ' || c == '-'))
+          /* Tolerate chopped leading spaces on empty lines. */
+          if (original_lines > 0 && (c == ' ' || (! eof && line->len == 0)))
             {
               hunk_seen = TRUE;
               original_lines--;
+              if (changed_line_seen)
+                trailing_context++;
+              else
+                leading_context++;
             }
-          else if (c == '+')
+          else if (c == add || c == del)
             {
               hunk_seen = TRUE;
-            }
-          /* Tolerate chopped leading spaces on empty lines. */
-          else if (original_lines > 0 && ! eof && line->len == 0)
-            {
-              hunk_seen = TRUE;
-              original_lines--;
+              changed_line_seen = TRUE;
+
+              /* A hunk may have context in the middle. We only want the
+                 last lines of context. */
+              if (trailing_context > 0)
+                trailing_context = 0;
+
+              if (original_lines > 0 && c == del)
+                original_lines--;
             }
           else
             {
@@ -308,8 +391,9 @@ parse_next_hunk(svn_hunk_t **hunk,
         {
           if (starts_with(line->data, atat))
             {
-              /* Looks like we have a hunk header, let's try to rip it apart. */
-              in_hunk = parse_hunk_header(line->data, *hunk, iterpool);
+              /* Looks like we have a hunk header, try to rip it apart. */
+              in_hunk = parse_hunk_header(line->data, *hunk, reverse,
+                                          iterpool);
               if (in_hunk)
                 original_lines = (*hunk)->original_length;
             }
@@ -318,12 +402,14 @@ parse_next_hunk(svn_hunk_t **hunk,
             break;
         }
     }
-  while (! eof);
+  /* Check for the line length since a file may not have a newline at the
+   * end and we depend upon the last line to be an empty one. */
+  while (! eof || line->len > 0);
   svn_pool_destroy(iterpool);
 
   if (! eof)
     /* Rewind to the start of the line just read, so subsequent calls
-     * to this function or svn_diff__parse_next_patch() don't end
+     * to this function or svn_diff_parse_next_patch() don't end
      * up skipping the line -- it may contain a patch or hunk header. */
     SVN_ERR(svn_io_file_seek(patch->patch_file, APR_SET, &last_line,
                              scratch_pool));
@@ -361,8 +447,20 @@ parse_next_hunk(svn_hunk_t **hunk,
                                                remove_leading_char_transformer);
       /* Set the hunk's texts. */
       (*hunk)->diff_text = diff_text;
-      (*hunk)->original_text = original_text;
-      (*hunk)->modified_text = modified_text;
+      if (reverse)
+        {
+          svn_stream_set_line_transformer_callback(diff_text,
+                                                   reverse_diff_transformer);
+          (*hunk)->original_text = modified_text;
+          (*hunk)->modified_text = original_text;
+        }
+      else
+        {
+          (*hunk)->original_text = original_text;
+          (*hunk)->modified_text = modified_text;
+        }
+      (*hunk)->leading_context = leading_context;
+      (*hunk)->trailing_context = trailing_context;
     }
   else
     /* Something went wrong, just discard the result. */
@@ -390,7 +488,7 @@ compare_hunks(const void *a, const void 
  * Ensure that all streams which were opened for HUNK are closed.
  */
 static svn_error_t *
-close_hunk(svn_hunk_t *hunk)
+close_hunk(const svn_hunk_t *hunk)
 {
   SVN_ERR(svn_stream_close(hunk->original_text));
   SVN_ERR(svn_stream_close(hunk->modified_text));
@@ -399,10 +497,11 @@ close_hunk(svn_hunk_t *hunk)
 }
 
 svn_error_t *
-svn_diff__parse_next_patch(svn_patch_t **patch,
-                           apr_file_t *patch_file,
-                           apr_pool_t *result_pool,
-                           apr_pool_t *scratch_pool)
+svn_diff_parse_next_patch(svn_patch_t **patch,
+                          apr_file_t *patch_file,
+                          svn_boolean_t reverse,
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool)
 {
   static const char * const minus = "--- ";
   static const char * const plus = "+++ ";
@@ -471,14 +570,20 @@ svn_diff__parse_next_patch(svn_patch_t *
           if ((! in_header) && strcmp(indicator, minus) == 0)
             {
               /* First line of header contains old filename. */
-              (*patch)->old_filename = apr_pstrdup(result_pool, canon_path);
+              if (reverse)
+                (*patch)->new_filename = apr_pstrdup(result_pool, canon_path);
+              else
+                (*patch)->old_filename = apr_pstrdup(result_pool, canon_path);
               indicator = plus;
               in_header = TRUE;
             }
           else if (in_header && strcmp(indicator, plus) == 0)
             {
               /* Second line of header contains new filename. */
-              (*patch)->new_filename = apr_pstrdup(result_pool, canon_path);
+              if (reverse)
+                (*patch)->old_filename = apr_pstrdup(result_pool, canon_path);
+              else
+                (*patch)->new_filename = apr_pstrdup(result_pool, canon_path);
               in_header = FALSE;
               break; /* All good! */
             }
@@ -501,8 +606,8 @@ svn_diff__parse_next_patch(svn_patch_t *
         {
           svn_pool_clear(iterpool);
 
-          SVN_ERR(parse_next_hunk(&hunk, *patch, stream, result_pool,
-                                  iterpool));
+          SVN_ERR(parse_next_hunk(&hunk, *patch, stream, reverse,
+                                  result_pool, iterpool));
           if (hunk)
             APR_ARRAY_PUSH((*patch)->hunks, svn_hunk_t *) = hunk;
         }
@@ -526,13 +631,13 @@ svn_diff__parse_next_patch(svn_patch_t *
 }
 
 svn_error_t *
-svn_diff__close_patch(svn_patch_t *patch)
+svn_diff_close_patch(const svn_patch_t *patch)
 {
   int i;
 
   for (i = 0; i < patch->hunks->nelts; i++)
     {
-      svn_hunk_t *hunk = APR_ARRAY_IDX(patch->hunks, i, svn_hunk_t *);
+      const svn_hunk_t *hunk = APR_ARRAY_IDX(patch->hunks, i, svn_hunk_t *);
       SVN_ERR(close_hunk(hunk));
     }
 

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs/fs-loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs/fs-loader.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs/fs-loader.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs/fs-loader.c Tue Aug 10 18:06:17 2010
@@ -760,7 +760,7 @@ svn_fs_change_txn_prop(svn_fs_txn_t *txn
 }
 
 svn_error_t *
-svn_fs_change_txn_props(svn_fs_txn_t *txn, apr_array_header_t *props,
+svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
                         apr_pool_t *pool)
 {
   return txn->vtable->change_props(txn, props, pool);

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs/fs-loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs/fs-loader.h?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs/fs-loader.h (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs/fs-loader.h Tue Aug 10 18:06:17 2010
@@ -214,7 +214,7 @@ typedef struct txn_vtable_t
                               const svn_string_t *value, apr_pool_t *pool);
   svn_error_t *(*root)(svn_fs_root_t **root_p, svn_fs_txn_t *txn,
                        apr_pool_t *pool);
-  svn_error_t *(*change_props)(svn_fs_txn_t *txn, apr_array_header_t *props,
+  svn_error_t *(*change_props)(svn_fs_txn_t *txn, const apr_array_header_t *props,
                                apr_pool_t *pool);
 } txn_vtable_t;
 

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/bdb/changes-table.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/bdb/changes-table.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/bdb/changes-table.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/bdb/changes-table.c Tue Aug 10 18:06:17 2010
@@ -168,6 +168,15 @@ fold_change(apr_hash_t *changes,
           (SVN_ERR_FS_CORRUPT, NULL,
            _("Invalid change ordering: non-add change on deleted path"));
 
+      /* Sanity check: an add can't follow anything except
+         a delete or reset.  */
+      if ((change->kind == svn_fs_path_change_add)
+          && (old_change->change_kind != svn_fs_path_change_delete)
+          && (old_change->change_kind != svn_fs_path_change_reset))
+        return svn_error_create
+          (SVN_ERR_FS_CORRUPT, NULL,
+           _("Invalid change ordering: add change on preexisting path"));
+
       /* Now, merge that change in. */
       switch (change->kind)
         {

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/dag.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/dag.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/dag.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/dag.c Tue Aug 10 18:06:17 2010
@@ -281,7 +281,7 @@ get_dir_entries(apr_hash_t **entries_p,
                 trail_t *trail,
                 apr_pool_t *pool)
 {
-  apr_hash_t *entries = apr_hash_make(pool);
+  apr_hash_t *entries = NULL;
   apr_hash_index_t *hi;
   svn_string_t entries_raw;
   svn_skel_t *entries_skel;
@@ -290,7 +290,7 @@ get_dir_entries(apr_hash_t **entries_p,
   if (noderev->kind != svn_node_dir)
     return svn_error_create
       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
-       _("Attempted to create entry in non-directory parent"));
+       _("Attempted to get entries of a non-directory node"));
 
   /* If there's a DATA-KEY, there might be entries to fetch. */
   if (noderev->data_key)
@@ -836,13 +836,6 @@ svn_fs_base__dag_clone_root(dag_node_t *
 }
 
 
-/* Delete the directory entry named NAME from PARENT, as part of
-   TRAIL.  PARENT must be mutable.  NAME must be a single path
-   component.  If REQUIRE_EMPTY is true and the node being deleted is
-   a directory, it must be empty.
-
-   If return SVN_ERR_FS_NO_SUCH_ENTRY, then there is no entry NAME in
-   PARENT.  */
 svn_error_t *
 svn_fs_base__dag_delete(dag_node_t *parent,
                         const char *name,
@@ -1043,8 +1036,8 @@ svn_fs_base__dag_delete_if_mutable(svn_f
         }
     }
 
-  /* ... then delete the node itself, after deleting any mutable
-     representations and strings it points to. */
+  /* ... then delete the node itself, any mutable representations and
+     strings it points to, and possibly its node-origins record. */
   return svn_fs_base__dag_remove_node(fs, id, txn_id, trail, pool);
 }
 
@@ -1342,8 +1335,8 @@ svn_fs_base__dag_finalize_edits(dag_node
     SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, old_data_key, txn_id,
                                                trail, pool));
 
-  /* If we've got a discardable rep (probably because we ended us
-     re-using a preexisting one).  Throw out the discardable rep. */
+  /* If we've got a discardable rep (probably because we ended up
+     re-using a preexisting one), throw out the discardable rep. */
   if (useless_data_key)
     SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, useless_data_key,
                                                txn_id, trail, pool));
@@ -1537,6 +1530,33 @@ svn_fs_base__dag_deltify(dag_node_t *tar
 }
 
 
+svn_error_t *
+svn_fs_base__dag_obliterate_rep(dag_node_t *node,
+                                dag_node_t *pred_node,
+                                trail_t *trail,
+                                apr_pool_t *pool)
+{
+  node_revision_t *node_rev, *pred_node_rev;
+  svn_fs_t *fs = svn_fs_base__dag_get_fs(node);
+  const char *pred_key;
+
+  SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, fs, node->id, trail, pool));
+  if (pred_node)
+    {
+      SVN_ERR(svn_fs_bdb__get_node_revision(&pred_node_rev, fs, pred_node->id,
+                                            trail, pool));
+      pred_key = pred_node_rev->data_key;
+    }
+  else
+    {
+      pred_key = NULL;
+    }
+
+  return svn_fs_base__rep_obliterate(trail->fs, node_rev->data_key, pred_key,
+                                     trail, pool);
+}
+
+
 /* Maybe store a `checksum-reps' index record for the representation whose
    key is REP.  (If there's already a rep for this checksum, we don't
    bother overwriting it.)  */
@@ -1643,7 +1663,8 @@ node_origins_update(const char *new_txn_
 
   /* To find the nodes that originate in the old txn, we'll look in the
    * "changes" table. Any change that added a node could have created a new
-   * node id. */
+   * node id, but that change may have been superceded by a later change that
+   * deleted that node id and its corresponding 'node-origins' record. */
   SVN_ERR(svn_fs_bdb__changes_fetch_raw(&changes, trail->fs, old_txn_id, trail,
                                         scratch_pool));
   for (i = 0; i < changes->nelts; i++)
@@ -1657,13 +1678,21 @@ node_origins_update(const char *new_txn_
         {
           const svn_fs_id_t *origin_id;
           const char *node_id, *id_copy_id, *id_txn_id;
+          svn_error_t *err;
 
           /* Find the destination node id of this change */
           node_id = svn_fs_base__id_node_id(change->noderev_id);
 
           /* Fetch the old node-origin */
-          SVN_ERR(svn_fs_bdb__get_node_origin(&origin_id, trail->fs, node_id,
+          err = (svn_fs_bdb__get_node_origin(&origin_id, trail->fs, node_id,
                                               trail, iterpool));
+          if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN)
+            {
+              svn_error_clear(err);
+              continue;
+            }
+          SVN_ERR(err);
+
           id_copy_id = svn_fs_base__id_copy_id(origin_id);
           id_txn_id = svn_fs_base__id_txn_id(origin_id);
 
@@ -1689,6 +1718,9 @@ node_origins_update(const char *new_txn_
 /* Modify each row in the "copies" table that is keyed by a (const char *)
  * copy-id listed in COPY_IDS, changing the txn-id component of its
  * "dst_noderev_id" field from OLD_TXN_ID to NEW_TXN_ID.
+ * If the dst_noderev_id then references a node-revision that does not exist,
+ * it must have been obliterated, so remove each such copy-id from COPY_IDS
+ * and from the "copies" table.
  * Each entry in COPY_IDS must match exactly one row in the "copies" table.
  *
  * Work within TRAIL. */
@@ -1699,14 +1731,18 @@ copies_update(const char *new_txn_id,
               trail_t *trail,
               apr_pool_t *scratch_pool)
 {
-  int i;
+  int i, i_out;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
+  /* Loop over COPY_IDS, reading from index I, writing to index I_OUT.
+   * At the end, if I_OUT < I then some elements have been deleted. */
+  i_out = 0;
   for (i = 0; i < copy_ids->nelts; i++)
     {
       const char *copy_id = APR_ARRAY_IDX(copy_ids, i, const char *);
       copy_t *copy;
       const char *id_node_id, *id_copy_id, *id_txn_id;
+      svn_error_t *err;
 
       svn_pool_clear(iterpool);
 
@@ -1723,14 +1759,31 @@ copies_update(const char *new_txn_id,
       copy->dst_noderev_id = svn_fs_base__id_create(id_node_id, id_copy_id,
                                                     new_txn_id, iterpool);
 
-      /* Write the new "copy" row back under the same key (copy_id) */
-      SVN_ERR(svn_fs_bdb__create_copy(trail->fs, copy_id,
-                                      copy->src_path, copy->src_txn_id,
-                                      copy->dst_noderev_id, copy->kind,
-                                      trail, iterpool));
-    }
+      /* Depending on whether the new node-revision exists ... */
+      err = svn_fs_bdb__get_node_revision(NULL, trail->fs,
+                                          copy->dst_noderev_id, trail,
+                                          iterpool);
+      if (err == NULL)
+        {
+          /* Write the new "copy" row back under the same key (copy_id) */
+          SVN_ERR(svn_fs_bdb__create_copy(trail->fs, copy_id,
+                                          copy->src_path, copy->src_txn_id,
+                                          copy->dst_noderev_id, copy->kind,
+                                          trail, iterpool));
+          APR_ARRAY_IDX(copy_ids, i_out++, const char *) = copy_id;
+        }
+      else
+        {
+          svn_error_clear(err);
 
+          /* Delete the copy id from COPY_IDS, and delete the copy row from
+           * the "copies" table. */
+          SVN_ERR(svn_fs_bdb__delete_copy(trail->fs, copy_id, trail, iterpool));
+        }
+    }
+  copy_ids->nelts = i_out;
   svn_pool_destroy(iterpool);
+
   return SVN_NO_ERROR;
 }
 
@@ -1766,6 +1819,10 @@ svn_fs_base__dag_commit_obliteration_txn
     SVN_ERR(copies_update(txn->id, old_txn_id, txn_obj->copies, trail,
                           scratch_pool));
 
+  /* Write back the new txn in case we changed its "copies" list */
+  SVN_ERR(svn_fs_bdb__put_txn(trail->fs, txn_obj, txn->id, trail,
+                              scratch_pool));
+
   /* Replace the revision entry in the `revisions' table. */
   revision.txn_id = txn->id;
   SVN_ERR(svn_fs_bdb__put_rev(&replacing_rev, txn->fs, &revision, trail,

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/dag.h
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/dag.h?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/dag.h (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/dag.h Tue Aug 10 18:06:17 2010
@@ -365,9 +365,12 @@ svn_error_t *svn_fs_base__dag_clone_chil
 /* Delete the directory entry named NAME from PARENT, as part of
    TRAIL.  PARENT must be mutable.  NAME must be a single path
    component; it cannot be a slash-separated directory path.  If the
-   node being deleted is a mutable directory, remove all mutable nodes
-   reachable from it.  TXN_ID is the Subversion transaction under
-   which this occurs.
+   entry being deleted points to a mutable node revision, also remove
+   that node revision and (if it is a directory) all mutable node
+   revisions reachable from it.  Also delete the node-origins record
+   for each deleted node revision that had no predecessor.
+
+   TXN_ID is the Subversion transaction under which this occurs.
 
    If return SVN_ERR_FS_NO_SUCH_ENTRY, then there is no entry NAME in
    PARENT.  */
@@ -380,9 +383,12 @@ svn_error_t *svn_fs_base__dag_delete(dag
 
 /* Delete the node revision assigned to node ID from FS's `nodes'
    table, as part of TRAIL.  Also delete any mutable representations
-   and strings associated with that node revision.  ID may refer to a
-   file or directory, which must be mutable.  TXN_ID is the Subversion
-   transaction under which this occurs.
+   and strings associated with that node revision.  Also delete the
+   node-origins record for this node revision's node id, if this node
+   revision had no predecessor.
+
+   ID may refer to a file or directory, which must be mutable.  TXN_ID
+   is the Subversion transaction under which this occurs.
 
    NOTE: If ID represents a directory, and that directory has mutable
    children, you risk orphaning those children by leaving them
@@ -398,9 +404,12 @@ svn_error_t *svn_fs_base__dag_remove_nod
 /* Delete all mutable node revisions reachable from node ID, including
    ID itself, from FS's `nodes' table, as part of TRAIL.  Also delete
    any mutable representations and strings associated with that node
-   revision.  ID may refer to a file or directory, which may be
-   mutable or immutable.  TXN_ID is the Subversion transaction under
-   which this occurs.  */
+   revision.  Also delete the node-origins record for each deleted
+   node revision that had no predecessor.
+
+   ID may refer to a file or directory, which may be mutable or
+   immutable.  TXN_ID is the Subversion transaction under which this
+   occurs.  */
 svn_error_t *svn_fs_base__dag_delete_if_mutable(svn_fs_t *fs,
                                                 const svn_fs_id_t *id,
                                                 const char *txn_id,
@@ -549,6 +558,15 @@ svn_error_t *svn_fs_base__dag_deltify(da
                                       trail_t *trail,
                                       apr_pool_t *pool);
 
+/* Obliterate NODE's data by constructing a new representation that
+   consists of a no-change delta from PRED_NODE, and changing NODE to
+   use that new rep, and leaving the old rep alone in case it is used
+   by other nodes.  If PRED_NODE is null
+   then construct a representation with an empty fulltext instead. */
+svn_error_t *svn_fs_base__dag_obliterate_rep(dag_node_t *node,
+                                             dag_node_t *pred_node,
+                                             trail_t *trail,
+                                             apr_pool_t *pool);
 
 /* Index NODE's backing data representations by their checksum.  Do
    this as part of TRAIL.  Use POOL for allocations. */

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/node-rev.h
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/node-rev.h?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/node-rev.h (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/node-rev.h Tue Aug 10 18:06:17 2010
@@ -82,7 +82,7 @@ svn_error_t *svn_fs_base__create_success
 /* Delete node revision ID from FS's `nodes' table, as part of TRAIL.
    If ORIGIN_ALSO is set, also delete the record for this ID's node ID
    from the `node-origins' index table (which is typically only done
-   if the caller things that ID points to the only node revision ID in
+   if the caller thinks that ID points to the only node revision ID in
    its line of history).
 
    WARNING: This does not check that the node revision is mutable!

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/obliterate.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/obliterate.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/obliterate.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/obliterate.c Tue Aug 10 18:06:17 2010
@@ -161,8 +161,8 @@ svn_fs_base__node_rev_dup(const svn_fs_i
           for (hi = apr_hash_first(scratch_pool, entries); hi;
                hi = apr_hash_next(hi))
             {
-              const char *child_name = svn_apr_hash_index_key(hi);
-              svn_fs_dirent_t *child_entry = svn_apr_hash_index_val(hi);
+              const char *child_name = svn__apr_hash_index_key(hi);
+              svn_fs_dirent_t *child_entry = svn__apr_hash_index_val(hi);
               const svn_fs_id_t *new_child_id;
 
               svn_pool_clear(iterpool);

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/reps-strings.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/reps-strings.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/reps-strings.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/reps-strings.c Tue Aug 10 18:06:17 2010
@@ -132,7 +132,7 @@ delta_string_keys(apr_array_header_t **k
 
 /* Delete the strings associated with array KEYS in FS as part of TRAIL.  */
 static svn_error_t *
-delete_strings(apr_array_header_t *keys,
+delete_strings(const apr_array_header_t *keys,
                svn_fs_t *fs,
                trail_t *trail,
                apr_pool_t *pool)
@@ -323,7 +323,7 @@ get_one_window(struct compose_handler_ba
 
 static svn_error_t *
 rep_undeltify_range(svn_fs_t *fs,
-                    apr_array_header_t *deltas,
+                    const apr_array_header_t *deltas,
                     representation_t *fulltext,
                     int cur_chunk,
                     char *buf,
@@ -1621,3 +1621,132 @@ svn_fs_base__rep_deltify(svn_fs_t *fs,
 
   return SVN_NO_ERROR;
 }
+
+svn_error_t *svn_fs_base__rep_obliterate(svn_fs_t *fs,
+                                         const char *key,
+                                         const char *pred_key,
+                                         trail_t *trail,
+                                         apr_pool_t *pool)
+{
+  const char *new_str = NULL;
+  representation_t *empty;
+  svn_stream_t *new_stream;
+  struct write_svndiff_strings_baton new_baton;
+  svn_stream_t *pred_stream1, *pred_stream2;
+  svn_txdelta_stream_t *txdelta_stream;
+  base_fs_data_t *bfd = fs->fsap_data;
+  svn_txdelta_window_handler_t new_handler;
+  void *new_handler_baton;
+  apr_pool_t *wpool;
+  apr_array_header_t *windows;
+  window_write_t *ww;
+  svn_txdelta_window_t *window;
+  svn_filesize_t tview_off = 0;
+  svn_filesize_t diffsize = 0;
+  const unsigned char *digest;
+  representation_t *pred_rep;
+  representation_t new_rep;
+  rep_delta_chunk_t *chunk;
+  apr_array_header_t *chunks;
+  int i;
+
+  if (!pred_key)
+    {
+      /* No predecessor so just write a new empty rep */
+      SVN_ERR(svn_fs_bdb__string_append(fs, &new_str, 0, NULL, trail, pool));
+      empty = make_fulltext_rep(new_str, NULL,
+                                svn_checksum_empty_checksum(svn_checksum_md5,
+                                                            pool),
+                                svn_checksum_empty_checksum(svn_checksum_sha1,
+                                                            pool),
+                                pool);
+      SVN_ERR(svn_fs_bdb__write_rep(fs, key, empty, trail, pool));
+
+      return SVN_NO_ERROR;
+    }
+
+  if (!strcmp(key, pred_key))
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             _("Attempt to obliterate '%s' using itself "),
+                             key);
+
+  new_baton.fs = fs;
+  new_baton.trail = trail;
+  new_baton.header_read = FALSE;
+  new_stream = svn_stream_create(&new_baton, pool);
+  svn_stream_set_write(new_stream, write_svndiff_strings);
+
+  /* ### Is there a simpler way to write a no-change delta? */
+  SVN_ERR(svn_fs_base__rep_contents_read_stream(&pred_stream1, fs, pred_key,
+                                                TRUE, trail, pool));
+  SVN_ERR(svn_fs_base__rep_contents_read_stream(&pred_stream2, fs, pred_key,
+                                                TRUE, trail, pool));
+  svn_txdelta(&txdelta_stream, pred_stream1, pred_stream2, pool);
+
+  if (bfd->format >= SVN_FS_BASE__MIN_SVNDIFF1_FORMAT)
+    svn_txdelta_to_svndiff2(&new_handler, &new_handler_baton,
+                            new_stream, 1, pool);
+  else
+    svn_txdelta_to_svndiff2(&new_handler, &new_handler_baton,
+                            new_stream, 0, pool);
+
+  wpool = svn_pool_create(pool);
+  windows = apr_array_make(pool, 1, sizeof(ww));
+  do
+    {
+      new_baton.size = 0;
+      new_baton.key = NULL;
+      svn_pool_clear(wpool);
+
+      SVN_ERR(svn_txdelta_next_window(&window, txdelta_stream, wpool));
+      SVN_ERR(new_handler(window, new_handler_baton));
+      if (window)
+        {
+          ww = apr_pcalloc(pool, sizeof(*ww));
+          ww->key = new_baton.key;
+          ww->svndiff_len = new_baton.size;
+          ww->text_off = tview_off;
+          ww->text_len = window->tview_len;
+          APR_ARRAY_PUSH(windows, window_write_t *) = ww;
+          tview_off += window->tview_len;
+          diffsize += ww->svndiff_len;
+        }
+    } while (window);
+
+  svn_pool_destroy(wpool);
+
+  digest = svn_txdelta_md5_digest(txdelta_stream);
+  if (!digest)
+    return svn_error_createf(SVN_ERR_DELTA_MD5_CHECKSUM_ABSENT, NULL,
+                             _("Failed to calculate MD5 digest for '%s'"),
+                             pred_key);
+  /* ### Check the digest against something?  pred_rep->md5_checksum? */
+
+  SVN_ERR(svn_fs_bdb__read_rep(&pred_rep, fs, pred_key, trail, pool));
+  new_rep.md5_checksum = svn_checksum_dup(pred_rep->md5_checksum, pool);
+  new_rep.sha1_checksum = svn_checksum_dup(pred_rep->sha1_checksum, pool);
+  new_rep.kind = rep_kind_delta;
+  new_rep.txn_id = NULL;
+
+  chunks = apr_array_make(pool, windows->nelts, sizeof(chunk));
+
+  for (i = 0; i < windows->nelts; i++)
+    {
+      ww = APR_ARRAY_IDX(windows, i, window_write_t *);
+
+      chunk = apr_palloc(pool, sizeof(*chunk));
+      chunk->offset = ww->text_off;
+
+      chunk->version = new_baton.version;
+      chunk->string_key = ww->key;
+      chunk->size = ww->text_len;
+      chunk->rep_key = pred_key;
+
+      APR_ARRAY_PUSH(chunks, rep_delta_chunk_t *) = chunk;
+    }
+
+  new_rep.contents.delta.chunks = chunks;
+  SVN_ERR(svn_fs_bdb__write_rep(fs, key, &new_rep, trail, pool));
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/reps-strings.h
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/reps-strings.h?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/reps-strings.h (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/reps-strings.h Tue Aug 10 18:06:17 2010
@@ -38,14 +38,14 @@ extern "C" {
 
 
 
-/* Get or create a mutable representation in FS, store the new rep's
-   key in *NEW_REP_KEY.
+/* Get or create a mutable representation in FS, and set *NEW_REP_KEY to its
+   key.
 
    TXN_ID is the id of the Subversion transaction under which this occurs.
 
-   If REP_KEY is already a mutable representation, set *NEW_REP_KEY to
-   REP_KEY, else set *NEW_REP_KEY to a brand new rep key allocated in
-   POOL. */
+   If REP_KEY is not null and is already a mutable representation, set
+   *NEW_REP_KEY to REP_KEY, else create a brand new rep and set *NEW_REP_KEY
+   to its key, allocated in POOL. */
 svn_error_t *svn_fs_base__get_mutable_rep(const char **new_rep_key,
                                           const char *rep_key,
                                           svn_fs_t *fs,
@@ -168,6 +168,18 @@ svn_error_t *svn_fs_base__rep_deltify(sv
                                       trail_t *trail,
                                       apr_pool_t *pool);
 
+/* Obliterate KEY's data by creating a new rep that consists of a
+   no-change delta from PRED_KEY's data.  If PRED_KEY is null then
+   construct an empty fulltext instead of a delta.  KEY's old data
+   remains in the database in case some other key's data is derived
+   from it. */
+/* ### TODO: clarify.  What kind of objects are KEY and PRED_KEY, and what
+   does it do with the new rep? */
+svn_error_t *svn_fs_base__rep_obliterate(svn_fs_t *fs,
+                                         const char *key,
+                                         const char *pred_key,
+                                         trail_t *trail,
+                                         apr_pool_t *pool);
 
 
 #ifdef __cplusplus

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/revs-txns.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/revs-txns.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/revs-txns.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/revs-txns.c Tue Aug 10 18:06:17 2010
@@ -56,8 +56,8 @@
 /* Set *txn_p to a transaction object allocated in POOL for the
    transaction in FS whose id is TXN_ID.  If EXPECT_DEAD is set, this
    transaction must be a dead one, else an error is returned.  If
-   EXPECT_DEAD is not set, an error is thrown if the transaction is
-   *not* dead. */
+   EXPECT_DEAD is not set, the transaction must *not* be a dead one,
+   else an error is returned. */
 static svn_error_t *
 get_txn(transaction_t **txn_p,
         svn_fs_t *fs,
@@ -437,9 +437,10 @@ svn_fs_base__add_txn_copy(svn_fs_t *fs,
 /* Duplicate all entries in the "changes" table that are keyed by OLD_TXN_ID,
  * creating new entries that are keyed by NEW_TXN_ID.
  *
- * Each new "change" has the same content as the old one, except that if the
+ * Give each new "change" the same content as the old one, except that if the
  * txn-id component of its noderev-id is OLD_TXN_ID (which is the case for
- * all changes except deletes) then that is changed to NEW_TXN_ID.
+ * all changes except deletes, and for some deletes, it seems) then change
+ * that to NEW_TXN_ID.
  *
  * Work within TRAIL. */
 static svn_error_t *
@@ -456,17 +457,15 @@ changes_dup(const char *new_txn_id,
   for (i = 0; i < changes->nelts; i++)
     {
       change_t *change = APR_ARRAY_IDX(changes, i, change_t *);
-
-      if (change->kind != svn_fs_path_change_delete
-          && change->kind != svn_fs_path_change_reset)
+      const char *node_id = svn_fs_base__id_node_id(change->noderev_id);
+      const char *copy_id = svn_fs_base__id_copy_id(change->noderev_id);
+      const char *txn_id = svn_fs_base__id_txn_id(change->noderev_id);
+
+      /* if (change->kind != svn_fs_path_change_delete
+       *     && change->kind != svn_fs_path_change_reset) */
+      if (svn_fs_base__key_compare(txn_id, old_txn_id) == 0)
         {
-          const char *node_id, *copy_id;
-
           /* Modify the "change": change noderev_id's txn_id to NEW_TXN_ID */
-          node_id = svn_fs_base__id_node_id(change->noderev_id);
-          copy_id = svn_fs_base__id_copy_id(change->noderev_id);
-          SVN_ERR_ASSERT(svn_fs_base__key_compare(
-            svn_fs_base__id_txn_id(change->noderev_id), old_txn_id) == 0);
           change->noderev_id = svn_fs_base__id_create(node_id, copy_id,
                                                       new_txn_id,
                                                       scratch_pool);
@@ -638,7 +637,7 @@ svn_fs_base__change_txn_prop(svn_fs_txn_
 
 svn_error_t *
 svn_fs_base__change_txn_props(svn_fs_txn_t *txn,
-                              apr_array_header_t *props,
+                              const apr_array_header_t *props,
                               apr_pool_t *pool)
 {
   apr_pool_t *iterpool = svn_pool_create(pool);

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/revs-txns.h
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/revs-txns.h?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/revs-txns.h (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/revs-txns.h Tue Aug 10 18:06:17 2010
@@ -226,7 +226,7 @@ svn_error_t *svn_fs_base__change_txn_pro
                                           apr_pool_t *pool);
 
 svn_error_t *svn_fs_base__change_txn_props(svn_fs_txn_t *txn,
-                                           apr_array_header_t *props,
+                                           const apr_array_header_t *props,
                                            apr_pool_t *pool);
 
 

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/tree.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/tree.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/tree.c Tue Aug 10 18:06:17 2010
@@ -2796,6 +2796,7 @@ svn_fs_base__commit_obliteration_txn(svn
 {
   struct commit_args commit_args;
 
+  /* Commit the replacement transaction. */
   /* We do not need a re-try loop like the (catch up to head, try to
    * commit) loop that svn_fs_base__commit_txn() uses, because the only
    * concurrent changes that can affect this old revision are other
@@ -2806,7 +2807,9 @@ svn_fs_base__commit_obliteration_txn(svn
   SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_commit_obliteration,
                                  &commit_args, FALSE, pool));
 
-  /* return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); */
+  /* Remove the old txn and any unreferenced data attached to it. */
+  /* ### ... */
+
   return SVN_NO_ERROR;
 }
 
@@ -2937,6 +2940,94 @@ svn_fs_base__deltify(svn_fs_t *fs,
 }
 
 
+struct txn_obliterate_rep_args
+{
+  const svn_fs_id_t *id;
+  svn_boolean_t has_pred;
+  const svn_fs_id_t *pred_id;
+};
+
+static svn_error_t *
+txn_body_obliterate_rep(void *baton, trail_t *trail)
+{
+  struct txn_obliterate_rep_args *args = baton;
+  dag_node_t *node, *pred_node;
+
+  SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
+                                    trail, trail->pool));
+  if (args->has_pred)
+    {
+      SVN_ERR(svn_fs_base__dag_get_node(&pred_node, trail->fs, args->pred_id,
+                                        trail, trail->pool));
+    }
+  else
+    {
+      pred_node = NULL;
+    }
+
+  SVN_ERR(svn_fs_base__dag_obliterate_rep(node, pred_node, trail, trail->pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_base__obliterate_rep(svn_fs_t *fs,
+                            const char *path,
+                            svn_revnum_t revision,
+                            apr_pool_t *pool)
+{
+  svn_fs_root_t *root;
+  const char *txn_id;
+  struct rev_get_txn_id_args get_txn_args;
+  const svn_fs_id_t *id;
+  svn_node_kind_t kind;
+  struct txn_pred_count_args pred_count_args;
+  struct txn_obliterate_rep_args oblit_args;
+
+  SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool));
+  get_txn_args.txn_id = &txn_id;
+  get_txn_args.revision = revision;
+  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &get_txn_args,
+                                 FALSE, pool));
+
+  SVN_ERR(base_node_id(&id, root, path, pool));
+  if (strcmp(svn_fs_base__id_txn_id(id), txn_id))
+    return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
+                             _("Unexpected immutable node at '%s'"), path);
+
+  SVN_ERR(base_check_path(&kind, root, path, pool));
+  if (kind != svn_node_file)
+    return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
+                             _("Cannot obliterate '%s' as it is not a file"),
+                             path);
+
+  pred_count_args.id = id;
+  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &pred_count_args,
+                                 FALSE, pool));
+
+  if (pred_count_args.pred_count > 0)
+    {
+      struct txn_pred_id_args pred_id_args;
+
+      pred_id_args.id = id;
+      pred_id_args.pool = pool;
+      SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &pred_id_args,
+                                     FALSE, pool));
+
+      oblit_args.has_pred = TRUE;
+      oblit_args.pred_id = pred_id_args.pred_id;
+    }
+  else
+    {
+      oblit_args.has_pred = FALSE;
+    }
+  oblit_args.id = id;
+
+  return svn_fs_base__retry_txn(fs, txn_body_obliterate_rep, &oblit_args,
+                                TRUE, pool);
+}
+
+
 
 /* Modifying directories */
 

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/tree.h
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/tree.h?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/tree.h (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/tree.h Tue Aug 10 18:06:17 2010
@@ -95,6 +95,13 @@ svn_error_t *svn_fs_base__get_path_creat
                                                trail_t *trail,
                                                apr_pool_t *pool);
 
+/* ### Experimental obliterate-like-deltify - see implementation. */
+svn_error_t *
+svn_fs_base__obliterate_rep(svn_fs_t *fs,
+                            const char *path,
+                            svn_revnum_t revision,
+                            apr_pool_t *pool);
+
 
 #ifdef __cplusplus
 }

Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/util/fs_skels.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/util/fs_skels.c?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/util/fs_skels.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_fs_base/util/fs_skels.c Tue Aug 10 18:06:17 2010
@@ -433,8 +433,9 @@ svn_fs_base__parse_transaction_skel(tran
     {
       /* Committed transactions have a revision number... */
       transaction->base_id = NULL;
-      transaction->revision = atoi(apr_pstrmemdup(pool, base_id_or_rev->data,
-                                                  base_id_or_rev->len));
+      transaction->revision =
+        SVN_STR_TO_REV(apr_pstrmemdup(pool, base_id_or_rev->data,
+                                      base_id_or_rev->len));
       if (! SVN_IS_VALID_REVNUM(transaction->revision))
         return skel_err("transaction");