31
31
#include "svn_string.h"
32
32
#include "svn_props.h"
34
#include "svn_private_config.h"
35
34
#include "svn_mergeinfo.h"
36
35
#include "svn_checksum.h"
37
36
#include "svn_subst.h"
38
#include "svn_ctype.h"
39
37
#include "svn_dirent_uri.h"
41
39
#include <apr_lib.h>
43
#include "private/svn_repos_private.h"
44
41
#include "private/svn_fspath.h"
45
42
#include "private/svn_dep_compat.h"
46
43
#include "private/svn_mergeinfo_private.h"
44
#include "private/svn_repos_private.h"
48
46
/*----------------------------------------------------------------------*/
84
83
SVN_INVALID_REVNUM. */
85
84
svn_revnum_t last_rev_mapped;
87
/* The oldest old revision loaded from the dump stream. If no revisions
86
/* The oldest revision loaded from the dump stream. If no revisions
88
87
have been loaded yet, this is set to SVN_INVALID_REVNUM. */
89
svn_revnum_t oldest_old_rev;
88
svn_revnum_t oldest_dumpstream_rev;
92
91
struct revision_baton
93
/* rev num from dump file */
96
96
svn_fs_root_t *txn_root;
98
98
const svn_string_t *datestamp;
100
/* (rev num from dump file) minus (rev num to be committed) */
100
101
apr_int32_t rev_offset;
101
102
svn_boolean_t skipped;
104
/* Array of svn_prop_t with revision properties. */
105
apr_array_header_t *revprops;
103
107
struct parse_baton *pb;
104
108
apr_pool_t *pool;
200
202
apr_hash_t *prefixed_mergeinfo, *mergeinfo;
201
203
apr_hash_index_t *hi;
204
205
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool));
205
206
prefixed_mergeinfo = apr_hash_make(pool);
206
207
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
209
const char *path, *merge_source;
209
const char *merge_source = apr_hash_this_key(hi);
210
svn_rangelist_t *rangelist = apr_hash_this_val(hi);
211
apr_hash_this(hi, &key, NULL, &rangelist);
212
merge_source = svn_relpath_canonicalize(key, pool);
213
merge_source = svn_relpath_canonicalize(merge_source, pool);
214
215
/* The svn:mergeinfo property syntax demands a repos abspath */
215
216
path = svn_fspath__canonicalize(svn_relpath_join(parent_dir,
224
225
/* Examine the mergeinfo in INITIAL_VAL, renumber revisions in rangelists
225
226
as appropriate, and return the (possibly new) mergeinfo in *FINAL_VAL
226
(allocated from POOL). */
227
/* ### FIXME: Consider somehow sharing code with
228
### svnrdump/load_editor.c:renumber_mergeinfo_revs() */
227
(allocated from POOL).
229
Adjust any mergeinfo revisions not older than OLDEST_DUMPSTREAM_REV by
230
using REV_MAP which maps (svn_revnum_t) old rev to (svn_revnum_t) new rev.
232
Adjust any mergeinfo revisions older than OLDEST_DUMPSTREAM_REV by
233
(-OLDER_REVS_OFFSET), dropping any that become <= 0.
229
235
static svn_error_t *
230
236
renumber_mergeinfo_revs(svn_string_t **final_val,
231
237
const svn_string_t *initial_val,
232
struct revision_baton *rb,
239
svn_revnum_t oldest_dumpstream_rev,
240
apr_int32_t older_revs_offset,
233
241
apr_pool_t *pool)
235
243
apr_pool_t *subpool = svn_pool_create(pool);
244
252
Remove mergeinfo older than the oldest revision in the dump stream
245
253
and adjust its revisions by the difference between the head rev of
246
254
the target repository and the current dump stream rev. */
247
if (rb->pb->oldest_old_rev > 1)
255
if (oldest_dumpstream_rev > 1)
257
/* predates_stream_mergeinfo := mergeinfo that refers to revs before
258
oldest_dumpstream_rev */
249
259
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
250
260
&predates_stream_mergeinfo, mergeinfo,
251
rb->pb->oldest_old_rev - 1, 0,
261
oldest_dumpstream_rev - 1, 0,
252
262
TRUE, subpool, subpool));
263
/* mergeinfo := mergeinfo that refers to revs >= oldest_dumpstream_rev */
253
264
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
254
265
&mergeinfo, mergeinfo,
255
rb->pb->oldest_old_rev - 1, 0,
266
oldest_dumpstream_rev - 1, 0,
256
267
FALSE, subpool, subpool));
257
268
SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists(
258
269
&predates_stream_mergeinfo, predates_stream_mergeinfo,
259
-rb->rev_offset, subpool, subpool));
270
-older_revs_offset, subpool, subpool));
266
277
for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi))
268
const char *merge_source;
269
svn_rangelist_t *rangelist;
270
struct parse_baton *pb = rb->pb;
279
const char *merge_source = apr_hash_this_key(hi);
280
svn_rangelist_t *rangelist = apr_hash_this_val(hi);
275
apr_hash_this(hi, &key, NULL, &val);
279
283
/* Possibly renumber revisions in merge source's rangelist. */
280
284
for (i = 0; i < rangelist->nelts; i++)
282
286
svn_revnum_t rev_from_map;
283
287
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
284
288
svn_merge_range_t *);
285
rev_from_map = get_revision_mapping(pb->rev_map, range->start);
289
rev_from_map = get_revision_mapping(rev_map, range->start);
286
290
if (SVN_IS_VALID_REVNUM(rev_from_map))
288
292
range->start = rev_from_map;
290
else if (range->start == pb->oldest_old_rev - 1)
294
else if (range->start == oldest_dumpstream_rev - 1)
292
296
/* Since the start revision of svn_merge_range_t are not
293
297
inclusive there is one possible valid start revision that
294
won't be found in the PB->REV_MAP mapping of load stream
298
won't be found in the REV_MAP mapping of load stream
295
299
revsions to loaded revisions: The revision immediately
296
preceeding the oldest revision from the load stream.
300
preceding the oldest revision from the load stream.
297
301
This is a valid revision for mergeinfo, but not a valid
298
copy from revision (which PB->REV_MAP also maps for) so it
302
copy from revision (which REV_MAP also maps for) so it
299
303
will never be in the mapping.
301
305
If that is what we have here, then find the mapping for the
302
306
oldest rev from the load stream and subtract 1 to get the
303
307
renumbered, non-inclusive, start revision. */
304
rev_from_map = get_revision_mapping(pb->rev_map,
308
rev_from_map = get_revision_mapping(rev_map,
309
oldest_dumpstream_rev);
306
310
if (SVN_IS_VALID_REVNUM(rev_from_map))
307
311
range->start = rev_from_map - 1;
489
506
if ((rb->rev > 0) && (! rb->skipped))
491
508
/* Create a new fs txn. */
492
SVN_ERR(svn_fs_begin_txn2(&(rb->txn), pb->fs, head_rev, 0, pool));
509
SVN_ERR(svn_fs_begin_txn2(&(rb->txn), pb->fs, head_rev,
510
SVN_FS_TXN_CLIENT_DATE, pool));
493
511
SVN_ERR(svn_fs_txn_root(&(rb->txn_root), rb->txn, pool));
495
513
if (pb->notify_func)
702
724
const svn_string_t *value)
704
726
struct revision_baton *rb = baton;
727
struct parse_baton *pb = rb->pb;
728
svn_boolean_t is_date = strcmp(name, SVN_PROP_REVISION_DATE) == 0;
706
731
/* If we're skipping this revision, we're done here. */
708
733
return SVN_NO_ERROR;
712
if (rb->pb->validate_props)
713
SVN_ERR(svn_repos_fs_change_txn_prop(rb->txn, name, value, rb->pool));
715
SVN_ERR(svn_fs_change_txn_prop(rb->txn, name, value, rb->pool));
717
/* Remember any datestamp that passes through! (See comment in
718
close_revision() below.) */
719
if (! strcmp(name, SVN_PROP_REVISION_DATE))
720
rb->datestamp = svn_string_dup(value, rb->pool);
722
else if (rb->rev == 0)
724
/* Special case: set revision 0 properties when loading into an
725
'empty' filesystem. */
726
struct parse_baton *pb = rb->pb;
727
svn_revnum_t youngest_rev;
729
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool));
731
if (youngest_rev == 0)
732
SVN_ERR(change_rev_prop(pb->repos, 0, name, value,
733
pb->validate_props, rb->pool));
735
/* If we're ignoring dates, and this is one, we're done here. */
736
if (is_date && pb->ignore_dates)
739
/* Collect property changes to apply them in one FS call in
741
prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t);
742
prop->name = apr_pstrdup(rb->pool, name);
743
prop->value = svn_string_dup(value, rb->pool);
745
/* Remember any datestamp that passes through! (See comment in
746
close_revision() below.) */
748
rb->datestamp = svn_string_dup(value, rb->pool);
736
750
return SVN_NO_ERROR;
741
* - normalize line endings (if all CRLF, change to LF; but error if mixed);
742
* - adjust revision numbers (see renumber_mergeinfo_revs());
743
* - adjust paths (see prefix_mergeinfo_paths()).
746
adjust_mergeinfo_property(struct revision_baton *rb,
747
svn_string_t **new_value_p,
748
const svn_string_t *old_value,
749
apr_pool_t *result_pool)
755
svn_repos__adjust_mergeinfo_property(svn_string_t **new_value_p,
756
const svn_string_t *old_value,
757
const char *parent_dir,
759
svn_revnum_t oldest_dumpstream_rev,
760
apr_int32_t older_revs_offset,
761
svn_repos_notify_func_t notify_func,
763
apr_pool_t *result_pool,
764
apr_pool_t *scratch_pool)
751
struct parse_baton *pb = rb->pb;
752
766
svn_string_t prop_val = *old_value;
754
768
/* Tolerate mergeinfo with "\r\n" line endings because some
755
769
dumpstream sources might contain as much. If so normalize
756
the line endings to '\n' and make a notification to
757
PARSE_BATON->FEEDBACK_STREAM that we have made this
770
the line endings to '\n' and notify that we have made this
759
772
if (strstr(prop_val.data, "\r"))
770
783
prop_val.data = prop_eol_normalized;
771
784
prop_val.len = strlen(prop_eol_normalized);
775
/* ### TODO: Use proper scratch pool instead of pb->notify_pool */
776
788
svn_repos_notify_t *notify
777
789
= svn_repos_notify_create(
778
790
svn_repos_notify_load_normalized_mergeinfo,
781
pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
782
svn_pool_clear(pb->notify_pool);
793
notify_func(notify_baton, notify, scratch_pool);
786
797
/* Renumber mergeinfo as appropriate. */
787
SVN_ERR(renumber_mergeinfo_revs(new_value_p, &prop_val, rb,
798
SVN_ERR(renumber_mergeinfo_revs(new_value_p, &prop_val,
799
rev_map, oldest_dumpstream_rev,
791
/* Prefix the merge source paths with PB->parent_dir. */
805
/* Prefix the merge source paths with PARENT_DIR. */
792
806
/* ASSUMPTION: All source paths are included in the dump stream. */
793
807
SVN_ERR(prefix_mergeinfo_paths(new_value_p, *new_value_p,
794
pb->parent_dir, result_pool));
808
parent_dir, result_pool));
797
811
return SVN_NO_ERROR;
821
835
svn_string_t *new_value;
822
836
svn_error_t *err;
824
err = adjust_mergeinfo_property(rb, &new_value, value, nb->pool);
838
err = svn_repos__adjust_mergeinfo_property(&new_value, value,
841
pb->oldest_dumpstream_rev,
843
pb->notify_func, pb->notify_baton,
844
nb->pool, pb->notify_pool);
845
svn_pool_clear(pb->notify_pool);
827
848
if (pb->validate_props)
983
1003
const char *txn_name = NULL;
984
1004
apr_hash_t *hooks_env;
986
/* If we're skipping this revision or it has an invalid revision
987
number, we're done here. */
988
if (rb->skipped || (rb->rev <= 0))
1006
/* If we're skipping this revision we're done here. */
989
1008
return SVN_NO_ERROR;
1012
/* Special case: set revision 0 properties when loading into an
1013
'empty' filesystem. */
1014
svn_revnum_t youngest_rev;
1016
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool));
1018
if (youngest_rev == 0)
1020
apr_hash_t *orig_props;
1021
apr_hash_t *new_props;
1022
apr_array_header_t *diff;
1025
SVN_ERR(svn_fs_revision_proplist(&orig_props, pb->fs, 0, rb->pool));
1026
new_props = svn_prop_array_to_hash(rb->revprops, rb->pool);
1027
SVN_ERR(svn_prop_diffs(&diff, new_props, orig_props, rb->pool));
1029
for (i = 0; i < diff->nelts; i++)
1031
const svn_prop_t *prop = &APR_ARRAY_IDX(diff, i, svn_prop_t);
1033
SVN_ERR(change_rev_prop(pb->repos, 0, prop->name, prop->value,
1034
pb->validate_props, rb->pool));
1038
return SVN_NO_ERROR;
1041
/* If the dumpstream doesn't have an 'svn:date' property and we
1042
aren't ignoring the dates in the dumpstream altogether, remove
1043
any 'svn:date' revision property that was set by FS layer when
1044
the TXN was created. */
1045
if (! (pb->ignore_dates || rb->datestamp))
1047
svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t);
1048
prop->name = SVN_PROP_REVISION_DATE;
1052
/* Apply revision property changes. */
1053
if (rb->pb->validate_props)
1054
SVN_ERR(svn_repos_fs_change_txn_props(rb->txn, rb->revprops, rb->pool));
1056
SVN_ERR(svn_fs_change_txn_props(rb->txn, rb->revprops, rb->pool));
991
1058
/* Get the txn name and hooks environment if they will be needed. */
992
1059
if (pb->use_pre_commit_hook || pb->use_post_commit_hook)
1073
1140
/* Deltify the predecessors of paths changed in this revision. */
1074
1141
SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool));
1076
/* Grrr, svn_fs_commit_txn rewrites the datestamp property to the
1077
current clock-time. We don't want that, we want to preserve
1078
history exactly. Good thing revision props aren't versioned!
1079
Note that if rb->datestamp is NULL, that's fine -- if the dump
1080
data doesn't carry a datestamp, we want to preserve that fact in
1082
SVN_ERR(change_rev_prop(pb->repos, committed_rev, SVN_PROP_REVISION_DATE,
1083
rb->datestamp, pb->validate_props, rb->pool));
1085
1143
if (pb->notify_func)
1087
1145
/* ### TODO: Use proper scratch pool instead of pb->notify_pool */
1188
1252
const svn_repos_parse_fns3_t *parser;
1189
1253
void *parse_baton;
1190
struct parse_baton *pb;
1192
1255
/* This is really simple. */
1194
SVN_ERR(svn_repos_get_fs_build_parser4(&parser, &parse_baton,
1257
SVN_ERR(svn_repos_get_fs_build_parser5(&parser, &parse_baton,
1196
1259
start_rev, end_rev,
1197
1260
TRUE, /* look for copyfrom revs */
1198
1261
validate_props,
1264
use_pre_commit_hook,
1265
use_post_commit_hook,
1205
/* Heh. We know this is a parse_baton. This file made it. So
1206
cast away, and set our hook booleans. */
1208
pb->use_pre_commit_hook = use_pre_commit_hook;
1209
pb->use_post_commit_hook = use_post_commit_hook;
1211
1271
return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE,
1212
1272
cancel_func, cancel_baton, pool);