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