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