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