subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From br...@apache.org
Subject svn commit: r1438999 [2/7] - in /subversion/branches/windows-build-update: ./ build/generator/ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_diff/ subversion/libsvn_ra_serf/ subversion/libsvn_repos/ subvers...
Date Sun, 27 Jan 2013 00:01:59 GMT
Modified: subversion/branches/windows-build-update/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_client/merge.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_client/merge.c Sun Jan 27 00:01:57 2013
@@ -255,8 +255,11 @@ typedef struct merge_cmd_baton_t {
                                          is TRUE.*/
   svn_boolean_t mergeinfo_capable;    /* Whether the merge source server
                                          is capable of Merge Tracking. */
-  svn_boolean_t ignore_ancestry;      /* Are we ignoring ancestry (and by
-                                         extension, mergeinfo)?  FALSE if
+  svn_boolean_t ignore_mergeinfo;     /* Don't honor mergeinfo; see
+                                         doc string of do_merge().  FALSE if
+                                         MERGE_SOURCE->ancestral is FALSE. */
+  svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see
+                                         doc string of do_merge().  FALSE if
                                          MERGE_SOURCE->ancestral is FALSE. */
   svn_boolean_t reintegrate_merge;    /* Whether this is a --reintegrate
                                          merge or not. */
@@ -286,7 +289,9 @@ typedef struct merge_cmd_baton_t {
   /* The list of any paths which remained in conflict after a
      resolution attempt was made.  We track this in-memory, rather
      than just using WC entry state, since the latter doesn't help us
-     when in dry_run mode. */
+     when in dry_run mode.
+     ### And because we only want to resolve conflicts that were
+         generated by this merge, not pre-existing ones? */
   apr_hash_t *conflicted_paths;
 
   /* A list of absolute paths which had no explicit mergeinfo prior to the
@@ -353,12 +358,12 @@ typedef struct merge_cmd_baton_t {
    changes to merge, for the merge described by MERGE_B.  Specifically, that
    is if the merge source server is capable of merge tracking, the left-side
    merge source is an ancestor of the right-side (or vice-versa), the merge
-   source is in the same repository as the merge target, and ancestry is
-   being considered. */
+   source is in the same repository as the merge target, and we are not
+   ignoring mergeinfo. */
 #define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable      \
                                   && (merge_b)->merge_source.ancestral  \
                                   && (merge_b)->same_repos          \
-                                  && (! (merge_b)->ignore_ancestry))
+                                  && (! (merge_b)->ignore_mergeinfo))
 
 
 /* Return TRUE iff we should be recording mergeinfo for the merge described
@@ -470,6 +475,37 @@ check_same_repos(const svn_client__pathr
   return SVN_NO_ERROR;
 }
 
+/* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
+   containing PATH_HASH. */
+static APR_INLINE void
+store_path(apr_hash_t *path_hash, const char *local_abspath)
+{
+  const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
+                                     local_abspath);
+
+  apr_hash_set(path_hash, dup_path, APR_HASH_KEY_STRING, dup_path);
+}
+
+/* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
+   containing *PATH_HASH_P.  If *PATH_HASH_P is NULL, then first set
+   *PATH_HASH_P to a new hash allocated from POOL.  */
+static APR_INLINE void
+alloc_and_store_path(apr_hash_t **path_hash_p,
+                     const char *local_abspath,
+                     apr_pool_t *pool)
+{
+  if (! *path_hash_p)
+    *path_hash_p = apr_hash_make(pool);
+  store_path(*path_hash_p, local_abspath);
+}
+
+/* Helper function to easy in checking if a path is in a path hash */
+static APR_INLINE svn_boolean_t
+contains_path(apr_hash_t *path_hash, const char *local_abspath)
+{
+  return apr_hash_get(path_hash, local_abspath, APR_HASH_KEY_STRING) != NULL;
+}
+
 /* Return true iff we're in dry-run mode and LOCAL_ABSPATH would have been
    deleted by now if we weren't in dry-run mode.
    Used to avoid spurious notifications (e.g. conflicts) from a merge
@@ -721,12 +757,8 @@ tree_conflict(merge_cmd_baton_t *merge_b
       SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
                                         merge_b->pool));
 
-      if (merge_b->conflicted_paths == NULL)
-        merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
-      victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
-
-      apr_hash_set(merge_b->conflicted_paths, victim_abspath,
-                   APR_HASH_KEY_STRING, victim_abspath);
+      alloc_and_store_path(&merge_b->conflicted_paths, victim_abspath,
+                           merge_b->pool);
     }
 
   return SVN_NO_ERROR;
@@ -765,12 +797,8 @@ tree_conflict_on_add(merge_cmd_baton_t *
       SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
                                         merge_b->pool));
 
-      if (merge_b->conflicted_paths == NULL)
-        merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
-      victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
-
-      apr_hash_set(merge_b->conflicted_paths, victim_abspath,
-                   APR_HASH_KEY_STRING, victim_abspath);
+      alloc_and_store_path(&merge_b->conflicted_paths, victim_abspath,
+                           merge_b->pool);
     }
   else if (existing_conflict->action == svn_wc_conflict_action_delete &&
            conflict->action == svn_wc_conflict_action_add)
@@ -796,12 +824,8 @@ tree_conflict_on_add(merge_cmd_baton_t *
       SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
                                         merge_b->pool));
 
-      if (merge_b->conflicted_paths == NULL)
-        merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
-      victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
-
-      apr_hash_set(merge_b->conflicted_paths, victim_abspath,
-                   APR_HASH_KEY_STRING, victim_abspath);
+      alloc_and_store_path(&merge_b->conflicted_paths, victim_abspath,
+                           merge_b->pool);
     }
 
   /* In any other cases, we don't touch the existing conflict. */
@@ -1315,29 +1339,13 @@ prepare_merge_props_changed(const apr_ar
 
               if (!has_pristine_mergeinfo && prop->value)
                 {
-                  /* If BATON->PATHS_WITH_NEW_MERGEINFO needs to be
-                     allocated do so in BATON->POOL so it has a
-                     sufficient lifetime. */
-                  if (!merge_b->paths_with_new_mergeinfo)
-                    merge_b->paths_with_new_mergeinfo =
-                      apr_hash_make(merge_b->pool);
-
-                  apr_hash_set(merge_b->paths_with_new_mergeinfo,
-                               apr_pstrdup(merge_b->pool, local_abspath),
-                               APR_HASH_KEY_STRING, local_abspath);
+                  alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
+                                       local_abspath, merge_b->pool);
                 }
               else if (has_pristine_mergeinfo && !prop->value)
                 {
-                  /* If BATON->PATHS_WITH_DELETED_MERGEINFO needs to be
-                     allocated do so in BATON->POOL so it has a
-                     sufficient lifetime. */
-                  if (!merge_b->paths_with_deleted_mergeinfo)
-                    merge_b->paths_with_deleted_mergeinfo =
-                      apr_hash_make(merge_b->pool);
-
-                  apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
-                               apr_pstrdup(merge_b->pool, local_abspath),
-                               APR_HASH_KEY_STRING, local_abspath);
+                  alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
+                                       local_abspath, merge_b->pool);
                 }
             }
         }
@@ -1396,6 +1404,213 @@ check_moved_here(svn_boolean_t *moved_he
   return SVN_NO_ERROR;
 }
 
+/* #defined HANDLE_NOTIFY_FROM_MERGE */
+
+/* Record the skip for future processing and (later) produce the
+   skip notification */
+static svn_error_t *
+record_skip(const merge_cmd_baton_t *merge_b,
+            const char *local_abspath,
+            svn_node_kind_t kind,
+            svn_wc_notify_state_t state,
+            apr_pool_t *scratch_pool)
+{
+  if (merge_b->merge_source.ancestral
+      || merge_b->reintegrate_merge)
+    {
+      store_path(merge_b->skipped_abspaths, local_abspath);
+    }
+
+#ifdef HANDLE_NOTIFY_FROM_MERGE
+  if (merge_b->ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip,
+                                    scratch_pool);
+      notify->kind = kind;
+      notify->content_state = notify->prop_state = state;
+
+      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
+                                    scratch_pool);
+    }
+#endif
+  return SVN_NO_ERROR;
+}
+
+/* Record the skip for future processing and (later) produce the
+   tree conflict notification */
+static svn_error_t *
+record_tree_conflict(const merge_cmd_baton_t *merge_b,
+                     const char *local_abspath,
+                     svn_node_kind_t kind,
+                     apr_pool_t *scratch_pool)
+{
+#ifdef HANDLE_NOTIFY_FROM_MERGE
+  /* On a replacement we currently get two tree conflicts */
+  if (merge_b->ctx->notify_func2
+      && !contains_path(merge_b->tree_conflicted_abspaths,
+                        local_abspath))
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
+                                    scratch_pool);
+      notify->kind = kind;
+
+      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
+                                    scratch_pool);
+    }
+#endif
+
+  if (merge_b->merge_source.ancestral
+      || merge_b->reintegrate_merge)
+    {
+      store_path(merge_b->tree_conflicted_abspaths, local_abspath);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Record the add for future processing and (later) produce the
+   update_add notification
+
+   Originally a helper for notification_receiver: Cache the roots of
+   subtrees added under TARGET_ABSPATH.
+ */
+static svn_error_t *
+record_update_add(merge_cmd_baton_t *merge_b,
+                  const char *local_abspath,
+                  svn_node_kind_t kind,
+                  apr_pool_t *scratch_pool)
+{
+  svn_boolean_t root_of_added_subtree = TRUE;
+
+  if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
+    {
+      /* Stash the root path of any added subtrees. */
+      if (merge_b->added_abspaths == NULL)
+        {
+          /* The first added path is always a root. */
+          merge_b->added_abspaths = apr_hash_make(merge_b->pool);
+        }
+      else
+        {
+          const char *added_path_dir = svn_dirent_dirname(local_abspath,
+                                                          scratch_pool);
+
+          /* Is NOTIFY->PATH the root of an added subtree? */
+          while (strcmp(merge_b->target->abspath, added_path_dir))
+            {
+              if (contains_path(merge_b->added_abspaths, added_path_dir))
+                {
+                  root_of_added_subtree = FALSE;
+                  break;
+                }
+
+              if (svn_dirent_is_root(added_path_dir, strlen(added_path_dir)))
+                break;
+              added_path_dir = svn_dirent_dirname(added_path_dir,
+                                                  scratch_pool);
+            }
+        }
+
+      if (root_of_added_subtree)
+        {
+          store_path(merge_b->added_abspaths, local_abspath);
+        }
+
+      store_path(merge_b->merged_abspaths, local_abspath);
+    }
+
+#ifdef HANDLE_NOTIFY_FROM_MERGE
+  if (merge_b->ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_add,
+                                    scratch_pool);
+      notify->kind = kind;
+
+      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
+                                    scratch_pool);
+    }
+#endif
+
+  return SVN_NO_ERROR;
+}
+
+/* Record the update for future processing and (later) produce the
+   update_add notification */
+static svn_error_t *
+record_update_update(const merge_cmd_baton_t *merge_b,
+                     const char *local_abspath,
+                     svn_node_kind_t kind,
+                     svn_wc_notify_state_t content_state,
+                     svn_wc_notify_state_t prop_state,
+                     apr_pool_t *scratch_pool)
+{
+  if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
+    {
+      store_path(merge_b->merged_abspaths, local_abspath);
+    }
+
+#ifdef HANDLE_NOTIFY_FROM_MERGE
+  if (merge_b->ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update,
+                                    scratch_pool);
+      notify->kind = kind;
+      notify->content_state = content_state;
+      notify->prop_state = prop_state;
+
+      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
+                                    scratch_pool);
+    }
+#endif
+
+  return SVN_NO_ERROR;
+}
+
+/* Record the delete for future processing and (later) produce the
+   update_delete notification */
+static svn_error_t *
+record_update_delete(const merge_cmd_baton_t *merge_b,
+                     const char *local_abspath,
+                     svn_node_kind_t kind,
+                     apr_pool_t *scratch_pool)
+{
+  /* Update the lists of merged, skipped, tree-conflicted and added paths. */
+  if (merge_b->merge_source.ancestral
+      || merge_b->reintegrate_merge)
+    {
+      /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
+         are now deleting it, then remove it from the list of added
+         paths. */
+      apr_hash_set(merge_b->added_abspaths, local_abspath,
+                   APR_HASH_KEY_STRING, NULL);
+      store_path(merge_b->merged_abspaths, local_abspath);
+    }
+
+#ifdef HANDLE_NOTIFY_FROM_MERGE
+  if (merge_b->ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_delete,
+                                    scratch_pool);
+      notify->kind = kind;
+
+      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
+                                    scratch_pool);
+    }
+#endif
+
+  return SVN_NO_ERROR;
+}
+
 /* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_dir_props_changed(svn_wc_notify_state_t *state,
@@ -1422,6 +1637,8 @@ merge_dir_props_changed(svn_wc_notify_st
   if (obstr_state != svn_wc_notify_state_inapplicable)
     {
       *state = obstr_state;
+      SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
+                          obstr_state, scratch_pool));
       return SVN_NO_ERROR;
     }
 
@@ -1449,6 +1666,8 @@ merge_dir_props_changed(svn_wc_notify_st
 
       *tree_conflicted = TRUE;
       *state = svn_wc_notify_state_missing;
+      SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir,
+                                   scratch_pool));
 
       return SVN_NO_ERROR;
     }
@@ -1460,6 +1679,74 @@ merge_dir_props_changed(svn_wc_notify_st
       return SVN_NO_ERROR; /* We can't do a real prop merge for added dirs */
     }
 
+  if (dir_was_added && merge_b->same_repos)
+    {
+      /* When the directory was added in merge_dir_added() we didn't update its
+         pristine properties. Instead we receive the property changes later and
+         apply them in this function.
+
+         If we would apply them as changes (such as before fixing issue #3405),
+         we would see the unmodified properties as local changes, and the
+         pristine properties would be out of sync with what the repository
+         expects for this directory.
+
+         Instead of doing that we now simply set the properties as the pristine
+         properties via a private libsvn_wc api.
+      */
+
+      const char *copyfrom_url;
+      svn_revnum_t copyfrom_rev;
+      const char *parent_abspath;
+      const char *child;
+
+      /* Creating a hash containing regular and entry props */
+      apr_hash_t *new_pristine_props = svn_prop__patch(original_props, propchanges,
+                                                       scratch_pool);
+
+      parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
+      child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL);
+      SVN_ERR_ASSERT(child != NULL);
+
+      copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url,
+                                                 child, scratch_pool);
+      copyfrom_rev = merge_b->merge_source.loc2->rev;
+
+      SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
+                                scratch_pool));
+
+      if (!merge_b->dry_run)
+        {
+          SVN_ERR(svn_wc__complete_directory_add(merge_b->ctx->wc_ctx,
+                                                local_abspath,
+                                                new_pristine_props,
+                                                copyfrom_url, copyfrom_rev,
+                                                scratch_pool));
+        }
+      *state = svn_wc_notify_state_changed; /* same as merge_props did */
+
+      /* Until issue #3405 was fixed the code for changed directories was
+         used for directory deletions, which made use apply svn:mergeinfo as if
+         additional changes were applied by the merge, which really weren't.
+
+         ### I wouldn't be surprised if we somehow relied on these for correct
+         ### merges, but in this case the fix added here should also be applied
+         ### for added files! */
+
+      /* ### The old code performed (via prepare_merge_props_changed):
+      if (apr_hash_get(new_pristine_props, SVN_PROP_MERGEINFO,
+                       APR_HASH_KEY_STRING))
+        {
+          alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
+                               local_abspath, merge_b->pool);
+        }
+         ### which is something merge_add_file() doesn't do, but
+         ### merge_tests.py 95: no self referential filtering on added path
+         ### fails if this block is not enabled.
+      */
+
+      return SVN_NO_ERROR;
+    }
+
   SVN_ERR(prepare_merge_props_changed(&props, local_abspath, propchanges,
                                       merge_b, scratch_pool, scratch_pool));
 
@@ -1480,6 +1767,15 @@ merge_dir_props_changed(svn_wc_notify_st
                                   ctx->conflict_func2, ctx->conflict_baton2,
                                   ctx->cancel_func, ctx->cancel_baton,
                                   scratch_pool));
+
+      if (*state == svn_wc_notify_state_conflicted
+          || *state == svn_wc_notify_state_merged
+          || *state == svn_wc_notify_state_changed)
+        {
+          SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
+                                       svn_wc_notify_state_inapplicable,
+                                       *state, scratch_pool));
+        }
     }
   else
     *state = svn_wc_notify_state_unchanged;
@@ -1490,12 +1786,12 @@ merge_dir_props_changed(svn_wc_notify_st
 /* Contains any state collected while resolving conflicts. */
 typedef struct conflict_resolver_baton_t
 {
-  /* The wrapped callback and baton. */
+  /* The wrapped callback and baton. If func is null, postpone conflicts. */
   svn_wc_conflict_resolver_func2_t wrapped_func;
   void *wrapped_baton;
 
   /* The list of any paths which remained in conflict after a
-     resolution attempt was made. */
+     resolution attempt was made. Non-null ptr to possibly-null ptr to hash. */
   apr_hash_t **conflicted_paths;
 
   /* Pool with a sufficient lifetime to be used for output members such as
@@ -1504,8 +1800,13 @@ typedef struct conflict_resolver_baton_t
 } conflict_resolver_baton_t;
 
 /* An implementation of the svn_wc_conflict_resolver_func_t interface.
-   We keep a record of paths which remain in conflict after any
-   resolution attempt from BATON->wrapped_func. */
+
+   This is called by the WC layer when a file merge conflicts.  We call
+   the wrapped conflict callback, BATON->wrapped_func, or, if that is
+   null, we postpone the conflict.
+
+   We keep a record of paths which remain in conflict.
+*/
 static svn_error_t *
 conflict_resolver(svn_wc_conflict_result_t **result,
                   const svn_wc_conflict_description2_t *description,
@@ -1531,33 +1832,15 @@ conflict_resolver(svn_wc_conflict_result
     }
 
   /* Keep a record of paths still in conflict after the resolution attempt. */
-  if ((! conflict_b->wrapped_func)
-      || (*result && ((*result)->choice == svn_wc_conflict_choose_postpone)))
+  if ((*result)->choice == svn_wc_conflict_choose_postpone)
     {
-      const char *conflicted_path = apr_pstrdup(conflict_b->pool,
-                                                description->local_abspath);
-
-      if (*conflict_b->conflicted_paths == NULL)
-        *conflict_b->conflicted_paths = apr_hash_make(conflict_b->pool);
-
-      apr_hash_set(*conflict_b->conflicted_paths, conflicted_path,
-                   APR_HASH_KEY_STRING, conflicted_path);
+      alloc_and_store_path(conflict_b->conflicted_paths,
+                           description->local_abspath, conflict_b->pool);
     }
 
   return svn_error_trace(err);
 }
 
-/* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
-   containing PATH_HASH */
-static APR_INLINE void
-store_path(apr_hash_t *path_hash, const char *local_abspath)
-{
-  const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
-                                     local_abspath);
-
-  apr_hash_set(path_hash, dup_path, APR_HASH_KEY_STRING, dup_path);
-}
-
 
 /* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
@@ -1617,6 +1900,8 @@ merge_file_changed(svn_wc_notify_state_t
         *content_state = obstr_state;
         if (obstr_state == svn_wc_notify_state_missing)
           *prop_state = svn_wc_notify_state_missing;
+        SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
+                            obstr_state, scratch_pool));
         return SVN_NO_ERROR;
       }
   }
@@ -1648,6 +1933,8 @@ merge_file_changed(svn_wc_notify_state_t
             {
               *content_state = svn_wc_notify_state_missing;
               *prop_state = svn_wc_notify_state_missing;
+              SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
+                                  svn_wc_notify_state_missing, scratch_pool));
               return SVN_NO_ERROR;
             }
         }
@@ -1671,6 +1958,8 @@ merge_file_changed(svn_wc_notify_state_t
       *tree_conflicted = TRUE;
       *content_state = svn_wc_notify_state_missing;
       *prop_state = svn_wc_notify_state_missing;
+      SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_file,
+                                   scratch_pool));
       return SVN_NO_ERROR;
     }
 
@@ -1740,7 +2029,7 @@ merge_file_changed(svn_wc_notify_state_t
       SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
                                       local_abspath, FALSE, scratch_pool));
 
-      /* Postpone all conflicts. */
+      /* Wrap the resolver so as to keep a note of all postponed conflicts. */
       conflict_baton.wrapped_func = ctx->conflict_func2;
       conflict_baton.wrapped_baton = ctx->conflict_baton2;
 
@@ -1774,6 +2063,18 @@ merge_file_changed(svn_wc_notify_state_t
         *content_state = svn_wc_notify_state_unchanged;
     }
 
+  if (*content_state == svn_wc_notify_state_conflicted
+      || *content_state == svn_wc_notify_state_merged
+      || *content_state == svn_wc_notify_state_changed
+      || *prop_state == svn_wc_notify_state_conflicted
+      || *prop_state == svn_wc_notify_state_merged
+      || *prop_state == svn_wc_notify_state_changed)
+    {
+      SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
+                                   *content_state, *prop_state,
+                                   scratch_pool));
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -1859,6 +2160,9 @@ merge_file_added(svn_wc_notify_state_t *
       {
         *content_state = obstr_state;
 
+        SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
+                            obstr_state, scratch_pool));
+
         return SVN_NO_ERROR;
       }
 
@@ -1884,6 +2188,10 @@ merge_file_added(svn_wc_notify_state_t *
       else
         /* this will make the repos_editor send a 'skipped' message */
         *content_state = svn_wc_notify_state_obstructed;
+
+      SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_file,
+                                   scratch_pool));
+
       return SVN_NO_ERROR;
     }
 
@@ -1908,6 +2216,8 @@ merge_file_added(svn_wc_notify_state_t *
       if (parent_kind != svn_node_dir)
         {
           *content_state = svn_wc_notify_state_obstructed;
+          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
+                              svn_wc_notify_state_obstructed, scratch_pool));
           return SVN_NO_ERROR;
         }
 
@@ -1976,6 +2286,8 @@ merge_file_added(svn_wc_notify_state_t *
                                        svn_wc_conflict_action_add,
                                        reason));
           *tree_conflicted = TRUE;
+          SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_file,
+                                       scratch_pool));
         }
       else
         {
@@ -2002,6 +2314,9 @@ merge_file_added(svn_wc_notify_state_t *
       store_path(merge_b->dry_run_added, local_abspath); */
     }
 
+  SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
+                            scratch_pool));
+
   *content_state = svn_wc_notify_state_changed;
   *prop_state = svn_wc_notify_state_changed;
 
@@ -2124,17 +2439,18 @@ merge_file_deleted(svn_wc_notify_state_t
     if (obstr_state != svn_wc_notify_state_inapplicable)
       {
         *state = obstr_state;
+        SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
+                            obstr_state, scratch_pool));
+
         return SVN_NO_ERROR;
       }
   }
 
   if (merge_b->dry_run)
     {
-      const char *wcpath = apr_pstrdup(merge_b->pool, local_abspath);
       /* Store deletion *after* obstruction check, or the registration will be
          noted as an obstruction */
-      apr_hash_set(merge_b->dry_run_deletions, wcpath,
-                   APR_HASH_KEY_STRING, wcpath);
+      store_path(merge_b->dry_run_deletions, local_abspath);
     }
 
   if (kind != svn_node_file || is_deleted)
@@ -2161,6 +2477,8 @@ merge_file_deleted(svn_wc_notify_state_t
 
       *tree_conflicted = TRUE;
       *state = svn_wc_notify_state_missing;
+      SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir,
+                                   scratch_pool));
 
       return SVN_NO_ERROR;
     }
@@ -2181,13 +2499,11 @@ merge_file_deleted(svn_wc_notify_state_t
       *state = svn_wc_notify_state_changed;
 
       /* Record that we might have deleted mergeinfo */
-      if (!merge_b->paths_with_deleted_mergeinfo)
-        merge_b->paths_with_deleted_mergeinfo =
-                                          apr_hash_make(merge_b->pool);
-
-      apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
-                   apr_pstrdup(merge_b->pool, local_abspath),
-                   APR_HASH_KEY_STRING, local_abspath);
+      alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
+                           local_abspath, merge_b->pool);
+
+      SVN_ERR(record_update_delete(merge_b, local_abspath, svn_node_file,
+                                   scratch_pool));
     }
   else
     {
@@ -2202,6 +2518,8 @@ merge_file_deleted(svn_wc_notify_state_t
       *tree_conflicted = TRUE;
 
       *state = svn_wc_notify_state_obstructed;
+      SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir,
+                                   scratch_pool));
     }
   
   return SVN_NO_ERROR;
@@ -2288,6 +2606,8 @@ merge_dir_added(svn_wc_notify_state_t *s
     if (obstr_state != svn_wc_notify_state_inapplicable)
       {
         *state = obstr_state;
+        SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
+                            obstr_state, scratch_pool));
         return SVN_NO_ERROR;
       }
 
@@ -2316,6 +2636,9 @@ merge_dir_added(svn_wc_notify_state_t *s
         /* this will make the repos_editor send a 'skipped' message */
         *state = svn_wc_notify_state_obstructed;
 
+      SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_file,
+                                   scratch_pool));
+
       return SVN_NO_ERROR;
     }
 
@@ -2342,6 +2665,9 @@ merge_dir_added(svn_wc_notify_state_t *s
     }
   *state = svn_wc_notify_state_changed;
 
+  SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
+                            scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -2378,6 +2704,8 @@ merge_dir_deleted(svn_wc_notify_state_t 
     if (obstr_state != svn_wc_notify_state_inapplicable)
       {
         *state = obstr_state;
+        SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
+                             obstr_state, scratch_pool));
         return SVN_NO_ERROR;
       }
 
@@ -2412,6 +2740,8 @@ merge_dir_deleted(svn_wc_notify_state_t 
 
       *tree_conflicted = TRUE;
       *state = svn_wc_notify_state_missing;
+      SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_file,
+                                   scratch_pool));
 
       return SVN_NO_ERROR;
     }
@@ -2441,18 +2771,20 @@ merge_dir_deleted(svn_wc_notify_state_t 
                             svn_wc_conflict_reason_edited));
       *tree_conflicted = TRUE;
       *state = svn_wc_notify_state_conflicted;
+      SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir,
+                                   scratch_pool));
     }
   else
     {
       *state = svn_wc_notify_state_changed;
-    }
 
-  /* Record that we might have deleted mergeinfo */
-  if (!merge_b->paths_with_deleted_mergeinfo)
-    merge_b->paths_with_deleted_mergeinfo =
-                                      apr_hash_make(merge_b->pool);
+      /* Record that we might have deleted mergeinfo */
+      alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
+                           local_abspath, merge_b->pool);
 
-  store_path(merge_b->paths_with_deleted_mergeinfo, local_abspath);
+      SVN_ERR(record_update_delete(merge_b, local_abspath, svn_node_dir,
+                                   scratch_pool));
+    }
 
   return SVN_NO_ERROR;
 }
@@ -2554,6 +2886,8 @@ merge_dir_opened(svn_boolean_t *tree_con
                                 svn_wc_conflict_reason_replaced));
           *tree_conflicted = TRUE;
           *skip_children = TRUE;
+          SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir,
+                                       scratch_pool));
         }
 
       /* If we're trying to open a directory that's locally deleted,
@@ -2584,7 +2918,10 @@ merge_dir_opened(svn_boolean_t *tree_con
           SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
                                 svn_wc_conflict_action_edit, reason));
           *tree_conflicted = TRUE;
+          *skip = TRUE;
           *skip_children = TRUE;
+          SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir,
+                                       scratch_pool));
         }
     }
 
@@ -2769,64 +3106,6 @@ notify_merge_completed(const char *targe
     }
 }
 
-/* Helper for notification_receiver: Cache the roots of subtrees added under
-   TARGET_ABSPATH.
-
-   If *ADDED_ABSPATHS is not null, then it is a hash of (const char *)
-   absolute WC paths mapped to the same.  If it is null, then allocate a
-   new hash in RESULT_POOL.
-
-   If ADDED_ABSPATH is a subtree of TARGET_ABSPATH, is not already found in
-   *ADDED_ABSPATHS, nor is a subtree of any path already found within the
-   hash, then add a copy of ADDED_ABSPATH to *ADDED_ABSPATHS.
-
-   All additions to *ADDED_ABSPATHS are allocated in RESULT_POOL.
-   SCRATCH_POOL is used for temporary allocations. */
-static void
-update_the_list_of_added_subtrees(const char *target_abspath,
-                                  const char *added_abspath,
-                                  apr_hash_t **added_abspaths,
-                                  apr_pool_t *result_pool,
-                                  apr_pool_t *scratch_pool)
-{
-  svn_boolean_t root_of_added_subtree = TRUE;
-
-  /* Stash the root path of any added subtrees. */
-  if (*added_abspaths == NULL)
-    {
-      /* The first added path is always a root. */
-      *added_abspaths = apr_hash_make(result_pool);
-    }
-  else
-    {
-      apr_pool_t *subpool = svn_pool_create(scratch_pool);
-      const char *added_path_parent =
-        svn_dirent_dirname(added_abspath, subpool);
-
-      /* Is NOTIFY->PATH the root of an added subtree? */
-      while (strcmp(target_abspath, added_path_parent))
-        {
-          if (apr_hash_get(*added_abspaths,
-                           added_path_parent,
-                           APR_HASH_KEY_STRING))
-            {
-              root_of_added_subtree = FALSE;
-              break;
-            }
-
-          added_path_parent = svn_dirent_dirname(
-            added_path_parent, subpool);
-        }
-
-      svn_pool_destroy(subpool);
-    }
-
-  if (root_of_added_subtree)
-    {
-      store_path(*added_abspaths, added_abspath);
-    }
-}
-
 /* Is the notification the result of a real operative merge? */
 #define IS_OPERATIVE_NOTIFICATION(notify)  \
                     (notify->content_state == svn_wc_notify_state_conflicted \
@@ -2881,74 +3160,6 @@ notification_receiver(void *baton, const
    * retrieve that info here, instead of querying the WC again here. */
   notify_abspath = svn_dirent_join(merge_b->target->abspath,
                                    notify->path, pool);
-  if (notify->action == svn_wc_notify_update_update
-      && notify->kind == svn_node_file)
-    {
-      svn_error_t *err;
-      const char *moved_to_abspath;
-
-      err = svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
-                                        merge_b->ctx->wc_ctx,
-                                        notify_abspath, pool, pool);
-      if (err)
-        {
-          if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-            {
-              svn_error_clear(err);
-              moved_to_abspath = NULL;
-            }
-          else
-            {
-              /* ### return svn_error_trace(err); */
-              svn_error_clear(err);
-              return;
-            }
-        }
-      if (moved_to_abspath)
-        notify_abspath = moved_to_abspath;
-    }
-
-  /* Update the lists of merged, skipped, tree-conflicted and added paths. */
-  if (merge_b->merge_source.ancestral
-      || merge_b->reintegrate_merge)
-    {
-      if (notify->content_state == svn_wc_notify_state_merged
-          || notify->content_state == svn_wc_notify_state_changed
-          || notify->prop_state == svn_wc_notify_state_merged
-          || notify->prop_state == svn_wc_notify_state_changed
-          || notify->action == svn_wc_notify_update_add)
-        {
-          store_path(merge_b->merged_abspaths, notify_abspath);
-        }
-
-      if (notify->action == svn_wc_notify_skip)
-        {
-          store_path(merge_b->skipped_abspaths, notify_abspath);
-        }
-
-      if (notify->action == svn_wc_notify_tree_conflict)
-        {
-          store_path(merge_b->tree_conflicted_abspaths, notify_abspath);
-        }
-
-      if (notify->action == svn_wc_notify_update_add)
-        {
-          update_the_list_of_added_subtrees(merge_b->target->abspath,
-                                            notify_abspath,
-                                            &(merge_b->added_abspaths),
-                                            notify_b->pool, pool);
-        }
-
-      if (notify->action == svn_wc_notify_update_delete
-          && merge_b->added_abspaths)
-        {
-          /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
-             are now deleting it, then remove it from the list of added
-             paths. */
-          apr_hash_set(merge_b->added_abspaths, notify_abspath,
-                       APR_HASH_KEY_STRING, NULL);
-        }
-    }
 
   /* Notify that a merge is beginning, if we haven't already done so.
    * (A single-file merge is notified separately: see single_file_merge_notify().) */
@@ -4669,11 +4880,11 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
    otherwise.  RANGELIST is the set of revisions being merged from
    MERGEINFO_PATH to MERGE_B->target. */
 static svn_error_t *
-record_skips(const char *mergeinfo_path,
-             const svn_rangelist_t *rangelist,
-             svn_boolean_t is_rollback,
-             merge_cmd_baton_t *merge_b,
-             apr_pool_t *scratch_pool)
+record_skips_in_mergeinfo(const char *mergeinfo_path,
+                          const svn_rangelist_t *rangelist,
+                          svn_boolean_t is_rollback,
+                          merge_cmd_baton_t *merge_b,
+                          apr_pool_t *scratch_pool)
 {
   apr_hash_index_t *hi;
   apr_hash_t *merges;
@@ -4893,6 +5104,7 @@ drive_merge_report_editor(const char *ta
   svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
   const char *old_sess1_url, *old_sess2_url;
   svn_boolean_t is_rollback = source->loc1->rev > source->loc2->rev;
+  apr_hash_t *absent_relpaths = apr_hash_make(scratch_pool);
 
   /* Start with a safe default starting revision for the editor and the
      merge target. */
@@ -4964,6 +5176,7 @@ drive_merge_report_editor(const char *ta
                                       merge_b->ra_session2, source->loc1->rev,
                                       FALSE /* walk_deleted_dirs */,
                                       TRUE /* text_deltas */,
+                                      absent_relpaths,
                                       &merge_callbacks, merge_b,
                                       merge_b->ctx->cancel_func,
                                       merge_b->ctx->cancel_baton,
@@ -4971,7 +5184,7 @@ drive_merge_report_editor(const char *ta
                                       scratch_pool));
   SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
                           &reporter, &report_baton, source->loc2->rev,
-                          "", depth, merge_b->ignore_ancestry,
+                          "", depth, merge_b->diff_ignore_ancestry,
                           TRUE,  /* text_deltas */
                           source->loc2->url, diff_editor, diff_edit_baton,
                           scratch_pool));
@@ -5085,6 +5298,24 @@ drive_merge_report_editor(const char *ta
   /* Caller must call svn_sleep_for_timestamps() */
   *(merge_b->use_sleep) = TRUE;
 
+  if (apr_hash_count(absent_relpaths))
+    {
+      apr_hash_index_t *hi;
+
+      for (hi = apr_hash_first(scratch_pool, absent_relpaths);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *absent_abspath;
+
+          absent_abspath = svn_dirent_join(target_abspath,
+                                           svn__apr_hash_index_key(hi),
+                                           scratch_pool);
+
+          record_skip(merge_b, absent_abspath, svn_node_unknown,
+                      svn_wc_notify_state_missing, scratch_pool);
+        }
+    }
   return SVN_NO_ERROR;
 }
 
@@ -5264,7 +5495,7 @@ single_file_merge_get_file(const char **
 
    If *HEADER_SENT is not set, then send a header notification for range R
    before sending the state notification, and set *HEADER_SENT to TRUE. */
-static APR_INLINE void
+static svn_error_t *
 single_file_merge_notify(notification_receiver_baton_t *notify_baton,
                          const char *target_relpath,
                          svn_wc_notify_action_t action,
@@ -5272,24 +5503,70 @@ single_file_merge_notify(notification_re
                          svn_wc_notify_state_t prop_state,
                          const svn_merge_range_t *r,
                          svn_boolean_t *header_sent,
-                         apr_pool_t *pool)
+                         apr_pool_t *scratch_pool)
 {
-  svn_wc_notify_t *notify = svn_wc_create_notify(target_relpath, action, pool);
+  svn_wc_notify_t *notify = svn_wc_create_notify(target_relpath, action,
+                                                 scratch_pool);
   notify->kind = svn_node_file;
   notify->content_state = text_state;
   notify->prop_state = prop_state;
   if (notify->content_state == svn_wc_notify_state_missing)
-    notify->action = svn_wc_notify_skip;
+    {
+      notify->action = svn_wc_notify_skip;
+      SVN_ERR(record_skip(notify_baton->merge_b,
+                          svn_dirent_join(
+                                notify_baton->merge_b->target->abspath,
+                                target_relpath, scratch_pool),
+                          svn_node_file, text_state,
+                          scratch_pool));
+    }
+
+  if (action == svn_wc_notify_update_add)
+    {
+      SVN_ERR(record_update_add(notify_baton->merge_b,
+                                svn_dirent_join(
+                                        notify_baton->merge_b->target->abspath,
+                                        target_relpath, scratch_pool),
+                                svn_node_file, scratch_pool));
+    }
+  else if (action == svn_wc_notify_update_update)
+    {
+      if (text_state == svn_wc_notify_state_conflicted
+          || text_state == svn_wc_notify_state_merged
+          || text_state == svn_wc_notify_state_changed
+          || prop_state == svn_wc_notify_state_conflicted
+          || prop_state == svn_wc_notify_state_merged
+          || prop_state == svn_wc_notify_state_changed)
+        {
+          SVN_ERR(record_update_update(notify_baton->merge_b,
+                                       svn_dirent_join(
+                                        notify_baton->merge_b->target->abspath,
+                                        target_relpath, scratch_pool),
+                                       svn_node_file,
+                                       text_state, prop_state,
+                                       scratch_pool));
+        }
+    }
+  else if (action == svn_wc_notify_update_delete)
+    {
+      SVN_ERR(record_update_delete(notify_baton->merge_b,
+                                   svn_dirent_join(
+                                        notify_baton->merge_b->target->abspath,
+                                        target_relpath, scratch_pool),
+                                   svn_node_file, scratch_pool));
+    }
 
   if (IS_OPERATIVE_NOTIFICATION(notify) && (! *header_sent))
     {
       notify_merge_begin(notify_baton->merge_b->target->abspath,
                          (notify_baton->merge_b->merge_source.ancestral ? r : NULL),
                          notify_baton->merge_b->same_repos,
-                         notify_baton->merge_b->ctx, pool);
+                         notify_baton->merge_b->ctx, scratch_pool);
       *header_sent = TRUE;
     }
-  notification_receiver(notify_baton, notify, pool);
+  notification_receiver(notify_baton, notify, scratch_pool);
+
+  return SVN_NO_ERROR;
 }
 
 /* Compare two svn_client__merge_path_t elements **A and **B, given the
@@ -5499,24 +5776,20 @@ pre_merge_status_cb(void *baton,
         dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
 
       apr_hash_set(pmsb->switched_subtrees,
-                   apr_pstrdup(pmsb->pool, local_abspath),
-                   APR_HASH_KEY_STRING,
-                   dup_abspath);
+                   dup_abspath, APR_HASH_KEY_STRING, dup_abspath);
     }
 
   if (status->depth == svn_depth_empty
       || status->depth == svn_depth_files)
     {
-      svn_depth_t *depth = apr_pcalloc(pmsb->pool, sizeof *depth);
+      svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth,
+                                       sizeof *depth);
 
       if (!dup_abspath)
         dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
 
-      *depth = status->depth;
       apr_hash_set(pmsb->shallow_subtrees,
-                   dup_abspath,
-                   APR_HASH_KEY_STRING,
-                   depth);
+                   dup_abspath, APR_HASH_KEY_STRING, depth);
     }
 
   if (status->node_status == svn_wc_status_missing)
@@ -5542,8 +5815,8 @@ pre_merge_status_cb(void *baton,
         }
 
       if (new_missing_root)
-        apr_hash_set(pmsb->missing_subtrees, dup_abspath,
-                     APR_HASH_KEY_STRING, dup_abspath);
+        apr_hash_set(pmsb->missing_subtrees,
+                     dup_abspath, APR_HASH_KEY_STRING, dup_abspath);
     }
 
   return SVN_NO_ERROR;
@@ -6866,12 +7139,9 @@ do_file_merge(svn_mergeinfo_catalog_t re
           /* Deduce property diffs. */
           SVN_ERR(svn_prop_diffs(&propchanges, props2, props1, iterpool));
 
-          /* If we aren't ignoring ancestry, then we've already done
-             ancestry relatedness checks.  If we are ignoring ancestry, or
-             our sources are known to be related, then we can do
-             text-n-props merge; otherwise, we have to do a delete-n-add
-             merge.  */
-          if (! (merge_b->ignore_ancestry || sources_related))
+          /* If the sources are related or we're ignoring ancestry in diffs,
+             do a text-n-props merge; otherwise, do a delete-n-add merge. */
+          if (! (merge_b->diff_ignore_ancestry || sources_related))
             {
               /* Delete... */
               SVN_ERR(merge_file_deleted(&text_state,
@@ -6881,12 +7151,13 @@ do_file_merge(svn_mergeinfo_catalog_t re
                                          mimetype1, mimetype2,
                                          props1,
                                          merge_b, iterpool));
-              single_file_merge_notify(notify_b, target_relpath,
-                                       tree_conflicted
-                                         ? svn_wc_notify_tree_conflict
-                                         : svn_wc_notify_update_delete,
-                                       text_state, svn_wc_notify_state_unknown,
-                                       r, &header_sent, iterpool);
+              SVN_ERR(single_file_merge_notify(notify_b, target_relpath,
+                                               tree_conflicted
+                                                 ? svn_wc_notify_tree_conflict
+                                                 : svn_wc_notify_update_delete,
+                                               text_state,
+                                               svn_wc_notify_state_unknown,
+                                               r, &header_sent, iterpool));
 
               /* ...plus add... */
               SVN_ERR(merge_file_added(&text_state, &prop_state,
@@ -6898,12 +7169,12 @@ do_file_merge(svn_mergeinfo_catalog_t re
                                        NULL, SVN_INVALID_REVNUM,
                                        propchanges, props1,
                                        merge_b, iterpool));
-              single_file_merge_notify(notify_b, target_relpath,
-                                       tree_conflicted
-                                         ? svn_wc_notify_tree_conflict
-                                         : svn_wc_notify_update_add,
-                                       text_state, prop_state,
-                                       r, &header_sent, iterpool);
+              SVN_ERR(single_file_merge_notify(notify_b, target_relpath,
+                                               tree_conflicted
+                                                 ? svn_wc_notify_tree_conflict
+                                                 : svn_wc_notify_update_add,
+                                               text_state, prop_state,
+                                               r, &header_sent, iterpool));
               /* ... equals replace. */
             }
           else
@@ -6916,12 +7187,12 @@ do_file_merge(svn_mergeinfo_catalog_t re
                                          mimetype1, mimetype2,
                                          propchanges, props1,
                                          merge_b, iterpool));
-              single_file_merge_notify(notify_b, target_relpath,
-                                       tree_conflicted
-                                         ? svn_wc_notify_tree_conflict
-                                         : svn_wc_notify_update_update,
-                                       text_state, prop_state,
-                                       r, &header_sent, iterpool);
+              SVN_ERR(single_file_merge_notify(notify_b, target_relpath,
+                                               tree_conflicted
+                                                 ? svn_wc_notify_tree_conflict
+                                                 : svn_wc_notify_update_update,
+                                               text_state, prop_state,
+                                               r, &header_sent, iterpool));
             }
 
           /* Remove the temporary files. Ignore if not found: they may
@@ -6956,7 +7227,10 @@ do_file_merge(svn_mergeinfo_catalog_t re
         &filtered_rangelist,
         mergeinfo_path,
         merge_target->implicit_mergeinfo,
-        &range, iterpool));
+        /* Only record partial mergeinfo if only a partial merge was
+           performed before a conflict was encountered. */
+        conflicted_range ? conflicted_range : &range,
+        iterpool));
 
       /* Only record mergeinfo if there is something other than
          self-referential mergeinfo, but don't record mergeinfo if
@@ -7154,15 +7428,18 @@ path_is_subtree(const char *local_abspat
   return FALSE;
 }
 
-/* Return true if any path in NOTIFY_B->MERGED_PATHS, NOTIFY_B->SKIPPED_PATHS,
-   NOTIFY_B->ADDED_PATHS, or NOTIFY_B->CONFLICTED_PATHS is equal to, or is a
-   subtree of LOCAL_ABSPATH.  Return false otherwise. */
+/* Return true if any merged, skipped, added or tree-conflicted path
+   recorded in MERGE_B is equal to, or is a subtree of LOCAL_ABSPATH.  Return
+   false otherwise.
+
+   ### Why not text- or prop-conflicted paths? Are such paths guaranteed
+       to be recorded as 'merged' or 'skipped' or 'added', perhaps?
+*/
 static svn_boolean_t
 subtree_touched_by_merge(const char *local_abspath,
-                         notification_receiver_baton_t *notify_b,
+                         merge_cmd_baton_t *merge_b,
                          apr_pool_t *pool)
 {
-  merge_cmd_baton_t *merge_b = notify_b->merge_b;
   return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool)
           || path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool)
           || path_is_subtree(local_abspath, merge_b->added_abspaths, pool)
@@ -7406,7 +7683,7 @@ get_operative_immediate_children(apr_has
    respectively.
 
    If OPERATIVE_MERGE is true, then the merge being described is operative
-   as per subtree_touched_by_merge.  OPERATIVE_MERGE is false otherwise.
+   as per subtree_touched_by_merge().  OPERATIVE_MERGE is false otherwise.
 
    MERGED_RANGE, MERGEINFO_FSPATH, DEPTH, NOTIFY_B, and MERGE_B are all
    cascaded from record_mergeinfo_for_dir_merge's arguments of the same
@@ -7490,7 +7767,7 @@ flag_subtrees_needing_mergeinfo(svn_bool
                                                  merge_b->ctx->wc_ctx,
                                                  child->abspath, iterpool));
           if (!child_is_deleted
-              && subtree_touched_by_merge(child->abspath, notify_b,
+              && subtree_touched_by_merge(child->abspath, notify_b->merge_b,
                                           iterpool))
             {
               /* This subtree was affected by the merge. */
@@ -7672,7 +7949,7 @@ record_mergeinfo_for_dir_merge(svn_merge
   /* Regardless of what subtrees in MERGE_B->target->abspath might be missing
      could this merge have been operative? */
   operative_merge = subtree_touched_by_merge(merge_b->target->abspath,
-                                             notify_b, iterpool);
+                                             notify_b->merge_b, iterpool);
 
   /* If this couldn't be an operative merge then don't bother with
      the added complexity (and user confusion) of non-inheritable ranges.
@@ -7730,8 +8007,9 @@ record_mergeinfo_for_dir_merge(svn_merge
              before we do, set override mergeinfo on skipped paths so they
              don't incorrectly inherit the mergeinfo we are about to set. */
           if (i == 0)
-            SVN_ERR(record_skips(mergeinfo_fspath, child_merge_rangelist,
-                                 is_rollback, merge_b, iterpool));
+            SVN_ERR(record_skips_in_mergeinfo(mergeinfo_fspath,
+                                              child_merge_rangelist,
+                                              is_rollback, merge_b, iterpool));
 
           /* We may need to record non-inheritable mergeinfo that applies
              only to CHILD->ABSPATH. */
@@ -8448,7 +8726,7 @@ remove_noop_subtree_ranges(const merge_s
    paths and the values are the new mergeinfos for each.  Allocate additions
    to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
 
-   Handle DEPTH as documented for svn_client_merge4().
+   Handle DEPTH as documented for svn_client_merge5().
 
    If ABORT_ON_CONFLICTS is TRUE raise an SVN_ERR_WC_FOUND_CONFLICT error
    if any merge conflicts occur.
@@ -8870,9 +9148,18 @@ ensure_ra_session_url(svn_ra_session_t *
    paths and the values are the new mergeinfos for each.  Allocate additions
    to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
 
-   FORCE_DELETE, DRY_RUN, RECORD_ONLY, IGNORE_ANCESTRY, DEPTH, MERGE_OPTIONS,
+   FORCE_DELETE, DRY_RUN, RECORD_ONLY, DEPTH, MERGE_OPTIONS,
    and CTX are as described in the docstring for svn_client_merge_peg3().
 
+   If IGNORE_MERGEINFO is true, disable merge tracking, by treating the two
+   sources as unrelated even if they actually have a common ancestor.  See
+   the macro HONOR_MERGEINFO().
+
+   If DIFF_IGNORE_ANCESTRY is true, diff the 'left' and 'right' versions
+   of a node (if they are the same kind) as if they were related, even if
+   they are not related.  Otherwise, diff unrelated items as a deletion
+   of one thing and the addition of another.
+
    If not NULL, RECORD_ONLY_PATHS is a hash of (const char *) paths mapped
    to the same.  If RECORD_ONLY is true and RECORD_ONLY_PATHS is not NULL,
    then record mergeinfo describing the merge only on subtrees which contain
@@ -8895,7 +9182,8 @@ do_merge(apr_hash_t **modified_subtrees,
          svn_ra_session_t *src_session,
          svn_boolean_t sources_related,
          svn_boolean_t same_repos,
-         svn_boolean_t ignore_ancestry,
+         svn_boolean_t ignore_mergeinfo,
+         svn_boolean_t diff_ignore_ancestry,
          svn_boolean_t force_delete,
          svn_boolean_t dry_run,
          svn_boolean_t record_only,
@@ -8978,7 +9266,8 @@ do_merge(apr_hash_t **modified_subtrees,
   merge_cmd_baton.force_delete = force_delete;
   merge_cmd_baton.dry_run = dry_run;
   merge_cmd_baton.record_only = record_only;
-  merge_cmd_baton.ignore_ancestry = ignore_ancestry;
+  merge_cmd_baton.ignore_mergeinfo = ignore_mergeinfo;
+  merge_cmd_baton.diff_ignore_ancestry = diff_ignore_ancestry;
   merge_cmd_baton.same_repos = same_repos;
   merge_cmd_baton.mergeinfo_capable = FALSE;
   merge_cmd_baton.ctx = ctx;
@@ -9019,6 +9308,7 @@ do_merge(apr_hash_t **modified_subtrees,
 
   for (i = 0; i < merge_sources->nelts; i++)
     {
+      svn_node_kind_t src1_kind;
       merge_source_t *source =
         APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
 
@@ -9061,8 +9351,11 @@ do_merge(apr_hash_t **modified_subtrees,
           checked_mergeinfo_capability = TRUE;
         }
 
-      /* Call our merge helpers based on TARGET's kind. */
-      if (target->kind == svn_node_file)
+      SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev,
+                                &src1_kind, iterpool));
+
+      /* Call our merge helpers based on SRC1's kind. */
+      if (src1_kind != svn_node_dir)
         {
           SVN_ERR(do_file_merge(result_catalog,
                                 source, target->abspath,
@@ -9071,7 +9364,7 @@ do_merge(apr_hash_t **modified_subtrees,
                                 &notify_baton,
                                 &merge_cmd_baton, iterpool));
         }
-      else if (target->kind == svn_node_dir)
+      else /* Directory */
         {
           /* If conflicts occur while merging any but the very last
            * revision range we want an error to be raised that aborts
@@ -9129,9 +9422,13 @@ do_merge(apr_hash_t **modified_subtrees,
    The diff to be merged is between SOURCE->loc1 (in URL1_RA_SESSION1)
    and SOURCE->loc2 (in URL2_RA_SESSION2); YCA is their youngest
    common ancestor.
+
    SAME_REPOS must be true if and only if the source URLs are in the same
-   repository as the target working copy.  Other arguments are as in
-   all of the public merge APIs.
+   repository as the target working copy.
+
+   DIFF_IGNORE_ANCESTRY is as in do_merge().
+
+   Other arguments are as in all of the public merge APIs.
 
    *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
    integrity, *USE_SLEEP will be unchanged if no sleep is required.
@@ -9146,7 +9443,7 @@ merge_cousins_and_supplement_mergeinfo(c
                                        const svn_client__pathrev_t *yca,
                                        svn_boolean_t same_repos,
                                        svn_depth_t depth,
-                                       svn_boolean_t ignore_ancestry,
+                                       svn_boolean_t diff_ignore_ancestry,
                                        svn_boolean_t force_delete,
                                        svn_boolean_t record_only,
                                        svn_boolean_t dry_run,
@@ -9192,7 +9489,8 @@ merge_cousins_and_supplement_mergeinfo(c
       APR_ARRAY_PUSH(faux_sources, const merge_source_t *) = source;
       SVN_ERR(do_merge(&modified_subtrees, NULL, faux_sources, target,
                        URL1_ra_session, TRUE, same_repos,
-                       ignore_ancestry, force_delete, dry_run, FALSE, NULL, TRUE,
+                       FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
+                       force_delete, dry_run, FALSE, NULL, TRUE,
                        FALSE, depth, merge_options, use_sleep, ctx,
                        scratch_pool, subpool));
     }
@@ -9224,14 +9522,16 @@ merge_cousins_and_supplement_mergeinfo(c
       svn_pool_clear(subpool);
       SVN_ERR(do_merge(NULL, add_result_catalog, add_sources, target,
                        URL1_ra_session, TRUE, same_repos,
-                       ignore_ancestry, force_delete, dry_run, TRUE,
+                       FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
+                       force_delete, dry_run, TRUE,
                        modified_subtrees, TRUE,
                        TRUE, depth, merge_options, use_sleep, ctx,
                        scratch_pool, subpool));
       svn_pool_clear(subpool);
       SVN_ERR(do_merge(NULL, remove_result_catalog, remove_sources, target,
                        URL1_ra_session, TRUE, same_repos,
-                       ignore_ancestry, force_delete, dry_run, TRUE,
+                       FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
+                       force_delete, dry_run, TRUE,
                        modified_subtrees, TRUE,
                        TRUE, depth, merge_options, use_sleep, ctx,
                        scratch_pool, subpool));
@@ -9428,7 +9728,14 @@ open_target_wc(merge_target_t **target_p
 
 /*** Public APIs ***/
 
-/* The body of svn_client_merge4(), which see for details. */
+/* The body of svn_client_merge5(), which see for details.
+ *
+ * If SOURCE1 @ REVISION1 is related to SOURCE2 @ REVISION2 then use merge
+ * tracking (subject to other constraints -- see HONOR_MERGEINFO());
+ * otherwise disable merge tracking.
+ *
+ * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
+ */
 static svn_error_t *
 merge_locked(const char *source1,
              const svn_opt_revision_t *revision1,
@@ -9436,7 +9743,8 @@ merge_locked(const char *source1,
              const svn_opt_revision_t *revision2,
              const char *target_abspath,
              svn_depth_t depth,
-             svn_boolean_t ignore_ancestry,
+             svn_boolean_t ignore_mergeinfo,
+             svn_boolean_t diff_ignore_ancestry,
              svn_boolean_t force_delete,
              svn_boolean_t record_only,
              svn_boolean_t dry_run,
@@ -9488,7 +9796,7 @@ merge_locked(const char *source1,
   same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */);
 
   /* Unless we're ignoring ancestry, see if the two sources are related.  */
-  if (! ignore_ancestry)
+  if (! ignore_mergeinfo)
     SVN_ERR(svn_client__get_youngest_common_ancestor(
                     &yca, source1_loc, source2_loc, ra_session1, ctx,
                     scratch_pool, scratch_pool));
@@ -9554,7 +9862,7 @@ merge_locked(const char *source1,
                                                        yca,
                                                        same_repos,
                                                        depth,
-                                                       ignore_ancestry,
+                                                       diff_ignore_ancestry,
                                                        force_delete,
                                                        record_only, dry_run,
                                                        merge_options,
@@ -9591,7 +9899,7 @@ merge_locked(const char *source1,
 
   err = do_merge(NULL, NULL, merge_sources, target,
                  ra_session1, sources_related, same_repos,
-                 ignore_ancestry, force_delete, dry_run,
+                 ignore_mergeinfo, diff_ignore_ancestry, force_delete, dry_run,
                  record_only, NULL, FALSE, FALSE, depth, merge_options,
                  &use_sleep, ctx, scratch_pool, scratch_pool);
 
@@ -9630,13 +9938,14 @@ get_target_and_lock_abspath(const char *
 }
 
 svn_error_t *
-svn_client_merge4(const char *source1,
+svn_client_merge5(const char *source1,
                   const svn_opt_revision_t *revision1,
                   const char *source2,
                   const svn_opt_revision_t *revision2,
                   const char *target_wcpath,
                   svn_depth_t depth,
-                  svn_boolean_t ignore_ancestry,
+                  svn_boolean_t ignore_mergeinfo,
+                  svn_boolean_t diff_ignore_ancestry,
                   svn_boolean_t force_delete,
                   svn_boolean_t record_only,
                   svn_boolean_t dry_run,
@@ -9669,13 +9978,15 @@ svn_client_merge4(const char *source1,
   if (!dry_run)
     SVN_WC__CALL_WITH_WRITE_LOCK(
       merge_locked(source1, revision1, source2, revision2,
-                   target_abspath, depth, ignore_ancestry,
+                   target_abspath, depth, ignore_mergeinfo,
+                   diff_ignore_ancestry,
                    force_delete, record_only, dry_run,
                    allow_mixed_rev, merge_options, ctx, pool),
       ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
   else
     SVN_ERR(merge_locked(source1, revision1, source2, revision2,
-                   target_abspath, depth, ignore_ancestry,
+                   target_abspath, depth, ignore_mergeinfo,
+                   diff_ignore_ancestry,
                    force_delete, record_only, dry_run,
                    allow_mixed_rev, merge_options, ctx, pool));
 
@@ -10702,59 +11013,12 @@ open_reintegrate_source_and_target(svn_r
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_client_find_reintegrate_merge(const char **url1_p,
-                                  svn_revnum_t *rev1_p,
-                                  const char **url2_p,
-                                  svn_revnum_t *rev2_p,
-                                  const char *source_path_or_url,
-                                  const svn_opt_revision_t *source_peg_revision,
-                                  const char *target_wcpath,
-                                  svn_client_ctx_t *ctx,
-                                  apr_pool_t *result_pool,
-                                  apr_pool_t *scratch_pool)
-{
-  const char *target_abspath;
-  svn_ra_session_t *source_ra_session;
-  svn_client__pathrev_t *source_loc;
-  svn_ra_session_t *target_ra_session;
-  merge_target_t *target;
-  merge_source_t *source;
-
-  SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath,
-                                  scratch_pool));
-
-  SVN_ERR(open_reintegrate_source_and_target(
-            &source_ra_session, &source_loc, &target_ra_session, &target,
-            source_path_or_url, source_peg_revision, target_abspath,
-            ctx, scratch_pool, scratch_pool));
-
-  SVN_ERR(find_reintegrate_merge(&source, NULL,
-                                 source_ra_session, source_loc,
-                                 target_ra_session, target,
-                                 ctx, result_pool, scratch_pool));
-  if (source)
-    {
-      *url1_p = source->loc1->url;
-      *rev1_p = source->loc1->rev;
-      *url2_p = source->loc2->url;
-      *rev2_p = source->loc2->rev;
-    }
-  else
-    {
-      *url1_p = NULL;
-      *rev1_p = SVN_INVALID_REVNUM;
-      *url2_p = NULL;
-      *rev2_p = SVN_INVALID_REVNUM;
-    }
-  return SVN_NO_ERROR;
-}
-
 /* The body of svn_client_merge_reintegrate(), which see for details. */
 static svn_error_t *
 merge_reintegrate_locked(const char *source_path_or_url,
                          const svn_opt_revision_t *source_peg_revision,
                          const char *target_abspath,
+                         svn_boolean_t diff_ignore_ancestry,
                          svn_boolean_t dry_run,
                          const apr_array_header_t *merge_options,
                          svn_client_ctx_t *ctx,
@@ -10796,7 +11060,7 @@ merge_reintegrate_locked(const char *sou
                                                source, yc_ancestor,
                                                TRUE /* same_repos */,
                                                svn_depth_infinity,
-                                               FALSE /* ignore_ancestry */,
+                                               diff_ignore_ancestry,
                                                FALSE /* force_delete */,
                                                FALSE /* record_only */,
                                                dry_run,
@@ -10830,24 +11094,31 @@ svn_client_merge_reintegrate(const char 
     SVN_WC__CALL_WITH_WRITE_LOCK(
       merge_reintegrate_locked(source_path_or_url, source_peg_revision,
                                target_abspath,
+                               FALSE /*diff_ignore_ancestry*/,
                                dry_run, merge_options, ctx, pool),
       ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
   else
     SVN_ERR(merge_reintegrate_locked(source_path_or_url, source_peg_revision,
                                      target_abspath,
+                                     FALSE /*diff_ignore_ancestry*/,
                                      dry_run, merge_options, ctx, pool));
 
   return SVN_NO_ERROR;
 }
 
 
+/* The body of svn_client_merge_peg5(), which see for details.
+ *
+ * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
+ */
 static svn_error_t *
 merge_peg_locked(const char *source_path_or_url,
                  const svn_opt_revision_t *source_peg_revision,
                  const svn_rangelist_t *ranges_to_merge,
                  const char *target_abspath,
                  svn_depth_t depth,
-                 svn_boolean_t ignore_ancestry,
+                 svn_boolean_t ignore_mergeinfo,
+                 svn_boolean_t diff_ignore_ancestry,
                  svn_boolean_t force_delete,
                  svn_boolean_t record_only,
                  svn_boolean_t dry_run,
@@ -10892,7 +11163,8 @@ merge_peg_locked(const char *source_path
   /* Do the real merge!  (We say with confidence that our merge
      sources are both ancestral and related.) */
   err = do_merge(NULL, NULL, merge_sources, target, ra_session,
-                 TRUE, same_repos, ignore_ancestry, force_delete, dry_run,
+                 TRUE /*sources_related*/, same_repos, ignore_mergeinfo,
+                 diff_ignore_ancestry, force_delete, dry_run,
                  record_only, NULL, FALSE, FALSE, depth, merge_options,
                  &use_sleep, ctx, sesspool, sesspool);
 
@@ -10906,12 +11178,13 @@ merge_peg_locked(const char *source_path
 }
 
 svn_error_t *
-svn_client_merge_peg4(const char *source_path_or_url,
+svn_client_merge_peg5(const char *source_path_or_url,
                       const apr_array_header_t *ranges_to_merge,
                       const svn_opt_revision_t *source_peg_revision,
                       const char *target_wcpath,
                       svn_depth_t depth,
-                      svn_boolean_t ignore_ancestry,
+                      svn_boolean_t ignore_mergeinfo,
+                      svn_boolean_t diff_ignore_ancestry,
                       svn_boolean_t force_delete,
                       svn_boolean_t record_only,
                       svn_boolean_t dry_run,
@@ -10933,14 +11206,16 @@ svn_client_merge_peg4(const char *source
     SVN_WC__CALL_WITH_WRITE_LOCK(
       merge_peg_locked(source_path_or_url, source_peg_revision,
                        ranges_to_merge,
-                       target_abspath, depth, ignore_ancestry,
+                       target_abspath, depth, ignore_mergeinfo,
+                       diff_ignore_ancestry,
                        force_delete, record_only, dry_run,
                        allow_mixed_rev, merge_options, ctx, pool),
       ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
   else
     SVN_ERR(merge_peg_locked(source_path_or_url, source_peg_revision,
                        ranges_to_merge,
-                       target_abspath, depth, ignore_ancestry,
+                       target_abspath, depth, ignore_mergeinfo,
+                       diff_ignore_ancestry,
                        force_delete, record_only, dry_run,
                        allow_mixed_rev, merge_options, ctx, pool));
 
@@ -11250,7 +11525,7 @@ find_base_on_source(svn_client__pathrev_
 /* Find a merge base location on the target branch, like in a reintegrate
  * merge.
  *
- *                     MID      S_T->source
+ *                              S_T->source
  *          o-----------o-------o---
  *         /    prev.  /         \
  *   -----o     merge /           \  this
@@ -11262,16 +11537,11 @@ find_base_on_source(svn_client__pathrev_
  * (at or after the YCA) at which all revisions up to BASE are recorded as
  * merged into S_T->source.
  *
- * Set *MID_P to the first location on the history of S_T->source at which
- * all revisions up to BASE are recorded as merged.
- * ### This is not implemented yet.
- *
  * If no locations on the history of S_T->target are recorded as merged to
- * S_T->source, set both *BASE_P and *MID_P to the YCA.
+ * S_T->source, set *BASE_P to the YCA.
  */
 static svn_error_t *
 find_base_on_target(svn_client__pathrev_t **base_p,
-                    svn_client__pathrev_t **mid_p,
                     source_and_target_t *s_t,
                     svn_client_ctx_t *ctx,
                     apr_pool_t *result_pool,
@@ -11282,7 +11552,6 @@ find_base_on_target(svn_client__pathrev_
                                     &s_t->target_branch,
                                     s_t->source_mergeinfo,
                                     ctx, result_pool, scratch_pool));
-  *mid_p = *base_p;  /* ### Wrong! */
 
   return SVN_NO_ERROR;
 }
@@ -11291,13 +11560,13 @@ find_base_on_target(svn_client__pathrev_
  */
 static svn_error_t *
 find_automatic_merge(svn_client__pathrev_t **base_p,
-                     svn_client__pathrev_t **mid_p,
+                     svn_boolean_t *is_reintegrate_like,
                      source_and_target_t *s_t,
                      svn_client_ctx_t *ctx,
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 {
-  svn_client__pathrev_t *base_on_source, *base_on_target, *mid;
+  svn_client__pathrev_t *base_on_source, *base_on_target;
 
   /* Fetch mergeinfo of source branch (tip) and target branch (working). */
   SVN_ERR(svn_client__get_repos_mergeinfo(&s_t->source_mergeinfo,
@@ -11349,19 +11618,19 @@ find_automatic_merge(svn_client__pathrev
    */
   SVN_ERR(find_base_on_source(&base_on_source, s_t,
                               ctx, scratch_pool, scratch_pool));
-  SVN_ERR(find_base_on_target(&base_on_target, &mid, s_t,
+  SVN_ERR(find_base_on_target(&base_on_target, s_t,
                               ctx, scratch_pool, scratch_pool));
 
   /* Choose a base. */
   if (base_on_source->rev >= base_on_target->rev)
     {
       *base_p = base_on_source;
-      *mid_p = NULL;
+      *is_reintegrate_like = FALSE;
     }
   else
     {
       *base_p = base_on_target;
-      *mid_p = mid;
+      *is_reintegrate_like = TRUE;
     }
 
   return SVN_NO_ERROR;
@@ -11370,7 +11639,8 @@ find_automatic_merge(svn_client__pathrev
 /* Details of an automatic merge. */
 struct svn_client_automatic_merge_t
 {
-  svn_client__pathrev_t *yca, *base, *mid, *right, *target;
+  svn_client__pathrev_t *yca, *base, *right, *target;
+  svn_boolean_t is_reintegrate_like;
   svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
 };
 
@@ -11405,7 +11675,7 @@ svn_client_find_automatic_merge_no_wc(
   s_t->target->abspath = NULL;  /* indicate the target is not a WC */
   s_t->target->loc = *target_loc;
 
-  SVN_ERR(find_automatic_merge(&merge->base, &merge->mid, s_t,
+  SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
                                ctx, result_pool, scratch_pool));
 
   merge->right = s_t->source;
@@ -11459,7 +11729,7 @@ svn_client_find_automatic_merge(svn_clie
                            &s_t->target->loc, target_wcpath,
                            TRUE /* strict_urls */, scratch_pool));
 
-  SVN_ERR(find_automatic_merge(&merge->base, &merge->mid, s_t,
+  SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
                                ctx, result_pool, scratch_pool));
   merge->yca = s_t->yca;
   merge->right = s_t->source;
@@ -11476,11 +11746,11 @@ svn_client_find_automatic_merge(svn_clie
 
 /* The body of svn_client_do_automatic_merge(), which see.
  *
- * Five locations are inputs: YCA, BASE, MID, RIGHT, TARGET, as shown
+ * Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
  * depending on whether the base is on the source branch or the target
  * branch of this merge.
  *
- *                     MID    RIGHT
+ *                            RIGHT     (is_reintegrate_like)
  *          o-----------o-------o---
  *         /    prev.  /         \
  *   -----o     merge /           \  this
@@ -11490,7 +11760,7 @@ svn_client_find_automatic_merge(svn_clie
  *
  * or
  *
- *                BASE        RIGHT      (and MID=NULL)
+ *                BASE        RIGHT      (! is_reintegrate_like)
  *          o-------o-----------o---
  *         /         \           \
  *   -----o     prev. \           \  this
@@ -11498,13 +11768,14 @@ svn_client_find_automatic_merge(svn_clie
  *          o-----------o-----------o
  *                                TARGET
  *
- * ### TODO: The reintegrate-type (MID!=NULL) code path does not yet
+ * ### TODO: The reintegrate-like code path does not yet
  * eliminate already-cherry-picked revisions from the source.
  */
 static svn_error_t *
 do_automatic_merge_locked(const svn_client_automatic_merge_t *merge,
                           const char *target_abspath,
                           svn_depth_t depth,
+                          svn_boolean_t diff_ignore_ancestry,
                           svn_boolean_t force_delete,
                           svn_boolean_t record_only,
                           svn_boolean_t dry_run,
@@ -11513,7 +11784,7 @@ do_automatic_merge_locked(const svn_clie
                           apr_pool_t *scratch_pool)
 {
   merge_target_t *target;
-  svn_boolean_t reintegrate_like = (merge->mid != NULL);
+  svn_boolean_t reintegrate_like = merge->is_reintegrate_like;
   svn_boolean_t use_sleep = FALSE;
   svn_error_t *err;
 
@@ -11569,7 +11840,7 @@ do_automatic_merge_locked(const svn_clie
 
       source.loc1 = merge->base;
       source.loc2 = merge->right;
-      source.ancestral = (merge->mid == NULL);
+      source.ancestral = ! merge->is_reintegrate_like;
 
       err = merge_cousins_and_supplement_mergeinfo(target,
                                                    base_ra_session,
@@ -11577,7 +11848,7 @@ do_automatic_merge_locked(const svn_clie
                                                    &source, merge->yca,
                                                    TRUE /* same_repos */,
                                                    depth,
-                                                   FALSE /*ignore_ancestry*/,
+                                                   FALSE /*diff_ignore_ancestry*/,
                                                    force_delete, record_only,
                                                    dry_run,
                                                    merge_options, &use_sleep,
@@ -11600,14 +11871,15 @@ do_automatic_merge_locked(const svn_clie
 
       source.loc1 = merge->yca;
       source.loc2 = merge->right;
-      source.ancestral = (merge->mid == NULL);
+      source.ancestral = ! merge->is_reintegrate_like;
 
       merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
       APR_ARRAY_PUSH(merge_sources, const merge_source_t *) = &source;
 
       err = do_merge(NULL, NULL, merge_sources, target, NULL,
                      TRUE /*related*/, TRUE /*same_repos*/,
-                     FALSE /*ignore_ancestry*/, force_delete, dry_run,
+                     FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
+                     force_delete, dry_run,
                      record_only, NULL, FALSE, FALSE, depth, merge_options,
                      &use_sleep, ctx, scratch_pool, scratch_pool);
     }
@@ -11624,6 +11896,7 @@ svn_error_t *
 svn_client_do_automatic_merge(const svn_client_automatic_merge_t *merge,
                               const char *target_wcpath,
                               svn_depth_t depth,
+                              svn_boolean_t diff_ignore_ancestry,
                               svn_boolean_t force_delete,
                               svn_boolean_t record_only,
                               svn_boolean_t dry_run,
@@ -11640,12 +11913,14 @@ svn_client_do_automatic_merge(const svn_
     SVN_WC__CALL_WITH_WRITE_LOCK(
       do_automatic_merge_locked(merge,
                                 target_abspath, depth,
+                                diff_ignore_ancestry,
                                 force_delete, record_only, dry_run,
                                 merge_options, ctx, pool),
       ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
   else
     SVN_ERR(do_automatic_merge_locked(merge,
                                 target_abspath, depth,
+                                diff_ignore_ancestry,
                                 force_delete, record_only, dry_run,
                                 merge_options, ctx, pool));
 
@@ -11656,7 +11931,7 @@ svn_boolean_t
 svn_client_automatic_merge_is_reintegrate_like(
         const svn_client_automatic_merge_t *merge)
 {
-  return merge->mid != NULL;
+  return merge->is_reintegrate_like;
 }
 
 svn_error_t *

Modified: subversion/branches/windows-build-update/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/windows-build-update/subversion/libsvn_client/patch.c?rev=1438999&r1=1438998&r2=1438999&view=diff
==============================================================================
--- subversion/branches/windows-build-update/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/windows-build-update/subversion/libsvn_client/patch.c Sun Jan 27 00:01:57 2013
@@ -1205,6 +1205,8 @@ readline(target_content_t *content,
   if ((line_raw && line_raw->len > 0) || eol_str)
     content->current_line++;
 
+  SVN_ERR_ASSERT(content->current_line > 0);
+
   return SVN_NO_ERROR;
 }
 
@@ -1536,26 +1538,45 @@ get_hunk_info(hunk_info_t **hi, patch_ta
    * the hunk applies at line 1. If the file already exists, the hunk
    * is rejected, unless the file is versioned and its content matches
    * the file the patch wants to create.  */
-  if (original_start == 0 && ! is_prop_hunk)
+  if (original_start == 0 && fuzz > 0)
+    {
+      matched_line = 0; /* reject any fuzz for new files */
+    }
+  else if (original_start == 0 && ! is_prop_hunk)
     {
       if (target->kind_on_disk == svn_node_file)
         {
-          if (target->db_kind == svn_node_file)
+          const svn_io_dirent2_t *dirent;
+          SVN_ERR(svn_io_stat_dirent2(&dirent, target->local_abspath, FALSE,
+                                      TRUE, scratch_pool, scratch_pool));
+
+          if (dirent->kind == svn_node_file
+              && !dirent->special
+              && dirent->filesize == 0)
+            {
+              matched_line = 1; /* Matched an on-disk empty file */
+            }
+          else
             {
-              svn_boolean_t file_matches;
+              if (target->db_kind == svn_node_file)
+                {
+                  svn_boolean_t file_matches;
 
-              SVN_ERR(match_existing_target(&file_matches, content, hunk,
+                  /* ### I can't reproduce anything but a no-match here.
+                         The content is already at eof, so any hunk fails */
+                  SVN_ERR(match_existing_target(&file_matches, content, hunk,
                                             scratch_pool));
-              if (file_matches)
-                {
-                  matched_line = 1;
-                  already_applied = TRUE;
+                  if (file_matches)
+                    {
+                      matched_line = 1;
+                      already_applied = TRUE;
+                    }
+                  else
+                    matched_line = 0; /* reject */
                 }
               else
                 matched_line = 0; /* reject */
             }
-          else
-            matched_line = 0; /* reject */
         }
       else
         matched_line = 1;



Mime
View raw message