1 /*
2 * diff_editor.c -- The diff editor for comparing the working copy against the
3 * repository.
4 *
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
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
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 * ====================================================================
23 */
24
25 /*
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.
34 *
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.
39 *
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.
43 *
44 * ### TODO: Make sure that we properly support and report multi layered
45 * operations instead of only simple file replacements.
46 *
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.
50 *
51 * ### TODO: Do we need to support copyfrom?
52 *
53 */
54
55 #include <apr_hash.h>
56 #include <apr_md5.h>
57
58 #include <assert.h>
59
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"
66
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"
72
73 #include "wc.h"
74 #include "props.h"
75 #include "adm_files.h"
76 #include "translate.h"
77 #include "diff.h"
78
79 #include "svn_private_config.h"
80
81 /*-------------------------------------------------------------------------*/
82
83
84 /* Overall crawler editor baton.
85 */
86 struct edit_baton_t
87 {
88 /* A wc db. */
89 svn_wc__db_t *db;
90
91 /* A diff tree processor, receiving the result of the diff. */
92 const svn_diff_tree_processor_t *processor;
93
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;
98
99 /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */
100 const char *target;
101 const char *anchor_abspath;
102
103 /* Target revision */
104 svn_revnum_t revnum;
105
106 /* Was the root opened? */
107 svn_boolean_t root_opened;
108
109 /* How does this diff descend as seen from target? */
110 svn_depth_t depth;
111
112 /* Should this diff ignore node ancestry? */
113 svn_boolean_t ignore_ancestry;
114
115 /* Possibly diff repos against text-bases instead of working files. */
116 svn_boolean_t diff_pristine;
117
118 /* Cancel function/baton */
119 svn_cancel_func_t cancel_func;
120 void *cancel_baton;
121
122 apr_pool_t *pool;
123 };
124
125 /* Directory level baton.
126 */
127 struct dir_baton_t
128 {
129 /* Reference to parent directory baton (or NULL for the root) */
130 struct dir_baton_t *parent_baton;
131
132 /* The depth at which this directory should be diffed. */
133 svn_depth_t depth;
134
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;
140
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;
147
148 /* Processor state */
149 void *pdb;
150 svn_boolean_t skip;
151 svn_boolean_t skip_children;
152
153 svn_diff_source_t *left_src;
154 svn_diff_source_t *right_src;
155
156 apr_hash_t *local_info;
157
158 /* A hash containing the basenames of the nodes reported deleted by the
159 repository (or NULL for no values). */
160 apr_hash_t *deletes;
161
162 /* Identifies those directory elements that get compared while running
163 the crawler. These elements should not be compared again when
164 recursively looking for local modifications.
165
166 This hash maps the basename of the node to an unimportant value.
167
168 If the directory's properties have been compared, an item with hash
169 key of "" will be present in the hash. */
170 apr_hash_t *compared;
171
172 /* The list of incoming BASE->repos propchanges. */
173 apr_array_header_t *propchanges;
174
175 /* Has a change on regular properties */
176 svn_boolean_t has_propchange;
177
178 /* The overall crawler editor baton. */
179 struct edit_baton_t *eb;
180
181 apr_pool_t *pool;
182 int users;
183 };
184
185 /* File level baton.
186 */
187 struct file_baton_t
188 {
189 struct dir_baton_t *parent_baton;
190
191 /* The name and path of this file as if they would be/are in the
192 parent directory, diff session and local working copy. */
193 const char *name;
194 const char *relpath;
195 const char *local_abspath;
196
197 /* Processor state */
198 void *pfb;
199 svn_boolean_t skip;
200
201 /* TRUE if the file is added by the editor drive. */
202 svn_boolean_t added;
203 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
204 svn_boolean_t repos_only;
205 /* TRUE if the node is to be compared with an unrelated node*/
206 svn_boolean_t ignoring_ancestry;
207
208 const svn_diff_source_t *left_src;
209 const svn_diff_source_t *right_src;
210
211 /* The list of incoming BASE->repos propchanges. */
212 apr_array_header_t *propchanges;
213
214 /* Has a change on regular properties */
215 svn_boolean_t has_propchange;
216
217 /* The current BASE checksum and props */
218 const svn_checksum_t *base_checksum;
219 apr_hash_t *base_props;
220
221 /* The resulting from apply_textdelta */
222 const char *temp_file_path;
223 unsigned char result_digest[APR_MD5_DIGESTSIZE];
224
225 /* The overall crawler editor baton. */
226 struct edit_baton_t *eb;
227
228 apr_pool_t *pool;
229 };
230
231 /* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths
232 * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON
233 * define the callbacks to compare files. DEPTH defines if and how to
234 * descend into subdirectories; see public doc string for exactly how.
235 * IGNORE_ANCESTRY defines whether to utilize node ancestry when
236 * calculating diffs. USE_TEXT_BASE defines whether to compare
237 * against working files or text-bases. REVERSE_ORDER defines which
238 * direction to perform the diff.
239 */
240 static svn_error_t *
make_edit_baton(struct edit_baton_t ** edit_baton,svn_wc__db_t * db,const char * anchor_abspath,const char * target,const svn_diff_tree_processor_t * diff_processor,svn_depth_t depth,svn_boolean_t ignore_ancestry,svn_boolean_t use_text_base,svn_boolean_t reverse_order,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)241 make_edit_baton(struct edit_baton_t **edit_baton,
242 svn_wc__db_t *db,
243 const char *anchor_abspath,
244 const char *target,
245 const svn_diff_tree_processor_t *diff_processor,
246 svn_depth_t depth,
247 svn_boolean_t ignore_ancestry,
248 svn_boolean_t use_text_base,
249 svn_boolean_t reverse_order,
250 svn_cancel_func_t cancel_func,
251 void *cancel_baton,
252 apr_pool_t *pool)
253 {
254 struct edit_baton_t *eb;
255
256 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
257
258 eb = apr_pcalloc(pool, sizeof(*eb));
259 eb->db = db;
260 eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
261 eb->target = apr_pstrdup(pool, target);
262 eb->processor = diff_processor;
263 eb->depth = depth;
264 eb->ignore_ancestry = ignore_ancestry;
265 eb->local_before_remote = reverse_order;
266 eb->diff_pristine = use_text_base;
267 eb->cancel_func = cancel_func;
268 eb->cancel_baton = cancel_baton;
269 eb->pool = pool;
270
271 *edit_baton = eb;
272 return SVN_NO_ERROR;
273 }
274
275 /* Create a new directory baton. PATH is the directory path,
276 * including anchor_path. ADDED is set if this directory is being
277 * added rather than replaced. PARENT_BATON is the baton of the
278 * parent directory, it will be null if this is the root of the
279 * comparison hierarchy. The directory and its parent may or may not
280 * exist in the working copy. EDIT_BATON is the overall crawler
281 * editor baton.
282 */
283 static struct dir_baton_t *
make_dir_baton(const char * path,struct dir_baton_t * parent_baton,struct edit_baton_t * eb,svn_boolean_t added,svn_depth_t depth,apr_pool_t * result_pool)284 make_dir_baton(const char *path,
285 struct dir_baton_t *parent_baton,
286 struct edit_baton_t *eb,
287 svn_boolean_t added,
288 svn_depth_t depth,
289 apr_pool_t *result_pool)
290 {
291 apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
292 : eb->pool);
293 struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
294
295 db->parent_baton = parent_baton;
296
297 /* Allocate 1 string for using as 3 strings */
298 db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
299 db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
300 db->name = svn_dirent_basename(db->relpath, NULL);
301
302 db->eb = eb;
303 db->added = added;
304 db->depth = depth;
305 db->pool = dir_pool;
306 db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
307 db->compared = apr_hash_make(dir_pool);
308
309 if (parent_baton != NULL)
310 {
311 parent_baton->users++;
312 }
313
314 db->users = 1;
315
316 return db;
317 }
318
319 /* Create a new file baton. PATH is the file path, including
320 * anchor_path. ADDED is set if this file is being added rather than
321 * replaced. PARENT_BATON is the baton of the parent directory.
322 * The directory and its parent may or may not exist in the working copy.
323 */
324 static struct file_baton_t *
make_file_baton(const char * path,svn_boolean_t added,struct dir_baton_t * parent_baton,apr_pool_t * result_pool)325 make_file_baton(const char *path,
326 svn_boolean_t added,
327 struct dir_baton_t *parent_baton,
328 apr_pool_t *result_pool)
329 {
330 apr_pool_t *file_pool = svn_pool_create(result_pool);
331 struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
332 struct edit_baton_t *eb = parent_baton->eb;
333
334 fb->eb = eb;
335 fb->parent_baton = parent_baton;
336 fb->parent_baton->users++;
337
338 /* Allocate 1 string for using as 3 strings */
339 fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
340 fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
341 fb->name = svn_dirent_basename(fb->relpath, NULL);
342
343 fb->added = added;
344 fb->pool = file_pool;
345 fb->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
346
347 return fb;
348 }
349
350 /* Destroy DB when there are no more registered users */
351 static svn_error_t *
maybe_done(struct dir_baton_t * db)352 maybe_done(struct dir_baton_t *db)
353 {
354 db->users--;
355
356 if (!db->users)
357 {
358 struct dir_baton_t *pb = db->parent_baton;
359
360 svn_pool_clear(db->pool);
361
362 if (pb != NULL)
363 SVN_ERR(maybe_done(pb));
364 }
365
366 return SVN_NO_ERROR;
367 }
368
369 /* Standard check to see if a node is represented in the local working copy */
370 #define NOT_PRESENT(status) \
371 ((status) == svn_wc__db_status_not_present \
372 || (status) == svn_wc__db_status_excluded \
373 || (status) == svn_wc__db_status_server_excluded)
374
375 svn_error_t *
svn_wc__diff_base_working_diff(svn_wc__db_t * db,const char * local_abspath,const char * relpath,svn_revnum_t revision,const svn_diff_tree_processor_t * processor,void * processor_dir_baton,svn_boolean_t diff_pristine,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)376 svn_wc__diff_base_working_diff(svn_wc__db_t *db,
377 const char *local_abspath,
378 const char *relpath,
379 svn_revnum_t revision,
380 const svn_diff_tree_processor_t *processor,
381 void *processor_dir_baton,
382 svn_boolean_t diff_pristine,
383 svn_cancel_func_t cancel_func,
384 void *cancel_baton,
385 apr_pool_t *scratch_pool)
386 {
387 void *file_baton = NULL;
388 svn_boolean_t skip = FALSE;
389 svn_wc__db_status_t status;
390 svn_revnum_t db_revision;
391 svn_boolean_t had_props;
392 svn_boolean_t props_mod;
393 svn_boolean_t files_same = FALSE;
394 svn_wc__db_status_t base_status;
395 const svn_checksum_t *working_checksum;
396 const svn_checksum_t *checksum;
397 svn_filesize_t recorded_size;
398 apr_time_t recorded_time;
399 const char *pristine_file;
400 const char *local_file;
401 svn_diff_source_t *left_src;
402 svn_diff_source_t *right_src;
403 apr_hash_t *base_props;
404 apr_hash_t *local_props;
405 apr_array_header_t *prop_changes;
406
407 SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
408 NULL, NULL, NULL, NULL, &working_checksum, NULL,
409 NULL, NULL, NULL, NULL, NULL, &recorded_size,
410 &recorded_time, NULL, NULL, NULL,
411 &had_props, &props_mod, NULL, NULL, NULL,
412 db, local_abspath, scratch_pool, scratch_pool));
413 checksum = working_checksum;
414
415 assert(status == svn_wc__db_status_normal
416 || status == svn_wc__db_status_added
417 || (status == svn_wc__db_status_deleted && diff_pristine));
418
419 if (status != svn_wc__db_status_normal)
420 {
421 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
422 NULL, NULL, NULL, NULL, NULL, NULL,
423 NULL, &checksum, NULL, NULL, &had_props,
424 NULL, NULL,
425 db, local_abspath,
426 scratch_pool, scratch_pool));
427 recorded_size = SVN_INVALID_FILESIZE;
428 recorded_time = 0;
429 props_mod = TRUE; /* Requires compare */
430 }
431 else if (diff_pristine)
432 files_same = TRUE;
433 else
434 {
435 const svn_io_dirent2_t *dirent;
436
437 /* Verify truename to mimic status for iota/IOTA difference on Windows */
438 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
439 TRUE /* verify truename */,
440 TRUE /* ingore_enoent */,
441 scratch_pool, scratch_pool));
442
443 /* If a file does not exist on disk (missing/obstructed) then we
444 can't provide a text diff */
445 if (dirent->kind != svn_node_file
446 || (dirent->kind == svn_node_file
447 && dirent->filesize == recorded_size
448 && dirent->mtime == recorded_time))
449 {
450 files_same = TRUE;
451 }
452 }
453
454 if (files_same && !props_mod)
455 return SVN_NO_ERROR; /* Cheap exit */
456
457 assert(checksum);
458
459 if (!SVN_IS_VALID_REVNUM(revision))
460 revision = db_revision;
461
462 left_src = svn_diff__source_create(revision, scratch_pool);
463 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
464
465 SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
466 left_src,
467 right_src,
468 NULL /* copyfrom_src */,
469 processor_dir_baton,
470 processor,
471 scratch_pool, scratch_pool));
472
473 if (skip)
474 return SVN_NO_ERROR;
475
476 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
477 db, local_abspath, checksum,
478 scratch_pool, scratch_pool));
479
480 if (diff_pristine)
481 SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
482 db, local_abspath,
483 working_checksum,
484 scratch_pool, scratch_pool));
485 else if (! (had_props || props_mod))
486 local_file = local_abspath;
487 else if (files_same)
488 local_file = pristine_file;
489 else
490 SVN_ERR(svn_wc__internal_translated_file(
491 &local_file, local_abspath,
492 db, local_abspath,
493 SVN_WC_TRANSLATE_TO_NF
494 | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
495 cancel_func, cancel_baton,
496 scratch_pool, scratch_pool));
497
498 if (! files_same)
499 SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
500 pristine_file, scratch_pool));
501
502 if (had_props)
503 SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
504 scratch_pool, scratch_pool));
505 else
506 base_props = apr_hash_make(scratch_pool);
507
508 if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
509 local_props = base_props;
510 else if (diff_pristine)
511 SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
512 scratch_pool, scratch_pool));
513 else
514 SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
515 scratch_pool, scratch_pool));
516
517 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
518
519 if (prop_changes->nelts || !files_same)
520 {
521 SVN_ERR(processor->file_changed(relpath,
522 left_src,
523 right_src,
524 pristine_file,
525 local_file,
526 base_props,
527 local_props,
528 ! files_same,
529 prop_changes,
530 file_baton,
531 processor,
532 scratch_pool));
533 }
534 else
535 {
536 SVN_ERR(processor->file_closed(relpath,
537 left_src,
538 right_src,
539 file_baton,
540 processor,
541 scratch_pool));
542 }
543
544 return SVN_NO_ERROR;
545 }
546
547 static svn_error_t *
ensure_local_info(struct dir_baton_t * db,apr_pool_t * scratch_pool)548 ensure_local_info(struct dir_baton_t *db,
549 apr_pool_t *scratch_pool)
550 {
551 apr_hash_t *conflicts;
552
553 if (db->local_info)
554 return SVN_NO_ERROR;
555
556 SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
557 db->eb->db, db->local_abspath,
558 FALSE /* base_tree_only */,
559 db->pool, scratch_pool));
560
561 return SVN_NO_ERROR;
562 }
563
564 /* Called when the directory is closed to compare any elements that have
565 * not yet been compared. This identifies local, working copy only
566 * changes. At this stage we are dealing with files/directories that do
567 * exist in the working copy.
568 *
569 * DIR_BATON is the baton for the directory.
570 */
571 static svn_error_t *
walk_local_nodes_diff(struct edit_baton_t * eb,const char * local_abspath,const char * path,svn_depth_t depth,apr_hash_t * compared,void * parent_baton,apr_pool_t * scratch_pool)572 walk_local_nodes_diff(struct edit_baton_t *eb,
573 const char *local_abspath,
574 const char *path,
575 svn_depth_t depth,
576 apr_hash_t *compared,
577 void *parent_baton,
578 apr_pool_t *scratch_pool)
579 {
580 svn_wc__db_t *db = eb->db;
581 svn_boolean_t in_anchor_not_target;
582 apr_pool_t *iterpool;
583 void *dir_baton = NULL;
584 svn_boolean_t skip = FALSE;
585 svn_boolean_t skip_children = FALSE;
586 svn_revnum_t revision;
587 svn_boolean_t props_mod;
588 svn_diff_source_t *left_src;
589 svn_diff_source_t *right_src;
590
591 /* Everything we do below is useless if we are comparing to BASE. */
592 if (eb->diff_pristine)
593 return SVN_NO_ERROR;
594
595 /* Determine if this is the anchor directory if the anchor is different
596 to the target. When the target is a file, the anchor is the parent
597 directory and if this is that directory the non-target entries must be
598 skipped. */
599 in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
600
601 iterpool = svn_pool_create(scratch_pool);
602
603 SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
604 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
605 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
606 NULL, &props_mod, NULL, NULL, NULL,
607 db, local_abspath, scratch_pool, scratch_pool));
608
609 left_src = svn_diff__source_create(revision, scratch_pool);
610 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
611
612 if (compared)
613 {
614 dir_baton = parent_baton;
615 skip = TRUE;
616 }
617 else if (!in_anchor_not_target)
618 SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
619 path,
620 left_src,
621 right_src,
622 NULL /* copyfrom_src */,
623 parent_baton,
624 eb->processor,
625 scratch_pool, scratch_pool));
626
627
628 if (!skip_children && depth != svn_depth_empty)
629 {
630 apr_hash_t *nodes;
631 apr_hash_t *conflicts;
632 apr_array_header_t *children;
633 svn_depth_t depth_below_here = depth;
634 svn_boolean_t diff_files;
635 svn_boolean_t diff_dirs;
636 int i;
637
638 if (depth_below_here == svn_depth_immediates)
639 depth_below_here = svn_depth_empty;
640
641 diff_files = (depth == svn_depth_unknown
642 || depth >= svn_depth_files);
643 diff_dirs = (depth == svn_depth_unknown
644 || depth >= svn_depth_immediates);
645
646 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
647 db, local_abspath,
648 FALSE /* base_tree_only */,
649 scratch_pool, iterpool));
650
651 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
652 scratch_pool);
653
654 for (i = 0; i < children->nelts; i++)
655 {
656 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
657 svn_sort__item_t);
658 const char *name = item->key;
659 struct svn_wc__db_info_t *info = item->value;
660
661 const char *child_abspath;
662 const char *child_relpath;
663 svn_boolean_t repos_only;
664 svn_boolean_t local_only;
665 svn_node_kind_t base_kind;
666
667 if (eb->cancel_func)
668 SVN_ERR(eb->cancel_func(eb->cancel_baton));
669
670 /* In the anchor directory, if the anchor is not the target then all
671 entries other than the target should not be diff'd. Running diff
672 on one file in a directory should not diff other files in that
673 directory. */
674 if (in_anchor_not_target && strcmp(eb->target, name))
675 continue;
676
677 if (compared && svn_hash_gets(compared, name))
678 continue;
679
680 if (NOT_PRESENT(info->status))
681 continue;
682
683 assert(info->status == svn_wc__db_status_normal
684 || info->status == svn_wc__db_status_added
685 || info->status == svn_wc__db_status_deleted);
686
687 svn_pool_clear(iterpool);
688 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
689 child_relpath = svn_relpath_join(path, name, iterpool);
690
691 repos_only = FALSE;
692 local_only = FALSE;
693
694 if (!info->have_base)
695 {
696 local_only = TRUE; /* Only report additions */
697
698 if (info->status == svn_wc__db_status_deleted)
699 continue; /* Nothing added (deleted copy) */
700 }
701 else if (info->status == svn_wc__db_status_normal)
702 {
703 /* Simple diff */
704 base_kind = info->kind;
705 }
706 else if (info->status == svn_wc__db_status_deleted
707 && (!eb->diff_pristine || !info->have_more_work))
708 {
709 svn_wc__db_status_t base_status;
710 repos_only = TRUE;
711 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
712 NULL, NULL, NULL, NULL, NULL,
713 NULL, NULL, NULL, NULL, NULL,
714 NULL, NULL, NULL,
715 db, child_abspath,
716 iterpool, iterpool));
717
718 if (NOT_PRESENT(base_status))
719 continue;
720 }
721 else
722 {
723 /* working status is either added or deleted */
724 svn_wc__db_status_t base_status;
725
726 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
727 NULL, NULL, NULL, NULL, NULL,
728 NULL, NULL, NULL, NULL, NULL,
729 NULL, NULL, NULL,
730 db, child_abspath,
731 iterpool, iterpool));
732
733 if (NOT_PRESENT(base_status))
734 local_only = TRUE;
735 else if (base_kind != info->kind || !eb->ignore_ancestry)
736 {
737 repos_only = TRUE;
738 local_only = TRUE;
739 }
740 }
741
742 if (eb->local_before_remote && local_only)
743 {
744 if (info->kind == svn_node_file && diff_files)
745 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
746 child_relpath,
747 eb->processor, dir_baton,
748 eb->diff_pristine,
749 eb->cancel_func,
750 eb->cancel_baton,
751 iterpool));
752 else if (info->kind == svn_node_dir && diff_dirs)
753 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
754 child_relpath,
755 depth_below_here,
756 eb->processor, dir_baton,
757 eb->diff_pristine,
758 eb->cancel_func,
759 eb->cancel_baton,
760 iterpool));
761 }
762
763 if (repos_only)
764 {
765 /* Report repository form deleted */
766 if (base_kind == svn_node_file && diff_files)
767 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
768 child_relpath, eb->revnum,
769 eb->processor, dir_baton,
770 iterpool));
771 else if (base_kind == svn_node_dir && diff_dirs)
772 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
773 child_relpath, eb->revnum,
774 depth_below_here,
775 eb->processor, dir_baton,
776 eb->cancel_func,
777 eb->cancel_baton,
778 iterpool));
779 }
780 else if (!local_only) /* Not local only nor remote only */
781 {
782 /* Diff base against actual */
783 if (info->kind == svn_node_file && diff_files)
784 {
785 if (info->status != svn_wc__db_status_normal
786 || !eb->diff_pristine)
787 {
788 SVN_ERR(svn_wc__diff_base_working_diff(
789 db, child_abspath,
790 child_relpath,
791 eb->revnum,
792 eb->processor, dir_baton,
793 eb->diff_pristine,
794 eb->cancel_func,
795 eb->cancel_baton,
796 scratch_pool));
797 }
798 }
799 else if (info->kind == svn_node_dir && diff_dirs)
800 SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
801 child_relpath,
802 depth_below_here,
803 NULL /* compared */,
804 dir_baton,
805 scratch_pool));
806 }
807
808 if (!eb->local_before_remote && local_only)
809 {
810 if (info->kind == svn_node_file && diff_files)
811 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
812 child_relpath,
813 eb->processor, dir_baton,
814 eb->diff_pristine,
815 eb->cancel_func,
816 eb->cancel_baton,
817 iterpool));
818 else if (info->kind == svn_node_dir && diff_dirs)
819 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
820 child_relpath, depth_below_here,
821 eb->processor, dir_baton,
822 eb->diff_pristine,
823 eb->cancel_func,
824 eb->cancel_baton,
825 iterpool));
826 }
827 }
828 }
829
830 if (compared)
831 return SVN_NO_ERROR;
832
833 /* Check for local property mods on this directory, if we haven't
834 already reported them. */
835 if (! skip
836 && ! in_anchor_not_target
837 && props_mod)
838 {
839 apr_array_header_t *propchanges;
840 apr_hash_t *left_props;
841 apr_hash_t *right_props;
842
843 SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
844 db, local_abspath,
845 scratch_pool, scratch_pool));
846
847 right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
848
849 SVN_ERR(eb->processor->dir_changed(path,
850 left_src,
851 right_src,
852 left_props,
853 right_props,
854 propchanges,
855 dir_baton,
856 eb->processor,
857 scratch_pool));
858 }
859 else if (! skip)
860 SVN_ERR(eb->processor->dir_closed(path,
861 left_src,
862 right_src,
863 dir_baton,
864 eb->processor,
865 scratch_pool));
866
867 svn_pool_destroy(iterpool);
868
869 return SVN_NO_ERROR;
870 }
871
872 svn_error_t *
svn_wc__diff_local_only_file(svn_wc__db_t * db,const char * local_abspath,const char * relpath,const svn_diff_tree_processor_t * processor,void * processor_parent_baton,svn_boolean_t diff_pristine,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)873 svn_wc__diff_local_only_file(svn_wc__db_t *db,
874 const char *local_abspath,
875 const char *relpath,
876 const svn_diff_tree_processor_t *processor,
877 void *processor_parent_baton,
878 svn_boolean_t diff_pristine,
879 svn_cancel_func_t cancel_func,
880 void *cancel_baton,
881 apr_pool_t *scratch_pool)
882 {
883 svn_diff_source_t *right_src;
884 svn_diff_source_t *copyfrom_src = NULL;
885 svn_wc__db_status_t status;
886 svn_node_kind_t kind;
887 const svn_checksum_t *checksum;
888 const char *original_repos_relpath;
889 svn_revnum_t original_revision;
890 svn_boolean_t had_props;
891 svn_boolean_t props_mod;
892 apr_hash_t *pristine_props;
893 apr_hash_t *right_props = NULL;
894 const char *pristine_file;
895 const char *translated_file;
896 svn_revnum_t revision;
897 void *file_baton = NULL;
898 svn_boolean_t skip = FALSE;
899 svn_boolean_t file_mod = TRUE;
900
901 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
902 NULL, NULL, NULL, NULL, &checksum, NULL,
903 &original_repos_relpath, NULL, NULL,
904 &original_revision, NULL, NULL, NULL,
905 NULL, NULL, NULL, &had_props,
906 &props_mod, NULL, NULL, NULL,
907 db, local_abspath,
908 scratch_pool, scratch_pool));
909
910 assert(kind == svn_node_file
911 && (status == svn_wc__db_status_normal
912 || status == svn_wc__db_status_added
913 || (status == svn_wc__db_status_deleted && diff_pristine)));
914
915
916 if (status == svn_wc__db_status_deleted)
917 {
918 assert(diff_pristine);
919
920 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
921 NULL, &checksum, NULL, &had_props,
922 &pristine_props,
923 db, local_abspath,
924 scratch_pool, scratch_pool));
925 props_mod = FALSE;
926 }
927 else if (!had_props)
928 pristine_props = apr_hash_make(scratch_pool);
929 else
930 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
931 db, local_abspath,
932 scratch_pool, scratch_pool));
933
934 if (original_repos_relpath)
935 {
936 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
937 copyfrom_src->repos_relpath = original_repos_relpath;
938 }
939
940 if (props_mod || !SVN_IS_VALID_REVNUM(revision))
941 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
942 else
943 {
944 if (diff_pristine)
945 file_mod = FALSE;
946 else
947 SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
948 FALSE, scratch_pool));
949
950 if (!file_mod)
951 right_src = svn_diff__source_create(revision, scratch_pool);
952 else
953 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
954 }
955
956 SVN_ERR(processor->file_opened(&file_baton, &skip,
957 relpath,
958 NULL /* left_source */,
959 right_src,
960 copyfrom_src,
961 processor_parent_baton,
962 processor,
963 scratch_pool, scratch_pool));
964
965 if (skip)
966 return SVN_NO_ERROR;
967
968 if (props_mod && !diff_pristine)
969 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
970 scratch_pool, scratch_pool));
971 else
972 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
973
974 if (checksum)
975 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
976 checksum, scratch_pool, scratch_pool));
977 else
978 pristine_file = NULL;
979
980 if (diff_pristine)
981 {
982 translated_file = pristine_file; /* No translation needed */
983 }
984 else
985 {
986 SVN_ERR(svn_wc__internal_translated_file(
987 &translated_file, local_abspath, db, local_abspath,
988 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
989 cancel_func, cancel_baton,
990 scratch_pool, scratch_pool));
991 }
992
993 SVN_ERR(processor->file_added(relpath,
994 copyfrom_src,
995 right_src,
996 copyfrom_src
997 ? pristine_file
998 : NULL,
999 translated_file,
1000 copyfrom_src
1001 ? pristine_props
1002 : NULL,
1003 right_props,
1004 file_baton,
1005 processor,
1006 scratch_pool));
1007
1008 return SVN_NO_ERROR;
1009 }
1010
1011 svn_error_t *
svn_wc__diff_local_only_dir(svn_wc__db_t * db,const char * local_abspath,const char * relpath,svn_depth_t depth,const svn_diff_tree_processor_t * processor,void * processor_parent_baton,svn_boolean_t diff_pristine,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1012 svn_wc__diff_local_only_dir(svn_wc__db_t *db,
1013 const char *local_abspath,
1014 const char *relpath,
1015 svn_depth_t depth,
1016 const svn_diff_tree_processor_t *processor,
1017 void *processor_parent_baton,
1018 svn_boolean_t diff_pristine,
1019 svn_cancel_func_t cancel_func,
1020 void *cancel_baton,
1021 apr_pool_t *scratch_pool)
1022 {
1023 svn_wc__db_status_t status;
1024 svn_node_kind_t kind;
1025 svn_boolean_t had_props;
1026 svn_boolean_t props_mod;
1027 const char *original_repos_relpath;
1028 svn_revnum_t original_revision;
1029 svn_diff_source_t *copyfrom_src = NULL;
1030 apr_hash_t *pristine_props;
1031 const apr_array_header_t *children;
1032 int i;
1033 apr_pool_t *iterpool;
1034 void *pdb = NULL;
1035 svn_boolean_t skip = FALSE;
1036 svn_boolean_t skip_children = FALSE;
1037 svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
1038 scratch_pool);
1039
1040 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
1041 NULL, NULL, NULL, NULL, NULL, NULL,
1042 &original_repos_relpath, NULL, NULL,
1043 &original_revision, NULL, NULL, NULL,
1044 NULL, NULL, NULL, &had_props,
1045 &props_mod, NULL, NULL, NULL,
1046 db, local_abspath,
1047 scratch_pool, scratch_pool));
1048 if (original_repos_relpath)
1049 {
1050 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
1051 copyfrom_src->repos_relpath = original_repos_relpath;
1052 }
1053
1054 /* svn_wc__db_status_incomplete should never happen, as the result won't be
1055 stable or guaranteed related to what is in the repository for this
1056 revision, but without this it would be hard to diagnose that status... */
1057 assert(kind == svn_node_dir
1058 && (status == svn_wc__db_status_normal
1059 || status == svn_wc__db_status_incomplete
1060 || status == svn_wc__db_status_added
1061 || (status == svn_wc__db_status_deleted && diff_pristine)));
1062
1063 if (status == svn_wc__db_status_deleted)
1064 {
1065 assert(diff_pristine);
1066
1067 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1068 NULL, NULL, NULL, &had_props,
1069 &pristine_props,
1070 db, local_abspath,
1071 scratch_pool, scratch_pool));
1072 props_mod = FALSE;
1073 }
1074 else if (!had_props)
1075 pristine_props = apr_hash_make(scratch_pool);
1076 else
1077 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
1078 db, local_abspath,
1079 scratch_pool, scratch_pool));
1080
1081 /* Report the addition of the directory's contents. */
1082 iterpool = svn_pool_create(scratch_pool);
1083
1084 SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
1085 relpath,
1086 NULL,
1087 right_src,
1088 copyfrom_src,
1089 processor_parent_baton,
1090 processor,
1091 scratch_pool, iterpool));
1092
1093 if ((depth > svn_depth_empty || depth == svn_depth_unknown)
1094 && ! skip_children)
1095 {
1096 svn_depth_t depth_below_here = depth;
1097 apr_hash_t *nodes;
1098 apr_hash_t *conflicts;
1099
1100 if (depth_below_here == svn_depth_immediates)
1101 depth_below_here = svn_depth_empty;
1102
1103 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
1104 db, local_abspath,
1105 FALSE /* base_tree_only */,
1106 scratch_pool, iterpool));
1107
1108
1109 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1110 scratch_pool);
1111
1112 for (i = 0; i < children->nelts; i++)
1113 {
1114 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
1115 const char *name = item->key;
1116 struct svn_wc__db_info_t *info = item->value;
1117 const char *child_abspath;
1118 const char *child_relpath;
1119
1120 svn_pool_clear(iterpool);
1121
1122 if (cancel_func)
1123 SVN_ERR(cancel_func(cancel_baton));
1124
1125 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1126
1127 if (NOT_PRESENT(info->status))
1128 {
1129 continue;
1130 }
1131
1132 /* If comparing against WORKING, skip entries that are
1133 schedule-deleted - they don't really exist. */
1134 if (!diff_pristine && info->status == svn_wc__db_status_deleted)
1135 continue;
1136
1137 child_relpath = svn_relpath_join(relpath, name, iterpool);
1138
1139 switch (info->kind)
1140 {
1141 case svn_node_file:
1142 case svn_node_symlink:
1143 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
1144 child_relpath,
1145 processor, pdb,
1146 diff_pristine,
1147 cancel_func, cancel_baton,
1148 scratch_pool));
1149 break;
1150
1151 case svn_node_dir:
1152 if (depth > svn_depth_files || depth == svn_depth_unknown)
1153 {
1154 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
1155 child_relpath,
1156 depth_below_here,
1157 processor, pdb,
1158 diff_pristine,
1159 cancel_func,
1160 cancel_baton,
1161 iterpool));
1162 }
1163 break;
1164
1165 default:
1166 break;
1167 }
1168 }
1169 }
1170
1171 if (!skip)
1172 {
1173 apr_hash_t *right_props;
1174
1175 if (props_mod && !diff_pristine)
1176 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1177 scratch_pool, scratch_pool));
1178 else
1179 right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1180
1181 SVN_ERR(processor->dir_added(relpath,
1182 copyfrom_src,
1183 right_src,
1184 copyfrom_src
1185 ? pristine_props
1186 : NULL,
1187 right_props,
1188 pdb,
1189 processor,
1190 iterpool));
1191 }
1192 svn_pool_destroy(iterpool);
1193
1194 return SVN_NO_ERROR;
1195 }
1196
1197 /* Reports local changes. */
1198 static svn_error_t *
handle_local_only(struct dir_baton_t * pb,const char * name,apr_pool_t * scratch_pool)1199 handle_local_only(struct dir_baton_t *pb,
1200 const char *name,
1201 apr_pool_t *scratch_pool)
1202 {
1203 struct edit_baton_t *eb = pb->eb;
1204 const struct svn_wc__db_info_t *info;
1205 svn_boolean_t repos_delete = (pb->deletes
1206 && svn_hash_gets(pb->deletes, name));
1207
1208 assert(!strchr(name, '/'));
1209 assert(!pb->added || eb->ignore_ancestry);
1210
1211 if (pb->skip_children)
1212 return SVN_NO_ERROR;
1213
1214 SVN_ERR(ensure_local_info(pb, scratch_pool));
1215
1216 info = svn_hash_gets(pb->local_info, name);
1217
1218 if (info == NULL || NOT_PRESENT(info->status))
1219 return SVN_NO_ERROR;
1220
1221 switch (info->status)
1222 {
1223 case svn_wc__db_status_incomplete:
1224 return SVN_NO_ERROR; /* Not local only */
1225
1226 case svn_wc__db_status_normal:
1227 if (!repos_delete)
1228 return SVN_NO_ERROR; /* Local and remote */
1229 svn_hash_sets(pb->deletes, name, NULL);
1230 break;
1231
1232 case svn_wc__db_status_deleted:
1233 if (!(eb->diff_pristine && repos_delete))
1234 return SVN_NO_ERROR;
1235 break;
1236
1237 case svn_wc__db_status_added:
1238 default:
1239 break;
1240 }
1241
1242 if (info->kind == svn_node_dir)
1243 {
1244 svn_depth_t depth ;
1245
1246 if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
1247 depth = pb->depth;
1248 else
1249 depth = svn_depth_empty;
1250
1251 SVN_ERR(svn_wc__diff_local_only_dir(
1252 eb->db,
1253 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1254 svn_relpath_join(pb->relpath, name, scratch_pool),
1255 repos_delete ? svn_depth_infinity : depth,
1256 eb->processor, pb->pdb,
1257 eb->diff_pristine,
1258 eb->cancel_func, eb->cancel_baton,
1259 scratch_pool));
1260 }
1261 else
1262 SVN_ERR(svn_wc__diff_local_only_file(
1263 eb->db,
1264 svn_dirent_join(pb->local_abspath, name, scratch_pool),
1265 svn_relpath_join(pb->relpath, name, scratch_pool),
1266 eb->processor, pb->pdb,
1267 eb->diff_pristine,
1268 eb->cancel_func, eb->cancel_baton,
1269 scratch_pool));
1270
1271 return SVN_NO_ERROR;
1272 }
1273
1274 /* Reports a file LOCAL_ABSPATH in BASE as deleted */
1275 svn_error_t *
svn_wc__diff_base_only_file(svn_wc__db_t * db,const char * local_abspath,const char * relpath,svn_revnum_t revision,const svn_diff_tree_processor_t * processor,void * processor_parent_baton,apr_pool_t * scratch_pool)1276 svn_wc__diff_base_only_file(svn_wc__db_t *db,
1277 const char *local_abspath,
1278 const char *relpath,
1279 svn_revnum_t revision,
1280 const svn_diff_tree_processor_t *processor,
1281 void *processor_parent_baton,
1282 apr_pool_t *scratch_pool)
1283 {
1284 svn_wc__db_status_t status;
1285 svn_node_kind_t kind;
1286 const svn_checksum_t *checksum;
1287 apr_hash_t *props;
1288 void *file_baton = NULL;
1289 svn_boolean_t skip = FALSE;
1290 svn_diff_source_t *left_src;
1291 const char *pristine_file;
1292
1293 SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
1294 SVN_IS_VALID_REVNUM(revision)
1295 ? NULL : &revision,
1296 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1297 &checksum, NULL, NULL, NULL, &props, NULL,
1298 db, local_abspath,
1299 scratch_pool, scratch_pool));
1300
1301 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
1302 && kind == svn_node_file
1303 && checksum);
1304
1305 left_src = svn_diff__source_create(revision, scratch_pool);
1306
1307 SVN_ERR(processor->file_opened(&file_baton, &skip,
1308 relpath,
1309 left_src,
1310 NULL /* right_src */,
1311 NULL /* copyfrom_source */,
1312 processor_parent_baton,
1313 processor,
1314 scratch_pool, scratch_pool));
1315
1316 if (skip)
1317 return SVN_NO_ERROR;
1318
1319 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
1320 db, local_abspath, checksum,
1321 scratch_pool, scratch_pool));
1322
1323 SVN_ERR(processor->file_deleted(relpath,
1324 left_src,
1325 pristine_file,
1326 props,
1327 file_baton,
1328 processor,
1329 scratch_pool));
1330
1331 return SVN_NO_ERROR;
1332 }
1333
1334 svn_error_t *
svn_wc__diff_base_only_dir(svn_wc__db_t * db,const char * local_abspath,const char * relpath,svn_revnum_t revision,svn_depth_t depth,const svn_diff_tree_processor_t * processor,void * processor_parent_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1335 svn_wc__diff_base_only_dir(svn_wc__db_t *db,
1336 const char *local_abspath,
1337 const char *relpath,
1338 svn_revnum_t revision,
1339 svn_depth_t depth,
1340 const svn_diff_tree_processor_t *processor,
1341 void *processor_parent_baton,
1342 svn_cancel_func_t cancel_func,
1343 void *cancel_baton,
1344 apr_pool_t *scratch_pool)
1345 {
1346 void *dir_baton = NULL;
1347 svn_boolean_t skip = FALSE;
1348 svn_boolean_t skip_children = FALSE;
1349 svn_diff_source_t *left_src;
1350 svn_revnum_t report_rev = revision;
1351
1352 if (!SVN_IS_VALID_REVNUM(report_rev))
1353 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
1354 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1355 NULL, NULL, NULL,
1356 db, local_abspath,
1357 scratch_pool, scratch_pool));
1358
1359 left_src = svn_diff__source_create(report_rev, scratch_pool);
1360
1361 SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
1362 relpath,
1363 left_src,
1364 NULL /* right_src */,
1365 NULL /* copyfrom_src */,
1366 processor_parent_baton,
1367 processor,
1368 scratch_pool, scratch_pool));
1369
1370 if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
1371 {
1372 apr_hash_t *nodes;
1373 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1374 apr_array_header_t *children;
1375 int i;
1376
1377 SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
1378 scratch_pool, iterpool));
1379
1380 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1381 scratch_pool);
1382
1383 for (i = 0; i < children->nelts; i++)
1384 {
1385 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1386 svn_sort__item_t);
1387 const char *name = item->key;
1388 struct svn_wc__db_base_info_t *info = item->value;
1389 const char *child_abspath;
1390 const char *child_relpath;
1391
1392 if (info->status != svn_wc__db_status_normal)
1393 continue;
1394
1395 if (cancel_func)
1396 SVN_ERR(cancel_func(cancel_baton));
1397
1398 svn_pool_clear(iterpool);
1399
1400 child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1401 child_relpath = svn_relpath_join(relpath, name, iterpool);
1402
1403 switch (info->kind)
1404 {
1405 case svn_node_file:
1406 case svn_node_symlink:
1407 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
1408 child_relpath,
1409 revision,
1410 processor, dir_baton,
1411 iterpool));
1412 break;
1413 case svn_node_dir:
1414 if (depth > svn_depth_files || depth == svn_depth_unknown)
1415 {
1416 svn_depth_t depth_below_here = depth;
1417
1418 if (depth_below_here == svn_depth_immediates)
1419 depth_below_here = svn_depth_empty;
1420
1421 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
1422 child_relpath,
1423 revision,
1424 depth_below_here,
1425 processor, dir_baton,
1426 cancel_func,
1427 cancel_baton,
1428 iterpool));
1429 }
1430 break;
1431
1432 default:
1433 break;
1434 }
1435 }
1436 }
1437
1438 if (!skip)
1439 {
1440 apr_hash_t *props;
1441 SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
1442 scratch_pool, scratch_pool));
1443
1444 SVN_ERR(processor->dir_deleted(relpath,
1445 left_src,
1446 props,
1447 dir_baton,
1448 processor,
1449 scratch_pool));
1450 }
1451
1452 return SVN_NO_ERROR;
1453 }
1454
1455 /* An svn_delta_editor_t function. */
1456 static svn_error_t *
set_target_revision(void * edit_baton,svn_revnum_t target_revision,apr_pool_t * pool)1457 set_target_revision(void *edit_baton,
1458 svn_revnum_t target_revision,
1459 apr_pool_t *pool)
1460 {
1461 struct edit_baton_t *eb = edit_baton;
1462 eb->revnum = target_revision;
1463
1464 return SVN_NO_ERROR;
1465 }
1466
1467 /* An svn_delta_editor_t function. The root of the comparison hierarchy */
1468 static svn_error_t *
open_root(void * edit_baton,svn_revnum_t base_revision,apr_pool_t * dir_pool,void ** root_baton)1469 open_root(void *edit_baton,
1470 svn_revnum_t base_revision,
1471 apr_pool_t *dir_pool,
1472 void **root_baton)
1473 {
1474 struct edit_baton_t *eb = edit_baton;
1475 struct dir_baton_t *db;
1476
1477 eb->root_opened = TRUE;
1478 db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
1479 *root_baton = db;
1480
1481 if (eb->target[0] == '\0')
1482 {
1483 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1484 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1485
1486 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
1487 &db->skip_children,
1488 "",
1489 db->left_src,
1490 db->right_src,
1491 NULL /* copyfrom_source */,
1492 NULL /* parent_baton */,
1493 eb->processor,
1494 db->pool, db->pool));
1495 }
1496 else
1497 db->skip = TRUE; /* Skip this, but not the children */
1498
1499 return SVN_NO_ERROR;
1500 }
1501
1502 /* An svn_delta_editor_t function. */
1503 static svn_error_t *
delete_entry(const char * path,svn_revnum_t base_revision,void * parent_baton,apr_pool_t * pool)1504 delete_entry(const char *path,
1505 svn_revnum_t base_revision,
1506 void *parent_baton,
1507 apr_pool_t *pool)
1508 {
1509 struct dir_baton_t *pb = parent_baton;
1510 const char *name = svn_dirent_basename(path, pb->pool);
1511
1512 if (!pb->deletes)
1513 pb->deletes = apr_hash_make(pb->pool);
1514
1515 svn_hash_sets(pb->deletes, name, "");
1516 return SVN_NO_ERROR;
1517 }
1518
1519 /* An svn_delta_editor_t function. */
1520 static svn_error_t *
add_directory(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_revision,apr_pool_t * dir_pool,void ** child_baton)1521 add_directory(const char *path,
1522 void *parent_baton,
1523 const char *copyfrom_path,
1524 svn_revnum_t copyfrom_revision,
1525 apr_pool_t *dir_pool,
1526 void **child_baton)
1527 {
1528 struct dir_baton_t *pb = parent_baton;
1529 struct edit_baton_t *eb = pb->eb;
1530 struct dir_baton_t *db;
1531 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1532 ? svn_depth_empty : pb->depth;
1533
1534 db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
1535 dir_pool);
1536 *child_baton = db;
1537
1538 if (pb->repos_only || !eb->ignore_ancestry)
1539 db->repos_only = TRUE;
1540 else
1541 {
1542 struct svn_wc__db_info_t *info;
1543 SVN_ERR(ensure_local_info(pb, dir_pool));
1544
1545 info = svn_hash_gets(pb->local_info, db->name);
1546
1547 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1548 db->repos_only = TRUE;
1549
1550 if (!db->repos_only && info->status != svn_wc__db_status_added)
1551 db->repos_only = TRUE;
1552
1553 if (!db->repos_only)
1554 {
1555 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1556 db->ignoring_ancestry = TRUE;
1557
1558 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1559 }
1560 }
1561
1562 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1563
1564 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1565 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1566
1567 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1568 db->relpath,
1569 db->left_src,
1570 db->right_src,
1571 NULL /* copyfrom src */,
1572 pb->pdb,
1573 eb->processor,
1574 db->pool, db->pool));
1575
1576 return SVN_NO_ERROR;
1577 }
1578
1579 /* An svn_delta_editor_t function. */
1580 static svn_error_t *
open_directory(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * dir_pool,void ** child_baton)1581 open_directory(const char *path,
1582 void *parent_baton,
1583 svn_revnum_t base_revision,
1584 apr_pool_t *dir_pool,
1585 void **child_baton)
1586 {
1587 struct dir_baton_t *pb = parent_baton;
1588 struct edit_baton_t *eb = pb->eb;
1589 struct dir_baton_t *db;
1590 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1591 ? svn_depth_empty : pb->depth;
1592
1593 /* Allocate path from the parent pool since the memory is used in the
1594 parent's compared hash */
1595 db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
1596 *child_baton = db;
1597
1598 if (pb->repos_only)
1599 db->repos_only = TRUE;
1600 else
1601 {
1602 struct svn_wc__db_info_t *info;
1603 SVN_ERR(ensure_local_info(pb, dir_pool));
1604
1605 info = svn_hash_gets(pb->local_info, db->name);
1606
1607 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1608 db->repos_only = TRUE;
1609
1610 if (!db->repos_only)
1611 switch (info->status)
1612 {
1613 case svn_wc__db_status_normal:
1614 break;
1615 case svn_wc__db_status_deleted:
1616 db->repos_only = TRUE;
1617
1618 if (!info->have_more_work)
1619 svn_hash_sets(pb->compared,
1620 apr_pstrdup(pb->pool, db->name), "");
1621 break;
1622 case svn_wc__db_status_added:
1623 if (eb->ignore_ancestry)
1624 db->ignoring_ancestry = TRUE;
1625 else
1626 db->repos_only = TRUE;
1627 break;
1628 default:
1629 SVN_ERR_MALFUNCTION();
1630 }
1631
1632 if (!db->repos_only)
1633 {
1634 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1635 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1636 }
1637 }
1638
1639 db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1640
1641 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1642 SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1643
1644 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1645 db->relpath,
1646 db->left_src,
1647 db->right_src,
1648 NULL /* copyfrom src */,
1649 pb->pdb,
1650 eb->processor,
1651 db->pool, db->pool));
1652
1653 return SVN_NO_ERROR;
1654 }
1655
1656
1657 /* An svn_delta_editor_t function. When a directory is closed, all the
1658 * directory elements that have been added or replaced will already have been
1659 * diff'd. However there may be other elements in the working copy
1660 * that have not yet been considered. */
1661 static svn_error_t *
close_directory(void * dir_baton,apr_pool_t * pool)1662 close_directory(void *dir_baton,
1663 apr_pool_t *pool)
1664 {
1665 struct dir_baton_t *db = dir_baton;
1666 struct dir_baton_t *pb = db->parent_baton;
1667 struct edit_baton_t *eb = db->eb;
1668 apr_pool_t *scratch_pool = db->pool;
1669 svn_boolean_t reported_closed = FALSE;
1670
1671 if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
1672 {
1673 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1674 apr_array_header_t *children;
1675 int i;
1676 children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
1677 scratch_pool);
1678
1679 for (i = 0; i < children->nelts; i++)
1680 {
1681 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1682 svn_sort__item_t);
1683 const char *name = item->key;
1684
1685 svn_pool_clear(iterpool);
1686 SVN_ERR(handle_local_only(db, name, iterpool));
1687
1688 svn_hash_sets(db->compared, name, "");
1689 }
1690
1691 svn_pool_destroy(iterpool);
1692 }
1693
1694 /* Report local modifications for this directory. Skip added
1695 directories since they can only contain added elements, all of
1696 which have already been diff'd. */
1697 if (!db->repos_only && !db->skip_children)
1698 {
1699 SVN_ERR(walk_local_nodes_diff(eb,
1700 db->local_abspath,
1701 db->relpath,
1702 db->depth,
1703 db->compared,
1704 db->pdb,
1705 scratch_pool));
1706 }
1707
1708 /* Report the property changes on the directory itself, if necessary. */
1709 if (db->skip)
1710 {
1711 /* Diff processor requested no directory details */
1712 }
1713 else if (db->propchanges->nelts > 0 || db->repos_only)
1714 {
1715 apr_hash_t *repos_props;
1716
1717 if (db->added)
1718 {
1719 repos_props = apr_hash_make(scratch_pool);
1720 }
1721 else
1722 {
1723 SVN_ERR(svn_wc__db_base_get_props(&repos_props,
1724 eb->db, db->local_abspath,
1725 scratch_pool, scratch_pool));
1726 }
1727
1728 /* Add received property changes and entry props */
1729 if (db->propchanges->nelts)
1730 repos_props = svn_prop__patch(repos_props, db->propchanges,
1731 scratch_pool);
1732
1733 if (db->repos_only)
1734 {
1735 SVN_ERR(eb->processor->dir_deleted(db->relpath,
1736 db->left_src,
1737 repos_props,
1738 db->pdb,
1739 eb->processor,
1740 scratch_pool));
1741 reported_closed = TRUE;
1742 }
1743 else
1744 {
1745 apr_hash_t *local_props;
1746 apr_array_header_t *prop_changes;
1747
1748 if (eb->diff_pristine)
1749 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1750 NULL, NULL, NULL, NULL,
1751 &local_props,
1752 eb->db, db->local_abspath,
1753 scratch_pool, scratch_pool));
1754 else
1755 SVN_ERR(svn_wc__db_read_props(&local_props,
1756 eb->db, db->local_abspath,
1757 scratch_pool, scratch_pool));
1758
1759 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
1760 scratch_pool));
1761
1762 /* ### as a good diff processor we should now only report changes
1763 if there are non-entry changes, but for now we stick to
1764 compatibility */
1765
1766 if (prop_changes->nelts)
1767 {
1768 SVN_ERR(eb->processor->dir_changed(db->relpath,
1769 db->left_src,
1770 db->right_src,
1771 repos_props,
1772 local_props,
1773 prop_changes,
1774 db->pdb,
1775 eb->processor,
1776 scratch_pool));
1777 reported_closed = TRUE;
1778 }
1779 }
1780 }
1781
1782 /* Mark this directory as compared in the parent directory's baton,
1783 unless this is the root of the comparison. */
1784 if (!reported_closed && !db->skip)
1785 SVN_ERR(eb->processor->dir_closed(db->relpath,
1786 db->left_src,
1787 db->right_src,
1788 db->pdb,
1789 eb->processor,
1790 scratch_pool));
1791
1792 if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1793 SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
1794
1795 SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
1796
1797 return SVN_NO_ERROR;
1798 }
1799
1800 /* An svn_delta_editor_t function. */
1801 static svn_error_t *
add_file(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_revision,apr_pool_t * file_pool,void ** file_baton)1802 add_file(const char *path,
1803 void *parent_baton,
1804 const char *copyfrom_path,
1805 svn_revnum_t copyfrom_revision,
1806 apr_pool_t *file_pool,
1807 void **file_baton)
1808 {
1809 struct dir_baton_t *pb = parent_baton;
1810 struct edit_baton_t *eb = pb->eb;
1811 struct file_baton_t *fb;
1812
1813 fb = make_file_baton(path, TRUE, pb, file_pool);
1814 *file_baton = fb;
1815
1816 if (pb->skip_children)
1817 {
1818 fb->skip = TRUE;
1819 return SVN_NO_ERROR;
1820 }
1821 else if (pb->repos_only || !eb->ignore_ancestry)
1822 fb->repos_only = TRUE;
1823 else
1824 {
1825 struct svn_wc__db_info_t *info;
1826 SVN_ERR(ensure_local_info(pb, file_pool));
1827
1828 info = svn_hash_gets(pb->local_info, fb->name);
1829
1830 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1831 fb->repos_only = TRUE;
1832
1833 if (!fb->repos_only && info->status != svn_wc__db_status_added)
1834 fb->repos_only = TRUE;
1835
1836 if (!fb->repos_only)
1837 {
1838 /* Add this path to the parent directory's list of elements that
1839 have been compared. */
1840 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1841 fb->ignoring_ancestry = TRUE;
1842
1843 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1844 }
1845 }
1846
1847 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1848
1849 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1850 fb->relpath,
1851 fb->left_src,
1852 fb->right_src,
1853 NULL /* copyfrom src */,
1854 pb->pdb,
1855 eb->processor,
1856 fb->pool, fb->pool));
1857
1858 return SVN_NO_ERROR;
1859 }
1860
1861 /* An svn_delta_editor_t function. */
1862 static svn_error_t *
open_file(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * file_pool,void ** file_baton)1863 open_file(const char *path,
1864 void *parent_baton,
1865 svn_revnum_t base_revision,
1866 apr_pool_t *file_pool,
1867 void **file_baton)
1868 {
1869 struct dir_baton_t *pb = parent_baton;
1870 struct edit_baton_t *eb = pb->eb;
1871 struct file_baton_t *fb;
1872
1873 fb = make_file_baton(path, FALSE, pb, file_pool);
1874 *file_baton = fb;
1875
1876 if (pb->skip_children)
1877 fb->skip = TRUE;
1878 else if (pb->repos_only)
1879 fb->repos_only = TRUE;
1880 else
1881 {
1882 struct svn_wc__db_info_t *info;
1883 SVN_ERR(ensure_local_info(pb, file_pool));
1884
1885 info = svn_hash_gets(pb->local_info, fb->name);
1886
1887 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1888 fb->repos_only = TRUE;
1889
1890 if (!fb->repos_only)
1891 switch (info->status)
1892 {
1893 case svn_wc__db_status_normal:
1894 break;
1895 case svn_wc__db_status_deleted:
1896 fb->repos_only = TRUE;
1897 if (!info->have_more_work)
1898 svn_hash_sets(pb->compared,
1899 apr_pstrdup(pb->pool, fb->name), "");
1900 break;
1901 case svn_wc__db_status_added:
1902 if (eb->ignore_ancestry)
1903 fb->ignoring_ancestry = TRUE;
1904 else
1905 fb->repos_only = TRUE;
1906 break;
1907 default:
1908 SVN_ERR_MALFUNCTION();
1909 }
1910
1911 if (!fb->repos_only)
1912 {
1913 /* Add this path to the parent directory's list of elements that
1914 have been compared. */
1915 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1916 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1917 }
1918 }
1919
1920 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1921
1922 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1923 NULL, NULL, NULL, &fb->base_checksum, NULL,
1924 NULL, NULL, &fb->base_props, NULL,
1925 eb->db, fb->local_abspath,
1926 fb->pool, fb->pool));
1927
1928 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1929 fb->relpath,
1930 fb->left_src,
1931 fb->right_src,
1932 NULL /* copyfrom src */,
1933 pb->pdb,
1934 eb->processor,
1935 fb->pool, fb->pool));
1936
1937 return SVN_NO_ERROR;
1938 }
1939
1940 /* An svn_delta_editor_t function. */
1941 static svn_error_t *
apply_textdelta(void * file_baton,const char * base_checksum_hex,apr_pool_t * pool,svn_txdelta_window_handler_t * handler,void ** handler_baton)1942 apply_textdelta(void *file_baton,
1943 const char *base_checksum_hex,
1944 apr_pool_t *pool,
1945 svn_txdelta_window_handler_t *handler,
1946 void **handler_baton)
1947 {
1948 struct file_baton_t *fb = file_baton;
1949 struct edit_baton_t *eb = fb->eb;
1950 svn_stream_t *source;
1951 svn_stream_t *temp_stream;
1952 svn_checksum_t *repos_checksum = NULL;
1953
1954 if (fb->skip)
1955 {
1956 *handler = svn_delta_noop_window_handler;
1957 *handler_baton = NULL;
1958 return SVN_NO_ERROR;
1959 }
1960
1961 if (base_checksum_hex && fb->base_checksum)
1962 {
1963 const svn_checksum_t *base_md5;
1964 SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
1965 base_checksum_hex, pool));
1966
1967 SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
1968 eb->db, eb->anchor_abspath,
1969 fb->base_checksum,
1970 pool, pool));
1971
1972 if (! svn_checksum_match(repos_checksum, base_md5))
1973 {
1974 /* ### I expect that there are some bad drivers out there
1975 ### that used to give bad results. We could look in
1976 ### working to see if the expected checksum matches and
1977 ### then return the pristine of that... But that only moves
1978 ### the problem */
1979
1980 /* If needed: compare checksum obtained via md5 of working.
1981 And if they match set fb->base_checksum and fb->base_props */
1982
1983 return svn_checksum_mismatch_err(
1984 base_md5,
1985 repos_checksum,
1986 pool,
1987 _("Checksum mismatch for '%s'"),
1988 svn_dirent_local_style(fb->local_abspath,
1989 pool));
1990 }
1991
1992 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1993 eb->db, fb->local_abspath,
1994 fb->base_checksum,
1995 pool, pool));
1996 }
1997 else if (fb->base_checksum)
1998 {
1999 SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
2000 eb->db, fb->local_abspath,
2001 fb->base_checksum,
2002 pool, pool));
2003 }
2004 else
2005 source = svn_stream_empty(pool);
2006
2007 /* This is the file that will contain the pristine repository version. */
2008 SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
2009 svn_io_file_del_on_pool_cleanup,
2010 fb->pool, fb->pool));
2011
2012 svn_txdelta_apply(source, temp_stream,
2013 fb->result_digest,
2014 fb->local_abspath /* error_info */,
2015 fb->pool,
2016 handler, handler_baton);
2017
2018 return SVN_NO_ERROR;
2019 }
2020
2021 /* An svn_delta_editor_t function. When the file is closed we have a temporary
2022 * file containing a pristine version of the repository file. This can
2023 * be compared against the working copy.
2024 *
2025 * Ignore TEXT_CHECKSUM.
2026 */
2027 static svn_error_t *
close_file(void * file_baton,const char * expected_md5_digest,apr_pool_t * pool)2028 close_file(void *file_baton,
2029 const char *expected_md5_digest,
2030 apr_pool_t *pool)
2031 {
2032 struct file_baton_t *fb = file_baton;
2033 struct dir_baton_t *pb = fb->parent_baton;
2034 struct edit_baton_t *eb = fb->eb;
2035 apr_pool_t *scratch_pool = fb->pool;
2036
2037 /* The repository information; constructed from BASE + Changes */
2038 const char *repos_file;
2039 apr_hash_t *repos_props;
2040
2041 if (fb->skip)
2042 {
2043 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2044 SVN_ERR(maybe_done(pb));
2045 return SVN_NO_ERROR;
2046 }
2047
2048 if (expected_md5_digest != NULL)
2049 {
2050 svn_checksum_t *expected_checksum;
2051 const svn_checksum_t *result_checksum;
2052
2053 if (fb->temp_file_path)
2054 result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
2055 scratch_pool);
2056 else
2057 result_checksum = fb->base_checksum;
2058
2059 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
2060 expected_md5_digest, scratch_pool));
2061
2062 if (result_checksum->kind != svn_checksum_md5)
2063 SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
2064 eb->db, fb->local_abspath,
2065 result_checksum,
2066 scratch_pool, scratch_pool));
2067
2068 if (!svn_checksum_match(expected_checksum, result_checksum))
2069 return svn_checksum_mismatch_err(
2070 expected_checksum,
2071 result_checksum,
2072 pool,
2073 _("Checksum mismatch for '%s'"),
2074 svn_dirent_local_style(fb->local_abspath,
2075 scratch_pool));
2076 }
2077
2078 if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2079 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2080
2081 {
2082 apr_hash_t *prop_base;
2083
2084 if (fb->added)
2085 prop_base = apr_hash_make(scratch_pool);
2086 else
2087 prop_base = fb->base_props;
2088
2089 /* includes entry props */
2090 repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
2091
2092 repos_file = fb->temp_file_path;
2093 if (! repos_file)
2094 {
2095 assert(fb->base_checksum);
2096 SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
2097 eb->db, eb->anchor_abspath,
2098 fb->base_checksum,
2099 scratch_pool, scratch_pool));
2100 }
2101 }
2102
2103 if (fb->repos_only)
2104 {
2105 SVN_ERR(eb->processor->file_deleted(fb->relpath,
2106 fb->left_src,
2107 fb->temp_file_path,
2108 repos_props,
2109 fb->pfb,
2110 eb->processor,
2111 scratch_pool));
2112 }
2113 else
2114 {
2115 /* Produce a diff of actual or pristine against repos */
2116 apr_hash_t *local_props;
2117 apr_array_header_t *prop_changes;
2118 const char *localfile;
2119
2120 /* pb->local_info contains some information that might allow optimizing
2121 this a bit */
2122
2123 if (eb->diff_pristine)
2124 {
2125 const svn_checksum_t *checksum;
2126 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
2127 NULL, &checksum, NULL, NULL,
2128 &local_props,
2129 eb->db, fb->local_abspath,
2130 scratch_pool, scratch_pool));
2131 assert(checksum);
2132 SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
2133 eb->db, eb->anchor_abspath,
2134 checksum,
2135 scratch_pool, scratch_pool));
2136 }
2137 else
2138 {
2139 SVN_ERR(svn_wc__db_read_props(&local_props,
2140 eb->db, fb->local_abspath,
2141 scratch_pool, scratch_pool));
2142
2143 /* a detranslated version of the working file */
2144 SVN_ERR(svn_wc__internal_translated_file(
2145 &localfile, fb->local_abspath, eb->db, fb->local_abspath,
2146 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
2147 eb->cancel_func, eb->cancel_baton,
2148 scratch_pool, scratch_pool));
2149 }
2150
2151 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
2152 scratch_pool));
2153
2154
2155 /* ### as a good diff processor we should now only report changes, and
2156 report file_closed() in other cases */
2157 SVN_ERR(eb->processor->file_changed(fb->relpath,
2158 fb->left_src,
2159 fb->right_src,
2160 repos_file /* left file */,
2161 localfile /* right file */,
2162 repos_props /* left_props */,
2163 local_props /* right props */,
2164 TRUE /* ### file_modified */,
2165 prop_changes,
2166 fb->pfb,
2167 eb->processor,
2168 scratch_pool));
2169 }
2170
2171 if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2172 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2173
2174 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2175 SVN_ERR(maybe_done(pb));
2176 return SVN_NO_ERROR;
2177 }
2178
2179
2180 /* An svn_delta_editor_t function. */
2181 static svn_error_t *
change_file_prop(void * file_baton,const char * name,const svn_string_t * value,apr_pool_t * pool)2182 change_file_prop(void *file_baton,
2183 const char *name,
2184 const svn_string_t *value,
2185 apr_pool_t *pool)
2186 {
2187 struct file_baton_t *fb = file_baton;
2188 svn_prop_t *propchange;
2189 svn_prop_kind_t propkind;
2190
2191 propkind = svn_property_kind2(name);
2192 if (propkind == svn_prop_wc_kind)
2193 return SVN_NO_ERROR;
2194 else if (propkind == svn_prop_regular_kind)
2195 fb->has_propchange = TRUE;
2196
2197 propchange = apr_array_push(fb->propchanges);
2198 propchange->name = apr_pstrdup(fb->pool, name);
2199 propchange->value = svn_string_dup(value, fb->pool);
2200
2201 return SVN_NO_ERROR;
2202 }
2203
2204
2205 /* An svn_delta_editor_t function. */
2206 static svn_error_t *
change_dir_prop(void * dir_baton,const char * name,const svn_string_t * value,apr_pool_t * pool)2207 change_dir_prop(void *dir_baton,
2208 const char *name,
2209 const svn_string_t *value,
2210 apr_pool_t *pool)
2211 {
2212 struct dir_baton_t *db = dir_baton;
2213 svn_prop_t *propchange;
2214 svn_prop_kind_t propkind;
2215
2216 propkind = svn_property_kind2(name);
2217 if (propkind == svn_prop_wc_kind)
2218 return SVN_NO_ERROR;
2219 else if (propkind == svn_prop_regular_kind)
2220 db->has_propchange = TRUE;
2221
2222 propchange = apr_array_push(db->propchanges);
2223 propchange->name = apr_pstrdup(db->pool, name);
2224 propchange->value = svn_string_dup(value, db->pool);
2225
2226 return SVN_NO_ERROR;
2227 }
2228
2229
2230 /* An svn_delta_editor_t function. */
2231 static svn_error_t *
close_edit(void * edit_baton,apr_pool_t * pool)2232 close_edit(void *edit_baton,
2233 apr_pool_t *pool)
2234 {
2235 struct edit_baton_t *eb = edit_baton;
2236
2237 if (!eb->root_opened)
2238 {
2239 SVN_ERR(walk_local_nodes_diff(eb,
2240 eb->anchor_abspath,
2241 "",
2242 eb->depth,
2243 NULL /* compared */,
2244 NULL /* No parent_baton */,
2245 eb->pool));
2246 }
2247
2248 return SVN_NO_ERROR;
2249 }
2250
2251 /* Public Interface */
2252
2253
2254 /* Create a diff editor and baton. */
2255 svn_error_t *
svn_wc__get_diff_editor(const svn_delta_editor_t ** editor,void ** edit_baton,svn_wc_context_t * wc_ctx,const char * anchor_abspath,const char * target,svn_depth_t depth,svn_boolean_t ignore_ancestry,svn_boolean_t use_text_base,svn_boolean_t reverse_order,svn_boolean_t server_performs_filtering,const apr_array_header_t * changelist_filter,const svn_diff_tree_processor_t * diff_processor,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2256 svn_wc__get_diff_editor(const svn_delta_editor_t **editor,
2257 void **edit_baton,
2258 svn_wc_context_t *wc_ctx,
2259 const char *anchor_abspath,
2260 const char *target,
2261 svn_depth_t depth,
2262 svn_boolean_t ignore_ancestry,
2263 svn_boolean_t use_text_base,
2264 svn_boolean_t reverse_order,
2265 svn_boolean_t server_performs_filtering,
2266 const apr_array_header_t *changelist_filter,
2267 const svn_diff_tree_processor_t *diff_processor,
2268 svn_cancel_func_t cancel_func,
2269 void *cancel_baton,
2270 apr_pool_t *result_pool,
2271 apr_pool_t *scratch_pool)
2272 {
2273 struct edit_baton_t *eb;
2274 void *inner_baton;
2275 svn_delta_editor_t *tree_editor;
2276 const svn_delta_editor_t *inner_editor;
2277 struct svn_wc__shim_fetch_baton_t *sfb;
2278 svn_delta_shim_callbacks_t *shim_callbacks =
2279 svn_delta_shim_callbacks_default(result_pool);
2280
2281 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
2282
2283 /* Apply changelist filtering to the output */
2284 if (changelist_filter && changelist_filter->nelts)
2285 {
2286 apr_hash_t *changelist_hash;
2287
2288 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
2289 result_pool));
2290 diff_processor = svn_wc__changelist_filter_tree_processor_create(
2291 diff_processor, wc_ctx, anchor_abspath,
2292 changelist_hash, result_pool);
2293 }
2294
2295 SVN_ERR(make_edit_baton(&eb,
2296 wc_ctx->db,
2297 anchor_abspath, target,
2298 diff_processor,
2299 depth, ignore_ancestry,
2300 use_text_base, reverse_order,
2301 cancel_func, cancel_baton,
2302 result_pool));
2303
2304 tree_editor = svn_delta_default_editor(eb->pool);
2305
2306 tree_editor->set_target_revision = set_target_revision;
2307 tree_editor->open_root = open_root;
2308 tree_editor->delete_entry = delete_entry;
2309 tree_editor->add_directory = add_directory;
2310 tree_editor->open_directory = open_directory;
2311 tree_editor->close_directory = close_directory;
2312 tree_editor->add_file = add_file;
2313 tree_editor->open_file = open_file;
2314 tree_editor->apply_textdelta = apply_textdelta;
2315 tree_editor->change_file_prop = change_file_prop;
2316 tree_editor->change_dir_prop = change_dir_prop;
2317 tree_editor->close_file = close_file;
2318 tree_editor->close_edit = close_edit;
2319
2320 inner_editor = tree_editor;
2321 inner_baton = eb;
2322
2323 if (!server_performs_filtering
2324 && depth == svn_depth_unknown)
2325 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2326 &inner_baton,
2327 wc_ctx->db,
2328 anchor_abspath,
2329 target,
2330 inner_editor,
2331 inner_baton,
2332 result_pool));
2333
2334 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
2335 cancel_baton,
2336 inner_editor,
2337 inner_baton,
2338 editor,
2339 edit_baton,
2340 result_pool));
2341
2342 sfb = apr_palloc(result_pool, sizeof(*sfb));
2343 sfb->db = wc_ctx->db;
2344 sfb->base_abspath = eb->anchor_abspath;
2345 sfb->fetch_base = TRUE;
2346
2347 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2348 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2349 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2350 shim_callbacks->fetch_baton = sfb;
2351
2352
2353 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2354 NULL, NULL, shim_callbacks,
2355 result_pool, scratch_pool));
2356
2357 return SVN_NO_ERROR;
2358 }
2359
2360 /* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
2361
2362 /* baton for the svn_diff_tree_processor_t wrapper */
2363 typedef struct wc_diff_wrap_baton_t
2364 {
2365 const svn_wc_diff_callbacks4_t *callbacks;
2366 void *callback_baton;
2367
2368 svn_boolean_t walk_deleted_dirs;
2369
2370 apr_pool_t *result_pool;
2371 const char *empty_file;
2372
2373 } wc_diff_wrap_baton_t;
2374
2375 static svn_error_t *
wrap_ensure_empty_file(wc_diff_wrap_baton_t * wb,apr_pool_t * scratch_pool)2376 wrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
2377 apr_pool_t *scratch_pool)
2378 {
2379 if (wb->empty_file)
2380 return SVN_NO_ERROR;
2381
2382 /* Create a unique file in the tempdir */
2383 SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
2384 svn_io_file_del_on_pool_cleanup,
2385 wb->result_pool, scratch_pool));
2386
2387 return SVN_NO_ERROR;
2388 }
2389
2390 /* svn_diff_tree_processor_t function */
2391 static svn_error_t *
wrap_dir_opened(void ** new_dir_baton,svn_boolean_t * skip,svn_boolean_t * skip_children,const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const svn_diff_source_t * copyfrom_source,void * parent_dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2392 wrap_dir_opened(void **new_dir_baton,
2393 svn_boolean_t *skip,
2394 svn_boolean_t *skip_children,
2395 const char *relpath,
2396 const svn_diff_source_t *left_source,
2397 const svn_diff_source_t *right_source,
2398 const svn_diff_source_t *copyfrom_source,
2399 void *parent_dir_baton,
2400 const svn_diff_tree_processor_t *processor,
2401 apr_pool_t *result_pool,
2402 apr_pool_t *scratch_pool)
2403 {
2404 wc_diff_wrap_baton_t *wb = processor->baton;
2405 svn_boolean_t tree_conflicted = FALSE;
2406
2407 assert(left_source || right_source); /* Must exist at one point. */
2408 assert(!left_source || !copyfrom_source); /* Either existed or added. */
2409
2410 /* Maybe store state and tree_conflicted in baton? */
2411 if (left_source != NULL)
2412 {
2413 /* Open for change or delete */
2414 SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
2415 relpath,
2416 right_source
2417 ? right_source->revision
2418 : (left_source
2419 ? left_source->revision
2420 : SVN_INVALID_REVNUM),
2421 wb->callback_baton,
2422 scratch_pool));
2423
2424 if (! right_source && !wb->walk_deleted_dirs)
2425 *skip_children = TRUE;
2426 }
2427 else /* left_source == NULL -> Add */
2428 {
2429 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2430 SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
2431 skip, skip_children,
2432 relpath,
2433 right_source->revision,
2434 copyfrom_source
2435 ? copyfrom_source->repos_relpath
2436 : NULL,
2437 copyfrom_source
2438 ? copyfrom_source->revision
2439 : SVN_INVALID_REVNUM,
2440 wb->callback_baton,
2441 scratch_pool));
2442 }
2443
2444 *new_dir_baton = NULL;
2445
2446 return SVN_NO_ERROR;
2447 }
2448
2449 /* svn_diff_tree_processor_t function */
2450 static svn_error_t *
wrap_dir_added(const char * relpath,const svn_diff_source_t * copyfrom_source,const svn_diff_source_t * right_source,apr_hash_t * copyfrom_props,apr_hash_t * right_props,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2451 wrap_dir_added(const char *relpath,
2452 const svn_diff_source_t *copyfrom_source,
2453 const svn_diff_source_t *right_source,
2454 /*const*/ apr_hash_t *copyfrom_props,
2455 /*const*/ apr_hash_t *right_props,
2456 void *dir_baton,
2457 const svn_diff_tree_processor_t *processor,
2458 apr_pool_t *scratch_pool)
2459 {
2460 wc_diff_wrap_baton_t *wb = processor->baton;
2461 svn_boolean_t tree_conflicted = FALSE;
2462 svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
2463 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2464 apr_hash_t *pristine_props = copyfrom_props;
2465 apr_array_header_t *prop_changes = NULL;
2466
2467 if (right_props && apr_hash_count(right_props))
2468 {
2469 if (!pristine_props)
2470 pristine_props = apr_hash_make(scratch_pool);
2471
2472 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
2473 scratch_pool));
2474
2475 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
2476 &tree_conflicted,
2477 relpath,
2478 TRUE /* dir_was_added */,
2479 prop_changes, pristine_props,
2480 wb->callback_baton,
2481 scratch_pool));
2482 }
2483
2484 SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
2485 &tree_conflicted,
2486 relpath,
2487 TRUE /* dir_was_added */,
2488 wb->callback_baton,
2489 scratch_pool));
2490 return SVN_NO_ERROR;
2491 }
2492
2493 /* svn_diff_tree_processor_t function */
2494 static svn_error_t *
wrap_dir_deleted(const char * relpath,const svn_diff_source_t * left_source,apr_hash_t * left_props,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2495 wrap_dir_deleted(const char *relpath,
2496 const svn_diff_source_t *left_source,
2497 /*const*/ apr_hash_t *left_props,
2498 void *dir_baton,
2499 const svn_diff_tree_processor_t *processor,
2500 apr_pool_t *scratch_pool)
2501 {
2502 wc_diff_wrap_baton_t *wb = processor->baton;
2503 svn_boolean_t tree_conflicted = FALSE;
2504 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2505
2506 SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
2507 relpath,
2508 wb->callback_baton,
2509 scratch_pool));
2510
2511 return SVN_NO_ERROR;
2512 }
2513
2514 /* svn_diff_tree_processor_t function */
2515 static svn_error_t *
wrap_dir_closed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2516 wrap_dir_closed(const char *relpath,
2517 const svn_diff_source_t *left_source,
2518 const svn_diff_source_t *right_source,
2519 void *dir_baton,
2520 const svn_diff_tree_processor_t *processor,
2521 apr_pool_t *scratch_pool)
2522 {
2523 wc_diff_wrap_baton_t *wb = processor->baton;
2524
2525 /* No previous implementations provided these arguments, so we
2526 are not providing them either */
2527 SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
2528 relpath,
2529 FALSE /* added */,
2530 wb->callback_baton,
2531 scratch_pool));
2532
2533 return SVN_NO_ERROR;
2534 }
2535
2536 /* svn_diff_tree_processor_t function */
2537 static svn_error_t *
wrap_dir_changed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,apr_hash_t * left_props,apr_hash_t * right_props,const apr_array_header_t * prop_changes,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2538 wrap_dir_changed(const char *relpath,
2539 const svn_diff_source_t *left_source,
2540 const svn_diff_source_t *right_source,
2541 /*const*/ apr_hash_t *left_props,
2542 /*const*/ apr_hash_t *right_props,
2543 const apr_array_header_t *prop_changes,
2544 void *dir_baton,
2545 const struct svn_diff_tree_processor_t *processor,
2546 apr_pool_t *scratch_pool)
2547 {
2548 wc_diff_wrap_baton_t *wb = processor->baton;
2549 svn_boolean_t tree_conflicted = FALSE;
2550 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2551
2552 assert(left_source && right_source);
2553
2554 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
2555 relpath,
2556 FALSE /* dir_was_added */,
2557 prop_changes,
2558 left_props,
2559 wb->callback_baton,
2560 scratch_pool));
2561
2562 /* And call dir_closed, etc */
2563 SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
2564 dir_baton, processor,
2565 scratch_pool));
2566 return SVN_NO_ERROR;
2567 }
2568
2569 /* svn_diff_tree_processor_t function */
2570 static svn_error_t *
wrap_file_opened(void ** new_file_baton,svn_boolean_t * skip,const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const svn_diff_source_t * copyfrom_source,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2571 wrap_file_opened(void **new_file_baton,
2572 svn_boolean_t *skip,
2573 const char *relpath,
2574 const svn_diff_source_t *left_source,
2575 const svn_diff_source_t *right_source,
2576 const svn_diff_source_t *copyfrom_source,
2577 void *dir_baton,
2578 const svn_diff_tree_processor_t *processor,
2579 apr_pool_t *result_pool,
2580 apr_pool_t *scratch_pool)
2581 {
2582 wc_diff_wrap_baton_t *wb = processor->baton;
2583 svn_boolean_t tree_conflicted = FALSE;
2584
2585 if (left_source) /* If ! added */
2586 SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
2587 right_source
2588 ? right_source->revision
2589 : (left_source
2590 ? left_source->revision
2591 : SVN_INVALID_REVNUM),
2592 wb->callback_baton, scratch_pool));
2593
2594 /* No old implementation used the output arguments for notify */
2595
2596 *new_file_baton = NULL;
2597 return SVN_NO_ERROR;
2598 }
2599
2600 /* svn_diff_tree_processor_t function */
2601 static svn_error_t *
wrap_file_added(const char * relpath,const svn_diff_source_t * copyfrom_source,const svn_diff_source_t * right_source,const char * copyfrom_file,const char * right_file,apr_hash_t * copyfrom_props,apr_hash_t * right_props,void * file_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2602 wrap_file_added(const char *relpath,
2603 const svn_diff_source_t *copyfrom_source,
2604 const svn_diff_source_t *right_source,
2605 const char *copyfrom_file,
2606 const char *right_file,
2607 /*const*/ apr_hash_t *copyfrom_props,
2608 /*const*/ apr_hash_t *right_props,
2609 void *file_baton,
2610 const svn_diff_tree_processor_t *processor,
2611 apr_pool_t *scratch_pool)
2612 {
2613 wc_diff_wrap_baton_t *wb = processor->baton;
2614 svn_boolean_t tree_conflicted = FALSE;
2615 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2616 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2617 apr_array_header_t *prop_changes;
2618
2619 if (! copyfrom_props)
2620 copyfrom_props = apr_hash_make(scratch_pool);
2621
2622 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
2623 scratch_pool));
2624
2625 if (! copyfrom_source)
2626 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2627
2628 SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
2629 relpath,
2630 copyfrom_source
2631 ? copyfrom_file
2632 : wb->empty_file,
2633 right_file,
2634 0,
2635 right_source->revision,
2636 copyfrom_props
2637 ? svn_prop_get_value(copyfrom_props,
2638 SVN_PROP_MIME_TYPE)
2639 : NULL,
2640 right_props
2641 ? svn_prop_get_value(right_props,
2642 SVN_PROP_MIME_TYPE)
2643 : NULL,
2644 copyfrom_source
2645 ? copyfrom_source->repos_relpath
2646 : NULL,
2647 copyfrom_source
2648 ? copyfrom_source->revision
2649 : SVN_INVALID_REVNUM,
2650 prop_changes, copyfrom_props,
2651 wb->callback_baton,
2652 scratch_pool));
2653 return SVN_NO_ERROR;
2654 }
2655
2656 static svn_error_t *
wrap_file_deleted(const char * relpath,const svn_diff_source_t * left_source,const char * left_file,apr_hash_t * left_props,void * file_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2657 wrap_file_deleted(const char *relpath,
2658 const svn_diff_source_t *left_source,
2659 const char *left_file,
2660 apr_hash_t *left_props,
2661 void *file_baton,
2662 const svn_diff_tree_processor_t *processor,
2663 apr_pool_t *scratch_pool)
2664 {
2665 wc_diff_wrap_baton_t *wb = processor->baton;
2666 svn_boolean_t tree_conflicted = FALSE;
2667 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2668
2669 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2670
2671 SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
2672 relpath,
2673 left_file, wb->empty_file,
2674 left_props
2675 ? svn_prop_get_value(left_props,
2676 SVN_PROP_MIME_TYPE)
2677 : NULL,
2678 NULL,
2679 left_props,
2680 wb->callback_baton,
2681 scratch_pool));
2682 return SVN_NO_ERROR;
2683 }
2684
2685 /* svn_diff_tree_processor_t function */
2686 static svn_error_t *
wrap_file_changed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const char * left_file,const char * right_file,apr_hash_t * left_props,apr_hash_t * right_props,svn_boolean_t file_modified,const apr_array_header_t * prop_changes,void * file_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2687 wrap_file_changed(const char *relpath,
2688 const svn_diff_source_t *left_source,
2689 const svn_diff_source_t *right_source,
2690 const char *left_file,
2691 const char *right_file,
2692 /*const*/ apr_hash_t *left_props,
2693 /*const*/ apr_hash_t *right_props,
2694 svn_boolean_t file_modified,
2695 const apr_array_header_t *prop_changes,
2696 void *file_baton,
2697 const svn_diff_tree_processor_t *processor,
2698 apr_pool_t *scratch_pool)
2699 {
2700 wc_diff_wrap_baton_t *wb = processor->baton;
2701 svn_boolean_t tree_conflicted = FALSE;
2702 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2703 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2704
2705 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2706
2707 assert(left_source && right_source);
2708
2709 SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
2710 relpath,
2711 file_modified ? left_file : NULL,
2712 file_modified ? right_file : NULL,
2713 left_source->revision,
2714 right_source->revision,
2715 left_props
2716 ? svn_prop_get_value(left_props,
2717 SVN_PROP_MIME_TYPE)
2718 : NULL,
2719 right_props
2720 ? svn_prop_get_value(right_props,
2721 SVN_PROP_MIME_TYPE)
2722 : NULL,
2723 prop_changes,
2724 left_props,
2725 wb->callback_baton,
2726 scratch_pool));
2727 return SVN_NO_ERROR;
2728 }
2729
2730 svn_error_t *
svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t ** diff_processor,const svn_wc_diff_callbacks4_t * callbacks,void * callback_baton,svn_boolean_t walk_deleted_dirs,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2731 svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
2732 const svn_wc_diff_callbacks4_t *callbacks,
2733 void *callback_baton,
2734 svn_boolean_t walk_deleted_dirs,
2735 apr_pool_t *result_pool,
2736 apr_pool_t *scratch_pool)
2737 {
2738 wc_diff_wrap_baton_t *wrap_baton;
2739 svn_diff_tree_processor_t *processor;
2740
2741 wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
2742
2743 wrap_baton->result_pool = result_pool;
2744 wrap_baton->callbacks = callbacks;
2745 wrap_baton->callback_baton = callback_baton;
2746 wrap_baton->empty_file = NULL;
2747 wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
2748
2749 processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
2750
2751 processor->dir_opened = wrap_dir_opened;
2752 processor->dir_added = wrap_dir_added;
2753 processor->dir_deleted = wrap_dir_deleted;
2754 processor->dir_changed = wrap_dir_changed;
2755 processor->dir_closed = wrap_dir_closed;
2756
2757 processor->file_opened = wrap_file_opened;
2758 processor->file_added = wrap_file_added;
2759 processor->file_deleted = wrap_file_deleted;
2760 processor->file_changed = wrap_file_changed;
2761 /*processor->file_closed = wrap_file_closed*/; /* Not needed */
2762
2763 *diff_processor = processor;
2764 return SVN_NO_ERROR;
2765 }
2766
2767 /* =====================================================================
2768 * A tree processor filter that filters by changelist membership
2769 * =====================================================================
2770 *
2771 * The current implementation queries the WC for the changelist of each
2772 * file as it comes through, and sets the 'skip' flag for a non-matching
2773 * file.
2774 *
2775 * (It doesn't set the 'skip' flag for a directory, as we need to receive
2776 * the changed/added/deleted/closed call to know when it is closed, in
2777 * order to preserve the strict open-close semantics for the wrapped tree
2778 * processor.)
2779 *
2780 * It passes on the opening and closing of every directory, even if there
2781 * are no file changes to be passed on inside that directory.
2782 */
2783
2784 typedef struct filter_tree_baton_t
2785 {
2786 const svn_diff_tree_processor_t *processor;
2787 svn_wc_context_t *wc_ctx;
2788 /* WC path of the root of the diff (where relpath = "") */
2789 const char *root_local_abspath;
2790 /* Hash whose keys are const char * changelist names. */
2791 apr_hash_t *changelist_hash;
2792 } filter_tree_baton_t;
2793
2794 static svn_error_t *
filter_dir_opened(void ** new_dir_baton,svn_boolean_t * skip,svn_boolean_t * skip_children,const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const svn_diff_source_t * copyfrom_source,void * parent_dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2795 filter_dir_opened(void **new_dir_baton,
2796 svn_boolean_t *skip,
2797 svn_boolean_t *skip_children,
2798 const char *relpath,
2799 const svn_diff_source_t *left_source,
2800 const svn_diff_source_t *right_source,
2801 const svn_diff_source_t *copyfrom_source,
2802 void *parent_dir_baton,
2803 const svn_diff_tree_processor_t *processor,
2804 apr_pool_t *result_pool,
2805 apr_pool_t *scratch_pool)
2806 {
2807 struct filter_tree_baton_t *fb = processor->baton;
2808
2809 SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
2810 relpath,
2811 left_source, right_source,
2812 copyfrom_source,
2813 parent_dir_baton,
2814 fb->processor,
2815 result_pool, scratch_pool));
2816 return SVN_NO_ERROR;
2817 }
2818
2819 static svn_error_t *
filter_dir_added(const char * relpath,const svn_diff_source_t * copyfrom_source,const svn_diff_source_t * right_source,apr_hash_t * copyfrom_props,apr_hash_t * right_props,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2820 filter_dir_added(const char *relpath,
2821 const svn_diff_source_t *copyfrom_source,
2822 const svn_diff_source_t *right_source,
2823 /*const*/ apr_hash_t *copyfrom_props,
2824 /*const*/ apr_hash_t *right_props,
2825 void *dir_baton,
2826 const svn_diff_tree_processor_t *processor,
2827 apr_pool_t *scratch_pool)
2828 {
2829 struct filter_tree_baton_t *fb = processor->baton;
2830
2831 SVN_ERR(fb->processor->dir_closed(relpath,
2832 NULL,
2833 right_source,
2834 dir_baton,
2835 fb->processor,
2836 scratch_pool));
2837
2838 return SVN_NO_ERROR;
2839 }
2840
2841 static svn_error_t *
filter_dir_deleted(const char * relpath,const svn_diff_source_t * left_source,apr_hash_t * left_props,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2842 filter_dir_deleted(const char *relpath,
2843 const svn_diff_source_t *left_source,
2844 /*const*/ apr_hash_t *left_props,
2845 void *dir_baton,
2846 const svn_diff_tree_processor_t *processor,
2847 apr_pool_t *scratch_pool)
2848 {
2849 struct filter_tree_baton_t *fb = processor->baton;
2850
2851 SVN_ERR(fb->processor->dir_closed(relpath,
2852 left_source,
2853 NULL,
2854 dir_baton,
2855 fb->processor,
2856 scratch_pool));
2857
2858 return SVN_NO_ERROR;
2859 }
2860
2861 static svn_error_t *
filter_dir_changed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,apr_hash_t * left_props,apr_hash_t * right_props,const apr_array_header_t * prop_changes,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2862 filter_dir_changed(const char *relpath,
2863 const svn_diff_source_t *left_source,
2864 const svn_diff_source_t *right_source,
2865 /*const*/ apr_hash_t *left_props,
2866 /*const*/ apr_hash_t *right_props,
2867 const apr_array_header_t *prop_changes,
2868 void *dir_baton,
2869 const struct svn_diff_tree_processor_t *processor,
2870 apr_pool_t *scratch_pool)
2871 {
2872 struct filter_tree_baton_t *fb = processor->baton;
2873
2874 SVN_ERR(fb->processor->dir_closed(relpath,
2875 left_source,
2876 right_source,
2877 dir_baton,
2878 fb->processor,
2879 scratch_pool));
2880 return SVN_NO_ERROR;
2881 }
2882
2883 static svn_error_t *
filter_dir_closed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2884 filter_dir_closed(const char *relpath,
2885 const svn_diff_source_t *left_source,
2886 const svn_diff_source_t *right_source,
2887 void *dir_baton,
2888 const svn_diff_tree_processor_t *processor,
2889 apr_pool_t *scratch_pool)
2890 {
2891 struct filter_tree_baton_t *fb = processor->baton;
2892
2893 SVN_ERR(fb->processor->dir_closed(relpath,
2894 left_source,
2895 right_source,
2896 dir_baton,
2897 fb->processor,
2898 scratch_pool));
2899 return SVN_NO_ERROR;
2900 }
2901
2902 static svn_error_t *
filter_file_opened(void ** new_file_baton,svn_boolean_t * skip,const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const svn_diff_source_t * copyfrom_source,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2903 filter_file_opened(void **new_file_baton,
2904 svn_boolean_t *skip,
2905 const char *relpath,
2906 const svn_diff_source_t *left_source,
2907 const svn_diff_source_t *right_source,
2908 const svn_diff_source_t *copyfrom_source,
2909 void *dir_baton,
2910 const svn_diff_tree_processor_t *processor,
2911 apr_pool_t *result_pool,
2912 apr_pool_t *scratch_pool)
2913 {
2914 struct filter_tree_baton_t *fb = processor->baton;
2915 const char *local_abspath
2916 = svn_dirent_join(fb->root_local_abspath, relpath, scratch_pool);
2917
2918 /* Skip if not a member of a given changelist */
2919 if (! svn_wc__changelist_match(fb->wc_ctx, local_abspath,
2920 fb->changelist_hash, scratch_pool))
2921 {
2922 *skip = TRUE;
2923 return SVN_NO_ERROR;
2924 }
2925
2926 SVN_ERR(fb->processor->file_opened(new_file_baton,
2927 skip,
2928 relpath,
2929 left_source,
2930 right_source,
2931 copyfrom_source,
2932 dir_baton,
2933 fb->processor,
2934 result_pool,
2935 scratch_pool));
2936 return SVN_NO_ERROR;
2937 }
2938
2939 static svn_error_t *
filter_file_added(const char * relpath,const svn_diff_source_t * copyfrom_source,const svn_diff_source_t * right_source,const char * copyfrom_file,const char * right_file,apr_hash_t * copyfrom_props,apr_hash_t * right_props,void * file_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2940 filter_file_added(const char *relpath,
2941 const svn_diff_source_t *copyfrom_source,
2942 const svn_diff_source_t *right_source,
2943 const char *copyfrom_file,
2944 const char *right_file,
2945 /*const*/ apr_hash_t *copyfrom_props,
2946 /*const*/ apr_hash_t *right_props,
2947 void *file_baton,
2948 const svn_diff_tree_processor_t *processor,
2949 apr_pool_t *scratch_pool)
2950 {
2951 struct filter_tree_baton_t *fb = processor->baton;
2952
2953 SVN_ERR(fb->processor->file_added(relpath,
2954 copyfrom_source,
2955 right_source,
2956 copyfrom_file,
2957 right_file,
2958 copyfrom_props,
2959 right_props,
2960 file_baton,
2961 fb->processor,
2962 scratch_pool));
2963 return SVN_NO_ERROR;
2964 }
2965
2966 static svn_error_t *
filter_file_deleted(const char * relpath,const svn_diff_source_t * left_source,const char * left_file,apr_hash_t * left_props,void * file_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2967 filter_file_deleted(const char *relpath,
2968 const svn_diff_source_t *left_source,
2969 const char *left_file,
2970 /*const*/ apr_hash_t *left_props,
2971 void *file_baton,
2972 const svn_diff_tree_processor_t *processor,
2973 apr_pool_t *scratch_pool)
2974 {
2975 struct filter_tree_baton_t *fb = processor->baton;
2976
2977 SVN_ERR(fb->processor->file_deleted(relpath,
2978 left_source,
2979 left_file,
2980 left_props,
2981 file_baton,
2982 fb->processor,
2983 scratch_pool));
2984
2985 return SVN_NO_ERROR;
2986 }
2987
2988 static svn_error_t *
filter_file_changed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const char * left_file,const char * right_file,apr_hash_t * left_props,apr_hash_t * right_props,svn_boolean_t file_modified,const apr_array_header_t * prop_changes,void * file_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2989 filter_file_changed(const char *relpath,
2990 const svn_diff_source_t *left_source,
2991 const svn_diff_source_t *right_source,
2992 const char *left_file,
2993 const char *right_file,
2994 /*const*/ apr_hash_t *left_props,
2995 /*const*/ apr_hash_t *right_props,
2996 svn_boolean_t file_modified,
2997 const apr_array_header_t *prop_changes,
2998 void *file_baton,
2999 const svn_diff_tree_processor_t *processor,
3000 apr_pool_t *scratch_pool)
3001 {
3002 struct filter_tree_baton_t *fb = processor->baton;
3003
3004 SVN_ERR(fb->processor->file_changed(relpath,
3005 left_source,
3006 right_source,
3007 left_file,
3008 right_file,
3009 left_props,
3010 right_props,
3011 file_modified,
3012 prop_changes,
3013 file_baton,
3014 fb->processor,
3015 scratch_pool));
3016 return SVN_NO_ERROR;
3017 }
3018
3019 static svn_error_t *
filter_file_closed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,void * file_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3020 filter_file_closed(const char *relpath,
3021 const svn_diff_source_t *left_source,
3022 const svn_diff_source_t *right_source,
3023 void *file_baton,
3024 const svn_diff_tree_processor_t *processor,
3025 apr_pool_t *scratch_pool)
3026 {
3027 struct filter_tree_baton_t *fb = processor->baton;
3028
3029 SVN_ERR(fb->processor->file_closed(relpath,
3030 left_source,
3031 right_source,
3032 file_baton,
3033 fb->processor,
3034 scratch_pool));
3035
3036 return SVN_NO_ERROR;
3037 }
3038
3039 static svn_error_t *
filter_node_absent(const char * relpath,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3040 filter_node_absent(const char *relpath,
3041 void *dir_baton,
3042 const svn_diff_tree_processor_t *processor,
3043 apr_pool_t *scratch_pool)
3044 {
3045 struct filter_tree_baton_t *fb = processor->baton;
3046
3047 SVN_ERR(fb->processor->node_absent(relpath,
3048 dir_baton,
3049 fb->processor,
3050 scratch_pool));
3051 return SVN_NO_ERROR;
3052 }
3053
3054 const svn_diff_tree_processor_t *
svn_wc__changelist_filter_tree_processor_create(const svn_diff_tree_processor_t * processor,svn_wc_context_t * wc_ctx,const char * root_local_abspath,apr_hash_t * changelist_hash,apr_pool_t * result_pool)3055 svn_wc__changelist_filter_tree_processor_create(
3056 const svn_diff_tree_processor_t *processor,
3057 svn_wc_context_t *wc_ctx,
3058 const char *root_local_abspath,
3059 apr_hash_t *changelist_hash,
3060 apr_pool_t *result_pool)
3061 {
3062 struct filter_tree_baton_t *fb;
3063 svn_diff_tree_processor_t *filter;
3064
3065 if (! changelist_hash)
3066 return processor;
3067
3068 fb = apr_pcalloc(result_pool, sizeof(*fb));
3069 fb->processor = processor;
3070 fb->wc_ctx = wc_ctx;
3071 fb->root_local_abspath = root_local_abspath;
3072 fb->changelist_hash = changelist_hash;
3073
3074 filter = svn_diff__tree_processor_create(fb, result_pool);
3075 filter->dir_opened = filter_dir_opened;
3076 filter->dir_added = filter_dir_added;
3077 filter->dir_deleted = filter_dir_deleted;
3078 filter->dir_changed = filter_dir_changed;
3079 filter->dir_closed = filter_dir_closed;
3080
3081 filter->file_opened = filter_file_opened;
3082 filter->file_added = filter_file_added;
3083 filter->file_deleted = filter_file_deleted;
3084 filter->file_changed = filter_file_changed;
3085 filter->file_closed = filter_file_closed;
3086
3087 filter->node_absent = filter_node_absent;
3088
3089 return filter;
3090 }
3091
3092