256
279
static struct file_baton *
257
280
make_file_baton(const char *path,
281
struct dir_baton *parent_baton,
258
282
svn_boolean_t added,
259
struct edit_baton *edit_baton,
283
apr_pool_t *result_pool)
262
apr_pool_t *file_pool = svn_pool_create(pool);
285
apr_pool_t *file_pool = svn_pool_create(result_pool);
263
286
struct file_baton *file_baton = apr_pcalloc(file_pool, sizeof(*file_baton));
265
file_baton->edit_baton = edit_baton;
288
file_baton->parent_baton = parent_baton;
289
file_baton->edit_baton = parent_baton->edit_baton;
266
290
file_baton->added = added;
267
291
file_baton->tree_conflicted = FALSE;
268
292
file_baton->skip = FALSE;
269
293
file_baton->pool = file_pool;
270
294
file_baton->path = apr_pstrdup(file_pool, path);
271
file_baton->wcpath = svn_dirent_join(edit_baton->target, path, file_pool);
272
file_baton->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t));
273
file_baton->base_revision = edit_baton->revision;
295
file_baton->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
296
file_baton->base_revision = parent_baton->edit_baton->revision;
298
parent_baton->users++;
275
300
return file_baton;
278
/* Helper function: return up to two svn:mime-type values buried
279
* within a file baton. Set *MIMETYPE1 to the value within the file's
280
* pristine properties, or NULL if not available. Set *MIMETYPE2 to
281
* the value within the "new" file's propchanges, or NULL if not
285
get_file_mime_types(const char **mimetype1,
286
const char **mimetype2,
287
struct file_baton *b)
293
if (b->pristine_props)
295
svn_string_t *pristine_val;
296
pristine_val = apr_hash_get(b->pristine_props, SVN_PROP_MIME_TYPE,
297
strlen(SVN_PROP_MIME_TYPE));
299
*mimetype2 = *mimetype1 = pristine_val->data;
305
svn_prop_t *propchange;
307
for (i = 0; i < b->propchanges->nelts; i++)
309
propchange = &APR_ARRAY_IDX(b->propchanges, i, svn_prop_t);
310
if (strcmp(propchange->name, SVN_PROP_MIME_TYPE) == 0)
312
if (propchange->value)
313
*mimetype2 = propchange->value->data;
321
/* Get revision REVISION of the file described by B from the repository.
322
* Set B->path_start_revision to the path of a new temporary file containing
323
* the file's text. Set B->pristine_props to a new hash containing the
324
* file's properties. Install a pool cleanup handler on B->pool to delete
303
/* Get revision FB->base_revision of the file described by FB from the
304
* repository, through FB->edit_baton->ra_session.
306
* Unless PROPS_ONLY is true:
307
* Set FB->path_start_revision to the path of a new temporary file containing
309
* Set FB->start_md5_checksum to that file's MD-5 checksum.
310
* Install a pool cleanup handler on FB->pool to delete the file.
313
* Set FB->pristine_props to a new hash containing the file's properties.
315
* Allocate all results in FB->pool.
327
317
static svn_error_t *
328
get_file_from_ra(struct file_baton *b,
318
get_file_from_ra(struct file_baton *fb,
329
319
svn_boolean_t props_only,
330
320
apr_pool_t *scratch_pool)
334
324
svn_stream_t *fstream;
336
SVN_ERR(svn_stream_open_unique(&fstream, &(b->path_start_revision), NULL,
337
svn_io_file_del_on_pool_cleanup, b->pool,
326
SVN_ERR(svn_stream_open_unique(&fstream, &(fb->path_start_revision),
327
NULL, svn_io_file_del_on_pool_cleanup,
328
fb->pool, scratch_pool));
340
fstream = svn_stream_checksummed2(fstream, NULL, &b->start_md5_checksum,
341
svn_checksum_md5, TRUE, b->pool);
330
fstream = svn_stream_checksummed2(fstream, NULL, &fb->start_md5_checksum,
331
svn_checksum_md5, TRUE, scratch_pool);
343
333
/* Retrieve the file and its properties */
344
SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
334
SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
348
&(b->pristine_props),
338
&(fb->pristine_props),
350
340
SVN_ERR(svn_stream_close(fstream));
354
SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
344
SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
358
&(b->pristine_props),
348
&(fb->pristine_props),
362
352
return SVN_NO_ERROR;
365
/* Issue #3657 'dav update report handler in skelta mode can cause
366
spurious conflicts'. When communicating with the repository via ra_serf
367
and ra_neon, the change_dir_prop and change_file_prop svn_delta_editor_t
355
/* Remove every no-op property change from CHANGES: that is, remove every
356
entry in which the target value is the same as the value of the
357
corresponding property in PRISTINE_PROPS.
359
Issue #3657 'dav update report handler in skelta mode can cause
360
spurious conflicts'. When communicating with the repository via ra_serf,
361
the change_dir_prop and change_file_prop svn_delta_editor_t
368
362
callbacks are called (obviously) when a directory or file property has
369
363
changed between the start and end of the edit. Less obvious however,
370
364
is that these callbacks may be made describing *all* of the properties
371
365
on FILE_BATON->PATH when using the DAV providers, not just the change(s).
372
(Specifically ra_neon does this for diff/merge and ra_serf does it
373
for diff/merge/update/switch).
366
(Specifically ra_serf does it for diff/merge/update/switch).
375
368
This means that the change_[file|dir]_prop svn_delta_editor_t callbacks
376
369
may be made where there are no property changes (i.e. a noop change of
476
454
void **root_baton)
478
456
struct edit_baton *eb = edit_baton;
479
struct dir_baton *b = make_dir_baton("", NULL, eb, FALSE, pool);
481
/* Override the wcpath in our baton. */
482
b->wcpath = apr_pstrdup(pool, eb->target);
484
SVN_ERR(get_dirprops_from_ra(b, base_revision));
490
/* Recursively walk tree rooted at DIR (at REVISION) in the repository,
491
* reporting all files as deleted. Part of a workaround for issue 2333.
457
struct dir_baton *db = make_dir_baton("", NULL, eb, FALSE, base_revision,
460
db->left_source = svn_diff__source_create(eb->revision, db->pool);
461
db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
463
SVN_ERR(eb->processor->dir_opened(&db->pdb,
473
db->pool /* scratch_pool */));
479
/* Compare a file being deleted against an empty file.
482
diff_deleted_file(const char *path,
483
struct dir_baton *db,
484
apr_pool_t *scratch_pool)
486
struct edit_baton *eb = db->edit_baton;
487
struct file_baton *fb = make_file_baton(path, db, FALSE, scratch_pool);
488
svn_boolean_t skip = FALSE;
489
svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
493
SVN_ERR(eb->cancel_func(eb->cancel_baton));
495
SVN_ERR(eb->processor->file_opened(&fb->pfb, &skip, path,
497
NULL /* right_source */,
498
NULL /* copyfrom_source */,
501
scratch_pool, scratch_pool));
504
SVN_ERR(eb->cancel_func(eb->cancel_baton));
509
SVN_ERR(get_file_from_ra(fb, ! eb->text_deltas, scratch_pool));
511
SVN_ERR(eb->processor->file_deleted(fb->path,
513
fb->path_start_revision,
522
/* Recursively walk tree rooted at DIR (at EB->revision) in the repository,
523
* reporting all children as deleted. Part of a workaround for issue 2333.
493
* DIR is a repository path relative to the URL in RA_SESSION. REVISION
494
* must be a valid revision number, not SVN_INVALID_REVNUM. EB is the
495
* overall crawler editor baton. If CANCEL_FUNC is not NULL, then it
496
* should refer to a cancellation function (along with CANCEL_BATON).
525
* DIR is a repository path relative to the URL in EB->ra_session. EB is
526
* the overall crawler editor baton. EB->revision must be a valid revision
527
* number, not SVN_INVALID_REVNUM. Use EB->cancel_func (if not null) with
528
* EB->cancel_baton for cancellation.
498
530
/* ### TODO: Handle depth. */
499
531
static svn_error_t *
500
diff_deleted_dir(const char *dir,
501
svn_revnum_t revision,
502
svn_ra_session_t *ra_session,
503
struct edit_baton *eb,
504
svn_cancel_func_t cancel_func,
532
diff_deleted_dir(const char *path,
533
struct dir_baton *pb,
534
apr_pool_t *scratch_pool)
509
apr_pool_t *iterpool = svn_pool_create(pool);
510
apr_hash_index_t *hi;
512
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
515
SVN_ERR(cancel_func(cancel_baton));
517
SVN_ERR(svn_ra_get_dir2(ra_session,
525
for (hi = apr_hash_first(pool, dirents); hi;
526
hi = apr_hash_next(hi))
536
struct edit_baton *eb = pb->edit_baton;
537
struct dir_baton *db;
538
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
539
svn_boolean_t skip = FALSE;
540
svn_boolean_t skip_children = FALSE;
541
apr_hash_t *dirents = NULL;
542
apr_hash_t *left_props = NULL;
543
svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
545
db = make_dir_baton(path, pb, pb->edit_baton, FALSE, SVN_INVALID_REVNUM,
548
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(eb->revision));
551
SVN_ERR(eb->cancel_func(eb->cancel_baton));
553
SVN_ERR(eb->processor->dir_opened(&db->pdb, &skip, &skip_children,
556
NULL /* right_source */,
557
NULL /* copyfrom_source */,
560
scratch_pool, iterpool));
562
if (!skip || !skip_children)
563
SVN_ERR(svn_ra_get_dir2(eb->ra_session,
564
skip_children ? NULL : &dirents,
566
skip ? NULL : &left_props,
572
/* The "old" dir will be skipped by the repository report. If required,
573
* crawl it recursively, diffing each file against the empty file. This
574
* is a workaround for issue 2333 "'svn diff URL1 URL2' not reverse of
575
* 'svn diff URL2 URL1'". */
529
const char *name = svn__apr_hash_index_key(hi);
530
svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
532
svn_pool_clear(iterpool);
534
path = svn_relpath_join(dir, name, iterpool);
536
if (dirent->kind == svn_node_file)
578
apr_hash_index_t *hi;
580
for (hi = apr_hash_first(scratch_pool, dirents); hi;
581
hi = apr_hash_next(hi))
538
struct file_baton *b;
539
const char *mimetype1, *mimetype2;
541
/* Compare a file being deleted against an empty file */
542
b = make_file_baton(path, FALSE, eb, iterpool);
543
SVN_ERR(get_file_from_ra(b, FALSE, iterpool));
545
SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
547
get_file_mime_types(&mimetype1, &mimetype2, b);
549
SVN_ERR(eb->diff_callbacks->file_deleted(
550
NULL, NULL, b->wcpath,
551
b->path_start_revision,
552
b->path_end_revision,
553
mimetype1, mimetype2,
555
b->edit_baton->diff_cmd_baton,
583
const char *child_path;
584
const char *name = svn__apr_hash_index_key(hi);
585
svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
587
svn_pool_clear(iterpool);
589
child_path = svn_relpath_join(path, name, iterpool);
591
if (dirent->kind == svn_node_file)
593
SVN_ERR(diff_deleted_file(child_path, db, iterpool));
595
else if (dirent->kind == svn_node_dir)
597
SVN_ERR(diff_deleted_dir(child_path, db, iterpool));
559
if (dirent->kind == svn_node_dir)
560
SVN_ERR(diff_deleted_dir(path,
604
SVN_ERR(eb->processor->dir_deleted(path,
612
SVN_ERR(release_dir(db));
569
614
svn_pool_destroy(iterpool);
570
615
return SVN_NO_ERROR;
573
/* An editor function. */
618
/* An svn_delta_editor_t function. */
574
619
static svn_error_t *
575
620
delete_entry(const char *path,
576
621
svn_revnum_t base_revision,
601
642
case svn_node_file:
603
const char *mimetype1, *mimetype2;
604
struct file_baton *b;
606
/* Compare a file being deleted against an empty file */
607
b = make_file_baton(path, FALSE, eb, scratch_pool);
608
SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool));
609
SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
611
get_file_mime_types(&mimetype1, &mimetype2, b);
613
SVN_ERR(eb->diff_callbacks->file_deleted(
614
&state, &tree_conflicted, b->wcpath,
615
b->path_start_revision,
616
b->path_end_revision,
617
mimetype1, mimetype2,
619
b->edit_baton->diff_cmd_baton,
644
SVN_ERR(diff_deleted_file(path, pb, scratch_pool));
624
647
case svn_node_dir:
626
SVN_ERR(eb->diff_callbacks->dir_deleted(
627
&state, &tree_conflicted,
628
svn_dirent_join(eb->target, path, pool),
629
eb->diff_cmd_baton, scratch_pool));
631
if (eb->walk_deleted_repos_dirs)
633
/* A workaround for issue 2333. The "old" dir will be
634
skipped by the repository report. Crawl it recursively,
635
diffing each file against the empty file. */
636
SVN_ERR(diff_deleted_dir(path,
649
SVN_ERR(diff_deleted_dir(path, pb, scratch_pool));
650
if ((state != svn_wc_notify_state_missing)
651
&& (state != svn_wc_notify_state_obstructed)
654
action = svn_wc_notify_update_delete;
659
const char* deleted_path;
660
deleted_path_notify_t *dpn = apr_pcalloc(eb->pool, sizeof(*dpn));
661
deleted_path = svn_dirent_join(eb->target, path, eb->pool);
663
dpn->action = tree_conflicted ? svn_wc_notify_tree_conflict : action;
665
dpn->tree_conflicted = tree_conflicted;
666
apr_hash_set(eb->deleted_paths, deleted_path, APR_HASH_KEY_STRING, dpn);
669
656
svn_pool_destroy(scratch_pool);
671
658
return SVN_NO_ERROR;
674
/* An editor function. */
661
/* An svn_delta_editor_t function. */
675
662
static svn_error_t *
676
663
add_directory(const char *path,
677
664
void *parent_baton,
683
670
struct dir_baton *pb = parent_baton;
684
671
struct edit_baton *eb = pb->edit_baton;
686
svn_wc_notify_state_t state;
672
struct dir_baton *db;
688
674
/* ### TODO: support copyfrom? */
690
b = make_dir_baton(path, pb, eb, TRUE, pool);
691
b->pristine_props = eb->empty_hash;
676
db = make_dir_baton(path, pb, eb, TRUE, SVN_INVALID_REVNUM, pb->pool);
694
679
/* Skip *everything* within a newly tree-conflicted directory,
695
680
* and directories the children of which should be skipped. */
696
if (pb->skip || pb->tree_conflicted || pb->skip_children)
681
if (pb->skip_children)
684
db->skip_children = TRUE;
699
685
return SVN_NO_ERROR;
703
SVN_ERR(eb->diff_callbacks->dir_added(
704
&state, &b->tree_conflicted,
705
&b->skip, &b->skip_children, b->wcpath,
706
eb->target_revision, copyfrom_path, copyfrom_revision,
707
eb->diff_cmd_baton, pool));
709
/* Notifications for directories are done at close_directory time.
710
* But for paths at which the editor drive adds directories, we make an
711
* exception to this rule, so that the path appears in the output before
712
* any children of the newly added directory. Since a deletion at this path
713
* must have happened before this addition, we can safely notify about
714
* replaced directories here, too. */
717
deleted_path_notify_t *dpn;
718
svn_wc_notify_t *notify;
719
svn_wc_notify_action_t action;
720
svn_node_kind_t kind = svn_node_dir;
722
/* Find out if a pending delete notification for this path is
724
dpn = apr_hash_get(eb->deleted_paths, b->wcpath, APR_HASH_KEY_STRING);
727
/* If any was found, we will handle the pending 'deleted path
728
* notification' (DPN) here. Remove it from the list. */
729
apr_hash_set(eb->deleted_paths, b->wcpath,
730
APR_HASH_KEY_STRING, NULL);
732
/* the pending delete might be on a different node kind. */
737
/* Determine what the notification (ACTION) should be.
738
* In case of a pending 'delete', this might become a 'replace'. */
739
if (b->tree_conflicted)
740
action = svn_wc_notify_tree_conflict;
743
if (dpn->action == svn_wc_notify_update_delete)
744
action = svn_wc_notify_update_replace;
746
/* Note: dpn->action might be svn_wc_notify_tree_conflict */
747
action = dpn->action;
749
else if (state == svn_wc_notify_state_missing ||
750
state == svn_wc_notify_state_obstructed)
751
action = svn_wc_notify_skip;
753
action = svn_wc_notify_update_add;
755
notify = svn_wc_create_notify(b->wcpath, action, pool);
757
notify->content_state = notify->prop_state = state;
758
(*eb->notify_func)(eb->notify_baton, notify, pool);
688
db->right_source = svn_diff__source_create(eb->target_revision,
691
SVN_ERR(eb->processor->dir_opened(&db->pdb,
697
NULL /* copyfrom_source */,
700
db->pool, db->pool));
761
702
return SVN_NO_ERROR;
764
/* An editor function. */
705
/* An svn_delta_editor_t function. */
765
706
static svn_error_t *
766
707
open_directory(const char *path,
767
708
void *parent_baton,
957
968
const char *expected_md5_digest,
958
969
apr_pool_t *pool)
960
struct file_baton *b = file_baton;
961
struct edit_baton *eb = b->edit_baton;
962
svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
963
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
971
struct file_baton *fb = file_baton;
972
struct dir_baton *pb = fb->parent_baton;
973
struct edit_baton *eb = fb->edit_baton;
964
974
apr_pool_t *scratch_pool;
966
976
/* Skip *everything* within a newly tree-conflicted directory. */
969
svn_pool_destroy(b->pool);
979
svn_pool_destroy(fb->pool);
980
SVN_ERR(release_dir(pb));
970
981
return SVN_NO_ERROR;
973
scratch_pool = b->pool;
984
scratch_pool = fb->pool;
975
if (expected_md5_digest)
986
if (expected_md5_digest && eb->text_deltas)
977
988
svn_checksum_t *expected_md5_checksum;
979
990
SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
980
991
expected_md5_digest, scratch_pool));
982
if (!svn_checksum_match(expected_md5_checksum, b->result_md5_checksum))
993
if (!svn_checksum_match(expected_md5_checksum, fb->result_md5_checksum))
983
994
return svn_error_trace(svn_checksum_mismatch_err(
984
995
expected_md5_checksum,
985
b->result_md5_checksum,
996
fb->result_md5_checksum,
987
998
_("Checksum mismatch for '%s'"),
991
if (!b->added && b->propchanges->nelts > 0)
1002
if (fb->added || fb->path_end_revision || fb->has_propchange)
993
if (!b->pristine_props)
1004
apr_hash_t *right_props;
1006
if (!fb->added && !fb->pristine_props)
995
1008
/* We didn't receive a text change, so we have no pristine props.
996
1009
Retrieve just the props now. */
997
SVN_ERR(get_file_from_ra(b, TRUE, scratch_pool));
1000
remove_non_prop_changes(b->pristine_props, b->propchanges);
1003
if (b->path_end_revision || b->propchanges->nelts > 0)
1005
const char *mimetype1, *mimetype2;
1006
get_file_mime_types(&mimetype1, &mimetype2, b);
1010
SVN_ERR(eb->diff_callbacks->file_added(
1011
&content_state, &prop_state, &b->tree_conflicted,
1013
b->path_end_revision ? b->path_start_revision : NULL,
1014
b->path_end_revision,
1016
b->edit_baton->target_revision,
1017
mimetype1, mimetype2,
1018
NULL, SVN_INVALID_REVNUM,
1019
b->propchanges, b->pristine_props,
1020
b->edit_baton->diff_cmd_baton,
1023
SVN_ERR(eb->diff_callbacks->file_changed(
1024
&content_state, &prop_state,
1025
&b->tree_conflicted, b->wcpath,
1026
b->path_end_revision ? b->path_start_revision : NULL,
1027
b->path_end_revision,
1028
b->edit_baton->revision,
1029
b->edit_baton->target_revision,
1030
mimetype1, mimetype2,
1031
b->propchanges, b->pristine_props,
1032
b->edit_baton->diff_cmd_baton,
1037
if (eb->notify_func)
1039
deleted_path_notify_t *dpn;
1040
svn_wc_notify_t *notify;
1041
svn_wc_notify_action_t action;
1042
svn_node_kind_t kind = svn_node_file;
1044
/* Find out if a pending delete notification for this path is
1046
dpn = apr_hash_get(eb->deleted_paths, b->wcpath, APR_HASH_KEY_STRING);
1049
/* If any was found, we will handle the pending 'deleted path
1050
* notification' (DPN) here. Remove it from the list. */
1051
apr_hash_set(eb->deleted_paths, b->wcpath,
1052
APR_HASH_KEY_STRING, NULL);
1054
/* the pending delete might be on a different node kind. */
1056
content_state = prop_state = dpn->state;
1059
/* Determine what the notification (ACTION) should be.
1060
* In case of a pending 'delete', this might become a 'replace'. */
1061
if (b->tree_conflicted)
1062
action = svn_wc_notify_tree_conflict;
1065
if (dpn->action == svn_wc_notify_update_delete
1067
action = svn_wc_notify_update_replace;
1069
/* Note: dpn->action might be svn_wc_notify_tree_conflict */
1070
action = dpn->action;
1072
else if ((content_state == svn_wc_notify_state_missing)
1073
|| (content_state == svn_wc_notify_state_obstructed))
1074
action = svn_wc_notify_skip;
1076
action = svn_wc_notify_update_add;
1078
action = svn_wc_notify_update_update;
1080
notify = svn_wc_create_notify(b->wcpath, action, scratch_pool);
1081
notify->kind = kind;
1082
notify->content_state = content_state;
1083
notify->prop_state = prop_state;
1084
(*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
1087
svn_pool_destroy(b->pool); /* Destroy file and scratch pool */
1010
SVN_ERR(get_file_from_ra(fb, TRUE, scratch_pool));
1013
if (fb->pristine_props)
1014
remove_non_prop_changes(fb->pristine_props, fb->propchanges);
1016
right_props = svn_prop__patch(fb->pristine_props, fb->propchanges,
1020
SVN_ERR(eb->processor->file_added(fb->path,
1021
NULL /* copyfrom_src */,
1023
NULL /* copyfrom_file */,
1024
fb->path_end_revision,
1025
NULL /* copyfrom_props */,
1031
SVN_ERR(eb->processor->file_changed(fb->path,
1034
fb->path_end_revision
1035
? fb->path_start_revision
1037
fb->path_end_revision,
1040
(fb->path_end_revision != NULL),
1047
svn_pool_destroy(fb->pool); /* Destroy file and scratch pool */
1049
SVN_ERR(release_dir(pb));
1089
1051
return SVN_NO_ERROR;
1092
/* An editor function. */
1054
/* Report any accumulated prop changes via the 'dir_props_changed' callback,
1055
* and then call the 'dir_closed' callback. Notify about any deleted paths
1056
* within this directory that have not already been notified, and then about
1057
* this directory itself (unless it was added, in which case the notification
1058
* was done at that time).
1060
* An svn_delta_editor_t function. */
1093
1061
static svn_error_t *
1094
1062
close_directory(void *dir_baton,
1095
1063
apr_pool_t *pool)
1097
struct dir_baton *b = dir_baton;
1098
struct edit_baton *eb = b->edit_baton;
1099
svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
1100
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
1101
svn_boolean_t skipped = FALSE;
1065
struct dir_baton *db = dir_baton;
1066
struct edit_baton *eb = db->edit_baton;
1102
1067
apr_pool_t *scratch_pool;
1104
/* Skip *everything* within a newly tree-conflicted directory. */
1107
svn_pool_destroy(b->pool);
1108
return SVN_NO_ERROR;
1111
scratch_pool = b->pool;
1113
if (!b->added && b->propchanges->nelts > 0)
1114
remove_non_prop_changes(b->pristine_props, b->propchanges);
1116
/* Don't do the props_changed stuff if this is a dry_run and we don't
1117
have an access baton, since in that case the directory will already
1118
have been recognised as added, in which case they cannot conflict. */
1119
if (b->propchanges->nelts > 0)
1121
svn_boolean_t tree_conflicted = FALSE;
1122
SVN_ERR(eb->diff_callbacks->dir_props_changed(
1123
&prop_state, &tree_conflicted,
1124
b->wcpath, b->added,
1125
b->propchanges, b->pristine_props,
1126
b->edit_baton->diff_cmd_baton, scratch_pool));
1127
if (tree_conflicted)
1128
b->tree_conflicted = TRUE;
1130
if (prop_state == svn_wc_notify_state_obstructed
1131
|| prop_state == svn_wc_notify_state_missing)
1133
content_state = prop_state;
1138
SVN_ERR(eb->diff_callbacks->dir_closed(NULL, NULL, NULL,
1139
b->wcpath, b->added,
1140
b->edit_baton->diff_cmd_baton,
1143
/* Don't notify added directories as they triggered notification
1144
in add_directory. */
1145
if (!skipped && !b->added && eb->notify_func)
1147
apr_hash_index_t *hi;
1149
for (hi = apr_hash_first(pool, eb->deleted_paths); hi;
1150
hi = apr_hash_next(hi))
1152
svn_wc_notify_t *notify;
1153
const char *deleted_path = svn__apr_hash_index_key(hi);
1154
deleted_path_notify_t *dpn = svn__apr_hash_index_val(hi);
1156
notify = svn_wc_create_notify(deleted_path, dpn->action, pool);
1157
notify->kind = dpn->kind;
1158
notify->content_state = notify->prop_state = dpn->state;
1159
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
1160
(*eb->notify_func)(eb->notify_baton, notify, pool);
1161
apr_hash_set(eb->deleted_paths, deleted_path,
1162
APR_HASH_KEY_STRING, NULL);
1166
if (!b->added && eb->notify_func)
1168
svn_wc_notify_t *notify;
1169
svn_wc_notify_action_t action;
1171
if (b->tree_conflicted)
1172
action = svn_wc_notify_tree_conflict;
1174
action = svn_wc_notify_skip;
1068
apr_hash_t *pristine_props;
1069
svn_boolean_t send_changed = FALSE;
1071
scratch_pool = db->pool;
1073
if ((db->has_propchange || db->added) && !db->skip)
1077
pristine_props = eb->empty_hash;
1176
action = svn_wc_notify_update_update;
1178
notify = svn_wc_create_notify(b->wcpath, action, pool);
1179
notify->kind = svn_node_dir;
1181
/* In case of a tree conflict during merge, the diff callback
1182
* sets content_state appropriately. So copy the state into the
1183
* notify_t to make sure conflicts get displayed. */
1184
notify->content_state = content_state;
1186
notify->prop_state = prop_state;
1187
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
1188
(*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
1191
svn_pool_destroy(b->pool); /* Destroy baton and scratch_pool */
1081
SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, &pristine_props,
1082
db->path, db->base_revision, 0, scratch_pool));
1085
if (db->propchanges->nelts > 0)
1087
remove_non_prop_changes(pristine_props, db->propchanges);
1090
if (db->propchanges->nelts > 0 || db->added)
1092
apr_hash_t *right_props;
1094
right_props = svn_prop__patch(pristine_props, db->propchanges,
1099
SVN_ERR(eb->processor->dir_added(db->path,
1100
NULL /* copyfrom */,
1102
NULL /* copyfrom props */,
1110
SVN_ERR(eb->processor->dir_changed(db->path,
1121
send_changed = TRUE; /* Skip dir_closed */
1125
if (! db->skip && !send_changed)
1127
SVN_ERR(eb->processor->dir_closed(db->path,
1134
SVN_ERR(release_dir(db));
1193
1136
return SVN_NO_ERROR;
1197
/* An editor function. */
1140
/* Record a prop change, which we will report later in close_file().
1142
* An svn_delta_editor_t function. */
1198
1143
static svn_error_t *
1199
1144
change_file_prop(void *file_baton,
1200
1145
const char *name,
1201
1146
const svn_string_t *value,
1202
1147
apr_pool_t *pool)
1204
struct file_baton *b = file_baton;
1149
struct file_baton *fb = file_baton;
1205
1150
svn_prop_t *propchange;
1151
svn_prop_kind_t propkind;
1207
1153
/* Skip *everything* within a newly tree-conflicted directory. */
1209
return SVN_NO_ERROR;
1211
propchange = apr_array_push(b->propchanges);
1212
propchange->name = apr_pstrdup(b->pool, name);
1213
propchange->value = value ? svn_string_dup(value, b->pool) : NULL;
1155
return SVN_NO_ERROR;
1157
propkind = svn_property_kind2(name);
1158
if (propkind == svn_prop_wc_kind)
1159
return SVN_NO_ERROR;
1160
else if (propkind == svn_prop_regular_kind)
1161
fb->has_propchange = TRUE;
1163
propchange = apr_array_push(fb->propchanges);
1164
propchange->name = apr_pstrdup(fb->pool, name);
1165
propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
1215
1167
return SVN_NO_ERROR;
1218
/* An editor function. */
1170
/* Make a note of this prop change, to be reported when the dir is closed.
1172
* An svn_delta_editor_t function. */
1219
1173
static svn_error_t *
1220
1174
change_dir_prop(void *dir_baton,
1221
1175
const char *name,
1287
1236
struct dir_baton *pb = parent_baton;
1288
1237
struct edit_baton *eb = pb->edit_baton;
1290
/* ### TODO: Raise a tree-conflict?? I sure hope not.*/
1292
if (eb->notify_func)
1294
svn_wc_notify_t *notify
1295
= svn_wc_create_notify(svn_dirent_join(pb->wcpath,
1296
svn_relpath_basename(path,
1299
svn_wc_notify_skip, pool);
1300
notify->kind = svn_node_file;
1301
notify->content_state = notify->prop_state
1302
= svn_wc_notify_state_missing;
1303
(*eb->notify_func)(eb->notify_baton, notify, pool);
1239
SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
1241
return SVN_NO_ERROR;
1244
static svn_error_t *
1245
fetch_kind_func(svn_node_kind_t *kind,
1248
svn_revnum_t base_revision,
1249
apr_pool_t *scratch_pool)
1251
struct edit_baton *eb = baton;
1253
if (!SVN_IS_VALID_REVNUM(base_revision))
1254
base_revision = eb->revision;
1256
SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, kind,
1259
return SVN_NO_ERROR;
1262
static svn_error_t *
1263
fetch_props_func(apr_hash_t **props,
1266
svn_revnum_t base_revision,
1267
apr_pool_t *result_pool,
1268
apr_pool_t *scratch_pool)
1270
struct edit_baton *eb = baton;
1271
svn_node_kind_t node_kind;
1273
if (!SVN_IS_VALID_REVNUM(base_revision))
1274
base_revision = eb->revision;
1276
SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
1279
if (node_kind == svn_node_file)
1281
SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision,
1282
NULL, NULL, props, result_pool));
1284
else if (node_kind == svn_node_dir)
1286
apr_array_header_t *tmp_props;
1288
SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
1289
base_revision, 0 /* Dirent fields */,
1291
tmp_props = svn_prop_hash_to_array(*props, result_pool);
1292
SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
1294
*props = svn_prop_array_to_hash(tmp_props, result_pool);
1298
*props = apr_hash_make(result_pool);
1301
return SVN_NO_ERROR;
1304
static svn_error_t *
1305
fetch_base_func(const char **filename,
1308
svn_revnum_t base_revision,
1309
apr_pool_t *result_pool,
1310
apr_pool_t *scratch_pool)
1312
struct edit_baton *eb = baton;
1313
svn_stream_t *fstream;
1316
if (!SVN_IS_VALID_REVNUM(base_revision))
1317
base_revision = eb->revision;
1319
SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
1320
svn_io_file_del_on_pool_cleanup,
1321
result_pool, scratch_pool));
1323
err = svn_ra_get_file(eb->ra_session, path, base_revision,
1324
fstream, NULL, NULL, scratch_pool);
1325
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
1327
svn_error_clear(err);
1328
SVN_ERR(svn_stream_close(fstream));
1331
return SVN_NO_ERROR;
1334
return svn_error_trace(err);
1336
SVN_ERR(svn_stream_close(fstream));
1306
1338
return SVN_NO_ERROR;
1309
1341
/* Create a repository diff editor and baton. */
1311
svn_client__get_diff_editor(const svn_delta_editor_t **editor,
1313
svn_wc_context_t *wc_ctx,
1316
svn_ra_session_t *ra_session,
1317
svn_revnum_t revision,
1318
svn_boolean_t walk_deleted_dirs,
1319
svn_boolean_t dry_run,
1320
const svn_wc_diff_callbacks4_t *diff_callbacks,
1321
void *diff_cmd_baton,
1322
svn_cancel_func_t cancel_func,
1324
svn_wc_notify_func2_t notify_func,
1326
apr_pool_t *result_pool,
1327
apr_pool_t *scratch_pool)
1343
svn_client__get_diff_editor2(const svn_delta_editor_t **editor,
1345
svn_ra_session_t *ra_session,
1347
svn_revnum_t revision,
1348
svn_boolean_t text_deltas,
1349
const svn_diff_tree_processor_t *processor,
1350
svn_cancel_func_t cancel_func,
1352
apr_pool_t *result_pool)
1329
1354
apr_pool_t *editor_pool = svn_pool_create(result_pool);
1330
1355
svn_delta_editor_t *tree_editor = svn_delta_default_editor(editor_pool);
1331
1356
struct edit_baton *eb = apr_pcalloc(editor_pool, sizeof(*eb));
1332
const char *target_abspath;
1334
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, editor_pool));
1357
svn_delta_shim_callbacks_t *shim_callbacks =
1358
svn_delta_shim_callbacks_default(editor_pool);
1336
1360
eb->pool = editor_pool;
1337
eb->target = target;
1338
eb->wc_ctx = wc_ctx;
1339
1361
eb->depth = depth;
1340
eb->diff_callbacks = diff_callbacks;
1341
eb->diff_cmd_baton = diff_cmd_baton;
1342
eb->dry_run = dry_run;
1363
eb->processor = processor;
1343
1365
eb->ra_session = ra_session;
1345
1367
eb->revision = revision;
1346
1368
eb->empty_file = NULL;
1347
1369
eb->empty_hash = apr_hash_make(eb->pool);
1348
eb->deleted_paths = apr_hash_make(eb->pool);
1349
eb->notify_func = notify_func;
1350
eb->notify_baton = notify_baton;
1351
eb->walk_deleted_repos_dirs = walk_deleted_dirs;
1370
eb->text_deltas = text_deltas;
1352
1371
eb->cancel_func = cancel_func;
1353
1372
eb->cancel_baton = cancel_baton;