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