Hi,

the attached patch fixes both issue 4856 and the (different) bug
we were seeing in one of our own repositories.
The problem boils down to the fact that with `svn log --xml --verbose`
a log-item is opened every time the PATH_CHANGE_RECEIVER callback is
used. But if the revision is later declared "unimportant", the
corresponding log-item closure is never sent via the REVISION_RECEIVER
callback.

The patch fixes that by always using an intermediate callback routine
and recording the call of inner() in a new flag in
interesting_merge_baton_t. This flag is then used to decide if the
REVISION_RECEIVER callback has to be called to close the log-item.

With yet another flag in interesting_merge_baton_t record_inner_call()
could be merged into interesting_merge() if you prefer that.

Franz.
Index: subversion-1.14.x/subversion/libsvn_ra_serf/log.c
===================================================================
--- subversion-1.14.x/subversion/libsvn_ra_serf/log.c   (revision 1921239)
+++ subversion-1.14.x/subversion/libsvn_ra_serf/log.c   (working copy)
@@ -299,6 +299,7 @@
                                                         "subtractive-merge",
                                                         FALSE);
 
+      SVN_ERR_ASSERT(attrs != NULL);
       rev_str = svn_hash_gets(attrs, "revision");
       if (rev_str)
         {
Index: subversion-1.14.x/subversion/libsvn_repos/log.c
===================================================================
--- subversion-1.14.x/subversion/libsvn_repos/log.c     (revision 1921239)
+++ subversion-1.14.x/subversion/libsvn_repos/log.c     (working copy)
@@ -1216,6 +1216,9 @@
   /* Set to TRUE if we found it. */
   svn_boolean_t found_rev_of_interest;
 
+  /* Set to TRUE if we called PATH_CHANGE_RECEIVER via INNER. */
+  svn_boolean_t inner_got_used;
+
   /* We need to invoke this user-provided callback if not NULL. */
   svn_repos_path_change_receiver_t inner;
   void *inner_baton;
@@ -1236,7 +1239,10 @@
   apr_hash_index_t *hi;
 
   if (b->inner)
-    SVN_ERR(b->inner(b->inner_baton, change, scratch_pool));
+    {
+      SVN_ERR(b->inner(b->inner_baton, change, scratch_pool));
+      b->inner_got_used = TRUE;
+    }
 
   if (b->found_rev_of_interest)
     return SVN_NO_ERROR;
@@ -1270,6 +1276,22 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+record_inner_call(void *baton,
+                  svn_repos_path_change_t *change,
+                  apr_pool_t *scratch_pool)
+{
+  interesting_merge_baton_t *b = baton;
+
+  if (b->inner)
+    {
+      SVN_ERR(b->inner(b->inner_baton, change, scratch_pool));
+      b->inner_got_used = TRUE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Send a log message for REV to the CALLBACKS.
 
    FS is used with REV to fetch the interesting history information,
@@ -1310,6 +1332,7 @@
 {
   svn_repos_log_entry_t log_entry = { 0 };
   log_callbacks_t my_callbacks = *callbacks;
+  apr_pool_t *scratch_pool;
 
   interesting_merge_baton_t baton;
 
@@ -1323,6 +1346,7 @@
       && apr_hash_count(log_target_history_as_mergeinfo))
     {
       baton.found_rev_of_interest = FALSE;
+      baton.inner_got_used = FALSE;
       baton.rev = rev;
       baton.log_target_history_as_mergeinfo = log_target_history_as_mergeinfo;
       baton.inner = callbacks->path_change_receiver;
@@ -1335,6 +1359,12 @@
   else
     {
       baton.found_rev_of_interest = TRUE;
+      baton.inner_got_used = FALSE;
+      baton.inner = callbacks->path_change_receiver;
+      baton.inner_baton = callbacks->path_change_receiver_baton;
+      my_callbacks.path_change_receiver = record_inner_call;
+      my_callbacks.path_change_receiver_baton = &baton;
+      callbacks = &my_callbacks;
     }
 
   SVN_ERR(fill_log_entry(&log_entry, rev, fs, revprops, callbacks, pool));
@@ -1345,15 +1375,14 @@
      revision. */
   if (baton.found_rev_of_interest)
     {
-      apr_pool_t *scratch_pool;
-
       /* Is REV a merged revision we've already sent? */
       if (nested_merges && handling_merged_revision)
         {
           if (svn_bit_array__get(nested_merges, rev))
             {
-              /* We already sent REV. */
-              return SVN_NO_ERROR;
+              if (!baton.inner_got_used)
+                /* We already sent REV and no log was streamed yet. */
+                return SVN_NO_ERROR;
             }
           else
             {
@@ -1371,7 +1400,20 @@
                                            &log_entry, scratch_pool));
       svn_pool_destroy(scratch_pool);
     }
+  else if (baton.inner_got_used)
+    {
+      svn_repos_log_entry_t empty_log_entry = { 0 };
 
+      /* Send the empty revision.  */
+      empty_log_entry.revision = SVN_INVALID_REVNUM;
+      /* Pass a scratch pool to ensure no temporary state stored
+         by the receiver callback persists. */
+      scratch_pool = svn_pool_create(pool);
+      SVN_ERR(callbacks->revision_receiver(callbacks->revision_receiver_baton,
+                                           &empty_log_entry, scratch_pool));
+      svn_pool_destroy(scratch_pool);
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -1719,8 +1761,8 @@
         int limit,
         svn_boolean_t strict_node_history,
         svn_boolean_t include_merged_revisions,
+        svn_boolean_t subtractive_merge,
         svn_boolean_t handling_merged_revisions,
-        svn_boolean_t subtractive_merge,
         svn_boolean_t ignore_missing_locations,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,

Reply via email to