80
82
subpool = svn_pool_create(pool);
81
83
for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
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;
88
89
svn_pool_clear(subpool);
89
apr_hash_this(hi, &key, NULL, &val);
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
* - 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.
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.
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.)
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,
186
190
apr_hash_t *prefetched_changes,
191
195
apr_hash_t *changes = prefetched_changes;
192
196
apr_hash_index_t *hi;
197
apr_pool_t *iterpool;
194
198
svn_boolean_t found_readable = FALSE;
195
199
svn_boolean_t found_unreadable = FALSE;
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));
206
SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
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. */
211
*changed = svn_hash__make(pool);
217
*changed = svn_hash__make(pool);
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. */
206
subpool = svn_pool_create(pool);
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;
228
iterpool = svn_pool_create(pool);
208
229
for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
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. */
215
svn_fs_path_change2_t *change;
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);
218
238
svn_log_changed_path2_t *item;
220
svn_pool_clear(subpool);
222
/* KEY will be the path, VAL the change. */
223
apr_hash_this(hi, &key, NULL, &val);
224
path = (const char *) key;
240
svn_pool_clear(iterpool);
227
242
/* Skip path if unreadable. */
228
243
if (authz_read_func)
288
303
svn_revnum_t prev_rev;
289
304
const char *parent_path, *name;
291
svn_fspath__split(&parent_path, &name, path, subpool);
306
svn_fspath__split(&parent_path, &name, path, iterpool);
293
SVN_ERR(svn_fs_node_history(&history, root, parent_path,
308
SVN_ERR(svn_fs_node_history2(&history, root, parent_path,
309
iterpool, iterpool));
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,
315
SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool,
301
318
SVN_ERR(svn_fs_history_location(&parent_path, &prev_rev, history,
303
SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, subpool));
304
check_path = svn_fspath__join(parent_path, name, subpool);
320
SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, iterpool));
321
check_path = svn_fspath__join(parent_path, name, iterpool);
307
324
SVN_ERR(svn_fs_check_path(&item->node_kind, check_root, check_path,
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(©from_rev, ©from_path,
322
root, path, subpool));
339
SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path,
340
root, path, iterpool));
341
copyfrom_path = apr_pstrdup(pool, copyfrom_path);
324
344
if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
330
350
svn_fs_root_t *copyfrom_root;
332
352
SVN_ERR(svn_fs_revision_root(©from_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));
338
358
found_unreadable = TRUE;
343
item->copyfrom_path = apr_pstrdup(pool, copyfrom_path);
363
item->copyfrom_path = copyfrom_path;
344
364
item->copyfrom_rev = copyfrom_rev;
348
svn_hash_sets(*changed, apr_pstrdup(pool, path), item);
369
apr_hash_set(*changed, path, path_len, item);
351
svn_pool_destroy(subpool);
372
svn_pool_destroy(iterpool);
353
374
if (! found_readable)
354
/* Every changed-path was unreadable. */
355
return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE,
358
if (found_unreadable)
359
/* At least one changed-path was unreadable. */
360
return svn_error_create(SVN_ERR_AUTHZ_PARTIALLY_READABLE,
363
/* Every changed-path was readable. */
376
/* Every changed-path was unreadable. */
377
*access_level = svn_repos_revision_access_none;
379
else if (found_unreadable)
381
/* At least one changed-path was unreadable. */
382
*access_level = svn_repos_revision_access_partial;
386
/* Every changed-path was readable. */
387
*access_level = svn_repos_revision_access_full;
364
390
return SVN_NO_ERROR;
422
449
subpool = info->newpool;
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));
426
454
hist = info->hist;
430
subpool = svn_pool_create(pool);
458
subpool = svn_pool_create(result_pool);
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,
436
SVN_ERR(svn_fs_node_history(&hist, history_root, info->path->data,
464
SVN_ERR(svn_fs_node_history2(&hist, history_root, info->path->data,
465
subpool, scratch_pool));
439
SVN_ERR(svn_fs_history_prev(&hist, hist, ! strict, subpool));
467
SVN_ERR(svn_fs_history_prev2(&hist, hist, ! strict, subpool,
441
470
if (info->first_time)
442
471
info->first_time = FALSE;
444
SVN_ERR(svn_fs_history_prev(&hist, hist, ! strict, subpool));
473
SVN_ERR(svn_fs_history_prev2(&hist, hist, ! strict, subpool,
537
568
rev where this path was changed. */
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);
543
574
/* Return the next interesting revision in our list of HISTORIES. */
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,
595
627
if (*prefetched_changes == NULL)
596
628
SVN_ERR(svn_fs_paths_changed2(prefetched_changes, root, scratch_pool));
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);
634
hi = apr_hash_next(hi))
636
svn_fs_path_change2_t *change = apr_hash_this_val(hi);
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;
643
switch (change->change_kind)
645
case svn_fs_path_change_add:
646
case svn_fs_path_change_replace:
655
/* No potential mergeinfo changes? We're done. */
600
657
return SVN_NO_ERROR;
602
659
/* Loop over changes, looking for anything that might carry an
608
665
hi = apr_hash_next(hi))
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;
618
svn_pool_clear(iterpool);
674
/* Cheap pre-checks that don't require memory allocation etc. */
620
/* KEY will be the path, VAL the change. */
621
apr_hash_this(hi, &key, NULL, &val);
676
/* No mergeinfo change? -> nothing to do here. */
677
if (change->mergeinfo_mod == svn_tristate_false)
625
680
/* If there was no property change on this item, ignore it. */
626
681
if (! change->prop_mod)
684
/* Begin actual processing */
685
changed_path = apr_hash_this_key(hi);
686
svn_pool_clear(iterpool);
629
688
switch (change->change_kind)
634
693
### difference would be the fallback case (path/rev-1 for
635
694
### modifies, NULL otherwise). -- cmpilato */
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:
643
const char *copyfrom_path;
644
svn_revnum_t copyfrom_rev;
646
SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path,
647
root, changed_path, iterpool));
648
if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
650
base_path = apr_pstrdup(scratch_pool, copyfrom_path);
651
base_rev = copyfrom_rev;
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
662
702
svn_revnum_t appeared_rev;
664
SVN_ERR(svn_repos__prev_location(&appeared_rev, &base_path,
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. */
709
SVN_ERR(svn_repos__prev_location(&appeared_rev, &base_path,
711
changed_path, iterpool));
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)))
719
base_path = changed_path;
674
725
base_path = changed_path;
675
726
base_rev = rev - 1;
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:
738
if (change->copyfrom_known)
740
base_rev = change->copyfrom_rev;
741
base_path = change->copyfrom_path;
745
SVN_ERR(svn_fs_copied_from(&base_rev, &base_path,
746
root, changed_path, iterpool));
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))
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))
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))
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;
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,
793
svn_mergeinfo_inherited, TRUE,
794
iterpool, iterpool));
723
795
if (tmp_mergeinfo)
724
796
SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_value,
728
800
else if (mergeinfo_value && (! prev_mergeinfo_value)
729
801
&& base_path && SVN_IS_VALID_REVNUM(base_rev))
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;
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,
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. */
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
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;
817
/* Create a work subpool and get a root for REV. */
818
SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
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);
887
scratch_pool, scratch_pool);
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;
909
/* Create a work subpool and get a root for REV. */
910
SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
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.) */
852
918
const char *path = APR_ARRAY_IDX(paths, i, const char *);
853
919
const char *prev_path;
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;
862
925
svn_pool_clear(iterpool);
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,
959
err = svn_fs__get_mergeinfo_for_path(&prev_mergeinfo,
960
prev_root, prev_path,
961
svn_mergeinfo_inherited, TRUE,
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))
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));
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));
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,
992
svn_mergeinfo_inherited, TRUE,
993
iterpool, iterpool));
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));
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,
998
svn_mergeinfo_nearest_ancestor,
999
FALSE, /* adjust_inherited_mergeinfo */
1000
iterpool, iterpool));
946
1002
if (!prev_mergeinfo && !mergeinfo)
985
1041
for (hi = apr_hash_first(scratch_pool, added_mergeinfo_catalog);
986
1042
hi; hi = apr_hash_next(hi))
991
const char *changed_path;
992
svn_mergeinfo_t added, deleted;
994
/* The path is the key, the mergeinfo delta is the value. */
995
apr_hash_this(hi, &key, &klen, &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;
999
1049
for (i = 0; i < paths->nelts; i++)
1002
1052
if (! svn_fspath__skip_ancestor(path, changed_path))
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));
1040
1091
&& (authz_read_func || discover_changed_paths))
1042
1093
svn_fs_root_t *newroot;
1043
svn_error_t *patherr;
1094
svn_repos_revision_access_level_t access_level;
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,
1097
SVN_ERR(detect_changed(&access_level, &changed_paths,
1098
newroot, fs, prefetched_changes,
1099
authz_read_func, authz_read_baton,
1052
&& patherr->apr_err == SVN_ERR_AUTHZ_UNREADABLE)
1102
if (access_level == svn_repos_revision_access_none)
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;
1060
&& patherr->apr_err == SVN_ERR_AUTHZ_PARTIALLY_READABLE)
1108
else if (access_level == svn_repos_revision_access_partial)
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;
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. */
1099
1146
/* Requested only some revprops... */
1101
for (i = 0; i < revprops->nelts; i++)
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};
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)
1103
char *name = APR_ARRAY_IDX(revprops, i, char *);
1104
svn_string_t *value = svn_hash_gets(r_props, name);
1106
&& !(strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0
1107
|| strcmp(name, SVN_PROP_REVISION_DATE) == 0))
1108
/* ... but we can only return author/date. */
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++)
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))
1168
/* hash does not match list of revprops we want */
1169
log_entry->revprops = NULL;
1175
/* slow, revprop-by-revprop filtering */
1176
if (log_entry->revprops == NULL)
1177
for (i = 0; i < revprops->nelts; i++)
1179
const svn_string_t *name
1180
= APR_ARRAY_IDX(revprops, i, const svn_string_t *);
1182
= apr_hash_get(r_props, name->data, name->len);
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. */
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);
1136
1214
(i.e. retrieve none if the array is empty).
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.
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,
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;
1184
1261
&& apr_hash_count(log_target_history_as_mergeinfo))
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);
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;
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);
1196
1273
hi = apr_hash_next(hi))
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);
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);
1207
1283
hi2 = apr_hash_next(hi2))
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);
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. */
1256
1330
if (found_rev_of_interest)
1332
apr_pool_t *scratch_pool;
1258
1334
/* Is REV a merged revision we've already sent? */
1259
1335
if (nested_merges && handling_merged_revision)
1261
svn_revnum_t *merged_rev = apr_hash_get(nested_merges, &rev,
1262
sizeof(svn_revnum_t *));
1337
if (svn_bit_array__get(nested_merges, rev))
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);
1283
return (*receiver)(receiver_baton, log_entry, pool);
1287
return SVN_NO_ERROR;
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);
1358
return SVN_NO_ERROR;
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);
1338
1409
if (authz_read_func)
1340
1411
svn_boolean_t readable;
1342
svn_pool_clear(iterpool);
1344
1412
SVN_ERR(authz_read_func(&readable, root, this_path,
1345
1413
authz_read_baton, iterpool));
1346
1414
if (! readable)
1355
1423
if (i < MAX_OPEN_HISTORIES)
1357
err = svn_fs_node_history(&info->hist, root, this_path, pool);
1425
err = svn_fs_node_history2(&info->hist, root, this_path, pool,
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,
1450
hist_start, pool, iterpool);
1383
1452
&& ignore_missing_locations
1384
1453
&& (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
1488
1557
struct rangelist_path *rp = apr_palloc(subpool, sizeof(*rp));
1489
apr_hash_this(hi, (void *) &rp->path, NULL,
1490
(void *) &rp->rangelist);
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;
1493
1563
/* We need to make local copies of the rangelist, since we will be
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);
1521
1590
/* Next, find the number of revision ranges which start with the same
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,
1680
1749
handle_merged_revisions(svn_revnum_t rev,
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,
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);
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,
1956
2025
struct path_info *info = APR_ARRAY_IDX(histories, i,
1957
2026
struct path_info *);
2028
svn_pool_clear(iterpool2);
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,
1963
2035
if (! info->done)
1964
2036
any_histories_left = TRUE;
2039
svn_pool_clear(iterpool2);
1967
2041
/* If any of the paths changed in this rev then add or send it. */
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);
2053
2127
if (added_mergeinfo || deleted_mergeinfo)
2055
svn_revnum_t *cur_rev = apr_pcalloc(pool, sizeof(*cur_rev));
2129
svn_revnum_t *cur_rev =
2130
apr_pmemdup(pool, ¤t, sizeof(cur_rev));
2056
2131
struct added_deleted_mergeinfo *add_and_del_mergeinfo =
2057
2132
apr_palloc(pool, sizeof(*add_and_del_mergeinfo));
2059
if (added_mergeinfo)
2060
add_and_del_mergeinfo->added_mergeinfo =
2061
svn_mergeinfo_dup(added_mergeinfo, pool);
2063
if (deleted_mergeinfo)
2064
add_and_del_mergeinfo->deleted_mergeinfo =
2065
svn_mergeinfo_dup(deleted_mergeinfo, pool);
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);
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),
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)
2252
2328
svn_boolean_t descending_order;
2253
2329
svn_mergeinfo_t paths_history_mergeinfo = NULL;
2334
apr_array_header_t *new_revprops
2335
= apr_array_make(pool, revprops->nelts, sizeof(svn_string_t *));
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);
2341
revprops = new_revprops;
2255
2344
/* Setup log range. */
2256
2345
SVN_ERR(svn_fs_youngest_rev(&head, fs, pool));
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));
2342
2430
svn_pool_destroy(iterpool);
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);