You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2011/10/11 21:52:46 UTC

svn commit: r1182053 [9/30] - in /subversion/branches/svn_mutex: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/ contrib/hook-scripts/enforcer/ contrib/server-side/ notes/ notes/merge-tr...

Modified: subversion/branches/svn_mutex/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn_mutex/subversion/libsvn_client/mergeinfo.c?rev=1182053&r1=1182052&r2=1182053&view=diff
==============================================================================
--- subversion/branches/svn_mutex/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/svn_mutex/subversion/libsvn_client/mergeinfo.c Tue Oct 11 19:52:34 2011
@@ -267,7 +267,7 @@ svn_client__get_wc_mergeinfo(svn_mergein
                                                 scratch_pool));
 
           /* Look in LOCAL_ABSPATH's parent for inherited mergeinfo if
-             LOCAL_ABSPATH's has no base revision because it is an uncommited
+             LOCAL_ABSPATH's has no base revision because it is an uncommitted
              addition, or if its base revision falls within the inclusive
              range of its parent's last changed revision to the parent's base
              revision; otherwise stop looking for inherited mergeinfo. */
@@ -438,16 +438,17 @@ svn_client__get_repos_mergeinfo(svn_ra_s
                                 svn_revnum_t rev,
                                 svn_mergeinfo_inheritance_t inherit,
                                 svn_boolean_t squelch_incapable,
-                                svn_boolean_t validate_inherited_mergeinfo,
                                 apr_pool_t *pool)
 {
   svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
 
   *target_mergeinfo = NULL;
 
-  SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
-    &tgt_mergeinfo_cat, ra_session, rel_path, rev, inherit,
-    squelch_incapable, FALSE, validate_inherited_mergeinfo, pool, pool));
+  SVN_ERR(svn_client__get_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
+                                                  ra_session,
+                                                  rel_path, rev, inherit,
+                                                  squelch_incapable, FALSE,
+                                                  pool, pool));
 
   if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
     {
@@ -463,48 +464,84 @@ svn_client__get_repos_mergeinfo(svn_ra_s
 }
 
 svn_error_t *
-svn_client__get_repos_mergeinfo_catalog(
-  svn_mergeinfo_catalog_t *mergeinfo_cat,
-  svn_ra_session_t *ra_session,
-  const char *rel_path,
-  svn_revnum_t rev,
-  svn_mergeinfo_inheritance_t inherit,
-  svn_boolean_t squelch_incapable,
-  svn_boolean_t include_descendants,
-  svn_boolean_t validate_inherited_mergeinfo,
-  apr_pool_t *result_pool,
-  apr_pool_t *scratch_pool)
+svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
+                                        svn_ra_session_t *ra_session,
+                                        const char *rel_path,
+                                        svn_revnum_t rev,
+                                        svn_mergeinfo_inheritance_t inherit,
+                                        svn_boolean_t squelch_incapable,
+                                        svn_boolean_t include_descendants,
+                                        apr_pool_t *result_pool,
+                                        apr_pool_t *scratch_pool)
 {
   svn_error_t *err;
-  svn_mergeinfo_t repos_mergeinfo;
+  svn_mergeinfo_t repos_mergeinfo_cat;
   apr_array_header_t *rel_paths = apr_array_make(scratch_pool, 1,
                                                  sizeof(rel_path));
 
   APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
 
   /* Fetch the mergeinfo. */
-  err = svn_ra_get_mergeinfo2(ra_session, &repos_mergeinfo, rel_paths, rev,
-                              inherit, validate_inherited_mergeinfo,
-                              include_descendants, result_pool);
+  err = svn_ra_get_mergeinfo(ra_session, &repos_mergeinfo_cat, rel_paths,
+                             rev, inherit, include_descendants, result_pool);
   if (err)
     {
       if (squelch_incapable && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
         {
           svn_error_clear(err);
-          repos_mergeinfo = NULL;
+          *mergeinfo_cat = NULL;
         }
       else
         return svn_error_trace(err);
     }
 
-  *mergeinfo_cat = repos_mergeinfo;
+  if (repos_mergeinfo_cat == NULL)
+    {
+      *mergeinfo_cat = NULL;
+    }
+  else
+    {
+      const char *repos_root;
+      const char *session_url;
+
+      SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, scratch_pool));
+      SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
+
+      if (strcmp(repos_root, session_url) == 0)
+        {
+          *mergeinfo_cat = repos_mergeinfo_cat;
+        }
+      else
+        {
+          apr_hash_index_t *hi;
+          svn_mergeinfo_catalog_t rekeyed_mergeinfo_cat =
+            apr_hash_make(result_pool);
+
+          for (hi = apr_hash_first(scratch_pool, repos_mergeinfo_cat);
+               hi;
+               hi = apr_hash_next(hi))
+            {
+              const char *path =
+                svn_path_url_add_component2(session_url,
+                                            svn__apr_hash_index_key(hi),
+                                            scratch_pool);
+              SVN_ERR(svn_ra_get_path_relative_to_root(ra_session, &path,
+                                                       path,
+                                                       result_pool));
+              apr_hash_set(rekeyed_mergeinfo_cat, path, APR_HASH_KEY_STRING,
+                           svn__apr_hash_index_val(hi));
+            }
+          *mergeinfo_cat = rekeyed_mergeinfo_cat;
+        }
+    }
   return SVN_NO_ERROR;
 }
 
 
 svn_error_t *
 svn_client__get_wc_or_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
-                                      svn_boolean_t *indirect,
+                                      svn_boolean_t *inherited,
+                                      svn_boolean_t *from_repos,
                                       svn_boolean_t repos_only,
                                       svn_mergeinfo_inheritance_t inherit,
                                       svn_ra_session_t *ra_session,
@@ -517,7 +554,8 @@ svn_client__get_wc_or_repos_mergeinfo(sv
   *target_mergeinfo = NULL;
 
   SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
-                                                        indirect, FALSE,
+                                                        inherited, from_repos,
+                                                        FALSE,
                                                         repos_only,
                                                         FALSE, inherit,
                                                         ra_session,
@@ -541,7 +579,8 @@ svn_client__get_wc_or_repos_mergeinfo(sv
 svn_error_t *
 svn_client__get_wc_or_repos_mergeinfo_catalog(
   svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
-  svn_boolean_t *indirect,
+  svn_boolean_t *inherited,
+  svn_boolean_t *from_repos,
   svn_boolean_t include_descendants,
   svn_boolean_t repos_only,
   svn_boolean_t ignore_invalid_mergeinfo,
@@ -557,10 +596,15 @@ svn_client__get_wc_or_repos_mergeinfo_ca
   const char *local_abspath;
   const char *repos_root;
   const char *repos_relpath;
+  svn_mergeinfo_catalog_t target_mergeinfo_cat_wc = NULL;
+  svn_mergeinfo_catalog_t target_mergeinfo_cat_repos = NULL;
 
   SVN_ERR(svn_dirent_get_absolute(&local_abspath, target_wcpath,
                                   scratch_pool));
 
+  if (from_repos)
+    *from_repos = FALSE;
+
   /* We may get an entry with abbreviated information from TARGET_WCPATH's
      parent if TARGET_WCPATH is missing.  These limited entries do not have
      a URL and without that we cannot get accurate mergeinfo for
@@ -575,23 +619,37 @@ svn_client__get_wc_or_repos_mergeinfo_ca
   else
     url = NULL;
 
+  if (!repos_only)
+    {
+      SVN_ERR(svn_client__get_wc_mergeinfo_catalog(&target_mergeinfo_cat_wc,
+                                                   inherited,
+                                                   include_descendants,
+                                                   inherit,
+                                                   local_abspath,
+                                                   NULL, NULL,
+                                                   ignore_invalid_mergeinfo,
+                                                   ctx,
+                                                   result_pool,
+                                                   scratch_pool));
+
+      /* If we want LOCAL_ABSPATH's inherited mergeinfo, were we able to
+         get it from the working copy?  If not, then we must ask the
+         repository. */
+      if (! ((*inherited)
+             || (inherit == svn_mergeinfo_explicit)
+             || (repos_relpath
+                 && target_mergeinfo_cat_wc
+                 && apr_hash_get(target_mergeinfo_cat_wc, repos_relpath,
+                                 APR_HASH_KEY_STRING))))
+        {
+          repos_only = TRUE;
+          /* We already have any subtree mergeinfo from the working copy, no
+             need to ask the server for it again. */
+          include_descendants = FALSE;
+        }
+    }
+
   if (repos_only)
-    *target_mergeinfo_catalog = NULL;
-  else
-    SVN_ERR(svn_client__get_wc_mergeinfo_catalog(target_mergeinfo_catalog,
-                                                 indirect,
-                                                 include_descendants,
-                                                 inherit,
-                                                 local_abspath,
-                                                 NULL, NULL,
-                                                 ignore_invalid_mergeinfo,
-                                                 ctx,
-                                                 result_pool, scratch_pool));
-
-  /* If there is no WC mergeinfo check the repository for inherited
-     mergeinfo, unless TARGET_WCPATH is a local addition or has a
-     local modification which has removed all of its pristine mergeinfo. */
-  if (*target_mergeinfo_catalog == NULL)
     {
       /* No need to check the repos if this is a local addition. */
       if (url != NULL)
@@ -609,7 +667,6 @@ svn_client__get_wc_or_repos_mergeinfo_ca
             {
               const char *session_url = NULL;
               apr_pool_t *sesspool = NULL;
-              svn_boolean_t validate_inherited_mergeinfo = FALSE;
 
               if (ra_session)
                 {
@@ -626,15 +683,20 @@ svn_client__get_wc_or_repos_mergeinfo_ca
                 }
 
               SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
-                        target_mergeinfo_catalog, ra_session,
+                        &target_mergeinfo_cat_repos, ra_session,
                         "", target_rev, inherit,
-                        TRUE, FALSE, validate_inherited_mergeinfo,
+                        TRUE, include_descendants,
                         result_pool, scratch_pool));
 
-              if (*target_mergeinfo_catalog
-                  && apr_hash_get(*target_mergeinfo_catalog, "",
+              if (target_mergeinfo_cat_repos
+                  && apr_hash_get(target_mergeinfo_cat_repos,
+                                  repos_relpath,
                                   APR_HASH_KEY_STRING))
-                *indirect = TRUE;
+                {
+                  *inherited = TRUE;
+                  if (from_repos)
+                    *from_repos = TRUE;
+                }
 
               /* If we created an RA_SESSION above, destroy it.
                  Otherwise, if reparented an existing session, point
@@ -651,6 +713,25 @@ svn_client__get_wc_or_repos_mergeinfo_ca
             }
         }
     }
+
+  /* Combine the mergeinfo from the working copy and repository as needed. */
+  if (target_mergeinfo_cat_wc)
+    {
+      *target_mergeinfo_catalog = target_mergeinfo_cat_wc;
+      if (target_mergeinfo_cat_repos)
+        SVN_ERR(svn_mergeinfo_catalog_merge(*target_mergeinfo_catalog,
+                                            target_mergeinfo_cat_repos,
+                                            result_pool, scratch_pool));
+    }
+  else if (target_mergeinfo_cat_repos)
+    {
+      *target_mergeinfo_catalog = target_mergeinfo_cat_repos;
+    }
+  else
+    {
+      *target_mergeinfo_catalog = NULL;
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -658,8 +739,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
 svn_error_t *
 svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p,
                                      svn_boolean_t *has_rev_zero_history,
-                                     const char *path_or_url,
-                                     const svn_opt_revision_t *peg_revision,
+                                     svn_revnum_t peg_revnum,
                                      svn_revnum_t range_youngest,
                                      svn_revnum_t range_oldest,
                                      svn_ra_session_t *ra_session,
@@ -667,65 +747,13 @@ svn_client__get_history_as_mergeinfo(svn
                                      apr_pool_t *pool)
 {
   apr_array_header_t *segments;
-  svn_revnum_t peg_revnum = SVN_INVALID_REVNUM;
-  apr_pool_t *sesspool = NULL;  /* only used for an RA session we open */
-  svn_ra_session_t *session = ra_session;
-
-  /* If SESSION is NULL, resolve the url */
-  if (session == NULL)
-    {
-      svn_opt_revision_t opt_rev;
-      const char *url;
-      opt_rev.kind = svn_opt_revision_unspecified;
-
-      sesspool = svn_pool_create(pool);
-
-      SVN_ERR(svn_client__ra_session_from_path(&session, &peg_revnum, &url,
-                                               path_or_url, NULL,
-                                               peg_revision, &opt_rev,
-                                               ctx, sesspool));
-    }
-  else
-    {
-      const char *local_abspath;
-      /* The session is rooted correctly, so we don't need the url,
-         but we do need a revision */
-      switch (peg_revision->kind)
-        {
-          case svn_opt_revision_head:
-          case svn_opt_revision_unspecified:
-            SVN_ERR(svn_ra_get_latest_revnum(session, &peg_revnum, pool));
-            break;
-          case svn_opt_revision_number:
-            peg_revnum = peg_revision->value.number;
-            break;
-          case svn_opt_revision_date:
-            SVN_ERR(svn_ra_get_dated_revision(session, &peg_revnum,
-                                              peg_revision->value.date, pool));
-            break;
-          default:
-            if (svn_path_is_url(path_or_url))
-              return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED,
-                                      NULL, NULL);
-
-            SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
-                                            pool));
-
-            SVN_ERR(svn_client__get_revision_number(&peg_revnum, NULL,
-                                                    ctx->wc_ctx,
-                                                    local_abspath,
-                                                    session, peg_revision,
-                                                    pool));
-            break;
-        }
-    }
 
   /* Fetch the location segments for our URL@PEG_REVNUM. */
   if (! SVN_IS_VALID_REVNUM(range_youngest))
     range_youngest = peg_revnum;
   if (! SVN_IS_VALID_REVNUM(range_oldest))
     range_oldest = 0;
-  SVN_ERR(svn_client__repos_location_segments(&segments, session, "",
+  SVN_ERR(svn_client__repos_location_segments(&segments, ra_session, "",
                                               peg_revnum, range_youngest,
                                               range_oldest, ctx, pool));
 
@@ -743,10 +771,6 @@ svn_client__get_history_as_mergeinfo(svn
 
   SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(mergeinfo_p, segments, pool));
 
-  /* If we opened an RA session, ensure its closure. */
-  if (sesspool)
-    svn_pool_destroy(sesspool);
-
   return SVN_NO_ERROR;
 }
 
@@ -959,7 +983,7 @@ svn_client__elide_mergeinfo(const char *
       if (!mergeinfo && !wc_elision_limit_path)
         {
           err = svn_client__get_wc_or_repos_mergeinfo(
-            &mergeinfo, &inherited, TRUE,
+            &mergeinfo, &inherited, NULL, TRUE,
             svn_mergeinfo_nearest_ancestor,
             NULL, target_wcpath, ctx, pool);
           if (err)
@@ -1065,50 +1089,19 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
 
   if (use_url)
     {
-      svn_mergeinfo_catalog_t tmp_catalog;
-      svn_boolean_t validate_inherited_mergeinfo = FALSE;
-
       rev = peg_rev;
       SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
-        &tmp_catalog, ra_session, "", rev, svn_mergeinfo_inherited,
-        FALSE, include_descendants, validate_inherited_mergeinfo,
+        mergeinfo_catalog, ra_session, "", rev, svn_mergeinfo_inherited,
+        FALSE, include_descendants,
         result_pool, scratch_pool));
-
-      /* If we're not querying the root of the repository, the catalog
-         we fetched will be keyed on paths relative to the session
-         URL.  But our caller is expecting repository relpaths.  So we
-         do a little dance...  */
-      if (tmp_catalog && (strcmp(url, *repos_root) != 0))
-        {
-          apr_hash_index_t *hi;
-
-          *mergeinfo_catalog = apr_hash_make(result_pool);
-
-          for (hi = apr_hash_first(scratch_pool, tmp_catalog);
-               hi; hi = apr_hash_next(hi))
-            {
-              /* session-relpath -> repos-url -> repos-relpath */
-              const char *path =
-                svn_path_url_add_component2(url, svn__apr_hash_index_key(hi),
-                                            scratch_pool);
-              SVN_ERR(svn_ra_get_path_relative_to_root(ra_session, &path, path,
-                                                       result_pool));
-              apr_hash_set(*mergeinfo_catalog, path, APR_HASH_KEY_STRING,
-                           svn__apr_hash_index_val(hi));
-            }
-        }
-      else
-        {
-          *mergeinfo_catalog = tmp_catalog;
-        }
     }
   else /* ! svn_path_is_url() */
     {
-      svn_boolean_t indirect;
+      svn_boolean_t inherited;
 
       /* Acquire return values. */
       SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(
-        mergeinfo_catalog, &indirect, include_descendants, FALSE,
+        mergeinfo_catalog, &inherited, NULL, include_descendants, FALSE,
         ignore_invalid_mergeinfo, svn_mergeinfo_inherited,
         ra_session, path_or_url, ctx,
         result_pool, scratch_pool));
@@ -1229,6 +1222,7 @@ svn_client__elide_mergeinfo_catalog(svn_
                                                       sizeof(const char *));
   svn_delta_editor_t *editor = svn_delta_default_editor(pool);
   struct elide_mergeinfo_catalog_cb_baton cb = { 0 };
+  void *eb;
   int i;
 
   cb.elidable_paths = elidable_paths;
@@ -1238,10 +1232,15 @@ svn_client__elide_mergeinfo_catalog(svn_
   editor->open_root = elide_mergeinfo_catalog_open_root;
   editor->open_directory = elide_mergeinfo_catalog_open_directory;
 
+  eb = mergeinfo_catalog;
+  SVN_ERR(svn_editor__insert_shims((const svn_delta_editor_t **)&editor, &eb,
+                                   editor, eb, NULL, NULL, NULL, NULL,
+                                   pool, pool));
+
   /* Walk over the paths, and build up a list of elidable ones. */
   SVN_ERR(svn_hash_keys(&paths, mergeinfo_catalog, pool));
   SVN_ERR(svn_delta_path_driver(editor,
-                                mergeinfo_catalog, /* as edit_baton */
+                                eb,
                                 SVN_INVALID_REVNUM,
                                 paths,
                                 elide_mergeinfo_catalog_cb,
@@ -1268,16 +1267,20 @@ svn_client__elide_mergeinfo_catalog(svn_
    Return a pointer to the mergeinfo value of the nearest path-wise ancestor
    of ABS_REPOS_PATH in DEPTH_FIRST_CATALOG_INDEX.  A path is considered its
    own ancestor, so if a key exactly matches ABS_REPOS_PATH, return that
-   key's mergeinfo.
+   key's mergeinfo and set *ANCESTOR_IS_SELF to true (set it to false in all
+   other cases).
 
    If DEPTH_FIRST_CATALOG_INDEX is NULL, empty, or no ancestor is found, then
    return NULL. */
 static svn_mergeinfo_t
 find_nearest_ancestor(const apr_array_header_t *depth_first_catalog_index,
+                      svn_boolean_t *ancestor_is_self,
                       const char *abs_repos_path)
 {
   int ancestor_index = -1;
 
+  *ancestor_is_self = FALSE;
+
   if (depth_first_catalog_index)
     {
       int i;
@@ -1286,9 +1289,18 @@ find_nearest_ancestor(const apr_array_he
         {
           svn_sort__item_t item = APR_ARRAY_IDX(depth_first_catalog_index, i,
                                                 svn_sort__item_t);
-          if (svn_fspath__is_ancestor(item.key, abs_repos_path)
-              || svn_path_compare_paths(item.key, abs_repos_path) == 0)
-            ancestor_index = i;
+          if (svn_fspath__is_ancestor(item.key, abs_repos_path))
+            {
+              ancestor_index = i;
+
+              /* There's no nearer ancestor than ABS_REPOS_PATH itself. */
+              if (strcmp(item.key, abs_repos_path) == 0)
+                {
+                  *ancestor_is_self = TRUE;
+                  break;
+                }
+            }
+
         }
     }
 
@@ -1304,6 +1316,8 @@ find_nearest_ancestor(const apr_array_he
    svn_log_entry_receiver_t callback. */
 struct filter_log_entry_baton_t
 {
+  /* Is TRUE if RANGELIST describes potentially merged revisions, is FALSE
+     if RANGELIST describes potentially eligible revisions. */
   svn_boolean_t filtering_merged;
 
   /* Unsorted array of repository relative paths representing the merge
@@ -1321,8 +1335,9 @@ struct filter_log_entry_baton_t
      TARGET_MERGEINFO_CATALOG. */
   apr_array_header_t *depth_first_catalog_index;
 
-  /* A rangelist describing all the ranges merged to ABS_REPOS_TARGET_PATH
-     from the */
+  /* A rangelist describing all the revisions potentially merged or
+     potentially eligible for merging (see FILTERING_MERGED) based on
+     the target's explicit or inherited mergeinfo. */
   const apr_array_header_t *rangelist;
 
   /* The wrapped svn_log_entry_receiver_t callback and baton which
@@ -1337,9 +1352,18 @@ struct filter_log_entry_baton_t
    `struct filter_log_entry_baton_t *'.
 
    Call the wrapped log receiver BATON->log_receiver (with
-   BATON->log_receiver_baton), only if the log entry falls within the
-   ranges in BATON->rangelist.
- */
+   BATON->log_receiver_baton) if:
+   
+   BATON->FILTERING_MERGED is FALSE and the changes represented by LOG_ENTRY
+   have been fully merged from BATON->MERGE_SOURCE_PATHS to the WC target
+   based on the mergeinfo for the WC contained in BATON->TARGET_MERGEINFO_CATALOG.
+
+   Or
+
+   BATON->FILTERING_MERGED is TRUE and the changes represented by LOG_ENTRY
+   have not been merged, or only partially merged, from
+   BATON->MERGE_SOURCE_PATHS to the WC target based on the mergeinfo for the
+   WC contained in BATON->TARGET_MERGEINFO_CATALOG. */
 static svn_error_t *
 filter_log_entry_with_rangelist(void *baton,
                                 svn_log_entry_t *log_entry,
@@ -1402,6 +1426,8 @@ filter_log_entry_with_rangelist(void *ba
           apr_hash_index_t *hi2;
           svn_boolean_t found_this_revision = FALSE;
           const char *merge_source_rel_target;
+          const char *merge_source_path;
+          svn_boolean_t ancestor_is_self;
 
           svn_pool_clear(iterpool);
 
@@ -1409,8 +1435,8 @@ filter_log_entry_with_rangelist(void *ba
              merge sources.  If not then ignore this path.  */
           for (i = 0; i < fleb->merge_source_paths->nelts; i++)
             {
-              const char *merge_source_path
-                = APR_ARRAY_IDX(fleb->merge_source_paths, i, const char *);
+              merge_source_path = APR_ARRAY_IDX(fleb->merge_source_paths,
+                                                i, const char *);
 
               merge_source_rel_target
                 = svn_fspath__skip_ancestor(merge_source_path, path);
@@ -1437,27 +1463,69 @@ filter_log_entry_with_rangelist(void *ba
 
           nearest_ancestor_mergeinfo =
             find_nearest_ancestor(fleb->depth_first_catalog_index,
+                                  &ancestor_is_self,
                                   target_path_affected);
+
+          /* Issue #3791: A path should never have explicit mergeinfo
+             describing its own addition (that's self-referential).  Nor will
+             it have explicit mergeinfo describing its own deletion (we
+             obviously can't add new mergeinfo to a path we are deleting).
+
+             This lack of explicit mergeinfo should not cause such revisions
+             to show up as eligible however.  If PATH was deleted, replaced,
+             or added in LOG_ENTRY->REVISION, but the corresponding
+             TARGET_PATH_AFFECTED already exists and has explicit mergeinfo
+             describing merges from PATH *after* LOG_ENTRY->REVISION, then
+             ignore this PATH.  If it was deleted in LOG_ENTRY->REVISION it's
+             obviously back.  If it was added or replaced it's still around
+             possibly it was replaced one or more times, but it's back now.
+             Regardless, LOG_ENTRY->REVISION is *not* an eligible revision! */
+          if (ancestor_is_self /* Explicit mergeinfo on TARGET_PATH_AFFECTED */
+              && (change->action != 'M'))
+            {
+              apr_array_header_t *rangelist = apr_hash_get(
+                nearest_ancestor_mergeinfo, path, APR_HASH_KEY_STRING);
+              svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
+                rangelist, rangelist->nelts - 1, svn_merge_range_t *);
+
+              if (youngest_range
+                  && (youngest_range->end > log_entry->revision))
+                continue;
+            }
+
           if (nearest_ancestor_mergeinfo)
             {
               for (hi2 = apr_hash_first(iterpool, nearest_ancestor_mergeinfo);
                    hi2;
                    hi2 = apr_hash_next(hi2))
                 {
+                  const char *mergeinfo_path = svn__apr_hash_index_key(hi2);
                   apr_array_header_t *rangelist = svn__apr_hash_index_val(hi2);
-                  SVN_ERR(svn_rangelist_intersect(&intersection, rangelist,
-                                                  this_rev_rangelist, FALSE,
-                                                  iterpool));
-                  if (intersection->nelts)
+
+                  /* Does the mergeinfo for PATH reflect if
+                     LOG_ENTRY->REVISION was previously merged
+                     from MERGE_SOURCE_PATH? */
+                  if (svn_fspath__is_ancestor(merge_source_path,
+                                              mergeinfo_path))
                     {
+                      /* Something was merged from MERGE_SOURCE_PATH, does
+                         it include LOG_ENTRY->REVISION? */
                       SVN_ERR(svn_rangelist_intersect(&intersection,
                                                       rangelist,
                                                       this_rev_rangelist,
-                                                      TRUE, iterpool));
+                                                      FALSE,
+                                                      iterpool));
                       if (intersection->nelts)
                         {
-                          found_this_revision = TRUE;
-                          break;
+                          SVN_ERR(svn_rangelist_intersect(&intersection,
+                                                          rangelist,
+                                                          this_rev_rangelist,
+                                                          TRUE, iterpool));
+                          if (intersection->nelts)
+                            {
+                              found_this_revision = TRUE;
+                              break;
+                            }
                         }
                     }
                 }
@@ -1648,10 +1716,10 @@ svn_client_mergeinfo_get_merged(apr_hash
 
 svn_error_t *
 svn_client_mergeinfo_log(svn_boolean_t finding_merged,
-                         const char *path_or_url,
-                         const svn_opt_revision_t *peg_revision,
-                         const char *merge_source_path_or_url,
-                         const svn_opt_revision_t *src_peg_revision,
+                         const char *target_path_or_url,
+                         const svn_opt_revision_t *target_peg_revision,
+                         const char *source_path_or_url,
+                         const svn_opt_revision_t *source_peg_revision,
                          svn_log_entry_receiver_t log_receiver,
                          void *log_receiver_baton,
                          svn_boolean_t discover_changed_paths,
@@ -1660,17 +1728,19 @@ svn_client_mergeinfo_log(svn_boolean_t f
                          svn_client_ctx_t *ctx,
                          apr_pool_t *scratch_pool)
 {
+  apr_pool_t *sesspool = svn_pool_create(scratch_pool);
+  svn_ra_session_t *source_session, *target_session;
   const char *log_target = NULL;
   const char *repos_root;
-  const char *path_or_url_repos_rel;
-  svn_mergeinfo_catalog_t path_or_url_mergeinfo_cat;
+  const char *target_repos_rel;
+  svn_mergeinfo_catalog_t target_mergeinfo_cat;
 
-  /* A hash of paths, at or under PATH_OR_URL, mapped to rangelists.  Not
+  /* A hash of paths, at or under TARGET_PATH_OR_URL, mapped to rangelists.  Not
      technically mergeinfo, so not using the svn_mergeinfo_t type. */
   apr_hash_t *inheritable_subtree_merges;
 
   svn_mergeinfo_t source_history;
-  svn_mergeinfo_t path_or_url_history;
+  svn_mergeinfo_t target_history;
   apr_array_header_t *master_noninheritable_rangelist;
   apr_array_header_t *master_inheritable_rangelist;
   apr_array_header_t *merge_source_paths =
@@ -1685,30 +1755,30 @@ svn_client_mergeinfo_log(svn_boolean_t f
       SVN_ERR_UNSUPPORTED_FEATURE, NULL,
       _("Only depths 'infinity' and 'empty' are currently supported"));
 
-  /* We need the union of PATH_OR_URL@PEG_REVISION's mergeinfo
+  /* We need the union of TARGET_PATH_OR_URL@TARGET_PEG_REVISION's mergeinfo
      and MERGE_SOURCE_URL's history.  It's not enough to do path
      matching, because renames in the history of MERGE_SOURCE_URL
      throw that all in a tizzy.  Of course, if there's no mergeinfo on
      the target, that vastly simplifies matters (we'll have nothing to
      do). */
   /* This get_mergeinfo() call doubles as a mergeinfo capabilities check. */
-  SVN_ERR(get_mergeinfo(&path_or_url_mergeinfo_cat, &repos_root,
-                        path_or_url, peg_revision,
+  SVN_ERR(get_mergeinfo(&target_mergeinfo_cat, &repos_root,
+                        target_path_or_url, target_peg_revision,
                         depth == svn_depth_infinity, TRUE,
                         ctx, scratch_pool, scratch_pool));
 
-  if (!svn_path_is_url(path_or_url))
-    SVN_ERR(svn_dirent_get_absolute(&path_or_url, path_or_url, scratch_pool));
+  if (!svn_path_is_url(target_path_or_url))
+    SVN_ERR(svn_dirent_get_absolute(&target_path_or_url, target_path_or_url, scratch_pool));
 
-  SVN_ERR(svn_client__path_relative_to_root(&path_or_url_repos_rel,
+  SVN_ERR(svn_client__path_relative_to_root(&target_repos_rel,
                                             ctx->wc_ctx,
-                                            path_or_url,
+                                            target_path_or_url,
                                             repos_root,
                                             FALSE, NULL,
                                             scratch_pool,
                                             scratch_pool));
 
-  if (!path_or_url_mergeinfo_cat)
+  if (!target_mergeinfo_cat)
     {
       /* If we are looking for what has been merged and there is no
          mergeinfo then we already know the answer.  If we are looking
@@ -1722,30 +1792,60 @@ svn_client_mergeinfo_log(svn_boolean_t f
         }
       else
         {
-          path_or_url_mergeinfo_cat = apr_hash_make(scratch_pool);
-          apr_hash_set(path_or_url_mergeinfo_cat,
-                       path_or_url_repos_rel,
+          target_mergeinfo_cat = apr_hash_make(scratch_pool);
+          apr_hash_set(target_mergeinfo_cat,
+                       target_repos_rel,
                        APR_HASH_KEY_STRING,
                        apr_hash_make(scratch_pool));
         }
     }
 
+  /* Open RA sessions to the repository for the source and target.
+   * ### TODO: As the source and target must be in the same repository, we
+   * should share a single session, tracking the two URLs separately. */
+  
   if (!finding_merged)
-    SVN_ERR(svn_client__get_history_as_mergeinfo(&path_or_url_history, NULL,
-                                                 path_or_url,
-                                                 peg_revision,
+    {
+      svn_revnum_t target_peg_revnum;
+      const char *url;
+
+      SVN_ERR(svn_client__ra_session_from_path(&target_session,
+                                               &target_peg_revnum, &url,
+                                               target_path_or_url, NULL,
+                                               target_peg_revision,
+                                               target_peg_revision,
+                                               ctx, scratch_pool));
+      
+      SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history, NULL,
+                                                   target_peg_revnum,
+                                                   SVN_INVALID_REVNUM,
+                                                   SVN_INVALID_REVNUM,
+                                                   target_session, ctx,
+                                                   scratch_pool));
+    }
+
+  {
+    svn_revnum_t source_peg_revnum;
+    const char *url;
+
+    SVN_ERR(svn_client__ra_session_from_path(&source_session,
+                                             &source_peg_revnum, &url,
+                                             source_path_or_url, NULL,
+                                             source_peg_revision,
+                                             source_peg_revision,
+                                             ctx, scratch_pool));
+
+    SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
+                                                 source_peg_revnum,
                                                  SVN_INVALID_REVNUM,
                                                  SVN_INVALID_REVNUM,
-                                                 NULL, ctx, scratch_pool));
+                                                 source_session, ctx,
+                                                 scratch_pool));
+  }
 
-  SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
-                                               merge_source_path_or_url,
-                                               src_peg_revision,
-                                               SVN_INVALID_REVNUM,
-                                               SVN_INVALID_REVNUM,
-                                               NULL, ctx, scratch_pool));
+  svn_pool_destroy(sesspool);
 
-  /* Separate the explicit or inherited mergeinfo on PATH_OR_URL, and possibly
+  /* Separate the explicit or inherited mergeinfo on TARGET_PATH_OR_URL, and possibly
      its explicit subtree mergeinfo, into their inheritable and non-inheritable
      parts. */
   master_noninheritable_rangelist =
@@ -1757,7 +1857,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
   iterpool = svn_pool_create(scratch_pool);
 
   for (hi_catalog = apr_hash_first(scratch_pool,
-                                   path_or_url_mergeinfo_cat);
+                                   target_mergeinfo_cat);
        hi_catalog;
        hi_catalog = apr_hash_next(hi_catalog))
     {
@@ -1771,16 +1871,16 @@ svn_client_mergeinfo_log(svn_boolean_t f
       svn_mergeinfo_t merged;
       const char *subtree_path = svn__apr_hash_index_key(hi_catalog);
       svn_boolean_t is_subtree = strcmp(subtree_path,
-                                        path_or_url_repos_rel) != 0;
+                                        target_repos_rel) != 0;
       svn_pool_clear(iterpool);
 
       if (is_subtree)
         {
-          /* If SUBTREE_PATH is a proper subtree of PATH_OR_URL then make
+          /* If SUBTREE_PATH is a proper subtree of TARGET_PATH_OR_URL then make
              a copy of SOURCE_HISTORY that is path adjusted for the
              subtree.  */
           const char *subtree_rel_path =
-            subtree_path + strlen(path_or_url_repos_rel) + 1;
+            subtree_path + strlen(target_repos_rel) + 1;
 
           SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
             &subtree_source_history, source_history,
@@ -1788,14 +1888,14 @@ svn_client_mergeinfo_log(svn_boolean_t f
 
           if (!finding_merged)
             SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
-                    &subtree_history, path_or_url_history,
+                    &subtree_history, target_history,
                     subtree_rel_path, scratch_pool, scratch_pool));
         }
       else
         {
           subtree_source_history = source_history;
           if (!finding_merged)
-            subtree_history = path_or_url_history;
+            subtree_history = target_history;
         }
 
       if (!finding_merged)
@@ -1804,7 +1904,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
           SVN_ERR(svn_mergeinfo_intersect2(&merged_via_history,
                                            subtree_history,
                                            subtree_source_history, TRUE,
-                                           scratch_pool, scratch_pool));
+                                           scratch_pool, iterpool));
           SVN_ERR(svn_mergeinfo_merge(subtree_mergeinfo,
                                       merged_via_history,
                                       scratch_pool));
@@ -1839,16 +1939,20 @@ svn_client_mergeinfo_log(svn_boolean_t f
          subtrees. */
       if (apr_hash_count(merged_noninheritable))
         {
+          apr_pool_t *iterpool2 = svn_pool_create(iterpool);
+
           for (hi = apr_hash_first(iterpool, merged_noninheritable);
                hi;
                hi = apr_hash_next(hi))
             {
               apr_array_header_t *list = svn__apr_hash_index_val(hi);
-              SVN_ERR(svn_rangelist_merge(
-                &master_noninheritable_rangelist,
+              svn_pool_clear(iterpool2);
+              SVN_ERR(svn_rangelist_merge2(
+                master_noninheritable_rangelist,
                 svn_rangelist_dup(list, scratch_pool),
-                scratch_pool));
+                scratch_pool, iterpool2));
             }
+          svn_pool_destroy(iterpool2);
         }
 
       /* Find the intersection of the inheritable part of TGT_MERGEINFO
@@ -1866,6 +1970,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
              to SUBTREE_PATH. */
           apr_array_header_t *subtree_merged_rangelist =
             apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
+          apr_pool_t *iterpool2 = svn_pool_create(iterpool);
 
           for (hi = apr_hash_first(iterpool, merged);
                hi;
@@ -1873,15 +1978,17 @@ svn_client_mergeinfo_log(svn_boolean_t f
             {
               apr_array_header_t *list = svn__apr_hash_index_val(hi);
 
-              SVN_ERR(svn_rangelist_merge(&master_inheritable_rangelist,
-                                          svn_rangelist_dup(list,
-                                                            scratch_pool),
-                                          scratch_pool));
-              SVN_ERR(svn_rangelist_merge(&subtree_merged_rangelist,
-                                          svn_rangelist_dup(list,
+              svn_pool_clear(iterpool2);
+              SVN_ERR(svn_rangelist_merge2(master_inheritable_rangelist,
+                                           svn_rangelist_dup(list,
+                                                             scratch_pool),
+                                           scratch_pool, iterpool2));
+              SVN_ERR(svn_rangelist_merge2(subtree_merged_rangelist,
+                                           svn_rangelist_dup(list,
                                                             scratch_pool),
-                                          scratch_pool));
+                                           scratch_pool, iterpool2));
             }
+          svn_pool_destroy(iterpool2);
 
           apr_hash_set(inheritable_subtree_merges,
                        apr_pstrdup(scratch_pool, subtree_path),
@@ -1903,7 +2010,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
   /* Make sure every range in MASTER_INHERITABLE_RANGELIST is fully merged to
      each subtree (including the target itself).  Any revisions which don't
      exist in *every* subtree are *potentially* only partially merged to the
-     tree rooted at PATH_OR_URL, so move those revisions to
+     tree rooted at TARGET_PATH_OR_URL, so move those revisions to
      MASTER_NONINHERITABLE_RANGELIST.  It may turn out that that a revision
      was merged to the only subtree it affects, but we need to examine the
      logs to make this determination (which will be done by
@@ -1929,9 +2036,9 @@ svn_client_mergeinfo_log(svn_boolean_t f
           if (deleted_rangelist->nelts)
             {
               svn_rangelist__set_inheritance(deleted_rangelist, FALSE);
-              SVN_ERR(svn_rangelist_merge(&master_noninheritable_rangelist,
-                                          deleted_rangelist,
-                                          scratch_pool));
+              SVN_ERR(svn_rangelist_merge2(master_noninheritable_rangelist,
+                                           deleted_rangelist,
+                                           scratch_pool, iterpool));
               SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
                                            deleted_rangelist,
                                            master_inheritable_rangelist,
@@ -1944,9 +2051,9 @@ svn_client_mergeinfo_log(svn_boolean_t f
   if (finding_merged)
     {
       /* Roll all the merged revisions into one rangelist. */
-      SVN_ERR(svn_rangelist_merge(&master_inheritable_rangelist,
-                                  master_noninheritable_rangelist,
-                                  scratch_pool));
+      SVN_ERR(svn_rangelist_merge2(master_inheritable_rangelist,
+                                   master_noninheritable_rangelist,
+                                   scratch_pool, scratch_pool));
 
     }
   else
@@ -1962,9 +2069,10 @@ svn_client_mergeinfo_log(svn_boolean_t f
           apr_array_header_t *subtree_merged_rangelist =
             svn__apr_hash_index_val(hi);
 
-          SVN_ERR(svn_rangelist_merge(&source_master_rangelist,
-                                      subtree_merged_rangelist,
-                                      iterpool));
+          svn_pool_clear(iterpool);
+          SVN_ERR(svn_rangelist_merge2(source_master_rangelist,
+                                       subtree_merged_rangelist,
+                                       scratch_pool, iterpool));
         }
 
       /* From what might be eligible subtract what we know is partially merged
@@ -1973,9 +2081,9 @@ svn_client_mergeinfo_log(svn_boolean_t f
                                    master_noninheritable_rangelist,
                                    source_master_rangelist,
                                    FALSE, scratch_pool));
-      SVN_ERR(svn_rangelist_merge(&source_master_rangelist,
-                                  master_noninheritable_rangelist,
-                                  scratch_pool));
+      SVN_ERR(svn_rangelist_merge2(source_master_rangelist,
+                                   master_noninheritable_rangelist,
+                                   scratch_pool, scratch_pool));
       SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
                                    master_inheritable_rangelist,
                                    source_master_rangelist,
@@ -2036,9 +2144,9 @@ svn_client_mergeinfo_log(svn_boolean_t f
   SVN_ERR(logs_for_mergeinfo_rangelist(log_target, merge_source_paths,
                                        finding_merged,
                                        master_inheritable_rangelist,
-                                       path_or_url_mergeinfo_cat,
+                                       target_mergeinfo_cat,
                                        svn_dirent_join("/",
-                                                       path_or_url_repos_rel,
+                                                       target_repos_rel,
                                                        scratch_pool),
                                        discover_changed_paths,
                                        revprops,

Modified: subversion/branches/svn_mutex/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/svn_mutex/subversion/libsvn_client/mergeinfo.h?rev=1182053&r1=1182052&r2=1182053&view=diff
==============================================================================
--- subversion/branches/svn_mutex/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/svn_mutex/subversion/libsvn_client/mergeinfo.h Tue Oct 11 19:52:34 2011
@@ -49,7 +49,7 @@ typedef struct svn_client__merge_path_t
                                         probably due to authz
                                         restrictions. */
 
-  svn_boolean_t child_of_noninheritable; /* ABSPATH has no explict mergeinfo
+  svn_boolean_t child_of_noninheritable; /* ABSPATH has no explicit mergeinfo
                                             itself but is the child of a
                                             path with noniheritable
                                             mergeinfo. */
@@ -69,7 +69,7 @@ typedef struct svn_client__merge_path_t
                                            May be NULL. */
   svn_mergeinfo_t implicit_mergeinfo;   /* Implicit mergeinfo on ABSPATH
                                            prior to a merge.  May be NULL. */
-  svn_boolean_t indirect_mergeinfo;     /* Whether PRE_MERGE_MERGEINFO was
+  svn_boolean_t inherited_mergeinfo;    /* Whether PRE_MERGE_MERGEINFO was
                                            explicit or inherited. */
   svn_boolean_t scheduled_for_deletion; /* ABSPATH is scheduled for
                                            deletion. */
@@ -99,7 +99,7 @@ svn_client__merge_path_dup(const svn_cli
    the range of the parent's last committed revision to the parent's base
    revision (inclusive) or is LOCAL_ABSPATH is a local addition.  If asking
    for the inherited mergeinfo of an added path (i.e. one with no base
-   revision), that path may inherit mergeinfo from it's nearest parent
+   revision), that path may inherit mergeinfo from its nearest parent
    with a base revision and explicit mergeinfo.
 
    INHERIT indicates whether explicit, explicit or inherited, or only
@@ -166,19 +166,7 @@ svn_client__get_wc_mergeinfo_catalog(svn
 
    If there is no mergeinfo available for REL_PATH, or if the server
    doesn't support a mergeinfo capability and SQUELCH_INCAPABLE is
-   TRUE, set *TARGET_MERGEINFO to NULL.
-
-   If the *TARGET_MERGEINFO for REL_PATH path is inherited and
-   *VALIDATE_INHERITED_MERGEINFO is TRUE, then *TARGET_MERGEINFO
-   will only contain merge source path-revisions that actually
-   exist in repository.
-
-   If the *TARGET_MERGEINFO for REL_PATH path is inherited,
-   VALIDATE_INHERITED_MERGEINFO is TRUE, and the server supports
-   the #SVN_RA_CAPABILITY_VALIDATE_INHERITED_MERGEINFO capability,
-   then request that the server validate the mergeinfo in
-   *TARGET_MERGEINFO, so it contains only merge source path-revisions
-   that actually exist in repository. */
+   TRUE, set *TARGET_MERGEINFO to NULL. */
 svn_error_t *
 svn_client__get_repos_mergeinfo(svn_ra_session_t *ra_session,
                                 svn_mergeinfo_t *target_mergeinfo,
@@ -186,13 +174,12 @@ svn_client__get_repos_mergeinfo(svn_ra_s
                                 svn_revnum_t rev,
                                 svn_mergeinfo_inheritance_t inherit,
                                 svn_boolean_t squelch_incapable,
-                                svn_boolean_t validate_inherited_mergeinfo,
                                 apr_pool_t *pool);
 
 /* If INCLUDE_DESCENDANTS is FALSE, behave exactly like
    svn_client__get_repos_mergeinfo() except the mergeinfo for REL_PATH
    is put in the mergeinfo catalog MERGEINFO_CAT, with the key being
-   REL_PATH itself.
+   the repository root-relative path of REL_PATH.
 
    If INCLUDE_DESCENDANTS is true, then any subtrees under REL_PATH
    with explicit mergeinfo are also included in MERGEINFO_CAT.  The
@@ -200,17 +187,15 @@ svn_client__get_repos_mergeinfo(svn_ra_s
    paths of the subtrees.  If no mergeinfo is found, then
    *TARGET_MERGEINFO_CAT is set to NULL. */
 svn_error_t *
-svn_client__get_repos_mergeinfo_catalog(
-  svn_mergeinfo_catalog_t *mergeinfo_cat,
-  svn_ra_session_t *ra_session,
-  const char *rel_path,
-  svn_revnum_t rev,
-  svn_mergeinfo_inheritance_t inherit,
-  svn_boolean_t squelch_incapable,
-  svn_boolean_t include_descendants,
-  svn_boolean_t validate_inherited_mergeinfo,
-  apr_pool_t *result_pool,
-  apr_pool_t *scratch_pool);
+svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
+                                        svn_ra_session_t *ra_session,
+                                        const char *rel_path,
+                                        svn_revnum_t rev,
+                                        svn_mergeinfo_inheritance_t inherit,
+                                        svn_boolean_t squelch_incapable,
+                                        svn_boolean_t include_descendants,
+                                        apr_pool_t *result_pool,
+                                        apr_pool_t *scratch_pool);
 
 /* Retrieve the direct mergeinfo for the TARGET_WCPATH from the WC's
    mergeinfo prop, or that inherited from its nearest ancestor if the
@@ -233,12 +218,17 @@ svn_client__get_repos_mergeinfo_catalog(
    INHERIT indicates whether explicit, explicit or inherited, or only
    inherited mergeinfo for TARGET_WCPATH is retrieved.
 
+   If FROM_REPOS is not NULL, then set *FROM_REPOS to true if
+   *TARGET_MERGEINFO is inherited and the repository was contacted to
+   obtain it.  Set *FROM_REPOS to false otherwise.
+
    If TARGET_WCPATH inherited its mergeinfo from a working copy ancestor
-   or if it was obtained from the repository, set *INDIRECT to TRUE, set it
-   to FALSE *otherwise. */
+   or if it was obtained from the repository, set *INHERITED to TRUE, set it
+   to FALSE otherwise. */
 svn_error_t *
 svn_client__get_wc_or_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
-                                      svn_boolean_t *indirect,
+                                      svn_boolean_t *inherited,
+                                      svn_boolean_t *from_repos,
                                       svn_boolean_t repos_only,
                                       svn_mergeinfo_inheritance_t inherit,
                                       svn_ra_session_t *ra_session,
@@ -252,6 +242,10 @@ svn_client__get_wc_or_repos_mergeinfo(sv
    TARGET_MERGEINFO_CATALOG, mapped from TARGET_WCPATH's repository
    root-relative path.
 
+   IGNORE_INVALID_MERGEINFO behaves as per the argument of the same
+   name to svn_client__get_wc_mergeinfo().  It is applicable only if
+   the mergeinfo for TARGET_WCPATH is obtained from the working copy.
+
    If INCLUDE_DESCENDANTS is true, then any subtrees under
    TARGET_WCPATH with explicit mergeinfo are also included in
    TARGET_MERGEINFO_CATALOG and again the keys are the repository
@@ -260,7 +254,8 @@ svn_client__get_wc_or_repos_mergeinfo(sv
 svn_error_t *
 svn_client__get_wc_or_repos_mergeinfo_catalog(
   svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
-  svn_boolean_t *indirect,
+  svn_boolean_t *inherited,
+  svn_boolean_t *from_repos,
   svn_boolean_t include_descendants,
   svn_boolean_t repos_only,
   svn_boolean_t ignore_invalid_mergeinfo,
@@ -272,20 +267,19 @@ svn_client__get_wc_or_repos_mergeinfo_ca
   apr_pool_t *scratch_pool);
 
 /* Set *MERGEINFO_P to a mergeinfo constructed solely from the
-   natural history of PATH_OR_URL@PEG_REVISION.  RA_SESSION is an RA
-   session whose session URL maps to PATH_OR_URL's URL, or NULL.
+   natural history of RA_SESSION's session URL at PEG_REVNUM.
+
    If RANGE_YOUNGEST and RANGE_OLDEST are valid, use them to bound the
-   revision ranges of returned mergeinfo.  See svn_ra_get_location_segments()
-   for the rules governing PEG_REVISION, START_REVISION, and END_REVISION.
+   revision ranges of returned mergeinfo.  They are governed by the same
+   rules as the PEG_REVISION, START_REV, and END_REV parameters of
+   svn_ra_get_location_segments().
 
    If HAS_REV_ZERO_HISTORY is not NULL, then set *HAS_REV_ZERO_HISTORY to
-   TRUE if the natural history of PATH_OR_URL@PEG_REVISION includes
-   revision 0.  Set *HAS_REV_ZERO_HISTORY to FALSE otherwise. */
+   TRUE if the natural history includes revision 0, else to FALSE. */
 svn_error_t *
 svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p,
                                      svn_boolean_t *has_rev_zero_history,
-                                     const char *path_or_url,
-                                     const svn_opt_revision_t *peg_revision,
+                                     svn_revnum_t peg_revnum,
                                      svn_revnum_t range_youngest,
                                      svn_revnum_t range_oldest,
                                      svn_ra_session_t *ra_session,
@@ -305,7 +299,7 @@ svn_client__parse_mergeinfo(svn_mergeinf
 /* Write MERGEINFO into the WC for LOCAL_ABSPATH.  If MERGEINFO is NULL,
    remove any SVN_PROP_MERGEINFO for LOCAL_ABSPATH.  If MERGEINFO is empty,
    record an empty property value (e.g. "").  If CTX->NOTIFY_FUNC2 is
-   not null call it with notification type svn_wc_notify_update_update
+   not null call it with notification type svn_wc_notify_merge_record_info
    if DO_NOTIFICATION is true.
 
    Use WC_CTX to access the working copy, and SCRATCH_POOL for any temporary

Modified: subversion/branches/svn_mutex/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn_mutex/subversion/libsvn_client/patch.c?rev=1182053&r1=1182052&r2=1182053&view=diff
==============================================================================
--- subversion/branches/svn_mutex/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/svn_mutex/subversion/libsvn_client/patch.c Tue Oct 11 19:52:34 2011
@@ -365,7 +365,7 @@ obtain_eol_and_keywords_for_file(apr_has
 static svn_error_t *
 resolve_target_path(patch_target_t *target,
                     const char *path_from_patchfile,
-                    const char *local_abspath,
+                    const char *wcroot_abspath,
                     int strip_count,
                     svn_boolean_t prop_changes_only,
                     svn_wc_context_t *wc_ctx,
@@ -398,7 +398,8 @@ resolve_target_path(patch_target_t *targ
 
   if (svn_dirent_is_absolute(stripped_path))
     {
-      target->local_relpath = svn_dirent_is_child(local_abspath, stripped_path,
+      target->local_relpath = svn_dirent_is_child(wcroot_abspath,
+                                                  stripped_path,
                                                   result_pool);
 
       if (! target->local_relpath)
@@ -419,7 +420,7 @@ resolve_target_path(patch_target_t *targ
   /* Make sure the path is secure to use. We want the target to be inside
    * of the working copy and not be fooled by symlinks it might contain. */
   SVN_ERR(svn_dirent_is_under_root(&under_root,
-                                   &target->local_abspath, local_abspath,
+                                   &target->local_abspath, wcroot_abspath,
                                    target->local_relpath, result_pool));
 
   if (! under_root)
@@ -478,11 +479,32 @@ resolve_target_path(patch_target_t *targ
       return SVN_NO_ERROR;
     }
 
-  /* ### Shouldn't libsvn_wc flag an obstruction in this case? */
-  if (target->locally_deleted && target->kind_on_disk != svn_node_none)
+  if (target->locally_deleted)
     {
-      target->skipped = TRUE;
-      return SVN_NO_ERROR;
+      const char *moved_to_abspath;
+
+      SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
+                                          wc_ctx, target->local_abspath,
+                                          result_pool, scratch_pool));
+      if (moved_to_abspath)
+        {
+          target->local_abspath = moved_to_abspath;
+          target->local_relpath = svn_dirent_skip_ancestor(wcroot_abspath,
+                                                          moved_to_abspath);
+          SVN_ERR_ASSERT(target->local_relpath &&
+                         target->local_relpath[0] != '\0');
+
+          /* As far as we are concerned this target is not locally deleted. */
+          target->locally_deleted = FALSE;
+
+          SVN_ERR(svn_io_check_path(target->local_abspath,
+                                    &target->kind_on_disk, scratch_pool));
+        }
+      else if (target->kind_on_disk != svn_node_none)
+        {
+          target->skipped = TRUE;
+          return SVN_NO_ERROR;
+        }
     }
 
   return SVN_NO_ERROR;
@@ -828,7 +850,7 @@ choose_target_filename(const svn_patch_t
 static svn_error_t *
 init_patch_target(patch_target_t **patch_target,
                   const svn_patch_t *patch,
-                  const char *base_dir,
+                  const char *wcroot_abspath,
                   svn_wc_context_t *wc_ctx, int strip_count,
                   svn_boolean_t remove_tempfiles,
                   apr_pool_t *result_pool, apr_pool_t *scratch_pool)
@@ -873,7 +895,7 @@ init_patch_target(patch_target_t **patch
   target->prop_targets = apr_hash_make(result_pool);
 
   SVN_ERR(resolve_target_path(target, choose_target_filename(patch),
-                              base_dir, strip_count, prop_changes_only,
+                              wcroot_abspath, strip_count, prop_changes_only,
                               wc_ctx, result_pool, scratch_pool));
   if (! target->skipped)
     {
@@ -2333,20 +2355,15 @@ install_patched_target(patch_target_t *t
             {
               svn_stream_t *stream;
               svn_stream_t *patched_stream;
-              apr_file_t *file;
 
-              SVN_ERR(svn_io_file_open(&file, target->patched_path,
-                                       APR_READ | APR_BINARY, APR_OS_DEFAULT,
-                                       pool));
-
-              patched_stream = svn_stream_from_aprfile2(file, FALSE /* disown */,
-                                                pool);
+              SVN_ERR(svn_stream_open_readonly(&patched_stream,
+                                               target->patched_path,
+                                               pool, pool));
               SVN_ERR(svn_subst_create_specialfile(&stream,
                                                    target->local_abspath,
                                                    pool, pool));
               SVN_ERR(svn_stream_copy3(patched_stream, stream,
-                                       NULL, /* cancel_func */
-                                       NULL, /* cancel_baton */
+                                       ctx->cancel_func, ctx->cancel_baton,
                                        pool));
             }
           else
@@ -2476,8 +2493,15 @@ install_patched_prop_targets(patch_targe
           target->added = TRUE;
         }
 
-      /* Attempt to set the property, and reject all hunks if this fails. */
-      prop_val = svn_stringbuf__morph_into_string(prop_target->patched_value);
+      /* Attempt to set the property, and reject all hunks if this
+         fails.  If the property had a non-empty value, but now has
+         an empty one, we'll just delete the property altogether.  */
+      if (prop_target->value && prop_target->value->len
+          && prop_target->patched_value && !prop_target->patched_value->len)
+        prop_val = NULL;
+      else
+        prop_val = svn_stringbuf__morph_into_string(prop_target->patched_value);
+
       if (dry_run)
         {
           const svn_string_t *canon_propval;
@@ -2896,7 +2920,7 @@ apply_patches(void *baton,
                                                    btn->ctx, btn->dry_run,
                                                    iterpool));
 
-                  if (target->has_prop_changes)
+                  if (target->has_prop_changes && (!target->deleted))
                     SVN_ERR(install_patched_prop_targets(target, btn->ctx,
                                                          btn->dry_run,
                                                          iterpool));

Modified: subversion/branches/svn_mutex/subversion/libsvn_client/ra.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn_mutex/subversion/libsvn_client/ra.c?rev=1182053&r1=1182052&r2=1182053&view=diff
==============================================================================
--- subversion/branches/svn_mutex/subversion/libsvn_client/ra.c (original)
+++ subversion/branches/svn_mutex/subversion/libsvn_client/ra.c Tue Oct 11 19:52:34 2011
@@ -89,7 +89,8 @@ get_wc_prop(void *baton,
             apr_pool_t *pool)
 {
   callback_baton_t *cb = baton;
-  const char *local_abspath;
+  const char *local_abspath = NULL;
+  svn_error_t *err;
 
   *value = NULL;
 
@@ -101,29 +102,39 @@ get_wc_prop(void *baton,
       for (i = 0; i < cb->commit_items->nelts; i++)
         {
           svn_client_commit_item3_t *item
-            = APR_ARRAY_IDX(cb->commit_items, i,
-                            svn_client_commit_item3_t *);
+            = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *);
 
           if (! strcmp(relpath, item->session_relpath))
             {
               SVN_ERR_ASSERT(svn_dirent_is_absolute(item->path));
-              return svn_error_trace(svn_wc_prop_get2(value, cb->ctx->wc_ctx,
-                                                      item->path, name,
-                                                      pool, pool));
+              local_abspath = item->path;
+              break;
             }
         }
 
-      return SVN_NO_ERROR;
+      /* Commits can only query relpaths in the commit_items list
+         since the commit driver traverses paths as they are, or will
+         be, in the repository.  Non-commits query relpaths in the
+         working copy. */
+      if (! local_abspath)
+        return SVN_NO_ERROR;
     }
 
   /* If we don't have a base directory, then there are no properties. */
   else if (cb->base_dir_abspath == NULL)
     return SVN_NO_ERROR;
 
-  local_abspath = svn_dirent_join(cb->base_dir_abspath, relpath, pool);
+  else
+    local_abspath = svn_dirent_join(cb->base_dir_abspath, relpath, pool);
 
-  return svn_error_trace(svn_wc_prop_get2(value, cb->ctx->wc_ctx,
-                                          local_abspath, name, pool, pool));
+  err = svn_wc_prop_get2(value, cb->ctx->wc_ctx, local_abspath, name,
+                         pool, pool);
+  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+    {
+      svn_error_clear(err);
+      err = NULL;
+    }
+  return svn_error_trace(err);
 }
 
 /* This implements the 'svn_ra_push_wc_prop_func_t' interface. */
@@ -357,7 +368,7 @@ svn_client__open_ra_session_internal(svn
     }
 
   return SVN_NO_ERROR;
- }
+}
 #undef SVN_CLIENT__MAX_REDIRECT_ATTEMPTS
 
 
@@ -424,25 +435,76 @@ path_or_url_local_style(const char *path
   return svn_dirent_local_style(path_or_url, pool);
 }
 
+/* Given PATH_OR_URL, which contains either a working copy path or an
+   absolute URL, a peg revision PEG_REVISION, and a desired revision
+   REVISION, find the path at which that object exists in REVISION,
+   following copy history if necessary.  If REVISION is younger than
+   PEG_REVISION, then check that PATH_OR_URL is the same node in both
+   PEG_REVISION and REVISION, and return @c
+   SVN_ERR_CLIENT_UNRELATED_RESOURCES if it is not the same node.
+
+   If PEG_REVISION's kind is svn_opt_revision_unspecified, interpret it
+   as "head" for a URL or "working" for a working-copy path.
+
+   Store the actual revision number of the object in *REV_P, and the
+   final resulting URL in *URL_P.
+
+   Use authentication baton cached in CTX to authenticate against the
+   repository.
+
+   Use POOL for all allocations. */
+static svn_error_t *
+resolve_rev_and_url(svn_revnum_t *rev_p,
+                    const char **url_p,
+                    svn_ra_session_t *ra_session,
+                    const char *path_or_url,
+                    const svn_opt_revision_t *peg_revision,
+                    const svn_opt_revision_t *revision,
+                    svn_client_ctx_t *ctx,
+                    apr_pool_t *pool)
+{
+  svn_opt_revision_t peg_rev = *peg_revision;
+  svn_opt_revision_t start_rev = *revision;
+  svn_opt_revision_t *good_rev;
+  const char *url;
+  svn_revnum_t rev;
+
+  SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev,
+                                    svn_path_is_url(path_or_url),
+                                    TRUE,
+                                    pool));
+
+  /* Run the history function to get the object's (possibly
+     different) url in REVISION. */
+  SVN_ERR(svn_client__repos_locations(&url, &good_rev, NULL, NULL,
+                                      ra_session, path_or_url, &peg_rev,
+                                      &start_rev, NULL, ctx, pool));
+
+  /* Resolve good_rev into a real revnum. */
+  if (good_rev->kind == svn_opt_revision_unspecified)
+    good_rev->kind = svn_opt_revision_head;
+  SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx, url,
+                                          ra_session, good_rev, pool));
+  *rev_p = rev;
+  *url_p = url;
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_client__ra_session_from_path(svn_ra_session_t **ra_session_p,
                                  svn_revnum_t *rev_p,
                                  const char **url_p,
                                  const char *path_or_url,
                                  const char *base_dir_abspath,
-                                 const svn_opt_revision_t *peg_revision_p,
+                                 const svn_opt_revision_t *peg_revision,
                                  const svn_opt_revision_t *revision,
                                  svn_client_ctx_t *ctx,
                                  apr_pool_t *pool)
 {
   svn_ra_session_t *ra_session;
-  const char *initial_url, *url;
-  svn_opt_revision_t *good_rev;
-  svn_opt_revision_t peg_revision, start_rev;
-  svn_opt_revision_t dead_end_rev;
-  svn_opt_revision_t *ignored_rev;
-  svn_revnum_t rev;
-  const char *ignored_url, *corrected_url;
+  const char *initial_url;
+  const char *corrected_url;
 
   SVN_ERR(svn_client_url_from_path2(&initial_url, path_or_url, ctx, pool,
                                     pool));
@@ -450,13 +512,6 @@ svn_client__ra_session_from_path(svn_ra_
     return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
                              _("'%s' has no URL"), path_or_url);
 
-  start_rev = *revision;
-  peg_revision = *peg_revision_p;
-  SVN_ERR(svn_opt_resolve_revisions(&peg_revision, &start_rev,
-                                    svn_path_is_url(path_or_url),
-                                    TRUE,
-                                    pool));
-
   SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
                                                initial_url,
                                                base_dir_abspath, NULL,
@@ -468,30 +523,14 @@ svn_client__ra_session_from_path(svn_ra_
   if (corrected_url && svn_path_is_url(path_or_url))
     path_or_url = corrected_url;
 
-  dead_end_rev.kind = svn_opt_revision_unspecified;
-
-  /* Run the history function to get the object's (possibly
-     different) url in REVISION. */
-  SVN_ERR(svn_client__repos_locations(&url, &good_rev,
-                                      &ignored_url, &ignored_rev,
-                                      ra_session,
-                                      path_or_url, &peg_revision,
-                                      /* search range: */
-                                      &start_rev, &dead_end_rev,
-                                      ctx, pool));
+  SVN_ERR(resolve_rev_and_url(rev_p, url_p, ra_session,
+                              path_or_url, peg_revision, revision,
+                              ctx, pool));
 
   /* Make the session point to the real URL. */
-  SVN_ERR(svn_ra_reparent(ra_session, url, pool));
-
-  /* Resolve good_rev into a real revnum. */
-  if (good_rev->kind == svn_opt_revision_unspecified)
-    good_rev->kind = svn_opt_revision_head;
-  SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx, url,
-                                          ra_session, good_rev, pool));
+  SVN_ERR(svn_ra_reparent(ra_session, *url_p, pool));
 
   *ra_session_p = ra_session;
-  *rev_p = rev;
-  *url_p = url;
 
   return SVN_NO_ERROR;
 }
@@ -608,9 +647,17 @@ svn_client__repos_locations(const char *
       || start->kind == svn_opt_revision_unspecified)
     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
 
-  /* Check to see if this is schedule add with history working copy
-     path.  If it is, then we need to use the URL and peg revision of
-     the copyfrom information. */
+  if (end == NULL)
+    {
+      static const svn_opt_revision_t unspecified_rev
+        = { svn_opt_revision_unspecified, { 0 } };
+
+      end = &unspecified_rev;
+    }
+
+  /* Determine LOCAL_ABSPATH_OR_URL, URL, and possibly PEG_REVNUM.
+     If we are looking at the working version of a WC path that is scheduled
+     as a copy, then we need to use the copy-from URL and peg revision. */
   if (! svn_path_is_url(path))
     {
       SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, subpool));
@@ -758,6 +805,8 @@ svn_client__get_youngest_common_ancestor
                                          svn_client_ctx_t *ctx,
                                          apr_pool_t *pool)
 {
+  apr_pool_t *sesspool = svn_pool_create(pool);
+  svn_ra_session_t *session1, *session2;
   apr_hash_t *history1, *history2;
   apr_hash_index_t *hi;
   svn_revnum_t yc_revision = SVN_INVALID_REVNUM;
@@ -770,22 +819,39 @@ svn_client__get_youngest_common_ancestor
   revision1.value.number = rev1;
   revision2.value.number = rev2;
 
+  /* Open RA sessions for the two locations.
+   * ### TODO: As they are assumed to be in the same repository, we
+   * should share a single session, tracking the two URLs separately. */
+  {
+    svn_revnum_t peg;
+    const char *url;
+
+    SVN_ERR(svn_client__ra_session_from_path(&session1, &peg, &url,
+                                             path_or_url1, NULL,
+                                             &revision1, &revision1,
+                                             ctx, pool));
+    SVN_ERR(svn_client__ra_session_from_path(&session2, &peg, &url,
+                                             path_or_url2, NULL,
+                                             &revision2, &revision2,
+                                             ctx, pool));
+  }
+
   /* We're going to cheat and use history-as-mergeinfo because it
      saves us a bunch of annoying custom data comparisons and such. */
   SVN_ERR(svn_client__get_history_as_mergeinfo(&history1,
                                                &has_rev_zero_history1,
-                                               path_or_url1,
-                                               &revision1,
+                                               rev1,
                                                SVN_INVALID_REVNUM,
                                                SVN_INVALID_REVNUM,
-                                               NULL, ctx, pool));
+                                               session1, ctx, pool));
   SVN_ERR(svn_client__get_history_as_mergeinfo(&history2,
                                                &has_rev_zero_history2,
-                                               path_or_url2,
-                                               &revision2,
+                                               rev2,
                                                SVN_INVALID_REVNUM,
                                                SVN_INVALID_REVNUM,
-                                               NULL, ctx, pool));
+                                               session2, ctx, pool));
+
+  svn_pool_destroy(sesspool);
 
   /* Loop through the first location's history, check for overlapping
      paths and ranges in the second location's history, and

Modified: subversion/branches/svn_mutex/subversion/libsvn_client/relocate.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn_mutex/subversion/libsvn_client/relocate.c?rev=1182053&r1=1182052&r2=1182053&view=diff
==============================================================================
--- subversion/branches/svn_mutex/subversion/libsvn_client/relocate.c (original)
+++ subversion/branches/svn_mutex/subversion/libsvn_client/relocate.c Tue Oct 11 19:52:34 2011
@@ -139,6 +139,7 @@ relocate_externals(const char *local_abs
                    apr_array_header_t *ext_desc,
                    const char *old_parent_repos_root_url,
                    const char *new_parent_repos_root_url,
+                   svn_boolean_t ignore_externals,
                    svn_client_ctx_t *ctx,
                    apr_pool_t *scratch_pool)
 {
@@ -202,7 +203,7 @@ relocate_externals(const char *local_abs
         SVN_ERR(svn_client_relocate2(target_abspath,
                                      old_parent_repos_root_url,
                                      new_parent_repos_root_url,
-                                     TRUE, ctx, iterpool));
+                                     ignore_externals, ctx, iterpool));
     }
 
   svn_pool_destroy(iterpool);
@@ -284,7 +285,8 @@ svn_client_relocate2(const char *wcroot_
                                                   iterpool));
       if (ext_desc->nelts)
         SVN_ERR(relocate_externals(this_abspath, ext_desc, old_repos_root_url,
-                                   new_repos_root_url, ctx, iterpool));
+                                   new_repos_root_url, ignore_externals, ctx,
+                                   iterpool));
     }
 
   svn_pool_destroy(iterpool);