]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_client/diff_local.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_client / diff_local.c
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 \f
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 \f
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 *
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 *
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           right_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 *
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 *
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 *
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 *
650 svn_client__arbitrary_nodes_diff(const char **root_relpath,
651                                  svn_boolean_t *root_is_dir,
652                                  const char *left_abspath,
653                                  const char *right_abspath,
654                                  svn_depth_t depth,
655                                  const svn_diff_tree_processor_t *diff_processor,
656                                  svn_client_ctx_t *ctx,
657                                  apr_pool_t *result_pool,
658                                  apr_pool_t *scratch_pool)
659 {
660   svn_node_kind_t left_kind;
661   svn_node_kind_t right_kind;
662   const char *left_root_abspath;
663   const char *right_root_abspath;
664   svn_boolean_t left_before_right = TRUE; /* Future argument? */
665
666   if (depth == svn_depth_unknown)
667     depth = svn_depth_infinity;
668
669   SVN_ERR(svn_io_check_resolved_path(left_abspath, &left_kind, scratch_pool));
670   SVN_ERR(svn_io_check_resolved_path(right_abspath, &right_kind, scratch_pool));
671
672   if (depth == svn_depth_unknown)
673     depth = svn_depth_infinity;
674
675   if (left_kind == svn_node_dir && right_kind == svn_node_dir)
676     {
677       left_root_abspath = left_abspath;
678       right_root_abspath = right_abspath;
679
680       if (root_relpath)
681         *root_relpath = "";
682       if (root_is_dir)
683         *root_is_dir = TRUE;
684     }
685   else
686     {
687       svn_dirent_split(&left_root_abspath, root_relpath, left_abspath,
688                        scratch_pool);
689       right_root_abspath = svn_dirent_dirname(right_abspath, scratch_pool);
690
691       if (root_relpath)
692         *root_relpath = apr_pstrdup(result_pool, *root_relpath);
693       if (root_is_dir)
694         *root_is_dir = FALSE;
695     }
696
697   if (left_kind == svn_node_dir && right_kind == svn_node_dir)
698     {
699       SVN_ERR(do_dir_diff(left_abspath, right_abspath,
700                           left_root_abspath, right_root_abspath,
701                           FALSE, FALSE, left_before_right,
702                           depth, NULL /* parent_baton */,
703                           diff_processor, ctx, scratch_pool));
704     }
705   else if (left_kind == svn_node_file && right_kind == svn_node_file)
706     {
707       SVN_ERR(do_file_diff(left_abspath, right_abspath,
708                            left_root_abspath, right_root_abspath,
709                            FALSE, FALSE,
710                            NULL /* parent_baton */,
711                            diff_processor, ctx, scratch_pool));
712     }
713   else if (left_kind == svn_node_file || left_kind == svn_node_dir
714            || right_kind == svn_node_file || right_kind == svn_node_dir)
715     {
716       void *dir_baton;
717       svn_boolean_t skip = FALSE;
718       svn_boolean_t skip_children = FALSE;
719       svn_diff_source_t *left_src;
720       svn_diff_source_t *right_src;
721
722       left_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
723       right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
724
725       /* The root is replaced... */
726       /* Report delete and/or add */
727
728       SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, "",
729                                          left_src,
730                                          right_src,
731                                          NULL /* copyfrom_src */,
732                                          NULL,
733                                          diff_processor,
734                                          scratch_pool, scratch_pool));
735
736       if (skip)
737         return SVN_NO_ERROR;
738       else if (!skip_children)
739         {
740           if (left_before_right)
741             {
742               if (left_kind == svn_node_file)
743                 SVN_ERR(do_file_diff(left_abspath, right_abspath,
744                                      left_root_abspath, right_root_abspath,
745                                      TRUE, FALSE, NULL /* parent_baton */,
746                                      diff_processor, ctx, scratch_pool));
747               else if (left_kind == svn_node_dir)
748                 SVN_ERR(do_dir_diff(left_abspath, right_abspath,
749                                     left_root_abspath, right_root_abspath,
750                                     TRUE, FALSE, left_before_right,
751                                     depth, NULL /* parent_baton */,
752                                     diff_processor, ctx, scratch_pool));
753             }
754
755           if (right_kind == svn_node_file)
756             SVN_ERR(do_file_diff(left_abspath, right_abspath,
757                                  left_root_abspath, right_root_abspath,
758                                  FALSE, TRUE, NULL /* parent_baton */,
759                                  diff_processor, ctx, scratch_pool));
760           else if (right_kind == svn_node_dir)
761             SVN_ERR(do_dir_diff(left_abspath, right_abspath,
762                                 left_root_abspath, right_root_abspath,
763                                 FALSE, TRUE,  left_before_right,
764                                 depth, NULL /* parent_baton */,
765                                 diff_processor, ctx, scratch_pool));
766
767           if (! left_before_right)
768             {
769               if (left_kind == svn_node_file)
770                 SVN_ERR(do_file_diff(left_abspath, right_abspath,
771                                      left_root_abspath, right_root_abspath,
772                                      TRUE, FALSE, NULL /* parent_baton */,
773                                      diff_processor, ctx, scratch_pool));
774               else if (left_kind == svn_node_dir)
775                 SVN_ERR(do_dir_diff(left_abspath, right_abspath,
776                                     left_root_abspath, right_root_abspath,
777                                     TRUE, FALSE,  left_before_right,
778                                     depth, NULL /* parent_baton */,
779                                     diff_processor, ctx, scratch_pool));
780             }
781         }
782
783       SVN_ERR(diff_processor->dir_closed("",
784                                          left_src,
785                                          right_src,
786                                          dir_baton,
787                                          diff_processor,
788                                          scratch_pool));
789     }
790   else
791     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
792                              _("'%s' is not a file or directory"),
793                              svn_dirent_local_style(
794                                     (left_kind == svn_node_none)
795                                         ? left_abspath
796                                         : right_abspath,
797                                     scratch_pool));
798
799   return SVN_NO_ERROR;
800 }