Win32: fix an incorrect error status being propagated to the caller in case
[svn/apache.git] / subversion / libsvn_wc / diff_editor.c
blob9b1b0d50baa0336d9a24d17ab57468853b9983da
1 /*
2 * diff_editor.c -- The diff editor for comparing the working copy against the
3 * repository.
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
21 * under the License.
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
42 * out.
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?
55 #include <apr_hash.h>
56 #include <apr_md5.h>
58 #include <assert.h>
60 #include "svn_error.h"
61 #include "svn_pools.h"
62 #include "svn_dirent_uri.h"
63 #include "svn_path.h"
64 #include "svn_hash.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"
73 #include "wc.h"
74 #include "props.h"
75 #include "adm_files.h"
76 #include "translate.h"
77 #include "diff.h"
79 #include "svn_private_config.h"
81 /*-------------------------------------------------------------------------*/
84 /* Overall crawler editor baton.
86 struct edit_baton_t
88 /* A wc db. */
89 svn_wc__db_t *db;
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. */
100 const char *target;
101 const char *anchor_abspath;
103 /* Target revision */
104 svn_revnum_t revnum;
106 /* Was the root opened? */
107 svn_boolean_t root_opened;
109 /* How does this diff descend as seen from target? */
110 svn_depth_t depth;
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;
120 void *cancel_baton;
122 apr_pool_t *pool;
125 /* Directory level baton.
127 struct dir_baton_t
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. */
133 svn_depth_t depth;
135 /* The name and path of this directory as if they would be/are in the
136 local working copy. */
137 const char *name;
138 const char *relpath;
139 const char *local_abspath;
141 /* TRUE if the file is added by the editor drive. */
142 svn_boolean_t added;
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 /* TRUE if the directory was reported incomplete to the repository */
149 svn_boolean_t is_incomplete;
151 /* Processor state */
152 void *pdb;
153 svn_boolean_t skip;
154 svn_boolean_t skip_children;
156 svn_diff_source_t *left_src;
157 svn_diff_source_t *right_src;
159 apr_hash_t *local_info;
161 /* A hash containing the basenames of the nodes reported deleted by the
162 repository (or NULL for no values). */
163 apr_hash_t *deletes;
165 /* Identifies those directory elements that get compared while running
166 the crawler. These elements should not be compared again when
167 recursively looking for local modifications.
169 This hash maps the basename of the node to an unimportant value.
171 If the directory's properties have been compared, an item with hash
172 key of "" will be present in the hash. */
173 apr_hash_t *compared;
175 /* The list of incoming BASE->repos propchanges. */
176 apr_array_header_t *propchanges;
178 /* Has a change on regular properties */
179 svn_boolean_t has_propchange;
181 /* The overall crawler editor baton. */
182 struct edit_baton_t *eb;
184 apr_pool_t *pool;
185 int users;
188 /* File level baton.
190 struct file_baton_t
192 struct dir_baton_t *parent_baton;
194 /* The name and path of this file as if they would be/are in the
195 parent directory, diff session and local working copy. */
196 const char *name;
197 const char *relpath;
198 const char *local_abspath;
200 /* Processor state */
201 void *pfb;
202 svn_boolean_t skip;
204 /* TRUE if the file is added by the editor drive. */
205 svn_boolean_t added;
206 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
207 svn_boolean_t repos_only;
208 /* TRUE if the node is to be compared with an unrelated node*/
209 svn_boolean_t ignoring_ancestry;
211 const svn_diff_source_t *left_src;
212 const svn_diff_source_t *right_src;
214 /* The list of incoming BASE->repos propchanges. */
215 apr_array_header_t *propchanges;
217 /* Has a change on regular properties */
218 svn_boolean_t has_propchange;
220 /* The current BASE checksum and props */
221 const svn_checksum_t *base_checksum;
222 apr_hash_t *base_props;
224 /* The resulting from apply_textdelta */
225 const char *temp_file_path;
226 unsigned char result_digest[APR_MD5_DIGESTSIZE];
228 /* The overall crawler editor baton. */
229 struct edit_baton_t *eb;
231 apr_pool_t *pool;
234 /* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths
235 * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON
236 * define the callbacks to compare files. DEPTH defines if and how to
237 * descend into subdirectories; see public doc string for exactly how.
238 * IGNORE_ANCESTRY defines whether to utilize node ancestry when
239 * calculating diffs. USE_TEXT_BASE defines whether to compare
240 * against working files or text-bases. REVERSE_ORDER defines which
241 * direction to perform the diff.
243 static svn_error_t *
244 make_edit_baton(struct edit_baton_t **edit_baton,
245 svn_wc__db_t *db,
246 const char *anchor_abspath,
247 const char *target,
248 const svn_diff_tree_processor_t *diff_processor,
249 svn_depth_t depth,
250 svn_boolean_t ignore_ancestry,
251 svn_boolean_t use_text_base,
252 svn_boolean_t reverse_order,
253 svn_cancel_func_t cancel_func,
254 void *cancel_baton,
255 apr_pool_t *pool)
257 struct edit_baton_t *eb;
259 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
261 eb = apr_pcalloc(pool, sizeof(*eb));
262 eb->db = db;
263 eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
264 eb->target = apr_pstrdup(pool, target);
265 eb->processor = diff_processor;
266 eb->depth = depth;
267 eb->ignore_ancestry = ignore_ancestry;
268 eb->local_before_remote = reverse_order;
269 eb->diff_pristine = use_text_base;
270 eb->cancel_func = cancel_func;
271 eb->cancel_baton = cancel_baton;
272 eb->pool = pool;
274 *edit_baton = eb;
275 return SVN_NO_ERROR;
278 /* Create a new directory baton. PATH is the directory path,
279 * including anchor_path. ADDED is set if this directory is being
280 * added rather than replaced. PARENT_BATON is the baton of the
281 * parent directory, it will be null if this is the root of the
282 * comparison hierarchy. The directory and its parent may or may not
283 * exist in the working copy. EDIT_BATON is the overall crawler
284 * editor baton.
286 static struct dir_baton_t *
287 make_dir_baton(const char *path,
288 struct dir_baton_t *parent_baton,
289 struct edit_baton_t *eb,
290 svn_boolean_t added,
291 svn_depth_t depth,
292 apr_pool_t *result_pool)
294 apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
295 : eb->pool);
296 struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
298 db->parent_baton = parent_baton;
300 /* Allocate 1 string for using as 3 strings */
301 db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
302 db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
303 db->name = svn_dirent_basename(db->relpath, NULL);
305 db->eb = eb;
306 db->added = added;
307 db->depth = depth;
308 db->pool = dir_pool;
309 db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
310 db->compared = apr_hash_make(dir_pool);
312 if (parent_baton != NULL)
314 parent_baton->users++;
317 db->users = 1;
319 return db;
322 /* Create a new file baton. PATH is the file path, including
323 * anchor_path. ADDED is set if this file is being added rather than
324 * replaced. PARENT_BATON is the baton of the parent directory.
325 * The directory and its parent may or may not exist in the working copy.
327 static struct file_baton_t *
328 make_file_baton(const char *path,
329 svn_boolean_t added,
330 struct dir_baton_t *parent_baton,
331 apr_pool_t *result_pool)
333 apr_pool_t *file_pool = svn_pool_create(result_pool);
334 struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
335 struct edit_baton_t *eb = parent_baton->eb;
337 fb->eb = eb;
338 fb->parent_baton = parent_baton;
339 fb->parent_baton->users++;
341 /* Allocate 1 string for using as 3 strings */
342 fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
343 fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
344 fb->name = svn_dirent_basename(fb->relpath, NULL);
346 fb->added = added;
347 fb->pool = file_pool;
348 fb->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
350 return fb;
353 /* Destroy DB when there are no more registered users */
354 static svn_error_t *
355 maybe_done(struct dir_baton_t *db)
357 db->users--;
359 if (!db->users)
361 struct dir_baton_t *pb = db->parent_baton;
363 svn_pool_clear(db->pool);
365 if (pb != NULL)
366 SVN_ERR(maybe_done(pb));
369 return SVN_NO_ERROR;
372 /* Standard check to see if a node is represented in the local working copy */
373 #define NOT_PRESENT(status) \
374 ((status) == svn_wc__db_status_not_present \
375 || (status) == svn_wc__db_status_excluded \
376 || (status) == svn_wc__db_status_server_excluded)
378 svn_error_t *
379 svn_wc__diff_base_working_diff(svn_wc__db_t *db,
380 const char *local_abspath,
381 const char *relpath,
382 svn_revnum_t revision,
383 const svn_diff_tree_processor_t *processor,
384 void *processor_dir_baton,
385 svn_boolean_t diff_pristine,
386 svn_cancel_func_t cancel_func,
387 void *cancel_baton,
388 apr_pool_t *scratch_pool)
390 void *file_baton = NULL;
391 svn_boolean_t skip = FALSE;
392 svn_wc__db_status_t status;
393 svn_revnum_t db_revision;
394 svn_boolean_t had_props;
395 svn_boolean_t props_mod;
396 svn_boolean_t files_same = FALSE;
397 svn_wc__db_status_t base_status;
398 const svn_checksum_t *working_checksum;
399 const svn_checksum_t *checksum;
400 svn_filesize_t recorded_size;
401 apr_time_t recorded_time;
402 const char *pristine_file;
403 const char *local_file;
404 svn_diff_source_t *left_src;
405 svn_diff_source_t *right_src;
406 apr_hash_t *base_props;
407 apr_hash_t *local_props;
408 apr_array_header_t *prop_changes;
410 SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
411 NULL, NULL, NULL, NULL, &working_checksum, NULL,
412 NULL, NULL, NULL, NULL, NULL, &recorded_size,
413 &recorded_time, NULL, NULL, NULL,
414 &had_props, &props_mod, NULL, NULL, NULL,
415 db, local_abspath, scratch_pool, scratch_pool));
416 checksum = working_checksum;
418 assert(status == svn_wc__db_status_normal
419 || status == svn_wc__db_status_added
420 || (status == svn_wc__db_status_deleted && diff_pristine));
422 if (status != svn_wc__db_status_normal)
424 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
425 NULL, NULL, NULL, NULL, NULL, NULL,
426 NULL, &checksum, NULL, NULL, &had_props,
427 NULL, NULL,
428 db, local_abspath,
429 scratch_pool, scratch_pool));
430 recorded_size = SVN_INVALID_FILESIZE;
431 recorded_time = 0;
432 props_mod = TRUE; /* Requires compare */
434 else if (diff_pristine)
435 files_same = TRUE;
436 else
438 const svn_io_dirent2_t *dirent;
440 /* Verify truename to mimic status for iota/IOTA difference on Windows */
441 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
442 TRUE /* verify truename */,
443 TRUE /* ingore_enoent */,
444 scratch_pool, scratch_pool));
446 /* If a file does not exist on disk (missing/obstructed) then we
447 can't provide a text diff */
448 if (dirent->kind != svn_node_file
449 || (dirent->kind == svn_node_file
450 && dirent->filesize == recorded_size
451 && dirent->mtime == recorded_time))
453 files_same = TRUE;
457 if (files_same && !props_mod)
458 return SVN_NO_ERROR; /* Cheap exit */
460 assert(checksum);
462 if (!SVN_IS_VALID_REVNUM(revision))
463 revision = db_revision;
465 left_src = svn_diff__source_create(revision, scratch_pool);
466 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
468 SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
469 left_src,
470 right_src,
471 NULL /* copyfrom_src */,
472 processor_dir_baton,
473 processor,
474 scratch_pool, scratch_pool));
476 if (skip)
477 return SVN_NO_ERROR;
479 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
480 db, local_abspath, checksum,
481 scratch_pool, scratch_pool));
483 if (diff_pristine)
484 SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
485 db, local_abspath,
486 working_checksum,
487 scratch_pool, scratch_pool));
488 else if (! (had_props || props_mod))
489 local_file = local_abspath;
490 else if (files_same)
491 local_file = pristine_file;
492 else
493 SVN_ERR(svn_wc__internal_translated_file(
494 &local_file, local_abspath,
495 db, local_abspath,
496 SVN_WC_TRANSLATE_TO_NF
497 | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
498 cancel_func, cancel_baton,
499 scratch_pool, scratch_pool));
501 if (! files_same)
502 SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
503 pristine_file, scratch_pool));
505 if (had_props)
506 SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
507 scratch_pool, scratch_pool));
508 else
509 base_props = apr_hash_make(scratch_pool);
511 if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
512 local_props = base_props;
513 else if (diff_pristine)
514 SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
515 scratch_pool, scratch_pool));
516 else
517 SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
518 scratch_pool, scratch_pool));
520 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
522 if (prop_changes->nelts || !files_same)
524 SVN_ERR(processor->file_changed(relpath,
525 left_src,
526 right_src,
527 pristine_file,
528 local_file,
529 base_props,
530 local_props,
531 ! files_same,
532 prop_changes,
533 file_baton,
534 processor,
535 scratch_pool));
537 else
539 SVN_ERR(processor->file_closed(relpath,
540 left_src,
541 right_src,
542 file_baton,
543 processor,
544 scratch_pool));
547 return SVN_NO_ERROR;
550 static svn_error_t *
551 ensure_local_info(struct dir_baton_t *db,
552 apr_pool_t *scratch_pool)
554 apr_hash_t *conflicts;
556 if (db->local_info)
557 return SVN_NO_ERROR;
559 SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
560 db->eb->db, db->local_abspath,
561 FALSE /* base_tree_only */,
562 db->pool, scratch_pool));
564 return SVN_NO_ERROR;
567 /* Called when the directory is closed to compare any elements that have
568 * not yet been compared. This identifies local, working copy only
569 * changes. At this stage we are dealing with files/directories that do
570 * exist in the working copy.
572 * DIR_BATON is the baton for the directory.
574 static svn_error_t *
575 walk_local_nodes_diff(struct edit_baton_t *eb,
576 const char *local_abspath,
577 const char *path,
578 svn_depth_t depth,
579 apr_hash_t *compared,
580 void *parent_baton,
581 apr_pool_t *scratch_pool)
583 svn_wc__db_t *db = eb->db;
584 svn_boolean_t in_anchor_not_target;
585 apr_pool_t *iterpool;
586 void *dir_baton = NULL;
587 svn_boolean_t skip = FALSE;
588 svn_boolean_t skip_children = FALSE;
589 svn_revnum_t revision;
590 svn_boolean_t props_mod;
591 svn_diff_source_t *left_src;
592 svn_diff_source_t *right_src;
594 /* Everything we do below is useless if we are comparing to BASE. */
595 if (eb->diff_pristine)
596 return SVN_NO_ERROR;
598 /* Determine if this is the anchor directory if the anchor is different
599 to the target. When the target is a file, the anchor is the parent
600 directory and if this is that directory the non-target entries must be
601 skipped. */
602 in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
604 iterpool = svn_pool_create(scratch_pool);
606 SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
607 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
608 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
609 NULL, &props_mod, NULL, NULL, NULL,
610 db, local_abspath, scratch_pool, scratch_pool));
612 left_src = svn_diff__source_create(revision, scratch_pool);
613 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
615 if (compared)
617 dir_baton = parent_baton;
618 skip = TRUE;
620 else if (!in_anchor_not_target)
621 SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
622 path,
623 left_src,
624 right_src,
625 NULL /* copyfrom_src */,
626 parent_baton,
627 eb->processor,
628 scratch_pool, scratch_pool));
631 if (!skip_children && depth != svn_depth_empty)
633 apr_hash_t *nodes;
634 apr_hash_t *conflicts;
635 apr_array_header_t *children;
636 svn_depth_t depth_below_here = depth;
637 svn_boolean_t diff_files;
638 svn_boolean_t diff_dirs;
639 int i;
641 if (depth_below_here == svn_depth_immediates)
642 depth_below_here = svn_depth_empty;
644 diff_files = (depth == svn_depth_unknown
645 || depth >= svn_depth_files);
646 diff_dirs = (depth == svn_depth_unknown
647 || depth >= svn_depth_immediates);
649 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
650 db, local_abspath,
651 FALSE /* base_tree_only */,
652 scratch_pool, iterpool));
654 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
655 scratch_pool);
657 for (i = 0; i < children->nelts; i++)
659 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
660 svn_sort__item_t);
661 const char *name = item->key;
662 struct svn_wc__db_info_t *info = item->value;
664 const char *child_abspath;
665 const char *child_relpath;
666 svn_boolean_t repos_only;
667 svn_boolean_t local_only;
668 svn_node_kind_t base_kind;
670 if (eb->cancel_func)
671 SVN_ERR(eb->cancel_func(eb->cancel_baton));
673 /* In the anchor directory, if the anchor is not the target then all
674 entries other than the target should not be diff'd. Running diff
675 on one file in a directory should not diff other files in that
676 directory. */
677 if (in_anchor_not_target && strcmp(eb->target, name))
678 continue;
680 if (compared && svn_hash_gets(compared, name))
681 continue;
683 if (NOT_PRESENT(info->status))
684 continue;
686 assert(info->status == svn_wc__db_status_normal
687 || info->status == svn_wc__db_status_added
688 || info->status == svn_wc__db_status_deleted);
690 svn_pool_clear(iterpool);
691 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
692 child_relpath = svn_relpath_join(path, name, iterpool);
694 repos_only = FALSE;
695 local_only = FALSE;
697 if (!info->have_base)
699 local_only = TRUE; /* Only report additions */
701 if (info->status == svn_wc__db_status_deleted)
702 continue; /* Nothing added (deleted copy) */
704 else if (info->status == svn_wc__db_status_normal)
706 /* Simple diff */
707 base_kind = info->kind;
709 else if (info->status == svn_wc__db_status_deleted
710 && (!eb->diff_pristine || !info->have_more_work))
712 svn_wc__db_status_t base_status;
713 repos_only = TRUE;
714 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
715 NULL, NULL, NULL, NULL, NULL,
716 NULL, NULL, NULL, NULL, NULL,
717 NULL, NULL, NULL,
718 db, child_abspath,
719 iterpool, iterpool));
721 if (NOT_PRESENT(base_status))
722 continue;
724 else
726 /* working status is either added or deleted */
727 svn_wc__db_status_t base_status;
729 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
730 NULL, NULL, NULL, NULL, NULL,
731 NULL, NULL, NULL, NULL, NULL,
732 NULL, NULL, NULL,
733 db, child_abspath,
734 iterpool, iterpool));
736 if (NOT_PRESENT(base_status))
737 local_only = TRUE;
738 else if (base_kind != info->kind || !eb->ignore_ancestry)
740 repos_only = TRUE;
741 local_only = TRUE;
745 if (eb->local_before_remote && local_only)
747 const char *moved_from_relpath;
749 if (info->moved_here)
751 const char *moved_from_abspath;
753 SVN_ERR(svn_wc__db_scan_moved(&moved_from_abspath,
754 NULL, NULL, NULL,
755 db, child_abspath,
756 iterpool, iterpool));
757 SVN_ERR_ASSERT(moved_from_abspath != NULL);
759 moved_from_relpath = svn_dirent_skip_ancestor(
760 eb->anchor_abspath,
761 moved_from_abspath);
763 else
764 moved_from_relpath = NULL;
766 if (info->kind == svn_node_file && diff_files)
767 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
768 child_relpath,
769 moved_from_relpath,
770 eb->processor, dir_baton,
771 eb->diff_pristine,
772 eb->cancel_func,
773 eb->cancel_baton,
774 iterpool));
775 else if (info->kind == svn_node_dir && diff_dirs)
776 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
777 child_relpath,
778 depth_below_here,
779 moved_from_relpath,
780 eb->processor, dir_baton,
781 eb->diff_pristine,
782 eb->cancel_func,
783 eb->cancel_baton,
784 iterpool));
787 if (repos_only)
789 /* Report repository form deleted */
790 if (base_kind == svn_node_file && diff_files)
791 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
792 child_relpath, eb->revnum,
793 eb->processor, dir_baton,
794 iterpool));
795 else if (base_kind == svn_node_dir && diff_dirs)
796 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
797 child_relpath, eb->revnum,
798 depth_below_here,
799 eb->processor, dir_baton,
800 eb->cancel_func,
801 eb->cancel_baton,
802 iterpool));
804 else if (!local_only) /* Not local only nor remote only */
806 /* Diff base against actual */
807 if (info->kind == svn_node_file && diff_files)
809 if (info->status != svn_wc__db_status_normal
810 || !eb->diff_pristine)
812 SVN_ERR(svn_wc__diff_base_working_diff(
813 db, child_abspath,
814 child_relpath,
815 eb->revnum,
816 eb->processor, dir_baton,
817 eb->diff_pristine,
818 eb->cancel_func,
819 eb->cancel_baton,
820 scratch_pool));
823 else if (info->kind == svn_node_dir && diff_dirs)
824 SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
825 child_relpath,
826 depth_below_here,
827 NULL /* compared */,
828 dir_baton,
829 scratch_pool));
832 if (!eb->local_before_remote && local_only)
834 const char *moved_from_relpath;
836 if (info->moved_here)
838 const char *moved_from_abspath;
840 SVN_ERR(svn_wc__db_scan_moved(&moved_from_abspath,
841 NULL, NULL, NULL,
842 db, child_abspath,
843 iterpool, iterpool));
844 SVN_ERR_ASSERT(moved_from_abspath != NULL);
846 moved_from_relpath = svn_dirent_skip_ancestor(
847 eb->anchor_abspath,
848 moved_from_abspath);
850 else
851 moved_from_relpath = NULL;
853 if (info->kind == svn_node_file && diff_files)
854 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
855 child_relpath,
856 moved_from_relpath,
857 eb->processor, dir_baton,
858 eb->diff_pristine,
859 eb->cancel_func,
860 eb->cancel_baton,
861 iterpool));
862 else if (info->kind == svn_node_dir && diff_dirs)
863 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
864 child_relpath, depth_below_here,
865 moved_from_relpath,
866 eb->processor, dir_baton,
867 eb->diff_pristine,
868 eb->cancel_func,
869 eb->cancel_baton,
870 iterpool));
875 if (compared)
876 return SVN_NO_ERROR;
878 /* Check for local property mods on this directory, if we haven't
879 already reported them. */
880 if (! skip
881 && ! in_anchor_not_target
882 && props_mod)
884 apr_array_header_t *propchanges;
885 apr_hash_t *left_props;
886 apr_hash_t *right_props;
888 SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
889 db, local_abspath,
890 scratch_pool, scratch_pool));
892 right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
894 SVN_ERR(eb->processor->dir_changed(path,
895 left_src,
896 right_src,
897 left_props,
898 right_props,
899 propchanges,
900 dir_baton,
901 eb->processor,
902 scratch_pool));
904 else if (! skip)
905 SVN_ERR(eb->processor->dir_closed(path,
906 left_src,
907 right_src,
908 dir_baton,
909 eb->processor,
910 scratch_pool));
912 svn_pool_destroy(iterpool);
914 return SVN_NO_ERROR;
917 svn_error_t *
918 svn_wc__diff_local_only_file(svn_wc__db_t *db,
919 const char *local_abspath,
920 const char *relpath,
921 const char *moved_from_relpath,
922 const svn_diff_tree_processor_t *processor,
923 void *processor_parent_baton,
924 svn_boolean_t diff_pristine,
925 svn_cancel_func_t cancel_func,
926 void *cancel_baton,
927 apr_pool_t *scratch_pool)
929 svn_diff_source_t *right_src;
930 svn_diff_source_t *copyfrom_src = NULL;
931 svn_wc__db_status_t status;
932 svn_node_kind_t kind;
933 const svn_checksum_t *checksum;
934 const char *original_repos_relpath;
935 svn_revnum_t original_revision;
936 svn_boolean_t had_props;
937 svn_boolean_t props_mod;
938 apr_hash_t *pristine_props;
939 apr_hash_t *right_props = NULL;
940 const char *pristine_file;
941 const char *translated_file;
942 svn_revnum_t revision;
943 void *file_baton = NULL;
944 svn_boolean_t skip = FALSE;
945 svn_boolean_t file_mod = TRUE;
947 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
948 NULL, NULL, NULL, NULL, &checksum, NULL,
949 &original_repos_relpath, NULL, NULL,
950 &original_revision, NULL, NULL, NULL,
951 NULL, NULL, NULL, &had_props,
952 &props_mod, NULL, NULL, NULL,
953 db, local_abspath,
954 scratch_pool, scratch_pool));
956 assert(kind == svn_node_file
957 && (status == svn_wc__db_status_normal
958 || status == svn_wc__db_status_added
959 || (status == svn_wc__db_status_deleted && diff_pristine)));
962 if (status == svn_wc__db_status_deleted)
964 assert(diff_pristine);
966 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
967 NULL, &checksum, NULL, &had_props,
968 &pristine_props,
969 db, local_abspath,
970 scratch_pool, scratch_pool));
971 props_mod = FALSE;
973 else if (!had_props)
974 pristine_props = apr_hash_make(scratch_pool);
975 else
976 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
977 db, local_abspath,
978 scratch_pool, scratch_pool));
980 if (original_repos_relpath)
982 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
983 copyfrom_src->repos_relpath = original_repos_relpath;
984 copyfrom_src->moved_from_relpath = moved_from_relpath;
987 if (props_mod || !SVN_IS_VALID_REVNUM(revision))
988 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
989 else
991 if (diff_pristine)
992 file_mod = FALSE;
993 else
994 SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
995 FALSE, scratch_pool));
997 if (!file_mod)
998 right_src = svn_diff__source_create(revision, scratch_pool);
999 else
1000 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
1003 SVN_ERR(processor->file_opened(&file_baton, &skip,
1004 relpath,
1005 NULL /* left_source */,
1006 right_src,
1007 copyfrom_src,
1008 processor_parent_baton,
1009 processor,
1010 scratch_pool, scratch_pool));
1012 if (skip)
1013 return SVN_NO_ERROR;
1015 if (props_mod && !diff_pristine)
1016 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1017 scratch_pool, scratch_pool));
1018 else
1019 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1021 if (checksum)
1022 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
1023 checksum, scratch_pool, scratch_pool));
1024 else
1025 pristine_file = NULL;
1027 if (diff_pristine)
1029 translated_file = pristine_file; /* No translation needed */
1031 else
1033 SVN_ERR(svn_wc__internal_translated_file(
1034 &translated_file, local_abspath, db, local_abspath,
1035 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
1036 cancel_func, cancel_baton,
1037 scratch_pool, scratch_pool));
1040 SVN_ERR(processor->file_added(relpath,
1041 copyfrom_src,
1042 right_src,
1043 copyfrom_src
1044 ? pristine_file
1045 : NULL,
1046 translated_file,
1047 copyfrom_src
1048 ? pristine_props
1049 : NULL,
1050 right_props,
1051 file_baton,
1052 processor,
1053 scratch_pool));
1055 return SVN_NO_ERROR;
1058 svn_error_t *
1059 svn_wc__diff_local_only_dir(svn_wc__db_t *db,
1060 const char *local_abspath,
1061 const char *relpath,
1062 svn_depth_t depth,
1063 const char *moved_from_relpath,
1064 const svn_diff_tree_processor_t *processor,
1065 void *processor_parent_baton,
1066 svn_boolean_t diff_pristine,
1067 svn_cancel_func_t cancel_func,
1068 void *cancel_baton,
1069 apr_pool_t *scratch_pool)
1071 svn_wc__db_status_t status;
1072 svn_node_kind_t kind;
1073 svn_boolean_t had_props;
1074 svn_boolean_t props_mod;
1075 const char *original_repos_relpath;
1076 svn_revnum_t original_revision;
1077 svn_diff_source_t *copyfrom_src = NULL;
1078 apr_hash_t *pristine_props;
1079 const apr_array_header_t *children;
1080 int i;
1081 apr_pool_t *iterpool;
1082 void *pdb = NULL;
1083 svn_boolean_t skip = FALSE;
1084 svn_boolean_t skip_children = FALSE;
1085 svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
1086 scratch_pool);
1088 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
1089 NULL, NULL, NULL, NULL, NULL, NULL,
1090 &original_repos_relpath, NULL, NULL,
1091 &original_revision, NULL, NULL, NULL,
1092 NULL, NULL, NULL, &had_props,
1093 &props_mod, NULL, NULL, NULL,
1094 db, local_abspath,
1095 scratch_pool, scratch_pool));
1096 if (original_repos_relpath)
1098 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
1099 copyfrom_src->repos_relpath = original_repos_relpath;
1100 copyfrom_src->moved_from_relpath = moved_from_relpath;
1103 /* svn_wc__db_status_incomplete should never happen, as the result won't be
1104 stable or guaranteed related to what is in the repository for this
1105 revision, but without this it would be hard to diagnose that status... */
1106 assert(kind == svn_node_dir
1107 && (status == svn_wc__db_status_normal
1108 || status == svn_wc__db_status_incomplete
1109 || status == svn_wc__db_status_added
1110 || (status == svn_wc__db_status_deleted && diff_pristine)));
1112 if (status == svn_wc__db_status_deleted)
1114 assert(diff_pristine);
1116 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1117 NULL, NULL, NULL, &had_props,
1118 &pristine_props,
1119 db, local_abspath,
1120 scratch_pool, scratch_pool));
1121 props_mod = FALSE;
1123 else if (!had_props)
1124 pristine_props = apr_hash_make(scratch_pool);
1125 else
1126 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
1127 db, local_abspath,
1128 scratch_pool, scratch_pool));
1130 /* Report the addition of the directory's contents. */
1131 iterpool = svn_pool_create(scratch_pool);
1133 SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
1134 relpath,
1135 NULL,
1136 right_src,
1137 copyfrom_src,
1138 processor_parent_baton,
1139 processor,
1140 scratch_pool, iterpool));
1142 if ((depth > svn_depth_empty || depth == svn_depth_unknown)
1143 && ! skip_children)
1145 svn_depth_t depth_below_here = depth;
1146 apr_hash_t *nodes;
1147 apr_hash_t *conflicts;
1149 if (depth_below_here == svn_depth_immediates)
1150 depth_below_here = svn_depth_empty;
1152 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
1153 db, local_abspath,
1154 FALSE /* base_tree_only */,
1155 scratch_pool, iterpool));
1158 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1159 scratch_pool);
1161 for (i = 0; i < children->nelts; i++)
1163 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
1164 const char *name = item->key;
1165 struct svn_wc__db_info_t *info = item->value;
1166 const char *child_abspath;
1167 const char *child_relpath;
1169 svn_pool_clear(iterpool);
1171 if (cancel_func)
1172 SVN_ERR(cancel_func(cancel_baton));
1174 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1176 if (NOT_PRESENT(info->status))
1178 continue;
1181 /* If comparing against WORKING, skip entries that are
1182 schedule-deleted - they don't really exist. */
1183 if (!diff_pristine && info->status == svn_wc__db_status_deleted)
1184 continue;
1186 child_relpath = svn_relpath_join(relpath, name, iterpool);
1188 if (info->moved_here)
1190 const char *moved_from_abspath;
1191 const char *a_abspath;
1192 const char *a_relpath;
1194 a_relpath = relpath;
1195 a_abspath = local_abspath;
1196 while (*a_relpath)
1198 a_relpath = svn_relpath_dirname(a_relpath, iterpool);
1199 a_abspath = svn_dirent_dirname(a_abspath, iterpool);
1202 SVN_ERR(svn_wc__db_scan_moved(&moved_from_abspath,
1203 NULL, NULL, NULL,
1204 db, child_abspath,
1205 iterpool, iterpool));
1206 SVN_ERR_ASSERT(moved_from_abspath != NULL);
1208 moved_from_relpath = svn_dirent_skip_ancestor(
1209 a_abspath,
1210 moved_from_abspath);
1212 else
1213 moved_from_relpath = NULL;
1215 switch (info->kind)
1217 case svn_node_file:
1218 case svn_node_symlink:
1219 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
1220 child_relpath,
1221 moved_from_relpath,
1222 processor, pdb,
1223 diff_pristine,
1224 cancel_func, cancel_baton,
1225 scratch_pool));
1226 break;
1228 case svn_node_dir:
1229 if (depth > svn_depth_files || depth == svn_depth_unknown)
1231 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
1232 child_relpath,
1233 depth_below_here,
1234 moved_from_relpath,
1235 processor, pdb,
1236 diff_pristine,
1237 cancel_func,
1238 cancel_baton,
1239 iterpool));
1241 break;
1243 default:
1244 break;
1249 if (!skip)
1251 apr_hash_t *right_props;
1253 if (props_mod && !diff_pristine)
1254 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1255 scratch_pool, scratch_pool));
1256 else
1257 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1259 SVN_ERR(processor->dir_added(relpath,
1260 copyfrom_src,
1261 right_src,
1262 copyfrom_src
1263 ? pristine_props
1264 : NULL,
1265 right_props,
1266 pdb,
1267 processor,
1268 iterpool));
1270 svn_pool_destroy(iterpool);
1272 return SVN_NO_ERROR;
1275 /* Reports local changes. */
1276 static svn_error_t *
1277 handle_local_only(struct dir_baton_t *pb,
1278 const char *name,
1279 apr_pool_t *scratch_pool)
1281 struct edit_baton_t *eb = pb->eb;
1282 const struct svn_wc__db_info_t *info;
1283 const char *child_abspath;
1284 const char *moved_from_relpath;
1285 svn_boolean_t repos_delete = (pb->deletes
1286 && svn_hash_gets(pb->deletes, name));
1288 assert(!strchr(name, '/'));
1289 assert(!pb->added || eb->ignore_ancestry);
1291 if (pb->skip_children)
1292 return SVN_NO_ERROR;
1294 SVN_ERR(ensure_local_info(pb, scratch_pool));
1296 info = svn_hash_gets(pb->local_info, name);
1298 if (info == NULL || NOT_PRESENT(info->status))
1299 return SVN_NO_ERROR;
1301 switch (info->status)
1303 case svn_wc__db_status_normal:
1304 case svn_wc__db_status_incomplete:
1305 if (!repos_delete)
1306 return SVN_NO_ERROR; /* Local and remote */
1307 svn_hash_sets(pb->deletes, name, NULL);
1308 break;
1310 case svn_wc__db_status_deleted:
1311 if (!(eb->diff_pristine && repos_delete))
1312 return SVN_NO_ERROR;
1313 break;
1315 case svn_wc__db_status_added:
1316 default:
1317 break;
1320 child_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
1322 if (info->moved_here)
1324 const char *moved_from_abspath;
1326 SVN_ERR(svn_wc__db_scan_moved(&moved_from_abspath,
1327 NULL, NULL, NULL,
1328 eb->db, child_abspath,
1329 scratch_pool, scratch_pool));
1330 SVN_ERR_ASSERT(moved_from_abspath != NULL);
1332 moved_from_relpath = svn_dirent_skip_ancestor(
1333 eb->anchor_abspath,
1334 moved_from_abspath);
1336 else
1337 moved_from_relpath = NULL;
1339 if (info->kind == svn_node_dir)
1341 svn_depth_t depth ;
1343 if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
1344 depth = pb->depth;
1345 else
1346 depth = svn_depth_empty;
1348 SVN_ERR(svn_wc__diff_local_only_dir(
1349 eb->db,
1350 child_abspath,
1351 svn_relpath_join(pb->relpath, name, scratch_pool),
1352 repos_delete ? svn_depth_infinity : depth,
1353 moved_from_relpath,
1354 eb->processor, pb->pdb,
1355 eb->diff_pristine,
1356 eb->cancel_func, eb->cancel_baton,
1357 scratch_pool));
1359 else
1360 SVN_ERR(svn_wc__diff_local_only_file(
1361 eb->db,
1362 child_abspath,
1363 svn_relpath_join(pb->relpath, name, scratch_pool),
1364 moved_from_relpath,
1365 eb->processor, pb->pdb,
1366 eb->diff_pristine,
1367 eb->cancel_func, eb->cancel_baton,
1368 scratch_pool));
1370 return SVN_NO_ERROR;
1373 /* Reports a file LOCAL_ABSPATH in BASE as deleted */
1374 svn_error_t *
1375 svn_wc__diff_base_only_file(svn_wc__db_t *db,
1376 const char *local_abspath,
1377 const char *relpath,
1378 svn_revnum_t revision,
1379 const svn_diff_tree_processor_t *processor,
1380 void *processor_parent_baton,
1381 apr_pool_t *scratch_pool)
1383 svn_wc__db_status_t status;
1384 svn_node_kind_t kind;
1385 const svn_checksum_t *checksum;
1386 apr_hash_t *props;
1387 void *file_baton = NULL;
1388 svn_boolean_t skip = FALSE;
1389 svn_diff_source_t *left_src;
1390 const char *pristine_file;
1392 SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
1393 SVN_IS_VALID_REVNUM(revision)
1394 ? NULL : &revision,
1395 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1396 &checksum, NULL, NULL, NULL, &props, NULL,
1397 db, local_abspath,
1398 scratch_pool, scratch_pool));
1400 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
1401 && kind == svn_node_file
1402 && checksum);
1404 left_src = svn_diff__source_create(revision, scratch_pool);
1406 SVN_ERR(processor->file_opened(&file_baton, &skip,
1407 relpath,
1408 left_src,
1409 NULL /* right_src */,
1410 NULL /* copyfrom_source */,
1411 processor_parent_baton,
1412 processor,
1413 scratch_pool, scratch_pool));
1415 if (skip)
1416 return SVN_NO_ERROR;
1418 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
1419 db, local_abspath, checksum,
1420 scratch_pool, scratch_pool));
1422 SVN_ERR(processor->file_deleted(relpath,
1423 left_src,
1424 pristine_file,
1425 props,
1426 file_baton,
1427 processor,
1428 scratch_pool));
1430 return SVN_NO_ERROR;
1433 svn_error_t *
1434 svn_wc__diff_base_only_dir(svn_wc__db_t *db,
1435 const char *local_abspath,
1436 const char *relpath,
1437 svn_revnum_t revision,
1438 svn_depth_t depth,
1439 const svn_diff_tree_processor_t *processor,
1440 void *processor_parent_baton,
1441 svn_cancel_func_t cancel_func,
1442 void *cancel_baton,
1443 apr_pool_t *scratch_pool)
1445 void *dir_baton = NULL;
1446 svn_boolean_t skip = FALSE;
1447 svn_boolean_t skip_children = FALSE;
1448 svn_diff_source_t *left_src;
1449 svn_revnum_t report_rev = revision;
1451 if (!SVN_IS_VALID_REVNUM(report_rev))
1452 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
1453 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1454 NULL, NULL, NULL,
1455 db, local_abspath,
1456 scratch_pool, scratch_pool));
1458 left_src = svn_diff__source_create(report_rev, scratch_pool);
1460 SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
1461 relpath,
1462 left_src,
1463 NULL /* right_src */,
1464 NULL /* copyfrom_src */,
1465 processor_parent_baton,
1466 processor,
1467 scratch_pool, scratch_pool));
1469 if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
1471 apr_hash_t *nodes;
1472 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1473 apr_array_header_t *children;
1474 int i;
1476 SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
1477 scratch_pool, iterpool));
1479 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1480 scratch_pool);
1482 for (i = 0; i < children->nelts; i++)
1484 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1485 svn_sort__item_t);
1486 const char *name = item->key;
1487 struct svn_wc__db_base_info_t *info = item->value;
1488 const char *child_abspath;
1489 const char *child_relpath;
1491 if (info->status != svn_wc__db_status_normal)
1492 continue;
1494 if (cancel_func)
1495 SVN_ERR(cancel_func(cancel_baton));
1497 svn_pool_clear(iterpool);
1499 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1500 child_relpath = svn_relpath_join(relpath, name, iterpool);
1502 switch (info->kind)
1504 case svn_node_file:
1505 case svn_node_symlink:
1506 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
1507 child_relpath,
1508 revision,
1509 processor, dir_baton,
1510 iterpool));
1511 break;
1512 case svn_node_dir:
1513 if (depth > svn_depth_files || depth == svn_depth_unknown)
1515 svn_depth_t depth_below_here = depth;
1517 if (depth_below_here == svn_depth_immediates)
1518 depth_below_here = svn_depth_empty;
1520 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
1521 child_relpath,
1522 revision,
1523 depth_below_here,
1524 processor, dir_baton,
1525 cancel_func,
1526 cancel_baton,
1527 iterpool));
1529 break;
1531 default:
1532 break;
1537 if (!skip)
1539 apr_hash_t *props;
1540 SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
1541 scratch_pool, scratch_pool));
1543 SVN_ERR(processor->dir_deleted(relpath,
1544 left_src,
1545 props,
1546 dir_baton,
1547 processor,
1548 scratch_pool));
1551 return SVN_NO_ERROR;
1554 /* An svn_delta_editor_t function. */
1555 static svn_error_t *
1556 set_target_revision(void *edit_baton,
1557 svn_revnum_t target_revision,
1558 apr_pool_t *pool)
1560 struct edit_baton_t *eb = edit_baton;
1561 eb->revnum = target_revision;
1563 return SVN_NO_ERROR;
1566 /* An svn_delta_editor_t function. The root of the comparison hierarchy */
1567 static svn_error_t *
1568 open_root(void *edit_baton,
1569 svn_revnum_t base_revision,
1570 apr_pool_t *dir_pool,
1571 void **root_baton)
1573 struct edit_baton_t *eb = edit_baton;
1574 struct dir_baton_t *db;
1576 eb->root_opened = TRUE;
1577 db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
1578 *root_baton = db;
1580 if (eb->target[0] == '\0')
1582 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1583 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1585 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
1586 &db->skip_children,
1588 db->left_src,
1589 db->right_src,
1590 NULL /* copyfrom_source */,
1591 NULL /* parent_baton */,
1592 eb->processor,
1593 db->pool, db->pool));
1595 else
1596 db->skip = TRUE; /* Skip this, but not the children */
1598 return SVN_NO_ERROR;
1601 /* An svn_delta_editor_t function. */
1602 static svn_error_t *
1603 delete_entry(const char *path,
1604 svn_revnum_t base_revision,
1605 void *parent_baton,
1606 apr_pool_t *pool)
1608 struct dir_baton_t *pb = parent_baton;
1609 const char *name = svn_dirent_basename(path, pb->pool);
1611 if (!pb->deletes)
1612 pb->deletes = apr_hash_make(pb->pool);
1614 svn_hash_sets(pb->deletes, name, "");
1615 return SVN_NO_ERROR;
1618 /* An svn_delta_editor_t function. */
1619 static svn_error_t *
1620 add_directory(const char *path,
1621 void *parent_baton,
1622 const char *copyfrom_path,
1623 svn_revnum_t copyfrom_revision,
1624 apr_pool_t *dir_pool,
1625 void **child_baton)
1627 struct dir_baton_t *pb = parent_baton;
1628 struct edit_baton_t *eb = pb->eb;
1629 struct dir_baton_t *db;
1630 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1631 ? svn_depth_empty : pb->depth;
1633 db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
1634 dir_pool);
1635 *child_baton = db;
1637 if (pb->repos_only || !eb->ignore_ancestry)
1638 db->repos_only = TRUE;
1639 else
1641 struct svn_wc__db_info_t *info;
1642 SVN_ERR(ensure_local_info(pb, dir_pool));
1644 info = svn_hash_gets(pb->local_info, db->name);
1646 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1647 db->repos_only = TRUE;
1649 if (!db->repos_only && info->status != svn_wc__db_status_added)
1650 db->repos_only = TRUE;
1652 if (!db->repos_only)
1654 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1655 db->ignoring_ancestry = TRUE;
1657 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1661 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1663 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1664 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1666 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1667 db->relpath,
1668 db->left_src,
1669 db->right_src,
1670 NULL /* copyfrom src */,
1671 pb->pdb,
1672 eb->processor,
1673 db->pool, db->pool));
1675 return SVN_NO_ERROR;
1678 /* An svn_delta_editor_t function. */
1679 static svn_error_t *
1680 open_directory(const char *path,
1681 void *parent_baton,
1682 svn_revnum_t base_revision,
1683 apr_pool_t *dir_pool,
1684 void **child_baton)
1686 struct dir_baton_t *pb = parent_baton;
1687 struct edit_baton_t *eb = pb->eb;
1688 struct dir_baton_t *db;
1689 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1690 ? svn_depth_empty : pb->depth;
1692 /* Allocate path from the parent pool since the memory is used in the
1693 parent's compared hash */
1694 db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
1695 *child_baton = db;
1697 if (pb->repos_only)
1698 db->repos_only = TRUE;
1699 else
1701 struct svn_wc__db_info_t *info;
1702 SVN_ERR(ensure_local_info(pb, dir_pool));
1704 info = svn_hash_gets(pb->local_info, db->name);
1706 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1707 db->repos_only = TRUE;
1709 if (!db->repos_only)
1711 switch (info->status)
1713 case svn_wc__db_status_normal:
1714 case svn_wc__db_status_incomplete:
1715 db->is_incomplete = (info->status ==
1716 svn_wc__db_status_incomplete);
1717 break;
1718 case svn_wc__db_status_deleted:
1719 db->repos_only = TRUE;
1721 if (!info->have_more_work)
1722 svn_hash_sets(pb->compared,
1723 apr_pstrdup(pb->pool, db->name), "");
1724 break;
1725 case svn_wc__db_status_added:
1726 if (eb->ignore_ancestry)
1727 db->ignoring_ancestry = TRUE;
1728 else
1729 db->repos_only = TRUE;
1730 break;
1731 default:
1732 SVN_ERR_MALFUNCTION();
1735 if (info->status == svn_wc__db_status_added
1736 || info->status == svn_wc__db_status_deleted)
1738 svn_wc__db_status_t base_status;
1740 /* Node is shadowed; check BASE */
1741 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, NULL,
1742 NULL, NULL, NULL, NULL, NULL,
1743 NULL, NULL, NULL, NULL, NULL,
1744 NULL, NULL, NULL,
1745 eb->db, db->local_abspath,
1746 dir_pool, dir_pool));
1748 if (base_status == svn_wc__db_status_incomplete)
1749 db->is_incomplete = TRUE;
1753 if (!db->repos_only)
1755 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1756 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1760 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1762 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1763 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1765 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1766 db->relpath,
1767 db->left_src,
1768 db->right_src,
1769 NULL /* copyfrom src */,
1770 pb->pdb,
1771 eb->processor,
1772 db->pool, db->pool));
1774 return SVN_NO_ERROR;
1778 /* An svn_delta_editor_t function. When a directory is closed, all the
1779 * directory elements that have been added or replaced will already have been
1780 * diff'd. However there may be other elements in the working copy
1781 * that have not yet been considered. */
1782 static svn_error_t *
1783 close_directory(void *dir_baton,
1784 apr_pool_t *pool)
1786 struct dir_baton_t *db = dir_baton;
1787 struct dir_baton_t *pb = db->parent_baton;
1788 struct edit_baton_t *eb = db->eb;
1789 apr_pool_t *scratch_pool = db->pool;
1790 svn_boolean_t reported_closed = FALSE;
1792 if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
1794 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1795 apr_array_header_t *children;
1796 int i;
1797 children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
1798 scratch_pool);
1800 for (i = 0; i < children->nelts; i++)
1802 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1803 svn_sort__item_t);
1804 const char *name = item->key;
1806 svn_pool_clear(iterpool);
1807 SVN_ERR(handle_local_only(db, name, iterpool));
1809 svn_hash_sets(db->compared, name, "");
1812 svn_pool_destroy(iterpool);
1815 /* Report local modifications for this directory. Skip added
1816 directories since they can only contain added elements, all of
1817 which have already been diff'd. */
1818 if (!db->repos_only && !db->skip_children)
1820 SVN_ERR(walk_local_nodes_diff(eb,
1821 db->local_abspath,
1822 db->relpath,
1823 db->depth,
1824 db->compared,
1825 db->pdb,
1826 scratch_pool));
1829 /* Report the property changes on the directory itself, if necessary. */
1830 if (db->skip)
1832 /* Diff processor requested no directory details */
1834 else if (db->propchanges->nelts > 0 || db->repos_only)
1836 apr_hash_t *repos_props;
1838 if (db->added || db->is_incomplete)
1840 repos_props = apr_hash_make(scratch_pool);
1842 else
1844 SVN_ERR(svn_wc__db_base_get_props(&repos_props,
1845 eb->db, db->local_abspath,
1846 scratch_pool, scratch_pool));
1849 /* Add received property changes and entry props */
1850 if (db->propchanges->nelts)
1851 repos_props = svn_prop__patch(repos_props, db->propchanges,
1852 scratch_pool);
1854 if (db->repos_only)
1856 SVN_ERR(eb->processor->dir_deleted(db->relpath,
1857 db->left_src,
1858 repos_props,
1859 db->pdb,
1860 eb->processor,
1861 scratch_pool));
1862 reported_closed = TRUE;
1864 else
1866 apr_hash_t *local_props;
1867 apr_array_header_t *prop_changes;
1869 if (eb->diff_pristine)
1870 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1871 NULL, NULL, NULL, NULL,
1872 &local_props,
1873 eb->db, db->local_abspath,
1874 scratch_pool, scratch_pool));
1875 else
1876 SVN_ERR(svn_wc__db_read_props(&local_props,
1877 eb->db, db->local_abspath,
1878 scratch_pool, scratch_pool));
1880 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
1881 scratch_pool));
1883 /* ### as a good diff processor we should now only report changes
1884 if there are non-entry changes, but for now we stick to
1885 compatibility */
1887 if (prop_changes->nelts)
1889 SVN_ERR(eb->processor->dir_changed(db->relpath,
1890 db->left_src,
1891 db->right_src,
1892 repos_props,
1893 local_props,
1894 prop_changes,
1895 db->pdb,
1896 eb->processor,
1897 scratch_pool));
1898 reported_closed = TRUE;
1903 /* Mark this directory as compared in the parent directory's baton,
1904 unless this is the root of the comparison. */
1905 if (!reported_closed && !db->skip)
1906 SVN_ERR(eb->processor->dir_closed(db->relpath,
1907 db->left_src,
1908 db->right_src,
1909 db->pdb,
1910 eb->processor,
1911 scratch_pool));
1913 if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1914 SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
1916 SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
1918 return SVN_NO_ERROR;
1921 /* An svn_delta_editor_t function. */
1922 static svn_error_t *
1923 add_file(const char *path,
1924 void *parent_baton,
1925 const char *copyfrom_path,
1926 svn_revnum_t copyfrom_revision,
1927 apr_pool_t *file_pool,
1928 void **file_baton)
1930 struct dir_baton_t *pb = parent_baton;
1931 struct edit_baton_t *eb = pb->eb;
1932 struct file_baton_t *fb;
1934 fb = make_file_baton(path, TRUE, pb, file_pool);
1935 *file_baton = fb;
1937 if (pb->skip_children)
1939 fb->skip = TRUE;
1940 return SVN_NO_ERROR;
1942 else if (pb->repos_only || !eb->ignore_ancestry)
1943 fb->repos_only = TRUE;
1944 else
1946 struct svn_wc__db_info_t *info;
1947 SVN_ERR(ensure_local_info(pb, file_pool));
1949 info = svn_hash_gets(pb->local_info, fb->name);
1951 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1952 fb->repos_only = TRUE;
1954 if (!fb->repos_only && info->status != svn_wc__db_status_added)
1955 fb->repos_only = TRUE;
1957 if (!fb->repos_only)
1959 /* Add this path to the parent directory's list of elements that
1960 have been compared. */
1961 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1962 fb->ignoring_ancestry = TRUE;
1964 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1968 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1970 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1971 fb->relpath,
1972 fb->left_src,
1973 fb->right_src,
1974 NULL /* copyfrom src */,
1975 pb->pdb,
1976 eb->processor,
1977 fb->pool, fb->pool));
1979 return SVN_NO_ERROR;
1982 /* An svn_delta_editor_t function. */
1983 static svn_error_t *
1984 open_file(const char *path,
1985 void *parent_baton,
1986 svn_revnum_t base_revision,
1987 apr_pool_t *file_pool,
1988 void **file_baton)
1990 struct dir_baton_t *pb = parent_baton;
1991 struct edit_baton_t *eb = pb->eb;
1992 struct file_baton_t *fb;
1994 fb = make_file_baton(path, FALSE, pb, file_pool);
1995 *file_baton = fb;
1997 if (pb->skip_children)
1998 fb->skip = TRUE;
1999 else if (pb->repos_only)
2000 fb->repos_only = TRUE;
2001 else
2003 struct svn_wc__db_info_t *info;
2004 SVN_ERR(ensure_local_info(pb, file_pool));
2006 info = svn_hash_gets(pb->local_info, fb->name);
2008 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
2009 fb->repos_only = TRUE;
2011 if (!fb->repos_only)
2012 switch (info->status)
2014 case svn_wc__db_status_normal:
2015 case svn_wc__db_status_incomplete:
2016 break;
2017 case svn_wc__db_status_deleted:
2018 fb->repos_only = TRUE;
2019 if (!info->have_more_work)
2020 svn_hash_sets(pb->compared,
2021 apr_pstrdup(pb->pool, fb->name), "");
2022 break;
2023 case svn_wc__db_status_added:
2024 if (eb->ignore_ancestry)
2025 fb->ignoring_ancestry = TRUE;
2026 else
2027 fb->repos_only = TRUE;
2028 break;
2029 default:
2030 SVN_ERR_MALFUNCTION();
2033 if (!fb->repos_only)
2035 /* Add this path to the parent directory's list of elements that
2036 have been compared. */
2037 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
2038 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
2042 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
2044 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2045 NULL, NULL, NULL, &fb->base_checksum, NULL,
2046 NULL, NULL, &fb->base_props, NULL,
2047 eb->db, fb->local_abspath,
2048 fb->pool, fb->pool));
2050 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
2051 fb->relpath,
2052 fb->left_src,
2053 fb->right_src,
2054 NULL /* copyfrom src */,
2055 pb->pdb,
2056 eb->processor,
2057 fb->pool, fb->pool));
2059 return SVN_NO_ERROR;
2062 /* An svn_delta_editor_t function. */
2063 static svn_error_t *
2064 apply_textdelta(void *file_baton,
2065 const char *base_checksum_hex,
2066 apr_pool_t *pool,
2067 svn_txdelta_window_handler_t *handler,
2068 void **handler_baton)
2070 struct file_baton_t *fb = file_baton;
2071 struct edit_baton_t *eb = fb->eb;
2072 svn_stream_t *source;
2073 svn_stream_t *temp_stream;
2074 svn_checksum_t *repos_checksum = NULL;
2076 if (fb->skip)
2078 *handler = svn_delta_noop_window_handler;
2079 *handler_baton = NULL;
2080 return SVN_NO_ERROR;
2083 if (base_checksum_hex && fb->base_checksum)
2085 const svn_checksum_t *base_md5;
2086 SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
2087 base_checksum_hex, pool));
2089 SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
2090 eb->db, eb->anchor_abspath,
2091 fb->base_checksum,
2092 pool, pool));
2094 if (! svn_checksum_match(repos_checksum, base_md5))
2096 /* ### I expect that there are some bad drivers out there
2097 ### that used to give bad results. We could look in
2098 ### working to see if the expected checksum matches and
2099 ### then return the pristine of that... But that only moves
2100 ### the problem */
2102 /* If needed: compare checksum obtained via md5 of working.
2103 And if they match set fb->base_checksum and fb->base_props */
2105 return svn_checksum_mismatch_err(
2106 base_md5,
2107 repos_checksum,
2108 pool,
2109 _("Checksum mismatch for '%s'"),
2110 svn_dirent_local_style(fb->local_abspath,
2111 pool));
2114 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
2115 eb->db, fb->local_abspath,
2116 fb->base_checksum,
2117 pool, pool));
2119 else if (fb->base_checksum)
2121 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
2122 eb->db, fb->local_abspath,
2123 fb->base_checksum,
2124 pool, pool));
2126 else
2127 source = svn_stream_empty(pool);
2129 /* This is the file that will contain the pristine repository version. */
2130 SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
2131 svn_io_file_del_on_pool_cleanup,
2132 fb->pool, fb->pool));
2134 svn_txdelta_apply(source, temp_stream,
2135 fb->result_digest,
2136 fb->local_abspath /* error_info */,
2137 fb->pool,
2138 handler, handler_baton);
2140 return SVN_NO_ERROR;
2143 /* An svn_delta_editor_t function. When the file is closed we have a temporary
2144 * file containing a pristine version of the repository file. This can
2145 * be compared against the working copy.
2147 * Ignore TEXT_CHECKSUM.
2149 static svn_error_t *
2150 close_file(void *file_baton,
2151 const char *expected_md5_digest,
2152 apr_pool_t *pool)
2154 struct file_baton_t *fb = file_baton;
2155 struct dir_baton_t *pb = fb->parent_baton;
2156 struct edit_baton_t *eb = fb->eb;
2157 apr_pool_t *scratch_pool = fb->pool;
2159 /* The repository information; constructed from BASE + Changes */
2160 const char *repos_file;
2161 apr_hash_t *repos_props;
2163 if (fb->skip)
2165 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2166 SVN_ERR(maybe_done(pb));
2167 return SVN_NO_ERROR;
2170 if (expected_md5_digest != NULL)
2172 svn_checksum_t *expected_checksum;
2173 const svn_checksum_t *result_checksum;
2175 if (fb->temp_file_path)
2176 result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
2177 scratch_pool);
2178 else
2179 result_checksum = fb->base_checksum;
2181 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
2182 expected_md5_digest, scratch_pool));
2184 if (result_checksum->kind != svn_checksum_md5)
2185 SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
2186 eb->db, fb->local_abspath,
2187 result_checksum,
2188 scratch_pool, scratch_pool));
2190 if (!svn_checksum_match(expected_checksum, result_checksum))
2191 return svn_checksum_mismatch_err(
2192 expected_checksum,
2193 result_checksum,
2194 pool,
2195 _("Checksum mismatch for '%s'"),
2196 svn_dirent_local_style(fb->local_abspath,
2197 scratch_pool));
2200 if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2201 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2204 apr_hash_t *prop_base;
2206 if (fb->added)
2207 prop_base = apr_hash_make(scratch_pool);
2208 else
2209 prop_base = fb->base_props;
2211 /* includes entry props */
2212 repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
2214 repos_file = fb->temp_file_path;
2215 if (! repos_file)
2217 assert(fb->base_checksum);
2218 SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
2219 eb->db, eb->anchor_abspath,
2220 fb->base_checksum,
2221 scratch_pool, scratch_pool));
2225 if (fb->repos_only)
2227 SVN_ERR(eb->processor->file_deleted(fb->relpath,
2228 fb->left_src,
2229 fb->temp_file_path,
2230 repos_props,
2231 fb->pfb,
2232 eb->processor,
2233 scratch_pool));
2235 else
2237 /* Produce a diff of actual or pristine against repos */
2238 apr_hash_t *local_props;
2239 apr_array_header_t *prop_changes;
2240 const char *localfile;
2242 /* pb->local_info contains some information that might allow optimizing
2243 this a bit */
2245 if (eb->diff_pristine)
2247 const svn_checksum_t *checksum;
2248 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
2249 NULL, &checksum, NULL, NULL,
2250 &local_props,
2251 eb->db, fb->local_abspath,
2252 scratch_pool, scratch_pool));
2253 assert(checksum);
2254 SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
2255 eb->db, eb->anchor_abspath,
2256 checksum,
2257 scratch_pool, scratch_pool));
2259 else
2261 SVN_ERR(svn_wc__db_read_props(&local_props,
2262 eb->db, fb->local_abspath,
2263 scratch_pool, scratch_pool));
2265 /* a detranslated version of the working file */
2266 SVN_ERR(svn_wc__internal_translated_file(
2267 &localfile, fb->local_abspath, eb->db, fb->local_abspath,
2268 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
2269 eb->cancel_func, eb->cancel_baton,
2270 scratch_pool, scratch_pool));
2273 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
2274 scratch_pool));
2277 /* ### as a good diff processor we should now only report changes, and
2278 report file_closed() in other cases */
2279 SVN_ERR(eb->processor->file_changed(fb->relpath,
2280 fb->left_src,
2281 fb->right_src,
2282 repos_file /* left file */,
2283 localfile /* right file */,
2284 repos_props /* left_props */,
2285 local_props /* right props */,
2286 TRUE /* ### file_modified */,
2287 prop_changes,
2288 fb->pfb,
2289 eb->processor,
2290 scratch_pool));
2293 if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2294 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2296 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2297 SVN_ERR(maybe_done(pb));
2298 return SVN_NO_ERROR;
2302 /* An svn_delta_editor_t function. */
2303 static svn_error_t *
2304 change_file_prop(void *file_baton,
2305 const char *name,
2306 const svn_string_t *value,
2307 apr_pool_t *pool)
2309 struct file_baton_t *fb = file_baton;
2310 svn_prop_t *propchange;
2311 svn_prop_kind_t propkind;
2313 propkind = svn_property_kind2(name);
2314 if (propkind == svn_prop_wc_kind)
2315 return SVN_NO_ERROR;
2316 else if (propkind == svn_prop_regular_kind)
2317 fb->has_propchange = TRUE;
2319 propchange = apr_array_push(fb->propchanges);
2320 propchange->name = apr_pstrdup(fb->pool, name);
2321 propchange->value = svn_string_dup(value, fb->pool);
2323 return SVN_NO_ERROR;
2327 /* An svn_delta_editor_t function. */
2328 static svn_error_t *
2329 change_dir_prop(void *dir_baton,
2330 const char *name,
2331 const svn_string_t *value,
2332 apr_pool_t *pool)
2334 struct dir_baton_t *db = dir_baton;
2335 svn_prop_t *propchange;
2336 svn_prop_kind_t propkind;
2338 propkind = svn_property_kind2(name);
2339 if (propkind == svn_prop_wc_kind)
2340 return SVN_NO_ERROR;
2341 else if (propkind == svn_prop_regular_kind)
2342 db->has_propchange = TRUE;
2344 propchange = apr_array_push(db->propchanges);
2345 propchange->name = apr_pstrdup(db->pool, name);
2346 propchange->value = svn_string_dup(value, db->pool);
2348 return SVN_NO_ERROR;
2352 /* An svn_delta_editor_t function. */
2353 static svn_error_t *
2354 close_edit(void *edit_baton,
2355 apr_pool_t *pool)
2357 struct edit_baton_t *eb = edit_baton;
2359 if (!eb->root_opened)
2361 SVN_ERR(walk_local_nodes_diff(eb,
2362 eb->anchor_abspath,
2364 eb->depth,
2365 NULL /* compared */,
2366 NULL /* No parent_baton */,
2367 eb->pool));
2370 return SVN_NO_ERROR;
2373 /* Public Interface */
2376 /* Create a diff editor and baton. */
2377 svn_error_t *
2378 svn_wc__get_diff_editor(const svn_delta_editor_t **editor,
2379 void **edit_baton,
2380 svn_wc_context_t *wc_ctx,
2381 const char *anchor_abspath,
2382 const char *target,
2383 svn_depth_t depth,
2384 svn_boolean_t ignore_ancestry,
2385 svn_boolean_t use_text_base,
2386 svn_boolean_t reverse_order,
2387 svn_boolean_t server_performs_filtering,
2388 const apr_array_header_t *changelist_filter,
2389 const svn_diff_tree_processor_t *diff_processor,
2390 svn_cancel_func_t cancel_func,
2391 void *cancel_baton,
2392 apr_pool_t *result_pool,
2393 apr_pool_t *scratch_pool)
2395 struct edit_baton_t *eb;
2396 void *inner_baton;
2397 svn_delta_editor_t *tree_editor;
2398 const svn_delta_editor_t *inner_editor;
2399 struct svn_wc__shim_fetch_baton_t *sfb;
2400 svn_delta_shim_callbacks_t *shim_callbacks =
2401 svn_delta_shim_callbacks_default(result_pool);
2403 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
2405 /* Apply changelist filtering to the output */
2406 if (changelist_filter && changelist_filter->nelts)
2408 apr_hash_t *changelist_hash;
2410 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
2411 result_pool));
2412 diff_processor = svn_wc__changelist_filter_tree_processor_create(
2413 diff_processor, wc_ctx, anchor_abspath,
2414 changelist_hash, result_pool);
2417 SVN_ERR(make_edit_baton(&eb,
2418 wc_ctx->db,
2419 anchor_abspath, target,
2420 diff_processor,
2421 depth, ignore_ancestry,
2422 use_text_base, reverse_order,
2423 cancel_func, cancel_baton,
2424 result_pool));
2426 tree_editor = svn_delta_default_editor(eb->pool);
2428 tree_editor->set_target_revision = set_target_revision;
2429 tree_editor->open_root = open_root;
2430 tree_editor->delete_entry = delete_entry;
2431 tree_editor->add_directory = add_directory;
2432 tree_editor->open_directory = open_directory;
2433 tree_editor->close_directory = close_directory;
2434 tree_editor->add_file = add_file;
2435 tree_editor->open_file = open_file;
2436 tree_editor->apply_textdelta = apply_textdelta;
2437 tree_editor->change_file_prop = change_file_prop;
2438 tree_editor->change_dir_prop = change_dir_prop;
2439 tree_editor->close_file = close_file;
2440 tree_editor->close_edit = close_edit;
2442 inner_editor = tree_editor;
2443 inner_baton = eb;
2445 if (!server_performs_filtering
2446 && depth == svn_depth_unknown)
2447 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2448 &inner_baton,
2449 wc_ctx->db,
2450 anchor_abspath,
2451 target,
2452 inner_editor,
2453 inner_baton,
2454 result_pool));
2456 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
2457 cancel_baton,
2458 inner_editor,
2459 inner_baton,
2460 editor,
2461 edit_baton,
2462 result_pool));
2464 sfb = apr_palloc(result_pool, sizeof(*sfb));
2465 sfb->db = wc_ctx->db;
2466 sfb->base_abspath = eb->anchor_abspath;
2467 sfb->fetch_base = TRUE;
2469 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2470 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2471 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2472 shim_callbacks->fetch_baton = sfb;
2475 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2476 NULL, NULL, shim_callbacks,
2477 result_pool, scratch_pool));
2479 return SVN_NO_ERROR;
2482 /* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
2484 /* baton for the svn_diff_tree_processor_t wrapper */
2485 typedef struct wc_diff_wrap_baton_t
2487 const svn_wc_diff_callbacks4_t *callbacks;
2488 void *callback_baton;
2490 svn_boolean_t walk_deleted_dirs;
2492 apr_pool_t *result_pool;
2493 const char *empty_file;
2495 } wc_diff_wrap_baton_t;
2497 static svn_error_t *
2498 wrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
2499 apr_pool_t *scratch_pool)
2501 if (wb->empty_file)
2502 return SVN_NO_ERROR;
2504 /* Create a unique file in the tempdir */
2505 SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
2506 svn_io_file_del_on_pool_cleanup,
2507 wb->result_pool, scratch_pool));
2509 return SVN_NO_ERROR;
2512 /* svn_diff_tree_processor_t function */
2513 static svn_error_t *
2514 wrap_dir_opened(void **new_dir_baton,
2515 svn_boolean_t *skip,
2516 svn_boolean_t *skip_children,
2517 const char *relpath,
2518 const svn_diff_source_t *left_source,
2519 const svn_diff_source_t *right_source,
2520 const svn_diff_source_t *copyfrom_source,
2521 void *parent_dir_baton,
2522 const svn_diff_tree_processor_t *processor,
2523 apr_pool_t *result_pool,
2524 apr_pool_t *scratch_pool)
2526 wc_diff_wrap_baton_t *wb = processor->baton;
2527 svn_boolean_t tree_conflicted = FALSE;
2529 assert(left_source || right_source); /* Must exist at one point. */
2530 assert(!left_source || !copyfrom_source); /* Either existed or added. */
2532 /* Maybe store state and tree_conflicted in baton? */
2533 if (left_source != NULL)
2535 /* Open for change or delete */
2536 SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
2537 relpath,
2538 right_source
2539 ? right_source->revision
2540 : (left_source
2541 ? left_source->revision
2542 : SVN_INVALID_REVNUM),
2543 wb->callback_baton,
2544 scratch_pool));
2546 if (! right_source && !wb->walk_deleted_dirs)
2547 *skip_children = TRUE;
2549 else /* left_source == NULL -> Add */
2551 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2552 SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
2553 skip, skip_children,
2554 relpath,
2555 right_source->revision,
2556 copyfrom_source
2557 ? copyfrom_source->repos_relpath
2558 : NULL,
2559 copyfrom_source
2560 ? copyfrom_source->revision
2561 : SVN_INVALID_REVNUM,
2562 wb->callback_baton,
2563 scratch_pool));
2566 *new_dir_baton = NULL;
2568 return SVN_NO_ERROR;
2571 /* svn_diff_tree_processor_t function */
2572 static svn_error_t *
2573 wrap_dir_added(const char *relpath,
2574 const svn_diff_source_t *copyfrom_source,
2575 const svn_diff_source_t *right_source,
2576 /*const*/ apr_hash_t *copyfrom_props,
2577 /*const*/ apr_hash_t *right_props,
2578 void *dir_baton,
2579 const svn_diff_tree_processor_t *processor,
2580 apr_pool_t *scratch_pool)
2582 wc_diff_wrap_baton_t *wb = processor->baton;
2583 svn_boolean_t tree_conflicted = FALSE;
2584 svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
2585 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2586 apr_hash_t *pristine_props = copyfrom_props;
2587 apr_array_header_t *prop_changes = NULL;
2589 if (right_props && apr_hash_count(right_props))
2591 if (!pristine_props)
2592 pristine_props = apr_hash_make(scratch_pool);
2594 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
2595 scratch_pool));
2597 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
2598 &tree_conflicted,
2599 relpath,
2600 TRUE /* dir_was_added */,
2601 prop_changes, pristine_props,
2602 wb->callback_baton,
2603 scratch_pool));
2606 SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
2607 &tree_conflicted,
2608 relpath,
2609 TRUE /* dir_was_added */,
2610 wb->callback_baton,
2611 scratch_pool));
2612 return SVN_NO_ERROR;
2615 /* svn_diff_tree_processor_t function */
2616 static svn_error_t *
2617 wrap_dir_deleted(const char *relpath,
2618 const svn_diff_source_t *left_source,
2619 /*const*/ apr_hash_t *left_props,
2620 void *dir_baton,
2621 const svn_diff_tree_processor_t *processor,
2622 apr_pool_t *scratch_pool)
2624 wc_diff_wrap_baton_t *wb = processor->baton;
2625 svn_boolean_t tree_conflicted = FALSE;
2626 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2628 SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
2629 relpath,
2630 wb->callback_baton,
2631 scratch_pool));
2633 return SVN_NO_ERROR;
2636 /* svn_diff_tree_processor_t function */
2637 static svn_error_t *
2638 wrap_dir_closed(const char *relpath,
2639 const svn_diff_source_t *left_source,
2640 const svn_diff_source_t *right_source,
2641 void *dir_baton,
2642 const svn_diff_tree_processor_t *processor,
2643 apr_pool_t *scratch_pool)
2645 wc_diff_wrap_baton_t *wb = processor->baton;
2647 /* No previous implementations provided these arguments, so we
2648 are not providing them either */
2649 SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
2650 relpath,
2651 FALSE /* added */,
2652 wb->callback_baton,
2653 scratch_pool));
2655 return SVN_NO_ERROR;
2658 /* svn_diff_tree_processor_t function */
2659 static svn_error_t *
2660 wrap_dir_changed(const char *relpath,
2661 const svn_diff_source_t *left_source,
2662 const svn_diff_source_t *right_source,
2663 /*const*/ apr_hash_t *left_props,
2664 /*const*/ apr_hash_t *right_props,
2665 const apr_array_header_t *prop_changes,
2666 void *dir_baton,
2667 const struct svn_diff_tree_processor_t *processor,
2668 apr_pool_t *scratch_pool)
2670 wc_diff_wrap_baton_t *wb = processor->baton;
2671 svn_boolean_t tree_conflicted = FALSE;
2672 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2674 assert(left_source && right_source);
2676 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
2677 relpath,
2678 FALSE /* dir_was_added */,
2679 prop_changes,
2680 left_props,
2681 wb->callback_baton,
2682 scratch_pool));
2684 /* And call dir_closed, etc */
2685 SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
2686 dir_baton, processor,
2687 scratch_pool));
2688 return SVN_NO_ERROR;
2691 /* svn_diff_tree_processor_t function */
2692 static svn_error_t *
2693 wrap_file_opened(void **new_file_baton,
2694 svn_boolean_t *skip,
2695 const char *relpath,
2696 const svn_diff_source_t *left_source,
2697 const svn_diff_source_t *right_source,
2698 const svn_diff_source_t *copyfrom_source,
2699 void *dir_baton,
2700 const svn_diff_tree_processor_t *processor,
2701 apr_pool_t *result_pool,
2702 apr_pool_t *scratch_pool)
2704 wc_diff_wrap_baton_t *wb = processor->baton;
2705 svn_boolean_t tree_conflicted = FALSE;
2707 if (left_source) /* If ! added */
2708 SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
2709 right_source
2710 ? right_source->revision
2711 : (left_source
2712 ? left_source->revision
2713 : SVN_INVALID_REVNUM),
2714 wb->callback_baton, scratch_pool));
2716 /* No old implementation used the output arguments for notify */
2718 *new_file_baton = NULL;
2719 return SVN_NO_ERROR;
2722 /* svn_diff_tree_processor_t function */
2723 static svn_error_t *
2724 wrap_file_added(const char *relpath,
2725 const svn_diff_source_t *copyfrom_source,
2726 const svn_diff_source_t *right_source,
2727 const char *copyfrom_file,
2728 const char *right_file,
2729 /*const*/ apr_hash_t *copyfrom_props,
2730 /*const*/ apr_hash_t *right_props,
2731 void *file_baton,
2732 const svn_diff_tree_processor_t *processor,
2733 apr_pool_t *scratch_pool)
2735 wc_diff_wrap_baton_t *wb = processor->baton;
2736 svn_boolean_t tree_conflicted = FALSE;
2737 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2738 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2739 apr_array_header_t *prop_changes;
2741 if (! copyfrom_props)
2742 copyfrom_props = apr_hash_make(scratch_pool);
2744 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
2745 scratch_pool));
2747 if (! copyfrom_source)
2748 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2750 SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
2751 relpath,
2752 copyfrom_source
2753 ? copyfrom_file
2754 : wb->empty_file,
2755 right_file,
2757 right_source->revision,
2758 copyfrom_props
2759 ? svn_prop_get_value(copyfrom_props,
2760 SVN_PROP_MIME_TYPE)
2761 : NULL,
2762 right_props
2763 ? svn_prop_get_value(right_props,
2764 SVN_PROP_MIME_TYPE)
2765 : NULL,
2766 copyfrom_source
2767 ? copyfrom_source->repos_relpath
2768 : NULL,
2769 copyfrom_source
2770 ? copyfrom_source->revision
2771 : SVN_INVALID_REVNUM,
2772 prop_changes, copyfrom_props,
2773 wb->callback_baton,
2774 scratch_pool));
2775 return SVN_NO_ERROR;
2778 static svn_error_t *
2779 wrap_file_deleted(const char *relpath,
2780 const svn_diff_source_t *left_source,
2781 const char *left_file,
2782 apr_hash_t *left_props,
2783 void *file_baton,
2784 const svn_diff_tree_processor_t *processor,
2785 apr_pool_t *scratch_pool)
2787 wc_diff_wrap_baton_t *wb = processor->baton;
2788 svn_boolean_t tree_conflicted = FALSE;
2789 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2791 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2793 SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
2794 relpath,
2795 left_file, wb->empty_file,
2796 left_props
2797 ? svn_prop_get_value(left_props,
2798 SVN_PROP_MIME_TYPE)
2799 : NULL,
2800 NULL,
2801 left_props,
2802 wb->callback_baton,
2803 scratch_pool));
2804 return SVN_NO_ERROR;
2807 /* svn_diff_tree_processor_t function */
2808 static svn_error_t *
2809 wrap_file_changed(const char *relpath,
2810 const svn_diff_source_t *left_source,
2811 const svn_diff_source_t *right_source,
2812 const char *left_file,
2813 const char *right_file,
2814 /*const*/ apr_hash_t *left_props,
2815 /*const*/ apr_hash_t *right_props,
2816 svn_boolean_t file_modified,
2817 const apr_array_header_t *prop_changes,
2818 void *file_baton,
2819 const svn_diff_tree_processor_t *processor,
2820 apr_pool_t *scratch_pool)
2822 wc_diff_wrap_baton_t *wb = processor->baton;
2823 svn_boolean_t tree_conflicted = FALSE;
2824 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2825 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2827 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2829 assert(left_source && right_source);
2831 SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
2832 relpath,
2833 file_modified ? left_file : NULL,
2834 file_modified ? right_file : NULL,
2835 left_source->revision,
2836 right_source->revision,
2837 left_props
2838 ? svn_prop_get_value(left_props,
2839 SVN_PROP_MIME_TYPE)
2840 : NULL,
2841 right_props
2842 ? svn_prop_get_value(right_props,
2843 SVN_PROP_MIME_TYPE)
2844 : NULL,
2845 prop_changes,
2846 left_props,
2847 wb->callback_baton,
2848 scratch_pool));
2849 return SVN_NO_ERROR;
2852 svn_error_t *
2853 svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
2854 const svn_wc_diff_callbacks4_t *callbacks,
2855 void *callback_baton,
2856 svn_boolean_t walk_deleted_dirs,
2857 apr_pool_t *result_pool,
2858 apr_pool_t *scratch_pool)
2860 wc_diff_wrap_baton_t *wrap_baton;
2861 svn_diff_tree_processor_t *processor;
2863 wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
2865 wrap_baton->result_pool = result_pool;
2866 wrap_baton->callbacks = callbacks;
2867 wrap_baton->callback_baton = callback_baton;
2868 wrap_baton->empty_file = NULL;
2869 wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
2871 processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
2873 processor->dir_opened = wrap_dir_opened;
2874 processor->dir_added = wrap_dir_added;
2875 processor->dir_deleted = wrap_dir_deleted;
2876 processor->dir_changed = wrap_dir_changed;
2877 processor->dir_closed = wrap_dir_closed;
2879 processor->file_opened = wrap_file_opened;
2880 processor->file_added = wrap_file_added;
2881 processor->file_deleted = wrap_file_deleted;
2882 processor->file_changed = wrap_file_changed;
2883 /*processor->file_closed = wrap_file_closed*/; /* Not needed */
2885 *diff_processor = processor;
2886 return SVN_NO_ERROR;
2889 /* =====================================================================
2890 * A tree processor filter that filters by changelist membership
2891 * =====================================================================
2893 * The current implementation queries the WC for the changelist of each
2894 * file as it comes through, and sets the 'skip' flag for a non-matching
2895 * file.
2897 * (It doesn't set the 'skip' flag for a directory, as we need to receive
2898 * the changed/added/deleted/closed call to know when it is closed, in
2899 * order to preserve the strict open-close semantics for the wrapped tree
2900 * processor.)
2902 * It passes on the opening and closing of every directory, even if there
2903 * are no file changes to be passed on inside that directory.
2906 typedef struct filter_tree_baton_t
2908 const svn_diff_tree_processor_t *processor;
2909 svn_wc_context_t *wc_ctx;
2910 /* WC path of the root of the diff (where relpath = "") */
2911 const char *root_local_abspath;
2912 /* Hash whose keys are const char * changelist names. */
2913 apr_hash_t *changelist_hash;
2914 } filter_tree_baton_t;
2916 static svn_error_t *
2917 filter_dir_opened(void **new_dir_baton,
2918 svn_boolean_t *skip,
2919 svn_boolean_t *skip_children,
2920 const char *relpath,
2921 const svn_diff_source_t *left_source,
2922 const svn_diff_source_t *right_source,
2923 const svn_diff_source_t *copyfrom_source,
2924 void *parent_dir_baton,
2925 const svn_diff_tree_processor_t *processor,
2926 apr_pool_t *result_pool,
2927 apr_pool_t *scratch_pool)
2929 struct filter_tree_baton_t *fb = processor->baton;
2931 SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
2932 relpath,
2933 left_source, right_source,
2934 copyfrom_source,
2935 parent_dir_baton,
2936 fb->processor,
2937 result_pool, scratch_pool));
2938 return SVN_NO_ERROR;
2941 static svn_error_t *
2942 filter_dir_added(const char *relpath,
2943 const svn_diff_source_t *copyfrom_source,
2944 const svn_diff_source_t *right_source,
2945 /*const*/ apr_hash_t *copyfrom_props,
2946 /*const*/ apr_hash_t *right_props,
2947 void *dir_baton,
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->dir_closed(relpath,
2954 NULL,
2955 right_source,
2956 dir_baton,
2957 fb->processor,
2958 scratch_pool));
2960 return SVN_NO_ERROR;
2963 static svn_error_t *
2964 filter_dir_deleted(const char *relpath,
2965 const svn_diff_source_t *left_source,
2966 /*const*/ apr_hash_t *left_props,
2967 void *dir_baton,
2968 const svn_diff_tree_processor_t *processor,
2969 apr_pool_t *scratch_pool)
2971 struct filter_tree_baton_t *fb = processor->baton;
2973 SVN_ERR(fb->processor->dir_closed(relpath,
2974 left_source,
2975 NULL,
2976 dir_baton,
2977 fb->processor,
2978 scratch_pool));
2980 return SVN_NO_ERROR;
2983 static svn_error_t *
2984 filter_dir_changed(const char *relpath,
2985 const svn_diff_source_t *left_source,
2986 const svn_diff_source_t *right_source,
2987 /*const*/ apr_hash_t *left_props,
2988 /*const*/ apr_hash_t *right_props,
2989 const apr_array_header_t *prop_changes,
2990 void *dir_baton,
2991 const struct svn_diff_tree_processor_t *processor,
2992 apr_pool_t *scratch_pool)
2994 struct filter_tree_baton_t *fb = processor->baton;
2996 SVN_ERR(fb->processor->dir_closed(relpath,
2997 left_source,
2998 right_source,
2999 dir_baton,
3000 fb->processor,
3001 scratch_pool));
3002 return SVN_NO_ERROR;
3005 static svn_error_t *
3006 filter_dir_closed(const char *relpath,
3007 const svn_diff_source_t *left_source,
3008 const svn_diff_source_t *right_source,
3009 void *dir_baton,
3010 const svn_diff_tree_processor_t *processor,
3011 apr_pool_t *scratch_pool)
3013 struct filter_tree_baton_t *fb = processor->baton;
3015 SVN_ERR(fb->processor->dir_closed(relpath,
3016 left_source,
3017 right_source,
3018 dir_baton,
3019 fb->processor,
3020 scratch_pool));
3021 return SVN_NO_ERROR;
3024 static svn_error_t *
3025 filter_file_opened(void **new_file_baton,
3026 svn_boolean_t *skip,
3027 const char *relpath,
3028 const svn_diff_source_t *left_source,
3029 const svn_diff_source_t *right_source,
3030 const svn_diff_source_t *copyfrom_source,
3031 void *dir_baton,
3032 const svn_diff_tree_processor_t *processor,
3033 apr_pool_t *result_pool,
3034 apr_pool_t *scratch_pool)
3036 struct filter_tree_baton_t *fb = processor->baton;
3037 const char *local_abspath
3038 = svn_dirent_join(fb->root_local_abspath, relpath, scratch_pool);
3040 /* Skip if not a member of a given changelist */
3041 if (! svn_wc__changelist_match(fb->wc_ctx, local_abspath,
3042 fb->changelist_hash, scratch_pool))
3044 *skip = TRUE;
3045 return SVN_NO_ERROR;
3048 SVN_ERR(fb->processor->file_opened(new_file_baton,
3049 skip,
3050 relpath,
3051 left_source,
3052 right_source,
3053 copyfrom_source,
3054 dir_baton,
3055 fb->processor,
3056 result_pool,
3057 scratch_pool));
3058 return SVN_NO_ERROR;
3061 static svn_error_t *
3062 filter_file_added(const char *relpath,
3063 const svn_diff_source_t *copyfrom_source,
3064 const svn_diff_source_t *right_source,
3065 const char *copyfrom_file,
3066 const char *right_file,
3067 /*const*/ apr_hash_t *copyfrom_props,
3068 /*const*/ apr_hash_t *right_props,
3069 void *file_baton,
3070 const svn_diff_tree_processor_t *processor,
3071 apr_pool_t *scratch_pool)
3073 struct filter_tree_baton_t *fb = processor->baton;
3075 SVN_ERR(fb->processor->file_added(relpath,
3076 copyfrom_source,
3077 right_source,
3078 copyfrom_file,
3079 right_file,
3080 copyfrom_props,
3081 right_props,
3082 file_baton,
3083 fb->processor,
3084 scratch_pool));
3085 return SVN_NO_ERROR;
3088 static svn_error_t *
3089 filter_file_deleted(const char *relpath,
3090 const svn_diff_source_t *left_source,
3091 const char *left_file,
3092 /*const*/ apr_hash_t *left_props,
3093 void *file_baton,
3094 const svn_diff_tree_processor_t *processor,
3095 apr_pool_t *scratch_pool)
3097 struct filter_tree_baton_t *fb = processor->baton;
3099 SVN_ERR(fb->processor->file_deleted(relpath,
3100 left_source,
3101 left_file,
3102 left_props,
3103 file_baton,
3104 fb->processor,
3105 scratch_pool));
3107 return SVN_NO_ERROR;
3110 static svn_error_t *
3111 filter_file_changed(const char *relpath,
3112 const svn_diff_source_t *left_source,
3113 const svn_diff_source_t *right_source,
3114 const char *left_file,
3115 const char *right_file,
3116 /*const*/ apr_hash_t *left_props,
3117 /*const*/ apr_hash_t *right_props,
3118 svn_boolean_t file_modified,
3119 const apr_array_header_t *prop_changes,
3120 void *file_baton,
3121 const svn_diff_tree_processor_t *processor,
3122 apr_pool_t *scratch_pool)
3124 struct filter_tree_baton_t *fb = processor->baton;
3126 SVN_ERR(fb->processor->file_changed(relpath,
3127 left_source,
3128 right_source,
3129 left_file,
3130 right_file,
3131 left_props,
3132 right_props,
3133 file_modified,
3134 prop_changes,
3135 file_baton,
3136 fb->processor,
3137 scratch_pool));
3138 return SVN_NO_ERROR;
3141 static svn_error_t *
3142 filter_file_closed(const char *relpath,
3143 const svn_diff_source_t *left_source,
3144 const svn_diff_source_t *right_source,
3145 void *file_baton,
3146 const svn_diff_tree_processor_t *processor,
3147 apr_pool_t *scratch_pool)
3149 struct filter_tree_baton_t *fb = processor->baton;
3151 SVN_ERR(fb->processor->file_closed(relpath,
3152 left_source,
3153 right_source,
3154 file_baton,
3155 fb->processor,
3156 scratch_pool));
3158 return SVN_NO_ERROR;
3161 static svn_error_t *
3162 filter_node_absent(const char *relpath,
3163 void *dir_baton,
3164 const svn_diff_tree_processor_t *processor,
3165 apr_pool_t *scratch_pool)
3167 struct filter_tree_baton_t *fb = processor->baton;
3169 SVN_ERR(fb->processor->node_absent(relpath,
3170 dir_baton,
3171 fb->processor,
3172 scratch_pool));
3173 return SVN_NO_ERROR;
3176 const svn_diff_tree_processor_t *
3177 svn_wc__changelist_filter_tree_processor_create(
3178 const svn_diff_tree_processor_t *processor,
3179 svn_wc_context_t *wc_ctx,
3180 const char *root_local_abspath,
3181 apr_hash_t *changelist_hash,
3182 apr_pool_t *result_pool)
3184 struct filter_tree_baton_t *fb;
3185 svn_diff_tree_processor_t *filter;
3187 if (! changelist_hash)
3188 return processor;
3190 fb = apr_pcalloc(result_pool, sizeof(*fb));
3191 fb->processor = processor;
3192 fb->wc_ctx = wc_ctx;
3193 fb->root_local_abspath = root_local_abspath;
3194 fb->changelist_hash = changelist_hash;
3196 filter = svn_diff__tree_processor_create(fb, result_pool);
3197 filter->dir_opened = filter_dir_opened;
3198 filter->dir_added = filter_dir_added;
3199 filter->dir_deleted = filter_dir_deleted;
3200 filter->dir_changed = filter_dir_changed;
3201 filter->dir_closed = filter_dir_closed;
3203 filter->file_opened = filter_file_opened;
3204 filter->file_added = filter_file_added;
3205 filter->file_deleted = filter_file_deleted;
3206 filter->file_changed = filter_file_changed;
3207 filter->file_closed = filter_file_closed;
3209 filter->node_absent = filter_node_absent;
3211 return filter;