~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_repos/rev_hunt.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* rev_hunt.c --- routines to hunt down particular fs revisions and
 
2
 *                their properties.
 
3
 *
 
4
 * ====================================================================
 
5
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
6
 *
 
7
 * This software is licensed as described in the file COPYING, which
 
8
 * you should have received as part of this distribution.  The terms
 
9
 * are also available at http://subversion.tigris.org/license-1.html.
 
10
 * If newer versions of this license are posted there, you may use a
 
11
 * newer version instead, at your option.
 
12
 *
 
13
 * This software consists of voluntary contributions made by many
 
14
 * individuals.  For exact contribution history, see the revision
 
15
 * history and logs, available at http://subversion.tigris.org/.
 
16
 * ====================================================================
 
17
 */
 
18
 
 
19
 
 
20
#include <string.h>
 
21
#include "svn_private_config.h"
 
22
#include "svn_pools.h"
 
23
#include "svn_error.h"
 
24
#include "svn_fs.h"
 
25
#include "svn_repos.h"
 
26
#include "svn_string.h"
 
27
#include "svn_time.h"
 
28
#include "svn_sorts.h"
 
29
#include "svn_props.h"
 
30
#include "repos.h"
 
31
 
 
32
#include <assert.h>
 
33
 
 
34
 
 
35
/* Note:  this binary search assumes that the datestamp properties on
 
36
   each revision are in chronological order.  That is if revision A >
 
37
   revision B, then A's datestamp is younger then B's datestamp.
 
38
 
 
39
   If some moron comes along and sets a bogus datestamp, this routine
 
40
   might not work right.
 
41
 
 
42
   ### todo:  you know, we *could* have svn_fs_change_rev_prop() do
 
43
   some semantic checking when it's asked to change special reserved
 
44
   svn: properties.  It could prevent such a problem. */
 
45
 
 
46
 
 
47
/* helper for svn_repos_dated_revision().
 
48
 
 
49
   Set *TM to the apr_time_t datestamp on revision REV in FS. */
 
50
static svn_error_t *
 
51
get_time (apr_time_t *tm,
 
52
          svn_fs_t *fs,
 
53
          svn_revnum_t rev,
 
54
          apr_pool_t *pool)
 
55
{
 
56
  svn_string_t *date_str;
 
57
 
 
58
  SVN_ERR (svn_fs_revision_prop (&date_str, fs, rev, SVN_PROP_REVISION_DATE,
 
59
                                 pool));
 
60
  if (! date_str)    
 
61
    return svn_error_createf
 
62
      (SVN_ERR_FS_GENERAL, NULL,
 
63
       _("Failed to find time on revision %ld"), rev);
 
64
 
 
65
  SVN_ERR (svn_time_from_cstring (tm, date_str->data, pool));
 
66
 
 
67
  return SVN_NO_ERROR;
 
68
}
 
69
 
 
70
 
 
71
svn_error_t *
 
72
svn_repos_dated_revision (svn_revnum_t *revision,
 
73
                          svn_repos_t *repos,
 
74
                          apr_time_t tm,
 
75
                          apr_pool_t *pool)
 
76
{
 
77
  svn_revnum_t rev_mid, rev_top, rev_bot, rev_latest;
 
78
  apr_time_t this_time;
 
79
  svn_fs_t *fs = repos->fs;
 
80
 
 
81
  /* Initialize top and bottom values of binary search. */
 
82
  SVN_ERR (svn_fs_youngest_rev (&rev_latest, fs, pool));
 
83
  rev_bot = 0;
 
84
  rev_top = rev_latest;
 
85
 
 
86
  while (rev_bot <= rev_top)
 
87
    {
 
88
      rev_mid = (rev_top + rev_bot) / 2;
 
89
      SVN_ERR (get_time (&this_time, fs, rev_mid, pool));
 
90
      
 
91
      if (this_time > tm)/* we've overshot */
 
92
        {
 
93
          apr_time_t previous_time;
 
94
 
 
95
          if ((rev_mid - 1) < 0)
 
96
            {
 
97
              *revision = 0;
 
98
              break;
 
99
            }
 
100
 
 
101
          /* see if time falls between rev_mid and rev_mid-1: */
 
102
          SVN_ERR (get_time (&previous_time, fs, rev_mid - 1, pool));
 
103
          if (previous_time <= tm)
 
104
            {
 
105
              *revision = rev_mid - 1;
 
106
              break;
 
107
            }
 
108
 
 
109
          rev_top = rev_mid - 1;
 
110
        }
 
111
 
 
112
      else if (this_time < tm) /* we've undershot */
 
113
        {
 
114
          apr_time_t next_time;
 
115
 
 
116
          if ((rev_mid + 1) > rev_latest)
 
117
            {
 
118
              *revision = rev_latest;
 
119
              break;
 
120
            }
 
121
          
 
122
          /* see if time falls between rev_mid and rev_mid+1: */
 
123
          SVN_ERR (get_time (&next_time, fs, rev_mid + 1, pool));
 
124
          if (next_time > tm)
 
125
            {
 
126
              *revision = rev_mid;
 
127
              break;
 
128
            }
 
129
 
 
130
          rev_bot = rev_mid + 1;
 
131
        }
 
132
 
 
133
      else
 
134
        {
 
135
          *revision = rev_mid;  /* exact match! */
 
136
          break;
 
137
        }
 
138
    }
 
139
 
 
140
  return SVN_NO_ERROR;
 
141
}
 
142
 
 
143
 
 
144
svn_error_t *
 
145
svn_repos_get_committed_info (svn_revnum_t *committed_rev,
 
146
                              const char **committed_date,
 
147
                              const char **last_author,
 
148
                              svn_fs_root_t *root,
 
149
                              const char *path,
 
150
                              apr_pool_t *pool)
 
151
{
 
152
  svn_fs_t *fs = svn_fs_root_fs (root);
 
153
 
 
154
  /* ### It might be simpler just to declare that revision
 
155
     properties have char * (i.e., UTF-8) values, not arbitrary
 
156
     binary values, hmmm. */
 
157
  svn_string_t *committed_date_s, *last_author_s;
 
158
  
 
159
  /* Get the CR field out of the node's skel. */
 
160
  SVN_ERR (svn_fs_node_created_rev (committed_rev, root, path, pool));
 
161
 
 
162
  /* Get the date property of this revision. */
 
163
  SVN_ERR (svn_fs_revision_prop (&committed_date_s, fs, *committed_rev,
 
164
                                 SVN_PROP_REVISION_DATE, pool));
 
165
 
 
166
  /* Get the author property of this revision. */
 
167
  SVN_ERR (svn_fs_revision_prop (&last_author_s, fs, *committed_rev,
 
168
                                 SVN_PROP_REVISION_AUTHOR, pool));
 
169
 
 
170
  *committed_date = committed_date_s ? committed_date_s->data : NULL;
 
171
  *last_author = last_author_s ? last_author_s->data : NULL;
 
172
  
 
173
  return SVN_NO_ERROR;
 
174
}
 
175
 
 
176
 
 
177
/* Deprecated. */
 
178
svn_error_t *
 
179
svn_repos_history (svn_fs_t *fs,
 
180
                   const char *path,
 
181
                   svn_repos_history_func_t history_func,
 
182
                   void *history_baton,
 
183
                   svn_revnum_t start,
 
184
                   svn_revnum_t end,
 
185
                   svn_boolean_t cross_copies,
 
186
                   apr_pool_t *pool)
 
187
{
 
188
  return svn_repos_history2 (fs, path, history_func, history_baton,
 
189
                             NULL, NULL,
 
190
                             start, end, cross_copies, pool);
 
191
}
 
192
 
 
193
 
 
194
 
 
195
svn_error_t *
 
196
svn_repos_history2 (svn_fs_t *fs,
 
197
                    const char *path,
 
198
                    svn_repos_history_func_t history_func,
 
199
                    void *history_baton,
 
200
                    svn_repos_authz_func_t authz_read_func,
 
201
                    void *authz_read_baton,
 
202
                    svn_revnum_t start,
 
203
                    svn_revnum_t end,
 
204
                    svn_boolean_t cross_copies,
 
205
                    apr_pool_t *pool)
 
206
{
 
207
  svn_fs_history_t *history;
 
208
  apr_pool_t *oldpool = svn_pool_create (pool);
 
209
  apr_pool_t *newpool = svn_pool_create (pool);
 
210
  const char *history_path;
 
211
  svn_revnum_t history_rev;
 
212
  svn_fs_root_t *root;
 
213
 
 
214
  /* Validate the revisions. */
 
215
  if (! SVN_IS_VALID_REVNUM (start))
 
216
    return svn_error_createf 
 
217
      (SVN_ERR_FS_NO_SUCH_REVISION, 0, 
 
218
       _("Invalid start revision %ld"), start);
 
219
  if (! SVN_IS_VALID_REVNUM (end))
 
220
    return svn_error_createf 
 
221
      (SVN_ERR_FS_NO_SUCH_REVISION, 0, 
 
222
       _("Invalid end revision %ld"), end);
 
223
 
 
224
  /* Ensure that the input is ordered. */
 
225
  if (start > end)
 
226
    {
 
227
      svn_revnum_t tmprev = start;
 
228
      start = end;
 
229
      end = tmprev;
 
230
    }
 
231
 
 
232
  /* Get a revision root for END, and an initial HISTORY baton.  */
 
233
  SVN_ERR (svn_fs_revision_root (&root, fs, end, pool));
 
234
 
 
235
  if (authz_read_func)
 
236
    {
 
237
      svn_boolean_t readable;
 
238
      SVN_ERR (authz_read_func (&readable, root, path,
 
239
                                authz_read_baton, pool));
 
240
      if (! readable)
 
241
        return svn_error_create (SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL);
 
242
    }
 
243
 
 
244
  SVN_ERR (svn_fs_node_history (&history, root, path, oldpool));
 
245
 
 
246
  /* Now, we loop over the history items, calling svn_fs_history_prev(). */
 
247
  do
 
248
    {
 
249
      /* Note that we have to do some crazy pool work here.  We can't
 
250
         get rid of the old history until we use it to get the new, so
 
251
         we alternate back and forth between our subpools.  */
 
252
      apr_pool_t *tmppool;
 
253
 
 
254
      SVN_ERR (svn_fs_history_prev (&history, history, cross_copies, newpool));
 
255
 
 
256
      /* Only continue if there is further history to deal with. */
 
257
      if (! history)
 
258
        break;
 
259
 
 
260
      /* Fetch the location information for this history step. */
 
261
      SVN_ERR (svn_fs_history_location (&history_path, &history_rev,
 
262
                                        history, newpool));
 
263
      
 
264
      /* If this history item predates our START revision, quit
 
265
         here. */
 
266
      if (history_rev < start)
 
267
        break;
 
268
 
 
269
      /* Is the history item readable?  If not, quit. */
 
270
      if (authz_read_func)
 
271
        {
 
272
          svn_boolean_t readable;
 
273
          svn_fs_root_t *history_root;
 
274
          SVN_ERR (svn_fs_revision_root (&history_root, fs,
 
275
                                         history_rev, newpool));
 
276
          SVN_ERR (authz_read_func (&readable, history_root, history_path,
 
277
                                    authz_read_baton, newpool));
 
278
          if (! readable)
 
279
            break;
 
280
        }
 
281
      
 
282
      /* Call the user-provided callback function. */
 
283
      SVN_ERR (history_func (history_baton, history_path, 
 
284
                             history_rev, newpool));
 
285
 
 
286
      /* We're done with the old history item, so we can clear its
 
287
         pool, and then toggle our notion of "the old pool". */
 
288
      svn_pool_clear (oldpool);
 
289
      tmppool = oldpool;
 
290
      oldpool = newpool;
 
291
      newpool = tmppool;
 
292
    }
 
293
  while (history); /* shouldn't hit this */
 
294
 
 
295
  svn_pool_destroy (oldpool);
 
296
  svn_pool_destroy (newpool);
 
297
  return SVN_NO_ERROR;
 
298
}
 
299
 
 
300
 
 
301
/* Helper func:  return SVN_ERR_AUTHZ_UNREADABLE if ROOT/PATH is
 
302
   unreadable. */
 
303
static svn_error_t *
 
304
check_readability (svn_fs_root_t *root,
 
305
                   const char *path,
 
306
                   svn_repos_authz_func_t authz_read_func,
 
307
                   void *authz_read_baton,                          
 
308
                   apr_pool_t *pool)
 
309
{
 
310
  svn_boolean_t readable;
 
311
  SVN_ERR (authz_read_func (&readable, root, path, authz_read_baton, pool));
 
312
  if (! readable)
 
313
    return svn_error_create (SVN_ERR_AUTHZ_UNREADABLE, NULL,
 
314
                             _("Unreadable path encountered; access denied"));
 
315
  return SVN_NO_ERROR;
 
316
}
 
317
 
 
318
 
 
319
/* The purpose of this function is to discover if fs_path@future_rev
 
320
 * is derived from fs_path@peg_rev.  The return is placed in *is_ancestor. */
 
321
 
 
322
static svn_error_t *
 
323
check_ancestry_of_peg_path (svn_boolean_t *is_ancestor,
 
324
                            svn_fs_t *fs,
 
325
                            const char *fs_path,
 
326
                            svn_revnum_t peg_revision,
 
327
                            svn_revnum_t future_revision,
 
328
                            apr_pool_t *pool)
 
329
{
 
330
  svn_fs_root_t *root;
 
331
  svn_fs_history_t *history;
 
332
  const char *path;
 
333
  svn_revnum_t revision;
 
334
  apr_pool_t *lastpool, *currpool;
 
335
 
 
336
  lastpool = svn_pool_create (pool);
 
337
  currpool = svn_pool_create (pool);
 
338
 
 
339
  SVN_ERR (svn_fs_revision_root (&root, fs, future_revision, pool));
 
340
 
 
341
  SVN_ERR (svn_fs_node_history (&history, root, fs_path, lastpool));
 
342
 
 
343
  /* Since paths that are different according to strcmp may still be
 
344
     equivalent (due to number of consecutive slashes and the fact that
 
345
     "" is the same as "/"), we get the "canonical" path in the first
 
346
     iteration below so that the comparison after the loop will work
 
347
     correctly. */
 
348
  fs_path = NULL;
 
349
 
 
350
  while (1)
 
351
    {
 
352
      apr_pool_t *tmppool;
 
353
 
 
354
      SVN_ERR (svn_fs_history_prev (&history, history, TRUE, currpool));
 
355
 
 
356
      if (!history)
 
357
        break;
 
358
 
 
359
      SVN_ERR (svn_fs_history_location (&path, &revision, history, currpool));
 
360
 
 
361
      if (!fs_path)
 
362
        fs_path = apr_pstrdup (pool, path);
 
363
 
 
364
      if (revision <= peg_revision)
 
365
        break;
 
366
 
 
367
      /* Clear old pool and flip. */
 
368
      svn_pool_clear (lastpool);
 
369
      tmppool = lastpool;
 
370
      lastpool = currpool;
 
371
      currpool = tmppool;
 
372
    }
 
373
 
 
374
  /* We must have had at least one iteration above where we
 
375
     reassigned fs_path. Else, the path wouldn't have existed at
 
376
     future_revision and svn_fs_history would have thrown. */
 
377
  assert (fs_path != NULL);
 
378
     
 
379
  *is_ancestor = (history && strcmp (path, fs_path) == 0);
 
380
 
 
381
  return SVN_NO_ERROR;
 
382
}
 
383
 
 
384
 
 
385
svn_error_t *
 
386
svn_repos_trace_node_locations (svn_fs_t *fs,
 
387
                                apr_hash_t **locations,
 
388
                                const char *fs_path,
 
389
                                svn_revnum_t peg_revision,
 
390
                                apr_array_header_t *location_revisions_orig,
 
391
                                svn_repos_authz_func_t authz_read_func,
 
392
                                void *authz_read_baton,
 
393
                                apr_pool_t *pool)
 
394
{
 
395
  apr_array_header_t *location_revisions;
 
396
  svn_revnum_t *revision_ptr, *revision_ptr_end;
 
397
  svn_fs_root_t *root;
 
398
  svn_fs_history_t *history;
 
399
  const char *path;
 
400
  svn_revnum_t revision;
 
401
  svn_boolean_t is_ancestor;
 
402
  apr_pool_t *lastpool, *currpool;
 
403
 
 
404
  /* Sanity check. */
 
405
  assert (location_revisions_orig->elt_size == sizeof(svn_revnum_t));
 
406
 
 
407
  /* Another sanity check. */
 
408
  if (authz_read_func)
 
409
    {
 
410
      svn_fs_root_t *peg_root;
 
411
      SVN_ERR (svn_fs_revision_root (&peg_root, fs, peg_revision, pool));
 
412
      SVN_ERR (check_readability (peg_root, fs_path,
 
413
                                  authz_read_func, authz_read_baton, pool));
 
414
    }
 
415
 
 
416
  *locations = apr_hash_make (pool);
 
417
 
 
418
  /* We flip between two pools in the second loop below. */
 
419
  lastpool = svn_pool_create (pool);
 
420
  currpool = svn_pool_create (pool);
 
421
 
 
422
  /* First - let's sort the array of the revisions from the greatest revision
 
423
   * downward, so it will be easier to search on. */
 
424
  location_revisions = apr_array_copy (pool, location_revisions_orig);
 
425
  qsort (location_revisions->elts, location_revisions->nelts,
 
426
         sizeof (*revision_ptr), svn_sort_compare_revisions);
 
427
 
 
428
  revision_ptr = (svn_revnum_t *)location_revisions->elts;
 
429
  revision_ptr_end = revision_ptr + location_revisions->nelts;
 
430
 
 
431
  /* Ignore revisions R that are younger than the peg_revisions where
 
432
     path@peg_revision is not an ancestor of path@R. */
 
433
  is_ancestor = FALSE;
 
434
  while (revision_ptr < revision_ptr_end && *revision_ptr > peg_revision)
 
435
    {
 
436
      svn_pool_clear (currpool);
 
437
      SVN_ERR (check_ancestry_of_peg_path (&is_ancestor, fs, fs_path,
 
438
                                           peg_revision, *revision_ptr,
 
439
                                           currpool));
 
440
      if (is_ancestor)
 
441
        break;
 
442
      ++revision_ptr;
 
443
    }
 
444
 
 
445
  SVN_ERR (svn_fs_revision_root (&root, fs,
 
446
                                 (is_ancestor ?
 
447
                                  (*revision_ptr) :
 
448
                                  peg_revision), pool));
 
449
  if (authz_read_func)
 
450
    SVN_ERR (check_readability (root, fs_path, authz_read_func,
 
451
                                authz_read_baton, pool));
 
452
 
 
453
  SVN_ERR (svn_fs_node_history (&history, root, fs_path, lastpool));
 
454
 
 
455
  while (revision_ptr < revision_ptr_end)
 
456
    {
 
457
      apr_pool_t *tmppool;
 
458
 
 
459
      SVN_ERR (svn_fs_history_prev (&history, history, TRUE, currpool));
 
460
      if (!history)
 
461
        break;
 
462
 
 
463
      SVN_ERR (svn_fs_history_location (&path, &revision, history, currpool));
 
464
 
 
465
      if (authz_read_func)
 
466
        {
 
467
          svn_boolean_t readable;
 
468
          svn_fs_root_t *tmp_root;
 
469
 
 
470
          SVN_ERR (svn_fs_revision_root (&tmp_root, fs, revision, currpool));
 
471
          SVN_ERR (authz_read_func (&readable, tmp_root, path,
 
472
                                    authz_read_baton, currpool));
 
473
          if (! readable)
 
474
            {
 
475
              return SVN_NO_ERROR;
 
476
            }
 
477
        }
 
478
 
 
479
      /* Assign the current path to all younger revisions until we reach
 
480
         the current one. */
 
481
      while ((revision_ptr < revision_ptr_end) && (*revision_ptr >= revision))
 
482
        {
 
483
          /* *revision_ptr is allocated out of pool, so we can point
 
484
             to in the hash table. */
 
485
          apr_hash_set (*locations, revision_ptr, sizeof (*revision_ptr),
 
486
                        apr_pstrdup (pool, path));
 
487
          revision_ptr++;
 
488
        }
 
489
 
 
490
      /* Clear last pool and switch. */
 
491
      svn_pool_clear (lastpool);
 
492
      tmppool = lastpool;
 
493
      lastpool = currpool;
 
494
      currpool = tmppool;
 
495
    }
 
496
 
 
497
  svn_pool_destroy (lastpool);
 
498
  svn_pool_destroy (currpool);
 
499
 
 
500
  return SVN_NO_ERROR;
 
501
}
 
502
 
 
503
svn_error_t *
 
504
svn_repos_get_file_revs (svn_repos_t *repos,
 
505
                         const char *path,
 
506
                         svn_revnum_t start,
 
507
                         svn_revnum_t end,
 
508
                         svn_repos_authz_func_t authz_read_func,
 
509
                         void *authz_read_baton,
 
510
                         svn_repos_file_rev_handler_t handler,
 
511
                         void *handler_baton,
 
512
                         apr_pool_t *pool)
 
513
{
 
514
  apr_pool_t *iter_pool, *last_pool;
 
515
  svn_fs_history_t *history;
 
516
  apr_array_header_t *revnums = apr_array_make (pool, 0,
 
517
                                                sizeof (svn_revnum_t));
 
518
  apr_array_header_t *paths = apr_array_make (pool, 0, sizeof (char *));
 
519
  apr_hash_t *last_props;
 
520
  svn_fs_root_t *root, *last_root;
 
521
  const char *last_path;
 
522
  int i;
 
523
  svn_node_kind_t kind;
 
524
 
 
525
  /* We switch betwwen two pools while looping, since we need information from
 
526
     the last iteration to be available. */
 
527
  iter_pool = svn_pool_create (pool);
 
528
  last_pool = svn_pool_create (pool);
 
529
 
 
530
  /* Open revision root for path@end. */
 
531
  /* ### Can we use last_pool for this? How long does the history
 
532
     object need the root? */
 
533
  SVN_ERR (svn_fs_revision_root (&root, repos->fs, end, pool));
 
534
 
 
535
  /* The path had better be a file in this revision. This avoids calling
 
536
     the callback before reporting an uglier error below. */
 
537
  SVN_ERR (svn_fs_check_path (&kind, root, path, pool));
 
538
  if (kind != svn_node_file)
 
539
    return svn_error_createf
 
540
      (SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path);
 
541
 
 
542
  /* Open a history object. */
 
543
  SVN_ERR (svn_fs_node_history (&history, root, path, last_pool));
 
544
  
 
545
  /* Get the revisions we are interested in. */
 
546
  while (1)
 
547
    {
 
548
      const char* rev_path;
 
549
      svn_revnum_t rev;
 
550
      apr_pool_t *tmp_pool;
 
551
 
 
552
      svn_pool_clear (iter_pool);
 
553
 
 
554
      SVN_ERR (svn_fs_history_prev (&history, history, TRUE, iter_pool));
 
555
      if (!history)
 
556
        break;
 
557
      SVN_ERR (svn_fs_history_location (&rev_path, &rev, history, iter_pool));
 
558
      if (authz_read_func)
 
559
        {
 
560
          svn_boolean_t readable;
 
561
          svn_fs_root_t *tmp_root;
 
562
 
 
563
          SVN_ERR (svn_fs_revision_root (&tmp_root, repos->fs, rev, iter_pool));
 
564
          SVN_ERR (authz_read_func (&readable, tmp_root, rev_path,
 
565
                                    authz_read_baton, iter_pool));
 
566
          if (! readable)
 
567
            {
 
568
              break;
 
569
            }
 
570
        }
 
571
      *(svn_revnum_t*) apr_array_push (revnums) = rev;
 
572
      *(char **) apr_array_push (paths) = apr_pstrdup (pool, rev_path);
 
573
      if (rev <= start)
 
574
        break;
 
575
 
 
576
      /* Swap pools. */
 
577
      tmp_pool = iter_pool;
 
578
      iter_pool = last_pool;
 
579
      last_pool = tmp_pool;
 
580
    }
 
581
 
 
582
  /* We must have at least one revision to get. */
 
583
  assert (revnums->nelts > 0);
 
584
 
 
585
  /* We want the first txdelta to be against the empty file. */
 
586
  last_root = NULL;
 
587
  last_path = NULL;
 
588
 
 
589
  /* Create an empty hash table for the first property diff. */
 
590
  last_props = apr_hash_make (last_pool);
 
591
 
 
592
  /* Walk through the revisions in chronological order. */
 
593
  for (i = revnums->nelts; i > 0; --i)
 
594
    {
 
595
      svn_revnum_t rev = APR_ARRAY_IDX (revnums, i - 1, svn_revnum_t);
 
596
      const char *rev_path = APR_ARRAY_IDX (paths, i - 1, const char *);
 
597
      apr_hash_t *rev_props;
 
598
      apr_hash_t *props;
 
599
      apr_array_header_t *prop_diffs;
 
600
      svn_txdelta_stream_t *delta_stream;
 
601
      svn_txdelta_window_handler_t delta_handler = NULL;
 
602
      void *delta_baton = NULL;
 
603
      apr_pool_t *tmp_pool;  /* For swapping */
 
604
      svn_boolean_t contents_changed;
 
605
 
 
606
      svn_pool_clear (iter_pool);
 
607
 
 
608
      /* Get the revision properties. */
 
609
      SVN_ERR (svn_fs_revision_proplist (&rev_props, repos->fs,
 
610
                                         rev, iter_pool));
 
611
 
 
612
      /* Open the revision root. */
 
613
      SVN_ERR (svn_fs_revision_root (&root, repos->fs, rev, iter_pool));
 
614
 
 
615
      /* Get the file's properties for this revision and compute the diffs. */
 
616
      SVN_ERR (svn_fs_node_proplist (&props, root, rev_path, iter_pool));
 
617
      SVN_ERR (svn_prop_diffs (&prop_diffs, props, last_props, pool));
 
618
 
 
619
      /* Check if the contents changed. */
 
620
      /* Special case: In the first revision, we always provide a delta. */
 
621
      if (last_root)
 
622
        SVN_ERR (svn_fs_contents_changed (&contents_changed,
 
623
                                          last_root, last_path,
 
624
                                          root, rev_path, iter_pool));
 
625
      else
 
626
        contents_changed = TRUE;
 
627
 
 
628
      /* We have all we need, give to the handler. */
 
629
      SVN_ERR (handler (handler_baton, rev_path, rev, rev_props,
 
630
                        contents_changed ? &delta_handler : NULL,
 
631
                        contents_changed ? &delta_baton : NULL,
 
632
                        prop_diffs, iter_pool));
 
633
 
 
634
      /* Compute and send delta if client asked for it.
 
635
         Note that this was initialized to NULL, so if !contents_changed,
 
636
         no deltas will be computed. */
 
637
      if (delta_handler)
 
638
        {
 
639
          /* Get the content delta. */
 
640
          SVN_ERR (svn_fs_get_file_delta_stream (&delta_stream,
 
641
                                                 last_root, last_path,
 
642
                                                 root, rev_path,
 
643
                                                 iter_pool));
 
644
          /* And send. */
 
645
          SVN_ERR (svn_txdelta_send_txstream (delta_stream,
 
646
                                              delta_handler, delta_baton,
 
647
                                              iter_pool));
 
648
        }
 
649
 
 
650
      /* Remember root, path and props for next iteration. */
 
651
      last_root = root;
 
652
      last_path = rev_path;
 
653
      last_props = props;
 
654
 
 
655
      /* Swap the pools. */
 
656
      tmp_pool = iter_pool;
 
657
      iter_pool = last_pool;
 
658
      last_pool = tmp_pool;
 
659
    }
 
660
 
 
661
  svn_pool_destroy (last_pool);
 
662
  svn_pool_destroy (iter_pool);
 
663
 
 
664
  return SVN_NO_ERROR;
 
665
}