1 /*
2 * diff_local.c: comparing local trees with each other
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 /* ==================================================================== */
25
26
27
28 /*** Includes. ***/
29
30 #include <apr_strings.h>
31 #include <apr_pools.h>
32 #include <apr_hash.h>
33 #include "svn_hash.h"
34 #include "svn_types.h"
35 #include "svn_wc.h"
36 #include "svn_diff.h"
37 #include "svn_client.h"
38 #include "svn_string.h"
39 #include "svn_error.h"
40 #include "svn_dirent_uri.h"
41 #include "svn_io.h"
42 #include "svn_pools.h"
43 #include "svn_props.h"
44 #include "svn_sorts.h"
45 #include "svn_subst.h"
46 #include "client.h"
47
48 #include "private/svn_sorts_private.h"
49 #include "private/svn_wc_private.h"
50 #include "private/svn_diff_tree.h"
51
52 #include "svn_private_config.h"
53
54
55 /* Try to get properties for LOCAL_ABSPATH and return them in the property
56 * hash *PROPS. If there are no properties because LOCAL_ABSPATH is not
57 * versioned, return an empty property hash. */
58 static svn_error_t *
get_props(apr_hash_t ** props,const char * local_abspath,svn_wc_context_t * wc_ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)59 get_props(apr_hash_t **props,
60 const char *local_abspath,
61 svn_wc_context_t *wc_ctx,
62 apr_pool_t *result_pool,
63 apr_pool_t *scratch_pool)
64 {
65 svn_error_t *err;
66
67 err = svn_wc_prop_list2(props, wc_ctx, local_abspath, result_pool,
68 scratch_pool);
69 if (err)
70 {
71 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
72 err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
73 {
74 svn_error_clear(err);
75 *props = apr_hash_make(result_pool);
76
77 /* ### Apply autoprops, like 'svn add' would? */
78 }
79 else
80 return svn_error_trace(err);
81 }
82
83 return SVN_NO_ERROR;
84 }
85
86 /* Forward declaration */
87 static svn_error_t *
88 do_file_diff(const char *left_abspath,
89 const char *right_abspath,
90 const char *left_root_abspath,
91 const char *right_root_abspath,
92 svn_boolean_t left_only,
93 svn_boolean_t right_only,
94 void *parent_baton,
95 const svn_diff_tree_processor_t *diff_processor,
96 svn_client_ctx_t *ctx,
97 apr_pool_t *scratch_pool);
98
99 /* Forward declaration */
100 static svn_error_t *
101 do_dir_diff(const char *left_abspath,
102 const char *right_abspath,
103 const char *left_root_abspath,
104 const char *right_root_abspath,
105 svn_boolean_t left_only,
106 svn_boolean_t right_only,
107 svn_boolean_t left_before_right,
108 svn_depth_t depth,
109 void *parent_baton,
110 const svn_diff_tree_processor_t *diff_processor,
111 svn_client_ctx_t *ctx,
112 apr_pool_t *scratch_pool);
113
114 /* Produce a diff of depth DEPTH between two arbitrary directories at
115 * LEFT_ABSPATH1 and RIGHT_ABSPATH2, using the provided diff callbacks
116 * to show file changes and, for versioned nodes, property changes.
117 *
118 * Report paths as relative from LEFT_ROOT_ABSPATH/RIGHT_ROOT_ABSPATH.
119 *
120 * If LEFT_ONLY is TRUE, only the left source exists (= everything will
121 * be reported as deleted). If RIGHT_ONLY is TRUE, only the right source
122 * exists (= everything will be reported as added).
123 *
124 * If LEFT_BEFORE_RIGHT is TRUE and left and right are unrelated, left is
125 * reported first. If false, right is reported first. (This is to allow
126 * producing a proper inverse diff).
127 *
128 * Walk the sources according to depth, and report with parent baton
129 * PARENT_BATON. */
130 static svn_error_t *
inner_dir_diff(const char * left_abspath,const char * right_abspath,const char * left_root_abspath,const char * right_root_abspath,svn_boolean_t left_only,svn_boolean_t right_only,svn_boolean_t left_before_right,svn_depth_t depth,void * parent_baton,const svn_diff_tree_processor_t * diff_processor,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)131 inner_dir_diff(const char *left_abspath,
132 const char *right_abspath,
133 const char *left_root_abspath,
134 const char *right_root_abspath,
135 svn_boolean_t left_only,
136 svn_boolean_t right_only,
137 svn_boolean_t left_before_right,
138 svn_depth_t depth,
139 void *parent_baton,
140 const svn_diff_tree_processor_t *diff_processor,
141 svn_client_ctx_t *ctx,
142 apr_pool_t *scratch_pool)
143 {
144 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
145 apr_hash_t *left_dirents;
146 apr_hash_t *right_dirents;
147 apr_array_header_t *sorted_dirents;
148 svn_error_t *err;
149 svn_depth_t depth_below_here;
150 int i;
151
152 SVN_ERR_ASSERT(depth >= svn_depth_files && depth <= svn_depth_infinity);
153
154 if (!right_only)
155 {
156 err = svn_io_get_dirents3(&left_dirents, left_abspath, FALSE,
157 scratch_pool, iterpool);
158
159 if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
160 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
161 {
162 svn_error_clear(err);
163 left_dirents = apr_hash_make(scratch_pool);
164 right_only = TRUE;
165 }
166 else
167 SVN_ERR(err);
168 }
169 else
170 left_dirents = apr_hash_make(scratch_pool);
171
172 if (!left_only)
173 {
174 err = svn_io_get_dirents3(&right_dirents, right_abspath, FALSE,
175 scratch_pool, iterpool);
176
177 if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
178 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
179 {
180 svn_error_clear(err);
181 right_dirents = apr_hash_make(scratch_pool);
182 left_only = TRUE;
183 }
184 else
185 SVN_ERR(err);
186 }
187 else
188 right_dirents = apr_hash_make(scratch_pool);
189
190 if (left_only && right_only)
191 return SVN_NO_ERROR; /* Somebody deleted the directory?? */
192
193 if (depth != svn_depth_infinity)
194 depth_below_here = svn_depth_empty;
195 else
196 depth_below_here = svn_depth_infinity;
197
198 sorted_dirents = svn_sort__hash(apr_hash_merge(iterpool, left_dirents,
199 right_dirents, NULL, NULL),
200 svn_sort_compare_items_as_paths,
201 scratch_pool);
202
203 for (i = 0; i < sorted_dirents->nelts; i++)
204 {
205 svn_sort__item_t* elt = &APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t);
206 svn_io_dirent2_t *left_dirent;
207 svn_io_dirent2_t *right_dirent;
208 const char *child_left_abspath;
209 const char *child_right_abspath;
210
211 svn_pool_clear(iterpool);
212
213 if (ctx->cancel_func)
214 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
215
216 if (svn_wc_is_adm_dir(elt->key, iterpool))
217 continue;
218
219 left_dirent = right_only ? NULL : svn_hash_gets(left_dirents, elt->key);
220 right_dirent = left_only ? NULL : svn_hash_gets(right_dirents, elt->key);
221
222 child_left_abspath = svn_dirent_join(left_abspath, elt->key, iterpool);
223 child_right_abspath = svn_dirent_join(right_abspath, elt->key, iterpool);
224
225 if (((left_dirent == NULL) != (right_dirent == NULL))
226 || (left_dirent->kind != right_dirent->kind))
227 {
228 /* Report delete and/or add */
229 if (left_dirent && left_before_right)
230 {
231 if (left_dirent->kind == svn_node_file)
232 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
233 left_root_abspath, right_root_abspath,
234 TRUE, FALSE, parent_baton,
235 diff_processor, ctx, iterpool));
236 else if (depth >= svn_depth_immediates)
237 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
238 left_root_abspath, right_root_abspath,
239 TRUE, FALSE, left_before_right,
240 depth_below_here, parent_baton,
241 diff_processor, ctx, iterpool));
242 }
243
244 if (right_dirent)
245 {
246 if (right_dirent->kind == svn_node_file)
247 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
248 left_root_abspath, right_root_abspath,
249 FALSE, TRUE, parent_baton,
250 diff_processor, ctx, iterpool));
251 else if (depth >= svn_depth_immediates)
252 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
253 left_root_abspath, right_root_abspath,
254 FALSE, TRUE, left_before_right,
255 depth_below_here, parent_baton,
256 diff_processor, ctx, iterpool));
257 }
258
259 if (left_dirent && !left_before_right)
260 {
261 if (left_dirent->kind == svn_node_file)
262 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
263 left_root_abspath, right_root_abspath,
264 TRUE, FALSE, parent_baton,
265 diff_processor, ctx, iterpool));
266 else if (depth >= svn_depth_immediates)
267 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
268 left_root_abspath, right_root_abspath,
269 TRUE, FALSE, left_before_right,
270 depth_below_here, parent_baton,
271 diff_processor, ctx, iterpool));
272 }
273 }
274 else if (left_dirent->kind == svn_node_file)
275 {
276 /* Perform file-file diff */
277 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
278 left_root_abspath, right_root_abspath,
279 FALSE, FALSE, parent_baton,
280 diff_processor, ctx, iterpool));
281 }
282 else if (depth >= svn_depth_immediates)
283 {
284 /* Perform dir-dir diff */
285 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
286 left_root_abspath, right_root_abspath,
287 FALSE, FALSE, left_before_right,
288 depth_below_here, parent_baton,
289 diff_processor, ctx, iterpool));
290 }
291 }
292
293 return SVN_NO_ERROR;
294 }
295
296 /* Translates *LEFT_ABSPATH to a temporary file if PROPS specify that the
297 file needs translation. *LEFT_ABSPATH is updated to point to a file that
298 lives at least as long as RESULT_POOL when translation is necessary.
299 Otherwise the value is not updated */
300 static svn_error_t *
translate_if_necessary(const char ** local_abspath,apr_hash_t * props,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)301 translate_if_necessary(const char **local_abspath,
302 apr_hash_t *props,
303 svn_cancel_func_t cancel_func,
304 void *cancel_baton,
305 apr_pool_t *result_pool,
306 apr_pool_t *scratch_pool)
307 {
308 const svn_string_t *eol_style_val;
309 const svn_string_t *keywords_val;
310 svn_subst_eol_style_t eol_style;
311 const char *eol;
312 apr_hash_t *keywords;
313 svn_stream_t *contents;
314 svn_stream_t *dst;
315
316 /* if (svn_hash_gets(props, SVN_PROP_SPECIAL))
317 ### TODO: Implement */
318
319 eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
320 keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS);
321
322 if (eol_style_val)
323 svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data);
324 else
325 {
326 eol = NULL;
327 eol_style = svn_subst_eol_style_none;
328 }
329
330 if (keywords_val)
331 SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data,
332 APR_STRINGIFY(SVN_INVALID_REVNUM),
333 "", "", 0, "", scratch_pool));
334 else
335 keywords = NULL;
336
337 if (!svn_subst_translation_required(eol_style, eol, keywords, FALSE, FALSE))
338 return SVN_NO_ERROR;
339
340 SVN_ERR(svn_stream_open_readonly(&contents, *local_abspath,
341 scratch_pool, scratch_pool));
342
343 SVN_ERR(svn_stream_open_unique(&dst, local_abspath, NULL,
344 svn_io_file_del_on_pool_cleanup,
345 result_pool, scratch_pool));
346
347 dst = svn_subst_stream_translated(dst, eol, TRUE /* repair */,
348 keywords, FALSE /* expand */,
349 scratch_pool);
350
351 SVN_ERR(svn_stream_copy3(contents, dst, cancel_func, cancel_baton,
352 scratch_pool));
353
354 return SVN_NO_ERROR;
355 }
356
357 /* Handles reporting of a file for inner_dir_diff */
358 static svn_error_t *
do_file_diff(const char * left_abspath,const char * right_abspath,const char * left_root_abspath,const char * right_root_abspath,svn_boolean_t left_only,svn_boolean_t right_only,void * parent_baton,const svn_diff_tree_processor_t * diff_processor,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)359 do_file_diff(const char *left_abspath,
360 const char *right_abspath,
361 const char *left_root_abspath,
362 const char *right_root_abspath,
363 svn_boolean_t left_only,
364 svn_boolean_t right_only,
365 void *parent_baton,
366 const svn_diff_tree_processor_t *diff_processor,
367 svn_client_ctx_t *ctx,
368 apr_pool_t *scratch_pool)
369 {
370 const char *relpath;
371 svn_diff_source_t *left_source;
372 svn_diff_source_t *right_source;
373 svn_boolean_t skip = FALSE;
374 apr_hash_t *left_props;
375 apr_hash_t *right_props;
376 void *file_baton;
377
378 relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath);
379
380 if (! right_only)
381 left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
382 else
383 left_source = NULL;
384
385 if (! left_only)
386 right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
387 else
388 right_source = NULL;
389
390 SVN_ERR(diff_processor->file_opened(&file_baton, &skip,
391 relpath,
392 left_source,
393 right_source,
394 NULL /* copyfrom_source */,
395 parent_baton,
396 diff_processor,
397 scratch_pool,
398 scratch_pool));
399
400 if (skip)
401 return SVN_NO_ERROR;
402
403 if (! right_only)
404 {
405 SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx,
406 scratch_pool, scratch_pool));
407
408 /* We perform a mimetype detection to avoid diffing binary files
409 for textual changes.*/
410 if (! svn_hash_gets(left_props, SVN_PROP_MIME_TYPE))
411 {
412 const char *mime_type;
413
414 /* ### Use libmagic magic? */
415 SVN_ERR(svn_io_detect_mimetype2(&mime_type, left_abspath,
416 ctx->mimetypes_map, scratch_pool));
417
418 if (mime_type)
419 svn_hash_sets(left_props, SVN_PROP_MIME_TYPE,
420 svn_string_create(mime_type, scratch_pool));
421 }
422
423 SVN_ERR(translate_if_necessary(&left_abspath, left_props,
424 ctx->cancel_func, ctx->cancel_baton,
425 scratch_pool, scratch_pool));
426 }
427 else
428 left_props = NULL;
429
430 if (! left_only)
431 {
432 SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx,
433 scratch_pool, scratch_pool));
434
435 /* We perform a mimetype detection to avoid diffing binary files
436 for textual changes.*/
437 if (! svn_hash_gets(right_props, SVN_PROP_MIME_TYPE))
438 {
439 const char *mime_type;
440
441 /* ### Use libmagic magic? */
442 SVN_ERR(svn_io_detect_mimetype2(&mime_type, right_abspath,
443 ctx->mimetypes_map, scratch_pool));
444
445 if (mime_type)
446 svn_hash_sets(right_props, SVN_PROP_MIME_TYPE,
447 svn_string_create(mime_type, scratch_pool));
448 }
449
450 SVN_ERR(translate_if_necessary(&right_abspath, right_props,
451 ctx->cancel_func, ctx->cancel_baton,
452 scratch_pool, scratch_pool));
453
454 }
455 else
456 right_props = NULL;
457
458 if (left_only)
459 {
460 SVN_ERR(diff_processor->file_deleted(relpath,
461 left_source,
462 left_abspath,
463 left_props,
464 file_baton,
465 diff_processor,
466 scratch_pool));
467 }
468 else if (right_only)
469 {
470 SVN_ERR(diff_processor->file_added(relpath,
471 NULL /* copyfrom_source */,
472 right_source,
473 NULL /* copyfrom_file */,
474 right_abspath,
475 NULL /* copyfrom_props */,
476 right_props,
477 file_baton,
478 diff_processor,
479 scratch_pool));
480 }
481 else
482 {
483 /* ### Perform diff -> close/changed */
484 svn_boolean_t same;
485 apr_array_header_t *prop_changes;
486
487 SVN_ERR(svn_io_files_contents_same_p(&same, left_abspath, right_abspath,
488 scratch_pool));
489
490 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props,
491 scratch_pool));
492
493 if (!same || prop_changes->nelts > 0)
494 {
495 SVN_ERR(diff_processor->file_changed(relpath,
496 left_source,
497 right_source,
498 same ? NULL : left_abspath,
499 same ? NULL : right_abspath,
500 left_props,
501 right_props,
502 !same,
503 prop_changes,
504 file_baton,
505 diff_processor,
506 scratch_pool));
507 }
508 else
509 {
510 SVN_ERR(diff_processor->file_closed(relpath,
511 left_source,
512 right_source,
513 file_baton,
514 diff_processor,
515 scratch_pool));
516 }
517 }
518 return SVN_NO_ERROR;
519 }
520
521
522 /* Handles reporting of a directory and its children for inner_dir_diff */
523 static svn_error_t *
do_dir_diff(const char * left_abspath,const char * right_abspath,const char * left_root_abspath,const char * right_root_abspath,svn_boolean_t left_only,svn_boolean_t right_only,svn_boolean_t left_before_right,svn_depth_t depth,void * parent_baton,const svn_diff_tree_processor_t * diff_processor,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)524 do_dir_diff(const char *left_abspath,
525 const char *right_abspath,
526 const char *left_root_abspath,
527 const char *right_root_abspath,
528 svn_boolean_t left_only,
529 svn_boolean_t right_only,
530 svn_boolean_t left_before_right,
531 svn_depth_t depth,
532 void *parent_baton,
533 const svn_diff_tree_processor_t *diff_processor,
534 svn_client_ctx_t *ctx,
535 apr_pool_t *scratch_pool)
536 {
537 const char *relpath;
538 svn_diff_source_t *left_source;
539 svn_diff_source_t *right_source;
540 svn_boolean_t skip = FALSE;
541 svn_boolean_t skip_children = FALSE;
542 void *dir_baton;
543 apr_hash_t *left_props;
544 apr_hash_t *right_props;
545
546 relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath);
547
548 if (! right_only)
549 {
550 left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
551 SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx,
552 scratch_pool, scratch_pool));
553 }
554 else
555 {
556 left_source = NULL;
557 left_props = NULL;
558 }
559
560 if (! left_only)
561 {
562 right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
563 SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx,
564 scratch_pool, scratch_pool));
565 }
566 else
567 {
568 right_source = NULL;
569 right_props = NULL;
570 }
571
572 SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children,
573 relpath,
574 left_source,
575 right_source,
576 NULL /* copyfrom_source */,
577 parent_baton,
578 diff_processor,
579 scratch_pool, scratch_pool));
580
581 if (!skip_children)
582 {
583 if (depth >= svn_depth_files)
584 SVN_ERR(inner_dir_diff(left_abspath, right_abspath,
585 left_root_abspath, right_root_abspath,
586 left_only, right_only,
587 left_before_right, depth,
588 dir_baton,
589 diff_processor, ctx, scratch_pool));
590 }
591 else if (skip)
592 return SVN_NO_ERROR;
593
594 if (left_props && right_props)
595 {
596 apr_array_header_t *prop_diffs;
597
598 SVN_ERR(svn_prop_diffs(&prop_diffs, right_props, left_props,
599 scratch_pool));
600
601 if (prop_diffs->nelts)
602 {
603 SVN_ERR(diff_processor->dir_changed(relpath,
604 left_source,
605 right_source,
606 left_props,
607 right_props,
608 prop_diffs,
609 dir_baton,
610 diff_processor,
611 scratch_pool));
612 return SVN_NO_ERROR;
613 }
614 }
615
616 if (left_source && right_source)
617 {
618 SVN_ERR(diff_processor->dir_closed(relpath,
619 left_source,
620 right_source,
621 dir_baton,
622 diff_processor,
623 scratch_pool));
624 }
625 else if (left_source)
626 {
627 SVN_ERR(diff_processor->dir_deleted(relpath,
628 left_source,
629 left_props,
630 dir_baton,
631 diff_processor,
632 scratch_pool));
633 }
634 else
635 {
636 SVN_ERR(diff_processor->dir_added(relpath,
637 NULL /* copyfrom_source */,
638 right_source,
639 NULL /* copyfrom_props */,
640 right_props,
641 dir_baton,
642 diff_processor,
643 scratch_pool));
644 }
645
646 return SVN_NO_ERROR;
647 }
648
649 svn_error_t *
svn_client__arbitrary_nodes_diff(const char * left_abspath,const char * right_abspath,svn_depth_t depth,const svn_diff_tree_processor_t * diff_processor,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)650 svn_client__arbitrary_nodes_diff(const char *left_abspath,
651 const char *right_abspath,
652 svn_depth_t depth,
653 const svn_diff_tree_processor_t *diff_processor,
654 svn_client_ctx_t *ctx,
655 apr_pool_t *scratch_pool)
656 {
657 svn_node_kind_t left_kind;
658 svn_node_kind_t right_kind;
659 const char *left_root_abspath = left_abspath;
660 const char *right_root_abspath = right_abspath;
661 svn_boolean_t left_before_right = TRUE; /* Future argument? */
662
663 if (depth == svn_depth_unknown)
664 depth = svn_depth_infinity;
665
666 SVN_ERR(svn_io_check_resolved_path(left_abspath, &left_kind, scratch_pool));
667 SVN_ERR(svn_io_check_resolved_path(right_abspath, &right_kind, scratch_pool));
668
669 if (left_kind == svn_node_dir && right_kind == svn_node_dir)
670 {
671 SVN_ERR(do_dir_diff(left_abspath, right_abspath,
672 left_root_abspath, right_root_abspath,
673 FALSE, FALSE, left_before_right,
674 depth, NULL /* parent_baton */,
675 diff_processor, ctx, scratch_pool));
676 }
677 else if (left_kind == svn_node_file && right_kind == svn_node_file)
678 {
679 SVN_ERR(do_file_diff(left_abspath, right_abspath,
680 left_root_abspath, right_root_abspath,
681 FALSE, FALSE,
682 NULL /* parent_baton */,
683 diff_processor, ctx, scratch_pool));
684 }
685 else if (left_kind == svn_node_file || left_kind == svn_node_dir
686 || right_kind == svn_node_file || right_kind == svn_node_dir)
687 {
688 /* The root is added/deleted/replaced. Report delete and/or add. */
689 if (left_before_right)
690 {
691 if (left_kind == svn_node_file)
692 SVN_ERR(do_file_diff(left_abspath, right_abspath,
693 left_root_abspath, right_root_abspath,
694 TRUE, FALSE, NULL /* parent_baton */,
695 diff_processor, ctx, scratch_pool));
696 else if (left_kind == svn_node_dir)
697 SVN_ERR(do_dir_diff(left_abspath, right_abspath,
698 left_root_abspath, right_root_abspath,
699 TRUE, FALSE, left_before_right,
700 depth, NULL /* parent_baton */,
701 diff_processor, ctx, scratch_pool));
702 }
703
704 if (right_kind == svn_node_file)
705 SVN_ERR(do_file_diff(left_abspath, right_abspath,
706 left_root_abspath, right_root_abspath,
707 FALSE, TRUE, NULL /* parent_baton */,
708 diff_processor, ctx, scratch_pool));
709 else if (right_kind == svn_node_dir)
710 SVN_ERR(do_dir_diff(left_abspath, right_abspath,
711 left_root_abspath, right_root_abspath,
712 FALSE, TRUE, left_before_right,
713 depth, NULL /* parent_baton */,
714 diff_processor, ctx, scratch_pool));
715
716 if (! left_before_right)
717 {
718 if (left_kind == svn_node_file)
719 SVN_ERR(do_file_diff(left_abspath, right_abspath,
720 left_root_abspath, right_root_abspath,
721 TRUE, FALSE, NULL /* parent_baton */,
722 diff_processor, ctx, scratch_pool));
723 else if (left_kind == svn_node_dir)
724 SVN_ERR(do_dir_diff(left_abspath, right_abspath,
725 left_root_abspath, right_root_abspath,
726 TRUE, FALSE, left_before_right,
727 depth, NULL /* parent_baton */,
728 diff_processor, ctx, scratch_pool));
729 }
730 }
731 else
732 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
733 _("'%s' is not a file or directory"),
734 svn_dirent_local_style(
735 (left_kind == svn_node_none)
736 ? left_abspath
737 : right_abspath,
738 scratch_pool));
739
740 return SVN_NO_ERROR;
741 }
742