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_diff_tree.h"
68 #include "private/svn_editor.h"
69 #include "private/svn_sorts_private.h"
70 #include "private/svn_subr_private.h"
71 #include "private/svn_wc_private.h"
75 #include "adm_files.h"
76 #include "translate.h"
79 #include "svn_private_config.h"
81 /*-------------------------------------------------------------------------*/
84 /* Overall crawler editor baton.
91 /* A diff tree processor, receiving the result of the diff. */
92 const svn_diff_tree_processor_t *processor;
94 /* A boolean indicating whether local additions should be reported before
95 remote deletes. The processor can transform adds in deletes and deletes
96 in adds, but it can't reorder the output. */
97 svn_boolean_t local_before_remote;
99 /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */
101 const char *anchor_abspath;
103 /* Target revision */
106 /* Was the root opened? */
107 svn_boolean_t root_opened;
109 /* How does this diff descend as seen from target? */
112 /* Should this diff ignore node ancestry? */
113 svn_boolean_t ignore_ancestry;
115 /* Possibly diff repos against text-bases instead of working files. */
116 svn_boolean_t diff_pristine;
118 /* Cancel function/baton */
119 svn_cancel_func_t cancel_func;
125 /* Directory level baton.
129 /* Reference to parent directory baton (or NULL for the root) */
130 struct dir_baton_t *parent_baton;
132 /* The depth at which this directory should be diffed. */
135 /* The name and path of this directory as if they would be/are in the
136 local working copy. */
139 const char *local_abspath;
141 /* TRUE if the file is added by the editor drive. */
143 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
144 svn_boolean_t repos_only;
145 /* TRUE if the node is to be compared with an unrelated node*/
146 svn_boolean_t ignoring_ancestry;
148 /* Processor state */
151 svn_boolean_t skip_children;
153 svn_diff_source_t *left_src;
154 svn_diff_source_t *right_src;
156 apr_hash_t *local_info;
158 /* A hash containing the basenames of the nodes reported deleted by the
159 repository (or NULL for no values). */
162 /* Identifies those directory elements that get compared while running
163 the crawler. These elements should not be compared again when
164 recursively looking for local modifications.
166 This hash maps the basename of the node to an unimportant value.
168 If the directory's properties have been compared, an item with hash
169 key of "" will be present in the hash. */
170 apr_hash_t *compared;
172 /* The list of incoming BASE->repos propchanges. */
173 apr_array_header_t *propchanges;
175 /* Has a change on regular properties */
176 svn_boolean_t has_propchange;
178 /* The overall crawler editor baton. */
179 struct edit_baton_t *eb;
189 struct dir_baton_t *parent_baton;
191 /* The name and path of this file as if they would be/are in the
192 parent directory, diff session and local working copy. */
195 const char *local_abspath;
197 /* Processor state */
201 /* TRUE if the file is added by the editor drive. */
203 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
204 svn_boolean_t repos_only;
205 /* TRUE if the node is to be compared with an unrelated node*/
206 svn_boolean_t ignoring_ancestry;
208 const svn_diff_source_t *left_src;
209 const svn_diff_source_t *right_src;
211 /* The list of incoming BASE->repos propchanges. */
212 apr_array_header_t *propchanges;
214 /* Has a change on regular properties */
215 svn_boolean_t has_propchange;
217 /* The current BASE checksum and props */
218 const svn_checksum_t *base_checksum;
219 apr_hash_t *base_props;
221 /* The resulting from apply_textdelta */
222 const char *temp_file_path;
223 unsigned char result_digest[APR_MD5_DIGESTSIZE];
225 /* The overall crawler editor baton. */
226 struct edit_baton_t *eb;
231 /* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths
232 * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON
233 * define the callbacks to compare files. DEPTH defines if and how to
234 * descend into subdirectories; see public doc string for exactly how.
235 * IGNORE_ANCESTRY defines whether to utilize node ancestry when
236 * calculating diffs. USE_TEXT_BASE defines whether to compare
237 * against working files or text-bases. REVERSE_ORDER defines which
238 * direction to perform the diff.
241 make_edit_baton(struct edit_baton_t **edit_baton,
243 const char *anchor_abspath,
245 const svn_diff_tree_processor_t *diff_processor,
247 svn_boolean_t ignore_ancestry,
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));
258 eb = apr_pcalloc(pool, sizeof(*eb));
260 eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
261 eb->target = apr_pstrdup(pool, target);
262 eb->processor = diff_processor;
264 eb->ignore_ancestry = ignore_ancestry;
265 eb->local_before_remote = reverse_order;
266 eb->diff_pristine = use_text_base;
267 eb->cancel_func = cancel_func;
268 eb->cancel_baton = cancel_baton;
275 /* Create a new directory baton. PATH is the directory path,
276 * including anchor_path. ADDED is set if this directory is being
277 * added rather than replaced. PARENT_BATON is the baton of the
278 * parent directory, it will be null if this is the root of the
279 * comparison hierarchy. The directory and its parent may or may not
280 * exist in the working copy. EDIT_BATON is the overall crawler
283 static struct dir_baton_t *
284 make_dir_baton(const char *path,
285 struct dir_baton_t *parent_baton,
286 struct edit_baton_t *eb,
289 apr_pool_t *result_pool)
291 apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
293 struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
295 db->parent_baton = parent_baton;
297 /* Allocate 1 string for using as 3 strings */
298 db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
299 db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
300 db->name = svn_dirent_basename(db->relpath, NULL);
306 db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
307 db->compared = apr_hash_make(dir_pool);
309 if (parent_baton != NULL)
311 parent_baton->users++;
319 /* Create a new file baton. PATH is the file path, including
320 * anchor_path. ADDED is set if this file is being added rather than
321 * replaced. PARENT_BATON is the baton of the parent directory.
322 * The directory and its parent may or may not exist in the working copy.
324 static struct file_baton_t *
325 make_file_baton(const char *path,
327 struct dir_baton_t *parent_baton,
328 apr_pool_t *result_pool)
330 apr_pool_t *file_pool = svn_pool_create(result_pool);
331 struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
332 struct edit_baton_t *eb = parent_baton->eb;
335 fb->parent_baton = parent_baton;
336 fb->parent_baton->users++;
338 /* Allocate 1 string for using as 3 strings */
339 fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
340 fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
341 fb->name = svn_dirent_basename(fb->relpath, NULL);
344 fb->pool = file_pool;
345 fb->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
350 /* Destroy DB when there are no more registered users */
352 maybe_done(struct dir_baton_t *db)
358 struct dir_baton_t *pb = db->parent_baton;
360 svn_pool_clear(db->pool);
363 SVN_ERR(maybe_done(pb));
369 /* Standard check to see if a node is represented in the local working copy */
370 #define NOT_PRESENT(status) \
371 ((status) == svn_wc__db_status_not_present \
372 || (status) == svn_wc__db_status_excluded \
373 || (status) == svn_wc__db_status_server_excluded)
376 svn_wc__diff_base_working_diff(svn_wc__db_t *db,
377 const char *local_abspath,
379 svn_revnum_t revision,
380 const svn_diff_tree_processor_t *processor,
381 void *processor_dir_baton,
382 svn_boolean_t diff_pristine,
383 svn_cancel_func_t cancel_func,
385 apr_pool_t *scratch_pool)
387 void *file_baton = NULL;
388 svn_boolean_t skip = FALSE;
389 svn_wc__db_status_t status;
390 svn_revnum_t db_revision;
391 svn_boolean_t had_props;
392 svn_boolean_t props_mod;
393 svn_boolean_t files_same = FALSE;
394 svn_wc__db_status_t base_status;
395 const svn_checksum_t *working_checksum;
396 const svn_checksum_t *checksum;
397 svn_filesize_t recorded_size;
398 apr_time_t recorded_time;
399 const char *pristine_file;
400 const char *local_file;
401 svn_diff_source_t *left_src;
402 svn_diff_source_t *right_src;
403 apr_hash_t *base_props;
404 apr_hash_t *local_props;
405 apr_array_header_t *prop_changes;
407 SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
408 NULL, NULL, NULL, NULL, &working_checksum, NULL,
409 NULL, NULL, NULL, NULL, NULL, &recorded_size,
410 &recorded_time, NULL, NULL, NULL,
411 &had_props, &props_mod, NULL, NULL, NULL,
412 db, local_abspath, scratch_pool, scratch_pool));
413 checksum = working_checksum;
415 assert(status == svn_wc__db_status_normal
416 || status == svn_wc__db_status_added
417 || (status == svn_wc__db_status_deleted && diff_pristine));
419 if (status != svn_wc__db_status_normal)
421 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
422 NULL, NULL, NULL, NULL, NULL, NULL,
423 NULL, &checksum, NULL, NULL, &had_props,
426 scratch_pool, scratch_pool));
427 recorded_size = SVN_INVALID_FILESIZE;
429 props_mod = TRUE; /* Requires compare */
431 else if (diff_pristine)
435 const svn_io_dirent2_t *dirent;
437 /* Verify truename to mimic status for iota/IOTA difference on Windows */
438 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
439 TRUE /* verify truename */,
440 TRUE /* ingore_enoent */,
441 scratch_pool, scratch_pool));
443 /* If a file does not exist on disk (missing/obstructed) then we
444 can't provide a text diff */
445 if (dirent->kind != svn_node_file
446 || (dirent->kind == svn_node_file
447 && dirent->filesize == recorded_size
448 && dirent->mtime == recorded_time))
454 if (files_same && !props_mod)
455 return SVN_NO_ERROR; /* Cheap exit */
459 if (!SVN_IS_VALID_REVNUM(revision))
460 revision = db_revision;
462 left_src = svn_diff__source_create(revision, scratch_pool);
463 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
465 SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
468 NULL /* copyfrom_src */,
471 scratch_pool, scratch_pool));
476 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
477 db, local_abspath, checksum,
478 scratch_pool, scratch_pool));
481 SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
484 scratch_pool, scratch_pool));
485 else if (! (had_props || props_mod))
486 local_file = local_abspath;
488 local_file = pristine_file;
490 SVN_ERR(svn_wc__internal_translated_file(
491 &local_file, local_abspath,
493 SVN_WC_TRANSLATE_TO_NF
494 | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
495 cancel_func, cancel_baton,
496 scratch_pool, scratch_pool));
499 SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
500 pristine_file, scratch_pool));
503 SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
504 scratch_pool, scratch_pool));
506 base_props = apr_hash_make(scratch_pool);
508 if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
509 local_props = base_props;
510 else if (diff_pristine)
511 SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
512 scratch_pool, scratch_pool));
514 SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
515 scratch_pool, scratch_pool));
517 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
519 if (prop_changes->nelts || !files_same)
521 SVN_ERR(processor->file_changed(relpath,
536 SVN_ERR(processor->file_closed(relpath,
548 ensure_local_info(struct dir_baton_t *db,
549 apr_pool_t *scratch_pool)
551 apr_hash_t *conflicts;
556 SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
557 db->eb->db, db->local_abspath,
558 FALSE /* base_tree_only */,
559 db->pool, scratch_pool));
564 /* Called when the directory is closed to compare any elements that have
565 * not yet been compared. This identifies local, working copy only
566 * changes. At this stage we are dealing with files/directories that do
567 * exist in the working copy.
569 * DIR_BATON is the baton for the directory.
572 walk_local_nodes_diff(struct edit_baton_t *eb,
573 const char *local_abspath,
576 apr_hash_t *compared,
578 apr_pool_t *scratch_pool)
580 svn_wc__db_t *db = eb->db;
581 svn_boolean_t in_anchor_not_target;
582 apr_pool_t *iterpool;
583 void *dir_baton = NULL;
584 svn_boolean_t skip = FALSE;
585 svn_boolean_t skip_children = FALSE;
586 svn_revnum_t revision;
587 svn_boolean_t props_mod;
588 svn_diff_source_t *left_src;
589 svn_diff_source_t *right_src;
591 /* Everything we do below is useless if we are comparing to BASE. */
592 if (eb->diff_pristine)
595 /* Determine if this is the anchor directory if the anchor is different
596 to the target. When the target is a file, the anchor is the parent
597 directory and if this is that directory the non-target entries must be
599 in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
601 iterpool = svn_pool_create(scratch_pool);
603 SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
604 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
605 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
606 NULL, &props_mod, NULL, NULL, NULL,
607 db, local_abspath, scratch_pool, scratch_pool));
609 left_src = svn_diff__source_create(revision, scratch_pool);
610 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
614 dir_baton = parent_baton;
617 else if (!in_anchor_not_target)
618 SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
622 NULL /* copyfrom_src */,
625 scratch_pool, scratch_pool));
628 if (!skip_children && depth != svn_depth_empty)
631 apr_hash_t *conflicts;
632 apr_array_header_t *children;
633 svn_depth_t depth_below_here = depth;
634 svn_boolean_t diff_files;
635 svn_boolean_t diff_dirs;
638 if (depth_below_here == svn_depth_immediates)
639 depth_below_here = svn_depth_empty;
641 diff_files = (depth == svn_depth_unknown
642 || depth >= svn_depth_files);
643 diff_dirs = (depth == svn_depth_unknown
644 || depth >= svn_depth_immediates);
646 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
648 FALSE /* base_tree_only */,
649 scratch_pool, iterpool));
651 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
654 for (i = 0; i < children->nelts; i++)
656 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
658 const char *name = item->key;
659 struct svn_wc__db_info_t *info = item->value;
661 const char *child_abspath;
662 const char *child_relpath;
663 svn_boolean_t repos_only;
664 svn_boolean_t local_only;
665 svn_node_kind_t base_kind;
668 SVN_ERR(eb->cancel_func(eb->cancel_baton));
670 /* In the anchor directory, if the anchor is not the target then all
671 entries other than the target should not be diff'd. Running diff
672 on one file in a directory should not diff other files in that
674 if (in_anchor_not_target && strcmp(eb->target, name))
677 if (compared && svn_hash_gets(compared, name))
680 if (NOT_PRESENT(info->status))
683 assert(info->status == svn_wc__db_status_normal
684 || info->status == svn_wc__db_status_added
685 || info->status == svn_wc__db_status_deleted);
687 svn_pool_clear(iterpool);
688 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
689 child_relpath = svn_relpath_join(path, name, iterpool);
694 if (!info->have_base)
696 local_only = TRUE; /* Only report additions */
698 if (info->status == svn_wc__db_status_deleted)
699 continue; /* Nothing added (deleted copy) */
701 else if (info->status == svn_wc__db_status_normal)
704 base_kind = info->kind;
706 else if (info->status == svn_wc__db_status_deleted
707 && (!eb->diff_pristine || !info->have_more_work))
709 svn_wc__db_status_t base_status;
711 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
712 NULL, NULL, NULL, NULL, NULL,
713 NULL, NULL, NULL, NULL, NULL,
716 iterpool, iterpool));
718 if (NOT_PRESENT(base_status))
723 /* working status is either added or deleted */
724 svn_wc__db_status_t base_status;
726 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
727 NULL, NULL, NULL, NULL, NULL,
728 NULL, NULL, NULL, NULL, NULL,
731 iterpool, iterpool));
733 if (NOT_PRESENT(base_status))
735 else if (base_kind != info->kind || !eb->ignore_ancestry)
742 if (eb->local_before_remote && local_only)
744 if (info->kind == svn_node_file && diff_files)
745 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
747 eb->processor, dir_baton,
752 else if (info->kind == svn_node_dir && diff_dirs)
753 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
756 eb->processor, dir_baton,
765 /* Report repository form deleted */
766 if (base_kind == svn_node_file && diff_files)
767 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
768 child_relpath, eb->revnum,
769 eb->processor, dir_baton,
771 else if (base_kind == svn_node_dir && diff_dirs)
772 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
773 child_relpath, eb->revnum,
775 eb->processor, dir_baton,
780 else if (!local_only) /* Not local only nor remote only */
782 /* Diff base against actual */
783 if (info->kind == svn_node_file && diff_files)
785 if (info->status != svn_wc__db_status_normal
786 || !eb->diff_pristine)
788 SVN_ERR(svn_wc__diff_base_working_diff(
792 eb->processor, dir_baton,
799 else if (info->kind == svn_node_dir && diff_dirs)
800 SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
808 if (!eb->local_before_remote && local_only)
810 if (info->kind == svn_node_file && diff_files)
811 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
813 eb->processor, dir_baton,
818 else if (info->kind == svn_node_dir && diff_dirs)
819 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
820 child_relpath, depth_below_here,
821 eb->processor, dir_baton,
833 /* Check for local property mods on this directory, if we haven't
834 already reported them. */
836 && ! in_anchor_not_target
839 apr_array_header_t *propchanges;
840 apr_hash_t *left_props;
841 apr_hash_t *right_props;
843 SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
845 scratch_pool, scratch_pool));
847 right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
849 SVN_ERR(eb->processor->dir_changed(path,
860 SVN_ERR(eb->processor->dir_closed(path,
867 svn_pool_destroy(iterpool);
873 svn_wc__diff_local_only_file(svn_wc__db_t *db,
874 const char *local_abspath,
876 const svn_diff_tree_processor_t *processor,
877 void *processor_parent_baton,
878 svn_boolean_t diff_pristine,
879 svn_cancel_func_t cancel_func,
881 apr_pool_t *scratch_pool)
883 svn_diff_source_t *right_src;
884 svn_diff_source_t *copyfrom_src = NULL;
885 svn_wc__db_status_t status;
886 svn_node_kind_t kind;
887 const svn_checksum_t *checksum;
888 const char *original_repos_relpath;
889 svn_revnum_t original_revision;
890 svn_boolean_t had_props;
891 svn_boolean_t props_mod;
892 apr_hash_t *pristine_props;
893 apr_hash_t *right_props = NULL;
894 const char *pristine_file;
895 const char *translated_file;
896 svn_revnum_t revision;
897 void *file_baton = NULL;
898 svn_boolean_t skip = FALSE;
899 svn_boolean_t file_mod = TRUE;
901 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
902 NULL, NULL, NULL, NULL, &checksum, NULL,
903 &original_repos_relpath, NULL, NULL,
904 &original_revision, NULL, NULL, NULL,
905 NULL, NULL, NULL, &had_props,
906 &props_mod, NULL, NULL, NULL,
908 scratch_pool, scratch_pool));
910 assert(kind == svn_node_file
911 && (status == svn_wc__db_status_normal
912 || status == svn_wc__db_status_added
913 || (status == svn_wc__db_status_deleted && diff_pristine)));
916 if (status == svn_wc__db_status_deleted)
918 assert(diff_pristine);
920 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
921 NULL, &checksum, NULL, &had_props,
924 scratch_pool, scratch_pool));
928 pristine_props = apr_hash_make(scratch_pool);
930 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
932 scratch_pool, scratch_pool));
934 if (original_repos_relpath)
936 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
937 copyfrom_src->repos_relpath = original_repos_relpath;
940 if (props_mod || !SVN_IS_VALID_REVNUM(revision))
941 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
947 SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
948 FALSE, scratch_pool));
951 right_src = svn_diff__source_create(revision, scratch_pool);
953 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
956 SVN_ERR(processor->file_opened(&file_baton, &skip,
958 NULL /* left_source */,
961 processor_parent_baton,
963 scratch_pool, scratch_pool));
968 if (props_mod && !diff_pristine)
969 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
970 scratch_pool, scratch_pool));
972 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
975 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
976 checksum, scratch_pool, scratch_pool));
978 pristine_file = NULL;
982 translated_file = pristine_file; /* No translation needed */
986 SVN_ERR(svn_wc__internal_translated_file(
987 &translated_file, local_abspath, db, local_abspath,
988 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
989 cancel_func, cancel_baton,
990 scratch_pool, scratch_pool));
993 SVN_ERR(processor->file_added(relpath,
1008 return SVN_NO_ERROR;
1012 svn_wc__diff_local_only_dir(svn_wc__db_t *db,
1013 const char *local_abspath,
1014 const char *relpath,
1016 const svn_diff_tree_processor_t *processor,
1017 void *processor_parent_baton,
1018 svn_boolean_t diff_pristine,
1019 svn_cancel_func_t cancel_func,
1021 apr_pool_t *scratch_pool)
1023 svn_wc__db_status_t status;
1024 svn_node_kind_t kind;
1025 svn_boolean_t had_props;
1026 svn_boolean_t props_mod;
1027 const char *original_repos_relpath;
1028 svn_revnum_t original_revision;
1029 svn_diff_source_t *copyfrom_src = NULL;
1030 apr_hash_t *pristine_props;
1031 const apr_array_header_t *children;
1033 apr_pool_t *iterpool;
1035 svn_boolean_t skip = FALSE;
1036 svn_boolean_t skip_children = FALSE;
1037 svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
1040 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
1041 NULL, NULL, NULL, NULL, NULL, NULL,
1042 &original_repos_relpath, NULL, NULL,
1043 &original_revision, NULL, NULL, NULL,
1044 NULL, NULL, NULL, &had_props,
1045 &props_mod, NULL, NULL, NULL,
1047 scratch_pool, scratch_pool));
1048 if (original_repos_relpath)
1050 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
1051 copyfrom_src->repos_relpath = original_repos_relpath;
1054 /* svn_wc__db_status_incomplete should never happen, as the result won't be
1055 stable or guaranteed related to what is in the repository for this
1056 revision, but without this it would be hard to diagnose that status... */
1057 assert(kind == svn_node_dir
1058 && (status == svn_wc__db_status_normal
1059 || status == svn_wc__db_status_incomplete
1060 || status == svn_wc__db_status_added
1061 || (status == svn_wc__db_status_deleted && diff_pristine)));
1063 if (status == svn_wc__db_status_deleted)
1065 assert(diff_pristine);
1067 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1068 NULL, NULL, NULL, &had_props,
1071 scratch_pool, scratch_pool));
1074 else if (!had_props)
1075 pristine_props = apr_hash_make(scratch_pool);
1077 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
1079 scratch_pool, scratch_pool));
1081 /* Report the addition of the directory's contents. */
1082 iterpool = svn_pool_create(scratch_pool);
1084 SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
1089 processor_parent_baton,
1091 scratch_pool, iterpool));
1093 if ((depth > svn_depth_empty || depth == svn_depth_unknown)
1096 svn_depth_t depth_below_here = depth;
1098 apr_hash_t *conflicts;
1100 if (depth_below_here == svn_depth_immediates)
1101 depth_below_here = svn_depth_empty;
1103 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
1105 FALSE /* base_tree_only */,
1106 scratch_pool, iterpool));
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,
1173 apr_hash_t *right_props;
1175 if (props_mod && !diff_pristine)
1176 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1177 scratch_pool, scratch_pool));
1179 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1181 SVN_ERR(processor->dir_added(relpath,
1192 svn_pool_destroy(iterpool);
1194 return SVN_NO_ERROR;
1197 /* Reports local changes. */
1198 static svn_error_t *
1199 handle_local_only(struct dir_baton_t *pb,
1201 apr_pool_t *scratch_pool)
1203 struct edit_baton_t *eb = pb->eb;
1204 const struct svn_wc__db_info_t *info;
1205 svn_boolean_t repos_delete = (pb->deletes
1206 && svn_hash_gets(pb->deletes, name));
1208 assert(!strchr(name, '/'));
1209 assert(!pb->added || eb->ignore_ancestry);
1211 if (pb->skip_children)
1212 return SVN_NO_ERROR;
1214 SVN_ERR(ensure_local_info(pb, scratch_pool));
1216 info = svn_hash_gets(pb->local_info, name);
1218 if (info == NULL || NOT_PRESENT(info->status))
1219 return SVN_NO_ERROR;
1221 switch (info->status)
1223 case svn_wc__db_status_incomplete:
1224 return SVN_NO_ERROR; /* Not local only */
1226 case svn_wc__db_status_normal:
1228 return SVN_NO_ERROR; /* Local and remote */
1229 svn_hash_sets(pb->deletes, name, NULL);
1232 case svn_wc__db_status_deleted:
1233 if (!(eb->diff_pristine && repos_delete))
1234 return SVN_NO_ERROR;
1237 case svn_wc__db_status_added:
1242 if (info->kind == svn_node_dir)
1246 if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
1249 depth = svn_depth_empty;
1251 SVN_ERR(svn_wc__diff_local_only_dir(
1253 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1254 svn_relpath_join(pb->relpath, name, scratch_pool),
1255 repos_delete ? svn_depth_infinity : depth,
1256 eb->processor, pb->pdb,
1258 eb->cancel_func, eb->cancel_baton,
1262 SVN_ERR(svn_wc__diff_local_only_file(
1264 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1265 svn_relpath_join(pb->relpath, name, scratch_pool),
1266 eb->processor, pb->pdb,
1268 eb->cancel_func, eb->cancel_baton,
1271 return SVN_NO_ERROR;
1274 /* Reports a file LOCAL_ABSPATH in BASE as deleted */
1276 svn_wc__diff_base_only_file(svn_wc__db_t *db,
1277 const char *local_abspath,
1278 const char *relpath,
1279 svn_revnum_t revision,
1280 const svn_diff_tree_processor_t *processor,
1281 void *processor_parent_baton,
1282 apr_pool_t *scratch_pool)
1284 svn_wc__db_status_t status;
1285 svn_node_kind_t kind;
1286 const svn_checksum_t *checksum;
1288 void *file_baton = NULL;
1289 svn_boolean_t skip = FALSE;
1290 svn_diff_source_t *left_src;
1291 const char *pristine_file;
1293 SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
1294 SVN_IS_VALID_REVNUM(revision)
1296 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1297 &checksum, NULL, NULL, NULL, &props, NULL,
1299 scratch_pool, scratch_pool));
1301 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
1302 && kind == svn_node_file
1305 left_src = svn_diff__source_create(revision, scratch_pool);
1307 SVN_ERR(processor->file_opened(&file_baton, &skip,
1310 NULL /* right_src */,
1311 NULL /* copyfrom_source */,
1312 processor_parent_baton,
1314 scratch_pool, scratch_pool));
1317 return SVN_NO_ERROR;
1319 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
1320 db, local_abspath, checksum,
1321 scratch_pool, scratch_pool));
1323 SVN_ERR(processor->file_deleted(relpath,
1331 return SVN_NO_ERROR;
1335 svn_wc__diff_base_only_dir(svn_wc__db_t *db,
1336 const char *local_abspath,
1337 const char *relpath,
1338 svn_revnum_t revision,
1340 const svn_diff_tree_processor_t *processor,
1341 void *processor_parent_baton,
1342 svn_cancel_func_t cancel_func,
1344 apr_pool_t *scratch_pool)
1346 void *dir_baton = NULL;
1347 svn_boolean_t skip = FALSE;
1348 svn_boolean_t skip_children = FALSE;
1349 svn_diff_source_t *left_src;
1350 svn_revnum_t report_rev = revision;
1352 if (!SVN_IS_VALID_REVNUM(report_rev))
1353 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
1354 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1357 scratch_pool, scratch_pool));
1359 left_src = svn_diff__source_create(report_rev, scratch_pool);
1361 SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
1364 NULL /* right_src */,
1365 NULL /* copyfrom_src */,
1366 processor_parent_baton,
1368 scratch_pool, scratch_pool));
1370 if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
1373 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1374 apr_array_header_t *children;
1377 SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
1378 scratch_pool, iterpool));
1380 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1383 for (i = 0; i < children->nelts; i++)
1385 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1387 const char *name = item->key;
1388 struct svn_wc__db_base_info_t *info = item->value;
1389 const char *child_abspath;
1390 const char *child_relpath;
1392 if (info->status != svn_wc__db_status_normal)
1396 SVN_ERR(cancel_func(cancel_baton));
1398 svn_pool_clear(iterpool);
1400 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1401 child_relpath = svn_relpath_join(relpath, name, iterpool);
1406 case svn_node_symlink:
1407 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
1410 processor, dir_baton,
1414 if (depth > svn_depth_files || depth == svn_depth_unknown)
1416 svn_depth_t depth_below_here = depth;
1418 if (depth_below_here == svn_depth_immediates)
1419 depth_below_here = svn_depth_empty;
1421 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
1425 processor, dir_baton,
1441 SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
1442 scratch_pool, scratch_pool));
1444 SVN_ERR(processor->dir_deleted(relpath,
1452 return SVN_NO_ERROR;
1455 /* An svn_delta_editor_t function. */
1456 static svn_error_t *
1457 set_target_revision(void *edit_baton,
1458 svn_revnum_t target_revision,
1461 struct edit_baton_t *eb = edit_baton;
1462 eb->revnum = target_revision;
1464 return SVN_NO_ERROR;
1467 /* An svn_delta_editor_t function. The root of the comparison hierarchy */
1468 static svn_error_t *
1469 open_root(void *edit_baton,
1470 svn_revnum_t base_revision,
1471 apr_pool_t *dir_pool,
1474 struct edit_baton_t *eb = edit_baton;
1475 struct dir_baton_t *db;
1477 eb->root_opened = TRUE;
1478 db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
1481 if (eb->target[0] == '\0')
1483 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1484 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1486 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
1491 NULL /* copyfrom_source */,
1492 NULL /* parent_baton */,
1494 db->pool, db->pool));
1497 db->skip = TRUE; /* Skip this, but not the children */
1499 return SVN_NO_ERROR;
1502 /* An svn_delta_editor_t function. */
1503 static svn_error_t *
1504 delete_entry(const char *path,
1505 svn_revnum_t base_revision,
1509 struct dir_baton_t *pb = parent_baton;
1510 const char *name = svn_dirent_basename(path, pb->pool);
1513 pb->deletes = apr_hash_make(pb->pool);
1515 svn_hash_sets(pb->deletes, name, "");
1516 return SVN_NO_ERROR;
1519 /* An svn_delta_editor_t function. */
1520 static svn_error_t *
1521 add_directory(const char *path,
1523 const char *copyfrom_path,
1524 svn_revnum_t copyfrom_revision,
1525 apr_pool_t *dir_pool,
1528 struct dir_baton_t *pb = parent_baton;
1529 struct edit_baton_t *eb = pb->eb;
1530 struct dir_baton_t *db;
1531 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1532 ? svn_depth_empty : pb->depth;
1534 db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
1538 if (pb->repos_only || !eb->ignore_ancestry)
1539 db->repos_only = TRUE;
1542 struct svn_wc__db_info_t *info;
1543 SVN_ERR(ensure_local_info(pb, dir_pool));
1545 info = svn_hash_gets(pb->local_info, db->name);
1547 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1548 db->repos_only = TRUE;
1550 if (!db->repos_only && info->status != svn_wc__db_status_added)
1551 db->repos_only = TRUE;
1553 if (!db->repos_only)
1555 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1556 db->ignoring_ancestry = TRUE;
1558 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1562 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1564 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1565 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1567 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1571 NULL /* copyfrom src */,
1574 db->pool, db->pool));
1576 return SVN_NO_ERROR;
1579 /* An svn_delta_editor_t function. */
1580 static svn_error_t *
1581 open_directory(const char *path,
1583 svn_revnum_t base_revision,
1584 apr_pool_t *dir_pool,
1587 struct dir_baton_t *pb = parent_baton;
1588 struct edit_baton_t *eb = pb->eb;
1589 struct dir_baton_t *db;
1590 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1591 ? svn_depth_empty : pb->depth;
1593 /* Allocate path from the parent pool since the memory is used in the
1594 parent's compared hash */
1595 db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
1599 db->repos_only = TRUE;
1602 struct svn_wc__db_info_t *info;
1603 SVN_ERR(ensure_local_info(pb, dir_pool));
1605 info = svn_hash_gets(pb->local_info, db->name);
1607 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1608 db->repos_only = TRUE;
1610 if (!db->repos_only)
1611 switch (info->status)
1613 case svn_wc__db_status_normal:
1615 case svn_wc__db_status_deleted:
1616 db->repos_only = TRUE;
1618 if (!info->have_more_work)
1619 svn_hash_sets(pb->compared,
1620 apr_pstrdup(pb->pool, db->name), "");
1622 case svn_wc__db_status_added:
1623 if (eb->ignore_ancestry)
1624 db->ignoring_ancestry = TRUE;
1626 db->repos_only = TRUE;
1629 SVN_ERR_MALFUNCTION();
1632 if (!db->repos_only)
1634 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1635 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1639 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1641 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1642 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1644 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1648 NULL /* copyfrom src */,
1651 db->pool, db->pool));
1653 return SVN_NO_ERROR;
1657 /* An svn_delta_editor_t function. When a directory is closed, all the
1658 * directory elements that have been added or replaced will already have been
1659 * diff'd. However there may be other elements in the working copy
1660 * that have not yet been considered. */
1661 static svn_error_t *
1662 close_directory(void *dir_baton,
1665 struct dir_baton_t *db = dir_baton;
1666 struct dir_baton_t *pb = db->parent_baton;
1667 struct edit_baton_t *eb = db->eb;
1668 apr_pool_t *scratch_pool = db->pool;
1669 svn_boolean_t reported_closed = FALSE;
1671 if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
1673 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1674 apr_array_header_t *children;
1676 children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
1679 for (i = 0; i < children->nelts; i++)
1681 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1683 const char *name = item->key;
1685 svn_pool_clear(iterpool);
1686 SVN_ERR(handle_local_only(db, name, iterpool));
1688 svn_hash_sets(db->compared, name, "");
1691 svn_pool_destroy(iterpool);
1694 /* Report local modifications for this directory. Skip added
1695 directories since they can only contain added elements, all of
1696 which have already been diff'd. */
1697 if (!db->repos_only && !db->skip_children)
1699 SVN_ERR(walk_local_nodes_diff(eb,
1708 /* Report the property changes on the directory itself, if necessary. */
1711 /* Diff processor requested no directory details */
1713 else if (db->propchanges->nelts > 0 || db->repos_only)
1715 apr_hash_t *repos_props;
1719 repos_props = apr_hash_make(scratch_pool);
1723 SVN_ERR(svn_wc__db_base_get_props(&repos_props,
1724 eb->db, db->local_abspath,
1725 scratch_pool, scratch_pool));
1728 /* Add received property changes and entry props */
1729 if (db->propchanges->nelts)
1730 repos_props = svn_prop__patch(repos_props, db->propchanges,
1735 SVN_ERR(eb->processor->dir_deleted(db->relpath,
1741 reported_closed = TRUE;
1745 apr_hash_t *local_props;
1746 apr_array_header_t *prop_changes;
1748 if (eb->diff_pristine)
1749 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1750 NULL, NULL, NULL, NULL,
1752 eb->db, db->local_abspath,
1753 scratch_pool, scratch_pool));
1755 SVN_ERR(svn_wc__db_read_props(&local_props,
1756 eb->db, db->local_abspath,
1757 scratch_pool, scratch_pool));
1759 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
1762 /* ### as a good diff processor we should now only report changes
1763 if there are non-entry changes, but for now we stick to
1766 if (prop_changes->nelts)
1768 SVN_ERR(eb->processor->dir_changed(db->relpath,
1777 reported_closed = TRUE;
1782 /* Mark this directory as compared in the parent directory's baton,
1783 unless this is the root of the comparison. */
1784 if (!reported_closed && !db->skip)
1785 SVN_ERR(eb->processor->dir_closed(db->relpath,
1792 if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1793 SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
1795 SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
1797 return SVN_NO_ERROR;
1800 /* An svn_delta_editor_t function. */
1801 static svn_error_t *
1802 add_file(const char *path,
1804 const char *copyfrom_path,
1805 svn_revnum_t copyfrom_revision,
1806 apr_pool_t *file_pool,
1809 struct dir_baton_t *pb = parent_baton;
1810 struct edit_baton_t *eb = pb->eb;
1811 struct file_baton_t *fb;
1813 fb = make_file_baton(path, TRUE, pb, file_pool);
1816 if (pb->skip_children)
1819 return SVN_NO_ERROR;
1821 else if (pb->repos_only || !eb->ignore_ancestry)
1822 fb->repos_only = TRUE;
1825 struct svn_wc__db_info_t *info;
1826 SVN_ERR(ensure_local_info(pb, file_pool));
1828 info = svn_hash_gets(pb->local_info, fb->name);
1830 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1831 fb->repos_only = TRUE;
1833 if (!fb->repos_only && info->status != svn_wc__db_status_added)
1834 fb->repos_only = TRUE;
1836 if (!fb->repos_only)
1838 /* Add this path to the parent directory's list of elements that
1839 have been compared. */
1840 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1841 fb->ignoring_ancestry = TRUE;
1843 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1847 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1849 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1853 NULL /* copyfrom src */,
1856 fb->pool, fb->pool));
1858 return SVN_NO_ERROR;
1861 /* An svn_delta_editor_t function. */
1862 static svn_error_t *
1863 open_file(const char *path,
1865 svn_revnum_t base_revision,
1866 apr_pool_t *file_pool,
1869 struct dir_baton_t *pb = parent_baton;
1870 struct edit_baton_t *eb = pb->eb;
1871 struct file_baton_t *fb;
1873 fb = make_file_baton(path, FALSE, pb, file_pool);
1876 if (pb->skip_children)
1878 else if (pb->repos_only)
1879 fb->repos_only = TRUE;
1882 struct svn_wc__db_info_t *info;
1883 SVN_ERR(ensure_local_info(pb, file_pool));
1885 info = svn_hash_gets(pb->local_info, fb->name);
1887 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1888 fb->repos_only = TRUE;
1890 if (!fb->repos_only)
1891 switch (info->status)
1893 case svn_wc__db_status_normal:
1895 case svn_wc__db_status_deleted:
1896 fb->repos_only = TRUE;
1897 if (!info->have_more_work)
1898 svn_hash_sets(pb->compared,
1899 apr_pstrdup(pb->pool, fb->name), "");
1901 case svn_wc__db_status_added:
1902 if (eb->ignore_ancestry)
1903 fb->ignoring_ancestry = TRUE;
1905 fb->repos_only = TRUE;
1908 SVN_ERR_MALFUNCTION();
1911 if (!fb->repos_only)
1913 /* Add this path to the parent directory's list of elements that
1914 have been compared. */
1915 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1916 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1920 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1922 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1923 NULL, NULL, NULL, &fb->base_checksum, NULL,
1924 NULL, NULL, &fb->base_props, NULL,
1925 eb->db, fb->local_abspath,
1926 fb->pool, fb->pool));
1928 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1932 NULL /* copyfrom src */,
1935 fb->pool, fb->pool));
1937 return SVN_NO_ERROR;
1940 /* An svn_delta_editor_t function. */
1941 static svn_error_t *
1942 apply_textdelta(void *file_baton,
1943 const char *base_checksum_hex,
1945 svn_txdelta_window_handler_t *handler,
1946 void **handler_baton)
1948 struct file_baton_t *fb = file_baton;
1949 struct edit_baton_t *eb = fb->eb;
1950 svn_stream_t *source;
1951 svn_stream_t *temp_stream;
1952 svn_checksum_t *repos_checksum = NULL;
1956 *handler = svn_delta_noop_window_handler;
1957 *handler_baton = NULL;
1958 return SVN_NO_ERROR;
1961 if (base_checksum_hex && fb->base_checksum)
1963 const svn_checksum_t *base_md5;
1964 SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
1965 base_checksum_hex, pool));
1967 SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
1968 eb->db, eb->anchor_abspath,
1972 if (! svn_checksum_match(repos_checksum, base_md5))
1974 /* ### I expect that there are some bad drivers out there
1975 ### that used to give bad results. We could look in
1976 ### working to see if the expected checksum matches and
1977 ### then return the pristine of that... But that only moves
1980 /* If needed: compare checksum obtained via md5 of working.
1981 And if they match set fb->base_checksum and fb->base_props */
1983 return svn_checksum_mismatch_err(
1987 _("Checksum mismatch for '%s'"),
1988 svn_dirent_local_style(fb->local_abspath,
1992 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1993 eb->db, fb->local_abspath,
1997 else if (fb->base_checksum)
1999 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
2000 eb->db, fb->local_abspath,
2005 source = svn_stream_empty(pool);
2007 /* This is the file that will contain the pristine repository version. */
2008 SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
2009 svn_io_file_del_on_pool_cleanup,
2010 fb->pool, fb->pool));
2012 svn_txdelta_apply(source, temp_stream,
2014 fb->local_abspath /* error_info */,
2016 handler, handler_baton);
2018 return SVN_NO_ERROR;
2021 /* An svn_delta_editor_t function. When the file is closed we have a temporary
2022 * file containing a pristine version of the repository file. This can
2023 * be compared against the working copy.
2025 * Ignore TEXT_CHECKSUM.
2027 static svn_error_t *
2028 close_file(void *file_baton,
2029 const char *expected_md5_digest,
2032 struct file_baton_t *fb = file_baton;
2033 struct dir_baton_t *pb = fb->parent_baton;
2034 struct edit_baton_t *eb = fb->eb;
2035 apr_pool_t *scratch_pool = fb->pool;
2037 /* The repository information; constructed from BASE + Changes */
2038 const char *repos_file;
2039 apr_hash_t *repos_props;
2043 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2044 SVN_ERR(maybe_done(pb));
2045 return SVN_NO_ERROR;
2048 if (expected_md5_digest != NULL)
2050 svn_checksum_t *expected_checksum;
2051 const svn_checksum_t *result_checksum;
2053 if (fb->temp_file_path)
2054 result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
2057 result_checksum = fb->base_checksum;
2059 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
2060 expected_md5_digest, scratch_pool));
2062 if (result_checksum->kind != svn_checksum_md5)
2063 SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
2064 eb->db, fb->local_abspath,
2066 scratch_pool, scratch_pool));
2068 if (!svn_checksum_match(expected_checksum, result_checksum))
2069 return svn_checksum_mismatch_err(
2073 _("Checksum mismatch for '%s'"),
2074 svn_dirent_local_style(fb->local_abspath,
2078 if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2079 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2082 apr_hash_t *prop_base;
2085 prop_base = apr_hash_make(scratch_pool);
2087 prop_base = fb->base_props;
2089 /* includes entry props */
2090 repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
2092 repos_file = fb->temp_file_path;
2095 assert(fb->base_checksum);
2096 SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
2097 eb->db, eb->anchor_abspath,
2099 scratch_pool, scratch_pool));
2105 SVN_ERR(eb->processor->file_deleted(fb->relpath,
2115 /* Produce a diff of actual or pristine against repos */
2116 apr_hash_t *local_props;
2117 apr_array_header_t *prop_changes;
2118 const char *localfile;
2120 /* pb->local_info contains some information that might allow optimizing
2123 if (eb->diff_pristine)
2125 const svn_checksum_t *checksum;
2126 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
2127 NULL, &checksum, NULL, NULL,
2129 eb->db, fb->local_abspath,
2130 scratch_pool, scratch_pool));
2132 SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
2133 eb->db, eb->anchor_abspath,
2135 scratch_pool, scratch_pool));
2139 SVN_ERR(svn_wc__db_read_props(&local_props,
2140 eb->db, fb->local_abspath,
2141 scratch_pool, scratch_pool));
2143 /* a detranslated version of the working file */
2144 SVN_ERR(svn_wc__internal_translated_file(
2145 &localfile, fb->local_abspath, eb->db, fb->local_abspath,
2146 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
2147 eb->cancel_func, eb->cancel_baton,
2148 scratch_pool, scratch_pool));
2151 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
2155 /* ### as a good diff processor we should now only report changes, and
2156 report file_closed() in other cases */
2157 SVN_ERR(eb->processor->file_changed(fb->relpath,
2160 repos_file /* left file */,
2161 localfile /* right file */,
2162 repos_props /* left_props */,
2163 local_props /* right props */,
2164 TRUE /* ### file_modified */,
2171 if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2172 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2174 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2175 SVN_ERR(maybe_done(pb));
2176 return SVN_NO_ERROR;
2180 /* An svn_delta_editor_t function. */
2181 static svn_error_t *
2182 change_file_prop(void *file_baton,
2184 const svn_string_t *value,
2187 struct file_baton_t *fb = file_baton;
2188 svn_prop_t *propchange;
2189 svn_prop_kind_t propkind;
2191 propkind = svn_property_kind2(name);
2192 if (propkind == svn_prop_wc_kind)
2193 return SVN_NO_ERROR;
2194 else if (propkind == svn_prop_regular_kind)
2195 fb->has_propchange = TRUE;
2197 propchange = apr_array_push(fb->propchanges);
2198 propchange->name = apr_pstrdup(fb->pool, name);
2199 propchange->value = svn_string_dup(value, fb->pool);
2201 return SVN_NO_ERROR;
2205 /* An svn_delta_editor_t function. */
2206 static svn_error_t *
2207 change_dir_prop(void *dir_baton,
2209 const svn_string_t *value,
2212 struct dir_baton_t *db = dir_baton;
2213 svn_prop_t *propchange;
2214 svn_prop_kind_t propkind;
2216 propkind = svn_property_kind2(name);
2217 if (propkind == svn_prop_wc_kind)
2218 return SVN_NO_ERROR;
2219 else if (propkind == svn_prop_regular_kind)
2220 db->has_propchange = TRUE;
2222 propchange = apr_array_push(db->propchanges);
2223 propchange->name = apr_pstrdup(db->pool, name);
2224 propchange->value = svn_string_dup(value, db->pool);
2226 return SVN_NO_ERROR;
2230 /* An svn_delta_editor_t function. */
2231 static svn_error_t *
2232 close_edit(void *edit_baton,
2235 struct edit_baton_t *eb = edit_baton;
2237 if (!eb->root_opened)
2239 SVN_ERR(walk_local_nodes_diff(eb,
2243 NULL /* compared */,
2244 NULL /* No parent_baton */,
2248 return SVN_NO_ERROR;
2251 /* Public Interface */
2254 /* Create a diff editor and baton. */
2256 svn_wc__get_diff_editor(const svn_delta_editor_t **editor,
2258 svn_wc_context_t *wc_ctx,
2259 const char *anchor_abspath,
2262 svn_boolean_t ignore_ancestry,
2263 svn_boolean_t use_text_base,
2264 svn_boolean_t reverse_order,
2265 svn_boolean_t server_performs_filtering,
2266 const apr_array_header_t *changelist_filter,
2267 const svn_diff_tree_processor_t *diff_processor,
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);
2281 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
2283 /* Apply changelist filtering to the output */
2284 if (changelist_filter && changelist_filter->nelts)
2286 apr_hash_t *changelist_hash;
2288 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
2290 diff_processor = svn_wc__changelist_filter_tree_processor_create(
2291 diff_processor, wc_ctx, anchor_abspath,
2292 changelist_hash, result_pool);
2295 SVN_ERR(make_edit_baton(&eb,
2297 anchor_abspath, target,
2299 depth, ignore_ancestry,
2300 use_text_base, reverse_order,
2301 cancel_func, cancel_baton,
2304 tree_editor = svn_delta_default_editor(eb->pool);
2306 tree_editor->set_target_revision = set_target_revision;
2307 tree_editor->open_root = open_root;
2308 tree_editor->delete_entry = delete_entry;
2309 tree_editor->add_directory = add_directory;
2310 tree_editor->open_directory = open_directory;
2311 tree_editor->close_directory = close_directory;
2312 tree_editor->add_file = add_file;
2313 tree_editor->open_file = open_file;
2314 tree_editor->apply_textdelta = apply_textdelta;
2315 tree_editor->change_file_prop = change_file_prop;
2316 tree_editor->change_dir_prop = change_dir_prop;
2317 tree_editor->close_file = close_file;
2318 tree_editor->close_edit = close_edit;
2320 inner_editor = tree_editor;
2323 if (!server_performs_filtering
2324 && depth == svn_depth_unknown)
2325 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2334 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
2342 sfb = apr_palloc(result_pool, sizeof(*sfb));
2343 sfb->db = wc_ctx->db;
2344 sfb->base_abspath = eb->anchor_abspath;
2345 sfb->fetch_base = TRUE;
2347 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2348 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2349 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2350 shim_callbacks->fetch_baton = sfb;
2353 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2354 NULL, NULL, shim_callbacks,
2355 result_pool, scratch_pool));
2357 return SVN_NO_ERROR;
2360 /* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
2362 /* baton for the svn_diff_tree_processor_t wrapper */
2363 typedef struct wc_diff_wrap_baton_t
2365 const svn_wc_diff_callbacks4_t *callbacks;
2366 void *callback_baton;
2368 svn_boolean_t walk_deleted_dirs;
2370 apr_pool_t *result_pool;
2371 const char *empty_file;
2373 } wc_diff_wrap_baton_t;
2375 static svn_error_t *
2376 wrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
2377 apr_pool_t *scratch_pool)
2380 return SVN_NO_ERROR;
2382 /* Create a unique file in the tempdir */
2383 SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
2384 svn_io_file_del_on_pool_cleanup,
2385 wb->result_pool, scratch_pool));
2387 return SVN_NO_ERROR;
2390 /* svn_diff_tree_processor_t function */
2391 static svn_error_t *
2392 wrap_dir_opened(void **new_dir_baton,
2393 svn_boolean_t *skip,
2394 svn_boolean_t *skip_children,
2395 const char *relpath,
2396 const svn_diff_source_t *left_source,
2397 const svn_diff_source_t *right_source,
2398 const svn_diff_source_t *copyfrom_source,
2399 void *parent_dir_baton,
2400 const svn_diff_tree_processor_t *processor,
2401 apr_pool_t *result_pool,
2402 apr_pool_t *scratch_pool)
2404 wc_diff_wrap_baton_t *wb = processor->baton;
2405 svn_boolean_t tree_conflicted = FALSE;
2407 assert(left_source || right_source); /* Must exist at one point. */
2408 assert(!left_source || !copyfrom_source); /* Either existed or added. */
2410 /* Maybe store state and tree_conflicted in baton? */
2411 if (left_source != NULL)
2413 /* Open for change or delete */
2414 SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
2417 ? right_source->revision
2419 ? left_source->revision
2420 : SVN_INVALID_REVNUM),
2424 if (! right_source && !wb->walk_deleted_dirs)
2425 *skip_children = TRUE;
2427 else /* left_source == NULL -> Add */
2429 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2430 SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
2431 skip, skip_children,
2433 right_source->revision,
2435 ? copyfrom_source->repos_relpath
2438 ? copyfrom_source->revision
2439 : SVN_INVALID_REVNUM,
2444 *new_dir_baton = NULL;
2446 return SVN_NO_ERROR;
2449 /* svn_diff_tree_processor_t function */
2450 static svn_error_t *
2451 wrap_dir_added(const char *relpath,
2452 const svn_diff_source_t *copyfrom_source,
2453 const svn_diff_source_t *right_source,
2454 /*const*/ apr_hash_t *copyfrom_props,
2455 /*const*/ apr_hash_t *right_props,
2457 const svn_diff_tree_processor_t *processor,
2458 apr_pool_t *scratch_pool)
2460 wc_diff_wrap_baton_t *wb = processor->baton;
2461 svn_boolean_t tree_conflicted = FALSE;
2462 svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
2463 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2464 apr_hash_t *pristine_props = copyfrom_props;
2465 apr_array_header_t *prop_changes = NULL;
2467 if (right_props && apr_hash_count(right_props))
2469 if (!pristine_props)
2470 pristine_props = apr_hash_make(scratch_pool);
2472 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
2475 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
2478 TRUE /* dir_was_added */,
2479 prop_changes, pristine_props,
2484 SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
2487 TRUE /* dir_was_added */,
2490 return SVN_NO_ERROR;
2493 /* svn_diff_tree_processor_t function */
2494 static svn_error_t *
2495 wrap_dir_deleted(const char *relpath,
2496 const svn_diff_source_t *left_source,
2497 /*const*/ apr_hash_t *left_props,
2499 const svn_diff_tree_processor_t *processor,
2500 apr_pool_t *scratch_pool)
2502 wc_diff_wrap_baton_t *wb = processor->baton;
2503 svn_boolean_t tree_conflicted = FALSE;
2504 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2506 SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
2511 return SVN_NO_ERROR;
2514 /* svn_diff_tree_processor_t function */
2515 static svn_error_t *
2516 wrap_dir_closed(const char *relpath,
2517 const svn_diff_source_t *left_source,
2518 const svn_diff_source_t *right_source,
2520 const svn_diff_tree_processor_t *processor,
2521 apr_pool_t *scratch_pool)
2523 wc_diff_wrap_baton_t *wb = processor->baton;
2525 /* No previous implementations provided these arguments, so we
2526 are not providing them either */
2527 SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
2533 return SVN_NO_ERROR;
2536 /* svn_diff_tree_processor_t function */
2537 static svn_error_t *
2538 wrap_dir_changed(const char *relpath,
2539 const svn_diff_source_t *left_source,
2540 const svn_diff_source_t *right_source,
2541 /*const*/ apr_hash_t *left_props,
2542 /*const*/ apr_hash_t *right_props,
2543 const apr_array_header_t *prop_changes,
2545 const struct svn_diff_tree_processor_t *processor,
2546 apr_pool_t *scratch_pool)
2548 wc_diff_wrap_baton_t *wb = processor->baton;
2549 svn_boolean_t tree_conflicted = FALSE;
2550 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2552 assert(left_source && right_source);
2554 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
2556 FALSE /* dir_was_added */,
2562 /* And call dir_closed, etc */
2563 SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
2564 dir_baton, processor,
2566 return SVN_NO_ERROR;
2569 /* svn_diff_tree_processor_t function */
2570 static svn_error_t *
2571 wrap_file_opened(void **new_file_baton,
2572 svn_boolean_t *skip,
2573 const char *relpath,
2574 const svn_diff_source_t *left_source,
2575 const svn_diff_source_t *right_source,
2576 const svn_diff_source_t *copyfrom_source,
2578 const svn_diff_tree_processor_t *processor,
2579 apr_pool_t *result_pool,
2580 apr_pool_t *scratch_pool)
2582 wc_diff_wrap_baton_t *wb = processor->baton;
2583 svn_boolean_t tree_conflicted = FALSE;
2585 if (left_source) /* If ! added */
2586 SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
2588 ? right_source->revision
2590 ? left_source->revision
2591 : SVN_INVALID_REVNUM),
2592 wb->callback_baton, scratch_pool));
2594 /* No old implementation used the output arguments for notify */
2596 *new_file_baton = NULL;
2597 return SVN_NO_ERROR;
2600 /* svn_diff_tree_processor_t function */
2601 static svn_error_t *
2602 wrap_file_added(const char *relpath,
2603 const svn_diff_source_t *copyfrom_source,
2604 const svn_diff_source_t *right_source,
2605 const char *copyfrom_file,
2606 const char *right_file,
2607 /*const*/ apr_hash_t *copyfrom_props,
2608 /*const*/ apr_hash_t *right_props,
2610 const svn_diff_tree_processor_t *processor,
2611 apr_pool_t *scratch_pool)
2613 wc_diff_wrap_baton_t *wb = processor->baton;
2614 svn_boolean_t tree_conflicted = FALSE;
2615 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2616 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2617 apr_array_header_t *prop_changes;
2619 if (! copyfrom_props)
2620 copyfrom_props = apr_hash_make(scratch_pool);
2622 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
2625 if (! copyfrom_source)
2626 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2628 SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
2635 right_source->revision,
2637 ? svn_prop_get_value(copyfrom_props,
2641 ? svn_prop_get_value(right_props,
2645 ? copyfrom_source->repos_relpath
2648 ? copyfrom_source->revision
2649 : SVN_INVALID_REVNUM,
2650 prop_changes, copyfrom_props,
2653 return SVN_NO_ERROR;
2656 static svn_error_t *
2657 wrap_file_deleted(const char *relpath,
2658 const svn_diff_source_t *left_source,
2659 const char *left_file,
2660 apr_hash_t *left_props,
2662 const svn_diff_tree_processor_t *processor,
2663 apr_pool_t *scratch_pool)
2665 wc_diff_wrap_baton_t *wb = processor->baton;
2666 svn_boolean_t tree_conflicted = FALSE;
2667 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2669 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2671 SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
2673 left_file, wb->empty_file,
2675 ? svn_prop_get_value(left_props,
2682 return SVN_NO_ERROR;
2685 /* svn_diff_tree_processor_t function */
2686 static svn_error_t *
2687 wrap_file_changed(const char *relpath,
2688 const svn_diff_source_t *left_source,
2689 const svn_diff_source_t *right_source,
2690 const char *left_file,
2691 const char *right_file,
2692 /*const*/ apr_hash_t *left_props,
2693 /*const*/ apr_hash_t *right_props,
2694 svn_boolean_t file_modified,
2695 const apr_array_header_t *prop_changes,
2697 const svn_diff_tree_processor_t *processor,
2698 apr_pool_t *scratch_pool)
2700 wc_diff_wrap_baton_t *wb = processor->baton;
2701 svn_boolean_t tree_conflicted = FALSE;
2702 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2703 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2705 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2707 assert(left_source && right_source);
2709 SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
2711 file_modified ? left_file : NULL,
2712 file_modified ? right_file : NULL,
2713 left_source->revision,
2714 right_source->revision,
2716 ? svn_prop_get_value(left_props,
2720 ? svn_prop_get_value(right_props,
2727 return SVN_NO_ERROR;
2731 svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
2732 const svn_wc_diff_callbacks4_t *callbacks,
2733 void *callback_baton,
2734 svn_boolean_t walk_deleted_dirs,
2735 apr_pool_t *result_pool,
2736 apr_pool_t *scratch_pool)
2738 wc_diff_wrap_baton_t *wrap_baton;
2739 svn_diff_tree_processor_t *processor;
2741 wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
2743 wrap_baton->result_pool = result_pool;
2744 wrap_baton->callbacks = callbacks;
2745 wrap_baton->callback_baton = callback_baton;
2746 wrap_baton->empty_file = NULL;
2747 wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
2749 processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
2751 processor->dir_opened = wrap_dir_opened;
2752 processor->dir_added = wrap_dir_added;
2753 processor->dir_deleted = wrap_dir_deleted;
2754 processor->dir_changed = wrap_dir_changed;
2755 processor->dir_closed = wrap_dir_closed;
2757 processor->file_opened = wrap_file_opened;
2758 processor->file_added = wrap_file_added;
2759 processor->file_deleted = wrap_file_deleted;
2760 processor->file_changed = wrap_file_changed;
2761 /*processor->file_closed = wrap_file_closed*/; /* Not needed */
2763 *diff_processor = processor;
2764 return SVN_NO_ERROR;
2767 /* =====================================================================
2768 * A tree processor filter that filters by changelist membership
2769 * =====================================================================
2771 * The current implementation queries the WC for the changelist of each
2772 * file as it comes through, and sets the 'skip' flag for a non-matching
2775 * (It doesn't set the 'skip' flag for a directory, as we need to receive
2776 * the changed/added/deleted/closed call to know when it is closed, in
2777 * order to preserve the strict open-close semantics for the wrapped tree
2780 * It passes on the opening and closing of every directory, even if there
2781 * are no file changes to be passed on inside that directory.
2784 typedef struct filter_tree_baton_t
2786 const svn_diff_tree_processor_t *processor;
2787 svn_wc_context_t *wc_ctx;
2788 /* WC path of the root of the diff (where relpath = "") */
2789 const char *root_local_abspath;
2790 /* Hash whose keys are const char * changelist names. */
2791 apr_hash_t *changelist_hash;
2792 } filter_tree_baton_t;
2794 static svn_error_t *
2795 filter_dir_opened(void **new_dir_baton,
2796 svn_boolean_t *skip,
2797 svn_boolean_t *skip_children,
2798 const char *relpath,
2799 const svn_diff_source_t *left_source,
2800 const svn_diff_source_t *right_source,
2801 const svn_diff_source_t *copyfrom_source,
2802 void *parent_dir_baton,
2803 const svn_diff_tree_processor_t *processor,
2804 apr_pool_t *result_pool,
2805 apr_pool_t *scratch_pool)
2807 struct filter_tree_baton_t *fb = processor->baton;
2809 SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
2811 left_source, right_source,
2815 result_pool, scratch_pool));
2816 return SVN_NO_ERROR;
2819 static svn_error_t *
2820 filter_dir_added(const char *relpath,
2821 const svn_diff_source_t *copyfrom_source,
2822 const svn_diff_source_t *right_source,
2823 /*const*/ apr_hash_t *copyfrom_props,
2824 /*const*/ apr_hash_t *right_props,
2826 const svn_diff_tree_processor_t *processor,
2827 apr_pool_t *scratch_pool)
2829 struct filter_tree_baton_t *fb = processor->baton;
2831 SVN_ERR(fb->processor->dir_closed(relpath,
2838 return SVN_NO_ERROR;
2841 static svn_error_t *
2842 filter_dir_deleted(const char *relpath,
2843 const svn_diff_source_t *left_source,
2844 /*const*/ apr_hash_t *left_props,
2846 const svn_diff_tree_processor_t *processor,
2847 apr_pool_t *scratch_pool)
2849 struct filter_tree_baton_t *fb = processor->baton;
2851 SVN_ERR(fb->processor->dir_closed(relpath,
2858 return SVN_NO_ERROR;
2861 static svn_error_t *
2862 filter_dir_changed(const char *relpath,
2863 const svn_diff_source_t *left_source,
2864 const svn_diff_source_t *right_source,
2865 /*const*/ apr_hash_t *left_props,
2866 /*const*/ apr_hash_t *right_props,
2867 const apr_array_header_t *prop_changes,
2869 const struct svn_diff_tree_processor_t *processor,
2870 apr_pool_t *scratch_pool)
2872 struct filter_tree_baton_t *fb = processor->baton;
2874 SVN_ERR(fb->processor->dir_closed(relpath,
2880 return SVN_NO_ERROR;
2883 static svn_error_t *
2884 filter_dir_closed(const char *relpath,
2885 const svn_diff_source_t *left_source,
2886 const svn_diff_source_t *right_source,
2888 const svn_diff_tree_processor_t *processor,
2889 apr_pool_t *scratch_pool)
2891 struct filter_tree_baton_t *fb = processor->baton;
2893 SVN_ERR(fb->processor->dir_closed(relpath,
2899 return SVN_NO_ERROR;
2902 static svn_error_t *
2903 filter_file_opened(void **new_file_baton,
2904 svn_boolean_t *skip,
2905 const char *relpath,
2906 const svn_diff_source_t *left_source,
2907 const svn_diff_source_t *right_source,
2908 const svn_diff_source_t *copyfrom_source,
2910 const svn_diff_tree_processor_t *processor,
2911 apr_pool_t *result_pool,
2912 apr_pool_t *scratch_pool)
2914 struct filter_tree_baton_t *fb = processor->baton;
2915 const char *local_abspath
2916 = svn_dirent_join(fb->root_local_abspath, relpath, scratch_pool);
2918 /* Skip if not a member of a given changelist */
2919 if (! svn_wc__changelist_match(fb->wc_ctx, local_abspath,
2920 fb->changelist_hash, scratch_pool))
2923 return SVN_NO_ERROR;
2926 SVN_ERR(fb->processor->file_opened(new_file_baton,
2936 return SVN_NO_ERROR;
2939 static svn_error_t *
2940 filter_file_added(const char *relpath,
2941 const svn_diff_source_t *copyfrom_source,
2942 const svn_diff_source_t *right_source,
2943 const char *copyfrom_file,
2944 const char *right_file,
2945 /*const*/ apr_hash_t *copyfrom_props,
2946 /*const*/ apr_hash_t *right_props,
2948 const svn_diff_tree_processor_t *processor,
2949 apr_pool_t *scratch_pool)
2951 struct filter_tree_baton_t *fb = processor->baton;
2953 SVN_ERR(fb->processor->file_added(relpath,
2963 return SVN_NO_ERROR;
2966 static svn_error_t *
2967 filter_file_deleted(const char *relpath,
2968 const svn_diff_source_t *left_source,
2969 const char *left_file,
2970 /*const*/ apr_hash_t *left_props,
2972 const svn_diff_tree_processor_t *processor,
2973 apr_pool_t *scratch_pool)
2975 struct filter_tree_baton_t *fb = processor->baton;
2977 SVN_ERR(fb->processor->file_deleted(relpath,
2985 return SVN_NO_ERROR;
2988 static svn_error_t *
2989 filter_file_changed(const char *relpath,
2990 const svn_diff_source_t *left_source,
2991 const svn_diff_source_t *right_source,
2992 const char *left_file,
2993 const char *right_file,
2994 /*const*/ apr_hash_t *left_props,
2995 /*const*/ apr_hash_t *right_props,
2996 svn_boolean_t file_modified,
2997 const apr_array_header_t *prop_changes,
2999 const svn_diff_tree_processor_t *processor,
3000 apr_pool_t *scratch_pool)
3002 struct filter_tree_baton_t *fb = processor->baton;
3004 SVN_ERR(fb->processor->file_changed(relpath,
3016 return SVN_NO_ERROR;
3019 static svn_error_t *
3020 filter_file_closed(const char *relpath,
3021 const svn_diff_source_t *left_source,
3022 const svn_diff_source_t *right_source,
3024 const svn_diff_tree_processor_t *processor,
3025 apr_pool_t *scratch_pool)
3027 struct filter_tree_baton_t *fb = processor->baton;
3029 SVN_ERR(fb->processor->file_closed(relpath,
3036 return SVN_NO_ERROR;
3039 static svn_error_t *
3040 filter_node_absent(const char *relpath,
3042 const svn_diff_tree_processor_t *processor,
3043 apr_pool_t *scratch_pool)
3045 struct filter_tree_baton_t *fb = processor->baton;
3047 SVN_ERR(fb->processor->node_absent(relpath,
3051 return SVN_NO_ERROR;
3054 const svn_diff_tree_processor_t *
3055 svn_wc__changelist_filter_tree_processor_create(
3056 const svn_diff_tree_processor_t *processor,
3057 svn_wc_context_t *wc_ctx,
3058 const char *root_local_abspath,
3059 apr_hash_t *changelist_hash,
3060 apr_pool_t *result_pool)
3062 struct filter_tree_baton_t *fb;
3063 svn_diff_tree_processor_t *filter;
3065 if (! changelist_hash)
3068 fb = apr_pcalloc(result_pool, sizeof(*fb));
3069 fb->processor = processor;
3070 fb->wc_ctx = wc_ctx;
3071 fb->root_local_abspath = root_local_abspath;
3072 fb->changelist_hash = changelist_hash;
3074 filter = svn_diff__tree_processor_create(fb, result_pool);
3075 filter->dir_opened = filter_dir_opened;
3076 filter->dir_added = filter_dir_added;
3077 filter->dir_deleted = filter_dir_deleted;
3078 filter->dir_changed = filter_dir_changed;
3079 filter->dir_closed = filter_dir_closed;
3081 filter->file_opened = filter_file_opened;
3082 filter->file_added = filter_file_added;
3083 filter->file_deleted = filter_file_deleted;
3084 filter->file_changed = filter_file_changed;
3085 filter->file_closed = filter_file_closed;
3087 filter->node_absent = filter_node_absent;