2 * diff_editor.c -- The diff editor for comparing the working copy against the
5 * ====================================================================
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance
12 * with the License. You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
22 * ====================================================================
26 * This code uses an svn_delta_editor_t editor driven by
27 * svn_wc_crawl_revisions (like the update command) to retrieve the
28 * differences between the working copy and the requested repository
29 * version. Rather than updating the working copy, this new editor creates
30 * temporary files that contain the pristine repository versions. When the
31 * crawler closes the files the editor calls back to a client layer
32 * function to compare the working copy and the temporary file. There is
33 * only ever one temporary file in existence at any time.
35 * When the crawler closes a directory, the editor then calls back to the
36 * client layer to compare any remaining files that may have been modified
37 * locally. Added directories do not have corresponding temporary
38 * directories created, as they are not needed.
40 * The diff result from this editor is a combination of the restructuring
41 * operations from the repository with the local restructurings since checking
44 * ### TODO: Make sure that we properly support and report multi layered
45 * operations instead of only simple file replacements.
47 * ### TODO: Replacements where the node kind changes needs support. It
48 * mostly works when the change is in the repository, but not when it is
49 * in the working copy.
51 * ### TODO: Do we need to support copyfrom?
60 #include "svn_error.h"
61 #include "svn_pools.h"
62 #include "svn_dirent_uri.h"
65 #include "svn_sorts.h"
67 #include "private/svn_subr_private.h"
68 #include "private/svn_wc_private.h"
69 #include "private/svn_diff_tree.h"
70 #include "private/svn_editor.h"
74 #include "adm_files.h"
75 #include "translate.h"
78 #include "svn_private_config.h"
80 /*-------------------------------------------------------------------------*/
83 /* Overall crawler editor baton.
90 /* A diff tree processor, receiving the result of the diff. */
91 const svn_diff_tree_processor_t *processor;
93 /* A boolean indicating whether local additions should be reported before
94 remote deletes. The processor can transform adds in deletes and deletes
95 in adds, but it can't reorder the output. */
96 svn_boolean_t local_before_remote;
98 /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */
100 const char *anchor_abspath;
102 /* Target revision */
105 /* Was the root opened? */
106 svn_boolean_t root_opened;
108 /* How does this diff descend as seen from target? */
111 /* Should this diff ignore node ancestry? */
112 svn_boolean_t ignore_ancestry;
114 /* Possibly diff repos against text-bases instead of working files. */
115 svn_boolean_t diff_pristine;
117 /* Hash whose keys are const char * changelist names. */
118 apr_hash_t *changelist_hash;
120 /* Cancel function/baton */
121 svn_cancel_func_t cancel_func;
127 /* Directory level baton.
131 /* Reference to parent directory baton (or NULL for the root) */
132 struct dir_baton_t *parent_baton;
134 /* The depth at which this directory should be diffed. */
137 /* The name and path of this directory as if they would be/are in the
138 local working copy. */
141 const char *local_abspath;
143 /* TRUE if the file is added by the editor drive. */
145 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
146 svn_boolean_t repos_only;
147 /* TRUE if the node is to be compared with an unrelated node*/
148 svn_boolean_t ignoring_ancestry;
150 /* Processor state */
153 svn_boolean_t skip_children;
155 svn_diff_source_t *left_src;
156 svn_diff_source_t *right_src;
158 apr_hash_t *local_info;
160 /* A hash containing the basenames of the nodes reported deleted by the
161 repository (or NULL for no values). */
164 /* Identifies those directory elements that get compared while running
165 the crawler. These elements should not be compared again when
166 recursively looking for local modifications.
168 This hash maps the basename of the node to an unimportant value.
170 If the directory's properties have been compared, an item with hash
171 key of "" will be present in the hash. */
172 apr_hash_t *compared;
174 /* The list of incoming BASE->repos propchanges. */
175 apr_array_header_t *propchanges;
177 /* Has a change on regular properties */
178 svn_boolean_t has_propchange;
180 /* The overall crawler editor baton. */
181 struct edit_baton_t *eb;
191 struct dir_baton_t *parent_baton;
193 /* The name and path of this file as if they would be/are in the
194 parent directory, diff session and local working copy. */
197 const char *local_abspath;
199 /* Processor state */
203 /* TRUE if the file is added by the editor drive. */
205 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
206 svn_boolean_t repos_only;
207 /* TRUE if the node is to be compared with an unrelated node*/
208 svn_boolean_t ignoring_ancestry;
210 const svn_diff_source_t *left_src;
211 const svn_diff_source_t *right_src;
213 /* The list of incoming BASE->repos propchanges. */
214 apr_array_header_t *propchanges;
216 /* Has a change on regular properties */
217 svn_boolean_t has_propchange;
219 /* The current BASE checksum and props */
220 const svn_checksum_t *base_checksum;
221 apr_hash_t *base_props;
223 /* The resulting from apply_textdelta */
224 const char *temp_file_path;
225 unsigned char result_digest[APR_MD5_DIGESTSIZE];
227 /* The overall crawler editor baton. */
228 struct edit_baton_t *eb;
233 /* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths
234 * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON
235 * define the callbacks to compare files. DEPTH defines if and how to
236 * descend into subdirectories; see public doc string for exactly how.
237 * IGNORE_ANCESTRY defines whether to utilize node ancestry when
238 * calculating diffs. USE_TEXT_BASE defines whether to compare
239 * against working files or text-bases. REVERSE_ORDER defines which
240 * direction to perform the diff.
242 * CHANGELIST_FILTER is a list of const char * changelist names, used to
243 * filter diff output responses to only those items in one of the
244 * specified changelists, empty (or NULL altogether) if no changelist
245 * filtering is requested.
248 make_edit_baton(struct edit_baton_t **edit_baton,
250 const char *anchor_abspath,
252 const svn_wc_diff_callbacks4_t *callbacks,
253 void *callback_baton,
255 svn_boolean_t ignore_ancestry,
256 svn_boolean_t show_copies_as_adds,
257 svn_boolean_t use_text_base,
258 svn_boolean_t reverse_order,
259 const apr_array_header_t *changelist_filter,
260 svn_cancel_func_t cancel_func,
264 apr_hash_t *changelist_hash = NULL;
265 struct edit_baton_t *eb;
266 const svn_diff_tree_processor_t *processor;
268 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
270 if (changelist_filter && changelist_filter->nelts)
271 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
274 SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
275 callbacks, callback_baton, TRUE,
279 processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
281 /* --show-copies-as-adds implies --notice-ancestry */
282 if (show_copies_as_adds)
283 ignore_ancestry = FALSE;
285 if (! show_copies_as_adds)
286 processor = svn_diff__tree_processor_copy_as_changed_create(processor,
289 eb = apr_pcalloc(pool, sizeof(*eb));
291 eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
292 eb->target = apr_pstrdup(pool, target);
293 eb->processor = processor;
295 eb->ignore_ancestry = ignore_ancestry;
296 eb->local_before_remote = reverse_order;
297 eb->diff_pristine = use_text_base;
298 eb->changelist_hash = changelist_hash;
299 eb->cancel_func = cancel_func;
300 eb->cancel_baton = cancel_baton;
307 /* Create a new directory baton. PATH is the directory path,
308 * including anchor_path. ADDED is set if this directory is being
309 * added rather than replaced. PARENT_BATON is the baton of the
310 * parent directory, it will be null if this is the root of the
311 * comparison hierarchy. The directory and its parent may or may not
312 * exist in the working copy. EDIT_BATON is the overall crawler
315 static struct dir_baton_t *
316 make_dir_baton(const char *path,
317 struct dir_baton_t *parent_baton,
318 struct edit_baton_t *eb,
321 apr_pool_t *result_pool)
323 apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
325 struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
327 db->parent_baton = parent_baton;
329 /* Allocate 1 string for using as 3 strings */
330 db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
331 db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
332 db->name = svn_dirent_basename(db->relpath, NULL);
338 db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
339 db->compared = apr_hash_make(dir_pool);
341 if (parent_baton != NULL)
343 parent_baton->users++;
351 /* Create a new file baton. PATH is the file path, including
352 * anchor_path. ADDED is set if this file is being added rather than
353 * replaced. PARENT_BATON is the baton of the parent directory.
354 * The directory and its parent may or may not exist in the working copy.
356 static struct file_baton_t *
357 make_file_baton(const char *path,
359 struct dir_baton_t *parent_baton,
360 apr_pool_t *result_pool)
362 apr_pool_t *file_pool = svn_pool_create(result_pool);
363 struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
364 struct edit_baton_t *eb = parent_baton->eb;
367 fb->parent_baton = parent_baton;
368 fb->parent_baton->users++;
370 /* Allocate 1 string for using as 3 strings */
371 fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
372 fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
373 fb->name = svn_dirent_basename(fb->relpath, NULL);
376 fb->pool = file_pool;
377 fb->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
382 /* Destroy DB when there are no more registered users */
384 maybe_done(struct dir_baton_t *db)
390 struct dir_baton_t *pb = db->parent_baton;
392 svn_pool_clear(db->pool);
395 SVN_ERR(maybe_done(pb));
401 /* Standard check to see if a node is represented in the local working copy */
402 #define NOT_PRESENT(status) \
403 ((status) == svn_wc__db_status_not_present \
404 || (status) == svn_wc__db_status_excluded \
405 || (status) == svn_wc__db_status_server_excluded)
408 svn_wc__diff_base_working_diff(svn_wc__db_t *db,
409 const char *local_abspath,
411 svn_revnum_t revision,
412 apr_hash_t *changelist_hash,
413 const svn_diff_tree_processor_t *processor,
414 void *processor_dir_baton,
415 svn_boolean_t diff_pristine,
416 svn_cancel_func_t cancel_func,
418 apr_pool_t *scratch_pool)
420 void *file_baton = NULL;
421 svn_boolean_t skip = FALSE;
422 svn_wc__db_status_t status;
423 svn_revnum_t db_revision;
424 svn_boolean_t had_props;
425 svn_boolean_t props_mod;
426 svn_boolean_t files_same = FALSE;
427 svn_wc__db_status_t base_status;
428 const svn_checksum_t *working_checksum;
429 const svn_checksum_t *checksum;
430 svn_filesize_t recorded_size;
431 apr_time_t recorded_time;
432 const char *pristine_file;
433 const char *local_file;
434 svn_diff_source_t *left_src;
435 svn_diff_source_t *right_src;
436 apr_hash_t *base_props;
437 apr_hash_t *local_props;
438 apr_array_header_t *prop_changes;
439 const char *changelist;
441 SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
442 NULL, NULL, NULL, NULL, &working_checksum, NULL,
443 NULL, NULL, NULL, NULL, NULL, &recorded_size,
444 &recorded_time, &changelist, NULL, NULL,
445 &had_props, &props_mod, NULL, NULL, NULL,
446 db, local_abspath, scratch_pool, scratch_pool));
447 checksum = working_checksum;
449 assert(status == svn_wc__db_status_normal
450 || status == svn_wc__db_status_added
451 || (status == svn_wc__db_status_deleted && diff_pristine));
453 /* If the item is not a member of a specified changelist (and there are
454 some specified changelists), skip it. */
455 if (changelist_hash && !svn_hash_gets(changelist_hash, changelist))
459 if (status != svn_wc__db_status_normal)
461 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
462 NULL, NULL, NULL, NULL, NULL, NULL,
463 NULL, &checksum, NULL, NULL, &had_props,
466 scratch_pool, scratch_pool));
467 recorded_size = SVN_INVALID_FILESIZE;
469 props_mod = TRUE; /* Requires compare */
471 else if (diff_pristine)
475 const svn_io_dirent2_t *dirent;
477 /* Verify truename to mimic status for iota/IOTA difference on Windows */
478 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
479 TRUE /* verify truename */,
480 TRUE /* ingore_enoent */,
481 scratch_pool, scratch_pool));
483 /* If a file does not exist on disk (missing/obstructed) then we
484 can't provide a text diff */
485 if (dirent->kind != svn_node_file
486 || (dirent->kind == svn_node_file
487 && dirent->filesize == recorded_size
488 && dirent->mtime == recorded_time))
494 if (files_same && !props_mod)
495 return SVN_NO_ERROR; /* Cheap exit */
499 if (!SVN_IS_VALID_REVNUM(revision))
500 revision = db_revision;
502 left_src = svn_diff__source_create(revision, scratch_pool);
503 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
505 SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
508 NULL /* copyfrom_src */,
511 scratch_pool, scratch_pool));
516 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
517 db, local_abspath, checksum,
518 scratch_pool, scratch_pool));
521 SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
524 scratch_pool, scratch_pool));
525 else if (! (had_props || props_mod))
526 local_file = local_abspath;
528 local_file = pristine_file;
530 SVN_ERR(svn_wc__internal_translated_file(
531 &local_file, local_abspath,
533 SVN_WC_TRANSLATE_TO_NF
534 | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
535 cancel_func, cancel_baton,
536 scratch_pool, scratch_pool));
539 SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
540 pristine_file, scratch_pool));
543 SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
544 scratch_pool, scratch_pool));
546 base_props = apr_hash_make(scratch_pool);
548 if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
549 local_props = base_props;
550 else if (diff_pristine)
551 SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
552 scratch_pool, scratch_pool));
554 SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
555 scratch_pool, scratch_pool));
557 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
559 if (prop_changes->nelts || !files_same)
561 SVN_ERR(processor->file_changed(relpath,
576 SVN_ERR(processor->file_closed(relpath,
588 ensure_local_info(struct dir_baton_t *db,
589 apr_pool_t *scratch_pool)
591 apr_hash_t *conflicts;
596 SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
597 db->eb->db, db->local_abspath,
598 db->pool, scratch_pool));
603 /* Called when the directory is closed to compare any elements that have
604 * not yet been compared. This identifies local, working copy only
605 * changes. At this stage we are dealing with files/directories that do
606 * exist in the working copy.
608 * DIR_BATON is the baton for the directory.
611 walk_local_nodes_diff(struct edit_baton_t *eb,
612 const char *local_abspath,
615 apr_hash_t *compared,
617 apr_pool_t *scratch_pool)
619 svn_wc__db_t *db = eb->db;
620 svn_boolean_t in_anchor_not_target;
621 apr_pool_t *iterpool;
622 void *dir_baton = NULL;
623 svn_boolean_t skip = FALSE;
624 svn_boolean_t skip_children = FALSE;
625 svn_revnum_t revision;
626 svn_boolean_t props_mod;
627 svn_diff_source_t *left_src;
628 svn_diff_source_t *right_src;
630 /* Everything we do below is useless if we are comparing to BASE. */
631 if (eb->diff_pristine)
634 /* Determine if this is the anchor directory if the anchor is different
635 to the target. When the target is a file, the anchor is the parent
636 directory and if this is that directory the non-target entries must be
638 in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
640 iterpool = svn_pool_create(scratch_pool);
642 SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
643 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
644 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
645 NULL, &props_mod, NULL, NULL, NULL,
646 db, local_abspath, scratch_pool, scratch_pool));
648 left_src = svn_diff__source_create(revision, scratch_pool);
649 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
653 dir_baton = parent_baton;
656 else if (!in_anchor_not_target)
657 SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
661 NULL /* copyfrom_src */,
664 scratch_pool, scratch_pool));
667 if (!skip_children && depth != svn_depth_empty)
670 apr_hash_t *conflicts;
671 apr_array_header_t *children;
672 svn_depth_t depth_below_here = depth;
673 svn_boolean_t diff_files;
674 svn_boolean_t diff_dirs;
677 if (depth_below_here == svn_depth_immediates)
678 depth_below_here = svn_depth_empty;
680 diff_files = (depth == svn_depth_unknown
681 || depth >= svn_depth_files);
682 diff_dirs = (depth == svn_depth_unknown
683 || depth >= svn_depth_immediates);
685 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
687 scratch_pool, iterpool));
689 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
692 for (i = 0; i < children->nelts; i++)
694 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
696 const char *name = item->key;
697 struct svn_wc__db_info_t *info = item->value;
699 const char *child_abspath;
700 const char *child_relpath;
701 svn_boolean_t repos_only;
702 svn_boolean_t local_only;
703 svn_node_kind_t base_kind;
706 SVN_ERR(eb->cancel_func(eb->cancel_baton));
708 /* In the anchor directory, if the anchor is not the target then all
709 entries other than the target should not be diff'd. Running diff
710 on one file in a directory should not diff other files in that
712 if (in_anchor_not_target && strcmp(eb->target, name))
715 if (compared && svn_hash_gets(compared, name))
718 if (NOT_PRESENT(info->status))
721 assert(info->status == svn_wc__db_status_normal
722 || info->status == svn_wc__db_status_added
723 || info->status == svn_wc__db_status_deleted);
725 svn_pool_clear(iterpool);
726 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
727 child_relpath = svn_relpath_join(path, name, iterpool);
732 if (!info->have_base)
734 local_only = TRUE; /* Only report additions */
736 else if (info->status == svn_wc__db_status_normal)
739 base_kind = info->kind;
741 else if (info->status == svn_wc__db_status_deleted
742 && (!eb->diff_pristine || !info->have_more_work))
744 svn_wc__db_status_t base_status;
746 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
747 NULL, NULL, NULL, NULL, NULL,
748 NULL, NULL, NULL, NULL, NULL,
751 iterpool, iterpool));
753 if (NOT_PRESENT(base_status))
758 /* working status is either added or deleted */
759 svn_wc__db_status_t base_status;
761 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
762 NULL, NULL, NULL, NULL, NULL,
763 NULL, NULL, NULL, NULL, NULL,
766 iterpool, iterpool));
768 if (NOT_PRESENT(base_status))
770 else if (base_kind != info->kind || !eb->ignore_ancestry)
777 if (eb->local_before_remote && local_only)
779 if (info->kind == svn_node_file && diff_files)
780 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
782 eb->processor, dir_baton,
788 else if (info->kind == svn_node_dir && diff_dirs)
789 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
792 eb->processor, dir_baton,
802 /* Report repository form deleted */
803 if (base_kind == svn_node_file && diff_files)
804 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
805 child_relpath, eb->revnum,
806 eb->processor, dir_baton,
808 else if (base_kind == svn_node_dir && diff_dirs)
809 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
810 child_relpath, eb->revnum,
812 eb->processor, dir_baton,
817 else if (!local_only) /* Not local only nor remote only */
819 /* Diff base against actual */
820 if (info->kind == svn_node_file && diff_files)
822 if (info->status != svn_wc__db_status_normal
823 || !eb->diff_pristine)
825 SVN_ERR(svn_wc__diff_base_working_diff(
830 eb->processor, dir_baton,
837 else if (info->kind == svn_node_dir && diff_dirs)
838 SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
846 if (!eb->local_before_remote && local_only)
848 if (info->kind == svn_node_file && diff_files)
849 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
851 eb->processor, dir_baton,
857 else if (info->kind == svn_node_dir && diff_dirs)
858 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
859 child_relpath, depth_below_here,
860 eb->processor, dir_baton,
873 /* Check for local property mods on this directory, if we haven't
874 already reported them and we aren't changelist-filted.
875 ### it should be noted that we do not currently allow directories
876 ### to be part of changelists, so if a changelist is provided, the
877 ### changelist check will always fail. */
879 && ! eb->changelist_hash
880 && ! in_anchor_not_target
883 apr_array_header_t *propchanges;
884 apr_hash_t *left_props;
885 apr_hash_t *right_props;
887 SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
889 scratch_pool, scratch_pool));
891 right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
893 SVN_ERR(eb->processor->dir_changed(path,
904 SVN_ERR(eb->processor->dir_closed(path,
911 svn_pool_destroy(iterpool);
917 svn_wc__diff_local_only_file(svn_wc__db_t *db,
918 const char *local_abspath,
920 const svn_diff_tree_processor_t *processor,
921 void *processor_parent_baton,
922 apr_hash_t *changelist_hash,
923 svn_boolean_t diff_pristine,
924 svn_cancel_func_t cancel_func,
926 apr_pool_t *scratch_pool)
928 svn_diff_source_t *right_src;
929 svn_diff_source_t *copyfrom_src = NULL;
930 svn_wc__db_status_t status;
931 svn_node_kind_t kind;
932 const svn_checksum_t *checksum;
933 const char *original_repos_relpath;
934 svn_revnum_t original_revision;
935 const char *changelist;
936 svn_boolean_t had_props;
937 svn_boolean_t props_mod;
938 apr_hash_t *pristine_props;
939 apr_hash_t *right_props = NULL;
940 const char *pristine_file;
941 const char *translated_file;
942 svn_revnum_t revision;
943 void *file_baton = NULL;
944 svn_boolean_t skip = FALSE;
945 svn_boolean_t file_mod = TRUE;
947 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
948 NULL, NULL, NULL, NULL, &checksum, NULL,
949 &original_repos_relpath, NULL, NULL,
950 &original_revision, NULL, NULL, NULL,
951 &changelist, NULL, NULL, &had_props,
952 &props_mod, NULL, NULL, NULL,
954 scratch_pool, scratch_pool));
956 assert(kind == svn_node_file
957 && (status == svn_wc__db_status_normal
958 || status == svn_wc__db_status_added
959 || (status == svn_wc__db_status_deleted && diff_pristine)));
962 if (changelist && changelist_hash
963 && !svn_hash_gets(changelist_hash, changelist))
966 if (status == svn_wc__db_status_deleted)
968 assert(diff_pristine);
970 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
971 NULL, &checksum, NULL, &had_props,
974 scratch_pool, scratch_pool));
978 pristine_props = apr_hash_make(scratch_pool);
980 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
982 scratch_pool, scratch_pool));
984 if (original_repos_relpath)
986 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
987 copyfrom_src->repos_relpath = original_repos_relpath;
990 if (props_mod || !SVN_IS_VALID_REVNUM(revision))
991 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
997 SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
998 FALSE, scratch_pool));
1001 right_src = svn_diff__source_create(revision, scratch_pool);
1003 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
1006 SVN_ERR(processor->file_opened(&file_baton, &skip,
1008 NULL /* left_source */,
1011 processor_parent_baton,
1013 scratch_pool, scratch_pool));
1016 return SVN_NO_ERROR;
1018 if (props_mod && !diff_pristine)
1019 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1020 scratch_pool, scratch_pool));
1022 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1025 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
1026 checksum, scratch_pool, scratch_pool));
1028 pristine_file = NULL;
1032 translated_file = pristine_file; /* No translation needed */
1036 SVN_ERR(svn_wc__internal_translated_file(
1037 &translated_file, local_abspath, db, local_abspath,
1038 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
1039 cancel_func, cancel_baton,
1040 scratch_pool, scratch_pool));
1043 SVN_ERR(processor->file_added(relpath,
1058 return SVN_NO_ERROR;
1062 svn_wc__diff_local_only_dir(svn_wc__db_t *db,
1063 const char *local_abspath,
1064 const char *relpath,
1066 const svn_diff_tree_processor_t *processor,
1067 void *processor_parent_baton,
1068 apr_hash_t *changelist_hash,
1069 svn_boolean_t diff_pristine,
1070 svn_cancel_func_t cancel_func,
1072 apr_pool_t *scratch_pool)
1074 const apr_array_header_t *children;
1076 apr_pool_t *iterpool;
1078 svn_boolean_t skip = FALSE;
1079 svn_boolean_t skip_children = FALSE;
1080 svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
1082 svn_depth_t depth_below_here = depth;
1084 apr_hash_t *conflicts;
1086 /* Report the addition of the directory's contents. */
1087 iterpool = svn_pool_create(scratch_pool);
1089 SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
1093 NULL /* copyfrom_src */,
1094 processor_parent_baton,
1096 scratch_pool, iterpool));
1098 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath,
1099 scratch_pool, iterpool));
1101 if (depth_below_here == svn_depth_immediates)
1102 depth_below_here = svn_depth_empty;
1104 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1107 for (i = 0; i < children->nelts; i++)
1109 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
1110 const char *name = item->key;
1111 struct svn_wc__db_info_t *info = item->value;
1112 const char *child_abspath;
1113 const char *child_relpath;
1115 svn_pool_clear(iterpool);
1118 SVN_ERR(cancel_func(cancel_baton));
1120 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1122 if (NOT_PRESENT(info->status))
1127 /* If comparing against WORKING, skip entries that are
1128 schedule-deleted - they don't really exist. */
1129 if (!diff_pristine && info->status == svn_wc__db_status_deleted)
1132 child_relpath = svn_relpath_join(relpath, name, iterpool);
1137 case svn_node_symlink:
1138 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
1143 cancel_func, cancel_baton,
1148 if (depth > svn_depth_files || depth == svn_depth_unknown)
1150 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
1151 child_relpath, depth_below_here,
1155 cancel_func, cancel_baton,
1167 apr_hash_t *right_props;
1169 SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath,
1170 scratch_pool, scratch_pool));
1172 SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath,
1173 scratch_pool, scratch_pool));
1175 SVN_ERR(processor->dir_added(relpath,
1176 NULL /* copyfrom_src */,
1184 svn_pool_destroy(iterpool);
1186 return SVN_NO_ERROR;
1189 /* Reports local changes. */
1190 static svn_error_t *
1191 handle_local_only(struct dir_baton_t *pb,
1193 apr_pool_t *scratch_pool)
1195 struct edit_baton_t *eb = pb->eb;
1196 const struct svn_wc__db_info_t *info;
1197 svn_boolean_t repos_delete = (pb->deletes
1198 && svn_hash_gets(pb->deletes, name));
1200 assert(!strchr(name, '/'));
1201 assert(!pb->added || eb->ignore_ancestry);
1203 if (pb->skip_children)
1204 return SVN_NO_ERROR;
1206 SVN_ERR(ensure_local_info(pb, scratch_pool));
1208 info = svn_hash_gets(pb->local_info, name);
1210 if (info == NULL || NOT_PRESENT(info->status))
1211 return SVN_NO_ERROR;
1213 switch (info->status)
1215 case svn_wc__db_status_incomplete:
1216 return SVN_NO_ERROR; /* Not local only */
1218 case svn_wc__db_status_normal:
1220 return SVN_NO_ERROR; /* Local and remote */
1221 svn_hash_sets(pb->deletes, name, NULL);
1224 case svn_wc__db_status_deleted:
1225 if (!(eb->diff_pristine && repos_delete))
1226 return SVN_NO_ERROR;
1229 case svn_wc__db_status_added:
1234 if (info->kind == svn_node_dir)
1238 if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
1241 depth = svn_depth_empty;
1243 SVN_ERR(svn_wc__diff_local_only_dir(
1245 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1246 svn_relpath_join(pb->relpath, name, scratch_pool),
1247 repos_delete ? svn_depth_infinity : depth,
1248 eb->processor, pb->pdb,
1249 eb->changelist_hash,
1251 eb->cancel_func, eb->cancel_baton,
1255 SVN_ERR(svn_wc__diff_local_only_file(
1257 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1258 svn_relpath_join(pb->relpath, name, scratch_pool),
1259 eb->processor, pb->pdb,
1260 eb->changelist_hash,
1262 eb->cancel_func, eb->cancel_baton,
1265 return SVN_NO_ERROR;
1268 /* Reports a file LOCAL_ABSPATH in BASE as deleted */
1270 svn_wc__diff_base_only_file(svn_wc__db_t *db,
1271 const char *local_abspath,
1272 const char *relpath,
1273 svn_revnum_t revision,
1274 const svn_diff_tree_processor_t *processor,
1275 void *processor_parent_baton,
1276 apr_pool_t *scratch_pool)
1278 svn_wc__db_status_t status;
1279 svn_node_kind_t kind;
1280 const svn_checksum_t *checksum;
1282 void *file_baton = NULL;
1283 svn_boolean_t skip = FALSE;
1284 svn_diff_source_t *left_src;
1285 const char *pristine_file;
1287 SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
1288 SVN_IS_VALID_REVNUM(revision)
1290 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1291 &checksum, NULL, NULL, NULL, &props, NULL,
1293 scratch_pool, scratch_pool));
1295 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
1296 && kind == svn_node_file
1299 left_src = svn_diff__source_create(revision, scratch_pool);
1301 SVN_ERR(processor->file_opened(&file_baton, &skip,
1304 NULL /* right_src */,
1305 NULL /* copyfrom_source */,
1306 processor_parent_baton,
1308 scratch_pool, scratch_pool));
1311 return SVN_NO_ERROR;
1313 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
1314 db, local_abspath, checksum,
1315 scratch_pool, scratch_pool));
1317 SVN_ERR(processor->file_deleted(relpath,
1325 return SVN_NO_ERROR;
1329 svn_wc__diff_base_only_dir(svn_wc__db_t *db,
1330 const char *local_abspath,
1331 const char *relpath,
1332 svn_revnum_t revision,
1334 const svn_diff_tree_processor_t *processor,
1335 void *processor_parent_baton,
1336 svn_cancel_func_t cancel_func,
1338 apr_pool_t *scratch_pool)
1340 void *dir_baton = NULL;
1341 svn_boolean_t skip = FALSE;
1342 svn_boolean_t skip_children = FALSE;
1343 svn_diff_source_t *left_src;
1344 svn_revnum_t report_rev = revision;
1346 if (!SVN_IS_VALID_REVNUM(report_rev))
1347 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
1348 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1351 scratch_pool, scratch_pool));
1353 left_src = svn_diff__source_create(report_rev, scratch_pool);
1355 SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
1358 NULL /* right_src */,
1359 NULL /* copyfrom_src */,
1360 processor_parent_baton,
1362 scratch_pool, scratch_pool));
1364 if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
1367 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1368 apr_array_header_t *children;
1371 SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
1372 scratch_pool, iterpool));
1374 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1377 for (i = 0; i < children->nelts; i++)
1379 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1381 const char *name = item->key;
1382 struct svn_wc__db_base_info_t *info = item->value;
1383 const char *child_abspath;
1384 const char *child_relpath;
1386 if (info->status != svn_wc__db_status_normal)
1390 SVN_ERR(cancel_func(cancel_baton));
1392 svn_pool_clear(iterpool);
1394 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1395 child_relpath = svn_relpath_join(relpath, name, iterpool);
1400 case svn_node_symlink:
1401 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
1404 processor, dir_baton,
1408 if (depth > svn_depth_files || depth == svn_depth_unknown)
1410 svn_depth_t depth_below_here = depth;
1412 if (depth_below_here == svn_depth_immediates)
1413 depth_below_here = svn_depth_empty;
1415 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
1419 processor, dir_baton,
1435 SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
1436 scratch_pool, scratch_pool));
1438 SVN_ERR(processor->dir_deleted(relpath,
1446 return SVN_NO_ERROR;
1449 /* An svn_delta_editor_t function. */
1450 static svn_error_t *
1451 set_target_revision(void *edit_baton,
1452 svn_revnum_t target_revision,
1455 struct edit_baton_t *eb = edit_baton;
1456 eb->revnum = target_revision;
1458 return SVN_NO_ERROR;
1461 /* An svn_delta_editor_t function. The root of the comparison hierarchy */
1462 static svn_error_t *
1463 open_root(void *edit_baton,
1464 svn_revnum_t base_revision,
1465 apr_pool_t *dir_pool,
1468 struct edit_baton_t *eb = edit_baton;
1469 struct dir_baton_t *db;
1471 eb->root_opened = TRUE;
1472 db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
1475 if (eb->target[0] == '\0')
1477 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1478 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1480 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
1485 NULL /* copyfrom_source */,
1486 NULL /* parent_baton */,
1488 db->pool, db->pool));
1491 db->skip = TRUE; /* Skip this, but not the children */
1493 return SVN_NO_ERROR;
1496 /* An svn_delta_editor_t function. */
1497 static svn_error_t *
1498 delete_entry(const char *path,
1499 svn_revnum_t base_revision,
1503 struct dir_baton_t *pb = parent_baton;
1504 const char *name = svn_dirent_basename(path, pb->pool);
1507 pb->deletes = apr_hash_make(pb->pool);
1509 svn_hash_sets(pb->deletes, name, "");
1510 return SVN_NO_ERROR;
1513 /* An svn_delta_editor_t function. */
1514 static svn_error_t *
1515 add_directory(const char *path,
1517 const char *copyfrom_path,
1518 svn_revnum_t copyfrom_revision,
1519 apr_pool_t *dir_pool,
1522 struct dir_baton_t *pb = parent_baton;
1523 struct edit_baton_t *eb = pb->eb;
1524 struct dir_baton_t *db;
1525 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1526 ? svn_depth_empty : pb->depth;
1528 db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
1532 if (pb->repos_only || !eb->ignore_ancestry)
1533 db->repos_only = TRUE;
1536 struct svn_wc__db_info_t *info;
1537 SVN_ERR(ensure_local_info(pb, dir_pool));
1539 info = svn_hash_gets(pb->local_info, db->name);
1541 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1542 db->repos_only = TRUE;
1544 if (!db->repos_only && info->status != svn_wc__db_status_added)
1545 db->repos_only = TRUE;
1547 if (!db->repos_only)
1549 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1550 db->ignoring_ancestry = TRUE;
1552 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1556 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1558 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1559 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1561 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1565 NULL /* copyfrom src */,
1568 db->pool, db->pool));
1570 return SVN_NO_ERROR;
1573 /* An svn_delta_editor_t function. */
1574 static svn_error_t *
1575 open_directory(const char *path,
1577 svn_revnum_t base_revision,
1578 apr_pool_t *dir_pool,
1581 struct dir_baton_t *pb = parent_baton;
1582 struct edit_baton_t *eb = pb->eb;
1583 struct dir_baton_t *db;
1584 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1585 ? svn_depth_empty : pb->depth;
1587 /* Allocate path from the parent pool since the memory is used in the
1588 parent's compared hash */
1589 db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
1593 db->repos_only = TRUE;
1596 struct svn_wc__db_info_t *info;
1597 SVN_ERR(ensure_local_info(pb, dir_pool));
1599 info = svn_hash_gets(pb->local_info, db->name);
1601 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1602 db->repos_only = TRUE;
1604 if (!db->repos_only)
1605 switch (info->status)
1607 case svn_wc__db_status_normal:
1609 case svn_wc__db_status_deleted:
1610 db->repos_only = TRUE;
1612 if (!info->have_more_work)
1613 svn_hash_sets(pb->compared,
1614 apr_pstrdup(pb->pool, db->name), "");
1616 case svn_wc__db_status_added:
1617 if (eb->ignore_ancestry)
1618 db->ignoring_ancestry = TRUE;
1620 db->repos_only = TRUE;
1623 SVN_ERR_MALFUNCTION();
1626 if (!db->repos_only)
1628 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1629 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1633 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1635 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1636 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1638 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1642 NULL /* copyfrom src */,
1645 db->pool, db->pool));
1647 return SVN_NO_ERROR;
1651 /* An svn_delta_editor_t function. When a directory is closed, all the
1652 * directory elements that have been added or replaced will already have been
1653 * diff'd. However there may be other elements in the working copy
1654 * that have not yet been considered. */
1655 static svn_error_t *
1656 close_directory(void *dir_baton,
1659 struct dir_baton_t *db = dir_baton;
1660 struct dir_baton_t *pb = db->parent_baton;
1661 struct edit_baton_t *eb = db->eb;
1662 apr_pool_t *scratch_pool = db->pool;
1663 svn_boolean_t reported_closed = FALSE;
1665 if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
1667 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1668 apr_array_header_t *children;
1670 children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
1673 for (i = 0; i < children->nelts; i++)
1675 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1677 const char *name = item->key;
1679 svn_pool_clear(iterpool);
1680 SVN_ERR(handle_local_only(db, name, iterpool));
1682 svn_hash_sets(db->compared, name, "");
1685 svn_pool_destroy(iterpool);
1688 /* Report local modifications for this directory. Skip added
1689 directories since they can only contain added elements, all of
1690 which have already been diff'd. */
1691 if (!db->repos_only && !db->skip_children)
1693 SVN_ERR(walk_local_nodes_diff(eb,
1702 /* Report the property changes on the directory itself, if necessary. */
1705 /* Diff processor requested no directory details */
1707 else if (db->propchanges->nelts > 0 || db->repos_only)
1709 apr_hash_t *repos_props;
1713 repos_props = apr_hash_make(scratch_pool);
1717 SVN_ERR(svn_wc__db_base_get_props(&repos_props,
1718 eb->db, db->local_abspath,
1719 scratch_pool, scratch_pool));
1722 /* Add received property changes and entry props */
1723 if (db->propchanges->nelts)
1724 repos_props = svn_prop__patch(repos_props, db->propchanges,
1729 SVN_ERR(eb->processor->dir_deleted(db->relpath,
1735 reported_closed = TRUE;
1739 apr_hash_t *local_props;
1740 apr_array_header_t *prop_changes;
1742 if (eb->diff_pristine)
1743 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1744 NULL, NULL, NULL, NULL,
1746 eb->db, db->local_abspath,
1747 scratch_pool, scratch_pool));
1749 SVN_ERR(svn_wc__db_read_props(&local_props,
1750 eb->db, db->local_abspath,
1751 scratch_pool, scratch_pool));
1753 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
1756 /* ### as a good diff processor we should now only report changes
1757 if there are non-entry changes, but for now we stick to
1760 if (prop_changes->nelts)
1762 SVN_ERR(eb->processor->dir_changed(db->relpath,
1771 reported_closed = TRUE;
1776 /* Mark this directory as compared in the parent directory's baton,
1777 unless this is the root of the comparison. */
1778 if (!reported_closed && !db->skip)
1779 SVN_ERR(eb->processor->dir_closed(db->relpath,
1786 if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1787 SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
1789 SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
1791 return SVN_NO_ERROR;
1794 /* An svn_delta_editor_t function. */
1795 static svn_error_t *
1796 add_file(const char *path,
1798 const char *copyfrom_path,
1799 svn_revnum_t copyfrom_revision,
1800 apr_pool_t *file_pool,
1803 struct dir_baton_t *pb = parent_baton;
1804 struct edit_baton_t *eb = pb->eb;
1805 struct file_baton_t *fb;
1807 fb = make_file_baton(path, TRUE, pb, file_pool);
1810 if (pb->skip_children)
1813 return SVN_NO_ERROR;
1815 else if (pb->repos_only || !eb->ignore_ancestry)
1816 fb->repos_only = TRUE;
1819 struct svn_wc__db_info_t *info;
1820 SVN_ERR(ensure_local_info(pb, file_pool));
1822 info = svn_hash_gets(pb->local_info, fb->name);
1824 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1825 fb->repos_only = TRUE;
1827 if (!fb->repos_only && info->status != svn_wc__db_status_added)
1828 fb->repos_only = TRUE;
1830 if (!fb->repos_only)
1832 /* Add this path to the parent directory's list of elements that
1833 have been compared. */
1834 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1835 fb->ignoring_ancestry = TRUE;
1837 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1841 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1843 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1847 NULL /* copyfrom src */,
1850 fb->pool, fb->pool));
1852 return SVN_NO_ERROR;
1855 /* An svn_delta_editor_t function. */
1856 static svn_error_t *
1857 open_file(const char *path,
1859 svn_revnum_t base_revision,
1860 apr_pool_t *file_pool,
1863 struct dir_baton_t *pb = parent_baton;
1864 struct edit_baton_t *eb = pb->eb;
1865 struct file_baton_t *fb;
1867 fb = make_file_baton(path, FALSE, pb, file_pool);
1870 if (pb->skip_children)
1872 else if (pb->repos_only)
1873 fb->repos_only = TRUE;
1876 struct svn_wc__db_info_t *info;
1877 SVN_ERR(ensure_local_info(pb, file_pool));
1879 info = svn_hash_gets(pb->local_info, fb->name);
1881 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1882 fb->repos_only = TRUE;
1884 if (!fb->repos_only)
1885 switch (info->status)
1887 case svn_wc__db_status_normal:
1889 case svn_wc__db_status_deleted:
1890 fb->repos_only = TRUE;
1891 if (!info->have_more_work)
1892 svn_hash_sets(pb->compared,
1893 apr_pstrdup(pb->pool, fb->name), "");
1895 case svn_wc__db_status_added:
1896 if (eb->ignore_ancestry)
1897 fb->ignoring_ancestry = TRUE;
1899 fb->repos_only = TRUE;
1902 SVN_ERR_MALFUNCTION();
1905 if (!fb->repos_only)
1907 /* Add this path to the parent directory's list of elements that
1908 have been compared. */
1909 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1910 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1914 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1916 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1917 NULL, NULL, NULL, &fb->base_checksum, NULL,
1918 NULL, NULL, &fb->base_props, NULL,
1919 eb->db, fb->local_abspath,
1920 fb->pool, fb->pool));
1922 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1926 NULL /* copyfrom src */,
1929 fb->pool, fb->pool));
1931 return SVN_NO_ERROR;
1934 /* An svn_delta_editor_t function. */
1935 static svn_error_t *
1936 apply_textdelta(void *file_baton,
1937 const char *base_checksum_hex,
1939 svn_txdelta_window_handler_t *handler,
1940 void **handler_baton)
1942 struct file_baton_t *fb = file_baton;
1943 struct edit_baton_t *eb = fb->eb;
1944 svn_stream_t *source;
1945 svn_stream_t *temp_stream;
1946 svn_checksum_t *repos_checksum = NULL;
1950 *handler = svn_delta_noop_window_handler;
1951 *handler_baton = NULL;
1952 return SVN_NO_ERROR;
1955 if (base_checksum_hex && fb->base_checksum)
1957 const svn_checksum_t *base_md5;
1958 SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
1959 base_checksum_hex, pool));
1961 SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
1962 eb->db, eb->anchor_abspath,
1966 if (! svn_checksum_match(repos_checksum, base_md5))
1968 /* ### I expect that there are some bad drivers out there
1969 ### that used to give bad results. We could look in
1970 ### working to see if the expected checksum matches and
1971 ### then return the pristine of that... But that only moves
1974 /* If needed: compare checksum obtained via md5 of working.
1975 And if they match set fb->base_checksum and fb->base_props */
1977 return svn_checksum_mismatch_err(
1981 _("Checksum mismatch for '%s'"),
1982 svn_dirent_local_style(fb->local_abspath,
1986 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1987 eb->db, fb->local_abspath,
1991 else if (fb->base_checksum)
1993 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1994 eb->db, fb->local_abspath,
1999 source = svn_stream_empty(pool);
2001 /* This is the file that will contain the pristine repository version. */
2002 SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
2003 svn_io_file_del_on_pool_cleanup,
2004 fb->pool, fb->pool));
2006 svn_txdelta_apply(source, temp_stream,
2008 fb->local_abspath /* error_info */,
2010 handler, handler_baton);
2012 return SVN_NO_ERROR;
2015 /* An svn_delta_editor_t function. When the file is closed we have a temporary
2016 * file containing a pristine version of the repository file. This can
2017 * be compared against the working copy.
2019 * Ignore TEXT_CHECKSUM.
2021 static svn_error_t *
2022 close_file(void *file_baton,
2023 const char *expected_md5_digest,
2026 struct file_baton_t *fb = file_baton;
2027 struct dir_baton_t *pb = fb->parent_baton;
2028 struct edit_baton_t *eb = fb->eb;
2029 apr_pool_t *scratch_pool = fb->pool;
2031 /* The repository information; constructed from BASE + Changes */
2032 const char *repos_file;
2033 apr_hash_t *repos_props;
2035 if (!fb->skip && expected_md5_digest != NULL)
2037 svn_checksum_t *expected_checksum;
2038 const svn_checksum_t *result_checksum;
2040 if (fb->temp_file_path)
2041 result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
2044 result_checksum = fb->base_checksum;
2046 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
2047 expected_md5_digest, scratch_pool));
2049 if (result_checksum->kind != svn_checksum_md5)
2050 SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
2051 eb->db, fb->local_abspath,
2053 scratch_pool, scratch_pool));
2055 if (!svn_checksum_match(expected_checksum, result_checksum))
2056 return svn_checksum_mismatch_err(
2060 _("Checksum mismatch for '%s'"),
2061 svn_dirent_local_style(fb->local_abspath,
2065 if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2066 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2069 apr_hash_t *prop_base;
2072 prop_base = apr_hash_make(scratch_pool);
2074 prop_base = fb->base_props;
2076 /* includes entry props */
2077 repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
2079 repos_file = fb->temp_file_path;
2082 assert(fb->base_checksum);
2083 SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
2084 eb->db, eb->anchor_abspath,
2086 scratch_pool, scratch_pool));
2092 /* Diff processor requested skipping information */
2094 else if (fb->repos_only)
2096 SVN_ERR(eb->processor->file_deleted(fb->relpath,
2106 /* Produce a diff of actual or pristine against repos */
2107 apr_hash_t *local_props;
2108 apr_array_header_t *prop_changes;
2109 const char *localfile;
2111 /* pb->local_info contains some information that might allow optimizing
2114 if (eb->diff_pristine)
2116 const svn_checksum_t *checksum;
2117 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
2118 NULL, &checksum, NULL, NULL,
2120 eb->db, fb->local_abspath,
2121 scratch_pool, scratch_pool));
2123 SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
2124 eb->db, eb->anchor_abspath,
2126 scratch_pool, scratch_pool));
2130 SVN_ERR(svn_wc__db_read_props(&local_props,
2131 eb->db, fb->local_abspath,
2132 scratch_pool, scratch_pool));
2134 /* a detranslated version of the working file */
2135 SVN_ERR(svn_wc__internal_translated_file(
2136 &localfile, fb->local_abspath, eb->db, fb->local_abspath,
2137 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
2138 eb->cancel_func, eb->cancel_baton,
2139 scratch_pool, scratch_pool));
2142 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
2146 /* ### as a good diff processor we should now only report changes, and
2147 report file_closed() in other cases */
2148 SVN_ERR(eb->processor->file_changed(fb->relpath,
2151 repos_file /* left file */,
2152 localfile /* right file */,
2153 repos_props /* left_props */,
2154 local_props /* right props */,
2155 TRUE /* ### file_modified */,
2162 if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2163 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2165 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2166 SVN_ERR(maybe_done(pb));
2167 return SVN_NO_ERROR;
2171 /* An svn_delta_editor_t function. */
2172 static svn_error_t *
2173 change_file_prop(void *file_baton,
2175 const svn_string_t *value,
2178 struct file_baton_t *fb = file_baton;
2179 svn_prop_t *propchange;
2180 svn_prop_kind_t propkind;
2182 propkind = svn_property_kind2(name);
2183 if (propkind == svn_prop_wc_kind)
2184 return SVN_NO_ERROR;
2185 else if (propkind == svn_prop_regular_kind)
2186 fb->has_propchange = TRUE;
2188 propchange = apr_array_push(fb->propchanges);
2189 propchange->name = apr_pstrdup(fb->pool, name);
2190 propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
2192 return SVN_NO_ERROR;
2196 /* An svn_delta_editor_t function. */
2197 static svn_error_t *
2198 change_dir_prop(void *dir_baton,
2200 const svn_string_t *value,
2203 struct dir_baton_t *db = dir_baton;
2204 svn_prop_t *propchange;
2205 svn_prop_kind_t propkind;
2207 propkind = svn_property_kind2(name);
2208 if (propkind == svn_prop_wc_kind)
2209 return SVN_NO_ERROR;
2210 else if (propkind == svn_prop_regular_kind)
2211 db->has_propchange = TRUE;
2213 propchange = apr_array_push(db->propchanges);
2214 propchange->name = apr_pstrdup(db->pool, name);
2215 propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2217 return SVN_NO_ERROR;
2221 /* An svn_delta_editor_t function. */
2222 static svn_error_t *
2223 close_edit(void *edit_baton,
2226 struct edit_baton_t *eb = edit_baton;
2228 if (!eb->root_opened)
2230 SVN_ERR(walk_local_nodes_diff(eb,
2234 NULL /* compared */,
2235 NULL /* No parent_baton */,
2239 return SVN_NO_ERROR;
2242 /* Public Interface */
2245 /* Create a diff editor and baton. */
2247 svn_wc__get_diff_editor(const svn_delta_editor_t **editor,
2249 svn_wc_context_t *wc_ctx,
2250 const char *anchor_abspath,
2253 svn_boolean_t ignore_ancestry,
2254 svn_boolean_t show_copies_as_adds,
2255 svn_boolean_t use_git_diff_format,
2256 svn_boolean_t use_text_base,
2257 svn_boolean_t reverse_order,
2258 svn_boolean_t server_performs_filtering,
2259 const apr_array_header_t *changelist_filter,
2260 const svn_wc_diff_callbacks4_t *callbacks,
2261 void *callback_baton,
2262 svn_cancel_func_t cancel_func,
2264 apr_pool_t *result_pool,
2265 apr_pool_t *scratch_pool)
2267 struct edit_baton_t *eb;
2269 svn_delta_editor_t *tree_editor;
2270 const svn_delta_editor_t *inner_editor;
2271 struct svn_wc__shim_fetch_baton_t *sfb;
2272 svn_delta_shim_callbacks_t *shim_callbacks =
2273 svn_delta_shim_callbacks_default(result_pool);
2275 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
2277 /* --git implies --show-copies-as-adds */
2278 if (use_git_diff_format)
2279 show_copies_as_adds = TRUE;
2281 SVN_ERR(make_edit_baton(&eb,
2283 anchor_abspath, target,
2284 callbacks, callback_baton,
2285 depth, ignore_ancestry, show_copies_as_adds,
2286 use_text_base, reverse_order, changelist_filter,
2287 cancel_func, cancel_baton,
2290 tree_editor = svn_delta_default_editor(eb->pool);
2292 tree_editor->set_target_revision = set_target_revision;
2293 tree_editor->open_root = open_root;
2294 tree_editor->delete_entry = delete_entry;
2295 tree_editor->add_directory = add_directory;
2296 tree_editor->open_directory = open_directory;
2297 tree_editor->close_directory = close_directory;
2298 tree_editor->add_file = add_file;
2299 tree_editor->open_file = open_file;
2300 tree_editor->apply_textdelta = apply_textdelta;
2301 tree_editor->change_file_prop = change_file_prop;
2302 tree_editor->change_dir_prop = change_dir_prop;
2303 tree_editor->close_file = close_file;
2304 tree_editor->close_edit = close_edit;
2306 inner_editor = tree_editor;
2309 if (!server_performs_filtering
2310 && depth == svn_depth_unknown)
2311 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2320 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
2328 sfb = apr_palloc(result_pool, sizeof(*sfb));
2329 sfb->db = wc_ctx->db;
2330 sfb->base_abspath = eb->anchor_abspath;
2331 sfb->fetch_base = TRUE;
2333 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2334 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2335 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2336 shim_callbacks->fetch_baton = sfb;
2339 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2340 NULL, NULL, shim_callbacks,
2341 result_pool, scratch_pool));
2343 return SVN_NO_ERROR;
2346 /* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
2348 /* baton for the svn_diff_tree_processor_t wrapper */
2349 typedef struct wc_diff_wrap_baton_t
2351 const svn_wc_diff_callbacks4_t *callbacks;
2352 void *callback_baton;
2354 svn_boolean_t walk_deleted_dirs;
2356 apr_pool_t *result_pool;
2357 const char *empty_file;
2359 } wc_diff_wrap_baton_t;
2361 static svn_error_t *
2362 wrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
2363 apr_pool_t *scratch_pool)
2366 return SVN_NO_ERROR;
2368 /* Create a unique file in the tempdir */
2369 SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
2370 svn_io_file_del_on_pool_cleanup,
2371 wb->result_pool, scratch_pool));
2373 return SVN_NO_ERROR;
2376 /* svn_diff_tree_processor_t function */
2377 static svn_error_t *
2378 wrap_dir_opened(void **new_dir_baton,
2379 svn_boolean_t *skip,
2380 svn_boolean_t *skip_children,
2381 const char *relpath,
2382 const svn_diff_source_t *left_source,
2383 const svn_diff_source_t *right_source,
2384 const svn_diff_source_t *copyfrom_source,
2385 void *parent_dir_baton,
2386 const svn_diff_tree_processor_t *processor,
2387 apr_pool_t *result_pool,
2388 apr_pool_t *scratch_pool)
2390 wc_diff_wrap_baton_t *wb = processor->baton;
2391 svn_boolean_t tree_conflicted = FALSE;
2393 assert(left_source || right_source);
2394 assert(!copyfrom_source || !right_source);
2396 /* Maybe store state and tree_conflicted in baton? */
2397 if (left_source != NULL)
2399 /* Open for change or delete */
2400 SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
2403 ? right_source->revision
2405 ? left_source->revision
2406 : SVN_INVALID_REVNUM),
2410 if (! right_source && !wb->walk_deleted_dirs)
2411 *skip_children = TRUE;
2413 else /* left_source == NULL -> Add */
2415 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2416 SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
2417 skip, skip_children,
2419 right_source->revision,
2421 ? copyfrom_source->repos_relpath
2424 ? copyfrom_source->revision
2425 : SVN_INVALID_REVNUM,
2430 *new_dir_baton = NULL;
2432 return SVN_NO_ERROR;
2435 /* svn_diff_tree_processor_t function */
2436 static svn_error_t *
2437 wrap_dir_added(const char *relpath,
2438 const svn_diff_source_t *right_source,
2439 const svn_diff_source_t *copyfrom_source,
2440 /*const*/ apr_hash_t *copyfrom_props,
2441 /*const*/ apr_hash_t *right_props,
2443 const svn_diff_tree_processor_t *processor,
2444 apr_pool_t *scratch_pool)
2446 wc_diff_wrap_baton_t *wb = processor->baton;
2447 svn_boolean_t tree_conflicted = FALSE;
2448 svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
2449 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2450 apr_hash_t *pristine_props = copyfrom_props;
2451 apr_array_header_t *prop_changes = NULL;
2453 if (right_props && apr_hash_count(right_props))
2455 if (!pristine_props)
2456 pristine_props = apr_hash_make(scratch_pool);
2458 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
2461 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
2464 TRUE /* dir_was_added */,
2465 prop_changes, pristine_props,
2470 SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
2473 TRUE /* dir_was_added */,
2476 return SVN_NO_ERROR;
2479 /* svn_diff_tree_processor_t function */
2480 static svn_error_t *
2481 wrap_dir_deleted(const char *relpath,
2482 const svn_diff_source_t *left_source,
2483 /*const*/ apr_hash_t *left_props,
2485 const svn_diff_tree_processor_t *processor,
2486 apr_pool_t *scratch_pool)
2488 wc_diff_wrap_baton_t *wb = processor->baton;
2489 svn_boolean_t tree_conflicted = FALSE;
2490 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2492 SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
2497 return SVN_NO_ERROR;
2500 /* svn_diff_tree_processor_t function */
2501 static svn_error_t *
2502 wrap_dir_closed(const char *relpath,
2503 const svn_diff_source_t *left_source,
2504 const svn_diff_source_t *right_source,
2506 const svn_diff_tree_processor_t *processor,
2507 apr_pool_t *scratch_pool)
2509 wc_diff_wrap_baton_t *wb = processor->baton;
2511 /* No previous implementations provided these arguments, so we
2512 are not providing them either */
2513 SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
2519 return SVN_NO_ERROR;
2522 /* svn_diff_tree_processor_t function */
2523 static svn_error_t *
2524 wrap_dir_changed(const char *relpath,
2525 const svn_diff_source_t *left_source,
2526 const svn_diff_source_t *right_source,
2527 /*const*/ apr_hash_t *left_props,
2528 /*const*/ apr_hash_t *right_props,
2529 const apr_array_header_t *prop_changes,
2531 const struct svn_diff_tree_processor_t *processor,
2532 apr_pool_t *scratch_pool)
2534 wc_diff_wrap_baton_t *wb = processor->baton;
2535 svn_boolean_t tree_conflicted = FALSE;
2536 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2538 assert(left_source && right_source);
2540 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
2542 FALSE /* dir_was_added */,
2548 /* And call dir_closed, etc */
2549 SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
2550 dir_baton, processor,
2552 return SVN_NO_ERROR;
2555 /* svn_diff_tree_processor_t function */
2556 static svn_error_t *
2557 wrap_file_opened(void **new_file_baton,
2558 svn_boolean_t *skip,
2559 const char *relpath,
2560 const svn_diff_source_t *left_source,
2561 const svn_diff_source_t *right_source,
2562 const svn_diff_source_t *copyfrom_source,
2564 const svn_diff_tree_processor_t *processor,
2565 apr_pool_t *result_pool,
2566 apr_pool_t *scratch_pool)
2568 wc_diff_wrap_baton_t *wb = processor->baton;
2569 svn_boolean_t tree_conflicted = FALSE;
2571 if (left_source) /* If ! added */
2572 SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
2574 ? right_source->revision
2576 ? left_source->revision
2577 : SVN_INVALID_REVNUM),
2578 wb->callback_baton, scratch_pool));
2580 /* No old implementation used the output arguments for notify */
2582 *new_file_baton = NULL;
2583 return SVN_NO_ERROR;
2586 /* svn_diff_tree_processor_t function */
2587 static svn_error_t *
2588 wrap_file_added(const char *relpath,
2589 const svn_diff_source_t *copyfrom_source,
2590 const svn_diff_source_t *right_source,
2591 const char *copyfrom_file,
2592 const char *right_file,
2593 /*const*/ apr_hash_t *copyfrom_props,
2594 /*const*/ apr_hash_t *right_props,
2596 const svn_diff_tree_processor_t *processor,
2597 apr_pool_t *scratch_pool)
2599 wc_diff_wrap_baton_t *wb = processor->baton;
2600 svn_boolean_t tree_conflicted = FALSE;
2601 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2602 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2603 apr_array_header_t *prop_changes;
2605 if (! copyfrom_props)
2606 copyfrom_props = apr_hash_make(scratch_pool);
2608 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
2611 if (! copyfrom_source)
2612 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2614 SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
2621 right_source->revision,
2623 ? svn_prop_get_value(copyfrom_props,
2627 ? svn_prop_get_value(right_props,
2631 ? copyfrom_source->repos_relpath
2634 ? copyfrom_source->revision
2635 : SVN_INVALID_REVNUM,
2636 prop_changes, copyfrom_props,
2639 return SVN_NO_ERROR;
2642 static svn_error_t *
2643 wrap_file_deleted(const char *relpath,
2644 const svn_diff_source_t *left_source,
2645 const char *left_file,
2646 apr_hash_t *left_props,
2648 const svn_diff_tree_processor_t *processor,
2649 apr_pool_t *scratch_pool)
2651 wc_diff_wrap_baton_t *wb = processor->baton;
2652 svn_boolean_t tree_conflicted = FALSE;
2653 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2655 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2657 SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
2659 left_file, wb->empty_file,
2661 ? svn_prop_get_value(left_props,
2668 return SVN_NO_ERROR;
2671 /* svn_diff_tree_processor_t function */
2672 static svn_error_t *
2673 wrap_file_changed(const char *relpath,
2674 const svn_diff_source_t *left_source,
2675 const svn_diff_source_t *right_source,
2676 const char *left_file,
2677 const char *right_file,
2678 /*const*/ apr_hash_t *left_props,
2679 /*const*/ apr_hash_t *right_props,
2680 svn_boolean_t file_modified,
2681 const apr_array_header_t *prop_changes,
2683 const svn_diff_tree_processor_t *processor,
2684 apr_pool_t *scratch_pool)
2686 wc_diff_wrap_baton_t *wb = processor->baton;
2687 svn_boolean_t tree_conflicted = FALSE;
2688 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2689 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2691 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2693 assert(left_source && right_source);
2695 SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
2697 file_modified ? left_file : NULL,
2698 file_modified ? right_file : NULL,
2699 left_source->revision,
2700 right_source->revision,
2702 ? svn_prop_get_value(left_props,
2706 ? svn_prop_get_value(right_props,
2713 return SVN_NO_ERROR;
2717 svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
2718 const svn_wc_diff_callbacks4_t *callbacks,
2719 void *callback_baton,
2720 svn_boolean_t walk_deleted_dirs,
2721 apr_pool_t *result_pool,
2722 apr_pool_t *scratch_pool)
2724 wc_diff_wrap_baton_t *wrap_baton;
2725 svn_diff_tree_processor_t *processor;
2727 wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
2729 wrap_baton->result_pool = result_pool;
2730 wrap_baton->callbacks = callbacks;
2731 wrap_baton->callback_baton = callback_baton;
2732 wrap_baton->empty_file = NULL;
2733 wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
2735 processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
2737 processor->dir_opened = wrap_dir_opened;
2738 processor->dir_added = wrap_dir_added;
2739 processor->dir_deleted = wrap_dir_deleted;
2740 processor->dir_changed = wrap_dir_changed;
2741 processor->dir_closed = wrap_dir_closed;
2743 processor->file_opened = wrap_file_opened;
2744 processor->file_added = wrap_file_added;
2745 processor->file_deleted = wrap_file_deleted;
2746 processor->file_changed = wrap_file_changed;
2747 /*processor->file_closed = wrap_file_closed*/; /* Not needed */
2749 *diff_processor = processor;
2750 return SVN_NO_ERROR;