Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h	(revision 1454520)
+++ subversion/include/svn_client.h	(working copy)
@@ -3978,6 +3978,9 @@
  * If a depth other than #svn_depth_empty or #svn_depth_infinity is
  * requested then return a #SVN_ERR_UNSUPPORTED_FEATURE error.
  *
+ * If @a limit is non-zero only invoke @a receiver on the first @a limit
+ * logs.
+ *
  * @a discover_changed_paths and @a revprops are the same as for
  * svn_client_log5().  Use @a scratch_pool for all temporary allocations.
  *
@@ -3998,6 +4001,7 @@
                           const svn_opt_revision_t *source_end_revision,
                           svn_log_entry_receiver_t receiver,
                           void *receiver_baton,
+                          int limit,
                           svn_boolean_t discover_changed_paths,
                           svn_depth_t depth,
                           const apr_array_header_t *revprops,
@@ -4006,7 +4010,8 @@
 
 /**
  * Similar to svn_client_mergeinfo_log2(), but with @a source_start_revision
- * and @a source_end_revision always of kind @c svn_opt_revision_unspecified;
+ * and @a source_end_revision always of kind @c svn_opt_revision_unspecified
+ * and @a limit always 0.
  *
  * @deprecated Provided for backwards compatibility with the 1.7 API.
  * @since New in 1.7.
Index: subversion/libsvn_client/deprecated.c
===================================================================
--- subversion/libsvn_client/deprecated.c	(revision 1454520)
+++ subversion/libsvn_client/deprecated.c	(working copy)
@@ -2868,7 +2868,7 @@
                                    target_path_or_url, target_peg_revision,
                                    source_path_or_url, source_peg_revision,
                                    &start_revision, &end_revision,
-                                   receiver, receiver_baton,
+                                   receiver, receiver_baton, 0,
                                    discover_changed_paths, depth, revprops,
                                    ctx, scratch_pool);
 }
Index: subversion/libsvn_client/merge.c
===================================================================
--- subversion/libsvn_client/merge.c	(revision 1454520)
+++ subversion/libsvn_client/merge.c	(working copy)
@@ -11826,10 +11826,23 @@
   return SVN_NO_ERROR;
 }
 
+/* Implements the svn_log_entry_receiver_t interface.
+
+  Set *BATON to LOG_ENTRY->revision. */
+static svn_error_t *
+operative_revs_receiver(void *baton,
+                        svn_log_entry_t *log_entry,
+                        apr_pool_t *pool)
+{
+  svn_revnum_t *oldest_eligible_rev = baton;
+
+  *oldest_eligible_rev = log_entry->revision;
+  return SVN_NO_ERROR;
+}
+
 /* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes
  * on SOURCE_BRANCH after YCA up to and including *BASE_P have already
- * been merged into the target branch -- or, specifically, are recorded in
- * TARGET_MERGEINFO.
+ * been merged into the target branch.
  *
  *               *BASE_P       TIP
  *          o-------o-----------o--- SOURCE_BRANCH
@@ -11864,6 +11877,7 @@
 find_last_merged_location(svn_client__pathrev_t **base_p,
                           svn_client__pathrev_t *yca,
                           const branch_history_t *source_branch,
+                          svn_client__pathrev_t *target,
                           svn_mergeinfo_t target_mergeinfo,
                           svn_client_ctx_t *ctx,
                           apr_pool_t *result_pool,
@@ -11890,6 +11904,8 @@
      inoperative merges to fill in mergeinfo gaps.
    */
   branch_history_t *eligible_locations;
+  svn_boolean_t no_prior_sync;
+  svn_mergeinfo_t eligible;
 
   /* Start with a list of all source locations after YCA up to the tip. */
   SVN_ERR(branch_history_intersect_range(
@@ -11899,9 +11915,17 @@
 
   /* Remove any locations that match (a), (b) or (c). */
   /* For (a), remove any locations that are in TARGET's mergeinfo. */
-  SVN_ERR(svn_mergeinfo_remove2(&eligible_locations->history,
+  SVN_ERR(svn_mergeinfo_remove2(&eligible,
                                 target_mergeinfo, eligible_locations->history,
                                 TRUE, scratch_pool, scratch_pool));
+
+  /* Has SOURCE_BRANCH ever been synced to TARGET_URL? */
+  SVN_ERR(svn_mergeinfo__equals(&no_prior_sync, eligible,
+                                eligible_locations->history, FALSE,
+                                scratch_pool));
+  if (!no_prior_sync)
+    eligible_locations->history = eligible;
+
   /* For (b) ... */
 
   /* For (c) ... */
@@ -11920,17 +11944,58 @@
 
       svn_client__pathrev_t *oldest_eligible;
       branch_history_t *contiguous_source;
+      svn_revnum_t adjusted_oldest_eligible_rev = SVN_INVALID_REVNUM;
+      svn_revnum_t oldest_eligible_rev;
+      svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
+        target_opt_rev;
 
+      /* Start with the assumption that the oldest eligible revision
+         is that dictated by the target's mergeinfo. */
       SVN_ERR(branch_history_get_endpoints(
                 &oldest_eligible, NULL,
                 eligible_locations, scratch_pool, scratch_pool));
+      oldest_eligible_rev = oldest_eligible->rev;
 
+      if (!no_prior_sync)
+        {
+          /* Possibly adjust the oldest eligible revision 'M', which we
+             calculated above based on a naive reading of TARGET_MERGEINFO,
+             to a younger revision 'N' iff all revisions in the range
+             (M+1):(N-1) would be inoperative if merged from SOURCE_BRANCH
+             to TARGET.  This may be because those revisions are simply
+             inoperative on SOURCE_BRANCH or they may have been fully merged
+             via a subtree merge. */
+          source_peg_rev.kind = svn_opt_revision_number;
+          source_peg_rev.value.number = source_branch->tip->rev;
+          source_start_rev.kind = svn_opt_revision_number;
+          source_start_rev.value.number = oldest_eligible->rev;
+          source_end_rev.kind = svn_opt_revision_number;
+          source_end_rev.value.number = source_branch->tip->rev;
+          target_opt_rev.kind = svn_opt_revision_number;
+          target_opt_rev.value.number = target->rev;
+          SVN_ERR(svn_client_mergeinfo_log2(FALSE, /* Find eligible */
+                                            target->url,
+                                            &target_opt_rev,
+                                            source_branch->tip->url,
+                                            &source_peg_rev,
+                                            &source_start_rev,
+                                            &source_end_rev,
+                                            operative_revs_receiver,
+                                            &adjusted_oldest_eligible_rev,
+                                            1, /* limit */
+                                            TRUE,
+                                            svn_depth_infinity, NULL, ctx,
+                                            scratch_pool));
+          if (SVN_IS_VALID_REVNUM(adjusted_oldest_eligible_rev))
+            oldest_eligible_rev = adjusted_oldest_eligible_rev;
+        }
+
       /* Find the branch location just before the oldest eligible rev.
        * (We can't just subtract 1 from the rev because the branch might
        * have a gap there.) */
       SVN_ERR(branch_history_intersect_range(
                 &contiguous_source,
-                source_branch, yca->rev, oldest_eligible->rev - 1,
+                source_branch, yca->rev, oldest_eligible_rev - 1,
                 scratch_pool, scratch_pool));
       SVN_ERR(branch_history_get_endpoints(
                 NULL, base_p,
@@ -11974,6 +12039,7 @@
   SVN_ERR(find_last_merged_location(base_p,
                                     s_t->yca,
                                     &s_t->source_branch,
+                                    s_t->target_branch.tip,
                                     s_t->target_mergeinfo,
                                     ctx, result_pool, scratch_pool));
   return SVN_NO_ERROR;
@@ -12007,6 +12073,7 @@
   SVN_ERR(find_last_merged_location(base_p,
                                     s_t->yca,
                                     &s_t->target_branch,
+                                    s_t->source,
                                     s_t->source_mergeinfo,
                                     ctx, result_pool, scratch_pool));
 
@@ -12070,8 +12137,8 @@
   /* Find the latest revision of A synced to B and the latest
    * revision of B synced to A.
    *
-   *   base_on_source = youngest_complete_synced_point(source, target)
-   *   base_on_target = youngest_complete_synced_point(target, source)
+   *   base_on_source = find_base_on_source(source, target)
+   *   base_on_target = find_base_on_source(target, source)
    */
   SVN_ERR(find_base_on_source(&base_on_source, s_t,
                               ctx, scratch_pool, scratch_pool));
Index: subversion/libsvn_client/mergeinfo.c
===================================================================
--- subversion/libsvn_client/mergeinfo.c	(revision 1454520)
+++ subversion/libsvn_client/mergeinfo.c	(working copy)
@@ -1258,6 +1258,13 @@
      the target's explicit or inherited mergeinfo. */
   const svn_rangelist_t *rangelist;
 
+  /* If non-zero, is the maximum number of times to call LOG_RECEIVER. */
+  int limit;
+
+  /* The number of times LOG_RECEIVER has been called.  Ignored if LIMIT
+     is zero. */
+  int unfiltered;
+
   /* The wrapped svn_log_entry_receiver_t callback and baton which
      filter_log_entry_with_rangelist() is acting as a filter for. */
   svn_log_entry_receiver_t log_receiver;
@@ -1297,6 +1304,13 @@
   if (log_entry->revision == 0)
     return SVN_NO_ERROR;
 
+  /* If we have a limit and we've reached it, then we are done.
+     ### TODO: How can we actually stop getting logs, rather
+     ###       than just ignoring those over the limit? */
+  if (fleb->limit &&
+      (fleb->limit == fleb->unfiltered))
+    return SVN_NO_ERROR;
+
   this_rangelist = svn_rangelist__initialize(log_entry->revision - 1,
                                              log_entry->revision,
                                              TRUE, pool);
@@ -1487,6 +1501,8 @@
         }
     }
 
+  if (fleb->limit)
+    fleb->unfiltered++;
   /* Call the wrapped log receiver which this function is filtering for. */
   return fleb->log_receiver(fleb->log_receiver_baton, log_entry, pool);
 }
@@ -1498,6 +1514,7 @@
                              const svn_rangelist_t *rangelist,
                              svn_mergeinfo_catalog_t target_mergeinfo_catalog,
                              const char *target_fspath,
+                             int limit,
                              svn_boolean_t discover_changed_paths,
                              const apr_array_header_t *revprops,
                              svn_log_entry_receiver_t log_receiver,
@@ -1553,6 +1570,8 @@
   fleb.log_receiver = log_receiver;
   fleb.log_receiver_baton = log_receiver_baton;
   fleb.ctx = ctx;
+  fleb.limit = limit;
+  fleb.unfiltered = 0;
 
   /* Drive the log. */
   revision_ranges = apr_array_make(scratch_pool, 1,
@@ -1560,9 +1579,9 @@
   APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
     = svn_opt__revision_range_create(&oldest_rev, &youngest_rev, scratch_pool);
   SVN_ERR(svn_client_log5(target, &youngest_rev, revision_ranges,
-                          0, discover_changed_paths, FALSE, FALSE, revprops,
-                          filter_log_entry_with_rangelist, &fleb, ctx,
-                          scratch_pool));
+                          0, discover_changed_paths, FALSE, FALSE,
+                          revprops, filter_log_entry_with_rangelist, &fleb,
+                          ctx, scratch_pool));
 
   /* Check for cancellation. */
   if (ctx->cancel_func)
@@ -1666,6 +1685,7 @@
                           const svn_opt_revision_t *source_end_revision,
                           svn_log_entry_receiver_t log_receiver,
                           void *log_receiver_baton,
+                          int limit,
                           svn_boolean_t discover_changed_paths,
                           svn_depth_t depth,
                           const apr_array_header_t *revprops,
@@ -2080,7 +2100,7 @@
                                        svn_fspath__join("/",
                                                         target_repos_relpath,
                                                         scratch_pool),
-                                       discover_changed_paths,
+                                       limit, discover_changed_paths,
                                        revprops,
                                        log_receiver, log_receiver_baton,
                                        ctx, scratch_pool));
Index: subversion/svn/mergeinfo-cmd.c
===================================================================
--- subversion/svn/mergeinfo-cmd.c	(revision 1454520)
+++ subversion/svn/mergeinfo-cmd.c	(working copy)
@@ -321,7 +321,7 @@
                                         source, &src_peg_revision,
                                         &(opt_state->start_revision),
                                         &(opt_state->end_revision),
-                                        print_log_rev, NULL,
+                                        print_log_rev, NULL, 0,
                                         TRUE, depth, NULL, ctx,
                                         pool));
     }
@@ -331,7 +331,7 @@
                                         source, &src_peg_revision,
                                         &(opt_state->start_revision),
                                         &(opt_state->end_revision),
-                                        print_log_rev, NULL,
+                                        print_log_rev, NULL, 0,
                                         TRUE, depth, NULL, ctx,
                                         pool));
     }
Index: subversion/tests/cmdline/merge_automatic_tests.py
===================================================================
--- subversion/tests/cmdline/merge_automatic_tests.py	(revision 1454520)
+++ subversion/tests/cmdline/merge_automatic_tests.py	(working copy)
@@ -1129,7 +1129,6 @@
 # source is fully synced'
 @SkipUnless(server_has_mergeinfo)
 @Issue(4329)
-@XFail()
 def effective_sync_results_in_reintegrate(sbox):
   "an effectively synced branch gets reintegrated"
 
@@ -1184,10 +1183,10 @@
   # Revert the merge and try it again, this time without the --reintegrate
   # option.  The merge should still work with the same results.
   #
-  # Currently this fails because the reintegrate code path is not followed,
+  # Previously this failed because the reintegrate code path is not followed,
   # rather the automatic merge attempts a sync style merge of the yca (^/A@1)
   # through the HEAD of the branch (^/branch@7).  This results in a spurious
-  # conflict on A/mu as the edit made in r3 is reapplied..
+  # conflict on A/mu as the edit made in r3 is reapplied.
   #
   # >svn merge ^/branch A
   # --- Merging r2 through r6 into 'A':
Index: subversion/tests/cmdline/merge_reintegrate_tests.py
===================================================================
--- subversion/tests/cmdline/merge_reintegrate_tests.py	(revision 1454520)
+++ subversion/tests/cmdline/merge_reintegrate_tests.py	(working copy)
@@ -1844,8 +1844,11 @@
                                        None, 1, 0)
 
 #----------------------------------------------------------------------
-# Test for issue #3577 '1.7 subtree mergeinfo recording breaks reintegrate'.
-@Issue(3577)
+# Test for issue #3577 '1.7 subtree mergeinfo recording breaks reintegrate'
+# and issue #4329 'automatic merge uses reintegrate type merge if source is
+# fully synced'.
+@Issue(3577,4329)
+@SkipUnless(server_has_mergeinfo)
 def reintegrate_with_subtree_merges(sbox):
   "reintegrate with prior subtree merges to source"
 
@@ -1857,6 +1860,7 @@
 
   # Some paths we'll care about
   A_path        = sbox.ospath('A')
+  psi_path      = sbox.ospath('A/D/H/psi')
   mu_COPY_path  = sbox.ospath('A_COPY/mu')
   A_COPY_path   = sbox.ospath('A_COPY')
   B_COPY_path   = sbox.ospath('A_COPY/B')
@@ -1974,6 +1978,28 @@
                              expected_A_skip,
                              None, 1, 1)
 
+  # Test issue #4329.  Revert previous merge and commit a new edit to
+  # A/D/H/psi. Attempt the same merge without the --reintegrate option.
+  # It should succeed because the automatic merge code should detect that
+  # a reintegrate-style merge is required, that merge should succeed and
+  # there should be not conflict on A/D/H/psi.
+  svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R', wc_dir)
+  svntest.main.file_write(psi_path, "Non-conflicting trunk edit.\n")
+  svntest.main.run_svn(None, 'commit', '-m',
+                       'An edit on trunk prior to reintegrate.', wc_dir)
+  sbox.simple_update()
+  expected_A_status.tweak(wc_rev=9)
+  expected_A_disk.tweak('', props={SVN_PROP_MERGEINFO: '/A_COPY:2-9'})
+  expected_A_disk.tweak('D/H/psi', contents='Non-conflicting trunk edit.\n')
+  svntest.actions.run_and_verify_merge(A_path, None, None,
+                                       sbox.repo_url + '/A_COPY', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_A_disk, expected_A_status,
+                                       expected_A_skip, None, None, None,
+                                       None, None, True, False, A_path)
+
 #----------------------------------------------------------------------
 # Test for issue #3654 'added subtrees with mergeinfo break reintegrate'.
 @Issue(3654)
