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 /* TRUE if the directory was reported incomplete to the repository */
149 svn_boolean_t is_incomplete
;
151 /* Processor state */
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). */
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
;
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. */
198 const char *local_abspath
;
200 /* Processor state */
204 /* TRUE if the file is added by the editor drive. */
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
;
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.
244 make_edit_baton(struct edit_baton_t
**edit_baton
,
246 const char *anchor_abspath
,
248 const svn_diff_tree_processor_t
*diff_processor
,
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
,
257 struct edit_baton_t
*eb
;
259 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath
));
261 eb
= apr_pcalloc(pool
, sizeof(*eb
));
263 eb
->anchor_abspath
= apr_pstrdup(pool
, anchor_abspath
);
264 eb
->target
= apr_pstrdup(pool
, target
);
265 eb
->processor
= diff_processor
;
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
;
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
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
,
292 apr_pool_t
*result_pool
)
294 apr_pool_t
*dir_pool
= svn_pool_create(parent_baton
? parent_baton
->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
);
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
++;
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
,
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
;
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
);
347 fb
->pool
= file_pool
;
348 fb
->propchanges
= apr_array_make(file_pool
, 8, sizeof(svn_prop_t
));
353 /* Destroy DB when there are no more registered users */
355 maybe_done(struct dir_baton_t
*db
)
361 struct dir_baton_t
*pb
= db
->parent_baton
;
363 svn_pool_clear(db
->pool
);
366 SVN_ERR(maybe_done(pb
));
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)
379 svn_wc__diff_base_working_diff(svn_wc__db_t
*db
,
380 const char *local_abspath
,
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
,
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
,
429 scratch_pool
, scratch_pool
));
430 recorded_size
= SVN_INVALID_FILESIZE
;
432 props_mod
= TRUE
; /* Requires compare */
434 else if (diff_pristine
)
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
))
457 if (files_same
&& !props_mod
)
458 return SVN_NO_ERROR
; /* Cheap exit */
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
,
471 NULL
/* copyfrom_src */,
474 scratch_pool
, scratch_pool
));
479 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file
,
480 db
, local_abspath
, checksum
,
481 scratch_pool
, scratch_pool
));
484 SVN_ERR(svn_wc__db_pristine_get_path(&local_file
,
487 scratch_pool
, scratch_pool
));
488 else if (! (had_props
|| props_mod
))
489 local_file
= local_abspath
;
491 local_file
= pristine_file
;
493 SVN_ERR(svn_wc__internal_translated_file(
494 &local_file
, 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
));
502 SVN_ERR(svn_io_files_contents_same_p(&files_same
, local_file
,
503 pristine_file
, scratch_pool
));
506 SVN_ERR(svn_wc__db_base_get_props(&base_props
, db
, local_abspath
,
507 scratch_pool
, scratch_pool
));
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
));
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
,
539 SVN_ERR(processor
->file_closed(relpath
,
551 ensure_local_info(struct dir_baton_t
*db
,
552 apr_pool_t
*scratch_pool
)
554 apr_hash_t
*conflicts
;
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
));
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.
575 walk_local_nodes_diff(struct edit_baton_t
*eb
,
576 const char *local_abspath
,
579 apr_hash_t
*compared
,
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
)
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
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
);
617 dir_baton
= parent_baton
;
620 else if (!in_anchor_not_target
)
621 SVN_ERR(eb
->processor
->dir_opened(&dir_baton
, &skip
, &skip_children
,
625 NULL
/* copyfrom_src */,
628 scratch_pool
, scratch_pool
));
631 if (!skip_children
&& depth
!= svn_depth_empty
)
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
;
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
,
651 FALSE
/* base_tree_only */,
652 scratch_pool
, iterpool
));
654 children
= svn_sort__hash(nodes
, svn_sort_compare_items_lexically
,
657 for (i
= 0; i
< children
->nelts
; i
++)
659 svn_sort__item_t
*item
= &APR_ARRAY_IDX(children
, i
,
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
;
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
677 if (in_anchor_not_target
&& strcmp(eb
->target
, name
))
680 if (compared
&& svn_hash_gets(compared
, name
))
683 if (NOT_PRESENT(info
->status
))
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
);
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
)
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
;
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
,
719 iterpool
, iterpool
));
721 if (NOT_PRESENT(base_status
))
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
,
734 iterpool
, iterpool
));
736 if (NOT_PRESENT(base_status
))
738 else if (base_kind
!= info
->kind
|| !eb
->ignore_ancestry
)
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
,
756 iterpool
, iterpool
));
757 SVN_ERR_ASSERT(moved_from_abspath
!= NULL
);
759 moved_from_relpath
= svn_dirent_skip_ancestor(
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
,
770 eb
->processor
, dir_baton
,
775 else if (info
->kind
== svn_node_dir
&& diff_dirs
)
776 SVN_ERR(svn_wc__diff_local_only_dir(db
, child_abspath
,
780 eb
->processor
, dir_baton
,
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
,
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
,
799 eb
->processor
, dir_baton
,
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(
816 eb
->processor
, dir_baton
,
823 else if (info
->kind
== svn_node_dir
&& diff_dirs
)
824 SVN_ERR(walk_local_nodes_diff(eb
, child_abspath
,
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
,
843 iterpool
, iterpool
));
844 SVN_ERR_ASSERT(moved_from_abspath
!= NULL
);
846 moved_from_relpath
= svn_dirent_skip_ancestor(
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
,
857 eb
->processor
, dir_baton
,
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
,
866 eb
->processor
, dir_baton
,
878 /* Check for local property mods on this directory, if we haven't
879 already reported them. */
881 && ! in_anchor_not_target
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
,
890 scratch_pool
, scratch_pool
));
892 right_props
= svn_prop__patch(left_props
, propchanges
, scratch_pool
);
894 SVN_ERR(eb
->processor
->dir_changed(path
,
905 SVN_ERR(eb
->processor
->dir_closed(path
,
912 svn_pool_destroy(iterpool
);
918 svn_wc__diff_local_only_file(svn_wc__db_t
*db
,
919 const char *local_abspath
,
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
,
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
,
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
,
970 scratch_pool
, scratch_pool
));
974 pristine_props
= apr_hash_make(scratch_pool
);
976 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props
,
978 scratch_pool
, scratch_pool
));
980 if (original_repos_relpath
)
982 copyfrom_src
= svn_diff__source_create(original_revision
, scratch_pool
);
983 copyfrom_src
->repos_relpath
= original_repos_relpath
;
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
);
994 SVN_ERR(svn_wc__internal_file_modified_p(&file_mod
, db
, local_abspath
,
995 FALSE
, scratch_pool
));
998 right_src
= svn_diff__source_create(revision
, scratch_pool
);
1000 right_src
= svn_diff__source_create(SVN_INVALID_REVNUM
, scratch_pool
);
1003 SVN_ERR(processor
->file_opened(&file_baton
, &skip
,
1005 NULL
/* left_source */,
1008 processor_parent_baton
,
1010 scratch_pool
, scratch_pool
));
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
));
1019 right_props
= svn_prop_hash_dup(pristine_props
, scratch_pool
);
1022 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file
, db
, local_abspath
,
1023 checksum
, scratch_pool
, scratch_pool
));
1025 pristine_file
= NULL
;
1029 translated_file
= pristine_file
; /* No translation needed */
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
,
1055 return SVN_NO_ERROR
;
1059 svn_wc__diff_local_only_dir(svn_wc__db_t
*db
,
1060 const char *local_abspath
,
1061 const char *relpath
,
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
,
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
;
1081 apr_pool_t
*iterpool
;
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
,
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
,
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
,
1120 scratch_pool
, scratch_pool
));
1123 else if (!had_props
)
1124 pristine_props
= apr_hash_make(scratch_pool
);
1126 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props
,
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
,
1138 processor_parent_baton
,
1140 scratch_pool
, iterpool
));
1142 if ((depth
> svn_depth_empty
|| depth
== svn_depth_unknown
)
1145 svn_depth_t depth_below_here
= depth
;
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
,
1154 FALSE
/* base_tree_only */,
1155 scratch_pool
, iterpool
));
1158 children
= svn_sort__hash(nodes
, svn_sort_compare_items_lexically
,
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
);
1172 SVN_ERR(cancel_func(cancel_baton
));
1174 child_abspath
= svn_dirent_join(local_abspath
, name
, iterpool
);
1176 if (NOT_PRESENT(info
->status
))
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
)
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
;
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
,
1205 iterpool
, iterpool
));
1206 SVN_ERR_ASSERT(moved_from_abspath
!= NULL
);
1208 moved_from_relpath
= svn_dirent_skip_ancestor(
1210 moved_from_abspath
);
1213 moved_from_relpath
= NULL
;
1218 case svn_node_symlink
:
1219 SVN_ERR(svn_wc__diff_local_only_file(db
, child_abspath
,
1224 cancel_func
, cancel_baton
,
1229 if (depth
> svn_depth_files
|| depth
== svn_depth_unknown
)
1231 SVN_ERR(svn_wc__diff_local_only_dir(db
, child_abspath
,
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
));
1257 right_props
= svn_prop_hash_dup(pristine_props
, scratch_pool
);
1259 SVN_ERR(processor
->dir_added(relpath
,
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
,
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
:
1306 return SVN_NO_ERROR
; /* Local and remote */
1307 svn_hash_sets(pb
->deletes
, name
, NULL
);
1310 case svn_wc__db_status_deleted
:
1311 if (!(eb
->diff_pristine
&& repos_delete
))
1312 return SVN_NO_ERROR
;
1315 case svn_wc__db_status_added
:
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
,
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(
1334 moved_from_abspath
);
1337 moved_from_relpath
= NULL
;
1339 if (info
->kind
== svn_node_dir
)
1343 if (pb
->depth
== svn_depth_infinity
|| pb
->depth
== svn_depth_unknown
)
1346 depth
= svn_depth_empty
;
1348 SVN_ERR(svn_wc__diff_local_only_dir(
1351 svn_relpath_join(pb
->relpath
, name
, scratch_pool
),
1352 repos_delete
? svn_depth_infinity
: depth
,
1354 eb
->processor
, pb
->pdb
,
1356 eb
->cancel_func
, eb
->cancel_baton
,
1360 SVN_ERR(svn_wc__diff_local_only_file(
1363 svn_relpath_join(pb
->relpath
, name
, scratch_pool
),
1365 eb
->processor
, pb
->pdb
,
1367 eb
->cancel_func
, eb
->cancel_baton
,
1370 return SVN_NO_ERROR
;
1373 /* Reports a file LOCAL_ABSPATH in BASE as deleted */
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
;
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
)
1395 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1396 &checksum
, NULL
, NULL
, NULL
, &props
, NULL
,
1398 scratch_pool
, scratch_pool
));
1400 SVN_ERR_ASSERT(status
== svn_wc__db_status_normal
1401 && kind
== svn_node_file
1404 left_src
= svn_diff__source_create(revision
, scratch_pool
);
1406 SVN_ERR(processor
->file_opened(&file_baton
, &skip
,
1409 NULL
/* right_src */,
1410 NULL
/* copyfrom_source */,
1411 processor_parent_baton
,
1413 scratch_pool
, scratch_pool
));
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
,
1430 return SVN_NO_ERROR
;
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
,
1439 const svn_diff_tree_processor_t
*processor
,
1440 void *processor_parent_baton
,
1441 svn_cancel_func_t cancel_func
,
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
,
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
,
1463 NULL
/* right_src */,
1464 NULL
/* copyfrom_src */,
1465 processor_parent_baton
,
1467 scratch_pool
, scratch_pool
));
1469 if (!skip_children
&& (depth
== svn_depth_unknown
|| depth
> svn_depth_empty
))
1472 apr_pool_t
*iterpool
= svn_pool_create(scratch_pool
);
1473 apr_array_header_t
*children
;
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
,
1482 for (i
= 0; i
< children
->nelts
; i
++)
1484 svn_sort__item_t
*item
= &APR_ARRAY_IDX(children
, i
,
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
)
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
);
1505 case svn_node_symlink
:
1506 SVN_ERR(svn_wc__diff_base_only_file(db
, child_abspath
,
1509 processor
, dir_baton
,
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
,
1524 processor
, dir_baton
,
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
,
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
,
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
,
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
);
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
,
1590 NULL
/* copyfrom_source */,
1591 NULL
/* parent_baton */,
1593 db
->pool
, db
->pool
));
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
,
1608 struct dir_baton_t
*pb
= parent_baton
;
1609 const char *name
= svn_dirent_basename(path
, pb
->pool
);
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
,
1622 const char *copyfrom_path
,
1623 svn_revnum_t copyfrom_revision
,
1624 apr_pool_t
*dir_pool
,
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
,
1637 if (pb
->repos_only
|| !eb
->ignore_ancestry
)
1638 db
->repos_only
= TRUE
;
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
,
1670 NULL
/* copyfrom src */,
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
,
1682 svn_revnum_t base_revision
,
1683 apr_pool_t
*dir_pool
,
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
);
1698 db
->repos_only
= TRUE
;
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
);
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
), "");
1725 case svn_wc__db_status_added
:
1726 if (eb
->ignore_ancestry
)
1727 db
->ignoring_ancestry
= TRUE
;
1729 db
->repos_only
= TRUE
;
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
,
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
,
1769 NULL
/* copyfrom src */,
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
,
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
;
1797 children
= svn_sort__hash(db
->deletes
, svn_sort_compare_items_lexically
,
1800 for (i
= 0; i
< children
->nelts
; i
++)
1802 svn_sort__item_t
*item
= &APR_ARRAY_IDX(children
, i
,
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
,
1829 /* Report the property changes on the directory itself, if necessary. */
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
);
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
,
1856 SVN_ERR(eb
->processor
->dir_deleted(db
->relpath
,
1862 reported_closed
= TRUE
;
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
,
1873 eb
->db
, db
->local_abspath
,
1874 scratch_pool
, scratch_pool
));
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
,
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
1887 if (prop_changes
->nelts
)
1889 SVN_ERR(eb
->processor
->dir_changed(db
->relpath
,
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
,
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
,
1925 const char *copyfrom_path
,
1926 svn_revnum_t copyfrom_revision
,
1927 apr_pool_t
*file_pool
,
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
);
1937 if (pb
->skip_children
)
1940 return SVN_NO_ERROR
;
1942 else if (pb
->repos_only
|| !eb
->ignore_ancestry
)
1943 fb
->repos_only
= TRUE
;
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
,
1974 NULL
/* copyfrom src */,
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
,
1986 svn_revnum_t base_revision
,
1987 apr_pool_t
*file_pool
,
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
);
1997 if (pb
->skip_children
)
1999 else if (pb
->repos_only
)
2000 fb
->repos_only
= TRUE
;
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
:
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
), "");
2023 case svn_wc__db_status_added
:
2024 if (eb
->ignore_ancestry
)
2025 fb
->ignoring_ancestry
= TRUE
;
2027 fb
->repos_only
= TRUE
;
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
,
2054 NULL
/* copyfrom src */,
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
,
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
;
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
,
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
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(
2109 _("Checksum mismatch for '%s'"),
2110 svn_dirent_local_style(fb
->local_abspath
,
2114 SVN_ERR(svn_wc__db_pristine_read(&source
, NULL
,
2115 eb
->db
, fb
->local_abspath
,
2119 else if (fb
->base_checksum
)
2121 SVN_ERR(svn_wc__db_pristine_read(&source
, NULL
,
2122 eb
->db
, fb
->local_abspath
,
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
,
2136 fb
->local_abspath
/* error_info */,
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
,
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
;
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
,
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
,
2188 scratch_pool
, scratch_pool
));
2190 if (!svn_checksum_match(expected_checksum
, result_checksum
))
2191 return svn_checksum_mismatch_err(
2195 _("Checksum mismatch for '%s'"),
2196 svn_dirent_local_style(fb
->local_abspath
,
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
;
2207 prop_base
= apr_hash_make(scratch_pool
);
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
;
2217 assert(fb
->base_checksum
);
2218 SVN_ERR(svn_wc__db_pristine_get_path(&repos_file
,
2219 eb
->db
, eb
->anchor_abspath
,
2221 scratch_pool
, scratch_pool
));
2227 SVN_ERR(eb
->processor
->file_deleted(fb
->relpath
,
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
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
,
2251 eb
->db
, fb
->local_abspath
,
2252 scratch_pool
, scratch_pool
));
2254 SVN_ERR(svn_wc__db_pristine_get_path(&localfile
,
2255 eb
->db
, eb
->anchor_abspath
,
2257 scratch_pool
, scratch_pool
));
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
,
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
,
2282 repos_file
/* left file */,
2283 localfile
/* right file */,
2284 repos_props
/* left_props */,
2285 local_props
/* right props */,
2286 TRUE
/* ### file_modified */,
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
,
2306 const svn_string_t
*value
,
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
,
2331 const svn_string_t
*value
,
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
,
2357 struct edit_baton_t
*eb
= edit_baton
;
2359 if (!eb
->root_opened
)
2361 SVN_ERR(walk_local_nodes_diff(eb
,
2365 NULL
/* compared */,
2366 NULL
/* No parent_baton */,
2370 return SVN_NO_ERROR
;
2373 /* Public Interface */
2376 /* Create a diff editor and baton. */
2378 svn_wc__get_diff_editor(const svn_delta_editor_t
**editor
,
2380 svn_wc_context_t
*wc_ctx
,
2381 const char *anchor_abspath
,
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
,
2392 apr_pool_t
*result_pool
,
2393 apr_pool_t
*scratch_pool
)
2395 struct edit_baton_t
*eb
;
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
,
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
,
2419 anchor_abspath
, target
,
2421 depth
, ignore_ancestry
,
2422 use_text_base
, reverse_order
,
2423 cancel_func
, cancel_baton
,
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
;
2445 if (!server_performs_filtering
2446 && depth
== svn_depth_unknown
)
2447 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor
,
2456 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func
,
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
)
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
,
2539 ? right_source
->revision
2541 ? left_source
->revision
2542 : SVN_INVALID_REVNUM
),
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
,
2555 right_source
->revision
,
2557 ? copyfrom_source
->repos_relpath
2560 ? copyfrom_source
->revision
2561 : SVN_INVALID_REVNUM
,
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
,
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
,
2597 SVN_ERR(wb
->callbacks
->dir_props_changed(&prop_state
,
2600 TRUE
/* dir_was_added */,
2601 prop_changes
, pristine_props
,
2606 SVN_ERR(wb
->callbacks
->dir_closed(&state
, &prop_state
,
2609 TRUE
/* dir_was_added */,
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
,
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
,
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
,
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
,
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
,
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
,
2678 FALSE
/* dir_was_added */,
2684 /* And call dir_closed, etc */
2685 SVN_ERR(wrap_dir_closed(relpath
, left_source
, right_source
,
2686 dir_baton
, processor
,
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
,
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
,
2710 ? right_source
->revision
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
,
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
,
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
,
2757 right_source
->revision
,
2759 ? svn_prop_get_value(copyfrom_props
,
2763 ? svn_prop_get_value(right_props
,
2767 ? copyfrom_source
->repos_relpath
2770 ? copyfrom_source
->revision
2771 : SVN_INVALID_REVNUM
,
2772 prop_changes
, copyfrom_props
,
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
,
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
,
2795 left_file
, wb
->empty_file
,
2797 ? svn_prop_get_value(left_props
,
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
,
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
,
2833 file_modified
? left_file
: NULL
,
2834 file_modified
? right_file
: NULL
,
2835 left_source
->revision
,
2836 right_source
->revision
,
2838 ? svn_prop_get_value(left_props
,
2842 ? svn_prop_get_value(right_props
,
2849 return SVN_NO_ERROR
;
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
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
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
,
2933 left_source
, right_source
,
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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
))
3045 return SVN_NO_ERROR
;
3048 SVN_ERR(fb
->processor
->file_opened(new_file_baton
,
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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
,
3158 return SVN_NO_ERROR
;
3161 static svn_error_t
*
3162 filter_node_absent(const char *relpath
,
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
,
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
)
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
;