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 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
478 FALSE /* verify truename */,
479 TRUE /* ingore_enoent */,
480 scratch_pool, scratch_pool));
482 if (dirent->kind == svn_node_file
483 && dirent->filesize == recorded_size
484 && dirent->mtime == recorded_time)
490 if (files_same && !props_mod)
491 return SVN_NO_ERROR; /* Cheap exit */
495 if (!SVN_IS_VALID_REVNUM(revision))
496 revision = db_revision;
498 left_src = svn_diff__source_create(revision, scratch_pool);
499 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
501 SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
504 NULL /* copyfrom_src */,
507 scratch_pool, scratch_pool));
512 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
513 db, local_abspath, checksum,
514 scratch_pool, scratch_pool));
517 SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
520 scratch_pool, scratch_pool));
521 else if (! (had_props || props_mod))
522 local_file = local_abspath;
524 local_file = pristine_file;
526 SVN_ERR(svn_wc__internal_translated_file(
527 &local_file, local_abspath,
529 SVN_WC_TRANSLATE_TO_NF
530 | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
531 cancel_func, cancel_baton,
532 scratch_pool, scratch_pool));
535 SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
536 pristine_file, scratch_pool));
539 SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
540 scratch_pool, scratch_pool));
542 base_props = apr_hash_make(scratch_pool);
544 if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
545 local_props = base_props;
546 else if (diff_pristine)
547 SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
548 scratch_pool, scratch_pool));
550 SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
551 scratch_pool, scratch_pool));
553 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
555 if (prop_changes->nelts || !files_same)
557 SVN_ERR(processor->file_changed(relpath,
572 SVN_ERR(processor->file_closed(relpath,
584 ensure_local_info(struct dir_baton_t *db,
585 apr_pool_t *scratch_pool)
587 apr_hash_t *conflicts;
592 SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
593 db->eb->db, db->local_abspath,
594 db->pool, scratch_pool));
599 /* Called when the directory is closed to compare any elements that have
600 * not yet been compared. This identifies local, working copy only
601 * changes. At this stage we are dealing with files/directories that do
602 * exist in the working copy.
604 * DIR_BATON is the baton for the directory.
607 walk_local_nodes_diff(struct edit_baton_t *eb,
608 const char *local_abspath,
611 apr_hash_t *compared,
613 apr_pool_t *scratch_pool)
615 svn_wc__db_t *db = eb->db;
616 svn_boolean_t in_anchor_not_target;
617 apr_pool_t *iterpool;
618 void *dir_baton = NULL;
619 svn_boolean_t skip = FALSE;
620 svn_boolean_t skip_children = FALSE;
621 svn_revnum_t revision;
622 svn_boolean_t props_mod;
623 svn_diff_source_t *left_src;
624 svn_diff_source_t *right_src;
626 /* Everything we do below is useless if we are comparing to BASE. */
627 if (eb->diff_pristine)
630 /* Determine if this is the anchor directory if the anchor is different
631 to the target. When the target is a file, the anchor is the parent
632 directory and if this is that directory the non-target entries must be
634 in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
636 iterpool = svn_pool_create(scratch_pool);
638 SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
639 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
640 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
641 NULL, &props_mod, NULL, NULL, NULL,
642 db, local_abspath, scratch_pool, scratch_pool));
644 left_src = svn_diff__source_create(revision, scratch_pool);
645 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
649 dir_baton = parent_baton;
652 else if (!in_anchor_not_target)
653 SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
657 NULL /* copyfrom_src */,
660 scratch_pool, scratch_pool));
663 if (!skip_children && depth != svn_depth_empty)
666 apr_hash_t *conflicts;
667 apr_array_header_t *children;
668 svn_depth_t depth_below_here = depth;
669 svn_boolean_t diff_files;
670 svn_boolean_t diff_dirs;
673 if (depth_below_here == svn_depth_immediates)
674 depth_below_here = svn_depth_empty;
676 diff_files = (depth == svn_depth_unknown
677 || depth >= svn_depth_files);
678 diff_dirs = (depth == svn_depth_unknown
679 || depth >= svn_depth_immediates);
681 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
683 scratch_pool, iterpool));
685 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
688 for (i = 0; i < children->nelts; i++)
690 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
692 const char *name = item->key;
693 struct svn_wc__db_info_t *info = item->value;
695 const char *child_abspath;
696 const char *child_relpath;
697 svn_boolean_t repos_only;
698 svn_boolean_t local_only;
699 svn_node_kind_t base_kind;
702 SVN_ERR(eb->cancel_func(eb->cancel_baton));
704 /* In the anchor directory, if the anchor is not the target then all
705 entries other than the target should not be diff'd. Running diff
706 on one file in a directory should not diff other files in that
708 if (in_anchor_not_target && strcmp(eb->target, name))
711 if (compared && svn_hash_gets(compared, name))
714 if (NOT_PRESENT(info->status))
717 assert(info->status == svn_wc__db_status_normal
718 || info->status == svn_wc__db_status_added
719 || info->status == svn_wc__db_status_deleted);
721 svn_pool_clear(iterpool);
722 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
723 child_relpath = svn_relpath_join(path, name, iterpool);
728 if (!info->have_base)
730 local_only = TRUE; /* Only report additions */
732 else if (info->status == svn_wc__db_status_normal)
735 base_kind = info->kind;
737 else if (info->status == svn_wc__db_status_deleted
738 && (!eb->diff_pristine || !info->have_more_work))
740 svn_wc__db_status_t base_status;
742 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
743 NULL, NULL, NULL, NULL, NULL,
744 NULL, NULL, NULL, NULL, NULL,
747 iterpool, iterpool));
749 if (NOT_PRESENT(base_status))
754 /* working status is either added or deleted */
755 svn_wc__db_status_t base_status;
757 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
758 NULL, NULL, NULL, NULL, NULL,
759 NULL, NULL, NULL, NULL, NULL,
762 iterpool, iterpool));
764 if (NOT_PRESENT(base_status))
766 else if (base_kind != info->kind || !eb->ignore_ancestry)
773 if (eb->local_before_remote && local_only)
775 if (info->kind == svn_node_file && diff_files)
776 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
778 eb->processor, dir_baton,
784 else if (info->kind == svn_node_dir && diff_dirs)
785 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
788 eb->processor, dir_baton,
798 /* Report repository form deleted */
799 if (base_kind == svn_node_file && diff_files)
800 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
801 child_relpath, eb->revnum,
802 eb->processor, dir_baton,
804 else if (base_kind == svn_node_dir && diff_dirs)
805 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
806 child_relpath, eb->revnum,
808 eb->processor, dir_baton,
813 else if (!local_only) /* Not local only nor remote only */
815 /* Diff base against actual */
816 if (info->kind == svn_node_file && diff_files)
818 if (info->status != svn_wc__db_status_normal
819 || !eb->diff_pristine)
821 SVN_ERR(svn_wc__diff_base_working_diff(
826 eb->processor, dir_baton,
833 else if (info->kind == svn_node_dir && diff_dirs)
834 SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
842 if (!eb->local_before_remote && local_only)
844 if (info->kind == svn_node_file && diff_files)
845 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
847 eb->processor, dir_baton,
853 else if (info->kind == svn_node_dir && diff_dirs)
854 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
855 child_relpath, depth_below_here,
856 eb->processor, dir_baton,
869 /* Check for local property mods on this directory, if we haven't
870 already reported them and we aren't changelist-filted.
871 ### it should be noted that we do not currently allow directories
872 ### to be part of changelists, so if a changelist is provided, the
873 ### changelist check will always fail. */
875 && ! eb->changelist_hash
876 && ! in_anchor_not_target
879 apr_array_header_t *propchanges;
880 apr_hash_t *left_props;
881 apr_hash_t *right_props;
883 SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
885 scratch_pool, scratch_pool));
887 right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
889 SVN_ERR(eb->processor->dir_changed(path,
900 SVN_ERR(eb->processor->dir_closed(path,
907 svn_pool_destroy(iterpool);
913 svn_wc__diff_local_only_file(svn_wc__db_t *db,
914 const char *local_abspath,
916 const svn_diff_tree_processor_t *processor,
917 void *processor_parent_baton,
918 apr_hash_t *changelist_hash,
919 svn_boolean_t diff_pristine,
920 svn_cancel_func_t cancel_func,
922 apr_pool_t *scratch_pool)
924 svn_diff_source_t *right_src;
925 svn_diff_source_t *copyfrom_src = NULL;
926 svn_wc__db_status_t status;
927 svn_node_kind_t kind;
928 const svn_checksum_t *checksum;
929 const char *original_repos_relpath;
930 svn_revnum_t original_revision;
931 const char *changelist;
932 svn_boolean_t had_props;
933 svn_boolean_t props_mod;
934 apr_hash_t *pristine_props;
935 apr_hash_t *right_props = NULL;
936 const char *pristine_file;
937 const char *translated_file;
938 svn_revnum_t revision;
939 void *file_baton = NULL;
940 svn_boolean_t skip = FALSE;
941 svn_boolean_t file_mod = TRUE;
943 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
944 NULL, NULL, NULL, NULL, &checksum, NULL,
945 &original_repos_relpath, NULL, NULL,
946 &original_revision, NULL, NULL, NULL,
947 &changelist, NULL, NULL, &had_props,
948 &props_mod, NULL, NULL, NULL,
950 scratch_pool, scratch_pool));
952 assert(kind == svn_node_file
953 && (status == svn_wc__db_status_normal
954 || status == svn_wc__db_status_added
955 || (status == svn_wc__db_status_deleted && diff_pristine)));
958 if (changelist && changelist_hash
959 && !svn_hash_gets(changelist_hash, changelist))
962 if (status == svn_wc__db_status_deleted)
964 assert(diff_pristine);
966 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
967 NULL, &checksum, NULL, &had_props,
970 scratch_pool, scratch_pool));
974 pristine_props = apr_hash_make(scratch_pool);
976 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
978 scratch_pool, scratch_pool));
980 if (original_repos_relpath)
982 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
983 copyfrom_src->repos_relpath = original_repos_relpath;
986 if (props_mod || !SVN_IS_VALID_REVNUM(revision))
987 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
993 SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
994 FALSE, scratch_pool));
997 right_src = svn_diff__source_create(revision, scratch_pool);
999 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
1002 SVN_ERR(processor->file_opened(&file_baton, &skip,
1004 NULL /* left_source */,
1007 processor_parent_baton,
1009 scratch_pool, scratch_pool));
1012 return SVN_NO_ERROR;
1014 if (props_mod && !diff_pristine)
1015 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1016 scratch_pool, scratch_pool));
1018 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1021 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
1022 checksum, scratch_pool, scratch_pool));
1024 pristine_file = NULL;
1028 translated_file = pristine_file; /* No translation needed */
1032 SVN_ERR(svn_wc__internal_translated_file(
1033 &translated_file, local_abspath, db, local_abspath,
1034 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
1035 cancel_func, cancel_baton,
1036 scratch_pool, scratch_pool));
1039 SVN_ERR(processor->file_added(relpath,
1054 return SVN_NO_ERROR;
1058 svn_wc__diff_local_only_dir(svn_wc__db_t *db,
1059 const char *local_abspath,
1060 const char *relpath,
1062 const svn_diff_tree_processor_t *processor,
1063 void *processor_parent_baton,
1064 apr_hash_t *changelist_hash,
1065 svn_boolean_t diff_pristine,
1066 svn_cancel_func_t cancel_func,
1068 apr_pool_t *scratch_pool)
1070 const apr_array_header_t *children;
1072 apr_pool_t *iterpool;
1074 svn_boolean_t skip = FALSE;
1075 svn_boolean_t skip_children = FALSE;
1076 svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
1078 svn_depth_t depth_below_here = depth;
1080 apr_hash_t *conflicts;
1082 /* Report the addition of the directory's contents. */
1083 iterpool = svn_pool_create(scratch_pool);
1085 SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
1089 NULL /* copyfrom_src */,
1090 processor_parent_baton,
1092 scratch_pool, iterpool));
1094 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath,
1095 scratch_pool, iterpool));
1097 if (depth_below_here == svn_depth_immediates)
1098 depth_below_here = svn_depth_empty;
1100 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1103 for (i = 0; i < children->nelts; i++)
1105 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
1106 const char *name = item->key;
1107 struct svn_wc__db_info_t *info = item->value;
1108 const char *child_abspath;
1109 const char *child_relpath;
1111 svn_pool_clear(iterpool);
1114 SVN_ERR(cancel_func(cancel_baton));
1116 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1118 if (NOT_PRESENT(info->status))
1123 /* If comparing against WORKING, skip entries that are
1124 schedule-deleted - they don't really exist. */
1125 if (!diff_pristine && info->status == svn_wc__db_status_deleted)
1128 child_relpath = svn_relpath_join(relpath, name, iterpool);
1133 case svn_node_symlink:
1134 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
1139 cancel_func, cancel_baton,
1144 if (depth > svn_depth_files || depth == svn_depth_unknown)
1146 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
1147 child_relpath, depth_below_here,
1151 cancel_func, cancel_baton,
1163 apr_hash_t *right_props;
1165 SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath,
1166 scratch_pool, scratch_pool));
1168 SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath,
1169 scratch_pool, scratch_pool));
1171 SVN_ERR(processor->dir_added(relpath,
1172 NULL /* copyfrom_src */,
1180 svn_pool_destroy(iterpool);
1182 return SVN_NO_ERROR;
1185 /* Reports local changes. */
1186 static svn_error_t *
1187 handle_local_only(struct dir_baton_t *pb,
1189 apr_pool_t *scratch_pool)
1191 struct edit_baton_t *eb = pb->eb;
1192 const struct svn_wc__db_info_t *info;
1193 svn_boolean_t repos_delete = (pb->deletes
1194 && svn_hash_gets(pb->deletes, name));
1196 assert(!strchr(name, '/'));
1197 assert(!pb->added || eb->ignore_ancestry);
1199 if (pb->skip_children)
1200 return SVN_NO_ERROR;
1202 SVN_ERR(ensure_local_info(pb, scratch_pool));
1204 info = svn_hash_gets(pb->local_info, name);
1206 if (info == NULL || NOT_PRESENT(info->status))
1207 return SVN_NO_ERROR;
1209 switch (info->status)
1211 case svn_wc__db_status_incomplete:
1212 return SVN_NO_ERROR; /* Not local only */
1214 case svn_wc__db_status_normal:
1216 return SVN_NO_ERROR; /* Local and remote */
1217 svn_hash_sets(pb->deletes, name, NULL);
1220 case svn_wc__db_status_deleted:
1221 if (!(eb->diff_pristine && repos_delete))
1222 return SVN_NO_ERROR;
1225 case svn_wc__db_status_added:
1230 if (info->kind == svn_node_dir)
1234 if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
1237 depth = svn_depth_empty;
1239 SVN_ERR(svn_wc__diff_local_only_dir(
1241 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1242 svn_relpath_join(pb->relpath, name, scratch_pool),
1243 repos_delete ? svn_depth_infinity : depth,
1244 eb->processor, pb->pdb,
1245 eb->changelist_hash,
1247 eb->cancel_func, eb->cancel_baton,
1251 SVN_ERR(svn_wc__diff_local_only_file(
1253 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1254 svn_relpath_join(pb->relpath, name, scratch_pool),
1255 eb->processor, pb->pdb,
1256 eb->changelist_hash,
1258 eb->cancel_func, eb->cancel_baton,
1261 return SVN_NO_ERROR;
1264 /* Reports a file LOCAL_ABSPATH in BASE as deleted */
1266 svn_wc__diff_base_only_file(svn_wc__db_t *db,
1267 const char *local_abspath,
1268 const char *relpath,
1269 svn_revnum_t revision,
1270 const svn_diff_tree_processor_t *processor,
1271 void *processor_parent_baton,
1272 apr_pool_t *scratch_pool)
1274 svn_wc__db_status_t status;
1275 svn_node_kind_t kind;
1276 const svn_checksum_t *checksum;
1278 void *file_baton = NULL;
1279 svn_boolean_t skip = FALSE;
1280 svn_diff_source_t *left_src;
1281 const char *pristine_file;
1283 SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
1284 SVN_IS_VALID_REVNUM(revision)
1286 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1287 &checksum, NULL, NULL, NULL, &props, NULL,
1289 scratch_pool, scratch_pool));
1291 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
1292 && kind == svn_node_file
1295 left_src = svn_diff__source_create(revision, scratch_pool);
1297 SVN_ERR(processor->file_opened(&file_baton, &skip,
1300 NULL /* right_src */,
1301 NULL /* copyfrom_source */,
1302 processor_parent_baton,
1304 scratch_pool, scratch_pool));
1307 return SVN_NO_ERROR;
1309 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
1310 db, local_abspath, checksum,
1311 scratch_pool, scratch_pool));
1313 SVN_ERR(processor->file_deleted(relpath,
1321 return SVN_NO_ERROR;
1325 svn_wc__diff_base_only_dir(svn_wc__db_t *db,
1326 const char *local_abspath,
1327 const char *relpath,
1328 svn_revnum_t revision,
1330 const svn_diff_tree_processor_t *processor,
1331 void *processor_parent_baton,
1332 svn_cancel_func_t cancel_func,
1334 apr_pool_t *scratch_pool)
1336 void *dir_baton = NULL;
1337 svn_boolean_t skip = FALSE;
1338 svn_boolean_t skip_children = FALSE;
1339 svn_diff_source_t *left_src;
1340 svn_revnum_t report_rev = revision;
1342 if (!SVN_IS_VALID_REVNUM(report_rev))
1343 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
1344 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1347 scratch_pool, scratch_pool));
1349 left_src = svn_diff__source_create(report_rev, scratch_pool);
1351 SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
1354 NULL /* right_src */,
1355 NULL /* copyfrom_src */,
1356 processor_parent_baton,
1358 scratch_pool, scratch_pool));
1360 if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
1363 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1364 apr_array_header_t *children;
1367 SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
1368 scratch_pool, iterpool));
1370 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1373 for (i = 0; i < children->nelts; i++)
1375 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1377 const char *name = item->key;
1378 struct svn_wc__db_base_info_t *info = item->value;
1379 const char *child_abspath;
1380 const char *child_relpath;
1382 if (info->status != svn_wc__db_status_normal)
1386 SVN_ERR(cancel_func(cancel_baton));
1388 svn_pool_clear(iterpool);
1390 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1391 child_relpath = svn_relpath_join(relpath, name, iterpool);
1396 case svn_node_symlink:
1397 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
1400 processor, dir_baton,
1404 if (depth > svn_depth_files || depth == svn_depth_unknown)
1406 svn_depth_t depth_below_here = depth;
1408 if (depth_below_here == svn_depth_immediates)
1409 depth_below_here = svn_depth_empty;
1411 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
1415 processor, dir_baton,
1431 SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
1432 scratch_pool, scratch_pool));
1434 SVN_ERR(processor->dir_deleted(relpath,
1442 return SVN_NO_ERROR;
1445 /* An svn_delta_editor_t function. */
1446 static svn_error_t *
1447 set_target_revision(void *edit_baton,
1448 svn_revnum_t target_revision,
1451 struct edit_baton_t *eb = edit_baton;
1452 eb->revnum = target_revision;
1454 return SVN_NO_ERROR;
1457 /* An svn_delta_editor_t function. The root of the comparison hierarchy */
1458 static svn_error_t *
1459 open_root(void *edit_baton,
1460 svn_revnum_t base_revision,
1461 apr_pool_t *dir_pool,
1464 struct edit_baton_t *eb = edit_baton;
1465 struct dir_baton_t *db;
1467 eb->root_opened = TRUE;
1468 db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
1471 if (eb->target[0] == '\0')
1473 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1474 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1476 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
1481 NULL /* copyfrom_source */,
1482 NULL /* parent_baton */,
1484 db->pool, db->pool));
1487 db->skip = TRUE; /* Skip this, but not the children */
1489 return SVN_NO_ERROR;
1492 /* An svn_delta_editor_t function. */
1493 static svn_error_t *
1494 delete_entry(const char *path,
1495 svn_revnum_t base_revision,
1499 struct dir_baton_t *pb = parent_baton;
1500 const char *name = svn_dirent_basename(path, pb->pool);
1503 pb->deletes = apr_hash_make(pb->pool);
1505 svn_hash_sets(pb->deletes, name, "");
1506 return SVN_NO_ERROR;
1509 /* An svn_delta_editor_t function. */
1510 static svn_error_t *
1511 add_directory(const char *path,
1513 const char *copyfrom_path,
1514 svn_revnum_t copyfrom_revision,
1515 apr_pool_t *dir_pool,
1518 struct dir_baton_t *pb = parent_baton;
1519 struct edit_baton_t *eb = pb->eb;
1520 struct dir_baton_t *db;
1521 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1522 ? svn_depth_empty : pb->depth;
1524 db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
1528 if (pb->repos_only || !eb->ignore_ancestry)
1529 db->repos_only = TRUE;
1532 struct svn_wc__db_info_t *info;
1533 SVN_ERR(ensure_local_info(pb, dir_pool));
1535 info = svn_hash_gets(pb->local_info, db->name);
1537 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1538 db->repos_only = TRUE;
1540 if (!db->repos_only && info->status != svn_wc__db_status_added)
1541 db->repos_only = TRUE;
1543 if (!db->repos_only)
1545 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1546 db->ignoring_ancestry = TRUE;
1548 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1552 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1554 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1555 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1557 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1561 NULL /* copyfrom src */,
1564 db->pool, db->pool));
1566 return SVN_NO_ERROR;
1569 /* An svn_delta_editor_t function. */
1570 static svn_error_t *
1571 open_directory(const char *path,
1573 svn_revnum_t base_revision,
1574 apr_pool_t *dir_pool,
1577 struct dir_baton_t *pb = parent_baton;
1578 struct edit_baton_t *eb = pb->eb;
1579 struct dir_baton_t *db;
1580 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1581 ? svn_depth_empty : pb->depth;
1583 /* Allocate path from the parent pool since the memory is used in the
1584 parent's compared hash */
1585 db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
1589 db->repos_only = TRUE;
1592 struct svn_wc__db_info_t *info;
1593 SVN_ERR(ensure_local_info(pb, dir_pool));
1595 info = svn_hash_gets(pb->local_info, db->name);
1597 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1598 db->repos_only = TRUE;
1600 if (!db->repos_only)
1601 switch (info->status)
1603 case svn_wc__db_status_normal:
1605 case svn_wc__db_status_deleted:
1606 db->repos_only = TRUE;
1608 if (!info->have_more_work)
1609 svn_hash_sets(pb->compared,
1610 apr_pstrdup(pb->pool, db->name), "");
1612 case svn_wc__db_status_added:
1613 if (eb->ignore_ancestry)
1614 db->ignoring_ancestry = TRUE;
1616 db->repos_only = TRUE;
1619 SVN_ERR_MALFUNCTION();
1622 if (!db->repos_only)
1624 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1625 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1629 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1631 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1632 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1634 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1638 NULL /* copyfrom src */,
1641 db->pool, db->pool));
1643 return SVN_NO_ERROR;
1647 /* An svn_delta_editor_t function. When a directory is closed, all the
1648 * directory elements that have been added or replaced will already have been
1649 * diff'd. However there may be other elements in the working copy
1650 * that have not yet been considered. */
1651 static svn_error_t *
1652 close_directory(void *dir_baton,
1655 struct dir_baton_t *db = dir_baton;
1656 struct dir_baton_t *pb = db->parent_baton;
1657 struct edit_baton_t *eb = db->eb;
1658 apr_pool_t *scratch_pool = db->pool;
1659 svn_boolean_t reported_closed = FALSE;
1661 if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
1663 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1664 apr_array_header_t *children;
1666 children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
1669 for (i = 0; i < children->nelts; i++)
1671 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1673 const char *name = item->key;
1675 svn_pool_clear(iterpool);
1676 SVN_ERR(handle_local_only(db, name, iterpool));
1678 svn_hash_sets(db->compared, name, "");
1681 svn_pool_destroy(iterpool);
1684 /* Report local modifications for this directory. Skip added
1685 directories since they can only contain added elements, all of
1686 which have already been diff'd. */
1687 if (!db->repos_only && !db->skip_children)
1689 SVN_ERR(walk_local_nodes_diff(eb,
1698 /* Report the property changes on the directory itself, if necessary. */
1701 /* Diff processor requested no directory details */
1703 else if (db->propchanges->nelts > 0 || db->repos_only)
1705 apr_hash_t *repos_props;
1709 repos_props = apr_hash_make(scratch_pool);
1713 SVN_ERR(svn_wc__db_base_get_props(&repos_props,
1714 eb->db, db->local_abspath,
1715 scratch_pool, scratch_pool));
1718 /* Add received property changes and entry props */
1719 if (db->propchanges->nelts)
1720 repos_props = svn_prop__patch(repos_props, db->propchanges,
1725 SVN_ERR(eb->processor->dir_deleted(db->relpath,
1731 reported_closed = TRUE;
1735 apr_hash_t *local_props;
1736 apr_array_header_t *prop_changes;
1738 if (eb->diff_pristine)
1739 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1740 NULL, NULL, NULL, NULL,
1742 eb->db, db->local_abspath,
1743 scratch_pool, scratch_pool));
1745 SVN_ERR(svn_wc__db_read_props(&local_props,
1746 eb->db, db->local_abspath,
1747 scratch_pool, scratch_pool));
1749 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
1752 /* ### as a good diff processor we should now only report changes
1753 if there are non-entry changes, but for now we stick to
1756 if (prop_changes->nelts)
1758 SVN_ERR(eb->processor->dir_changed(db->relpath,
1767 reported_closed = TRUE;
1772 /* Mark this directory as compared in the parent directory's baton,
1773 unless this is the root of the comparison. */
1774 if (!reported_closed && !db->skip)
1775 SVN_ERR(eb->processor->dir_closed(db->relpath,
1782 if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1783 SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
1785 SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
1787 return SVN_NO_ERROR;
1790 /* An svn_delta_editor_t function. */
1791 static svn_error_t *
1792 add_file(const char *path,
1794 const char *copyfrom_path,
1795 svn_revnum_t copyfrom_revision,
1796 apr_pool_t *file_pool,
1799 struct dir_baton_t *pb = parent_baton;
1800 struct edit_baton_t *eb = pb->eb;
1801 struct file_baton_t *fb;
1803 fb = make_file_baton(path, TRUE, pb, file_pool);
1806 if (pb->skip_children)
1809 return SVN_NO_ERROR;
1811 else if (pb->repos_only || !eb->ignore_ancestry)
1812 fb->repos_only = TRUE;
1815 struct svn_wc__db_info_t *info;
1816 SVN_ERR(ensure_local_info(pb, file_pool));
1818 info = svn_hash_gets(pb->local_info, fb->name);
1820 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1821 fb->repos_only = TRUE;
1823 if (!fb->repos_only && info->status != svn_wc__db_status_added)
1824 fb->repos_only = TRUE;
1826 if (!fb->repos_only)
1828 /* Add this path to the parent directory's list of elements that
1829 have been compared. */
1830 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1831 fb->ignoring_ancestry = TRUE;
1833 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1837 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1839 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1843 NULL /* copyfrom src */,
1846 fb->pool, fb->pool));
1848 return SVN_NO_ERROR;
1851 /* An svn_delta_editor_t function. */
1852 static svn_error_t *
1853 open_file(const char *path,
1855 svn_revnum_t base_revision,
1856 apr_pool_t *file_pool,
1859 struct dir_baton_t *pb = parent_baton;
1860 struct edit_baton_t *eb = pb->eb;
1861 struct file_baton_t *fb;
1863 fb = make_file_baton(path, FALSE, pb, file_pool);
1866 if (pb->skip_children)
1868 else if (pb->repos_only)
1869 fb->repos_only = TRUE;
1872 struct svn_wc__db_info_t *info;
1873 SVN_ERR(ensure_local_info(pb, file_pool));
1875 info = svn_hash_gets(pb->local_info, fb->name);
1877 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1878 fb->repos_only = TRUE;
1880 if (!fb->repos_only)
1881 switch (info->status)
1883 case svn_wc__db_status_normal:
1885 case svn_wc__db_status_deleted:
1886 fb->repos_only = TRUE;
1887 if (!info->have_more_work)
1888 svn_hash_sets(pb->compared,
1889 apr_pstrdup(pb->pool, fb->name), "");
1891 case svn_wc__db_status_added:
1892 if (eb->ignore_ancestry)
1893 fb->ignoring_ancestry = TRUE;
1895 fb->repos_only = TRUE;
1898 SVN_ERR_MALFUNCTION();
1901 if (!fb->repos_only)
1903 /* Add this path to the parent directory's list of elements that
1904 have been compared. */
1905 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1906 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1910 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1912 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1913 NULL, NULL, NULL, &fb->base_checksum, NULL,
1914 NULL, NULL, &fb->base_props, NULL,
1915 eb->db, fb->local_abspath,
1916 fb->pool, fb->pool));
1918 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1922 NULL /* copyfrom src */,
1925 fb->pool, fb->pool));
1927 return SVN_NO_ERROR;
1930 /* An svn_delta_editor_t function. */
1931 static svn_error_t *
1932 apply_textdelta(void *file_baton,
1933 const char *base_checksum_hex,
1935 svn_txdelta_window_handler_t *handler,
1936 void **handler_baton)
1938 struct file_baton_t *fb = file_baton;
1939 struct edit_baton_t *eb = fb->eb;
1940 svn_stream_t *source;
1941 svn_stream_t *temp_stream;
1942 svn_checksum_t *repos_checksum = NULL;
1946 *handler = svn_delta_noop_window_handler;
1947 *handler_baton = NULL;
1948 return SVN_NO_ERROR;
1951 if (base_checksum_hex && fb->base_checksum)
1953 const svn_checksum_t *base_md5;
1954 SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
1955 base_checksum_hex, pool));
1957 SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
1958 eb->db, eb->anchor_abspath,
1962 if (! svn_checksum_match(repos_checksum, base_md5))
1964 /* ### I expect that there are some bad drivers out there
1965 ### that used to give bad results. We could look in
1966 ### working to see if the expected checksum matches and
1967 ### then return the pristine of that... But that only moves
1970 /* If needed: compare checksum obtained via md5 of working.
1971 And if they match set fb->base_checksum and fb->base_props */
1973 return svn_checksum_mismatch_err(
1977 _("Checksum mismatch for '%s'"),
1978 svn_dirent_local_style(fb->local_abspath,
1982 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1983 eb->db, fb->local_abspath,
1987 else if (fb->base_checksum)
1989 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1990 eb->db, fb->local_abspath,
1995 source = svn_stream_empty(pool);
1997 /* This is the file that will contain the pristine repository version. */
1998 SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
1999 svn_io_file_del_on_pool_cleanup,
2000 fb->pool, fb->pool));
2002 svn_txdelta_apply(source, temp_stream,
2004 fb->local_abspath /* error_info */,
2006 handler, handler_baton);
2008 return SVN_NO_ERROR;
2011 /* An svn_delta_editor_t function. When the file is closed we have a temporary
2012 * file containing a pristine version of the repository file. This can
2013 * be compared against the working copy.
2015 * Ignore TEXT_CHECKSUM.
2017 static svn_error_t *
2018 close_file(void *file_baton,
2019 const char *expected_md5_digest,
2022 struct file_baton_t *fb = file_baton;
2023 struct dir_baton_t *pb = fb->parent_baton;
2024 struct edit_baton_t *eb = fb->eb;
2025 apr_pool_t *scratch_pool = fb->pool;
2027 /* The repository information; constructed from BASE + Changes */
2028 const char *repos_file;
2029 apr_hash_t *repos_props;
2031 if (!fb->skip && expected_md5_digest != NULL)
2033 svn_checksum_t *expected_checksum;
2034 const svn_checksum_t *result_checksum;
2036 if (fb->temp_file_path)
2037 result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
2040 result_checksum = fb->base_checksum;
2042 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
2043 expected_md5_digest, scratch_pool));
2045 if (result_checksum->kind != svn_checksum_md5)
2046 SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
2047 eb->db, fb->local_abspath,
2049 scratch_pool, scratch_pool));
2051 if (!svn_checksum_match(expected_checksum, result_checksum))
2052 return svn_checksum_mismatch_err(
2056 _("Checksum mismatch for '%s'"),
2057 svn_dirent_local_style(fb->local_abspath,
2061 if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2062 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2065 apr_hash_t *prop_base;
2068 prop_base = apr_hash_make(scratch_pool);
2070 prop_base = fb->base_props;
2072 /* includes entry props */
2073 repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
2075 repos_file = fb->temp_file_path;
2078 assert(fb->base_checksum);
2079 SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
2080 eb->db, eb->anchor_abspath,
2082 scratch_pool, scratch_pool));
2088 /* Diff processor requested skipping information */
2090 else if (fb->repos_only)
2092 SVN_ERR(eb->processor->file_deleted(fb->relpath,
2102 /* Produce a diff of actual or pristine against repos */
2103 apr_hash_t *local_props;
2104 apr_array_header_t *prop_changes;
2105 const char *localfile;
2107 /* pb->local_info contains some information that might allow optimizing
2110 if (eb->diff_pristine)
2112 const svn_checksum_t *checksum;
2113 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
2114 NULL, &checksum, NULL, NULL,
2116 eb->db, fb->local_abspath,
2117 scratch_pool, scratch_pool));
2119 SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
2120 eb->db, eb->anchor_abspath,
2122 scratch_pool, scratch_pool));
2126 SVN_ERR(svn_wc__db_read_props(&local_props,
2127 eb->db, fb->local_abspath,
2128 scratch_pool, scratch_pool));
2130 /* a detranslated version of the working file */
2131 SVN_ERR(svn_wc__internal_translated_file(
2132 &localfile, fb->local_abspath, eb->db, fb->local_abspath,
2133 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
2134 eb->cancel_func, eb->cancel_baton,
2135 scratch_pool, scratch_pool));
2138 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
2142 /* ### as a good diff processor we should now only report changes, and
2143 report file_closed() in other cases */
2144 SVN_ERR(eb->processor->file_changed(fb->relpath,
2147 repos_file /* left file */,
2148 localfile /* right file */,
2149 repos_props /* left_props */,
2150 local_props /* right props */,
2151 TRUE /* ### file_modified */,
2158 if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2159 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2161 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2162 SVN_ERR(maybe_done(pb));
2163 return SVN_NO_ERROR;
2167 /* An svn_delta_editor_t function. */
2168 static svn_error_t *
2169 change_file_prop(void *file_baton,
2171 const svn_string_t *value,
2174 struct file_baton_t *fb = file_baton;
2175 svn_prop_t *propchange;
2176 svn_prop_kind_t propkind;
2178 propkind = svn_property_kind2(name);
2179 if (propkind == svn_prop_wc_kind)
2180 return SVN_NO_ERROR;
2181 else if (propkind == svn_prop_regular_kind)
2182 fb->has_propchange = TRUE;
2184 propchange = apr_array_push(fb->propchanges);
2185 propchange->name = apr_pstrdup(fb->pool, name);
2186 propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
2188 return SVN_NO_ERROR;
2192 /* An svn_delta_editor_t function. */
2193 static svn_error_t *
2194 change_dir_prop(void *dir_baton,
2196 const svn_string_t *value,
2199 struct dir_baton_t *db = dir_baton;
2200 svn_prop_t *propchange;
2201 svn_prop_kind_t propkind;
2203 propkind = svn_property_kind2(name);
2204 if (propkind == svn_prop_wc_kind)
2205 return SVN_NO_ERROR;
2206 else if (propkind == svn_prop_regular_kind)
2207 db->has_propchange = TRUE;
2209 propchange = apr_array_push(db->propchanges);
2210 propchange->name = apr_pstrdup(db->pool, name);
2211 propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2213 return SVN_NO_ERROR;
2217 /* An svn_delta_editor_t function. */
2218 static svn_error_t *
2219 close_edit(void *edit_baton,
2222 struct edit_baton_t *eb = edit_baton;
2224 if (!eb->root_opened)
2226 SVN_ERR(walk_local_nodes_diff(eb,
2230 NULL /* compared */,
2231 NULL /* No parent_baton */,
2235 return SVN_NO_ERROR;
2238 /* Public Interface */
2241 /* Create a diff editor and baton. */
2243 svn_wc__get_diff_editor(const svn_delta_editor_t **editor,
2245 svn_wc_context_t *wc_ctx,
2246 const char *anchor_abspath,
2249 svn_boolean_t ignore_ancestry,
2250 svn_boolean_t show_copies_as_adds,
2251 svn_boolean_t use_git_diff_format,
2252 svn_boolean_t use_text_base,
2253 svn_boolean_t reverse_order,
2254 svn_boolean_t server_performs_filtering,
2255 const apr_array_header_t *changelist_filter,
2256 const svn_wc_diff_callbacks4_t *callbacks,
2257 void *callback_baton,
2258 svn_cancel_func_t cancel_func,
2260 apr_pool_t *result_pool,
2261 apr_pool_t *scratch_pool)
2263 struct edit_baton_t *eb;
2265 svn_delta_editor_t *tree_editor;
2266 const svn_delta_editor_t *inner_editor;
2267 struct svn_wc__shim_fetch_baton_t *sfb;
2268 svn_delta_shim_callbacks_t *shim_callbacks =
2269 svn_delta_shim_callbacks_default(result_pool);
2271 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
2273 /* --git implies --show-copies-as-adds */
2274 if (use_git_diff_format)
2275 show_copies_as_adds = TRUE;
2277 SVN_ERR(make_edit_baton(&eb,
2279 anchor_abspath, target,
2280 callbacks, callback_baton,
2281 depth, ignore_ancestry, show_copies_as_adds,
2282 use_text_base, reverse_order, changelist_filter,
2283 cancel_func, cancel_baton,
2286 tree_editor = svn_delta_default_editor(eb->pool);
2288 tree_editor->set_target_revision = set_target_revision;
2289 tree_editor->open_root = open_root;
2290 tree_editor->delete_entry = delete_entry;
2291 tree_editor->add_directory = add_directory;
2292 tree_editor->open_directory = open_directory;
2293 tree_editor->close_directory = close_directory;
2294 tree_editor->add_file = add_file;
2295 tree_editor->open_file = open_file;
2296 tree_editor->apply_textdelta = apply_textdelta;
2297 tree_editor->change_file_prop = change_file_prop;
2298 tree_editor->change_dir_prop = change_dir_prop;
2299 tree_editor->close_file = close_file;
2300 tree_editor->close_edit = close_edit;
2302 inner_editor = tree_editor;
2305 if (!server_performs_filtering
2306 && depth == svn_depth_unknown)
2307 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2316 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
2324 sfb = apr_palloc(result_pool, sizeof(*sfb));
2325 sfb->db = wc_ctx->db;
2326 sfb->base_abspath = eb->anchor_abspath;
2327 sfb->fetch_base = TRUE;
2329 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2330 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2331 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2332 shim_callbacks->fetch_baton = sfb;
2335 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2336 NULL, NULL, shim_callbacks,
2337 result_pool, scratch_pool));
2339 return SVN_NO_ERROR;
2342 /* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
2344 /* baton for the svn_diff_tree_processor_t wrapper */
2345 typedef struct wc_diff_wrap_baton_t
2347 const svn_wc_diff_callbacks4_t *callbacks;
2348 void *callback_baton;
2350 svn_boolean_t walk_deleted_dirs;
2352 apr_pool_t *result_pool;
2353 const char *empty_file;
2355 } wc_diff_wrap_baton_t;
2357 static svn_error_t *
2358 wrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
2359 apr_pool_t *scratch_pool)
2362 return SVN_NO_ERROR;
2364 /* Create a unique file in the tempdir */
2365 SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
2366 svn_io_file_del_on_pool_cleanup,
2367 wb->result_pool, scratch_pool));
2369 return SVN_NO_ERROR;
2372 /* svn_diff_tree_processor_t function */
2373 static svn_error_t *
2374 wrap_dir_opened(void **new_dir_baton,
2375 svn_boolean_t *skip,
2376 svn_boolean_t *skip_children,
2377 const char *relpath,
2378 const svn_diff_source_t *left_source,
2379 const svn_diff_source_t *right_source,
2380 const svn_diff_source_t *copyfrom_source,
2381 void *parent_dir_baton,
2382 const svn_diff_tree_processor_t *processor,
2383 apr_pool_t *result_pool,
2384 apr_pool_t *scratch_pool)
2386 wc_diff_wrap_baton_t *wb = processor->baton;
2387 svn_boolean_t tree_conflicted = FALSE;
2389 assert(left_source || right_source);
2390 assert(!copyfrom_source || !right_source);
2392 /* Maybe store state and tree_conflicted in baton? */
2393 if (left_source != NULL)
2395 /* Open for change or delete */
2396 SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
2399 ? right_source->revision
2401 ? left_source->revision
2402 : SVN_INVALID_REVNUM),
2406 if (! right_source && !wb->walk_deleted_dirs)
2407 *skip_children = TRUE;
2409 else /* left_source == NULL -> Add */
2411 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2412 SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
2413 skip, skip_children,
2415 right_source->revision,
2417 ? copyfrom_source->repos_relpath
2420 ? copyfrom_source->revision
2421 : SVN_INVALID_REVNUM,
2426 *new_dir_baton = NULL;
2428 return SVN_NO_ERROR;
2431 /* svn_diff_tree_processor_t function */
2432 static svn_error_t *
2433 wrap_dir_added(const char *relpath,
2434 const svn_diff_source_t *right_source,
2435 const svn_diff_source_t *copyfrom_source,
2436 /*const*/ apr_hash_t *copyfrom_props,
2437 /*const*/ apr_hash_t *right_props,
2439 const svn_diff_tree_processor_t *processor,
2440 apr_pool_t *scratch_pool)
2442 wc_diff_wrap_baton_t *wb = processor->baton;
2443 svn_boolean_t tree_conflicted = FALSE;
2444 svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
2445 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2446 apr_hash_t *pristine_props = copyfrom_props;
2447 apr_array_header_t *prop_changes = NULL;
2449 if (right_props && apr_hash_count(right_props))
2451 if (!pristine_props)
2452 pristine_props = apr_hash_make(scratch_pool);
2454 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
2457 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
2460 TRUE /* dir_was_added */,
2461 prop_changes, pristine_props,
2466 SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
2469 TRUE /* dir_was_added */,
2472 return SVN_NO_ERROR;
2475 /* svn_diff_tree_processor_t function */
2476 static svn_error_t *
2477 wrap_dir_deleted(const char *relpath,
2478 const svn_diff_source_t *left_source,
2479 /*const*/ apr_hash_t *left_props,
2481 const svn_diff_tree_processor_t *processor,
2482 apr_pool_t *scratch_pool)
2484 wc_diff_wrap_baton_t *wb = processor->baton;
2485 svn_boolean_t tree_conflicted = FALSE;
2486 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2488 SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
2493 return SVN_NO_ERROR;
2496 /* svn_diff_tree_processor_t function */
2497 static svn_error_t *
2498 wrap_dir_closed(const char *relpath,
2499 const svn_diff_source_t *left_source,
2500 const svn_diff_source_t *right_source,
2502 const svn_diff_tree_processor_t *processor,
2503 apr_pool_t *scratch_pool)
2505 wc_diff_wrap_baton_t *wb = processor->baton;
2507 /* No previous implementations provided these arguments, so we
2508 are not providing them either */
2509 SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
2515 return SVN_NO_ERROR;
2518 /* svn_diff_tree_processor_t function */
2519 static svn_error_t *
2520 wrap_dir_changed(const char *relpath,
2521 const svn_diff_source_t *left_source,
2522 const svn_diff_source_t *right_source,
2523 /*const*/ apr_hash_t *left_props,
2524 /*const*/ apr_hash_t *right_props,
2525 const apr_array_header_t *prop_changes,
2527 const struct svn_diff_tree_processor_t *processor,
2528 apr_pool_t *scratch_pool)
2530 wc_diff_wrap_baton_t *wb = processor->baton;
2531 svn_boolean_t tree_conflicted = FALSE;
2532 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2534 assert(left_source && right_source);
2536 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
2538 FALSE /* dir_was_added */,
2544 /* And call dir_closed, etc */
2545 SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
2546 dir_baton, processor,
2548 return SVN_NO_ERROR;
2551 /* svn_diff_tree_processor_t function */
2552 static svn_error_t *
2553 wrap_file_opened(void **new_file_baton,
2554 svn_boolean_t *skip,
2555 const char *relpath,
2556 const svn_diff_source_t *left_source,
2557 const svn_diff_source_t *right_source,
2558 const svn_diff_source_t *copyfrom_source,
2560 const svn_diff_tree_processor_t *processor,
2561 apr_pool_t *result_pool,
2562 apr_pool_t *scratch_pool)
2564 wc_diff_wrap_baton_t *wb = processor->baton;
2565 svn_boolean_t tree_conflicted = FALSE;
2567 if (left_source) /* If ! added */
2568 SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
2570 ? right_source->revision
2572 ? left_source->revision
2573 : SVN_INVALID_REVNUM),
2574 wb->callback_baton, scratch_pool));
2576 /* No old implementation used the output arguments for notify */
2578 *new_file_baton = NULL;
2579 return SVN_NO_ERROR;
2582 /* svn_diff_tree_processor_t function */
2583 static svn_error_t *
2584 wrap_file_added(const char *relpath,
2585 const svn_diff_source_t *copyfrom_source,
2586 const svn_diff_source_t *right_source,
2587 const char *copyfrom_file,
2588 const char *right_file,
2589 /*const*/ apr_hash_t *copyfrom_props,
2590 /*const*/ apr_hash_t *right_props,
2592 const svn_diff_tree_processor_t *processor,
2593 apr_pool_t *scratch_pool)
2595 wc_diff_wrap_baton_t *wb = processor->baton;
2596 svn_boolean_t tree_conflicted = FALSE;
2597 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2598 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2599 apr_array_header_t *prop_changes;
2601 if (! copyfrom_props)
2602 copyfrom_props = apr_hash_make(scratch_pool);
2604 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
2607 if (! copyfrom_source)
2608 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2610 SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
2617 right_source->revision,
2619 ? svn_prop_get_value(copyfrom_props,
2623 ? svn_prop_get_value(right_props,
2627 ? copyfrom_source->repos_relpath
2630 ? copyfrom_source->revision
2631 : SVN_INVALID_REVNUM,
2632 prop_changes, copyfrom_props,
2635 return SVN_NO_ERROR;
2638 static svn_error_t *
2639 wrap_file_deleted(const char *relpath,
2640 const svn_diff_source_t *left_source,
2641 const char *left_file,
2642 apr_hash_t *left_props,
2644 const svn_diff_tree_processor_t *processor,
2645 apr_pool_t *scratch_pool)
2647 wc_diff_wrap_baton_t *wb = processor->baton;
2648 svn_boolean_t tree_conflicted = FALSE;
2649 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2651 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2653 SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
2655 left_file, wb->empty_file,
2657 ? svn_prop_get_value(left_props,
2664 return SVN_NO_ERROR;
2667 /* svn_diff_tree_processor_t function */
2668 static svn_error_t *
2669 wrap_file_changed(const char *relpath,
2670 const svn_diff_source_t *left_source,
2671 const svn_diff_source_t *right_source,
2672 const char *left_file,
2673 const char *right_file,
2674 /*const*/ apr_hash_t *left_props,
2675 /*const*/ apr_hash_t *right_props,
2676 svn_boolean_t file_modified,
2677 const apr_array_header_t *prop_changes,
2679 const svn_diff_tree_processor_t *processor,
2680 apr_pool_t *scratch_pool)
2682 wc_diff_wrap_baton_t *wb = processor->baton;
2683 svn_boolean_t tree_conflicted = FALSE;
2684 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2685 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2687 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2689 assert(left_source && right_source);
2691 SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
2693 file_modified ? left_file : NULL,
2694 file_modified ? right_file : NULL,
2695 left_source->revision,
2696 right_source->revision,
2698 ? svn_prop_get_value(left_props,
2702 ? svn_prop_get_value(right_props,
2709 return SVN_NO_ERROR;
2713 svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
2714 const svn_wc_diff_callbacks4_t *callbacks,
2715 void *callback_baton,
2716 svn_boolean_t walk_deleted_dirs,
2717 apr_pool_t *result_pool,
2718 apr_pool_t *scratch_pool)
2720 wc_diff_wrap_baton_t *wrap_baton;
2721 svn_diff_tree_processor_t *processor;
2723 wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
2725 wrap_baton->result_pool = result_pool;
2726 wrap_baton->callbacks = callbacks;
2727 wrap_baton->callback_baton = callback_baton;
2728 wrap_baton->empty_file = NULL;
2729 wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
2731 processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
2733 processor->dir_opened = wrap_dir_opened;
2734 processor->dir_added = wrap_dir_added;
2735 processor->dir_deleted = wrap_dir_deleted;
2736 processor->dir_changed = wrap_dir_changed;
2737 processor->dir_closed = wrap_dir_closed;
2739 processor->file_opened = wrap_file_opened;
2740 processor->file_added = wrap_file_added;
2741 processor->file_deleted = wrap_file_deleted;
2742 processor->file_changed = wrap_file_changed;
2743 /*processor->file_closed = wrap_file_closed*/; /* Not needed */
2745 *diff_processor = processor;
2746 return SVN_NO_ERROR;