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

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/log.c

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
#include "svn_compat.h"
33
33
#include "svn_error.h"
34
34
#include "svn_dirent_uri.h"
 
35
#include "svn_hash.h"
35
36
#include "svn_path.h"
36
37
#include "svn_sorts.h"
37
38
#include "svn_props.h"
41
42
#include "svn_private_config.h"
42
43
#include "private/svn_wc_private.h"
43
44
 
 
45
#include <assert.h>
44
46
 
45
47
/*** Getting misc. information ***/
46
48
 
90
92
}
91
93
 
92
94
svn_error_t *
93
 
svn_client__get_copy_source(const char *path_or_url,
 
95
svn_client__get_copy_source(const char **original_repos_relpath,
 
96
                            svn_revnum_t *original_revision,
 
97
                            const char *path_or_url,
94
98
                            const svn_opt_revision_t *revision,
95
 
                            const char **copyfrom_path,
96
 
                            svn_revnum_t *copyfrom_rev,
97
99
                            svn_client_ctx_t *ctx,
98
 
                            apr_pool_t *pool)
 
100
                            apr_pool_t *result_pool,
 
101
                            apr_pool_t *scratch_pool)
99
102
{
100
103
  svn_error_t *err;
101
104
  copyfrom_info_t copyfrom_info = { 0 };
102
 
  apr_pool_t *sesspool = svn_pool_create(pool);
 
105
  apr_pool_t *sesspool = svn_pool_create(scratch_pool);
103
106
  svn_ra_session_t *ra_session;
104
 
  svn_revnum_t at_rev;
105
 
  const char *at_url;
 
107
  svn_client__pathrev_t *at_loc;
106
108
 
107
109
  copyfrom_info.is_first = TRUE;
108
110
  copyfrom_info.path = NULL;
109
111
  copyfrom_info.rev = SVN_INVALID_REVNUM;
110
 
  copyfrom_info.pool = pool;
 
112
  copyfrom_info.pool = result_pool;
111
113
 
112
 
  SVN_ERR(svn_client__ra_session_from_path(&ra_session, &at_rev, &at_url,
113
 
                                           path_or_url, NULL,
114
 
                                           revision, revision,
115
 
                                           ctx, sesspool));
 
114
  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &at_loc,
 
115
                                            path_or_url, NULL,
 
116
                                            revision, revision,
 
117
                                            ctx, sesspool));
116
118
 
117
119
  /* Find the copy source.  Walk the location segments to find the revision
118
120
     at which this node was created (copied or added). */
119
121
 
120
 
  err = svn_ra_get_location_segments(ra_session, "", at_rev, at_rev,
 
122
  err = svn_ra_get_location_segments(ra_session, "", at_loc->rev, at_loc->rev,
121
123
                                     SVN_INVALID_REVNUM,
122
124
                                     copyfrom_info_receiver, &copyfrom_info,
123
 
                                     pool);
 
125
                                     scratch_pool);
124
126
 
125
127
  svn_pool_destroy(sesspool);
126
128
 
134
136
            svn_error_clear(err);
135
137
            err = SVN_NO_ERROR;
136
138
 
137
 
            *copyfrom_path = NULL;
138
 
            *copyfrom_rev = SVN_INVALID_REVNUM;
 
139
            *original_repos_relpath = NULL;
 
140
            *original_revision = SVN_INVALID_REVNUM;
139
141
        }
140
142
      return svn_error_trace(err);
141
143
    }
142
144
 
143
 
  *copyfrom_path = copyfrom_info.path;
144
 
  *copyfrom_rev = copyfrom_info.rev;
 
145
  *original_repos_relpath = copyfrom_info.path;
 
146
  *original_revision = copyfrom_info.rev;
145
147
  return SVN_NO_ERROR;
146
148
}
147
149
 
203
205
            }
204
206
 
205
207
          if (rb->ra_session == NULL)
206
 
            SVN_ERR(svn_client_open_ra_session(&rb->ra_session,
207
 
                                                rb->ra_session_url,
208
 
                                               rb->ctx, rb->ra_session_pool));
 
208
            SVN_ERR(svn_client_open_ra_session2(&rb->ra_session,
 
209
                                                rb->ra_session_url, NULL,
 
210
                                                rb->ctx, rb->ra_session_pool,
 
211
                                                pool));
209
212
 
210
213
          SVN_ERR(svn_ra_rev_prop(rb->ra_session, log_entry->revision,
211
214
                                  name, &value, pool));
212
215
          if (log_entry->revprops == NULL)
213
216
            log_entry->revprops = apr_hash_make(pool);
214
 
          apr_hash_set(log_entry->revprops, (const void *)name,
215
 
                       APR_HASH_KEY_STRING, (const void *)value);
 
217
          svn_hash_sets(log_entry->revprops, name, value);
216
218
        }
217
219
      if (log_entry->revprops)
218
220
        {
219
221
          /* Pre-1.5 servers send the standard revprops unconditionally;
220
222
             clear those the caller doesn't want. */
221
223
          if (!want_author)
222
 
            apr_hash_set(log_entry->revprops, SVN_PROP_REVISION_AUTHOR,
223
 
                         APR_HASH_KEY_STRING, NULL);
 
224
            svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR, NULL);
224
225
          if (!want_date)
225
 
            apr_hash_set(log_entry->revprops, SVN_PROP_REVISION_DATE,
226
 
                         APR_HASH_KEY_STRING, NULL);
 
226
            svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_DATE, NULL);
227
227
          if (!want_log)
228
 
            apr_hash_set(log_entry->revprops, SVN_PROP_REVISION_LOG,
229
 
                         APR_HASH_KEY_STRING, NULL);
 
228
            svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_LOG, NULL);
230
229
        }
231
230
    }
232
231
  else
233
232
    {
234
233
      if (rb->ra_session == NULL)
235
 
        SVN_ERR(svn_client_open_ra_session(&rb->ra_session,
236
 
                                           rb->ra_session_url,
237
 
                                           rb->ctx, rb->ra_session_pool));
 
234
        SVN_ERR(svn_client_open_ra_session2(&rb->ra_session,
 
235
                                            rb->ra_session_url, NULL,
 
236
                                            rb->ctx, rb->ra_session_pool,
 
237
                                            pool));
238
238
 
239
239
      SVN_ERR(svn_ra_rev_proplist(rb->ra_session, log_entry->revision,
240
240
                                  &log_entry->revprops, pool));
261
261
  return rb->receiver(rb->baton, log_entry, pool);
262
262
}
263
263
 
264
 
 
265
 
/*** Public Interface. ***/
266
 
 
267
 
 
268
 
svn_error_t *
269
 
svn_client_log5(const apr_array_header_t *targets,
270
 
                const svn_opt_revision_t *peg_revision,
271
 
                const apr_array_header_t *revision_ranges,
272
 
                int limit,
273
 
                svn_boolean_t discover_changed_paths,
274
 
                svn_boolean_t strict_node_history,
275
 
                svn_boolean_t include_merged_revisions,
276
 
                const apr_array_header_t *revprops,
277
 
                svn_log_entry_receiver_t real_receiver,
278
 
                void *real_receiver_baton,
279
 
                svn_client_ctx_t *ctx,
280
 
                apr_pool_t *pool)
281
 
{
282
 
  svn_ra_session_t *ra_session;
283
 
  const char *url_or_path;
284
 
  svn_boolean_t has_log_revprops;
285
 
  const char *actual_url;
286
 
  apr_array_header_t *condensed_targets;
287
 
  svn_revnum_t ignored_revnum;
288
 
  svn_opt_revision_t session_opt_rev;
289
 
  const char *ra_target;
290
 
  pre_15_receiver_baton_t rb = {0};
291
 
  apr_pool_t *iterpool;
292
 
  int i;
293
 
  svn_opt_revision_t peg_rev;
294
 
 
295
 
  if (revision_ranges->nelts == 0)
296
 
    {
297
 
      return svn_error_create
298
 
        (SVN_ERR_CLIENT_BAD_REVISION, NULL,
299
 
         _("Missing required revision specification"));
300
 
    }
301
 
 
302
 
  /* Make a copy of PEG_REVISION, we may need to change it to a
303
 
     default value. */
304
 
  peg_rev.kind = peg_revision->kind;
305
 
  peg_rev.value = peg_revision->value;
306
 
 
307
 
  /* Use the passed URL, if there is one.  */
308
 
  url_or_path = APR_ARRAY_IDX(targets, 0, const char *);
309
 
  session_opt_rev.kind = svn_opt_revision_unspecified;
310
 
 
311
 
  for (i = 0; i < revision_ranges->nelts; i++)
 
264
/* Resolve the URLs or WC path in TARGETS as per the svn_client_log5 API.
 
265
 
 
266
   The limitations on TARGETS specified by svn_client_log5 are enforced here.
 
267
   So TARGETS can only contain a single WC path or a URL and zero or more
 
268
   relative paths -- anything else will raise an error. 
 
269
 
 
270
   PEG_REVISION, TARGETS, and CTX are as per svn_client_log5.
 
271
 
 
272
   If TARGETS contains a single WC path then set *RA_TARGET to the absolute
 
273
   path of that single path if PEG_REVISION is dependent on the working copy
 
274
   (e.g. PREV).  Otherwise set *RA_TARGET to the corresponding URL for the
 
275
   single WC path.  Set *RELATIVE_TARGETS to an array with a single
 
276
   element "".
 
277
 
 
278
   If TARGETS contains only a single URL, then set *RA_TARGET to a copy of
 
279
   that URL and *RELATIVE_TARGETS to an array with a single element "".
 
280
 
 
281
   If TARGETS contains a single URL and one or more relative paths, then
 
282
   set *RA_TARGET to a copy of that URL and *RELATIVE_TARGETS to a copy of
 
283
   each relative path after the URL.
 
284
 
 
285
   If *PEG_REVISION is svn_opt_revision_unspecified, then *PEG_REVISION is
 
286
   set to svn_opt_revision_head for URLs or svn_opt_revision_working for a
 
287
   WC path.
 
288
 
 
289
   *RA_TARGET and *RELATIVE_TARGETS are allocated in RESULT_POOL. */
 
290
static svn_error_t *
 
291
resolve_log_targets(apr_array_header_t **relative_targets,
 
292
                    const char **ra_target,
 
293
                    svn_opt_revision_t *peg_revision,
 
294
                    const apr_array_header_t *targets,
 
295
                    svn_client_ctx_t *ctx,
 
296
                    apr_pool_t *result_pool,
 
297
                    apr_pool_t *scratch_pool)
 
298
{
 
299
  int i;
 
300
  svn_boolean_t url_targets;
 
301
 
 
302
  /* Per svn_client_log5, TARGETS contains either a URL followed by zero or
 
303
     more relative paths, or one working copy path. */
 
304
  const char *url_or_path = APR_ARRAY_IDX(targets, 0, const char *);
 
305
 
 
306
  /* svn_client_log5 requires at least one target. */
 
307
  if (targets->nelts == 0)
 
308
    return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
 
309
                            _("No valid target found"));
 
310
 
 
311
  /* Initialize the output array.  At a minimum, we need room for one
 
312
     (possibly empty) relpath.  Otherwise, we have to hold a relpath
 
313
     for every item in TARGETS except the first.  */
 
314
  *relative_targets = apr_array_make(result_pool,
 
315
                                     MAX(1, targets->nelts - 1),
 
316
                                     sizeof(const char *));
 
317
 
 
318
  if (svn_path_is_url(url_or_path))
 
319
    {
 
320
      /* An unspecified PEG_REVISION for a URL path defaults
 
321
         to svn_opt_revision_head. */
 
322
      if (peg_revision->kind == svn_opt_revision_unspecified)
 
323
        peg_revision->kind = svn_opt_revision_head;
 
324
 
 
325
      /* The logic here is this: If we get passed one argument, we assume
 
326
         it is the full URL to a file/dir we want log info for. If we get
 
327
         a URL plus some paths, then we assume that the URL is the base,
 
328
         and that the paths passed are relative to it.  */
 
329
      if (targets->nelts > 1)
 
330
        {
 
331
          /* We have some paths, let's use them. Start after the URL.  */
 
332
          for (i = 1; i < targets->nelts; i++)
 
333
            {
 
334
              const char *target;
 
335
 
 
336
              target = APR_ARRAY_IDX(targets, i, const char *);
 
337
 
 
338
              if (svn_path_is_url(target) || svn_dirent_is_absolute(target))
 
339
                return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
 
340
                                         _("'%s' is not a relative path"),
 
341
                                          target);
 
342
 
 
343
              APR_ARRAY_PUSH(*relative_targets, const char *) =
 
344
                apr_pstrdup(result_pool, target);
 
345
            }
 
346
        }
 
347
      else
 
348
        {
 
349
          /* If we have a single URL, then the session will be rooted at
 
350
             it, so just send an empty string for the paths we are
 
351
             interested in. */
 
352
          APR_ARRAY_PUSH(*relative_targets, const char *) = "";
 
353
        }
 
354
 
 
355
      /* Remember that our targets are URLs. */
 
356
      url_targets = TRUE;
 
357
    }
 
358
  else /* WC path target. */
 
359
    {
 
360
      const char *target;
 
361
      const char *target_abspath;
 
362
 
 
363
      url_targets = FALSE;
 
364
      if (targets->nelts > 1)
 
365
        return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
 
366
                                _("When specifying working copy paths, only "
 
367
                                  "one target may be given"));
 
368
 
 
369
      /* An unspecified PEG_REVISION for a working copy path defaults
 
370
         to svn_opt_revision_working. */
 
371
      if (peg_revision->kind == svn_opt_revision_unspecified)
 
372
        peg_revision->kind = svn_opt_revision_working;
 
373
 
 
374
      /* Get URLs for each target */
 
375
      target = APR_ARRAY_IDX(targets, 0, const char *);
 
376
 
 
377
      SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, scratch_pool));
 
378
      SVN_ERR(svn_wc__node_get_url(&url_or_path, ctx->wc_ctx, target_abspath,
 
379
                                   scratch_pool, scratch_pool));
 
380
      APR_ARRAY_PUSH(*relative_targets, const char *) = "";
 
381
    }
 
382
 
 
383
  /* If this is a revision type that requires access to the working copy,
 
384
   * we use our initial target path to figure out where to root the RA
 
385
   * session, otherwise we use our URL. */
 
386
  if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
 
387
    {
 
388
      if (url_targets)
 
389
        return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
 
390
                                _("PREV, BASE, or COMMITTED revision "
 
391
                                  "keywords are invalid for URL"));
 
392
 
 
393
      else
 
394
        SVN_ERR(svn_dirent_get_absolute(
 
395
          ra_target, APR_ARRAY_IDX(targets, 0, const char *), result_pool));
 
396
    }
 
397
  else
 
398
    {
 
399
      *ra_target = apr_pstrdup(result_pool, url_or_path);
 
400
    }
 
401
 
 
402
  return SVN_NO_ERROR;
 
403
}
 
404
 
 
405
/* Keep track of oldest and youngest opt revs found.
 
406
 
 
407
   If REV is younger than *YOUNGEST_REV, or *YOUNGEST_REV is
 
408
   svn_opt_revision_unspecified, then set *YOUNGEST_REV equal to REV.
 
409
 
 
410
   If REV is older than *OLDEST_REV, or *OLDEST_REV is
 
411
   svn_opt_revision_unspecified, then set *OLDEST_REV equal to REV. */
 
412
static void
 
413
find_youngest_and_oldest_revs(svn_revnum_t *youngest_rev,
 
414
                              svn_revnum_t *oldest_rev,
 
415
                              svn_revnum_t rev)
 
416
{
 
417
  /* Is REV younger than YOUNGEST_REV? */
 
418
  if (! SVN_IS_VALID_REVNUM(*youngest_rev)
 
419
      || rev > *youngest_rev)
 
420
    *youngest_rev = rev;
 
421
 
 
422
  if (! SVN_IS_VALID_REVNUM(*oldest_rev)
 
423
      || rev < *oldest_rev)
 
424
    *oldest_rev = rev;
 
425
}
 
426
 
 
427
typedef struct rev_range_t
 
428
{
 
429
  svn_revnum_t range_start;
 
430
  svn_revnum_t range_end;
 
431
} rev_range_t;
 
432
 
 
433
/* Convert array of svn_opt_revision_t ranges to an array of svn_revnum_t
 
434
   ranges.
 
435
 
 
436
   Given a log target URL_OR_ABSPATH@PEG_REV and an array of
 
437
   svn_opt_revision_range_t's OPT_REV_RANGES, resolve the opt revs in
 
438
   OPT_REV_RANGES to svn_revnum_t's and return these in *REVISION_RANGES, an
 
439
   array of rev_range_t *.
 
440
 
 
441
   Set *YOUNGEST_REV and *OLDEST_REV to the youngest and oldest revisions
 
442
   found in *REVISION_RANGES.
 
443
 
 
444
   If the repository needs to be contacted to resolve svn_opt_revision_date or
 
445
   svn_opt_revision_head revisions, then the session used to do this is
 
446
   RA_SESSION; it must be an open session to any URL in the right repository.
 
447
*/
 
448
static svn_error_t*
 
449
convert_opt_rev_array_to_rev_range_array(
 
450
  apr_array_header_t **revision_ranges,
 
451
  svn_revnum_t *youngest_rev,
 
452
  svn_revnum_t *oldest_rev,
 
453
  svn_ra_session_t *ra_session,
 
454
  const char *url_or_abspath,
 
455
  const apr_array_header_t *opt_rev_ranges,
 
456
  const svn_opt_revision_t *peg_rev,
 
457
  svn_client_ctx_t *ctx,
 
458
  apr_pool_t *result_pool,
 
459
  apr_pool_t *scratch_pool)
 
460
{
 
461
  int i;
 
462
  svn_revnum_t head_rev = SVN_INVALID_REVNUM;
 
463
 
 
464
  /* Initialize the input/output parameters. */
 
465
  *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
 
466
 
 
467
  /* Convert OPT_REV_RANGES to an array of rev_range_t and find the youngest
 
468
     and oldest revision range that spans all of OPT_REV_RANGES. */
 
469
  *revision_ranges = apr_array_make(result_pool, opt_rev_ranges->nelts,
 
470
                                    sizeof(rev_range_t *));
 
471
 
 
472
  for (i = 0; i < opt_rev_ranges->nelts; i++)
312
473
    {
313
474
      svn_opt_revision_range_t *range;
314
 
 
315
 
      range = APR_ARRAY_IDX(revision_ranges, i, svn_opt_revision_range_t *);
316
 
 
 
475
      rev_range_t *rev_range;
 
476
      svn_boolean_t start_same_as_end = FALSE;
 
477
 
 
478
      range = APR_ARRAY_IDX(opt_rev_ranges, i, svn_opt_revision_range_t *);
 
479
 
 
480
      /* Right now RANGE can be any valid pair of svn_opt_revision_t's.  We
 
481
         will now convert all RANGEs in place to the corresponding
 
482
         svn_opt_revision_number kind. */
317
483
      if ((range->start.kind != svn_opt_revision_unspecified)
318
484
          && (range->end.kind == svn_opt_revision_unspecified))
319
485
        {
330
496
      else if (range->start.kind == svn_opt_revision_unspecified)
331
497
        {
332
498
          /* Default to any specified peg revision.  Otherwise, if the
333
 
           * first target is an URL, then we default to HEAD:0.  Lastly,
 
499
           * first target is a URL, then we default to HEAD:0.  Lastly,
334
500
           * the default is BASE:0 since WC@HEAD may not exist. */
335
 
          if (peg_rev.kind == svn_opt_revision_unspecified)
 
501
          if (peg_rev->kind == svn_opt_revision_unspecified)
336
502
            {
337
 
              if (svn_path_is_url(url_or_path))
 
503
              if (svn_path_is_url(url_or_abspath))
338
504
                range->start.kind = svn_opt_revision_head;
339
505
              else
340
506
                range->start.kind = svn_opt_revision_base;
341
507
            }
342
508
          else
343
 
            range->start = peg_rev;
 
509
            range->start = *peg_rev;
344
510
 
345
511
          if (range->end.kind == svn_opt_revision_unspecified)
346
512
            {
357
523
             _("Missing required revision specification"));
358
524
        }
359
525
 
360
 
      /* Determine the revision to open the RA session to. */
361
 
      if (session_opt_rev.kind == svn_opt_revision_unspecified)
362
 
        {
363
 
          if (range->start.kind == svn_opt_revision_number &&
364
 
              range->end.kind == svn_opt_revision_number)
365
 
            {
366
 
              session_opt_rev =
367
 
                  (range->start.value.number > range->end.value.number ?
368
 
                   range->start : range->end);
369
 
            }
370
 
          else if (range->start.kind == svn_opt_revision_head ||
371
 
                   range->end.kind == svn_opt_revision_head)
372
 
            {
373
 
              session_opt_rev.kind = svn_opt_revision_head;
374
 
            }
375
 
          else if (range->start.kind == svn_opt_revision_date &&
376
 
                   range->end.kind == svn_opt_revision_date)
377
 
            {
378
 
              session_opt_rev =
379
 
                  (range->start.value.date > range->end.value.date ?
380
 
                   range->start : range->end);
381
 
            }
382
 
        }
383
 
    }
384
 
 
385
 
  /* Use the passed URL, if there is one.  */
386
 
  if (svn_path_is_url(url_or_path))
387
 
    {
388
 
      /* Initialize this array, since we'll be building it below */
389
 
      condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
390
 
 
391
 
      /* The logic here is this: If we get passed one argument, we assume
392
 
         it is the full URL to a file/dir we want log info for. If we get
393
 
         a URL plus some paths, then we assume that the URL is the base,
394
 
         and that the paths passed are relative to it.  */
395
 
      if (targets->nelts > 1)
396
 
        {
397
 
          /* We have some paths, let's use them. Start after the URL.  */
398
 
          for (i = 1; i < targets->nelts; i++)
399
 
            {
400
 
              const char *target;
401
 
 
402
 
              target = APR_ARRAY_IDX(targets, i, const char *);
403
 
 
404
 
              if (svn_path_is_url(target) || svn_dirent_is_absolute(target))
405
 
                return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
406
 
                                         _("'%s' is not a relative path"),
407
 
                                          target);
408
 
 
409
 
              APR_ARRAY_PUSH(condensed_targets, const char *) = target;
410
 
            }
411
 
        }
 
526
      /* Does RANGE describe a single svn_opt_revision_t? */
 
527
      if (range->start.kind == range->end.kind)
 
528
        {
 
529
          if (range->start.kind == svn_opt_revision_number)
 
530
            {
 
531
              if (range->start.value.number == range->end.value.number)
 
532
                start_same_as_end = TRUE;
 
533
            }
 
534
          else if (range->start.kind == svn_opt_revision_date)
 
535
            {
 
536
              if (range->start.value.date == range->end.value.date)
 
537
                start_same_as_end = TRUE;
 
538
            }
 
539
          else
 
540
            {
 
541
              start_same_as_end = TRUE;
 
542
            }
 
543
        }
 
544
 
 
545
      rev_range = apr_palloc(result_pool, sizeof(*rev_range));
 
546
      SVN_ERR(svn_client__get_revision_number(
 
547
                &rev_range->range_start, &head_rev,
 
548
                ctx->wc_ctx, url_or_abspath, ra_session,
 
549
                &range->start, scratch_pool));
 
550
      if (start_same_as_end)
 
551
        rev_range->range_end = rev_range->range_start;
412
552
      else
413
 
        {
414
 
          /* If we have a single URL, then the session will be rooted at
415
 
             it, so just send an empty string for the paths we are
416
 
             interested in. */
417
 
          APR_ARRAY_PUSH(condensed_targets, const char *) = "";
418
 
        }
 
553
        SVN_ERR(svn_client__get_revision_number(
 
554
                  &rev_range->range_end, &head_rev,
 
555
                  ctx->wc_ctx, url_or_abspath, ra_session,
 
556
                  &range->end, scratch_pool));
 
557
 
 
558
      /* Possibly update the oldest and youngest revisions requested. */
 
559
      find_youngest_and_oldest_revs(youngest_rev,
 
560
                                    oldest_rev,
 
561
                                    rev_range->range_start);
 
562
      find_youngest_and_oldest_revs(youngest_rev,
 
563
                                    oldest_rev,
 
564
                                    rev_range->range_end);
 
565
      APR_ARRAY_PUSH(*revision_ranges, rev_range_t *) = rev_range;
419
566
    }
 
567
 
 
568
  return SVN_NO_ERROR;
 
569
}
 
570
 
 
571
static int
 
572
compare_rev_to_segment(const void *key_p,
 
573
                       const void *element_p)
 
574
{
 
575
  svn_revnum_t rev =
 
576
    * (svn_revnum_t *)key_p;
 
577
  const svn_location_segment_t *segment =
 
578
    *((const svn_location_segment_t * const *) element_p);
 
579
 
 
580
  if (rev < segment->range_start)
 
581
    return -1;
 
582
  else if (rev > segment->range_end)
 
583
    return 1;
420
584
  else
 
585
    return 0;
 
586
}
 
587
 
 
588
/* Run svn_ra_get_log2 for PATHS, one or more paths relative to RA_SESSION's
 
589
   common parent, for each revision in REVISION_RANGES, an array of
 
590
   rev_range_t.
 
591
 
 
592
   RA_SESSION is an open session pointing to ACTUAL_LOC.
 
593
 
 
594
   LOG_SEGMENTS is an array of svn_location_segment_t * items representing the
 
595
   history of PATHS from the oldest to youngest revisions found in
 
596
   REVISION_RANGES.
 
597
 
 
598
   The TARGETS, LIMIT, DISCOVER_CHANGED_PATHS, STRICT_NODE_HISTORY,
 
599
   INCLUDE_MERGED_REVISIONS, REVPROPS, REAL_RECEIVER, and REAL_RECEIVER_BATON
 
600
   parameters are all as per the svn_client_log5 API. */
 
601
static svn_error_t *
 
602
run_ra_get_log(apr_array_header_t *revision_ranges,
 
603
               apr_array_header_t *paths,
 
604
               apr_array_header_t *log_segments,
 
605
               svn_client__pathrev_t *actual_loc,
 
606
               svn_ra_session_t *ra_session,
 
607
               /* The following are as per svn_client_log5. */ 
 
608
               const apr_array_header_t *targets,
 
609
               int limit,
 
610
               svn_boolean_t discover_changed_paths,
 
611
               svn_boolean_t strict_node_history,
 
612
               svn_boolean_t include_merged_revisions,
 
613
               const apr_array_header_t *revprops,
 
614
               svn_log_entry_receiver_t real_receiver,
 
615
               void *real_receiver_baton,
 
616
               svn_client_ctx_t *ctx,
 
617
               apr_pool_t *scratch_pool)
 
618
{
 
619
  int i;
 
620
  pre_15_receiver_baton_t rb = {0};
 
621
  apr_pool_t *iterpool;
 
622
  svn_boolean_t has_log_revprops;
 
623
 
 
624
  SVN_ERR(svn_ra_has_capability(ra_session, &has_log_revprops,
 
625
                                SVN_RA_CAPABILITY_LOG_REVPROPS,
 
626
                                scratch_pool));
 
627
 
 
628
  if (!has_log_revprops)
421
629
    {
422
 
      apr_array_header_t *target_urls;
423
 
      apr_array_header_t *real_targets;
424
 
 
425
 
      /* See FIXME about multiple wc targets, below. */
426
 
      if (targets->nelts > 1)
427
 
        return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
428
 
                                _("When specifying working copy paths, only "
429
 
                                  "one target may be given"));
430
 
 
431
 
      /* An unspecified PEG_REVISION for a working copy path defautls
432
 
         to svn_opt_revision_working. */
433
 
      if (peg_rev.kind == svn_opt_revision_unspecified)
434
 
          peg_rev.kind = svn_opt_revision_working;
435
 
 
436
 
      /* Get URLs for each target */
437
 
      target_urls = apr_array_make(pool, 1, sizeof(const char *));
438
 
      real_targets = apr_array_make(pool, 1, sizeof(const char *));
439
 
      iterpool = svn_pool_create(pool);
440
 
      for (i = 0; i < targets->nelts; i++)
441
 
        {
442
 
          const char *url;
443
 
          const char *target = APR_ARRAY_IDX(targets, i, const char *);
444
 
          const char *target_abspath;
445
 
 
446
 
          svn_pool_clear(iterpool);
447
 
          SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, iterpool));
448
 
          SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, target_abspath,
449
 
                                       pool, iterpool));
450
 
 
451
 
          if (! url)
452
 
            return svn_error_createf
453
 
              (SVN_ERR_ENTRY_MISSING_URL, NULL,
454
 
               _("Entry '%s' has no URL"),
455
 
               svn_dirent_local_style(target, pool));
456
 
 
457
 
          APR_ARRAY_PUSH(target_urls, const char *) = url;
458
 
          APR_ARRAY_PUSH(real_targets, const char *) = target;
459
 
        }
460
 
 
461
 
      /* if we have no valid target_urls, just exit. */
462
 
      if (target_urls->nelts == 0)
463
 
        return SVN_NO_ERROR;
464
 
 
465
 
      /* Find the base URL and condensed targets relative to it. */
466
 
      SVN_ERR(svn_uri_condense_targets(&url_or_path, &condensed_targets,
467
 
                                       target_urls, TRUE, pool, iterpool));
468
 
 
469
 
      if (condensed_targets->nelts == 0)
470
 
        APR_ARRAY_PUSH(condensed_targets, const char *) = "";
471
 
 
472
 
      /* 'targets' now becomes 'real_targets', which has bogus,
473
 
         unversioned things removed from it. */
474
 
      targets = real_targets;
475
 
      svn_pool_destroy(iterpool);
476
 
    }
477
 
 
478
 
 
479
 
  {
480
 
    /* If this is a revision type that requires access to the working copy,
481
 
     * we use our initial target path to figure out where to root the RA
482
 
     * session, otherwise we use our URL. */
483
 
    if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_rev.kind))
484
 
      SVN_ERR(svn_dirent_condense_targets(&ra_target, NULL, targets,
485
 
                                          TRUE, pool, pool));
486
 
    else
487
 
      ra_target = url_or_path;
488
 
 
489
 
    SVN_ERR(svn_client__ra_session_from_path(&ra_session, &ignored_revnum,
490
 
                                             &actual_url, ra_target, NULL,
491
 
                                             &peg_rev, &session_opt_rev,
492
 
                                             ctx, pool));
493
 
 
494
 
    SVN_ERR(svn_ra_has_capability(ra_session, &has_log_revprops,
495
 
                                  SVN_RA_CAPABILITY_LOG_REVPROPS, pool));
496
 
 
497
 
    if (!has_log_revprops) {
498
630
      /* See above pre-1.5 notes. */
499
631
      rb.ctx = ctx;
500
632
 
501
633
      /* Create ra session on first use */
502
 
      rb.ra_session_pool = pool;
503
 
      rb.ra_session_url = actual_url;
 
634
      rb.ra_session_pool = scratch_pool;
 
635
      rb.ra_session_url = actual_loc->url;
504
636
    }
505
 
  }
506
637
 
507
638
  /* It's a bit complex to correctly handle the special revision words
508
639
   * such as "BASE", "COMMITTED", and "PREV".  For example, if the
551
682
   * epg wonders if the repository could send a unified stream of log
552
683
   * entries if the paths and revisions were passed down.
553
684
   */
554
 
  iterpool = svn_pool_create(pool);
 
685
  iterpool = svn_pool_create(scratch_pool);
555
686
  for (i = 0; i < revision_ranges->nelts; i++)
556
687
    {
557
 
      svn_revnum_t start_revnum, end_revnum, youngest_rev = SVN_INVALID_REVNUM;
 
688
      const char *old_session_url;
558
689
      const char *path = APR_ARRAY_IDX(targets, 0, const char *);
559
690
      const char *local_abspath_or_url;
560
 
      svn_opt_revision_range_t *range;
 
691
      rev_range_t *range;
561
692
      limit_receiver_baton_t lb;
562
693
      svn_log_entry_receiver_t passed_receiver;
563
694
      void *passed_receiver_baton;
564
695
      const apr_array_header_t *passed_receiver_revprops;
 
696
      svn_location_segment_t **matching_segment;
 
697
      svn_revnum_t younger_rev;
565
698
 
566
699
      svn_pool_clear(iterpool);
567
700
 
568
701
      if (!svn_path_is_url(path))
569
 
        SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, iterpool));
 
702
        SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path,
 
703
                                        iterpool));
570
704
      else
571
705
        local_abspath_or_url = path;
572
706
 
573
 
      range = APR_ARRAY_IDX(revision_ranges, i, svn_opt_revision_range_t *);
 
707
      range = APR_ARRAY_IDX(revision_ranges, i, rev_range_t *);
574
708
 
575
 
      SVN_ERR(svn_client__get_revision_number(&start_revnum, &youngest_rev,
576
 
                                              ctx->wc_ctx, local_abspath_or_url,
577
 
                                              ra_session, &range->start,
578
 
                                              iterpool));
579
 
      SVN_ERR(svn_client__get_revision_number(&end_revnum, &youngest_rev,
580
 
                                              ctx->wc_ctx, local_abspath_or_url,
581
 
                                              ra_session, &range->end,
582
 
                                              iterpool));
 
709
      /* Issue #4355: Account for renames spanning requested
 
710
         revision ranges. */
 
711
      younger_rev = MAX(range->range_start, range->range_end);
 
712
      matching_segment = bsearch(&younger_rev, log_segments->elts,
 
713
                                 log_segments->nelts, log_segments->elt_size,
 
714
                                 compare_rev_to_segment);
 
715
      /* LOG_SEGMENTS is supposed to represent the history of PATHS from
 
716
         the oldest to youngest revs in REVISION_RANGES.  This function's
 
717
         current sole caller svn_client_log5 *should* be providing
 
718
         LOG_SEGMENTS that span the oldest to youngest revs in
 
719
         REVISION_RANGES, even if one or more of the svn_location_segment_t's
 
720
         returned have NULL path members indicating a gap in the history. So
 
721
         MATCHING_SEGMENT should never be NULL, but clearly sometimes it is,
 
722
         see http://svn.haxx.se/dev/archive-2013-06/0522.shtml
 
723
         So to be safe we handle that case. */
 
724
      if (matching_segment == NULL)
 
725
        continue;
 
726
      
 
727
      /* A segment with a NULL path means there is gap in the history.
 
728
         We'll just proceed and let svn_ra_get_log2 fail with a useful
 
729
         error...*/
 
730
      if ((*matching_segment)->path != NULL)
 
731
        {
 
732
          /* ...but if there is history, then we must account for issue
 
733
             #4355 and make sure our RA session is pointing at the correct
 
734
             location. */
 
735
          const char *segment_url = svn_path_url_add_component2(
 
736
            actual_loc->repos_root_url, (*matching_segment)->path,
 
737
            scratch_pool);
 
738
          SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
 
739
                                                    ra_session,
 
740
                                                    segment_url,
 
741
                                                    scratch_pool));
 
742
        }
583
743
 
584
744
      if (has_log_revprops)
585
745
        {
609
769
        }
610
770
 
611
771
      SVN_ERR(svn_ra_get_log2(ra_session,
612
 
                              condensed_targets,
613
 
                              start_revnum,
614
 
                              end_revnum,
 
772
                              paths,
 
773
                              range->range_start,
 
774
                              range->range_end,
615
775
                              limit,
616
776
                              discover_changed_paths,
617
777
                              strict_node_history,
634
794
 
635
795
  return SVN_NO_ERROR;
636
796
}
 
797
 
 
798
/*** Public Interface. ***/
 
799
 
 
800
svn_error_t *
 
801
svn_client_log5(const apr_array_header_t *targets,
 
802
                const svn_opt_revision_t *peg_revision,
 
803
                const apr_array_header_t *opt_rev_ranges,
 
804
                int limit,
 
805
                svn_boolean_t discover_changed_paths,
 
806
                svn_boolean_t strict_node_history,
 
807
                svn_boolean_t include_merged_revisions,
 
808
                const apr_array_header_t *revprops,
 
809
                svn_log_entry_receiver_t real_receiver,
 
810
                void *real_receiver_baton,
 
811
                svn_client_ctx_t *ctx,
 
812
                apr_pool_t *pool)
 
813
{
 
814
  svn_ra_session_t *ra_session;
 
815
  const char *old_session_url;
 
816
  const char *ra_target;
 
817
  svn_opt_revision_t youngest_opt_rev;
 
818
  svn_revnum_t youngest_rev;
 
819
  svn_revnum_t oldest_rev;
 
820
  svn_opt_revision_t peg_rev;
 
821
  svn_client__pathrev_t *actual_loc;
 
822
  apr_array_header_t *log_segments;
 
823
  apr_array_header_t *revision_ranges;
 
824
  apr_array_header_t *relative_targets;
 
825
 
 
826
  if (opt_rev_ranges->nelts == 0)
 
827
    {
 
828
      return svn_error_create
 
829
        (SVN_ERR_CLIENT_BAD_REVISION, NULL,
 
830
         _("Missing required revision specification"));
 
831
    }
 
832
 
 
833
  /* Make a copy of PEG_REVISION, we may need to change it to a
 
834
     default value. */
 
835
  peg_rev = *peg_revision;
 
836
 
 
837
  SVN_ERR(resolve_log_targets(&relative_targets, &ra_target, &peg_rev,
 
838
                              targets, ctx, pool, pool));
 
839
 
 
840
  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &actual_loc,
 
841
                                            ra_target, NULL, &peg_rev, &peg_rev,
 
842
                                            ctx, pool));
 
843
 
 
844
  /* Convert OPT_REV_RANGES to an array of rev_range_t and find the youngest
 
845
     and oldest revision range that spans all of OPT_REV_RANGES. */
 
846
  SVN_ERR(convert_opt_rev_array_to_rev_range_array(&revision_ranges,
 
847
                                                   &youngest_rev,
 
848
                                                   &oldest_rev,
 
849
                                                   ra_session,
 
850
                                                   ra_target,
 
851
                                                   opt_rev_ranges, &peg_rev,
 
852
                                                   ctx, pool,  pool));
 
853
 
 
854
  /* Make ACTUAL_LOC and RA_SESSION point to the youngest operative rev. */
 
855
  youngest_opt_rev.kind = svn_opt_revision_number;
 
856
  youngest_opt_rev.value.number = youngest_rev;
 
857
  SVN_ERR(svn_client__resolve_rev_and_url(&actual_loc, ra_session,
 
858
                                          ra_target, &peg_rev,
 
859
                                          &youngest_opt_rev, ctx, pool));
 
860
  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
 
861
                                            actual_loc->url, pool));
 
862
 
 
863
  /* Save us an RA layer round trip if we are on the repository root and
 
864
     know the result in advance.  All the revision data has already been
 
865
     validated.
 
866
   */
 
867
  if (strcmp(actual_loc->url, actual_loc->repos_root_url) == 0)
 
868
    {
 
869
      svn_location_segment_t *segment = apr_pcalloc(pool, sizeof(*segment));
 
870
      log_segments = apr_array_make(pool, 1, sizeof(segment));
 
871
 
 
872
      segment->range_start = oldest_rev;
 
873
      segment->range_end = actual_loc->rev;
 
874
      segment->path = "";
 
875
      APR_ARRAY_PUSH(log_segments, svn_location_segment_t *) = segment;
 
876
    }
 
877
  else
 
878
    {
 
879
      /* Get the svn_location_segment_t's representing the requested log
 
880
       * ranges. */
 
881
      SVN_ERR(svn_client__repos_location_segments(&log_segments, ra_session,
 
882
                                                  actual_loc->url,
 
883
                                                  actual_loc->rev, /* peg */
 
884
                                                  actual_loc->rev, /* start */
 
885
                                                  oldest_rev,      /* end */
 
886
                                                  ctx, pool));
 
887
    }
 
888
 
 
889
 
 
890
  SVN_ERR(run_ra_get_log(revision_ranges, relative_targets, log_segments,
 
891
                         actual_loc, ra_session, targets, limit,
 
892
                         discover_changed_paths, strict_node_history,
 
893
                         include_merged_revisions, revprops, real_receiver,
 
894
                         real_receiver_baton, ctx, pool));
 
895
 
 
896
  return SVN_NO_ERROR;
 
897
}