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