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/05/20 15:48:55 UTC

svn commit: r1125392 - in /subversion/branches/1.6.x: ./ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_repos/ subversion/libsvn_subr/ subversion/tests/cmdline/

Author: stsp
Date: Fri May 20 13:48:55 2011
New Revision: 1125392

URL: http://svn.apache.org/viewvc?rev=1125392&view=rev
Log:
Reintegrate the 1.6.x-issue3650 branch.

 * r1076730, r1078954, r1081255
   Fix issue #3650 "log -g produces excessive duplicate output"
   Justification:
     Eliminates potentially massive duplication when tracing logs
     across merge reintegrations.
   Branch:
     ^/subversion/branches/1.6.x-issue3650
   Votes:
     +1: pburba, stsp, sbutler

Modified:
    subversion/branches/1.6.x/   (props changed)
    subversion/branches/1.6.x/STATUS
    subversion/branches/1.6.x/subversion/include/private/svn_mergeinfo_private.h
    subversion/branches/1.6.x/subversion/include/svn_repos.h
    subversion/branches/1.6.x/subversion/libsvn_client/merge.c
    subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.c
    subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.h
    subversion/branches/1.6.x/subversion/libsvn_repos/log.c
    subversion/branches/1.6.x/subversion/libsvn_subr/mergeinfo.c
    subversion/branches/1.6.x/subversion/tests/cmdline/log_tests.py

Propchange: subversion/branches/1.6.x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri May 20 13:48:55 2011
@@ -21,6 +21,7 @@
 /subversion/branches/1.6.x-issue3641:1072041-1125381
 /subversion/branches/1.6.x-issue3646:957327-1002699
 /subversion/branches/1.6.x-issue3648:964172-1001805
+/subversion/branches/1.6.x-issue3650:1095508-1125384
 /subversion/branches/1.6.x-issue3651:952977-954985
 /subversion/branches/1.6.x-issue3654:953882-955338
 /subversion/branches/1.6.x-issue3683:965785-988062
@@ -117,4 +118,4 @@
 /subversion/branches/tc_url_rev:874351-874483
 /subversion/branches/tree-conflicts:868291-873154
 /subversion/branches/tree-conflicts-notify:873926-874008
-/subversion/trunk:875965,875968,876004,876012,876017,876019,876022,876024,876032,876041-876042,876048,876051,876055-876056,876059,876083,876091,876097,876101,876104,876109,876123-876125,876129,876132,876138,876160,876167,876175,876180,876185,876205,876223-876225,876230,876233,876245,876252,876256,876283,876287,876312,876326-876327,876330,876366,876372,876374,876376,876383,876386,876442,876456-876457,876462-876464,876467,876469,876480,876486,876495-876497,876516-876518,876524,876526,876583,876601,876614-876615,876628,876633,876641,876645,876659,876687,876689,876705,876715,876726,876760,876763,876794,876804,876815-876816,876821,876825,876837,876840-876841,876843,876849,876857-876858,876862,876873,876890,876897,876905,876908,876925,876931,876934,876948-876949,876953,876987,876993,877011,877014,877016,877028-877029,877038,877119,877127,877146,877157,877191,877195,877203,877211,877230,877234,877237,877243,877249,877259,877261,877304,877319,877407,877437,877441-877442,877453,87745
 9,877472,877544,877553,877565,877568,877573,877593,877595,877597,877601,877612,877665,877667,877681,877692,877696,877701,877720,877730,877784,877793,877797,877809,877814-877815,877819,877821,877842,877848,877853,877867,877869,877873,877901,877909,877916,877931,877942,877953,877964,877968,877970,877981-877982,878005,878013,878015,878020,878046,878053,878062,878074,878080,878089,878091,878093,878095,878127,878129,878131,878142,878173-878176,878216,878240,878242,878255,878269,878272,878279,878296-878297,878303,878321,878335,878338,878341,878343,878353,878364,878367-878368,878385,878399,878423,878426,878447,878462,878484,878491,878498,878532,878590,878595,878607,878625-878627,878646,878659,878673,878682-878683,878690-878691,878693,878723,878760-878761,878873,878875,878877,878879,878905,878910-878911,878915-878916,878924-878925,878946,878949,878955,878960,878970,878981,879001,879033,879056,879074,879076,879081-879082,879093,879105,879126,879148,879170,879198-879199,879201,879271,
 879293,879357,879375-879376,879403,879631,879635-879636,879688,879709-879711,879747,879902,879916,879954,879961,879966,879971,880027,880082,880095,880105,880146,880162,880226,880274-880275,880370,880450,880461,880474,880525-880526,880552,881905,884842,886164,886197,888715,888979,889081,889840,891672,892050,892085,895514,895653,896522,896915,898048,898963,899826,899828,900797,901304,901752,902093,902467,904301,904394,904594,905303,905326,906256,906305,906587,907644,908980-908981,917523,917640,918211,922516,923389,923391,926151,926167,927323,927328,931209,931211,931392,931568,932942,933299,934599,934603,935631,935992,935996,937610,939000,939002,939375-939376,944635,945350,946355,946767,947006,948512,948916,949307,950931,950933,951753,952992,953317,955369,957507,958024,959004,959760,961055,961970,962377-962378,964167,964349,964767,965405,965469,965508,979045,979429,980811,981449,981921,984565,984928,984931,991534,992114,996884,997026,997070,997457,997466,997471,997474,1000038,1
 000060,1000607,1000612,1001009,1002094,1005446,1022675,1024269,1027957,1028084,1028108,1028125,1031165,1031186,1032808,1033166,1033290,1033665,1033685,1033921,1034557,1035745,1036534,1036978,1037762,1038792,1039040,1041438,1041638,1051632,1051638,1051733,1051744-1051745,1051751,1051761,1051763,1051775,1051778,1051968,1051978,1051988,1052029,1052041,1052068,1053185,1053208,1053233,1053499,1053984,1058269,1058722,1063572-1063573,1063592,1063870,1063946,1064839,1066249,1066270,1066276,1068988,1070912,1071239,1071307,1072084,1072953,1074572,1076759,1076826,1084575,1084581,1084764,1084962,1084978,1086222,1094692,1095654,1098608,1102803,1103665,1104309
+/subversion/trunk:875965,875968,876004,876012,876017,876019,876022,876024,876032,876041-876042,876048,876051,876055-876056,876059,876083,876091,876097,876101,876104,876109,876123-876125,876129,876132,876138,876160,876167,876175,876180,876185,876205,876223-876225,876230,876233,876245,876252,876256,876283,876287,876312,876326-876327,876330,876366,876372,876374,876376,876383,876386,876442,876456-876457,876462-876464,876467,876469,876480,876486,876495-876497,876516-876518,876524,876526,876583,876601,876614-876615,876628,876633,876641,876645,876659,876687,876689,876705,876715,876726,876760,876763,876794,876804,876815-876816,876821,876825,876837,876840-876841,876843,876849,876857-876858,876862,876873,876890,876897,876905,876908,876925,876931,876934,876948-876949,876953,876987,876993,877011,877014,877016,877028-877029,877038,877119,877127,877146,877157,877191,877195,877203,877211,877230,877234,877237,877243,877249,877259,877261,877304,877319,877407,877437,877441-877442,877453,87745
 9,877472,877544,877553,877565,877568,877573,877593,877595,877597,877601,877612,877665,877667,877681,877692,877696,877701,877720,877730,877784,877793,877797,877809,877814-877815,877819,877821,877842,877848,877853,877867,877869,877873,877901,877909,877916,877931,877942,877953,877964,877968,877970,877981-877982,878005,878013,878015,878020,878046,878053,878062,878074,878080,878089,878091,878093,878095,878127,878129,878131,878142,878173-878176,878216,878240,878242,878255,878269,878272,878279,878296-878297,878303,878321,878335,878338,878341,878343,878353,878364,878367-878368,878385,878399,878423,878426,878447,878462,878484,878491,878498,878532,878590,878595,878607,878625-878627,878646,878659,878673,878682-878683,878690-878691,878693,878723,878760-878761,878873,878875,878877,878879,878905,878910-878911,878915-878916,878924-878925,878946,878949,878955,878960,878970,878981,879001,879033,879056,879074,879076,879081-879082,879093,879105,879126,879148,879170,879198-879199,879201,879271,
 879293,879357,879375-879376,879403,879631,879635-879636,879688,879709-879711,879747,879902,879916,879954,879961,879966,879971,880027,880082,880095,880105,880146,880162,880226,880274-880275,880370,880450,880461,880474,880525-880526,880552,881905,884842,886164,886197,888715,888979,889081,889840,891672,892050,892085,895514,895653,896522,896915,898048,898963,899826,899828,900797,901304,901752,902093,902467,904301,904394,904594,905303,905326,906256,906305,906587,907644,908980-908981,917523,917640,918211,922516,923389,923391,926151,926167,927323,927328,931209,931211,931392,931568,932942,933299,934599,934603,935631,935992,935996,937610,939000,939002,939375-939376,944635,945350,946355,946767,947006,948512,948916,949307,950931,950933,951753,952992,953317,955369,957507,958024,959004,959760,961055,961970,962377-962378,964167,964349,964767,965405,965469,965508,979045,979429,980811,981449,981921,984565,984928,984931,991534,992114,996884,997026,997070,997457,997466,997471,997474,1000038,1
 000060,1000607,1000612,1001009,1002094,1005446,1022675,1024269,1027957,1028084,1028108,1028125,1031165,1031186,1032808,1033166,1033290,1033665,1033685,1033921,1034557,1035745,1036534,1036978,1037762,1038792,1039040,1041438,1041638,1051632,1051638,1051733,1051744-1051745,1051751,1051761,1051763,1051775,1051778,1051968,1051978,1051988,1052029,1052041,1052068,1053185,1053208,1053233,1053499,1053984,1058269,1058722,1063572-1063573,1063592,1063870,1063946,1064839,1066249,1066270,1066276,1068988,1070912,1071239,1071307,1072084,1072953,1074572,1076730,1076759,1076826,1078954,1081255,1084575,1084581,1084764,1084962,1084978,1086222,1094692,1095654,1098608,1102803,1103665,1104309

Modified: subversion/branches/1.6.x/STATUS
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/STATUS?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/STATUS (original)
+++ subversion/branches/1.6.x/STATUS Fri May 20 13:48:55 2011
@@ -186,12 +186,3 @@ Veto-blocked changes:
 Approved changes:
 =================
 
- * r1076730, r1078954, r1081255
-   Fix issue #3650 "log -g produces excessive duplicate output"
-   Justification:
-     Eliminates potentially massive duplication when tracing logs
-     across merge reintegrations.
-   Branch:
-     ^/subversion/branches/1.6.x-issue3650
-   Votes:
-     +1: pburba, stsp, sbutler

Modified: subversion/branches/1.6.x/subversion/include/private/svn_mergeinfo_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/subversion/include/private/svn_mergeinfo_private.h?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/subversion/include/private/svn_mergeinfo_private.h (original)
+++ subversion/branches/1.6.x/subversion/include/private/svn_mergeinfo_private.h Fri May 20 13:48:55 2011
@@ -209,6 +209,14 @@ svn_mergeinfo__intersect2(svn_mergeinfo_
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool);
 
+/* Translates an array SEGMENTS (of svn_location_t *), like the one
+   returned from svn_client__repos_location_segments, into a mergeinfo
+   *MERGEINFO_P, allocated in POOL. */
+svn_error_t *
+svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
+                                       const apr_array_header_t *segments,
+                                       apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/1.6.x/subversion/include/svn_repos.h
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/subversion/include/svn_repos.h?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/subversion/include/svn_repos.h (original)
+++ subversion/branches/1.6.x/subversion/include/svn_repos.h Fri May 20 13:48:55 2011
@@ -1377,7 +1377,10 @@ svn_repos_node_location_segments(svn_rep
  * not be traversed while harvesting revision logs for each path.
  *
  * If @a include_merged_revisions is set, log information for revisions
- * which have been merged to @a targets will also be returned.
+ * which have been merged to @a paths will also be returned, unless these
+ * revisions are already part of @a start to @a end in @a repos's
+ * filesystem, as limted by @a paths. In the latter case those revisions
+ * are skipped and @a receiver is not invoked.
  *
  * If @a revprops is NULL, retrieve all revprops; else, retrieve only the
  * revprops named in the array (i.e. retrieve none if the array is empty).

Modified: subversion/branches/1.6.x/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/subversion/libsvn_client/merge.c?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/1.6.x/subversion/libsvn_client/merge.c Fri May 20 13:48:55 2011
@@ -8175,9 +8175,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
         source_path_rel_to_session++;
 
       /* Convert this target path's natural history into mergeinfo. */
-      SVN_ERR(svn_client__mergeinfo_from_segments(&target_history_as_mergeinfo,
-                                                  segments,
-                                                  iterpool));
+      SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
+        &target_history_as_mergeinfo, segments, iterpool));
 
       /* Remove any target history that is also part of the source's history,
          i.e. their common ancestry.  By definition this has already been
@@ -8267,8 +8266,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
                                                   source_rev, source_rev,
                                                   SVN_INVALID_REVNUM,
                                                   ctx, iterpool));
-      SVN_ERR(svn_client__mergeinfo_from_segments(&source_history_as_mergeinfo,
-                                                  segments, iterpool));
+      SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
+        &source_history_as_mergeinfo, segments, iterpool));
       SVN_ERR(svn_mergeinfo_merge(source_mergeinfo,
                                   source_history_as_mergeinfo, iterpool));
 
@@ -8356,7 +8355,7 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
             {
               svn_mergeinfo_t explicit_source_target_history_intersection;
 
-              SVN_ERR(svn_client__mergeinfo_from_segments(
+              SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
                 &target_history_as_mergeinfo, segments, iterpool));
 
               /* If there is an intersection between the *explicit* mergeinfo
@@ -8391,7 +8390,7 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
                 target_rev,
                 SVN_INVALID_REVNUM,
                 ctx, iterpool));
-              SVN_ERR(svn_client__mergeinfo_from_segments(
+              SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
                 &source_history_as_mergeinfo, segments, iterpool));
               SVN_ERR(svn_mergeinfo_merge(source_mergeinfo,
                                           source_history_as_mergeinfo,

Modified: subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.c?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.c Fri May 20 13:48:55 2011
@@ -434,50 +434,6 @@ svn_client__get_wc_or_repos_mergeinfo(sv
 
 
 svn_error_t *
-svn_client__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
-                                    apr_array_header_t *segments,
-                                    apr_pool_t *pool)
-{
-  svn_mergeinfo_t mergeinfo = apr_hash_make(pool);
-  int i;
-
-  /* Translate location segments into merge sources and ranges. */
-  for (i = 0; i < segments->nelts; i++)
-    {
-      svn_location_segment_t *segment =
-        APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
-      apr_array_header_t *path_ranges;
-      svn_merge_range_t *range;
-      const char *source_path;
-
-      /* No path segment?  Skip it. */
-      if (! segment->path)
-        continue;
-
-      /* Prepend a leading slash to our path. */
-      source_path = apr_pstrcat(pool, "/", segment->path, NULL);
-
-      /* See if we already stored ranges for this path.  If not, make
-         a new list.  */
-      path_ranges = apr_hash_get(mergeinfo, source_path, APR_HASH_KEY_STRING);
-      if (! path_ranges)
-        path_ranges = apr_array_make(pool, 1, sizeof(range));
-
-      /* Build a merge range, push it onto the list of ranges, and for
-         good measure, (re)store it in the hash. */
-      range = apr_pcalloc(pool, sizeof(*range));
-      range->start = MAX(segment->range_start - 1, 0);
-      range->end = segment->range_end;
-      range->inheritable = TRUE;
-      APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range;
-      apr_hash_set(mergeinfo, source_path, APR_HASH_KEY_STRING, path_ranges);
-    }
-
-  *mergeinfo_p = mergeinfo;
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
 svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p,
                                      const char *path_or_url,
                                      const svn_opt_revision_t *peg_revision,
@@ -520,7 +476,7 @@ svn_client__get_history_as_mergeinfo(svn
                                               peg_revnum, range_youngest,
                                               range_oldest, ctx, pool));
 
-  SVN_ERR(svn_client__mergeinfo_from_segments(mergeinfo_p, segments, pool));
+  SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(mergeinfo_p, segments, pool));
 
   /* If we opened an RA session, ensure its closure. */
   if (sesspool)

Modified: subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.h?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/1.6.x/subversion/libsvn_client/mergeinfo.h Fri May 20 13:48:55 2011
@@ -182,14 +182,6 @@ svn_client__get_history_as_mergeinfo(svn
                                      svn_client_ctx_t *ctx,
                                      apr_pool_t *pool);
 
-/* Translates an array SEGMENTS (of svn_location_t *), like the one
-   returned from svn_client__repos_location_segments, into a mergeinfo
-   *MERGEINFO_P, allocated in POOL. */
-svn_error_t *
-svn_client__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
-                                    apr_array_header_t *segments,
-                                    apr_pool_t *pool);
-
 /* Parse any mergeinfo from the WCPATH's ENTRY and store it in
    MERGEINFO.  If PRISTINE is true parse the pristine mergeinfo,
    working otherwise. If no record of any mergeinfo exists, set

Modified: subversion/branches/1.6.x/subversion/libsvn_repos/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/subversion/libsvn_repos/log.c?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/subversion/libsvn_repos/log.c (original)
+++ subversion/branches/1.6.x/subversion/libsvn_repos/log.c Fri May 20 13:48:55 2011
@@ -32,6 +32,7 @@
 #include "svn_props.h"
 #include "svn_mergeinfo.h"
 #include "repos.h"
+#include "private/svn_mergeinfo_private.h"
 
 
 
@@ -983,22 +984,35 @@ fill_log_entry(svn_log_entry_t *log_entr
 }
 
 /* Send a log message for REV to RECEIVER with its RECEIVER_BATON.
- *
- * FS is used with REV to fetch the interesting history information,
- * such as changed paths, revprops, etc.
- *
- * The detect_changed function is used if either AUTHZ_READ_FUNC is
- * not NULL, or if DISCOVER_CHANGED_PATHS is TRUE.  See it for details.
- *
- * If DESCENDING_ORDER is true, send child messages in descending order.
- *
- * If REVPROPS is NULL, retrieve all revprops; else, retrieve only the
- * revprops named in the array (i.e. retrieve none if the array is empty).
- */
+
+   FS is used with REV to fetch the interesting history information,
+   such as changed paths, revprops, etc.
+
+   The detect_changed function is used if either AUTHZ_READ_FUNC is
+   not NULL, or if DISCOVER_CHANGED_PATHS is TRUE.  See it for details.
+
+   If DESCENDING_ORDER is true, send child messages in descending order.
+
+   If REVPROPS is NULL, retrieve all revprops; else, retrieve only the
+   revprops named in the array (i.e. retrieve none if the array is empty).
+
+   LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, and
+   NESTED_MERGES are as per the arguments of the same name to DO_LOGS.  If
+   HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
+   already represented in LOG_TARGET_HISTORY_AS_MERGEINFO, then don't send
+   the log message for REV.
+
+   If HANDLING_MERGED_REVISIONS is FALSE then ignore NESTED_MERGES.  Otherwise
+   if NESTED_MERGES is not NULL and REV is contained in it, then don't send
+   the log for REV, otherwise send it normally and add REV to
+   NESTED_MERGES. */
 static svn_error_t *
 send_log(svn_revnum_t rev,
          svn_fs_t *fs,
+         svn_mergeinfo_t log_target_history_as_mergeinfo,
+         apr_hash_t *nested_merges,
          svn_boolean_t discover_changed_paths,
+         svn_boolean_t handling_merged_revision,
          const apr_array_header_t *revprops,
          svn_boolean_t has_children,
          svn_log_entry_receiver_t receiver,
@@ -1008,15 +1022,132 @@ send_log(svn_revnum_t rev,
          apr_pool_t *pool)
 {
   svn_log_entry_t *log_entry;
+  /* Assume we want to send the log for REV. */
+  svn_boolean_t found_rev_of_interest = TRUE;
 
   log_entry = svn_log_entry_create(pool);
-  SVN_ERR(fill_log_entry(log_entry, rev, fs, discover_changed_paths,
+  SVN_ERR(fill_log_entry(log_entry, rev, fs,
+                         discover_changed_paths || handling_merged_revision,
                          revprops, authz_read_func, authz_read_baton,
                          pool));
   log_entry->has_children = has_children;
 
-  /* Send the entry to the receiver. */
-  return (*receiver)(receiver_baton, log_entry, pool);
+  /* Is REV a merged revision that is already part of
+     LOG_TARGET_HISTORY_AS_MERGEINFO?  If so then there is no
+     need to send it, since it already was (or will be) sent. */
+  if (handling_merged_revision
+      && log_entry->changed_paths2
+      && log_target_history_as_mergeinfo
+      && apr_hash_count(log_target_history_as_mergeinfo))
+    {
+      svn_boolean_t path_is_in_history = FALSE;
+      apr_hash_index_t *hi;
+      apr_pool_t *subpool = svn_pool_create(pool);
+
+      /* REV was merged in, but it might already be part of the log target's
+         natural history, so change our starting assumption. */
+      found_rev_of_interest = FALSE;
+
+      /* Look at each changed path in REV. */
+      for (hi = apr_hash_first(subpool, log_entry->changed_paths2);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          const void *key;
+          void *val;
+          const char *changed_path;
+          apr_hash_index_t *hi2;
+          apr_pool_t *inner_subpool = svn_pool_create(subpool);
+
+          apr_hash_this(hi, &key, NULL, &val);
+          changed_path = key;
+
+          for (hi2 = apr_hash_first(inner_subpool,
+                                    log_target_history_as_mergeinfo);
+               hi2;
+               hi2 = apr_hash_next(hi2))
+            {
+              const char *mergeinfo_path;
+              apr_array_header_t *rangelist;
+          
+              apr_hash_this(hi2, &key, NULL, &val);
+              mergeinfo_path = key;
+              rangelist = val;
+
+              if (svn_path_is_ancestor(mergeinfo_path,
+                                       changed_path))
+                {
+                  int i;
+
+                  for (i = 0; i < rangelist->nelts; i++)
+                    {
+                      svn_merge_range_t *range =
+                        APR_ARRAY_IDX(rangelist, i,
+                                      svn_merge_range_t *);
+                      if (rev > range->start && rev <= range->end)
+                        {
+                          path_is_in_history = TRUE;
+                          break;
+                        }
+                    }
+                }
+              if (path_is_in_history)
+                break;
+            }
+          svn_pool_destroy(inner_subpool);
+
+          if (!path_is_in_history)
+            {
+              /* If even one path in LOG_ENTRY->CHANGED_PATHS2 is not part of
+                 LOG_TARGET_HISTORY_AS_MERGEINFO, then we want to send the
+                 log for REV. */
+              found_rev_of_interest = TRUE;
+              break;
+            }
+        }
+      svn_pool_destroy(subpool);
+    }
+    
+  /* If we only got changed paths the sake of detecting redundant merged
+     revisions, then be sure we don't send that info to the receiver. */
+  if (!discover_changed_paths && handling_merged_revision)
+    log_entry->changed_paths = log_entry->changed_paths2 = NULL;
+
+  /* Send the entry to the receiver, unless it is a redundant merged
+     revision. */
+  if (found_rev_of_interest)
+    {
+      /* Is REV a merged revision we've already sent? */
+      if (nested_merges && handling_merged_revision)
+        {
+          svn_revnum_t *merged_rev = apr_hash_get(nested_merges, &rev,
+                                                  sizeof(svn_revnum_t *));
+
+          if (merged_rev)
+            {
+              /* We already sent REV. */
+              return SVN_NO_ERROR;
+            }
+          else
+            {
+              /* NESTED_REVS needs to last across all the send_log, do_logs,
+                 handle_merged_revisions() recursions, so use the pool it
+                 was created in at the top of the recursion. */
+              apr_pool_t *hash_pool = apr_hash_pool_get(nested_merges);
+              svn_revnum_t *long_lived_rev = apr_palloc(hash_pool,
+                                                        sizeof(svn_revnum_t));
+              *long_lived_rev = rev;
+              apr_hash_set(nested_merges, long_lived_rev,
+                           sizeof(svn_revnum_t *), long_lived_rev);
+            }
+        }
+
+      return (*receiver)(receiver_baton, log_entry, pool);
+    }
+  else
+    {
+      return SVN_NO_ERROR;
+    }
 }
 
 /* This controls how many history objects we keep open.  For any targets
@@ -1346,12 +1477,15 @@ combine_mergeinfo_path_lists(apr_array_h
 static svn_error_t *
 do_logs(svn_fs_t *fs,
         const apr_array_header_t *paths,
+        svn_mergeinfo_t log_target_history_as_mergeinfo,
+        apr_hash_t *nested_merges,
         svn_revnum_t hist_start,
         svn_revnum_t hist_end,
         int limit,
         svn_boolean_t discover_changed_paths,
         svn_boolean_t strict_node_history,
         svn_boolean_t include_merged_revisions,
+        svn_boolean_t handling_merged_revisions,
         svn_boolean_t ignore_missing_locations,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
@@ -1372,6 +1506,8 @@ do_logs(svn_fs_t *fs,
 static svn_error_t *
 handle_merged_revisions(svn_revnum_t rev,
                         svn_fs_t *fs,
+                        svn_mergeinfo_t log_target_history_as_mergeinfo,
+                        apr_hash_t *nested_merges,
                         svn_mergeinfo_t mergeinfo,
                         svn_boolean_t discover_changed_paths,
                         svn_boolean_t strict_node_history,
@@ -1401,9 +1537,11 @@ handle_merged_revisions(svn_revnum_t rev
         = APR_ARRAY_IDX(combined_list, i, struct path_list_range *);
 
       svn_pool_clear(iterpool);
-      SVN_ERR(do_logs(fs, pl_range->paths, pl_range->range.start,
-                      pl_range->range.end, 0, discover_changed_paths,
-                      strict_node_history, TRUE, TRUE, revprops, TRUE,
+      SVN_ERR(do_logs(fs, pl_range->paths, log_target_history_as_mergeinfo,
+                      nested_merges,
+                      pl_range->range.start, pl_range->range.end, 0,
+                      discover_changed_paths, strict_node_history,
+                      TRUE, TRUE, TRUE, revprops, TRUE,
                       receiver, receiver_baton, authz_read_func,
                       authz_read_baton, iterpool));
     }
@@ -1423,17 +1561,35 @@ handle_merged_revisions(svn_revnum_t rev
    If IGNORE_MISSING_LOCATIONS is set, don't treat requests for bogus
    repository locations as fatal -- just ignore them.
 
-   Other parameters are the same as svn_repos_get_logs4().
+   If LOG_TARGET_HISTORY_AS_MERGEINFO is not NULL then it contains mergeinfo
+   representing the history of PATHS between HIST_START and HIST_END.
+
+   If HANDLING_MERGED_REVISIONS is TRUE then this is a recursive call for
+   merged revisions, see INCLUDE_MERGED_REVISIONS argument to
+   svn_repos_get_logs4().
+
+   If NESTED_MERGES is not NULL then it is a hash of revisions (svn_revnum_t *
+   mapped to svn_revnum_t *) for logs that were previously sent.  On the first
+   call to do_logs it should always be NULL.  If INCLUDE_MERGED_REVISIONS is
+   TRUE, then NESTED_MERGES will be created on the first call to do_logs,
+   allocated in POOL.  It is then shared across
+   do_logs()/send_logs()/handle_merge_revisions() recursions, see also the
+   argument of the same name in send_logs().
+
+   All other parameters are the same as svn_repos_get_logs4().
  */
 static svn_error_t *
 do_logs(svn_fs_t *fs,
         const apr_array_header_t *paths,
+        svn_mergeinfo_t log_target_history_as_mergeinfo,
+        apr_hash_t *nested_merges,
         svn_revnum_t hist_start,
         svn_revnum_t hist_end,
         int limit,
         svn_boolean_t discover_changed_paths,
         svn_boolean_t strict_node_history,
         svn_boolean_t include_merged_revisions,
+        svn_boolean_t handling_merged_revisions,
         svn_boolean_t ignore_missing_locations,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
@@ -1444,6 +1600,7 @@ do_logs(svn_fs_t *fs,
         apr_pool_t *pool)
 {
   apr_pool_t *iterpool;
+  apr_pool_t *subpool = NULL;
   apr_array_header_t *revs = NULL;
   apr_hash_t *rev_mergeinfo = NULL;
   svn_revnum_t current;
@@ -1518,18 +1675,35 @@ do_logs(svn_fs_t *fs,
              in anyway). */
           if (descending_order)
             {
-              SVN_ERR(send_log(current, fs, discover_changed_paths,
-                               revprops, has_children, receiver, receiver_baton,
+              SVN_ERR(send_log(current, fs,
+                               log_target_history_as_mergeinfo, nested_merges,
+                               discover_changed_paths,
+                               handling_merged_revisions, revprops,
+                               has_children, receiver, receiver_baton,
                                authz_read_func, authz_read_baton, iterpool));
-              if (has_children)
+
+              if (has_children) /* Implies include_merged_revisions == TRUE */
                 {
-                  SVN_ERR(handle_merged_revisions(current, fs, mergeinfo,
-                                                  discover_changed_paths,
-                                                  strict_node_history, revprops,
-                                                  receiver, receiver_baton,
-                                                  authz_read_func,
-                                                  authz_read_baton,
-                                                  iterpool));
+                  if (!nested_merges)
+                    {
+                      /* We're at the start of the recursion stack, create a
+                         single hash to be shared across all of the merged
+                         recursions so we can track and squelch duplicates. */
+                      subpool = svn_pool_create(pool);
+                      nested_merges = apr_hash_make(subpool);
+                    }
+
+                  SVN_ERR(handle_merged_revisions(
+                    current, fs,
+                    log_target_history_as_mergeinfo, nested_merges,
+                    mergeinfo,
+                    discover_changed_paths,
+                    strict_node_history,
+                    revprops,
+                    receiver, receiver_baton,
+                    authz_read_func,
+                    authz_read_baton,
+                    iterpool));
                 }
               if (limit && ++send_count >= limit)
                 break;
@@ -1558,6 +1732,12 @@ do_logs(svn_fs_t *fs,
     }
   svn_pool_destroy(iterpool);
 
+  if (subpool)
+    {
+      nested_merges = NULL;
+      svn_pool_destroy(subpool);
+    }
+
   if (revs)
     {
       /* Work loop for processing the revisions we found since they wanted
@@ -1582,13 +1762,24 @@ do_logs(svn_fs_t *fs,
               has_children = (apr_hash_count(mergeinfo) > 0);
             }
 
-          SVN_ERR(send_log(current, fs,
-                           discover_changed_paths, revprops, has_children,
+          SVN_ERR(send_log(current, fs, log_target_history_as_mergeinfo,
+                           nested_merges,
+                           discover_changed_paths, handling_merged_revisions,
+                           revprops, has_children,
                            receiver, receiver_baton, authz_read_func,
                            authz_read_baton, iterpool));
           if (has_children)
             {
-              SVN_ERR(handle_merged_revisions(current, fs, mergeinfo,
+              if (!nested_merges)
+                {
+                  subpool = svn_pool_create(pool);
+                  nested_merges = apr_hash_make(subpool);
+                }
+
+              SVN_ERR(handle_merged_revisions(current, fs,
+                                              log_target_history_as_mergeinfo,
+                                              nested_merges,
+                                              mergeinfo,
                                               discover_changed_paths,
                                               strict_node_history, revprops,
                                               receiver, receiver_baton,
@@ -1605,6 +1796,91 @@ do_logs(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
+struct location_segment_baton
+{
+  apr_array_header_t *history_segments;
+  apr_pool_t *pool;
+};
+
+/* svn_location_segment_receiver_t implementation for svn_repos_get_logs4. */
+static svn_error_t *
+location_segment_receiver(svn_location_segment_t *segment,
+                          void *baton,
+                          apr_pool_t *pool)
+{
+  struct location_segment_baton *b = baton;
+
+  APR_ARRAY_PUSH(b->history_segments, svn_location_segment_t *) =
+    svn_location_segment_dup(segment, b->pool);
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Populate *PATHS_HISTORY_MERGEINFO with mergeinfo representing the combined
+   history of each path in PATHS between START_REV and END_REV in REPOS's
+   filesystem.  START_REV and END_REV must be valid revisions.  RESULT_POOL
+   is used to allocate *PATHS_HISTORY_MERGEINFO, SCRATCH_POOL is used for all
+   other (temporary) allocations.  Other parameters are the same as
+   svn_repos_get_logs4(). */
+static svn_error_t *
+get_paths_history_as_mergeinfo(svn_mergeinfo_t *paths_history_mergeinfo,
+                               svn_repos_t *repos,
+                               const apr_array_header_t *paths,
+                               svn_revnum_t start_rev,
+                               svn_revnum_t end_rev,
+                               svn_repos_authz_func_t authz_read_func,
+                               void *authz_read_baton,
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool)
+{
+  int i;
+  svn_mergeinfo_t path_history_mergeinfo;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start_rev));
+  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(end_rev));
+
+  /* Ensure START_REV is the oldest revision, as required by
+     svn_repos_node_location_segments, for which this is an iterative
+     wrapper. */
+  if (start_rev < end_rev)
+    {
+      svn_revnum_t tmp_rev = start_rev;
+      start_rev = end_rev;
+      end_rev = tmp_rev;
+    }
+
+  *paths_history_mergeinfo = apr_hash_make(result_pool);
+
+  for (i = 0; i < paths->nelts; i++)
+    {
+      const char *this_path = APR_ARRAY_IDX(paths, i, const char *);
+      struct location_segment_baton loc_seg_baton;
+   
+      svn_pool_clear(iterpool);
+      loc_seg_baton.pool = scratch_pool;
+      loc_seg_baton.history_segments =
+        apr_array_make(iterpool, 4, sizeof(svn_location_segment_t *));
+
+      SVN_ERR(svn_repos_node_location_segments(repos, this_path, start_rev,
+                                               start_rev, end_rev,
+                                               location_segment_receiver,
+                                               &loc_seg_baton,
+                                               authz_read_func,
+                                               authz_read_baton,
+                                               iterpool));
+
+      SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
+        &path_history_mergeinfo, loc_seg_baton.history_segments, iterpool));
+      SVN_ERR(svn_mergeinfo_merge(*paths_history_mergeinfo,
+                                  svn_mergeinfo_dup(path_history_mergeinfo,
+                                                    result_pool),
+                                  result_pool));
+    }
+  svn_pool_destroy(iterpool);
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_repos_get_logs4(svn_repos_t *repos,
@@ -1625,6 +1901,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
   svn_revnum_t head = SVN_INVALID_REVNUM;
   svn_fs_t *fs = repos->fs;
   svn_boolean_t descending_order;
+  svn_mergeinfo_t paths_history_mergeinfo = NULL;
 
   /* Setup log range. */
   SVN_ERR(svn_fs_youngest_rev(&head, fs, pool));
@@ -1684,7 +1961,8 @@ svn_repos_get_logs4(svn_repos_t *repos,
 
           if (descending_order)
             rev = end - i;
-          SVN_ERR(send_log(rev, fs, discover_changed_paths, revprops, FALSE,
+          SVN_ERR(send_log(rev, fs, NULL, NULL, discover_changed_paths, FALSE,
+                           revprops, FALSE,
                            receiver, receiver_baton, authz_read_func,
                            authz_read_baton, iterpool));
         }
@@ -1693,9 +1971,26 @@ svn_repos_get_logs4(svn_repos_t *repos,
       return SVN_NO_ERROR;
     }
 
-  return do_logs(repos->fs, paths, start, end, limit,
-                 discover_changed_paths, strict_node_history,
-                 include_merged_revisions, FALSE, revprops,
+  /* If we are including merged revisions, then create mergeinfo that
+     represents all of PATHS' history between START and END.  We will use
+     this later to squelch duplicate log revisions that might exist in
+     both natural history and merged-in history.  See
+     http://subversion.tigris.org/issues/show_bug.cgi?id=3650#desc5 */
+  if (include_merged_revisions)
+    {
+      apr_pool_t *subpool = svn_pool_create(pool);
+
+      SVN_ERR(get_paths_history_as_mergeinfo(&paths_history_mergeinfo,
+                                             repos, paths, start, end,
+                                             authz_read_func,
+                                             authz_read_baton,
+                                             pool, subpool));
+      svn_pool_destroy(subpool);
+    }
+
+  return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, start, end,
+                 limit, discover_changed_paths, strict_node_history,
+                 include_merged_revisions, FALSE, FALSE, revprops,
                  descending_order, receiver, receiver_baton,
                  authz_read_func, authz_read_baton, pool);
 }

Modified: subversion/branches/1.6.x/subversion/libsvn_subr/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/subversion/libsvn_subr/mergeinfo.c?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/branches/1.6.x/subversion/libsvn_subr/mergeinfo.c Fri May 20 13:48:55 2011
@@ -2019,3 +2019,48 @@ svn_mergeinfo__filter_mergeinfo_by_range
     }
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
+                                       const apr_array_header_t *segments,
+                                       apr_pool_t *pool)
+{
+  svn_mergeinfo_t mergeinfo = apr_hash_make(pool);
+  int i;
+
+  /* Translate location segments into merge sources and ranges. */
+  for (i = 0; i < segments->nelts; i++)
+    {
+      svn_location_segment_t *segment =
+        APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
+      apr_array_header_t *path_ranges;
+      svn_merge_range_t *range;
+      const char *source_path;
+
+      /* No path segment?  Skip it. */
+      if (! segment->path)
+        continue;
+
+      /* Prepend a leading slash to our path. */
+      source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL);
+
+      /* See if we already stored ranges for this path.  If not, make
+         a new list.  */
+      path_ranges = apr_hash_get(mergeinfo, source_path, APR_HASH_KEY_STRING);
+      if (! path_ranges)
+        path_ranges = apr_array_make(pool, 1, sizeof(range));
+
+      /* Build a merge range, push it onto the list of ranges, and for
+         good measure, (re)store it in the hash. */
+      range = apr_pcalloc(pool, sizeof(*range));
+      range->start = MAX(segment->range_start - 1, 0);
+      range->end = segment->range_end;
+      range->inheritable = TRUE;
+      APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range;
+      apr_hash_set(mergeinfo, source_path, APR_HASH_KEY_STRING, path_ranges);
+    }
+
+  *mergeinfo_p = mergeinfo;
+  return SVN_NO_ERROR;
+}
+

Modified: subversion/branches/1.6.x/subversion/tests/cmdline/log_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/1.6.x/subversion/tests/cmdline/log_tests.py?rev=1125392&r1=1125391&r2=1125392&view=diff
==============================================================================
--- subversion/branches/1.6.x/subversion/tests/cmdline/log_tests.py (original)
+++ subversion/branches/1.6.x/subversion/tests/cmdline/log_tests.py Fri May 20 13:48:55 2011
@@ -1692,6 +1692,106 @@ def log_of_local_copy(sbox):
                           "differs from that on move source '%s'"
                           % (psi_moved_path, psi_path))
 
+#----------------------------------------------------------------------
+
+def merge_sensitive_log_ignores_cyclic_merges(sbox):
+  "log -g should ignore cyclic merges"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  wc_disk, wc_status = set_up_branch(sbox)
+
+  A_path        = os.path.join(wc_dir, 'A')
+  X_path        = os.path.join(wc_dir, 'A', 'C', 'X')
+  kappa_path    = os.path.join(wc_dir, 'A', 'C', 'X', 'kappa')
+  chi_path      = os.path.join(wc_dir, 'A', 'D', 'H', 'chi')
+  A_COPY_path   = os.path.join(wc_dir, 'A_COPY')
+  mu_COPY_path  = os.path.join(wc_dir, 'A_COPY', 'mu')
+  tau_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'G', 'tau')
+  Z_COPY_path   = os.path.join(wc_dir, 'A_COPY', 'C', 'Z')
+  nu_COPY_path  = os.path.join(wc_dir, 'A_COPY', 'C', 'Z', 'nu')
+
+  # Make an edit on the "branch" to A_COPY/mu, commit as r7.
+  svntest.main.file_write(mu_COPY_path, "Branch edit.\n")
+  svntest.main.run_svn(None, 'ci', '-m', 'Branch edit', wc_dir)
+
+  # Make an edit on both the "trunk" and the "branch", commit as r8.
+  svntest.main.file_write(chi_path, "Trunk edit.\n")
+  svntest.main.file_write(tau_COPY_path, "Branch edit.\n")
+  svntest.main.run_svn(None, 'ci', '-m', 'Branch and trunk edits in one rev',
+                       wc_dir)
+
+  # Sync merge A to A_COPY, commit as r9
+  svntest.main.run_svn(None, 'up', wc_dir)
+  svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path)
+  svntest.main.run_svn(None, 'ci', '-m', 'Sync merge A to A_COPY', wc_dir)
+
+  # Reintegrate A_COPY to A, commit as r10
+  svntest.main.run_svn(None, 'up', wc_dir)
+  svntest.main.run_svn(None, 'merge', '--reintegrate',
+                       sbox.repo_url + '/A_COPY', A_path)
+  svntest.main.run_svn(None, 'ci', '-m', 'Reintegrate A_COPY to A', wc_dir)
+
+  # Do a --record-only merge of r10 from A to A_COPY, commit as r11.
+  # This will allow us to continue using the branch without deleting it.
+  svntest.main.run_svn(None, 'up', wc_dir)
+  svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path)
+  svntest.main.run_svn(None, 'ci', '-m',
+                       '--record-only merge r10 from A to A_COPY', wc_dir)
+
+  # Make an edit on the "branch"; add A_COPY/C and A_COPY/C/Z/nu,
+  # commit as r12.
+  svntest.main.run_svn(None, 'mkdir', Z_COPY_path)
+  svntest.main.file_write(nu_COPY_path, "A new branch file.\n")
+  svntest.main.run_svn(None, 'add', nu_COPY_path)
+  svntest.main.run_svn(None, 'ci', '-m', 'Branch edit: Add a subtree', wc_dir)
+
+  # Make an edit on the "trunk"; add A/C/X and A/C/X/kappa,
+  # commit as r13.
+  svntest.main.run_svn(None, 'mkdir', X_path)
+  svntest.main.file_write(kappa_path, "A new trunk file.\n")
+  svntest.main.run_svn(None, 'add', kappa_path)
+  svntest.main.run_svn(None, 'ci', '-m', 'Trunk edit: Add a subtree', wc_dir)
+  svntest.main.run_svn(None, 'up', wc_dir)
+
+  # Sync merge A to A_COPY, commit as r14
+  svntest.main.run_svn(None, 'up', wc_dir)
+  svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', A_COPY_path)
+  svntest.main.run_svn(None, 'ci', '-m', 'Sync merge A to A_COPY', wc_dir)
+
+  # Reintegrate A_COPY to A, commit as r15
+  svntest.main.run_svn(None, 'up', wc_dir)
+  svntest.main.run_svn(None, 'merge', '--reintegrate',
+                       sbox.repo_url + '/A_COPY', A_path)
+  svntest.main.run_svn(None, 'ci', '-m', '2nd reintegrate of A_COPY to A',
+                       wc_dir)
+
+  # Run 'svn log -g A'.  We expect to see r13, r10, r6, r5, r4, and r3 only
+  # once, as part of A's own history, not as merged in from A_COPY.
+  expected_merges = {
+    15 : [],
+    14 : [15],
+    13 : [],
+    12 : [15],
+    11 : [15],
+    10 : [],
+    9  : [15,11],
+    8  : [15,11,9],
+    7  : [15,11],
+    6  : [],
+    5  : [],    
+    4  : [],
+    3  : [],
+    2  : [15,11],
+    1  : [],
+  }
+  svntest.main.run_svn(None, 'up', wc_dir)
+  exit_code, out, err = svntest.actions.run_and_verify_svn(None, None, [],
+                                                           'log', '-g',
+                                                           A_path)
+  log_chain = parse_log_output(out)
+  check_merge_results(log_chain, expected_merges)
+
 ########################################################################
 # Run the tests
 
@@ -1733,6 +1833,8 @@ test_list = [ None,
               SkipUnless(merge_sensitive_log_propmod_merge_inheriting_path,
                          server_has_mergeinfo),
               log_of_local_copy,
+              SkipUnless(merge_sensitive_log_ignores_cyclic_merges,
+                         server_has_mergeinfo),
              ]
 
 if __name__ == '__main__':