49
52
struct dump_edit_baton *eb;
50
53
struct dir_baton *parent_dir_baton;
55
/* Pool for per-directory allocations */
52
58
/* is this directory a new addition to this revision? */
53
59
svn_boolean_t added;
55
61
/* has this directory been written to the output stream? */
56
62
svn_boolean_t written_out;
58
/* the absolute path to this directory */
59
const char *abspath; /* an fspath */
64
/* the path to this directory */
65
const char *repos_relpath; /* a relpath */
61
67
/* Copyfrom info for the node, if any. */
62
68
const char *copyfrom_path; /* a relpath */
63
69
svn_revnum_t copyfrom_rev;
71
/* Properties which were modified during change_dir_prop. */
74
/* Properties which were deleted during change_dir_prop. */
75
apr_hash_t *deleted_props;
65
77
/* Hash of paths that need to be deleted, though some -might- be
66
78
replaced. Maps const char * paths to this dir_baton. Note that
67
79
they're full paths, because that's what the editor driver gives
68
80
us, although they're all really within this directory. */
69
81
apr_hash_t *deleted_entries;
83
/* Flags to trigger dumping props and record termination newlines. */
84
svn_boolean_t dump_props;
85
svn_boolean_t dump_newlines;
88
/* A file baton used by all file-related callback functions in the dump
92
struct dump_edit_baton *eb;
93
struct dir_baton *parent_dir_baton;
95
/* Pool for per-file allocations */
98
/* the path to this file */
99
const char *repos_relpath; /* a relpath */
101
/* Properties which were modified during change_file_prop. */
104
/* Properties which were deleted during change_file_prop. */
105
apr_hash_t *deleted_props;
107
/* The checksum of the file the delta is being applied to */
108
const char *base_checksum;
110
/* Copy state and source information (if any). */
111
svn_boolean_t is_copy;
112
const char *copyfrom_path;
113
svn_revnum_t copyfrom_rev;
115
/* The action associate with this node. */
116
enum svn_node_action action;
118
/* Flags to trigger dumping props and text. */
119
svn_boolean_t dump_text;
120
svn_boolean_t dump_props;
72
123
/* A handler baton to be used in window_handler(). */
81
132
/* The output stream we write the dumpfile to */
82
133
svn_stream_t *stream;
135
/* A backdoor ra session to fetch additional information during the edit. */
136
svn_ra_session_t *ra_session;
138
/* The repository relpath of the anchor of the editor when driven
139
via the RA update mechanism; NULL otherwise. (When the editor is
140
driven via the RA "replay" mechanism instead, the editor is
141
always anchored at the repository, we don't need to prepend an
142
anchor path to the dumped node paths, and open_root() doesn't
143
need to manufacture directory additions.) */
144
const char *update_anchor_relpath;
84
146
/* Pool for per-revision allocations */
87
/* Properties which were modified during change_file_prop
88
* or change_dir_prop. */
91
/* Properties which were deleted during change_file_prop
92
* or change_dir_prop. */
93
apr_hash_t *deleted_props;
95
/* Temporary buffer to write property hashes to in human-readable
96
* form. ### Is this really needed? */
97
svn_stringbuf_t *propstring;
99
149
/* Temporary file used for textdelta application along with its
100
150
absolute path; these two variables should be allocated in the
101
151
per-edit-session pool */
102
152
const char *delta_abspath;
103
153
apr_file_t *delta_file;
105
/* The checksum of the file the delta is being applied to */
106
const char *base_checksum;
155
/* The revision we're currently dumping. */
156
svn_revnum_t current_revision;
108
/* Flags to trigger dumping props and text */
109
svn_boolean_t dump_text;
110
svn_boolean_t dump_props;
111
svn_boolean_t dump_newlines;
158
/* The kind (file or directory) and baton of the item whose block of
159
dump stream data has not been fully completed; NULL if there's no
161
svn_node_kind_t pending_kind;
114
165
/* Make a directory baton to represent the directory at PATH (relative
119
170
* information is valid, the directory will be compared against its
122
* PARENT_DIR_BATON is the directory baton of this directory's parent,
123
* or NULL if this is the top-level directory of the edit. ADDED
124
* indicates if this directory is newly added in this revision.
125
* Perform all allocations in POOL. */
173
* PB is the directory baton of this directory's parent, or NULL if
174
* this is the top-level directory of the edit. ADDED indicates if
175
* this directory is newly added in this revision. Perform all
176
* allocations in POOL. */
126
177
static struct dir_baton *
127
178
make_dir_baton(const char *path,
128
179
const char *copyfrom_path,
129
180
svn_revnum_t copyfrom_rev,
130
181
void *edit_baton,
131
void *parent_dir_baton,
182
struct dir_baton *pb,
132
183
svn_boolean_t added,
133
184
apr_pool_t *pool)
135
186
struct dump_edit_baton *eb = edit_baton;
136
struct dir_baton *pb = parent_dir_baton;
137
187
struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
188
const char *repos_relpath;
140
190
/* Construct the full path of this node. */
141
/* ### FIXME: Not sure why we use an abspath here. If I understand
142
### correctly, the only place we used this path is in dump_node(),
143
### which immediately converts it into a relpath. -- cmpilato. */
145
abspath = svn_fspath__canonicalize(path, pool);
192
repos_relpath = svn_relpath_canonicalize(path, pool);
149
196
/* Strip leading slash from copyfrom_path so that the path is
150
197
canonical and svn_relpath_join can be used */
155
202
new_db->parent_dir_baton = pb;
156
new_db->abspath = abspath;
157
new_db->copyfrom_path = copyfrom_path;
204
new_db->repos_relpath = repos_relpath;
205
new_db->copyfrom_path = copyfrom_path
206
? svn_relpath_canonicalize(copyfrom_path, pool)
158
208
new_db->copyfrom_rev = copyfrom_rev;
159
209
new_db->added = added;
160
210
new_db->written_out = FALSE;
211
new_db->props = apr_hash_make(pool);
212
new_db->deleted_props = apr_hash_make(pool);
161
213
new_db->deleted_entries = apr_hash_make(pool);
166
/* Extract and dump properties stored in edit baton EB, using POOL for
167
* any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
168
* Unless DUMP_DATA_TOO is set, only property headers are dumped.
218
/* Make a file baton to represent the directory at PATH (relative to
219
* PB->eb). PB is the directory baton of this directory's parent, or
220
* NULL if this is the top-level directory of the edit. Perform all
221
* allocations in POOL. */
222
static struct file_baton *
223
make_file_baton(const char *path,
224
struct dir_baton *pb,
227
struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb));
230
new_fb->parent_dir_baton = pb;
232
new_fb->repos_relpath = svn_relpath_canonicalize(path, pool);
233
new_fb->props = apr_hash_make(pool);
234
new_fb->deleted_props = apr_hash_make(pool);
235
new_fb->is_copy = FALSE;
236
new_fb->copyfrom_path = NULL;
237
new_fb->copyfrom_rev = SVN_INVALID_REVNUM;
238
new_fb->action = svn_node_action_change;
243
/* Return in *HEADER and *CONTENT the headers and content for PROPS. */
170
244
static svn_error_t *
171
do_dump_props(struct dump_edit_baton *eb,
172
svn_boolean_t *trigger_var,
173
svn_boolean_t dump_data_too,
245
get_props_content(svn_stringbuf_t **header,
246
svn_stringbuf_t **content,
248
apr_hash_t *deleted_props,
249
apr_pool_t *result_pool,
250
apr_pool_t *scratch_pool)
176
svn_stream_t *propstream;
252
svn_stream_t *content_stream;
177
253
apr_hash_t *normal_props;
179
if (trigger_var && !*trigger_var)
182
SVN_ERR(svn_rdump__normalize_props(&normal_props, eb->props, eb->pool));
183
svn_stringbuf_setempty(eb->propstring);
184
propstream = svn_stream_from_stringbuf(eb->propstring, eb->pool);
185
SVN_ERR(svn_hash_write_incremental(normal_props, eb->deleted_props,
186
propstream, "PROPS-END", pool));
187
SVN_ERR(svn_stream_close(propstream));
256
*content = svn_stringbuf_create_empty(result_pool);
257
*header = svn_stringbuf_create_empty(result_pool);
259
content_stream = svn_stream_from_stringbuf(*content, scratch_pool);
261
SVN_ERR(svn_rdump__normalize_props(&normal_props, props, scratch_pool));
262
SVN_ERR(svn_hash_write_incremental(normal_props, deleted_props,
263
content_stream, "PROPS-END",
265
SVN_ERR(svn_stream_close(content_stream));
189
267
/* Prop-delta: true */
190
SVN_ERR(svn_stream_printf(eb->stream, pool,
191
SVN_REPOS_DUMPFILE_PROP_DELTA
268
*header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA
194
271
/* Prop-content-length: 193 */
195
SVN_ERR(svn_stream_printf(eb->stream, pool,
196
SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
197
": %" APR_SIZE_T_FMT "\n", eb->propstring->len));
272
buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
273
": %" APR_SIZE_T_FMT "\n", (*content)->len);
274
svn_stringbuf_appendcstr(*header, buf);
279
/* Extract and dump properties stored in PROPS and property deletions
280
* stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to
283
* If PROPSTRING is non-NULL, set *PROPSTRING to a string containing
284
* the content block of the property changes; otherwise, dump that to
288
do_dump_props(svn_stringbuf_t **propstring,
289
svn_stream_t *stream,
291
apr_hash_t *deleted_props,
292
svn_boolean_t *trigger_var,
293
apr_pool_t *result_pool,
294
apr_pool_t *scratch_pool)
296
svn_stringbuf_t *header;
297
svn_stringbuf_t *content;
300
if (trigger_var && !*trigger_var)
303
SVN_ERR(get_props_content(&header, &content, props, deleted_props,
304
result_pool, scratch_pool));
306
SVN_ERR(svn_stream_write(stream, header->data, &len));
310
*propstring = content;
201
314
/* Content-length: 14 */
202
SVN_ERR(svn_stream_printf(eb->stream, pool,
315
SVN_ERR(svn_stream_printf(stream, scratch_pool,
203
316
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
204
317
": %" APR_SIZE_T_FMT "\n\n",
205
eb->propstring->len));
207
/* The properties. */
208
SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
209
&(eb->propstring->len)));
321
SVN_ERR(svn_stream_write(stream, content->data, &len));
211
323
/* No text is going to be dumped. Write a couple of newlines and
212
324
wait for the next node/ revision. */
213
SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
325
SVN_ERR(svn_stream_puts(stream, "\n\n"));
215
327
/* Cleanup so that data is never dumped twice. */
216
SVN_ERR(svn_hash__clear(eb->props, eb->pool));
217
SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
328
apr_hash_clear(props);
329
apr_hash_clear(deleted_props);
219
331
*trigger_var = FALSE;
249
361
static svn_error_t *
250
362
dump_node(struct dump_edit_baton *eb,
251
const char *path, /* an absolute path. */
252
svn_node_kind_t kind,
363
const char *repos_relpath,
364
struct dir_baton *db,
365
struct file_baton *fb,
253
366
enum svn_node_action action,
254
367
svn_boolean_t is_copy,
255
368
const char *copyfrom_path,
256
369
svn_revnum_t copyfrom_rev,
257
370
apr_pool_t *pool)
259
/* Remove leading slashes from path and copyfrom_path */
261
path = svn_relpath_canonicalize(path, pool);
264
copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool);
266
/* Node-path: commons/STATUS */
372
const char *node_relpath = repos_relpath;
374
assert(svn_relpath_is_canonical(repos_relpath));
375
assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path));
376
assert(! (db && fb));
378
/* Add the edit root relpath prefix if necessary. */
379
if (eb->update_anchor_relpath)
380
node_relpath = svn_relpath_join(eb->update_anchor_relpath,
267
384
SVN_ERR(svn_stream_printf(eb->stream, pool,
268
SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path));
385
SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
270
/* Node-kind: file */
271
if (kind == svn_node_file)
388
/* Node-kind: "file" | "dir" */
272
390
SVN_ERR(svn_stream_printf(eb->stream, pool,
273
391
SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
274
else if (kind == svn_node_dir)
275
393
SVN_ERR(svn_stream_printf(eb->stream, pool,
276
394
SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
282
400
case svn_node_action_change:
283
401
/* We are here after a change_file_prop or change_dir_prop. They
284
402
set up whatever dump_props they needed to- nothing to
285
do here but print node action information */
286
SVN_ERR(svn_stream_printf(eb->stream, pool,
287
SVN_REPOS_DUMPFILE_NODE_ACTION
403
do here but print node action information.
405
Node-action: change. */
406
SVN_ERR(svn_stream_puts(eb->stream,
407
SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n"));
291
410
case svn_node_action_replace:
413
/* Delete the original, and then re-add the replacement as a
414
copy using recursive calls into this function. */
415
SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete,
416
FALSE, NULL, SVN_INVALID_REVNUM, pool));
417
SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add,
418
is_copy, copyfrom_path, copyfrom_rev, pool));
294
422
/* Node-action: replace */
295
SVN_ERR(svn_stream_printf(eb->stream, pool,
296
SVN_REPOS_DUMPFILE_NODE_ACTION
423
SVN_ERR(svn_stream_puts(eb->stream,
424
SVN_REPOS_DUMPFILE_NODE_ACTION
299
427
/* Wait for a change_*_prop to be called before dumping
301
eb->dump_props = TRUE;
430
fb->dump_props = TRUE;
432
db->dump_props = TRUE;
304
/* More complex case: is_copy is true, and copyfrom_path/
305
copyfrom_rev are present: delete the original, and then re-add
308
SVN_ERR(svn_stream_printf(eb->stream, pool,
309
SVN_REPOS_DUMPFILE_NODE_ACTION
312
/* Recurse: Print an additional add-with-history record. */
313
SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
314
is_copy, copyfrom_path, copyfrom_rev, pool));
316
/* We can leave this routine quietly now, don't need to dump any
317
content; that was already done in the second record. */
320
436
case svn_node_action_delete:
321
SVN_ERR(svn_stream_printf(eb->stream, pool,
322
SVN_REPOS_DUMPFILE_NODE_ACTION
437
/* Node-action: delete */
438
SVN_ERR(svn_stream_puts(eb->stream,
439
SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n"));
325
441
/* We can leave this routine quietly now. Nothing more to do-
326
442
print a couple of newlines because we're not dumping props or
328
SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
444
SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
331
448
case svn_node_action_add:
332
SVN_ERR(svn_stream_printf(eb->stream, pool,
333
SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
337
/* eb->dump_props for files is handled in close_file
338
which is called immediately. However, directories are not
339
closed until all the work inside them has been done;
340
eb->dump_props for directories is handled in all the
341
functions that can possibly be called after add_directory:
342
add_directory, open_directory, delete_entry, close_directory,
343
add_file, open_file. change_dir_prop is a special case. */
345
/* Wait for a change_*_prop to be called before dumping
347
eb->dump_props = TRUE;
351
SVN_ERR(svn_stream_printf(eb->stream, pool,
352
SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
354
SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
356
copyfrom_rev, copyfrom_path));
358
/* Ugly hack: If a directory was copied from a previous
359
revision, nothing like close_file() will be called to write two
360
blank lines. If change_dir_prop() is called, props are dumped
361
(along with the necessary PROPS-END\n\n and we're good. So
362
set DUMP_NEWLINES here to print the newlines unless
363
change_dir_prop() is called next otherwise the `svnadmin load`
365
if (kind == svn_node_dir)
366
eb->dump_newlines = TRUE;
449
/* Node-action: add */
450
SVN_ERR(svn_stream_puts(eb->stream,
451
SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
455
/* Node-copyfrom-rev / Node-copyfrom-path */
456
SVN_ERR(svn_stream_printf(eb->stream, pool,
457
SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
459
SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
461
copyfrom_rev, copyfrom_path));
463
/* Ugly hack: If a directory was copied from a previous
464
revision, nothing like close_file() will be called to write two
465
blank lines. If change_dir_prop() is called, props are dumped
466
(along with the necessary PROPS-END\n\n and we're good. So
467
set DUMP_NEWLINES here to print the newlines unless
468
change_dir_prop() is called next otherwise the `svnadmin load`
471
db->dump_newlines = TRUE;
475
/* fb->dump_props (for files) is handled in close_file()
476
which is called immediately.
478
However, directories are not closed until all the work
479
inside them has been done; db->dump_props (for directories)
480
is handled (via dump_pending()) in all the functions that
481
can possibly be called after add_directory():
490
change_dir_prop() is a special case. */
492
fb->dump_props = TRUE;
494
db->dump_props = TRUE;
373
502
static svn_error_t *
503
dump_mkdir(struct dump_edit_baton *eb,
504
const char *repos_relpath,
507
svn_stringbuf_t *prop_header, *prop_content;
512
SVN_ERR(svn_stream_printf(eb->stream, pool,
513
SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
517
SVN_ERR(svn_stream_printf(eb->stream, pool,
518
SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
520
/* Node-action: add */
521
SVN_ERR(svn_stream_puts(eb->stream,
522
SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
524
/* Dump the (empty) property block. */
525
SVN_ERR(get_props_content(&prop_header, &prop_content,
526
apr_hash_make(pool), apr_hash_make(pool),
528
len = prop_header->len;
529
SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len));
530
len = prop_content->len;
531
buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH
532
": %" APR_SIZE_T_FMT "\n", len);
533
SVN_ERR(svn_stream_puts(eb->stream, buf));
534
SVN_ERR(svn_stream_puts(eb->stream, "\n"));
535
SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len));
537
/* Newlines to tie it all off. */
538
SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
543
/* Dump pending items from the specified node, to allow starting the dump
546
dump_pending(struct dump_edit_baton *eb,
547
apr_pool_t *scratch_pool)
549
if (! eb->pending_baton)
552
if (eb->pending_kind == svn_node_dir)
554
struct dir_baton *db = eb->pending_baton;
556
/* Some pending properties to dump? */
557
SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props,
558
&(db->dump_props), db->pool, scratch_pool));
560
/* Some pending newlines to dump? */
561
SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool));
563
else if (eb->pending_kind == svn_node_file)
565
struct file_baton *fb = eb->pending_baton;
567
/* Some pending properties to dump? */
568
SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props,
569
&(fb->dump_props), fb->pool, scratch_pool));
574
/* Anything that was pending is pending no longer. */
575
eb->pending_baton = NULL;
576
eb->pending_kind = svn_node_none;
583
/*** Editor Function Implementations ***/
374
586
open_root(void *edit_baton,
375
587
svn_revnum_t base_revision,
376
588
apr_pool_t *pool,
377
589
void **root_baton)
379
591
struct dump_edit_baton *eb = edit_baton;
592
struct dir_baton *new_db = NULL;
381
594
/* Clear the per-revision pool after each revision */
382
595
svn_pool_clear(eb->pool);
384
eb->props = apr_hash_make(eb->pool);
385
eb->deleted_props = apr_hash_make(eb->pool);
386
eb->propstring = svn_stringbuf_create("", eb->pool);
388
*root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
389
edit_baton, NULL, FALSE, eb->pool);
390
597
LDR_DBG(("open_root %p\n", *root_baton));
599
if (eb->update_anchor_relpath)
602
const char *parent_path = eb->update_anchor_relpath;
603
apr_array_header_t *dirs_to_add =
604
apr_array_make(pool, 4, sizeof(const char *));
605
apr_pool_t *iterpool = svn_pool_create(pool);
607
while (! svn_path_is_empty(parent_path))
609
APR_ARRAY_PUSH(dirs_to_add, const char *) = parent_path;
610
parent_path = svn_relpath_dirname(parent_path, pool);
613
for (i = dirs_to_add->nelts; i; --i)
615
const char *dir_to_add =
616
APR_ARRAY_IDX(dirs_to_add, i - 1, const char *);
618
svn_pool_clear(iterpool);
620
/* For parents of the source directory, we just manufacture
621
the adds ourselves. */
624
SVN_ERR(dump_mkdir(eb, dir_to_add, iterpool));
628
/* ... but for the source directory itself, we'll defer
629
to letting the typical plumbing handle this task. */
630
new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
631
edit_baton, NULL, TRUE, pool);
632
SVN_ERR(dump_node(eb, new_db->repos_relpath, new_db,
633
NULL, svn_node_action_add, FALSE,
634
NULL, SVN_INVALID_REVNUM, pool));
636
/* Remember that we've started but not yet finished
637
handling this directory. */
638
new_db->written_out = TRUE;
639
eb->pending_baton = new_db;
640
eb->pending_kind = svn_node_dir;
643
svn_pool_destroy(iterpool);
648
new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
649
edit_baton, NULL, FALSE, pool);
652
*root_baton = new_db;
392
653
return SVN_NO_ERROR;
403
664
LDR_DBG(("delete_entry %s\n", path));
405
/* Some pending properties to dump? */
406
SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
408
/* Some pending newlines to dump? */
409
SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
411
/* Add this path to the deleted_entries of the parent directory
413
apr_hash_set(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path),
414
APR_HASH_KEY_STRING, pb);
666
SVN_ERR(dump_pending(pb->eb, pool));
668
/* We don't dump this deletion immediate. Rather, we add this path
669
to the deleted_entries of the parent directory baton. That way,
670
we can tell (later) an addition from a replacement. All the real
671
deletions get handled in close_directory(). */
672
svn_hash_sets(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path), pb);
416
674
return SVN_NO_ERROR;
432
690
LDR_DBG(("add_directory %s\n", path));
692
SVN_ERR(dump_pending(pb->eb, pool));
434
694
new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb,
435
695
pb, TRUE, pb->eb->pool);
437
/* Some pending properties to dump? */
438
SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
440
/* Some pending newlines to dump? */
441
SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
443
697
/* This might be a replacement -- is the path already deleted? */
444
val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
698
val = svn_hash_gets(pb->deleted_entries, path);
446
700
/* Detect an add-with-history */
447
701
is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
449
703
/* Dump the node */
450
SVN_ERR(dump_node(pb->eb, path,
704
SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL,
452
705
val ? svn_node_action_replace : svn_node_action_add,
454
is_copy ? copyfrom_path : NULL,
707
is_copy ? new_db->copyfrom_path : NULL,
455
708
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
459
712
/* Delete the path, it's now been dumped */
460
apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
713
svn_hash_sets(pb->deleted_entries, path, NULL);
715
/* Remember that we've started, but not yet finished handling this
462
717
new_db->written_out = TRUE;
718
pb->eb->pending_baton = new_db;
719
pb->eb->pending_kind = svn_node_dir;
464
721
*child_baton = new_db;
465
722
return SVN_NO_ERROR;
506
760
apr_pool_t *pool)
508
762
struct dir_baton *db = dir_baton;
509
struct dump_edit_baton *eb = db->eb;
510
763
apr_hash_index_t *hi;
764
svn_boolean_t this_pending;
512
766
LDR_DBG(("close_directory %p\n", dir_baton));
514
/* Some pending properties to dump? */
515
SVN_ERR(do_dump_props(eb, &(eb->dump_props), TRUE, pool));
517
/* Some pending newlines to dump? */
518
SVN_ERR(do_dump_newlines(eb, &(eb->dump_newlines), pool));
768
/* Remember if this directory is the one currently pending. */
769
this_pending = (db->eb->pending_baton == db);
771
SVN_ERR(dump_pending(db->eb, pool));
773
/* If this directory was pending, then dump_pending() should have
774
taken care of all the props and such. Of course, the only way
775
that would be the case is if this directory was added/replaced.
777
Otherwise, if stuff for this directory has already been written
778
out (at some point in the past, prior to our handling other
779
nodes), we might need to generate a second "change" record just
780
to carry the information we've since learned about the
782
if ((! this_pending) && (db->dump_props))
784
SVN_ERR(dump_node(db->eb, db->repos_relpath, db, NULL,
785
svn_node_action_change, FALSE,
786
NULL, SVN_INVALID_REVNUM, pool));
787
db->eb->pending_baton = db;
788
db->eb->pending_kind = svn_node_dir;
789
SVN_ERR(dump_pending(db->eb, pool));
520
792
/* Dump the deleted directory entries */
521
793
for (hi = apr_hash_first(pool, db->deleted_entries); hi;
540
814
void **file_baton)
542
816
struct dir_baton *pb = parent_baton;
817
struct file_baton *fb;
544
svn_boolean_t is_copy;
546
820
LDR_DBG(("add_file %s\n", path));
548
/* Some pending properties to dump? */
549
SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
822
SVN_ERR(dump_pending(pb->eb, pool));
551
/* Some pending newlines to dump? */
552
SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
824
/* Make the file baton. */
825
fb = make_file_baton(path, pb, pool);
554
827
/* This might be a replacement -- is the path already deleted? */
555
val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
828
val = svn_hash_gets(pb->deleted_entries, path);
557
830
/* Detect add-with-history. */
558
is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
561
SVN_ERR(dump_node(pb->eb, path,
563
val ? svn_node_action_replace : svn_node_action_add,
565
is_copy ? copyfrom_path : NULL,
566
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
831
if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev))
833
fb->copyfrom_path = svn_relpath_canonicalize(copyfrom_path, fb->pool);
834
fb->copyfrom_rev = copyfrom_rev;
837
fb->action = val ? svn_node_action_replace : svn_node_action_add;
839
/* Delete the path, it's now been dumped. */
570
/* delete the path, it's now been dumped. */
571
apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
573
/* Build a nice file baton to pass to change_file_prop and
575
*file_baton = pb->eb;
841
svn_hash_sets(pb->deleted_entries, path, NULL);
577
844
return SVN_NO_ERROR;
585
852
void **file_baton)
587
854
struct dir_baton *pb = parent_baton;
588
const char *copyfrom_path = NULL;
589
svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
855
struct file_baton *fb;
591
857
LDR_DBG(("open_file %s\n", path));
593
/* Some pending properties to dump? */
594
SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
859
SVN_ERR(dump_pending(pb->eb, pool));
596
/* Some pending newlines to dump? */
597
SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
861
/* Make the file baton. */
862
fb = make_file_baton(path, pb, pool);
599
864
/* If the parent directory has explicit copyfrom path and rev,
600
865
record the same for this one. */
601
866
if (ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
603
copyfrom_path = svn_relpath_join(pb->copyfrom_path,
604
svn_relpath_basename(path, NULL),
606
copyfrom_rev = pb->copyfrom_rev;
868
fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path,
869
svn_relpath_basename(path, NULL),
871
fb->copyfrom_rev = pb->copyfrom_rev;
609
SVN_ERR(dump_node(pb->eb, path, svn_node_file, svn_node_action_change,
610
FALSE, copyfrom_path, copyfrom_rev, pool));
612
/* Build a nice file baton to pass to change_file_prop and
614
*file_baton = pb->eb;
616
875
return SVN_NO_ERROR;
623
882
apr_pool_t *pool)
625
884
struct dir_baton *db = parent_baton;
885
svn_boolean_t this_pending;
627
887
LDR_DBG(("change_dir_prop %p\n", parent_baton));
629
if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
889
/* This directory is not pending, but something else is, so handle
890
the "something else". */
891
this_pending = (db->eb->pending_baton == db);
893
SVN_ERR(dump_pending(db->eb, pool));
895
if (svn_property_kind2(name) != svn_prop_regular_kind)
630
896
return SVN_NO_ERROR;
633
apr_hash_set(db->eb->props, apr_pstrdup(db->eb->pool, name),
634
APR_HASH_KEY_STRING, svn_string_dup(value, db->eb->pool));
899
svn_hash_sets(db->props,
900
apr_pstrdup(db->pool, name),
901
svn_string_dup(value, db->pool));
636
apr_hash_set(db->eb->deleted_props, apr_pstrdup(db->eb->pool, name),
637
APR_HASH_KEY_STRING, "");
639
if (! db->written_out)
641
/* If db->written_out is set, it means that the node information
642
corresponding to this directory has already been written: don't
643
do anything; do_dump_props() will take care of dumping the
644
props. If it not, dump the node itself before dumping the
647
SVN_ERR(dump_node(db->eb, db->abspath, svn_node_dir,
648
svn_node_action_change, FALSE, db->copyfrom_path,
649
db->copyfrom_rev, pool));
650
db->written_out = TRUE;
903
svn_hash_sets(db->deleted_props, apr_pstrdup(db->pool, name), "");
653
905
/* Make sure we eventually output the props, and disable printing
654
906
a couple of extra newlines */
655
db->eb->dump_newlines = FALSE;
656
db->eb->dump_props = TRUE;
907
db->dump_newlines = FALSE;
908
db->dump_props = TRUE;
658
910
return SVN_NO_ERROR;
664
916
const svn_string_t *value,
665
917
apr_pool_t *pool)
667
struct dump_edit_baton *eb = file_baton;
919
struct file_baton *fb = file_baton;
669
921
LDR_DBG(("change_file_prop %p\n", file_baton));
671
if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
923
if (svn_property_kind2(name) != svn_prop_regular_kind)
672
924
return SVN_NO_ERROR;
675
apr_hash_set(eb->props, apr_pstrdup(eb->pool, name),
676
APR_HASH_KEY_STRING, svn_string_dup(value, eb->pool));
927
svn_hash_sets(fb->props,
928
apr_pstrdup(fb->pool, name),
929
svn_string_dup(value, fb->pool));
678
apr_hash_set(eb->deleted_props, apr_pstrdup(eb->pool, name),
679
APR_HASH_KEY_STRING, "");
931
svn_hash_sets(fb->deleted_props, apr_pstrdup(fb->pool, name), "");
681
933
/* Dump the property headers and wait; close_file might need
682
934
to write text headers too depending on whether
683
935
apply_textdelta is called */
684
eb->dump_props = TRUE;
936
fb->dump_props = TRUE;
686
938
return SVN_NO_ERROR;
744
996
const char *text_checksum,
745
997
apr_pool_t *pool)
747
struct dump_edit_baton *eb = file_baton;
999
struct file_baton *fb = file_baton;
1000
struct dump_edit_baton *eb = fb->eb;
748
1001
apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));
1002
svn_stringbuf_t *propstring;
750
1004
LDR_DBG(("close_file %p\n", file_baton));
752
/* Some pending properties to dump? Dump just the headers- dump the
753
props only after dumping the text headers too (if present) */
754
SVN_ERR(do_dump_props(eb, &(eb->dump_props), FALSE, pool));
1006
SVN_ERR(dump_pending(eb, pool));
1008
/* Dump the node. */
1009
SVN_ERR(dump_node(eb, fb->repos_relpath, NULL, fb,
1010
fb->action, fb->is_copy, fb->copyfrom_path,
1011
fb->copyfrom_rev, pool));
1013
/* Some pending properties to dump? We'll dump just the headers for
1014
now, then dump the actual propchange content only after dumping
1015
the text headers too (if present). */
1016
SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props,
1017
&(fb->dump_props), pool, pool));
756
1019
/* Dump the text headers */
759
1022
apr_status_t err;
761
1024
/* Text-delta: true */
762
SVN_ERR(svn_stream_printf(eb->stream, pool,
763
SVN_REPOS_DUMPFILE_TEXT_DELTA
1025
SVN_ERR(svn_stream_puts(eb->stream,
1026
SVN_REPOS_DUMPFILE_TEXT_DELTA
766
1029
err = apr_file_info_get(info, APR_FINFO_SIZE, eb->delta_file);
768
1031
SVN_ERR(svn_error_wrap_apr(err, NULL));
770
if (eb->base_checksum)
1033
if (fb->base_checksum)
771
1034
/* Text-delta-base-md5: */
772
1035
SVN_ERR(svn_stream_printf(eb->stream, pool,
773
1036
SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
1038
fb->base_checksum));
777
1040
/* Text-content-length: 39 */
778
1041
SVN_ERR(svn_stream_printf(eb->stream, pool,
790
1053
/* Content-length: 1549 */
791
1054
/* If both text and props are absent, skip this header */
793
1056
SVN_ERR(svn_stream_printf(eb->stream, pool,
794
1057
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
796
(unsigned long)info->size + eb->propstring->len));
797
else if (eb->dump_text)
1059
(unsigned long)info->size + propstring->len));
1060
else if (fb->dump_text)
798
1061
SVN_ERR(svn_stream_printf(eb->stream, pool,
799
1062
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
801
1064
(unsigned long)info->size));
803
1066
/* Dump the props now */
806
SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
807
&(eb->propstring->len)));
1069
SVN_ERR(svn_stream_write(eb->stream, propstring->data,
1070
&(propstring->len)));
810
eb->dump_props = FALSE;
811
SVN_ERR(svn_hash__clear(eb->props, eb->pool));
812
SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
1073
fb->dump_props = FALSE;
1074
apr_hash_clear(fb->props);
1075
apr_hash_clear(fb->deleted_props);
815
1078
/* Dump the text */
818
1081
/* Seek to the beginning of the delta file, map it to a stream,
819
1082
and copy the stream to eb->stream. Then close the stream and
846
1108
return SVN_NO_ERROR;
1111
static svn_error_t *
1112
fetch_base_func(const char **filename,
1115
svn_revnum_t base_revision,
1116
apr_pool_t *result_pool,
1117
apr_pool_t *scratch_pool)
1119
struct dump_edit_baton *eb = baton;
1120
svn_stream_t *fstream;
1126
if (! SVN_IS_VALID_REVNUM(base_revision))
1127
base_revision = eb->current_revision - 1;
1129
SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
1130
svn_io_file_del_on_pool_cleanup,
1131
result_pool, scratch_pool));
1133
err = svn_ra_get_file(eb->ra_session, path, base_revision,
1134
fstream, NULL, NULL, scratch_pool);
1135
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
1137
svn_error_clear(err);
1138
SVN_ERR(svn_stream_close(fstream));
1141
return SVN_NO_ERROR;
1144
return svn_error_trace(err);
1146
SVN_ERR(svn_stream_close(fstream));
1148
return SVN_NO_ERROR;
1151
static svn_error_t *
1152
fetch_props_func(apr_hash_t **props,
1155
svn_revnum_t base_revision,
1156
apr_pool_t *result_pool,
1157
apr_pool_t *scratch_pool)
1159
struct dump_edit_baton *eb = baton;
1160
svn_node_kind_t node_kind;
1165
if (! SVN_IS_VALID_REVNUM(base_revision))
1166
base_revision = eb->current_revision - 1;
1168
SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
1171
if (node_kind == svn_node_file)
1173
SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision,
1174
NULL, NULL, props, result_pool));
1176
else if (node_kind == svn_node_dir)
1178
apr_array_header_t *tmp_props;
1180
SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
1181
base_revision, 0 /* Dirent fields */,
1183
tmp_props = svn_prop_hash_to_array(*props, result_pool);
1184
SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
1186
*props = svn_prop_array_to_hash(tmp_props, result_pool);
1190
*props = apr_hash_make(result_pool);
1193
return SVN_NO_ERROR;
1196
static svn_error_t *
1197
fetch_kind_func(svn_node_kind_t *kind,
1200
svn_revnum_t base_revision,
1201
apr_pool_t *scratch_pool)
1203
struct dump_edit_baton *eb = baton;
1208
if (! SVN_IS_VALID_REVNUM(base_revision))
1209
base_revision = eb->current_revision - 1;
1211
SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, kind,
1214
return SVN_NO_ERROR;
850
1218
svn_rdump__get_dump_editor(const svn_delta_editor_t **editor,
851
1219
void **edit_baton,
1220
svn_revnum_t revision,
852
1221
svn_stream_t *stream,
1222
svn_ra_session_t *ra_session,
1223
const char *update_anchor_relpath,
853
1224
svn_cancel_func_t cancel_func,
854
1225
void *cancel_baton,
855
1226
apr_pool_t *pool)
857
1228
struct dump_edit_baton *eb;
858
1229
svn_delta_editor_t *de;
1230
svn_delta_shim_callbacks_t *shim_callbacks =
1231
svn_delta_shim_callbacks_default(pool);
860
1233
eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));
861
1234
eb->stream = stream;
1235
eb->ra_session = ra_session;
1236
eb->update_anchor_relpath = update_anchor_relpath;
1237
eb->current_revision = revision;
1238
eb->pending_kind = svn_node_none;
863
1240
/* Create a special per-revision pool */
864
1241
eb->pool = svn_pool_create(pool);