~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/libsvn_repos/log.c

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
39
39
#include "svn_mergeinfo.h"
40
40
#include "repos.h"
41
41
#include "private/svn_fspath.h"
 
42
#include "private/svn_fs_private.h"
42
43
#include "private/svn_mergeinfo_private.h"
43
44
#include "private/svn_subr_private.h"
 
45
#include "private/svn_sorts_private.h"
44
46
 
45
47
 
46
48
 
80
82
  subpool = svn_pool_create(pool);
81
83
  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
82
84
    {
83
 
      const void *key;
84
 
      void *val;
85
 
      svn_fs_path_change2_t *change;
 
85
      const char *key = apr_hash_this_key(hi);
 
86
      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
86
87
      svn_boolean_t readable;
87
88
 
88
89
      svn_pool_clear(subpool);
89
 
      apr_hash_this(hi, &key, NULL, &val);
90
 
      change = val;
91
90
 
92
91
      SVN_ERR(authz_read_func(&readable, rev_root, key,
93
92
                              authz_read_baton, subpool));
169
168
 * AUTHZ_READ_BATON and FS) to check whether each changed-path (and
170
169
 * copyfrom_path) is readable:
171
170
 *
 
171
 *     - If absolutely every changed-path (and copyfrom_path) is
 
172
 *     readable, then return the full CHANGED hash, and set
 
173
 *     *ACCESS_LEVEL to svn_repos_revision_access_full.
 
174
 *
172
175
 *     - If some paths are readable and some are not, then silently
173
 
 *     omit the unreadable paths from the CHANGED hash, and return
174
 
 *     SVN_ERR_AUTHZ_PARTIALLY_READABLE.
 
176
 *     omit the unreadable paths from the CHANGED hash, and set
 
177
 *     *ACCESS_LEVEL to svn_repos_revision_access_partial.
175
178
 *
176
179
 *     - If absolutely every changed-path (and copyfrom_path) is
177
 
 *     unreadable, then return an empty CHANGED hash and
178
 
 *     SVN_ERR_AUTHZ_UNREADABLE.  (This is to distinguish a revision
179
 
 *     which truly has no changed paths from a revision in which all
180
 
 *     paths are unreadable.)
 
180
 *     unreadable, then return an empty CHANGED hash, and set
 
181
 *     *ACCESS_LEVEL to svn_repos_revision_access_none.  (This is
 
182
 *     to distinguish a revision which truly has no changed paths
 
183
 *     from a revision in which all paths are unreadable.)
181
184
 */
182
185
static svn_error_t *
183
 
detect_changed(apr_hash_t **changed,
 
186
detect_changed(svn_repos_revision_access_level_t *access_level,
 
187
               apr_hash_t **changed,
184
188
               svn_fs_root_t *root,
185
189
               svn_fs_t *fs,
186
190
               apr_hash_t *prefetched_changes,
190
194
{
191
195
  apr_hash_t *changes = prefetched_changes;
192
196
  apr_hash_index_t *hi;
193
 
  apr_pool_t *subpool;
 
197
  apr_pool_t *iterpool;
194
198
  svn_boolean_t found_readable = FALSE;
195
199
  svn_boolean_t found_unreadable = FALSE;
196
200
 
197
 
  *changed = svn_hash__make(pool);
 
201
  /* If we create the CHANGES hash ourselves, we can reuse it as the
 
202
   * result hash as it contains the exact same keys - but with _all_
 
203
   * values being replaced by structs of a different type. */
198
204
  if (changes == NULL)
199
 
    SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
 
205
    {
 
206
      SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
 
207
 
 
208
      /* If we are going to filter the results, we won't use the exact
 
209
       * same keys but put them into a new hash. */
 
210
      if (authz_read_func)
 
211
        *changed = svn_hash__make(pool);
 
212
      else
 
213
        *changed = changes;
 
214
    }
 
215
  else
 
216
    {
 
217
      *changed = svn_hash__make(pool);
 
218
    }
200
219
 
201
220
  if (apr_hash_count(changes) == 0)
202
 
    /* No paths changed in this revision?  Uh, sure, I guess the
203
 
       revision is readable, then.  */
204
 
    return SVN_NO_ERROR;
205
 
 
206
 
  subpool = svn_pool_create(pool);
207
 
 
 
221
    {
 
222
      /* No paths changed in this revision?  Uh, sure, I guess the
 
223
         revision is readable, then.  */
 
224
      *access_level = svn_repos_revision_access_full;
 
225
      return SVN_NO_ERROR;
 
226
    }
 
227
 
 
228
  iterpool = svn_pool_create(pool);
208
229
  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
209
230
    {
210
231
      /* NOTE:  Much of this loop is going to look quite similar to
211
232
         svn_repos_check_revision_access(), but we have to do more things
212
233
         here, so we'll live with the duplication. */
213
 
      const void *key;
214
 
      void *val;
215
 
      svn_fs_path_change2_t *change;
216
 
      const char *path;
 
234
      const char *path = apr_hash_this_key(hi);
 
235
      apr_ssize_t path_len = apr_hash_this_key_len(hi);
 
236
      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
217
237
      char action;
218
238
      svn_log_changed_path2_t *item;
219
239
 
220
 
      svn_pool_clear(subpool);
221
 
 
222
 
      /* KEY will be the path, VAL the change. */
223
 
      apr_hash_this(hi, &key, NULL, &val);
224
 
      path = (const char *) key;
225
 
      change = val;
 
240
      svn_pool_clear(iterpool);
226
241
 
227
242
      /* Skip path if unreadable. */
228
243
      if (authz_read_func)
230
245
          svn_boolean_t readable;
231
246
          SVN_ERR(authz_read_func(&readable,
232
247
                                  root, path,
233
 
                                  authz_read_baton, subpool));
 
248
                                  authz_read_baton, iterpool));
234
249
          if (! readable)
235
250
            {
236
251
              found_unreadable = TRUE;
288
303
              svn_revnum_t prev_rev;
289
304
              const char *parent_path, *name;
290
305
 
291
 
              svn_fspath__split(&parent_path, &name, path, subpool);
 
306
              svn_fspath__split(&parent_path, &name, path, iterpool);
292
307
 
293
 
              SVN_ERR(svn_fs_node_history(&history, root, parent_path,
294
 
                                          subpool));
 
308
              SVN_ERR(svn_fs_node_history2(&history, root, parent_path,
 
309
                                           iterpool, iterpool));
295
310
 
296
311
              /* Two calls because the first call returns the original
297
312
                 revision as the deleted child means it is 'interesting' */
298
 
              SVN_ERR(svn_fs_history_prev(&history, history, TRUE, subpool));
299
 
              SVN_ERR(svn_fs_history_prev(&history, history, TRUE, subpool));
 
313
              SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool,
 
314
                                           iterpool));
 
315
              SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool,
 
316
                                           iterpool));
300
317
 
301
318
              SVN_ERR(svn_fs_history_location(&parent_path, &prev_rev, history,
302
 
                                              subpool));
303
 
              SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, subpool));
304
 
              check_path = svn_fspath__join(parent_path, name, subpool);
 
319
                                              iterpool));
 
320
              SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, iterpool));
 
321
              check_path = svn_fspath__join(parent_path, name, iterpool);
305
322
            }
306
323
 
307
324
          SVN_ERR(svn_fs_check_path(&item->node_kind, check_root, check_path,
308
 
                                    subpool));
 
325
                                    iterpool));
309
326
        }
310
327
 
311
328
 
318
335
             we will follow the DAG from ROOT to PATH and that requires
319
336
             actually reading the directories along the way. */
320
337
          if (!change->copyfrom_known)
321
 
            SVN_ERR(svn_fs_copied_from(&copyfrom_rev, &copyfrom_path,
322
 
                                      root, path, subpool));
 
338
            {
 
339
              SVN_ERR(svn_fs_copied_from(&copyfrom_rev, &copyfrom_path,
 
340
                                        root, path, iterpool));
 
341
              copyfrom_path = apr_pstrdup(pool, copyfrom_path);
 
342
            }
323
343
 
324
344
          if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
325
345
            {
330
350
                  svn_fs_root_t *copyfrom_root;
331
351
 
332
352
                  SVN_ERR(svn_fs_revision_root(&copyfrom_root, fs,
333
 
                                               copyfrom_rev, subpool));
 
353
                                               copyfrom_rev, iterpool));
334
354
                  SVN_ERR(authz_read_func(&readable,
335
355
                                          copyfrom_root, copyfrom_path,
336
 
                                          authz_read_baton, subpool));
 
356
                                          authz_read_baton, iterpool));
337
357
                  if (! readable)
338
358
                    found_unreadable = TRUE;
339
359
                }
340
360
 
341
361
              if (readable)
342
362
                {
343
 
                  item->copyfrom_path = apr_pstrdup(pool, copyfrom_path);
 
363
                  item->copyfrom_path = copyfrom_path;
344
364
                  item->copyfrom_rev = copyfrom_rev;
345
365
                }
346
366
            }
347
367
        }
348
 
      svn_hash_sets(*changed, apr_pstrdup(pool, path), item);
 
368
 
 
369
      apr_hash_set(*changed, path, path_len, item);
349
370
    }
350
371
 
351
 
  svn_pool_destroy(subpool);
 
372
  svn_pool_destroy(iterpool);
352
373
 
353
374
  if (! found_readable)
354
 
    /* Every changed-path was unreadable. */
355
 
    return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE,
356
 
                            NULL, NULL);
357
 
 
358
 
  if (found_unreadable)
359
 
    /* At least one changed-path was unreadable. */
360
 
    return svn_error_create(SVN_ERR_AUTHZ_PARTIALLY_READABLE,
361
 
                            NULL, NULL);
362
 
 
363
 
  /* Every changed-path was readable. */
 
375
    {
 
376
      /* Every changed-path was unreadable. */
 
377
      *access_level = svn_repos_revision_access_none;
 
378
    }
 
379
  else if (found_unreadable)
 
380
    {
 
381
      /* At least one changed-path was unreadable. */
 
382
      *access_level = svn_repos_revision_access_partial;
 
383
    }
 
384
  else
 
385
    {
 
386
      /* Every changed-path was readable. */
 
387
      *access_level = svn_repos_revision_access_full;
 
388
    }
 
389
 
364
390
  return SVN_NO_ERROR;
365
391
}
366
392
 
410
436
            svn_repos_authz_func_t authz_read_func,
411
437
            void *authz_read_baton,
412
438
            svn_revnum_t start,
413
 
            apr_pool_t *pool)
 
439
            apr_pool_t *result_pool,
 
440
            apr_pool_t *scratch_pool)
414
441
{
415
442
  svn_fs_root_t *history_root = NULL;
416
443
  svn_fs_history_t *hist;
421
448
    {
422
449
      subpool = info->newpool;
423
450
 
424
 
      SVN_ERR(svn_fs_history_prev(&info->hist, info->hist, ! strict, subpool));
 
451
      SVN_ERR(svn_fs_history_prev2(&info->hist, info->hist, ! strict,
 
452
                                   subpool, scratch_pool));
425
453
 
426
454
      hist = info->hist;
427
455
    }
428
456
  else
429
457
    {
430
 
      subpool = svn_pool_create(pool);
 
458
      subpool = svn_pool_create(result_pool);
431
459
 
432
460
      /* Open the history located at the last rev we were at. */
433
461
      SVN_ERR(svn_fs_revision_root(&history_root, fs, info->history_rev,
434
462
                                   subpool));
435
463
 
436
 
      SVN_ERR(svn_fs_node_history(&hist, history_root, info->path->data,
437
 
                                  subpool));
 
464
      SVN_ERR(svn_fs_node_history2(&hist, history_root, info->path->data,
 
465
                                   subpool, scratch_pool));
438
466
 
439
 
      SVN_ERR(svn_fs_history_prev(&hist, hist, ! strict, subpool));
 
467
      SVN_ERR(svn_fs_history_prev2(&hist, hist, ! strict, subpool,
 
468
                                   scratch_pool));
440
469
 
441
470
      if (info->first_time)
442
471
        info->first_time = FALSE;
443
472
      else
444
 
        SVN_ERR(svn_fs_history_prev(&hist, hist, ! strict, subpool));
 
473
        SVN_ERR(svn_fs_history_prev2(&hist, hist, ! strict, subpool,
 
474
                                     scratch_pool));
445
475
    }
446
476
 
447
477
  if (! hist)
476
506
      svn_boolean_t readable;
477
507
      SVN_ERR(svn_fs_revision_root(&history_root, fs,
478
508
                                   info->history_rev,
479
 
                                   subpool));
 
509
                                   scratch_pool));
480
510
      SVN_ERR(authz_read_func(&readable, history_root,
481
511
                              info->path->data,
482
512
                              authz_read_baton,
483
 
                              subpool));
 
513
                              scratch_pool));
484
514
      if (! readable)
485
515
        info->done = TRUE;
486
516
    }
518
548
              svn_repos_authz_func_t authz_read_func,
519
549
              void *authz_read_baton,
520
550
              svn_revnum_t start,
521
 
              apr_pool_t *pool)
 
551
              apr_pool_t *result_pool,
 
552
              apr_pool_t *scratch_pool)
522
553
{
523
554
  /* If we're already done with histories for this path,
524
555
     don't try to fetch any more. */
537
568
     rev where this path was changed. */
538
569
  *changed = TRUE;
539
570
  return get_history(info, fs, strict, authz_read_func,
540
 
                     authz_read_baton, start, pool);
 
571
                     authz_read_baton, start, result_pool, scratch_pool);
541
572
}
542
573
 
543
574
/* Return the next interesting revision in our list of HISTORIES. */
562
593
 
563
594
/* Set *DELETED_MERGEINFO_CATALOG and *ADDED_MERGEINFO_CATALOG to
564
595
   catalogs describing how mergeinfo values on paths (which are the
565
 
   keys of those catalogs) were changed in REV.  If *PREFETCHED_CAHNGES
 
596
   keys of those catalogs) were changed in REV.  If *PREFETCHED_CHANGES
566
597
   already contains the changed paths for REV, use that.  Otherwise,
567
598
   request that data and return it in *PREFETCHED_CHANGES. */
568
599
/* ### TODO: This would make a *great*, useful public function,
575
606
                     svn_revnum_t rev,
576
607
                     apr_pool_t *result_pool,
577
608
                     apr_pool_t *scratch_pool)
578
 
 
579
609
{
580
610
  svn_fs_root_t *root;
581
611
  apr_pool_t *iterpool;
582
612
  apr_hash_index_t *hi;
 
613
  svn_boolean_t any_mergeinfo = FALSE;
 
614
  svn_boolean_t any_copy = FALSE;
583
615
 
584
616
  /* Initialize return variables. */
585
617
  *deleted_mergeinfo_catalog = svn_hash__make(result_pool);
595
627
  if (*prefetched_changes == NULL)
596
628
    SVN_ERR(svn_fs_paths_changed2(prefetched_changes, root, scratch_pool));
597
629
 
598
 
  /* No changed paths?  We're done. */
599
 
  if (apr_hash_count(*prefetched_changes) == 0)
 
630
  /* Look for copies and (potential) mergeinfo changes.
 
631
     We will use both flags to take shortcuts further down the road. */
 
632
  for (hi = apr_hash_first(scratch_pool, *prefetched_changes);
 
633
       hi;
 
634
       hi = apr_hash_next(hi))
 
635
    {
 
636
      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
 
637
 
 
638
      /* If there was a prop change and we are not positive that _no_
 
639
         mergeinfo change happened, we must assume that it might have. */
 
640
      if (change->mergeinfo_mod != svn_tristate_false && change->prop_mod)
 
641
        any_mergeinfo = TRUE;
 
642
 
 
643
      switch (change->change_kind)
 
644
        {
 
645
        case svn_fs_path_change_add:
 
646
        case svn_fs_path_change_replace:
 
647
          any_copy = TRUE;
 
648
          break;
 
649
 
 
650
        default:
 
651
          break;
 
652
        }
 
653
    }
 
654
 
 
655
  /* No potential mergeinfo changes?  We're done. */
 
656
  if (! any_mergeinfo)
600
657
    return SVN_NO_ERROR;
601
658
 
602
659
  /* Loop over changes, looking for anything that might carry an
607
664
       hi;
608
665
       hi = apr_hash_next(hi))
609
666
    {
610
 
      const void *key;
611
 
      void *val;
612
 
      svn_fs_path_change2_t *change;
613
 
      const char *changed_path, *base_path = NULL;
 
667
      const char *changed_path;
 
668
      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
 
669
      const char *base_path = NULL;
614
670
      svn_revnum_t base_rev = SVN_INVALID_REVNUM;
615
671
      svn_fs_root_t *base_root = NULL;
616
672
      svn_string_t *prev_mergeinfo_value = NULL, *mergeinfo_value;
617
673
 
618
 
      svn_pool_clear(iterpool);
 
674
      /* Cheap pre-checks that don't require memory allocation etc. */
619
675
 
620
 
      /* KEY will be the path, VAL the change. */
621
 
      apr_hash_this(hi, &key, NULL, &val);
622
 
      changed_path = key;
623
 
      change = val;
 
676
      /* No mergeinfo change? -> nothing to do here. */
 
677
      if (change->mergeinfo_mod == svn_tristate_false)
 
678
        continue;
624
679
 
625
680
      /* If there was no property change on this item, ignore it. */
626
681
      if (! change->prop_mod)
627
682
        continue;
628
683
 
 
684
      /* Begin actual processing */
 
685
      changed_path = apr_hash_this_key(hi);
 
686
      svn_pool_clear(iterpool);
 
687
 
629
688
      switch (change->change_kind)
630
689
        {
631
690
 
634
693
           ### difference would be the fallback case (path/rev-1 for
635
694
           ### modifies, NULL otherwise).  -- cmpilato  */
636
695
 
637
 
        /* If the path was added or replaced, see if it was created via
638
 
           copy.  If so, that will tell us where its previous location
639
 
           was.  If not, there's no previous location to examine.  */
640
 
        case svn_fs_path_change_add:
641
 
        case svn_fs_path_change_replace:
642
 
          {
643
 
            const char *copyfrom_path;
644
 
            svn_revnum_t copyfrom_rev;
645
 
 
646
 
            SVN_ERR(svn_fs_copied_from(&copyfrom_rev, &copyfrom_path,
647
 
                                       root, changed_path, iterpool));
648
 
            if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
649
 
              {
650
 
                base_path = apr_pstrdup(scratch_pool, copyfrom_path);
651
 
                base_rev = copyfrom_rev;
652
 
              }
653
 
            break;
654
 
          }
655
 
 
656
696
        /* If the path was merely modified, see if its previous
657
697
           location was affected by a copy which happened in this
658
698
           revision before assuming it holds the same path it did the
661
701
          {
662
702
            svn_revnum_t appeared_rev;
663
703
 
664
 
            SVN_ERR(svn_repos__prev_location(&appeared_rev, &base_path,
665
 
                                             &base_rev, fs, rev,
666
 
                                             changed_path, iterpool));
 
704
            /* If there were no copies in this revision, the path will have
 
705
               existed in the previous rev.  Otherwise, we might just got
 
706
               copied here and need to check for that eventuality. */
 
707
            if (any_copy)
 
708
              {
 
709
                SVN_ERR(svn_repos__prev_location(&appeared_rev, &base_path,
 
710
                                                 &base_rev, fs, rev,
 
711
                                                 changed_path, iterpool));
667
712
 
668
 
            /* If this path isn't the result of a copy that occurred
669
 
               in this revision, we can find the previous version of
670
 
               it in REV - 1 at the same path. */
671
 
            if (! (base_path && SVN_IS_VALID_REVNUM(base_rev)
672
 
                   && (appeared_rev == rev)))
 
713
                /* If this path isn't the result of a copy that occurred
 
714
                   in this revision, we can find the previous version of
 
715
                   it in REV - 1 at the same path. */
 
716
                if (! (base_path && SVN_IS_VALID_REVNUM(base_rev)
 
717
                      && (appeared_rev == rev)))
 
718
                  {
 
719
                    base_path = changed_path;
 
720
                    base_rev = rev - 1;
 
721
                  }
 
722
              }
 
723
            else
673
724
              {
674
725
                base_path = changed_path;
675
726
                base_rev = rev - 1;
677
728
            break;
678
729
          }
679
730
 
 
731
        /* If the path was added or replaced, see if it was created via
 
732
           copy.  If so, set BASE_REV/BASE_PATH to its previous location.
 
733
           If not, there's no previous location to examine -- leave
 
734
           BASE_REV/BASE_PATH = -1/NULL.  */
 
735
        case svn_fs_path_change_add:
 
736
        case svn_fs_path_change_replace:
 
737
          {
 
738
            if (change->copyfrom_known)
 
739
              {
 
740
                base_rev = change->copyfrom_rev;
 
741
                base_path = change->copyfrom_path;
 
742
              }
 
743
            else
 
744
              {
 
745
                SVN_ERR(svn_fs_copied_from(&base_rev, &base_path,
 
746
                                          root, changed_path, iterpool));
 
747
              }
 
748
            break;
 
749
          }
 
750
 
680
751
        /* We don't care about any of the other cases. */
681
752
        case svn_fs_path_change_delete:
682
753
        case svn_fs_path_change_reset:
704
775
      if (! (mergeinfo_value || prev_mergeinfo_value))
705
776
        continue;
706
777
 
 
778
      /* Mergeinfo on both sides but it did not change? Skip that too. */
 
779
      if (   mergeinfo_value && prev_mergeinfo_value
 
780
          && svn_string_compare(mergeinfo_value, prev_mergeinfo_value))
 
781
        continue;
 
782
 
707
783
      /* If mergeinfo was explicitly added or removed on this path, we
708
784
         need to check to see if that was a real semantic change of
709
785
         meaning.  So, fill in the "missing" mergeinfo value with the
710
786
         inherited mergeinfo for that path/revision.  */
711
787
      if (prev_mergeinfo_value && (! mergeinfo_value))
712
788
        {
713
 
          apr_array_header_t *query_paths =
714
 
            apr_array_make(iterpool, 1, sizeof(const char *));
715
789
          svn_mergeinfo_t tmp_mergeinfo;
716
 
          svn_mergeinfo_catalog_t tmp_catalog;
717
790
 
718
 
          APR_ARRAY_PUSH(query_paths, const char *) = changed_path;
719
 
          SVN_ERR(svn_fs_get_mergeinfo2(&tmp_catalog, root,
720
 
                                        query_paths, svn_mergeinfo_inherited,
721
 
                                        FALSE, TRUE, iterpool, iterpool));
722
 
          tmp_mergeinfo = svn_hash_gets(tmp_catalog, changed_path);
 
791
          SVN_ERR(svn_fs__get_mergeinfo_for_path(&tmp_mergeinfo,
 
792
                                                 root, changed_path,
 
793
                                                 svn_mergeinfo_inherited, TRUE,
 
794
                                                 iterpool, iterpool));
723
795
          if (tmp_mergeinfo)
724
796
            SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_value,
725
797
                                            tmp_mergeinfo,
728
800
      else if (mergeinfo_value && (! prev_mergeinfo_value)
729
801
               && base_path && SVN_IS_VALID_REVNUM(base_rev))
730
802
        {
731
 
          apr_array_header_t *query_paths =
732
 
            apr_array_make(iterpool, 1, sizeof(const char *));
733
803
          svn_mergeinfo_t tmp_mergeinfo;
734
 
          svn_mergeinfo_catalog_t tmp_catalog;
735
804
 
736
 
          APR_ARRAY_PUSH(query_paths, const char *) = base_path;
737
 
          SVN_ERR(svn_fs_get_mergeinfo2(&tmp_catalog, base_root,
738
 
                                        query_paths, svn_mergeinfo_inherited,
739
 
                                        FALSE, TRUE, iterpool, iterpool));
740
 
          tmp_mergeinfo = svn_hash_gets(tmp_catalog, base_path);
 
805
          SVN_ERR(svn_fs__get_mergeinfo_for_path(&tmp_mergeinfo,
 
806
                                                 base_root, base_path,
 
807
                                                 svn_mergeinfo_inherited, TRUE,
 
808
                                                 iterpool, iterpool));
741
809
          if (tmp_mergeinfo)
742
810
            SVN_ERR(svn_mergeinfo_to_string(&prev_mergeinfo_value,
743
811
                                            tmp_mergeinfo,
744
812
                                            iterpool));
745
813
        }
746
814
 
747
 
      /* If the old and new mergeinfo differ in any way, store the
748
 
         before and after mergeinfo values in our return hashes. */
749
 
      if ((prev_mergeinfo_value && (! mergeinfo_value))
750
 
          || ((! prev_mergeinfo_value) && mergeinfo_value)
751
 
          || (prev_mergeinfo_value && mergeinfo_value
752
 
              && (! svn_string_compare(mergeinfo_value,
753
 
                                       prev_mergeinfo_value))))
 
815
      /* Old and new mergeinfo probably differ in some way (we already
 
816
         checked for textual equality further up). Store the before and
 
817
         after mergeinfo values in our return hashes.  They may still be
 
818
         equal as manual intervention may have only changed the formatting
 
819
         but not the relevant contents. */
754
820
        {
755
821
          svn_mergeinfo_t prev_mergeinfo = NULL, mergeinfo = NULL;
756
822
          svn_mergeinfo_t deleted, added;
781
847
/* Determine what (if any) mergeinfo for PATHS was modified in
782
848
   revision REV, returning the differences for added mergeinfo in
783
849
   *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO.
784
 
   If *PREFETCHED_CAHNGES already contains the changed paths for
 
850
   If *PREFETCHED_CHANGES already contains the changed paths for
785
851
   REV, use that.  Otherwise, request that data and return it in
786
 
   *PREFETCHED_CHANGES.
787
 
   Use POOL for all allocations. */
 
852
   *PREFETCHED_CHANGES. */
788
853
static svn_error_t *
789
854
get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo,
790
855
                               svn_mergeinfo_t *deleted_mergeinfo,
814
879
  if (! paths->nelts)
815
880
    return SVN_NO_ERROR;
816
881
 
817
 
  /* Create a work subpool and get a root for REV. */
818
 
  SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
819
 
 
820
882
  /* Fetch the mergeinfo changes for REV. */
821
883
  err = fs_mergeinfo_changed(&deleted_mergeinfo_catalog,
822
884
                             &added_mergeinfo_catalog,
823
885
                             prefetched_changes,
824
 
                             fs, rev, scratch_pool, scratch_pool);
 
886
                             fs, rev,
 
887
                             scratch_pool, scratch_pool);
825
888
  if (err)
826
889
    {
827
890
      if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
842
905
  if (   apr_hash_count(deleted_mergeinfo_catalog) == 0
843
906
      && apr_hash_count(added_mergeinfo_catalog) == 0)
844
907
    return SVN_NO_ERROR;
845
 
  
 
908
 
 
909
  /* Create a work subpool and get a root for REV. */
 
910
  SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
 
911
 
846
912
  /* Check our PATHS for any changes to their inherited mergeinfo.
847
913
     (We deal with changes to mergeinfo directly *on* the paths in the
848
914
     following loop.)  */
851
917
    {
852
918
      const char *path = APR_ARRAY_IDX(paths, i, const char *);
853
919
      const char *prev_path;
854
 
      apr_ssize_t klen;
855
920
      svn_revnum_t appeared_rev, prev_rev;
856
921
      svn_fs_root_t *prev_root;
857
 
      svn_mergeinfo_catalog_t catalog, inherited_catalog;
858
922
      svn_mergeinfo_t prev_mergeinfo, mergeinfo, deleted, added,
859
923
        prev_inherited_mergeinfo, inherited_mergeinfo;
860
 
      apr_array_header_t *query_paths;
861
924
 
862
925
      svn_pool_clear(iterpool);
863
926
 
893
956
         this path.  Ignore not-found errors returned by the
894
957
         filesystem or invalid mergeinfo (Issue #3896).*/
895
958
      SVN_ERR(svn_fs_revision_root(&prev_root, fs, prev_rev, iterpool));
896
 
      query_paths = apr_array_make(iterpool, 1, sizeof(const char *));
897
 
      APR_ARRAY_PUSH(query_paths, const char *) = prev_path;
898
 
      err = svn_fs_get_mergeinfo2(&catalog, prev_root, query_paths,
899
 
                                  svn_mergeinfo_inherited, FALSE, TRUE,
900
 
                                  iterpool, iterpool);
 
959
      err = svn_fs__get_mergeinfo_for_path(&prev_mergeinfo,
 
960
                                           prev_root, prev_path,
 
961
                                           svn_mergeinfo_inherited, TRUE,
 
962
                                           iterpool, iterpool);
901
963
      if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
902
964
                  err->apr_err == SVN_ERR_FS_NOT_DIRECTORY ||
903
965
                  err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR))
917
979
 
918
980
         To check for this we must fetch the "raw" previous inherited
919
981
         mergeinfo and the "raw" mergeinfo @REV then compare these. */
920
 
      SVN_ERR(svn_fs_get_mergeinfo2(&inherited_catalog, prev_root, query_paths,
921
 
                                    svn_mergeinfo_nearest_ancestor, FALSE,
922
 
                                    FALSE, /* adjust_inherited_mergeinfo */
923
 
                                    iterpool, iterpool));
924
 
 
925
 
      klen = strlen(prev_path);
926
 
      prev_mergeinfo = apr_hash_get(catalog, prev_path, klen);
927
 
      prev_inherited_mergeinfo = apr_hash_get(inherited_catalog, prev_path, klen);
 
982
      SVN_ERR(svn_fs__get_mergeinfo_for_path(&prev_inherited_mergeinfo,
 
983
                                             prev_root, prev_path,
 
984
                                             svn_mergeinfo_nearest_ancestor,
 
985
                                             FALSE, /* adjust_inherited_mergeinfo */
 
986
                                             iterpool, iterpool));
928
987
 
929
988
      /* Fetch the current mergeinfo (as of REV, and including
930
989
         inherited stuff) for this path. */
931
 
      APR_ARRAY_IDX(query_paths, 0, const char *) = path;
932
 
      SVN_ERR(svn_fs_get_mergeinfo2(&catalog, root, query_paths,
933
 
                                    svn_mergeinfo_inherited, FALSE, TRUE,
934
 
                                    iterpool, iterpool));
 
990
      SVN_ERR(svn_fs__get_mergeinfo_for_path(&mergeinfo,
 
991
                                             root, path,
 
992
                                             svn_mergeinfo_inherited, TRUE,
 
993
                                             iterpool, iterpool));
935
994
 
936
995
      /* Issue #4022 again, fetch the raw inherited mergeinfo. */
937
 
      SVN_ERR(svn_fs_get_mergeinfo2(&inherited_catalog, root, query_paths,
938
 
                                    svn_mergeinfo_nearest_ancestor, FALSE,
939
 
                                    FALSE, /* adjust_inherited_mergeinfo */
940
 
                                    iterpool, iterpool));
941
 
 
942
 
      klen = strlen(path);
943
 
      mergeinfo = apr_hash_get(catalog, path, klen);
944
 
      inherited_mergeinfo = apr_hash_get(inherited_catalog, path, klen);
 
996
      SVN_ERR(svn_fs__get_mergeinfo_for_path(&inherited_mergeinfo,
 
997
                                             root, path,
 
998
                                             svn_mergeinfo_nearest_ancestor,
 
999
                                             FALSE, /* adjust_inherited_mergeinfo */
 
1000
                                             iterpool, iterpool));
945
1001
 
946
1002
      if (!prev_mergeinfo && !mergeinfo)
947
1003
        continue;
965
1021
          svn_boolean_t same_mergeinfo;
966
1022
          SVN_ERR(svn_mergeinfo__equals(&same_mergeinfo,
967
1023
                                        prev_inherited_mergeinfo,
968
 
                                        FALSE,
 
1024
                                        NULL,
969
1025
                                        TRUE, iterpool));
970
1026
          if (same_mergeinfo)
971
1027
            continue;
985
1041
  for (hi = apr_hash_first(scratch_pool, added_mergeinfo_catalog);
986
1042
       hi; hi = apr_hash_next(hi))
987
1043
    {
988
 
      const void *key;
989
 
      apr_ssize_t klen;
990
 
      void *val;
991
 
      const char *changed_path;
992
 
      svn_mergeinfo_t added, deleted;
993
 
 
994
 
      /* The path is the key, the mergeinfo delta is the value. */
995
 
      apr_hash_this(hi, &key, &klen, &val);
996
 
      changed_path = key;
997
 
      added = val;
 
1044
      const char *changed_path = apr_hash_this_key(hi);
 
1045
      apr_ssize_t klen = apr_hash_this_key_len(hi);
 
1046
      svn_mergeinfo_t added = apr_hash_this_val(hi);
 
1047
      svn_mergeinfo_t deleted;
998
1048
 
999
1049
      for (i = 0; i < paths->nelts; i++)
1000
1050
        {
1002
1052
          if (! svn_fspath__skip_ancestor(path, changed_path))
1003
1053
            continue;
1004
1054
          svn_pool_clear(iterpool);
1005
 
          deleted = apr_hash_get(deleted_mergeinfo_catalog, key, klen);
 
1055
          deleted = apr_hash_get(deleted_mergeinfo_catalog, changed_path, klen);
1006
1056
          SVN_ERR(svn_mergeinfo_merge2(*deleted_mergeinfo,
1007
1057
                                       svn_mergeinfo_dup(deleted, result_pool),
1008
1058
                                       result_pool, iterpool));
1033
1083
{
1034
1084
  apr_hash_t *r_props, *changed_paths = NULL;
1035
1085
  svn_boolean_t get_revprops = TRUE, censor_revprops = FALSE;
 
1086
  svn_boolean_t want_revprops = !revprops || revprops->nelts;
1036
1087
 
1037
1088
  /* Discover changed paths if the user requested them
1038
1089
     or if we need to check that they are readable. */
1040
1091
      && (authz_read_func || discover_changed_paths))
1041
1092
    {
1042
1093
      svn_fs_root_t *newroot;
1043
 
      svn_error_t *patherr;
 
1094
      svn_repos_revision_access_level_t access_level;
1044
1095
 
1045
1096
      SVN_ERR(svn_fs_revision_root(&newroot, fs, rev, pool));
1046
 
      patherr = detect_changed(&changed_paths,
1047
 
                               newroot, fs, prefetched_changes,
1048
 
                               authz_read_func, authz_read_baton,
1049
 
                               pool);
 
1097
      SVN_ERR(detect_changed(&access_level, &changed_paths,
 
1098
                             newroot, fs, prefetched_changes,
 
1099
                             authz_read_func, authz_read_baton,
 
1100
                             pool));
1050
1101
 
1051
 
      if (patherr
1052
 
          && patherr->apr_err == SVN_ERR_AUTHZ_UNREADABLE)
 
1102
      if (access_level == svn_repos_revision_access_none)
1053
1103
        {
1054
1104
          /* All changed-paths are unreadable, so clear all fields. */
1055
 
          svn_error_clear(patherr);
1056
1105
          changed_paths = NULL;
1057
1106
          get_revprops = FALSE;
1058
1107
        }
1059
 
      else if (patherr
1060
 
               && patherr->apr_err == SVN_ERR_AUTHZ_PARTIALLY_READABLE)
 
1108
      else if (access_level == svn_repos_revision_access_partial)
1061
1109
        {
1062
1110
          /* At least one changed-path was unreadable, so censor all
1063
1111
             but author and date.  (The unreadable paths are already
1064
1112
             missing from the hash.) */
1065
 
          svn_error_clear(patherr);
1066
1113
          censor_revprops = TRUE;
1067
1114
        }
1068
 
      else if (patherr)
1069
 
        return patherr;
1070
1115
 
1071
1116
      /* It may be the case that an authz func was passed in, but
1072
1117
         the user still doesn't want to see any changed-paths. */
1074
1119
        changed_paths = NULL;
1075
1120
    }
1076
1121
 
1077
 
  if (get_revprops)
 
1122
  if (get_revprops && want_revprops)
1078
1123
    {
1079
1124
      /* User is allowed to see at least some revprops. */
1080
1125
      SVN_ERR(svn_fs_revision_proplist(&r_props, fs, rev, pool));
1096
1141
        }
1097
1142
      else
1098
1143
        {
 
1144
          int i;
 
1145
 
1099
1146
          /* Requested only some revprops... */
1100
 
          int i;
1101
 
          for (i = 0; i < revprops->nelts; i++)
 
1147
 
 
1148
          /* Make "svn:author" and "svn:date" available as svn_string_t
 
1149
             for efficient comparison via svn_string_compare().  Note that
 
1150
             we want static initialization here and must therefore emulate
 
1151
             strlen(x) by sizeof(x)-1. */
 
1152
          static const svn_string_t svn_prop_revision_author
 
1153
            = {SVN_PROP_REVISION_AUTHOR, sizeof(SVN_PROP_REVISION_AUTHOR)-1};
 
1154
          static const svn_string_t svn_prop_revision_date
 
1155
            = {SVN_PROP_REVISION_DATE, sizeof(SVN_PROP_REVISION_DATE)-1};
 
1156
 
 
1157
          /* often only the standard revprops got requested and delivered.
 
1158
             In that case, we can simply pass the hash on. */
 
1159
          if (revprops->nelts == apr_hash_count(r_props) && !censor_revprops)
1102
1160
            {
1103
 
              char *name = APR_ARRAY_IDX(revprops, i, char *);
1104
 
              svn_string_t *value = svn_hash_gets(r_props, name);
1105
 
              if (censor_revprops
1106
 
                  && !(strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0
1107
 
                       || strcmp(name, SVN_PROP_REVISION_DATE) == 0))
1108
 
                /* ... but we can only return author/date. */
1109
 
                continue;
1110
 
              if (log_entry->revprops == NULL)
1111
 
                log_entry->revprops = svn_hash__make(pool);
1112
 
              svn_hash_sets(log_entry->revprops, name, value);
 
1161
              log_entry->revprops = r_props;
 
1162
              for (i = 0; i < revprops->nelts; i++)
 
1163
                {
 
1164
                  const svn_string_t *name
 
1165
                    = APR_ARRAY_IDX(revprops, i, const svn_string_t *);
 
1166
                  if (!apr_hash_get(r_props, name->data, name->len))
 
1167
                    {
 
1168
                      /* hash does not match list of revprops we want */
 
1169
                      log_entry->revprops = NULL;
 
1170
                      break;
 
1171
                    }
 
1172
                }
1113
1173
            }
 
1174
 
 
1175
          /* slow, revprop-by-revprop filtering */
 
1176
          if (log_entry->revprops == NULL)
 
1177
            for (i = 0; i < revprops->nelts; i++)
 
1178
              {
 
1179
                const svn_string_t *name
 
1180
                  = APR_ARRAY_IDX(revprops, i, const svn_string_t *);
 
1181
                svn_string_t *value
 
1182
                  = apr_hash_get(r_props, name->data, name->len);
 
1183
                if (censor_revprops
 
1184
                    && !svn_string_compare(name, &svn_prop_revision_author)
 
1185
                    && !svn_string_compare(name, &svn_prop_revision_date))
 
1186
                  /* ... but we can only return author/date. */
 
1187
                  continue;
 
1188
                if (log_entry->revprops == NULL)
 
1189
                  log_entry->revprops = svn_hash__make(pool);
 
1190
                apr_hash_set(log_entry->revprops, name->data, name->len, value);
 
1191
              }
1114
1192
        }
1115
1193
    }
1116
1194
 
1136
1214
   (i.e. retrieve none if the array is empty).
1137
1215
 
1138
1216
   LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, and
1139
 
   NESTED_MERGES are as per the arguments of the same name to DO_LOGS.  If
1140
 
   HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
 
1217
   NESTED_MERGES are as per the arguments of the same name to DO_LOGS.
 
1218
   If HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
1141
1219
   already represented in LOG_TARGET_HISTORY_AS_MERGEINFO, then don't send
1142
1220
   the log message for REV.  If SUBTRACTIVE_MERGE is true, then REV was
1143
1221
   reverse merged.
1151
1229
         svn_fs_t *fs,
1152
1230
         apr_hash_t *prefetched_changes,
1153
1231
         svn_mergeinfo_t log_target_history_as_mergeinfo,
1154
 
         apr_hash_t *nested_merges,
 
1232
         svn_bit_array__t *nested_merges,
1155
1233
         svn_boolean_t discover_changed_paths,
1156
1234
         svn_boolean_t subtractive_merge,
1157
1235
         svn_boolean_t handling_merged_revision,
1170
1248
  log_entry = svn_log_entry_create(pool);
1171
1249
  SVN_ERR(fill_log_entry(log_entry, rev, fs, prefetched_changes,
1172
1250
                         discover_changed_paths || handling_merged_revision,
1173
 
                         revprops, authz_read_func, authz_read_baton,
1174
 
                         pool));
 
1251
                         revprops, authz_read_func, authz_read_baton, pool));
1175
1252
  log_entry->has_children = has_children;
1176
1253
  log_entry->subtractive_merge = subtractive_merge;
1177
1254
 
1184
1261
      && apr_hash_count(log_target_history_as_mergeinfo))
1185
1262
    {
1186
1263
      apr_hash_index_t *hi;
1187
 
      apr_pool_t *subpool = svn_pool_create(pool);
 
1264
      apr_pool_t *iterpool = svn_pool_create(pool);
1188
1265
 
1189
1266
      /* REV was merged in, but it might already be part of the log target's
1190
1267
         natural history, so change our starting assumption. */
1191
1268
      found_rev_of_interest = FALSE;
1192
1269
 
1193
1270
      /* Look at each changed path in REV. */
1194
 
      for (hi = apr_hash_first(subpool, log_entry->changed_paths2);
 
1271
      for (hi = apr_hash_first(pool, log_entry->changed_paths2);
1195
1272
           hi;
1196
1273
           hi = apr_hash_next(hi))
1197
1274
        {
1198
1275
          svn_boolean_t path_is_in_history = FALSE;
1199
 
          const char *changed_path = svn__apr_hash_index_key(hi);
 
1276
          const char *changed_path = apr_hash_this_key(hi);
1200
1277
          apr_hash_index_t *hi2;
1201
 
          apr_pool_t *inner_subpool = svn_pool_create(subpool);
1202
1278
 
1203
1279
          /* Look at each path on the log target's mergeinfo. */
1204
 
          for (hi2 = apr_hash_first(inner_subpool,
 
1280
          for (hi2 = apr_hash_first(iterpool,
1205
1281
                                    log_target_history_as_mergeinfo);
1206
1282
               hi2;
1207
1283
               hi2 = apr_hash_next(hi2))
1208
1284
            {
1209
 
              const char *mergeinfo_path =
1210
 
                svn__apr_hash_index_key(hi2);
1211
 
              svn_rangelist_t *rangelist =
1212
 
                svn__apr_hash_index_val(hi2);
 
1285
              const char *mergeinfo_path = apr_hash_this_key(hi2);
 
1286
              svn_rangelist_t *rangelist = apr_hash_this_val(hi2);
1213
1287
 
1214
1288
              /* Check whether CHANGED_PATH at revision REV is a child of
1215
1289
                 a (path, revision) tuple in LOG_TARGET_HISTORY_AS_MERGEINFO. */
1232
1306
              if (path_is_in_history)
1233
1307
                break;
1234
1308
            }
1235
 
          svn_pool_destroy(inner_subpool);
 
1309
          svn_pool_clear(iterpool);
1236
1310
 
1237
1311
          if (!path_is_in_history)
1238
1312
            {
1243
1317
              break;
1244
1318
            }
1245
1319
        }
1246
 
      svn_pool_destroy(subpool);
 
1320
      svn_pool_destroy(iterpool);
1247
1321
    }
1248
1322
 
1249
1323
  /* If we only got changed paths the sake of detecting redundant merged
1255
1329
     revision. */
1256
1330
  if (found_rev_of_interest)
1257
1331
    {
 
1332
      apr_pool_t *scratch_pool;
 
1333
 
1258
1334
      /* Is REV a merged revision we've already sent? */
1259
1335
      if (nested_merges && handling_merged_revision)
1260
1336
        {
1261
 
          svn_revnum_t *merged_rev = apr_hash_get(nested_merges, &rev,
1262
 
                                                  sizeof(svn_revnum_t *));
1263
 
 
1264
 
          if (merged_rev)
 
1337
          if (svn_bit_array__get(nested_merges, rev))
1265
1338
            {
1266
1339
              /* We already sent REV. */
1267
1340
              return SVN_NO_ERROR;
1271
1344
              /* NESTED_REVS needs to last across all the send_log, do_logs,
1272
1345
                 handle_merged_revisions() recursions, so use the pool it
1273
1346
                 was created in at the top of the recursion. */
1274
 
              apr_pool_t *hash_pool = apr_hash_pool_get(nested_merges);
1275
 
              svn_revnum_t *long_lived_rev = apr_palloc(hash_pool,
1276
 
                                                        sizeof(svn_revnum_t));
1277
 
              *long_lived_rev = rev;
1278
 
              apr_hash_set(nested_merges, long_lived_rev,
1279
 
                           sizeof(svn_revnum_t *), long_lived_rev);
 
1347
              svn_bit_array__set(nested_merges, rev, TRUE);
1280
1348
            }
1281
1349
        }
1282
1350
 
1283
 
      return (*receiver)(receiver_baton, log_entry, pool);
1284
 
    }
1285
 
  else
1286
 
    {
1287
 
      return SVN_NO_ERROR;
1288
 
    }
 
1351
      /* Pass a scratch pool to ensure no temporary state stored
 
1352
         by the receiver callback persists. */
 
1353
      scratch_pool = svn_pool_create(pool);
 
1354
      SVN_ERR(receiver(receiver_baton, log_entry, scratch_pool));
 
1355
      svn_pool_destroy(scratch_pool);
 
1356
    }
 
1357
 
 
1358
  return SVN_NO_ERROR;
1289
1359
}
1290
1360
 
1291
1361
/* This controls how many history objects we keep open.  For any targets
1334
1404
      const char *this_path = APR_ARRAY_IDX(paths, i, const char *);
1335
1405
      struct path_info *info = apr_palloc(pool,
1336
1406
                                          sizeof(struct path_info));
 
1407
      svn_pool_clear(iterpool);
1337
1408
 
1338
1409
      if (authz_read_func)
1339
1410
        {
1340
1411
          svn_boolean_t readable;
1341
 
 
1342
 
          svn_pool_clear(iterpool);
1343
 
 
1344
1412
          SVN_ERR(authz_read_func(&readable, root, this_path,
1345
1413
                                  authz_read_baton, iterpool));
1346
1414
          if (! readable)
1354
1422
 
1355
1423
      if (i < MAX_OPEN_HISTORIES)
1356
1424
        {
1357
 
          err = svn_fs_node_history(&info->hist, root, this_path, pool);
 
1425
          err = svn_fs_node_history2(&info->hist, root, this_path, pool,
 
1426
                                     iterpool);
1358
1427
          if (err
1359
1428
              && ignore_missing_locations
1360
1429
              && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
1378
1447
      err = get_history(info, fs,
1379
1448
                        strict_node_history,
1380
1449
                        authz_read_func, authz_read_baton,
1381
 
                        hist_start, pool);
 
1450
                        hist_start, pool, iterpool);
1382
1451
      if (err
1383
1452
          && ignore_missing_locations
1384
1453
          && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
1486
1555
    {
1487
1556
      int i;
1488
1557
      struct rangelist_path *rp = apr_palloc(subpool, sizeof(*rp));
1489
 
      apr_hash_this(hi, (void *) &rp->path, NULL,
1490
 
                    (void *) &rp->rangelist);
 
1558
 
 
1559
      rp->path = apr_hash_this_key(hi);
 
1560
      rp->rangelist = apr_hash_this_val(hi);
1491
1561
      APR_ARRAY_PUSH(rangelist_paths, struct rangelist_path *) = rp;
1492
1562
 
1493
1563
      /* We need to make local copies of the rangelist, since we will be
1515
1585
 
1516
1586
      /* First, sort the list such that the start revision of the first
1517
1587
         revision arrays are sorted. */
1518
 
      qsort(rangelist_paths->elts, rangelist_paths->nelts,
1519
 
            rangelist_paths->elt_size, compare_rangelist_paths);
 
1588
      svn_sort__array(rangelist_paths, compare_rangelist_paths);
1520
1589
 
1521
1590
      /* Next, find the number of revision ranges which start with the same
1522
1591
         revision. */
1627
1696
        const apr_array_header_t *paths,
1628
1697
        svn_mergeinfo_t log_target_history_as_mergeinfo,
1629
1698
        svn_mergeinfo_t processed,
1630
 
        apr_hash_t *nested_merges,
 
1699
        svn_bit_array__t *nested_merges,
1631
1700
        svn_revnum_t hist_start,
1632
1701
        svn_revnum_t hist_end,
1633
1702
        int limit,
1680
1749
handle_merged_revisions(svn_revnum_t rev,
1681
1750
                        svn_fs_t *fs,
1682
1751
                        svn_mergeinfo_t log_target_history_as_mergeinfo,
1683
 
                        apr_hash_t *nested_merges,
 
1752
                        svn_bit_array__t *nested_merges,
1684
1753
                        svn_mergeinfo_t processed,
1685
1754
                        svn_mergeinfo_t added_mergeinfo,
1686
1755
                        svn_mergeinfo_t deleted_mergeinfo,
1711
1780
                                          TRUE, pool));
1712
1781
 
1713
1782
  SVN_ERR_ASSERT(combined_list != NULL);
1714
 
  qsort(combined_list->elts, combined_list->nelts,
1715
 
        combined_list->elt_size, compare_path_list_range);
 
1783
  svn_sort__array(combined_list, compare_path_list_range);
1716
1784
 
1717
1785
  /* Because the combined_lists are ordered youngest to oldest,
1718
1786
     iterate over them in reverse. */
1889
1957
        const apr_array_header_t *paths,
1890
1958
        svn_mergeinfo_t log_target_history_as_mergeinfo,
1891
1959
        svn_mergeinfo_t processed,
1892
 
        apr_hash_t *nested_merges,
 
1960
        svn_bit_array__t *nested_merges,
1893
1961
        svn_revnum_t hist_start,
1894
1962
        svn_revnum_t hist_end,
1895
1963
        int limit,
1907
1975
        void *authz_read_baton,
1908
1976
        apr_pool_t *pool)
1909
1977
{
1910
 
  apr_pool_t *iterpool;
 
1978
  apr_pool_t *iterpool, *iterpool2;
1911
1979
  apr_pool_t *subpool = NULL;
1912
1980
  apr_array_header_t *revs = NULL;
1913
1981
  apr_hash_t *rev_mergeinfo = NULL;
1943
2011
     where a path was changed to the array, or if they wanted
1944
2012
     history in reverse order just send it to them right away. */
1945
2013
  iterpool = svn_pool_create(pool);
 
2014
  iterpool2 = svn_pool_create(pool);
1946
2015
  for (current = hist_end;
1947
2016
       any_histories_left;
1948
2017
       current = next_history_rev(histories))
1956
2025
          struct path_info *info = APR_ARRAY_IDX(histories, i,
1957
2026
                                                 struct path_info *);
1958
2027
 
 
2028
          svn_pool_clear(iterpool2);
 
2029
 
1959
2030
          /* Check history for this path in current rev. */
1960
2031
          SVN_ERR(check_history(&changed, info, fs, current,
1961
2032
                                strict_node_history, authz_read_func,
1962
 
                                authz_read_baton, hist_start, pool));
 
2033
                                authz_read_baton, hist_start, pool,
 
2034
                                iterpool2));
1963
2035
          if (! info->done)
1964
2036
            any_histories_left = TRUE;
1965
2037
        }
1966
2038
 
 
2039
      svn_pool_clear(iterpool2);
 
2040
 
1967
2041
      /* If any of the paths changed in this rev then add or send it. */
1968
2042
      if (changed)
1969
2043
        {
1993
2067
                                                     &deleted_mergeinfo,
1994
2068
                                                     &changes,
1995
2069
                                                     fs, cur_paths,
1996
 
                                                     current, iterpool,
1997
 
                                                     iterpool));
 
2070
                                                     current,
 
2071
                                                     iterpool, iterpool));
1998
2072
              has_children = (apr_hash_count(added_mergeinfo) > 0
1999
2073
                              || apr_hash_count(deleted_mergeinfo) > 0);
2000
2074
            }
2020
2094
                         single hash to be shared across all of the merged
2021
2095
                         recursions so we can track and squelch duplicates. */
2022
2096
                      subpool = svn_pool_create(pool);
2023
 
                      nested_merges = svn_hash__make(subpool);
 
2097
                      nested_merges = svn_bit_array__create(hist_end, subpool);
2024
2098
                      processed = svn_hash__make(subpool);
2025
2099
                    }
2026
2100
 
2052
2126
 
2053
2127
              if (added_mergeinfo || deleted_mergeinfo)
2054
2128
                {
2055
 
                  svn_revnum_t *cur_rev = apr_pcalloc(pool, sizeof(*cur_rev));
 
2129
                  svn_revnum_t *cur_rev =
 
2130
                    apr_pmemdup(pool, &current, sizeof(cur_rev));
2056
2131
                  struct added_deleted_mergeinfo *add_and_del_mergeinfo =
2057
2132
                    apr_palloc(pool, sizeof(*add_and_del_mergeinfo));
2058
2133
 
2059
 
                  if (added_mergeinfo)
2060
 
                    add_and_del_mergeinfo->added_mergeinfo =
2061
 
                      svn_mergeinfo_dup(added_mergeinfo, pool);
2062
 
 
2063
 
                  if (deleted_mergeinfo)
2064
 
                    add_and_del_mergeinfo->deleted_mergeinfo =
2065
 
                      svn_mergeinfo_dup(deleted_mergeinfo, pool);
2066
 
 
2067
 
                  *cur_rev = current;
 
2134
                  /* If we have added or deleted mergeinfo, both are non-null */
 
2135
                  SVN_ERR_ASSERT(added_mergeinfo && deleted_mergeinfo);
 
2136
                  add_and_del_mergeinfo->added_mergeinfo =
 
2137
                    svn_mergeinfo_dup(added_mergeinfo, pool);
 
2138
                  add_and_del_mergeinfo->deleted_mergeinfo =
 
2139
                    svn_mergeinfo_dup(deleted_mergeinfo, pool);
 
2140
 
2068
2141
                  if (! rev_mergeinfo)
2069
2142
                    rev_mergeinfo = svn_hash__make(pool);
2070
2143
                  apr_hash_set(rev_mergeinfo, cur_rev, sizeof(*cur_rev),
2073
2146
            }
2074
2147
        }
2075
2148
    }
 
2149
  svn_pool_destroy(iterpool2);
2076
2150
  svn_pool_destroy(iterpool);
2077
2151
 
2078
2152
  if (subpool)
2112
2186
          SVN_ERR(send_log(current, fs, NULL,
2113
2187
                           log_target_history_as_mergeinfo, nested_merges,
2114
2188
                           discover_changed_paths, subtractive_merge,
2115
 
                           handling_merged_revisions, revprops, has_children,
 
2189
                           handling_merged_revisions,
 
2190
                           revprops, has_children,
2116
2191
                           receiver, receiver_baton, authz_read_func,
2117
2192
                           authz_read_baton, iterpool));
2118
2193
          if (has_children)
2120
2195
              if (!nested_merges)
2121
2196
                {
2122
2197
                  subpool = svn_pool_create(pool);
2123
 
                  nested_merges = svn_hash__make(subpool);
 
2198
                  nested_merges = svn_bit_array__create(current, subpool);
2124
2199
                }
2125
2200
 
2126
2201
              SVN_ERR(handle_merged_revisions(current, fs,
2130
2205
                                              added_mergeinfo,
2131
2206
                                              deleted_mergeinfo,
2132
2207
                                              discover_changed_paths,
2133
 
                                              strict_node_history, revprops,
 
2208
                                              strict_node_history,
 
2209
                                              revprops,
2134
2210
                                              receiver, receiver_baton,
2135
2211
                                              authz_read_func,
2136
2212
                                              authz_read_baton,
2252
2328
  svn_boolean_t descending_order;
2253
2329
  svn_mergeinfo_t paths_history_mergeinfo = NULL;
2254
2330
 
 
2331
  if (revprops)
 
2332
    {
 
2333
      int i;
 
2334
      apr_array_header_t *new_revprops
 
2335
        = apr_array_make(pool, revprops->nelts, sizeof(svn_string_t *));
 
2336
 
 
2337
      for (i = 0; i < revprops->nelts; ++i)
 
2338
        APR_ARRAY_PUSH(new_revprops, svn_string_t *)
 
2339
          = svn_string_create(APR_ARRAY_IDX(revprops, i, const char *), pool);
 
2340
 
 
2341
      revprops = new_revprops;
 
2342
    }
 
2343
 
2255
2344
  /* Setup log range. */
2256
2345
  SVN_ERR(svn_fs_youngest_rev(&head, fs, pool));
2257
2346
 
2321
2410
        }
2322
2411
 
2323
2412
      send_count = end - start + 1;
2324
 
      if (limit && send_count > limit)
 
2413
      if (limit > 0 && send_count > limit)
2325
2414
        send_count = limit;
2326
2415
      for (i = 0; i < send_count; ++i)
2327
2416
        {
2335
2424
            rev = start + i;
2336
2425
          SVN_ERR(send_log(rev, fs, NULL, NULL, NULL,
2337
2426
                           discover_changed_paths, FALSE,
2338
 
                           FALSE, revprops, FALSE, receiver,
2339
 
                           receiver_baton, authz_read_func,
2340
 
                           authz_read_baton, iterpool));
 
2427
                           FALSE, revprops, FALSE, receiver, receiver_baton,
 
2428
                           authz_read_func, authz_read_baton, iterpool));
2341
2429
        }
2342
2430
      svn_pool_destroy(iterpool);
2343
2431
 
2363
2451
 
2364
2452
  return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, start, end,
2365
2453
                 limit, discover_changed_paths, strict_node_history,
2366
 
                 include_merged_revisions, FALSE, FALSE, FALSE, revprops,
2367
 
                 descending_order, receiver, receiver_baton,
 
2454
                 include_merged_revisions, FALSE, FALSE, FALSE,
 
2455
                 revprops, descending_order, receiver, receiver_baton,
2368
2456
                 authz_read_func, authz_read_baton, pool);
2369
2457
}