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 /* Cancel function/baton */
118 svn_cancel_func_t cancel_func;
124 /* Directory level baton.
128 /* Reference to parent directory baton (or NULL for the root) */
129 struct dir_baton_t *parent_baton;
131 /* The depth at which this directory should be diffed. */
134 /* The name and path of this directory as if they would be/are in the
135 local working copy. */
138 const char *local_abspath;
140 /* TRUE if the file is added by the editor drive. */
142 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
143 svn_boolean_t repos_only;
144 /* TRUE if the node is to be compared with an unrelated node*/
145 svn_boolean_t ignoring_ancestry;
147 /* Processor state */
150 svn_boolean_t skip_children;
152 svn_diff_source_t *left_src;
153 svn_diff_source_t *right_src;
155 apr_hash_t *local_info;
157 /* A hash containing the basenames of the nodes reported deleted by the
158 repository (or NULL for no values). */
161 /* Identifies those directory elements that get compared while running
162 the crawler. These elements should not be compared again when
163 recursively looking for local modifications.
165 This hash maps the basename of the node to an unimportant value.
167 If the directory's properties have been compared, an item with hash
168 key of "" will be present in the hash. */
169 apr_hash_t *compared;
171 /* The list of incoming BASE->repos propchanges. */
172 apr_array_header_t *propchanges;
174 /* Has a change on regular properties */
175 svn_boolean_t has_propchange;
177 /* The overall crawler editor baton. */
178 struct edit_baton_t *eb;
188 struct dir_baton_t *parent_baton;
190 /* The name and path of this file as if they would be/are in the
191 parent directory, diff session and local working copy. */
194 const char *local_abspath;
196 /* Processor state */
200 /* TRUE if the file is added by the editor drive. */
202 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
203 svn_boolean_t repos_only;
204 /* TRUE if the node is to be compared with an unrelated node*/
205 svn_boolean_t ignoring_ancestry;
207 const svn_diff_source_t *left_src;
208 const svn_diff_source_t *right_src;
210 /* The list of incoming BASE->repos propchanges. */
211 apr_array_header_t *propchanges;
213 /* Has a change on regular properties */
214 svn_boolean_t has_propchange;
216 /* The current BASE checksum and props */
217 const svn_checksum_t *base_checksum;
218 apr_hash_t *base_props;
220 /* The resulting from apply_textdelta */
221 const char *temp_file_path;
222 unsigned char result_digest[APR_MD5_DIGESTSIZE];
224 /* The overall crawler editor baton. */
225 struct edit_baton_t *eb;
230 /* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths
231 * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON
232 * define the callbacks to compare files. DEPTH defines if and how to
233 * descend into subdirectories; see public doc string for exactly how.
234 * IGNORE_ANCESTRY defines whether to utilize node ancestry when
235 * calculating diffs. USE_TEXT_BASE defines whether to compare
236 * against working files or text-bases. REVERSE_ORDER defines which
237 * direction to perform the diff.
240 make_edit_baton(struct edit_baton_t **edit_baton,
242 const char *anchor_abspath,
244 const svn_diff_tree_processor_t *processor,
246 svn_boolean_t ignore_ancestry,
247 svn_boolean_t show_copies_as_adds,
248 svn_boolean_t use_text_base,
249 svn_boolean_t reverse_order,
250 svn_cancel_func_t cancel_func,
254 struct edit_baton_t *eb;
256 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
259 processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
261 /* --show-copies-as-adds implies --notice-ancestry */
262 if (show_copies_as_adds)
263 ignore_ancestry = FALSE;
265 if (! show_copies_as_adds)
266 processor = svn_diff__tree_processor_copy_as_changed_create(processor,
269 eb = apr_pcalloc(pool, sizeof(*eb));
271 eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
272 eb->target = apr_pstrdup(pool, target);
273 eb->processor = processor;
275 eb->ignore_ancestry = ignore_ancestry;
276 eb->local_before_remote = reverse_order;
277 eb->diff_pristine = use_text_base;
278 eb->cancel_func = cancel_func;
279 eb->cancel_baton = cancel_baton;
286 /* Create a new directory baton. PATH is the directory path,
287 * including anchor_path. ADDED is set if this directory is being
288 * added rather than replaced. PARENT_BATON is the baton of the
289 * parent directory, it will be null if this is the root of the
290 * comparison hierarchy. The directory and its parent may or may not
291 * exist in the working copy. EDIT_BATON is the overall crawler
294 static struct dir_baton_t *
295 make_dir_baton(const char *path,
296 struct dir_baton_t *parent_baton,
297 struct edit_baton_t *eb,
300 apr_pool_t *result_pool)
302 apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
304 struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
306 db->parent_baton = parent_baton;
308 /* Allocate 1 string for using as 3 strings */
309 db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
310 db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
311 db->name = svn_dirent_basename(db->relpath, NULL);
317 db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
318 db->compared = apr_hash_make(dir_pool);
320 if (parent_baton != NULL)
322 parent_baton->users++;
330 /* Create a new file baton. PATH is the file path, including
331 * anchor_path. ADDED is set if this file is being added rather than
332 * replaced. PARENT_BATON is the baton of the parent directory.
333 * The directory and its parent may or may not exist in the working copy.
335 static struct file_baton_t *
336 make_file_baton(const char *path,
338 struct dir_baton_t *parent_baton,
339 apr_pool_t *result_pool)
341 apr_pool_t *file_pool = svn_pool_create(result_pool);
342 struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
343 struct edit_baton_t *eb = parent_baton->eb;
346 fb->parent_baton = parent_baton;
347 fb->parent_baton->users++;
349 /* Allocate 1 string for using as 3 strings */
350 fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
351 fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
352 fb->name = svn_dirent_basename(fb->relpath, NULL);
355 fb->pool = file_pool;
356 fb->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
361 /* Destroy DB when there are no more registered users */
363 maybe_done(struct dir_baton_t *db)
369 struct dir_baton_t *pb = db->parent_baton;
371 svn_pool_clear(db->pool);
374 SVN_ERR(maybe_done(pb));
380 /* Standard check to see if a node is represented in the local working copy */
381 #define NOT_PRESENT(status) \
382 ((status) == svn_wc__db_status_not_present \
383 || (status) == svn_wc__db_status_excluded \
384 || (status) == svn_wc__db_status_server_excluded)
387 svn_wc__diff_base_working_diff(svn_wc__db_t *db,
388 const char *local_abspath,
390 svn_revnum_t revision,
391 const svn_diff_tree_processor_t *processor,
392 void *processor_dir_baton,
393 svn_boolean_t diff_pristine,
394 svn_cancel_func_t cancel_func,
396 apr_pool_t *scratch_pool)
398 void *file_baton = NULL;
399 svn_boolean_t skip = FALSE;
400 svn_wc__db_status_t status;
401 svn_revnum_t db_revision;
402 svn_boolean_t had_props;
403 svn_boolean_t props_mod;
404 svn_boolean_t files_same = FALSE;
405 svn_wc__db_status_t base_status;
406 const svn_checksum_t *working_checksum;
407 const svn_checksum_t *checksum;
408 svn_filesize_t recorded_size;
409 apr_time_t recorded_time;
410 const char *pristine_file;
411 const char *local_file;
412 svn_diff_source_t *left_src;
413 svn_diff_source_t *right_src;
414 apr_hash_t *base_props;
415 apr_hash_t *local_props;
416 apr_array_header_t *prop_changes;
418 SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
419 NULL, NULL, NULL, NULL, &working_checksum, NULL,
420 NULL, NULL, NULL, NULL, NULL, &recorded_size,
421 &recorded_time, NULL, NULL, NULL,
422 &had_props, &props_mod, NULL, NULL, NULL,
423 db, local_abspath, scratch_pool, scratch_pool));
424 checksum = working_checksum;
426 assert(status == svn_wc__db_status_normal
427 || status == svn_wc__db_status_added
428 || (status == svn_wc__db_status_deleted && diff_pristine));
430 if (status != svn_wc__db_status_normal)
432 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
433 NULL, NULL, NULL, NULL, NULL, NULL,
434 NULL, &checksum, NULL, NULL, &had_props,
437 scratch_pool, scratch_pool));
438 recorded_size = SVN_INVALID_FILESIZE;
440 props_mod = TRUE; /* Requires compare */
442 else if (diff_pristine)
446 const svn_io_dirent2_t *dirent;
448 /* Verify truename to mimic status for iota/IOTA difference on Windows */
449 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
450 TRUE /* verify truename */,
451 TRUE /* ingore_enoent */,
452 scratch_pool, scratch_pool));
454 /* If a file does not exist on disk (missing/obstructed) then we
455 can't provide a text diff */
456 if (dirent->kind != svn_node_file
457 || (dirent->kind == svn_node_file
458 && dirent->filesize == recorded_size
459 && dirent->mtime == recorded_time))
465 if (files_same && !props_mod)
466 return SVN_NO_ERROR; /* Cheap exit */
470 if (!SVN_IS_VALID_REVNUM(revision))
471 revision = db_revision;
473 left_src = svn_diff__source_create(revision, scratch_pool);
474 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
476 SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
479 NULL /* copyfrom_src */,
482 scratch_pool, scratch_pool));
487 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
488 db, local_abspath, checksum,
489 scratch_pool, scratch_pool));
492 SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
495 scratch_pool, scratch_pool));
496 else if (! (had_props || props_mod))
497 local_file = local_abspath;
499 local_file = pristine_file;
501 SVN_ERR(svn_wc__internal_translated_file(
502 &local_file, local_abspath,
504 SVN_WC_TRANSLATE_TO_NF
505 | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
506 cancel_func, cancel_baton,
507 scratch_pool, scratch_pool));
510 SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
511 pristine_file, scratch_pool));
514 SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
515 scratch_pool, scratch_pool));
517 base_props = apr_hash_make(scratch_pool);
519 if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
520 local_props = base_props;
521 else if (diff_pristine)
522 SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
523 scratch_pool, scratch_pool));
525 SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
526 scratch_pool, scratch_pool));
528 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
530 if (prop_changes->nelts || !files_same)
532 SVN_ERR(processor->file_changed(relpath,
547 SVN_ERR(processor->file_closed(relpath,
559 ensure_local_info(struct dir_baton_t *db,
560 apr_pool_t *scratch_pool)
562 apr_hash_t *conflicts;
567 SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
568 db->eb->db, db->local_abspath,
569 db->pool, scratch_pool));
574 /* Called when the directory is closed to compare any elements that have
575 * not yet been compared. This identifies local, working copy only
576 * changes. At this stage we are dealing with files/directories that do
577 * exist in the working copy.
579 * DIR_BATON is the baton for the directory.
582 walk_local_nodes_diff(struct edit_baton_t *eb,
583 const char *local_abspath,
586 apr_hash_t *compared,
588 apr_pool_t *scratch_pool)
590 svn_wc__db_t *db = eb->db;
591 svn_boolean_t in_anchor_not_target;
592 apr_pool_t *iterpool;
593 void *dir_baton = NULL;
594 svn_boolean_t skip = FALSE;
595 svn_boolean_t skip_children = FALSE;
596 svn_revnum_t revision;
597 svn_boolean_t props_mod;
598 svn_diff_source_t *left_src;
599 svn_diff_source_t *right_src;
601 /* Everything we do below is useless if we are comparing to BASE. */
602 if (eb->diff_pristine)
605 /* Determine if this is the anchor directory if the anchor is different
606 to the target. When the target is a file, the anchor is the parent
607 directory and if this is that directory the non-target entries must be
609 in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
611 iterpool = svn_pool_create(scratch_pool);
613 SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
614 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
615 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
616 NULL, &props_mod, NULL, NULL, NULL,
617 db, local_abspath, scratch_pool, scratch_pool));
619 left_src = svn_diff__source_create(revision, scratch_pool);
620 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
624 dir_baton = parent_baton;
627 else if (!in_anchor_not_target)
628 SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
632 NULL /* copyfrom_src */,
635 scratch_pool, scratch_pool));
638 if (!skip_children && depth != svn_depth_empty)
641 apr_hash_t *conflicts;
642 apr_array_header_t *children;
643 svn_depth_t depth_below_here = depth;
644 svn_boolean_t diff_files;
645 svn_boolean_t diff_dirs;
648 if (depth_below_here == svn_depth_immediates)
649 depth_below_here = svn_depth_empty;
651 diff_files = (depth == svn_depth_unknown
652 || depth >= svn_depth_files);
653 diff_dirs = (depth == svn_depth_unknown
654 || depth >= svn_depth_immediates);
656 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
658 scratch_pool, iterpool));
660 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
663 for (i = 0; i < children->nelts; i++)
665 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
667 const char *name = item->key;
668 struct svn_wc__db_info_t *info = item->value;
670 const char *child_abspath;
671 const char *child_relpath;
672 svn_boolean_t repos_only;
673 svn_boolean_t local_only;
674 svn_node_kind_t base_kind;
677 SVN_ERR(eb->cancel_func(eb->cancel_baton));
679 /* In the anchor directory, if the anchor is not the target then all
680 entries other than the target should not be diff'd. Running diff
681 on one file in a directory should not diff other files in that
683 if (in_anchor_not_target && strcmp(eb->target, name))
686 if (compared && svn_hash_gets(compared, name))
689 if (NOT_PRESENT(info->status))
692 assert(info->status == svn_wc__db_status_normal
693 || info->status == svn_wc__db_status_added
694 || info->status == svn_wc__db_status_deleted);
696 svn_pool_clear(iterpool);
697 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
698 child_relpath = svn_relpath_join(path, name, iterpool);
703 if (!info->have_base)
705 local_only = TRUE; /* Only report additions */
707 else if (info->status == svn_wc__db_status_normal)
710 base_kind = info->kind;
712 else if (info->status == svn_wc__db_status_deleted
713 && (!eb->diff_pristine || !info->have_more_work))
715 svn_wc__db_status_t base_status;
717 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
718 NULL, NULL, NULL, NULL, NULL,
719 NULL, NULL, NULL, NULL, NULL,
722 iterpool, iterpool));
724 if (NOT_PRESENT(base_status))
729 /* working status is either added or deleted */
730 svn_wc__db_status_t base_status;
732 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
733 NULL, NULL, NULL, NULL, NULL,
734 NULL, NULL, NULL, NULL, NULL,
737 iterpool, iterpool));
739 if (NOT_PRESENT(base_status))
741 else if (base_kind != info->kind || !eb->ignore_ancestry)
748 if (eb->local_before_remote && local_only)
750 if (info->kind == svn_node_file && diff_files)
751 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
753 eb->processor, dir_baton,
758 else if (info->kind == svn_node_dir && diff_dirs)
759 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
762 eb->processor, dir_baton,
771 /* Report repository form deleted */
772 if (base_kind == svn_node_file && diff_files)
773 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
774 child_relpath, eb->revnum,
775 eb->processor, dir_baton,
777 else if (base_kind == svn_node_dir && diff_dirs)
778 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
779 child_relpath, eb->revnum,
781 eb->processor, dir_baton,
786 else if (!local_only) /* Not local only nor remote only */
788 /* Diff base against actual */
789 if (info->kind == svn_node_file && diff_files)
791 if (info->status != svn_wc__db_status_normal
792 || !eb->diff_pristine)
794 SVN_ERR(svn_wc__diff_base_working_diff(
798 eb->processor, dir_baton,
805 else if (info->kind == svn_node_dir && diff_dirs)
806 SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
814 if (!eb->local_before_remote && local_only)
816 if (info->kind == svn_node_file && diff_files)
817 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
819 eb->processor, dir_baton,
824 else if (info->kind == svn_node_dir && diff_dirs)
825 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
826 child_relpath, depth_below_here,
827 eb->processor, dir_baton,
839 /* Check for local property mods on this directory, if we haven't
840 already reported them. */
842 && ! in_anchor_not_target
845 apr_array_header_t *propchanges;
846 apr_hash_t *left_props;
847 apr_hash_t *right_props;
849 SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
851 scratch_pool, scratch_pool));
853 right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
855 SVN_ERR(eb->processor->dir_changed(path,
866 SVN_ERR(eb->processor->dir_closed(path,
873 svn_pool_destroy(iterpool);
879 svn_wc__diff_local_only_file(svn_wc__db_t *db,
880 const char *local_abspath,
882 const svn_diff_tree_processor_t *processor,
883 void *processor_parent_baton,
884 svn_boolean_t diff_pristine,
885 svn_cancel_func_t cancel_func,
887 apr_pool_t *scratch_pool)
889 svn_diff_source_t *right_src;
890 svn_diff_source_t *copyfrom_src = NULL;
891 svn_wc__db_status_t status;
892 svn_node_kind_t kind;
893 const svn_checksum_t *checksum;
894 const char *original_repos_relpath;
895 svn_revnum_t original_revision;
896 svn_boolean_t had_props;
897 svn_boolean_t props_mod;
898 apr_hash_t *pristine_props;
899 apr_hash_t *right_props = NULL;
900 const char *pristine_file;
901 const char *translated_file;
902 svn_revnum_t revision;
903 void *file_baton = NULL;
904 svn_boolean_t skip = FALSE;
905 svn_boolean_t file_mod = TRUE;
907 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
908 NULL, NULL, NULL, NULL, &checksum, NULL,
909 &original_repos_relpath, NULL, NULL,
910 &original_revision, NULL, NULL, NULL,
911 NULL, NULL, NULL, &had_props,
912 &props_mod, NULL, NULL, NULL,
914 scratch_pool, scratch_pool));
916 assert(kind == svn_node_file
917 && (status == svn_wc__db_status_normal
918 || status == svn_wc__db_status_added
919 || (status == svn_wc__db_status_deleted && diff_pristine)));
922 if (status == svn_wc__db_status_deleted)
924 assert(diff_pristine);
926 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
927 NULL, &checksum, NULL, &had_props,
930 scratch_pool, scratch_pool));
934 pristine_props = apr_hash_make(scratch_pool);
936 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
938 scratch_pool, scratch_pool));
940 if (original_repos_relpath)
942 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
943 copyfrom_src->repos_relpath = original_repos_relpath;
946 if (props_mod || !SVN_IS_VALID_REVNUM(revision))
947 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
953 SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
954 FALSE, scratch_pool));
957 right_src = svn_diff__source_create(revision, scratch_pool);
959 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
962 SVN_ERR(processor->file_opened(&file_baton, &skip,
964 NULL /* left_source */,
967 processor_parent_baton,
969 scratch_pool, scratch_pool));
974 if (props_mod && !diff_pristine)
975 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
976 scratch_pool, scratch_pool));
978 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
981 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
982 checksum, scratch_pool, scratch_pool));
984 pristine_file = NULL;
988 translated_file = pristine_file; /* No translation needed */
992 SVN_ERR(svn_wc__internal_translated_file(
993 &translated_file, local_abspath, db, local_abspath,
994 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
995 cancel_func, cancel_baton,
996 scratch_pool, scratch_pool));
999 SVN_ERR(processor->file_added(relpath,
1014 return SVN_NO_ERROR;
1018 svn_wc__diff_local_only_dir(svn_wc__db_t *db,
1019 const char *local_abspath,
1020 const char *relpath,
1022 const svn_diff_tree_processor_t *processor,
1023 void *processor_parent_baton,
1024 svn_boolean_t diff_pristine,
1025 svn_cancel_func_t cancel_func,
1027 apr_pool_t *scratch_pool)
1029 svn_wc__db_status_t status;
1030 svn_node_kind_t kind;
1031 svn_boolean_t had_props;
1032 svn_boolean_t props_mod;
1033 const char *original_repos_relpath;
1034 svn_revnum_t original_revision;
1035 svn_diff_source_t *copyfrom_src = NULL;
1036 apr_hash_t *pristine_props;
1037 const apr_array_header_t *children;
1039 apr_pool_t *iterpool;
1041 svn_boolean_t skip = FALSE;
1042 svn_boolean_t skip_children = FALSE;
1043 svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
1045 svn_depth_t depth_below_here = depth;
1047 apr_hash_t *conflicts;
1049 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
1050 NULL, NULL, NULL, NULL, NULL, NULL,
1051 &original_repos_relpath, NULL, NULL,
1052 &original_revision, NULL, NULL, NULL,
1053 NULL, NULL, NULL, &had_props,
1054 &props_mod, NULL, NULL, NULL,
1056 scratch_pool, scratch_pool));
1057 if (original_repos_relpath)
1059 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
1060 copyfrom_src->repos_relpath = original_repos_relpath;
1063 /* svn_wc__db_status_incomplete should never happen, as the result won't be
1064 stable or guaranteed related to what is in the repository for this
1065 revision, but without this it would be hard to diagnose that status... */
1066 assert(kind == svn_node_dir
1067 && (status == svn_wc__db_status_normal
1068 || status == svn_wc__db_status_incomplete
1069 || status == svn_wc__db_status_added
1070 || (status == svn_wc__db_status_deleted && diff_pristine)));
1072 if (status == svn_wc__db_status_deleted)
1074 assert(diff_pristine);
1076 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1077 NULL, NULL, NULL, &had_props,
1080 scratch_pool, scratch_pool));
1083 else if (!had_props)
1084 pristine_props = apr_hash_make(scratch_pool);
1086 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
1088 scratch_pool, scratch_pool));
1090 /* Report the addition of the directory's contents. */
1091 iterpool = svn_pool_create(scratch_pool);
1093 SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
1098 processor_parent_baton,
1100 scratch_pool, iterpool));
1101 /* ### skip_children is not used */
1103 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath,
1104 scratch_pool, iterpool));
1106 if (depth_below_here == svn_depth_immediates)
1107 depth_below_here = svn_depth_empty;
1109 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1112 for (i = 0; i < children->nelts; i++)
1114 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
1115 const char *name = item->key;
1116 struct svn_wc__db_info_t *info = item->value;
1117 const char *child_abspath;
1118 const char *child_relpath;
1120 svn_pool_clear(iterpool);
1123 SVN_ERR(cancel_func(cancel_baton));
1125 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1127 if (NOT_PRESENT(info->status))
1132 /* If comparing against WORKING, skip entries that are
1133 schedule-deleted - they don't really exist. */
1134 if (!diff_pristine && info->status == svn_wc__db_status_deleted)
1137 child_relpath = svn_relpath_join(relpath, name, iterpool);
1142 case svn_node_symlink:
1143 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
1147 cancel_func, cancel_baton,
1152 if (depth > svn_depth_files || depth == svn_depth_unknown)
1154 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
1155 child_relpath, depth_below_here,
1158 cancel_func, cancel_baton,
1170 apr_hash_t *right_props;
1172 if (props_mod && !diff_pristine)
1173 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1174 scratch_pool, scratch_pool));
1176 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1178 SVN_ERR(processor->dir_added(relpath,
1189 svn_pool_destroy(iterpool);
1191 return SVN_NO_ERROR;
1194 /* Reports local changes. */
1195 static svn_error_t *
1196 handle_local_only(struct dir_baton_t *pb,
1198 apr_pool_t *scratch_pool)
1200 struct edit_baton_t *eb = pb->eb;
1201 const struct svn_wc__db_info_t *info;
1202 svn_boolean_t repos_delete = (pb->deletes
1203 && svn_hash_gets(pb->deletes, name));
1205 assert(!strchr(name, '/'));
1206 assert(!pb->added || eb->ignore_ancestry);
1208 if (pb->skip_children)
1209 return SVN_NO_ERROR;
1211 SVN_ERR(ensure_local_info(pb, scratch_pool));
1213 info = svn_hash_gets(pb->local_info, name);
1215 if (info == NULL || NOT_PRESENT(info->status))
1216 return SVN_NO_ERROR;
1218 switch (info->status)
1220 case svn_wc__db_status_incomplete:
1221 return SVN_NO_ERROR; /* Not local only */
1223 case svn_wc__db_status_normal:
1225 return SVN_NO_ERROR; /* Local and remote */
1226 svn_hash_sets(pb->deletes, name, NULL);
1229 case svn_wc__db_status_deleted:
1230 if (!(eb->diff_pristine && repos_delete))
1231 return SVN_NO_ERROR;
1234 case svn_wc__db_status_added:
1239 if (info->kind == svn_node_dir)
1243 if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
1246 depth = svn_depth_empty;
1248 SVN_ERR(svn_wc__diff_local_only_dir(
1250 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1251 svn_relpath_join(pb->relpath, name, scratch_pool),
1252 repos_delete ? svn_depth_infinity : depth,
1253 eb->processor, pb->pdb,
1255 eb->cancel_func, eb->cancel_baton,
1259 SVN_ERR(svn_wc__diff_local_only_file(
1261 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1262 svn_relpath_join(pb->relpath, name, scratch_pool),
1263 eb->processor, pb->pdb,
1265 eb->cancel_func, eb->cancel_baton,
1268 return SVN_NO_ERROR;
1271 /* Reports a file LOCAL_ABSPATH in BASE as deleted */
1273 svn_wc__diff_base_only_file(svn_wc__db_t *db,
1274 const char *local_abspath,
1275 const char *relpath,
1276 svn_revnum_t revision,
1277 const svn_diff_tree_processor_t *processor,
1278 void *processor_parent_baton,
1279 apr_pool_t *scratch_pool)
1281 svn_wc__db_status_t status;
1282 svn_node_kind_t kind;
1283 const svn_checksum_t *checksum;
1285 void *file_baton = NULL;
1286 svn_boolean_t skip = FALSE;
1287 svn_diff_source_t *left_src;
1288 const char *pristine_file;
1290 SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
1291 SVN_IS_VALID_REVNUM(revision)
1293 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1294 &checksum, NULL, NULL, NULL, &props, NULL,
1296 scratch_pool, scratch_pool));
1298 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
1299 && kind == svn_node_file
1302 left_src = svn_diff__source_create(revision, scratch_pool);
1304 SVN_ERR(processor->file_opened(&file_baton, &skip,
1307 NULL /* right_src */,
1308 NULL /* copyfrom_source */,
1309 processor_parent_baton,
1311 scratch_pool, scratch_pool));
1314 return SVN_NO_ERROR;
1316 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
1317 db, local_abspath, checksum,
1318 scratch_pool, scratch_pool));
1320 SVN_ERR(processor->file_deleted(relpath,
1328 return SVN_NO_ERROR;
1332 svn_wc__diff_base_only_dir(svn_wc__db_t *db,
1333 const char *local_abspath,
1334 const char *relpath,
1335 svn_revnum_t revision,
1337 const svn_diff_tree_processor_t *processor,
1338 void *processor_parent_baton,
1339 svn_cancel_func_t cancel_func,
1341 apr_pool_t *scratch_pool)
1343 void *dir_baton = NULL;
1344 svn_boolean_t skip = FALSE;
1345 svn_boolean_t skip_children = FALSE;
1346 svn_diff_source_t *left_src;
1347 svn_revnum_t report_rev = revision;
1349 if (!SVN_IS_VALID_REVNUM(report_rev))
1350 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
1351 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1354 scratch_pool, scratch_pool));
1356 left_src = svn_diff__source_create(report_rev, scratch_pool);
1358 SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
1361 NULL /* right_src */,
1362 NULL /* copyfrom_src */,
1363 processor_parent_baton,
1365 scratch_pool, scratch_pool));
1367 if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
1370 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1371 apr_array_header_t *children;
1374 SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
1375 scratch_pool, iterpool));
1377 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1380 for (i = 0; i < children->nelts; i++)
1382 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1384 const char *name = item->key;
1385 struct svn_wc__db_base_info_t *info = item->value;
1386 const char *child_abspath;
1387 const char *child_relpath;
1389 if (info->status != svn_wc__db_status_normal)
1393 SVN_ERR(cancel_func(cancel_baton));
1395 svn_pool_clear(iterpool);
1397 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1398 child_relpath = svn_relpath_join(relpath, name, iterpool);
1403 case svn_node_symlink:
1404 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
1407 processor, dir_baton,
1411 if (depth > svn_depth_files || depth == svn_depth_unknown)
1413 svn_depth_t depth_below_here = depth;
1415 if (depth_below_here == svn_depth_immediates)
1416 depth_below_here = svn_depth_empty;
1418 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
1422 processor, dir_baton,
1438 SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
1439 scratch_pool, scratch_pool));
1441 SVN_ERR(processor->dir_deleted(relpath,
1449 return SVN_NO_ERROR;
1452 /* An svn_delta_editor_t function. */
1453 static svn_error_t *
1454 set_target_revision(void *edit_baton,
1455 svn_revnum_t target_revision,
1458 struct edit_baton_t *eb = edit_baton;
1459 eb->revnum = target_revision;
1461 return SVN_NO_ERROR;
1464 /* An svn_delta_editor_t function. The root of the comparison hierarchy */
1465 static svn_error_t *
1466 open_root(void *edit_baton,
1467 svn_revnum_t base_revision,
1468 apr_pool_t *dir_pool,
1471 struct edit_baton_t *eb = edit_baton;
1472 struct dir_baton_t *db;
1474 eb->root_opened = TRUE;
1475 db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
1478 if (eb->target[0] == '\0')
1480 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1481 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1483 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
1488 NULL /* copyfrom_source */,
1489 NULL /* parent_baton */,
1491 db->pool, db->pool));
1494 db->skip = TRUE; /* Skip this, but not the children */
1496 return SVN_NO_ERROR;
1499 /* An svn_delta_editor_t function. */
1500 static svn_error_t *
1501 delete_entry(const char *path,
1502 svn_revnum_t base_revision,
1506 struct dir_baton_t *pb = parent_baton;
1507 const char *name = svn_dirent_basename(path, pb->pool);
1510 pb->deletes = apr_hash_make(pb->pool);
1512 svn_hash_sets(pb->deletes, name, "");
1513 return SVN_NO_ERROR;
1516 /* An svn_delta_editor_t function. */
1517 static svn_error_t *
1518 add_directory(const char *path,
1520 const char *copyfrom_path,
1521 svn_revnum_t copyfrom_revision,
1522 apr_pool_t *dir_pool,
1525 struct dir_baton_t *pb = parent_baton;
1526 struct edit_baton_t *eb = pb->eb;
1527 struct dir_baton_t *db;
1528 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1529 ? svn_depth_empty : pb->depth;
1531 db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
1535 if (pb->repos_only || !eb->ignore_ancestry)
1536 db->repos_only = TRUE;
1539 struct svn_wc__db_info_t *info;
1540 SVN_ERR(ensure_local_info(pb, dir_pool));
1542 info = svn_hash_gets(pb->local_info, db->name);
1544 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1545 db->repos_only = TRUE;
1547 if (!db->repos_only && info->status != svn_wc__db_status_added)
1548 db->repos_only = TRUE;
1550 if (!db->repos_only)
1552 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1553 db->ignoring_ancestry = TRUE;
1555 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1559 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1561 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1562 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1564 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1568 NULL /* copyfrom src */,
1571 db->pool, db->pool));
1573 return SVN_NO_ERROR;
1576 /* An svn_delta_editor_t function. */
1577 static svn_error_t *
1578 open_directory(const char *path,
1580 svn_revnum_t base_revision,
1581 apr_pool_t *dir_pool,
1584 struct dir_baton_t *pb = parent_baton;
1585 struct edit_baton_t *eb = pb->eb;
1586 struct dir_baton_t *db;
1587 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1588 ? svn_depth_empty : pb->depth;
1590 /* Allocate path from the parent pool since the memory is used in the
1591 parent's compared hash */
1592 db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
1596 db->repos_only = TRUE;
1599 struct svn_wc__db_info_t *info;
1600 SVN_ERR(ensure_local_info(pb, dir_pool));
1602 info = svn_hash_gets(pb->local_info, db->name);
1604 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1605 db->repos_only = TRUE;
1607 if (!db->repos_only)
1608 switch (info->status)
1610 case svn_wc__db_status_normal:
1612 case svn_wc__db_status_deleted:
1613 db->repos_only = TRUE;
1615 if (!info->have_more_work)
1616 svn_hash_sets(pb->compared,
1617 apr_pstrdup(pb->pool, db->name), "");
1619 case svn_wc__db_status_added:
1620 if (eb->ignore_ancestry)
1621 db->ignoring_ancestry = TRUE;
1623 db->repos_only = TRUE;
1626 SVN_ERR_MALFUNCTION();
1629 if (!db->repos_only)
1631 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1632 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1636 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1638 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1639 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1641 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1645 NULL /* copyfrom src */,
1648 db->pool, db->pool));
1650 return SVN_NO_ERROR;
1654 /* An svn_delta_editor_t function. When a directory is closed, all the
1655 * directory elements that have been added or replaced will already have been
1656 * diff'd. However there may be other elements in the working copy
1657 * that have not yet been considered. */
1658 static svn_error_t *
1659 close_directory(void *dir_baton,
1662 struct dir_baton_t *db = dir_baton;
1663 struct dir_baton_t *pb = db->parent_baton;
1664 struct edit_baton_t *eb = db->eb;
1665 apr_pool_t *scratch_pool = db->pool;
1666 svn_boolean_t reported_closed = FALSE;
1668 if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
1670 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1671 apr_array_header_t *children;
1673 children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
1676 for (i = 0; i < children->nelts; i++)
1678 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1680 const char *name = item->key;
1682 svn_pool_clear(iterpool);
1683 SVN_ERR(handle_local_only(db, name, iterpool));
1685 svn_hash_sets(db->compared, name, "");
1688 svn_pool_destroy(iterpool);
1691 /* Report local modifications for this directory. Skip added
1692 directories since they can only contain added elements, all of
1693 which have already been diff'd. */
1694 if (!db->repos_only && !db->skip_children)
1696 SVN_ERR(walk_local_nodes_diff(eb,
1705 /* Report the property changes on the directory itself, if necessary. */
1708 /* Diff processor requested no directory details */
1710 else if (db->propchanges->nelts > 0 || db->repos_only)
1712 apr_hash_t *repos_props;
1716 repos_props = apr_hash_make(scratch_pool);
1720 SVN_ERR(svn_wc__db_base_get_props(&repos_props,
1721 eb->db, db->local_abspath,
1722 scratch_pool, scratch_pool));
1725 /* Add received property changes and entry props */
1726 if (db->propchanges->nelts)
1727 repos_props = svn_prop__patch(repos_props, db->propchanges,
1732 SVN_ERR(eb->processor->dir_deleted(db->relpath,
1738 reported_closed = TRUE;
1742 apr_hash_t *local_props;
1743 apr_array_header_t *prop_changes;
1745 if (eb->diff_pristine)
1746 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1747 NULL, NULL, NULL, NULL,
1749 eb->db, db->local_abspath,
1750 scratch_pool, scratch_pool));
1752 SVN_ERR(svn_wc__db_read_props(&local_props,
1753 eb->db, db->local_abspath,
1754 scratch_pool, scratch_pool));
1756 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
1759 /* ### as a good diff processor we should now only report changes
1760 if there are non-entry changes, but for now we stick to
1763 if (prop_changes->nelts)
1765 SVN_ERR(eb->processor->dir_changed(db->relpath,
1774 reported_closed = TRUE;
1779 /* Mark this directory as compared in the parent directory's baton,
1780 unless this is the root of the comparison. */
1781 if (!reported_closed && !db->skip)
1782 SVN_ERR(eb->processor->dir_closed(db->relpath,
1789 if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1790 SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
1792 SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
1794 return SVN_NO_ERROR;
1797 /* An svn_delta_editor_t function. */
1798 static svn_error_t *
1799 add_file(const char *path,
1801 const char *copyfrom_path,
1802 svn_revnum_t copyfrom_revision,
1803 apr_pool_t *file_pool,
1806 struct dir_baton_t *pb = parent_baton;
1807 struct edit_baton_t *eb = pb->eb;
1808 struct file_baton_t *fb;
1810 fb = make_file_baton(path, TRUE, pb, file_pool);
1813 if (pb->skip_children)
1816 return SVN_NO_ERROR;
1818 else if (pb->repos_only || !eb->ignore_ancestry)
1819 fb->repos_only = TRUE;
1822 struct svn_wc__db_info_t *info;
1823 SVN_ERR(ensure_local_info(pb, file_pool));
1825 info = svn_hash_gets(pb->local_info, fb->name);
1827 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1828 fb->repos_only = TRUE;
1830 if (!fb->repos_only && info->status != svn_wc__db_status_added)
1831 fb->repos_only = TRUE;
1833 if (!fb->repos_only)
1835 /* Add this path to the parent directory's list of elements that
1836 have been compared. */
1837 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1838 fb->ignoring_ancestry = TRUE;
1840 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1844 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1846 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1850 NULL /* copyfrom src */,
1853 fb->pool, fb->pool));
1855 return SVN_NO_ERROR;
1858 /* An svn_delta_editor_t function. */
1859 static svn_error_t *
1860 open_file(const char *path,
1862 svn_revnum_t base_revision,
1863 apr_pool_t *file_pool,
1866 struct dir_baton_t *pb = parent_baton;
1867 struct edit_baton_t *eb = pb->eb;
1868 struct file_baton_t *fb;
1870 fb = make_file_baton(path, FALSE, pb, file_pool);
1873 if (pb->skip_children)
1875 else if (pb->repos_only)
1876 fb->repos_only = TRUE;
1879 struct svn_wc__db_info_t *info;
1880 SVN_ERR(ensure_local_info(pb, file_pool));
1882 info = svn_hash_gets(pb->local_info, fb->name);
1884 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1885 fb->repos_only = TRUE;
1887 if (!fb->repos_only)
1888 switch (info->status)
1890 case svn_wc__db_status_normal:
1892 case svn_wc__db_status_deleted:
1893 fb->repos_only = TRUE;
1894 if (!info->have_more_work)
1895 svn_hash_sets(pb->compared,
1896 apr_pstrdup(pb->pool, fb->name), "");
1898 case svn_wc__db_status_added:
1899 if (eb->ignore_ancestry)
1900 fb->ignoring_ancestry = TRUE;
1902 fb->repos_only = TRUE;
1905 SVN_ERR_MALFUNCTION();
1908 if (!fb->repos_only)
1910 /* Add this path to the parent directory's list of elements that
1911 have been compared. */
1912 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1913 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1917 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1919 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1920 NULL, NULL, NULL, &fb->base_checksum, NULL,
1921 NULL, NULL, &fb->base_props, NULL,
1922 eb->db, fb->local_abspath,
1923 fb->pool, fb->pool));
1925 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1929 NULL /* copyfrom src */,
1932 fb->pool, fb->pool));
1934 return SVN_NO_ERROR;
1937 /* An svn_delta_editor_t function. */
1938 static svn_error_t *
1939 apply_textdelta(void *file_baton,
1940 const char *base_checksum_hex,
1942 svn_txdelta_window_handler_t *handler,
1943 void **handler_baton)
1945 struct file_baton_t *fb = file_baton;
1946 struct edit_baton_t *eb = fb->eb;
1947 svn_stream_t *source;
1948 svn_stream_t *temp_stream;
1949 svn_checksum_t *repos_checksum = NULL;
1953 *handler = svn_delta_noop_window_handler;
1954 *handler_baton = NULL;
1955 return SVN_NO_ERROR;
1958 if (base_checksum_hex && fb->base_checksum)
1960 const svn_checksum_t *base_md5;
1961 SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
1962 base_checksum_hex, pool));
1964 SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
1965 eb->db, eb->anchor_abspath,
1969 if (! svn_checksum_match(repos_checksum, base_md5))
1971 /* ### I expect that there are some bad drivers out there
1972 ### that used to give bad results. We could look in
1973 ### working to see if the expected checksum matches and
1974 ### then return the pristine of that... But that only moves
1977 /* If needed: compare checksum obtained via md5 of working.
1978 And if they match set fb->base_checksum and fb->base_props */
1980 return svn_checksum_mismatch_err(
1984 _("Checksum mismatch for '%s'"),
1985 svn_dirent_local_style(fb->local_abspath,
1989 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1990 eb->db, fb->local_abspath,
1994 else if (fb->base_checksum)
1996 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1997 eb->db, fb->local_abspath,
2002 source = svn_stream_empty(pool);
2004 /* This is the file that will contain the pristine repository version. */
2005 SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
2006 svn_io_file_del_on_pool_cleanup,
2007 fb->pool, fb->pool));
2009 svn_txdelta_apply(source, temp_stream,
2011 fb->local_abspath /* error_info */,
2013 handler, handler_baton);
2015 return SVN_NO_ERROR;
2018 /* An svn_delta_editor_t function. When the file is closed we have a temporary
2019 * file containing a pristine version of the repository file. This can
2020 * be compared against the working copy.
2022 * Ignore TEXT_CHECKSUM.
2024 static svn_error_t *
2025 close_file(void *file_baton,
2026 const char *expected_md5_digest,
2029 struct file_baton_t *fb = file_baton;
2030 struct dir_baton_t *pb = fb->parent_baton;
2031 struct edit_baton_t *eb = fb->eb;
2032 apr_pool_t *scratch_pool = fb->pool;
2034 /* The repository information; constructed from BASE + Changes */
2035 const char *repos_file;
2036 apr_hash_t *repos_props;
2040 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2041 SVN_ERR(maybe_done(pb));
2042 return SVN_NO_ERROR;
2045 if (expected_md5_digest != NULL)
2047 svn_checksum_t *expected_checksum;
2048 const svn_checksum_t *result_checksum;
2050 if (fb->temp_file_path)
2051 result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
2054 result_checksum = fb->base_checksum;
2056 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
2057 expected_md5_digest, scratch_pool));
2059 if (result_checksum->kind != svn_checksum_md5)
2060 SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
2061 eb->db, fb->local_abspath,
2063 scratch_pool, scratch_pool));
2065 if (!svn_checksum_match(expected_checksum, result_checksum))
2066 return svn_checksum_mismatch_err(
2070 _("Checksum mismatch for '%s'"),
2071 svn_dirent_local_style(fb->local_abspath,
2075 if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2076 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2079 apr_hash_t *prop_base;
2082 prop_base = apr_hash_make(scratch_pool);
2084 prop_base = fb->base_props;
2086 /* includes entry props */
2087 repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
2089 repos_file = fb->temp_file_path;
2092 assert(fb->base_checksum);
2093 SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
2094 eb->db, eb->anchor_abspath,
2096 scratch_pool, scratch_pool));
2102 SVN_ERR(eb->processor->file_deleted(fb->relpath,
2112 /* Produce a diff of actual or pristine against repos */
2113 apr_hash_t *local_props;
2114 apr_array_header_t *prop_changes;
2115 const char *localfile;
2117 /* pb->local_info contains some information that might allow optimizing
2120 if (eb->diff_pristine)
2122 const svn_checksum_t *checksum;
2123 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
2124 NULL, &checksum, NULL, NULL,
2126 eb->db, fb->local_abspath,
2127 scratch_pool, scratch_pool));
2129 SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
2130 eb->db, eb->anchor_abspath,
2132 scratch_pool, scratch_pool));
2136 SVN_ERR(svn_wc__db_read_props(&local_props,
2137 eb->db, fb->local_abspath,
2138 scratch_pool, scratch_pool));
2140 /* a detranslated version of the working file */
2141 SVN_ERR(svn_wc__internal_translated_file(
2142 &localfile, fb->local_abspath, eb->db, fb->local_abspath,
2143 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
2144 eb->cancel_func, eb->cancel_baton,
2145 scratch_pool, scratch_pool));
2148 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
2152 /* ### as a good diff processor we should now only report changes, and
2153 report file_closed() in other cases */
2154 SVN_ERR(eb->processor->file_changed(fb->relpath,
2157 repos_file /* left file */,
2158 localfile /* right file */,
2159 repos_props /* left_props */,
2160 local_props /* right props */,
2161 TRUE /* ### file_modified */,
2168 if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2169 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2171 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2172 SVN_ERR(maybe_done(pb));
2173 return SVN_NO_ERROR;
2177 /* An svn_delta_editor_t function. */
2178 static svn_error_t *
2179 change_file_prop(void *file_baton,
2181 const svn_string_t *value,
2184 struct file_baton_t *fb = file_baton;
2185 svn_prop_t *propchange;
2186 svn_prop_kind_t propkind;
2188 propkind = svn_property_kind2(name);
2189 if (propkind == svn_prop_wc_kind)
2190 return SVN_NO_ERROR;
2191 else if (propkind == svn_prop_regular_kind)
2192 fb->has_propchange = TRUE;
2194 propchange = apr_array_push(fb->propchanges);
2195 propchange->name = apr_pstrdup(fb->pool, name);
2196 propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
2198 return SVN_NO_ERROR;
2202 /* An svn_delta_editor_t function. */
2203 static svn_error_t *
2204 change_dir_prop(void *dir_baton,
2206 const svn_string_t *value,
2209 struct dir_baton_t *db = dir_baton;
2210 svn_prop_t *propchange;
2211 svn_prop_kind_t propkind;
2213 propkind = svn_property_kind2(name);
2214 if (propkind == svn_prop_wc_kind)
2215 return SVN_NO_ERROR;
2216 else if (propkind == svn_prop_regular_kind)
2217 db->has_propchange = TRUE;
2219 propchange = apr_array_push(db->propchanges);
2220 propchange->name = apr_pstrdup(db->pool, name);
2221 propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2223 return SVN_NO_ERROR;
2227 /* An svn_delta_editor_t function. */
2228 static svn_error_t *
2229 close_edit(void *edit_baton,
2232 struct edit_baton_t *eb = edit_baton;
2234 if (!eb->root_opened)
2236 SVN_ERR(walk_local_nodes_diff(eb,
2240 NULL /* compared */,
2241 NULL /* No parent_baton */,
2245 return SVN_NO_ERROR;
2248 /* Public Interface */
2251 /* Create a diff editor and baton. */
2253 svn_wc__get_diff_editor(const svn_delta_editor_t **editor,
2255 svn_wc_context_t *wc_ctx,
2256 const char *anchor_abspath,
2259 svn_boolean_t ignore_ancestry,
2260 svn_boolean_t show_copies_as_adds,
2261 svn_boolean_t use_git_diff_format,
2262 svn_boolean_t use_text_base,
2263 svn_boolean_t reverse_order,
2264 svn_boolean_t server_performs_filtering,
2265 const apr_array_header_t *changelist_filter,
2266 const svn_wc_diff_callbacks4_t *callbacks,
2267 void *callback_baton,
2268 svn_cancel_func_t cancel_func,
2270 apr_pool_t *result_pool,
2271 apr_pool_t *scratch_pool)
2273 struct edit_baton_t *eb;
2275 svn_delta_editor_t *tree_editor;
2276 const svn_delta_editor_t *inner_editor;
2277 struct svn_wc__shim_fetch_baton_t *sfb;
2278 svn_delta_shim_callbacks_t *shim_callbacks =
2279 svn_delta_shim_callbacks_default(result_pool);
2280 const svn_diff_tree_processor_t *diff_processor;
2282 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
2284 /* --git implies --show-copies-as-adds */
2285 if (use_git_diff_format)
2286 show_copies_as_adds = TRUE;
2288 SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
2289 callbacks, callback_baton, TRUE,
2290 result_pool, scratch_pool));
2292 /* Apply changelist filtering to the output */
2293 if (changelist_filter && changelist_filter->nelts)
2295 apr_hash_t *changelist_hash;
2297 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
2299 diff_processor = svn_wc__changelist_filter_tree_processor_create(
2300 diff_processor, wc_ctx, anchor_abspath,
2301 changelist_hash, result_pool);
2304 SVN_ERR(make_edit_baton(&eb,
2306 anchor_abspath, target,
2308 depth, ignore_ancestry, show_copies_as_adds,
2309 use_text_base, reverse_order,
2310 cancel_func, cancel_baton,
2313 tree_editor = svn_delta_default_editor(eb->pool);
2315 tree_editor->set_target_revision = set_target_revision;
2316 tree_editor->open_root = open_root;
2317 tree_editor->delete_entry = delete_entry;
2318 tree_editor->add_directory = add_directory;
2319 tree_editor->open_directory = open_directory;
2320 tree_editor->close_directory = close_directory;
2321 tree_editor->add_file = add_file;
2322 tree_editor->open_file = open_file;
2323 tree_editor->apply_textdelta = apply_textdelta;
2324 tree_editor->change_file_prop = change_file_prop;
2325 tree_editor->change_dir_prop = change_dir_prop;
2326 tree_editor->close_file = close_file;
2327 tree_editor->close_edit = close_edit;
2329 inner_editor = tree_editor;
2332 if (!server_performs_filtering
2333 && depth == svn_depth_unknown)
2334 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2343 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
2351 sfb = apr_palloc(result_pool, sizeof(*sfb));
2352 sfb->db = wc_ctx->db;
2353 sfb->base_abspath = eb->anchor_abspath;
2354 sfb->fetch_base = TRUE;
2356 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2357 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2358 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2359 shim_callbacks->fetch_baton = sfb;
2362 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2363 NULL, NULL, shim_callbacks,
2364 result_pool, scratch_pool));
2366 return SVN_NO_ERROR;
2369 /* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
2371 /* baton for the svn_diff_tree_processor_t wrapper */
2372 typedef struct wc_diff_wrap_baton_t
2374 const svn_wc_diff_callbacks4_t *callbacks;
2375 void *callback_baton;
2377 svn_boolean_t walk_deleted_dirs;
2379 apr_pool_t *result_pool;
2380 const char *empty_file;
2382 } wc_diff_wrap_baton_t;
2384 static svn_error_t *
2385 wrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
2386 apr_pool_t *scratch_pool)
2389 return SVN_NO_ERROR;
2391 /* Create a unique file in the tempdir */
2392 SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
2393 svn_io_file_del_on_pool_cleanup,
2394 wb->result_pool, scratch_pool));
2396 return SVN_NO_ERROR;
2399 /* svn_diff_tree_processor_t function */
2400 static svn_error_t *
2401 wrap_dir_opened(void **new_dir_baton,
2402 svn_boolean_t *skip,
2403 svn_boolean_t *skip_children,
2404 const char *relpath,
2405 const svn_diff_source_t *left_source,
2406 const svn_diff_source_t *right_source,
2407 const svn_diff_source_t *copyfrom_source,
2408 void *parent_dir_baton,
2409 const svn_diff_tree_processor_t *processor,
2410 apr_pool_t *result_pool,
2411 apr_pool_t *scratch_pool)
2413 wc_diff_wrap_baton_t *wb = processor->baton;
2414 svn_boolean_t tree_conflicted = FALSE;
2416 assert(left_source || right_source); /* Must exist at one point. */
2417 assert(!left_source || !copyfrom_source); /* Either existed or added. */
2419 /* Maybe store state and tree_conflicted in baton? */
2420 if (left_source != NULL)
2422 /* Open for change or delete */
2423 SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
2426 ? right_source->revision
2428 ? left_source->revision
2429 : SVN_INVALID_REVNUM),
2433 if (! right_source && !wb->walk_deleted_dirs)
2434 *skip_children = TRUE;
2436 else /* left_source == NULL -> Add */
2438 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2439 SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
2440 skip, skip_children,
2442 right_source->revision,
2444 ? copyfrom_source->repos_relpath
2447 ? copyfrom_source->revision
2448 : SVN_INVALID_REVNUM,
2453 *new_dir_baton = NULL;
2455 return SVN_NO_ERROR;
2458 /* svn_diff_tree_processor_t function */
2459 static svn_error_t *
2460 wrap_dir_added(const char *relpath,
2461 const svn_diff_source_t *right_source,
2462 const svn_diff_source_t *copyfrom_source,
2463 /*const*/ apr_hash_t *copyfrom_props,
2464 /*const*/ apr_hash_t *right_props,
2466 const svn_diff_tree_processor_t *processor,
2467 apr_pool_t *scratch_pool)
2469 wc_diff_wrap_baton_t *wb = processor->baton;
2470 svn_boolean_t tree_conflicted = FALSE;
2471 svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
2472 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2473 apr_hash_t *pristine_props = copyfrom_props;
2474 apr_array_header_t *prop_changes = NULL;
2476 if (right_props && apr_hash_count(right_props))
2478 if (!pristine_props)
2479 pristine_props = apr_hash_make(scratch_pool);
2481 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
2484 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
2487 TRUE /* dir_was_added */,
2488 prop_changes, pristine_props,
2493 SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
2496 TRUE /* dir_was_added */,
2499 return SVN_NO_ERROR;
2502 /* svn_diff_tree_processor_t function */
2503 static svn_error_t *
2504 wrap_dir_deleted(const char *relpath,
2505 const svn_diff_source_t *left_source,
2506 /*const*/ apr_hash_t *left_props,
2508 const svn_diff_tree_processor_t *processor,
2509 apr_pool_t *scratch_pool)
2511 wc_diff_wrap_baton_t *wb = processor->baton;
2512 svn_boolean_t tree_conflicted = FALSE;
2513 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2515 SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
2520 return SVN_NO_ERROR;
2523 /* svn_diff_tree_processor_t function */
2524 static svn_error_t *
2525 wrap_dir_closed(const char *relpath,
2526 const svn_diff_source_t *left_source,
2527 const svn_diff_source_t *right_source,
2529 const svn_diff_tree_processor_t *processor,
2530 apr_pool_t *scratch_pool)
2532 wc_diff_wrap_baton_t *wb = processor->baton;
2534 /* No previous implementations provided these arguments, so we
2535 are not providing them either */
2536 SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
2542 return SVN_NO_ERROR;
2545 /* svn_diff_tree_processor_t function */
2546 static svn_error_t *
2547 wrap_dir_changed(const char *relpath,
2548 const svn_diff_source_t *left_source,
2549 const svn_diff_source_t *right_source,
2550 /*const*/ apr_hash_t *left_props,
2551 /*const*/ apr_hash_t *right_props,
2552 const apr_array_header_t *prop_changes,
2554 const struct svn_diff_tree_processor_t *processor,
2555 apr_pool_t *scratch_pool)
2557 wc_diff_wrap_baton_t *wb = processor->baton;
2558 svn_boolean_t tree_conflicted = FALSE;
2559 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2561 assert(left_source && right_source);
2563 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
2565 FALSE /* dir_was_added */,
2571 /* And call dir_closed, etc */
2572 SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
2573 dir_baton, processor,
2575 return SVN_NO_ERROR;
2578 /* svn_diff_tree_processor_t function */
2579 static svn_error_t *
2580 wrap_file_opened(void **new_file_baton,
2581 svn_boolean_t *skip,
2582 const char *relpath,
2583 const svn_diff_source_t *left_source,
2584 const svn_diff_source_t *right_source,
2585 const svn_diff_source_t *copyfrom_source,
2587 const svn_diff_tree_processor_t *processor,
2588 apr_pool_t *result_pool,
2589 apr_pool_t *scratch_pool)
2591 wc_diff_wrap_baton_t *wb = processor->baton;
2592 svn_boolean_t tree_conflicted = FALSE;
2594 if (left_source) /* If ! added */
2595 SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
2597 ? right_source->revision
2599 ? left_source->revision
2600 : SVN_INVALID_REVNUM),
2601 wb->callback_baton, scratch_pool));
2603 /* No old implementation used the output arguments for notify */
2605 *new_file_baton = NULL;
2606 return SVN_NO_ERROR;
2609 /* svn_diff_tree_processor_t function */
2610 static svn_error_t *
2611 wrap_file_added(const char *relpath,
2612 const svn_diff_source_t *copyfrom_source,
2613 const svn_diff_source_t *right_source,
2614 const char *copyfrom_file,
2615 const char *right_file,
2616 /*const*/ apr_hash_t *copyfrom_props,
2617 /*const*/ apr_hash_t *right_props,
2619 const svn_diff_tree_processor_t *processor,
2620 apr_pool_t *scratch_pool)
2622 wc_diff_wrap_baton_t *wb = processor->baton;
2623 svn_boolean_t tree_conflicted = FALSE;
2624 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2625 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2626 apr_array_header_t *prop_changes;
2628 if (! copyfrom_props)
2629 copyfrom_props = apr_hash_make(scratch_pool);
2631 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
2634 if (! copyfrom_source)
2635 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2637 SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
2644 right_source->revision,
2646 ? svn_prop_get_value(copyfrom_props,
2650 ? svn_prop_get_value(right_props,
2654 ? copyfrom_source->repos_relpath
2657 ? copyfrom_source->revision
2658 : SVN_INVALID_REVNUM,
2659 prop_changes, copyfrom_props,
2662 return SVN_NO_ERROR;
2665 static svn_error_t *
2666 wrap_file_deleted(const char *relpath,
2667 const svn_diff_source_t *left_source,
2668 const char *left_file,
2669 apr_hash_t *left_props,
2671 const svn_diff_tree_processor_t *processor,
2672 apr_pool_t *scratch_pool)
2674 wc_diff_wrap_baton_t *wb = processor->baton;
2675 svn_boolean_t tree_conflicted = FALSE;
2676 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2678 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2680 SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
2682 left_file, wb->empty_file,
2684 ? svn_prop_get_value(left_props,
2691 return SVN_NO_ERROR;
2694 /* svn_diff_tree_processor_t function */
2695 static svn_error_t *
2696 wrap_file_changed(const char *relpath,
2697 const svn_diff_source_t *left_source,
2698 const svn_diff_source_t *right_source,
2699 const char *left_file,
2700 const char *right_file,
2701 /*const*/ apr_hash_t *left_props,
2702 /*const*/ apr_hash_t *right_props,
2703 svn_boolean_t file_modified,
2704 const apr_array_header_t *prop_changes,
2706 const svn_diff_tree_processor_t *processor,
2707 apr_pool_t *scratch_pool)
2709 wc_diff_wrap_baton_t *wb = processor->baton;
2710 svn_boolean_t tree_conflicted = FALSE;
2711 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2712 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2714 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2716 assert(left_source && right_source);
2718 SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
2720 file_modified ? left_file : NULL,
2721 file_modified ? right_file : NULL,
2722 left_source->revision,
2723 right_source->revision,
2725 ? svn_prop_get_value(left_props,
2729 ? svn_prop_get_value(right_props,
2736 return SVN_NO_ERROR;
2740 svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
2741 const svn_wc_diff_callbacks4_t *callbacks,
2742 void *callback_baton,
2743 svn_boolean_t walk_deleted_dirs,
2744 apr_pool_t *result_pool,
2745 apr_pool_t *scratch_pool)
2747 wc_diff_wrap_baton_t *wrap_baton;
2748 svn_diff_tree_processor_t *processor;
2750 wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
2752 wrap_baton->result_pool = result_pool;
2753 wrap_baton->callbacks = callbacks;
2754 wrap_baton->callback_baton = callback_baton;
2755 wrap_baton->empty_file = NULL;
2756 wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
2758 processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
2760 processor->dir_opened = wrap_dir_opened;
2761 processor->dir_added = wrap_dir_added;
2762 processor->dir_deleted = wrap_dir_deleted;
2763 processor->dir_changed = wrap_dir_changed;
2764 processor->dir_closed = wrap_dir_closed;
2766 processor->file_opened = wrap_file_opened;
2767 processor->file_added = wrap_file_added;
2768 processor->file_deleted = wrap_file_deleted;
2769 processor->file_changed = wrap_file_changed;
2770 /*processor->file_closed = wrap_file_closed*/; /* Not needed */
2772 *diff_processor = processor;
2773 return SVN_NO_ERROR;
2776 /* =====================================================================
2777 * A tree processor filter that filters by changelist membership
2778 * =====================================================================
2780 * The current implementation queries the WC for the changelist of each
2781 * file as it comes through, and sets the 'skip' flag for a non-matching
2784 * (It doesn't set the 'skip' flag for a directory, as we need to receive
2785 * the changed/added/deleted/closed call to know when it is closed, in
2786 * order to preserve the strict open-close semantics for the wrapped tree
2789 * It passes on the opening and closing of every directory, even if there
2790 * are no file changes to be passed on inside that directory.
2793 typedef struct filter_tree_baton_t
2795 const svn_diff_tree_processor_t *processor;
2796 svn_wc_context_t *wc_ctx;
2797 /* WC path of the root of the diff (where relpath = "") */
2798 const char *root_local_abspath;
2799 /* Hash whose keys are const char * changelist names. */
2800 apr_hash_t *changelist_hash;
2801 } filter_tree_baton_t;
2803 static svn_error_t *
2804 filter_dir_opened(void **new_dir_baton,
2805 svn_boolean_t *skip,
2806 svn_boolean_t *skip_children,
2807 const char *relpath,
2808 const svn_diff_source_t *left_source,
2809 const svn_diff_source_t *right_source,
2810 const svn_diff_source_t *copyfrom_source,
2811 void *parent_dir_baton,
2812 const svn_diff_tree_processor_t *processor,
2813 apr_pool_t *result_pool,
2814 apr_pool_t *scratch_pool)
2816 struct filter_tree_baton_t *fb = processor->baton;
2818 SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
2820 left_source, right_source,
2824 result_pool, scratch_pool));
2825 return SVN_NO_ERROR;
2828 static svn_error_t *
2829 filter_dir_added(const char *relpath,
2830 const svn_diff_source_t *copyfrom_source,
2831 const svn_diff_source_t *right_source,
2832 /*const*/ apr_hash_t *copyfrom_props,
2833 /*const*/ apr_hash_t *right_props,
2835 const svn_diff_tree_processor_t *processor,
2836 apr_pool_t *scratch_pool)
2838 struct filter_tree_baton_t *fb = processor->baton;
2840 SVN_ERR(fb->processor->dir_closed(relpath,
2847 return SVN_NO_ERROR;
2850 static svn_error_t *
2851 filter_dir_deleted(const char *relpath,
2852 const svn_diff_source_t *left_source,
2853 /*const*/ apr_hash_t *left_props,
2855 const svn_diff_tree_processor_t *processor,
2856 apr_pool_t *scratch_pool)
2858 struct filter_tree_baton_t *fb = processor->baton;
2860 SVN_ERR(fb->processor->dir_closed(relpath,
2867 return SVN_NO_ERROR;
2870 static svn_error_t *
2871 filter_dir_changed(const char *relpath,
2872 const svn_diff_source_t *left_source,
2873 const svn_diff_source_t *right_source,
2874 /*const*/ apr_hash_t *left_props,
2875 /*const*/ apr_hash_t *right_props,
2876 const apr_array_header_t *prop_changes,
2878 const struct svn_diff_tree_processor_t *processor,
2879 apr_pool_t *scratch_pool)
2881 struct filter_tree_baton_t *fb = processor->baton;
2883 SVN_ERR(fb->processor->dir_closed(relpath,
2889 return SVN_NO_ERROR;
2892 static svn_error_t *
2893 filter_dir_closed(const char *relpath,
2894 const svn_diff_source_t *left_source,
2895 const svn_diff_source_t *right_source,
2897 const svn_diff_tree_processor_t *processor,
2898 apr_pool_t *scratch_pool)
2900 struct filter_tree_baton_t *fb = processor->baton;
2902 SVN_ERR(fb->processor->dir_closed(relpath,
2908 return SVN_NO_ERROR;
2911 static svn_error_t *
2912 filter_file_opened(void **new_file_baton,
2913 svn_boolean_t *skip,
2914 const char *relpath,
2915 const svn_diff_source_t *left_source,
2916 const svn_diff_source_t *right_source,
2917 const svn_diff_source_t *copyfrom_source,
2919 const svn_diff_tree_processor_t *processor,
2920 apr_pool_t *result_pool,
2921 apr_pool_t *scratch_pool)
2923 struct filter_tree_baton_t *fb = processor->baton;
2924 const char *local_abspath
2925 = svn_dirent_join(fb->root_local_abspath, relpath, scratch_pool);
2927 /* Skip if not a member of a given changelist */
2928 if (! svn_wc__changelist_match(fb->wc_ctx, local_abspath,
2929 fb->changelist_hash, scratch_pool))
2932 return SVN_NO_ERROR;
2935 SVN_ERR(fb->processor->file_opened(new_file_baton,
2945 return SVN_NO_ERROR;
2948 static svn_error_t *
2949 filter_file_added(const char *relpath,
2950 const svn_diff_source_t *copyfrom_source,
2951 const svn_diff_source_t *right_source,
2952 const char *copyfrom_file,
2953 const char *right_file,
2954 /*const*/ apr_hash_t *copyfrom_props,
2955 /*const*/ apr_hash_t *right_props,
2957 const svn_diff_tree_processor_t *processor,
2958 apr_pool_t *scratch_pool)
2960 struct filter_tree_baton_t *fb = processor->baton;
2962 SVN_ERR(fb->processor->file_added(relpath,
2972 return SVN_NO_ERROR;
2975 static svn_error_t *
2976 filter_file_deleted(const char *relpath,
2977 const svn_diff_source_t *left_source,
2978 const char *left_file,
2979 /*const*/ apr_hash_t *left_props,
2981 const svn_diff_tree_processor_t *processor,
2982 apr_pool_t *scratch_pool)
2984 struct filter_tree_baton_t *fb = processor->baton;
2986 SVN_ERR(fb->processor->file_deleted(relpath,
2994 return SVN_NO_ERROR;
2997 static svn_error_t *
2998 filter_file_changed(const char *relpath,
2999 const svn_diff_source_t *left_source,
3000 const svn_diff_source_t *right_source,
3001 const char *left_file,
3002 const char *right_file,
3003 /*const*/ apr_hash_t *left_props,
3004 /*const*/ apr_hash_t *right_props,
3005 svn_boolean_t file_modified,
3006 const apr_array_header_t *prop_changes,
3008 const svn_diff_tree_processor_t *processor,
3009 apr_pool_t *scratch_pool)
3011 struct filter_tree_baton_t *fb = processor->baton;
3013 SVN_ERR(fb->processor->file_changed(relpath,
3025 return SVN_NO_ERROR;
3028 static svn_error_t *
3029 filter_file_closed(const char *relpath,
3030 const svn_diff_source_t *left_source,
3031 const svn_diff_source_t *right_source,
3033 const svn_diff_tree_processor_t *processor,
3034 apr_pool_t *scratch_pool)
3036 struct filter_tree_baton_t *fb = processor->baton;
3038 SVN_ERR(fb->processor->file_closed(relpath,
3045 return SVN_NO_ERROR;
3048 static svn_error_t *
3049 filter_node_absent(const char *relpath,
3051 const svn_diff_tree_processor_t *processor,
3052 apr_pool_t *scratch_pool)
3054 struct filter_tree_baton_t *fb = processor->baton;
3056 SVN_ERR(fb->processor->node_absent(relpath,
3060 return SVN_NO_ERROR;
3063 const svn_diff_tree_processor_t *
3064 svn_wc__changelist_filter_tree_processor_create(
3065 const svn_diff_tree_processor_t *processor,
3066 svn_wc_context_t *wc_ctx,
3067 const char *root_local_abspath,
3068 apr_hash_t *changelist_hash,
3069 apr_pool_t *result_pool)
3071 struct filter_tree_baton_t *fb;
3072 svn_diff_tree_processor_t *filter;
3074 if (! changelist_hash)
3077 fb = apr_pcalloc(result_pool, sizeof(*fb));
3078 fb->processor = processor;
3079 fb->wc_ctx = wc_ctx;
3080 fb->root_local_abspath = root_local_abspath;
3081 fb->changelist_hash = changelist_hash;
3083 filter = svn_diff__tree_processor_create(fb, result_pool);
3084 filter->dir_opened = filter_dir_opened;
3085 filter->dir_added = filter_dir_added;
3086 filter->dir_deleted = filter_dir_deleted;
3087 filter->dir_changed = filter_dir_changed;
3088 filter->dir_closed = filter_dir_closed;
3090 filter->file_opened = filter_file_opened;
3091 filter->file_added = filter_file_added;
3092 filter->file_deleted = filter_file_deleted;
3093 filter->file_changed = filter_file_changed;
3094 filter->file_closed = filter_file_closed;
3096 filter->node_absent = filter_node_absent;