1251881Speter/*
2251881Speter * diff_editor.c -- The diff editor for comparing the working copy against the
3251881Speter *                  repository.
4251881Speter *
5251881Speter * ====================================================================
6251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
7251881Speter *    or more contributor license agreements.  See the NOTICE file
8251881Speter *    distributed with this work for additional information
9251881Speter *    regarding copyright ownership.  The ASF licenses this file
10251881Speter *    to you under the Apache License, Version 2.0 (the
11251881Speter *    "License"); you may not use this file except in compliance
12251881Speter *    with the License.  You may obtain a copy of the License at
13251881Speter *
14251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
15251881Speter *
16251881Speter *    Unless required by applicable law or agreed to in writing,
17251881Speter *    software distributed under the License is distributed on an
18251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19251881Speter *    KIND, either express or implied.  See the License for the
20251881Speter *    specific language governing permissions and limitations
21251881Speter *    under the License.
22251881Speter * ====================================================================
23251881Speter */
24251881Speter
25251881Speter/*
26251881Speter * This code uses an svn_delta_editor_t editor driven by
27251881Speter * svn_wc_crawl_revisions (like the update command) to retrieve the
28251881Speter * differences between the working copy and the requested repository
29251881Speter * version. Rather than updating the working copy, this new editor creates
30251881Speter * temporary files that contain the pristine repository versions. When the
31251881Speter * crawler closes the files the editor calls back to a client layer
32251881Speter * function to compare the working copy and the temporary file. There is
33251881Speter * only ever one temporary file in existence at any time.
34251881Speter *
35251881Speter * When the crawler closes a directory, the editor then calls back to the
36251881Speter * client layer to compare any remaining files that may have been modified
37251881Speter * locally. Added directories do not have corresponding temporary
38251881Speter * directories created, as they are not needed.
39251881Speter *
40251881Speter * The diff result from this editor is a combination of the restructuring
41251881Speter * operations from the repository with the local restructurings since checking
42251881Speter * out.
43251881Speter *
44251881Speter * ### TODO: Make sure that we properly support and report multi layered
45251881Speter *           operations instead of only simple file replacements.
46251881Speter *
47251881Speter * ### TODO: Replacements where the node kind changes needs support. It
48251881Speter * mostly works when the change is in the repository, but not when it is
49251881Speter * in the working copy.
50251881Speter *
51251881Speter * ### TODO: Do we need to support copyfrom?
52251881Speter *
53251881Speter */
54251881Speter
55251881Speter#include <apr_hash.h>
56251881Speter#include <apr_md5.h>
57251881Speter
58251881Speter#include <assert.h>
59251881Speter
60251881Speter#include "svn_error.h"
61251881Speter#include "svn_pools.h"
62251881Speter#include "svn_dirent_uri.h"
63251881Speter#include "svn_path.h"
64251881Speter#include "svn_hash.h"
65251881Speter#include "svn_sorts.h"
66251881Speter
67289180Speter#include "private/svn_diff_tree.h"
68289180Speter#include "private/svn_editor.h"
69289180Speter#include "private/svn_sorts_private.h"
70251881Speter#include "private/svn_subr_private.h"
71251881Speter#include "private/svn_wc_private.h"
72251881Speter
73251881Speter#include "wc.h"
74251881Speter#include "props.h"
75251881Speter#include "adm_files.h"
76251881Speter#include "translate.h"
77251881Speter#include "diff.h"
78251881Speter
79251881Speter#include "svn_private_config.h"
80251881Speter
81251881Speter/*-------------------------------------------------------------------------*/
82251881Speter
83251881Speter
84251881Speter/* Overall crawler editor baton.
85251881Speter */
86251881Speterstruct edit_baton_t
87251881Speter{
88251881Speter  /* A wc db. */
89251881Speter  svn_wc__db_t *db;
90251881Speter
91251881Speter  /* A diff tree processor, receiving the result of the diff. */
92251881Speter  const svn_diff_tree_processor_t *processor;
93251881Speter
94251881Speter  /* A boolean indicating whether local additions should be reported before
95251881Speter     remote deletes. The processor can transform adds in deletes and deletes
96251881Speter     in adds, but it can't reorder the output. */
97251881Speter  svn_boolean_t local_before_remote;
98251881Speter
99251881Speter  /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */
100251881Speter  const char *target;
101251881Speter  const char *anchor_abspath;
102251881Speter
103251881Speter  /* Target revision */
104251881Speter  svn_revnum_t revnum;
105251881Speter
106251881Speter  /* Was the root opened? */
107251881Speter  svn_boolean_t root_opened;
108251881Speter
109251881Speter  /* How does this diff descend as seen from target? */
110251881Speter  svn_depth_t depth;
111251881Speter
112251881Speter  /* Should this diff ignore node ancestry? */
113251881Speter  svn_boolean_t ignore_ancestry;
114251881Speter
115251881Speter  /* Possibly diff repos against text-bases instead of working files. */
116251881Speter  svn_boolean_t diff_pristine;
117251881Speter
118251881Speter  /* Cancel function/baton */
119251881Speter  svn_cancel_func_t cancel_func;
120251881Speter  void *cancel_baton;
121251881Speter
122251881Speter  apr_pool_t *pool;
123251881Speter};
124251881Speter
125251881Speter/* Directory level baton.
126251881Speter */
127251881Speterstruct dir_baton_t
128251881Speter{
129251881Speter  /* Reference to parent directory baton (or NULL for the root) */
130251881Speter  struct dir_baton_t *parent_baton;
131251881Speter
132251881Speter  /* The depth at which this directory should be diffed. */
133251881Speter  svn_depth_t depth;
134251881Speter
135251881Speter  /* The name and path of this directory as if they would be/are in the
136251881Speter      local working copy. */
137251881Speter  const char *name;
138251881Speter  const char *relpath;
139251881Speter  const char *local_abspath;
140251881Speter
141251881Speter  /* TRUE if the file is added by the editor drive. */
142251881Speter  svn_boolean_t added;
143251881Speter  /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
144251881Speter  svn_boolean_t repos_only;
145251881Speter  /* TRUE if the node is to be compared with an unrelated node*/
146251881Speter  svn_boolean_t ignoring_ancestry;
147251881Speter
148362181Sdim  /* TRUE if the directory was reported incomplete to the repository */
149362181Sdim  svn_boolean_t is_incomplete;
150362181Sdim
151251881Speter  /* Processor state */
152251881Speter  void *pdb;
153251881Speter  svn_boolean_t skip;
154251881Speter  svn_boolean_t skip_children;
155251881Speter
156251881Speter  svn_diff_source_t *left_src;
157251881Speter  svn_diff_source_t *right_src;
158251881Speter
159251881Speter  apr_hash_t *local_info;
160251881Speter
161251881Speter  /* A hash containing the basenames of the nodes reported deleted by the
162251881Speter     repository (or NULL for no values). */
163251881Speter  apr_hash_t *deletes;
164251881Speter
165251881Speter  /* Identifies those directory elements that get compared while running
166251881Speter     the crawler.  These elements should not be compared again when
167251881Speter     recursively looking for local modifications.
168251881Speter
169251881Speter     This hash maps the basename of the node to an unimportant value.
170251881Speter
171251881Speter     If the directory's properties have been compared, an item with hash
172251881Speter     key of "" will be present in the hash. */
173251881Speter  apr_hash_t *compared;
174251881Speter
175251881Speter  /* The list of incoming BASE->repos propchanges. */
176251881Speter  apr_array_header_t *propchanges;
177251881Speter
178251881Speter  /* Has a change on regular properties */
179251881Speter  svn_boolean_t has_propchange;
180251881Speter
181251881Speter  /* The overall crawler editor baton. */
182251881Speter  struct edit_baton_t *eb;
183251881Speter
184251881Speter  apr_pool_t *pool;
185251881Speter  int users;
186251881Speter};
187251881Speter
188251881Speter/* File level baton.
189251881Speter */
190251881Speterstruct file_baton_t
191251881Speter{
192251881Speter  struct dir_baton_t *parent_baton;
193251881Speter
194251881Speter  /* The name and path of this file as if they would be/are in the
195251881Speter     parent directory, diff session and local working copy. */
196251881Speter  const char *name;
197251881Speter  const char *relpath;
198251881Speter  const char *local_abspath;
199251881Speter
200251881Speter  /* Processor state */
201251881Speter  void *pfb;
202251881Speter  svn_boolean_t skip;
203251881Speter
204251881Speter  /* TRUE if the file is added by the editor drive. */
205251881Speter  svn_boolean_t added;
206251881Speter  /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
207251881Speter  svn_boolean_t repos_only;
208251881Speter  /* TRUE if the node is to be compared with an unrelated node*/
209251881Speter  svn_boolean_t ignoring_ancestry;
210251881Speter
211251881Speter  const svn_diff_source_t *left_src;
212251881Speter  const svn_diff_source_t *right_src;
213251881Speter
214251881Speter  /* The list of incoming BASE->repos propchanges. */
215251881Speter  apr_array_header_t *propchanges;
216251881Speter
217251881Speter  /* Has a change on regular properties */
218251881Speter  svn_boolean_t has_propchange;
219251881Speter
220251881Speter  /* The current BASE checksum and props */
221251881Speter  const svn_checksum_t *base_checksum;
222251881Speter  apr_hash_t *base_props;
223251881Speter
224251881Speter  /* The resulting from apply_textdelta */
225251881Speter  const char *temp_file_path;
226251881Speter  unsigned char result_digest[APR_MD5_DIGESTSIZE];
227251881Speter
228251881Speter  /* The overall crawler editor baton. */
229251881Speter  struct edit_baton_t *eb;
230251881Speter
231251881Speter  apr_pool_t *pool;
232251881Speter};
233251881Speter
234251881Speter/* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths
235251881Speter * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON
236251881Speter * define the callbacks to compare files. DEPTH defines if and how to
237251881Speter * descend into subdirectories; see public doc string for exactly how.
238251881Speter * IGNORE_ANCESTRY defines whether to utilize node ancestry when
239251881Speter * calculating diffs.  USE_TEXT_BASE defines whether to compare
240251881Speter * against working files or text-bases.  REVERSE_ORDER defines which
241251881Speter * direction to perform the diff.
242251881Speter */
243251881Speterstatic svn_error_t *
244251881Spetermake_edit_baton(struct edit_baton_t **edit_baton,
245251881Speter                svn_wc__db_t *db,
246251881Speter                const char *anchor_abspath,
247251881Speter                const char *target,
248289180Speter                const svn_diff_tree_processor_t *diff_processor,
249251881Speter                svn_depth_t depth,
250251881Speter                svn_boolean_t ignore_ancestry,
251251881Speter                svn_boolean_t use_text_base,
252251881Speter                svn_boolean_t reverse_order,
253251881Speter                svn_cancel_func_t cancel_func,
254251881Speter                void *cancel_baton,
255251881Speter                apr_pool_t *pool)
256251881Speter{
257251881Speter  struct edit_baton_t *eb;
258251881Speter
259251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
260251881Speter
261251881Speter  eb = apr_pcalloc(pool, sizeof(*eb));
262251881Speter  eb->db = db;
263251881Speter  eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
264251881Speter  eb->target = apr_pstrdup(pool, target);
265289180Speter  eb->processor = diff_processor;
266251881Speter  eb->depth = depth;
267251881Speter  eb->ignore_ancestry = ignore_ancestry;
268251881Speter  eb->local_before_remote = reverse_order;
269251881Speter  eb->diff_pristine = use_text_base;
270251881Speter  eb->cancel_func = cancel_func;
271251881Speter  eb->cancel_baton = cancel_baton;
272251881Speter  eb->pool = pool;
273251881Speter
274251881Speter  *edit_baton = eb;
275251881Speter  return SVN_NO_ERROR;
276251881Speter}
277251881Speter
278251881Speter/* Create a new directory baton.  PATH is the directory path,
279251881Speter * including anchor_path.  ADDED is set if this directory is being
280251881Speter * added rather than replaced.  PARENT_BATON is the baton of the
281251881Speter * parent directory, it will be null if this is the root of the
282251881Speter * comparison hierarchy.  The directory and its parent may or may not
283251881Speter * exist in the working copy.  EDIT_BATON is the overall crawler
284251881Speter * editor baton.
285251881Speter */
286251881Speterstatic struct dir_baton_t *
287251881Spetermake_dir_baton(const char *path,
288251881Speter               struct dir_baton_t *parent_baton,
289251881Speter               struct edit_baton_t *eb,
290251881Speter               svn_boolean_t added,
291251881Speter               svn_depth_t depth,
292251881Speter               apr_pool_t *result_pool)
293251881Speter{
294251881Speter  apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
295251881Speter                                                      : eb->pool);
296251881Speter  struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
297251881Speter
298251881Speter  db->parent_baton = parent_baton;
299251881Speter
300251881Speter  /* Allocate 1 string for using as 3 strings */
301251881Speter  db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
302251881Speter  db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
303251881Speter  db->name = svn_dirent_basename(db->relpath, NULL);
304251881Speter
305251881Speter  db->eb = eb;
306251881Speter  db->added = added;
307251881Speter  db->depth = depth;
308251881Speter  db->pool = dir_pool;
309251881Speter  db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
310251881Speter  db->compared = apr_hash_make(dir_pool);
311251881Speter
312251881Speter  if (parent_baton != NULL)
313251881Speter    {
314251881Speter      parent_baton->users++;
315251881Speter    }
316251881Speter
317251881Speter  db->users = 1;
318251881Speter
319251881Speter  return db;
320251881Speter}
321251881Speter
322251881Speter/* Create a new file baton.  PATH is the file path, including
323251881Speter * anchor_path.  ADDED is set if this file is being added rather than
324251881Speter * replaced.  PARENT_BATON is the baton of the parent directory.
325251881Speter * The directory and its parent may or may not exist in the working copy.
326251881Speter */
327251881Speterstatic struct file_baton_t *
328251881Spetermake_file_baton(const char *path,
329251881Speter                svn_boolean_t added,
330251881Speter                struct dir_baton_t *parent_baton,
331251881Speter                apr_pool_t *result_pool)
332251881Speter{
333251881Speter  apr_pool_t *file_pool = svn_pool_create(result_pool);
334251881Speter  struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
335251881Speter  struct edit_baton_t *eb = parent_baton->eb;
336251881Speter
337251881Speter  fb->eb = eb;
338251881Speter  fb->parent_baton = parent_baton;
339251881Speter  fb->parent_baton->users++;
340251881Speter
341251881Speter  /* Allocate 1 string for using as 3 strings */
342251881Speter  fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
343251881Speter  fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
344251881Speter  fb->name = svn_dirent_basename(fb->relpath, NULL);
345251881Speter
346251881Speter  fb->added = added;
347251881Speter  fb->pool = file_pool;
348251881Speter  fb->propchanges  = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
349251881Speter
350251881Speter  return fb;
351251881Speter}
352251881Speter
353251881Speter/* Destroy DB when there are no more registered users */
354251881Speterstatic svn_error_t *
355251881Spetermaybe_done(struct dir_baton_t *db)
356251881Speter{
357251881Speter  db->users--;
358251881Speter
359251881Speter  if (!db->users)
360251881Speter    {
361251881Speter      struct dir_baton_t *pb = db->parent_baton;
362251881Speter
363251881Speter      svn_pool_clear(db->pool);
364251881Speter
365251881Speter      if (pb != NULL)
366251881Speter        SVN_ERR(maybe_done(pb));
367251881Speter    }
368251881Speter
369251881Speter  return SVN_NO_ERROR;
370251881Speter}
371251881Speter
372251881Speter/* Standard check to see if a node is represented in the local working copy */
373251881Speter#define NOT_PRESENT(status)                                    \
374251881Speter            ((status) == svn_wc__db_status_not_present          \
375251881Speter             || (status) == svn_wc__db_status_excluded          \
376251881Speter             || (status) == svn_wc__db_status_server_excluded)
377251881Speter
378251881Spetersvn_error_t *
379251881Spetersvn_wc__diff_base_working_diff(svn_wc__db_t *db,
380251881Speter                               const char *local_abspath,
381251881Speter                               const char *relpath,
382251881Speter                               svn_revnum_t revision,
383251881Speter                               const svn_diff_tree_processor_t *processor,
384251881Speter                               void *processor_dir_baton,
385251881Speter                               svn_boolean_t diff_pristine,
386251881Speter                               svn_cancel_func_t cancel_func,
387251881Speter                               void *cancel_baton,
388251881Speter                               apr_pool_t *scratch_pool)
389251881Speter{
390251881Speter  void *file_baton = NULL;
391251881Speter  svn_boolean_t skip = FALSE;
392251881Speter  svn_wc__db_status_t status;
393251881Speter  svn_revnum_t db_revision;
394251881Speter  svn_boolean_t had_props;
395251881Speter  svn_boolean_t props_mod;
396251881Speter  svn_boolean_t files_same = FALSE;
397251881Speter  svn_wc__db_status_t base_status;
398251881Speter  const svn_checksum_t *working_checksum;
399251881Speter  const svn_checksum_t *checksum;
400251881Speter  svn_filesize_t recorded_size;
401251881Speter  apr_time_t recorded_time;
402251881Speter  const char *pristine_file;
403251881Speter  const char *local_file;
404251881Speter  svn_diff_source_t *left_src;
405251881Speter  svn_diff_source_t *right_src;
406251881Speter  apr_hash_t *base_props;
407251881Speter  apr_hash_t *local_props;
408251881Speter  apr_array_header_t *prop_changes;
409251881Speter
410251881Speter  SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
411251881Speter                               NULL, NULL, NULL, NULL, &working_checksum, NULL,
412251881Speter                               NULL, NULL, NULL, NULL, NULL, &recorded_size,
413286506Speter                               &recorded_time, NULL, NULL, NULL,
414251881Speter                               &had_props, &props_mod, NULL, NULL, NULL,
415251881Speter                               db, local_abspath, scratch_pool, scratch_pool));
416251881Speter  checksum = working_checksum;
417251881Speter
418251881Speter  assert(status == svn_wc__db_status_normal
419251881Speter         || status == svn_wc__db_status_added
420251881Speter         || (status == svn_wc__db_status_deleted && diff_pristine));
421251881Speter
422251881Speter  if (status != svn_wc__db_status_normal)
423251881Speter    {
424251881Speter      SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
425251881Speter                                       NULL, NULL, NULL, NULL, NULL, NULL,
426251881Speter                                       NULL, &checksum, NULL, NULL, &had_props,
427251881Speter                                       NULL, NULL,
428251881Speter                                       db, local_abspath,
429251881Speter                                       scratch_pool, scratch_pool));
430251881Speter      recorded_size = SVN_INVALID_FILESIZE;
431251881Speter      recorded_time = 0;
432251881Speter      props_mod = TRUE; /* Requires compare */
433251881Speter    }
434251881Speter  else if (diff_pristine)
435251881Speter    files_same = TRUE;
436251881Speter  else
437251881Speter    {
438251881Speter      const svn_io_dirent2_t *dirent;
439251881Speter
440257936Speter      /* Verify truename to mimic status for iota/IOTA difference on Windows */
441251881Speter      SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
442257936Speter                                  TRUE /* verify truename */,
443251881Speter                                  TRUE /* ingore_enoent */,
444251881Speter                                  scratch_pool, scratch_pool));
445251881Speter
446257936Speter      /* If a file does not exist on disk (missing/obstructed) then we
447257936Speter         can't provide a text diff */
448257936Speter      if (dirent->kind != svn_node_file
449257936Speter          || (dirent->kind == svn_node_file
450257936Speter              && dirent->filesize == recorded_size
451257936Speter              && dirent->mtime == recorded_time))
452251881Speter        {
453251881Speter          files_same = TRUE;
454251881Speter        }
455251881Speter    }
456251881Speter
457251881Speter  if (files_same && !props_mod)
458251881Speter    return SVN_NO_ERROR; /* Cheap exit */
459251881Speter
460251881Speter  assert(checksum);
461251881Speter
462251881Speter  if (!SVN_IS_VALID_REVNUM(revision))
463251881Speter    revision = db_revision;
464251881Speter
465251881Speter  left_src = svn_diff__source_create(revision, scratch_pool);
466251881Speter  right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
467251881Speter
468251881Speter  SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
469251881Speter                                 left_src,
470251881Speter                                 right_src,
471251881Speter                                 NULL /* copyfrom_src */,
472251881Speter                                 processor_dir_baton,
473251881Speter                                 processor,
474251881Speter                                 scratch_pool, scratch_pool));
475251881Speter
476251881Speter  if (skip)
477251881Speter    return SVN_NO_ERROR;
478251881Speter
479251881Speter  SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
480251881Speter                                       db, local_abspath, checksum,
481251881Speter                                       scratch_pool, scratch_pool));
482251881Speter
483251881Speter  if (diff_pristine)
484251881Speter    SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
485251881Speter                                         db, local_abspath,
486251881Speter                                         working_checksum,
487251881Speter                                         scratch_pool, scratch_pool));
488251881Speter  else if (! (had_props || props_mod))
489251881Speter    local_file = local_abspath;
490251881Speter  else if (files_same)
491251881Speter    local_file = pristine_file;
492251881Speter  else
493251881Speter    SVN_ERR(svn_wc__internal_translated_file(
494251881Speter                            &local_file, local_abspath,
495251881Speter                            db, local_abspath,
496251881Speter                            SVN_WC_TRANSLATE_TO_NF
497251881Speter                                | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
498251881Speter                            cancel_func, cancel_baton,
499251881Speter                            scratch_pool, scratch_pool));
500251881Speter
501251881Speter  if (! files_same)
502251881Speter    SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
503251881Speter                                         pristine_file, scratch_pool));
504251881Speter
505251881Speter  if (had_props)
506251881Speter    SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
507251881Speter                                      scratch_pool, scratch_pool));
508251881Speter  else
509251881Speter    base_props = apr_hash_make(scratch_pool);
510251881Speter
511251881Speter  if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
512251881Speter    local_props = base_props;
513251881Speter  else if (diff_pristine)
514251881Speter    SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
515251881Speter                                           scratch_pool, scratch_pool));
516251881Speter  else
517251881Speter    SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
518251881Speter                                  scratch_pool, scratch_pool));
519251881Speter
520251881Speter  SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
521251881Speter
522251881Speter  if (prop_changes->nelts || !files_same)
523251881Speter    {
524251881Speter      SVN_ERR(processor->file_changed(relpath,
525251881Speter                                      left_src,
526251881Speter                                      right_src,
527251881Speter                                      pristine_file,
528251881Speter                                      local_file,
529251881Speter                                      base_props,
530251881Speter                                      local_props,
531251881Speter                                      ! files_same,
532251881Speter                                      prop_changes,
533251881Speter                                      file_baton,
534251881Speter                                      processor,
535251881Speter                                      scratch_pool));
536251881Speter    }
537251881Speter  else
538251881Speter    {
539251881Speter      SVN_ERR(processor->file_closed(relpath,
540251881Speter                                     left_src,
541251881Speter                                     right_src,
542251881Speter                                     file_baton,
543251881Speter                                     processor,
544251881Speter                                     scratch_pool));
545251881Speter    }
546251881Speter
547251881Speter  return SVN_NO_ERROR;
548251881Speter}
549251881Speter
550251881Speterstatic svn_error_t *
551251881Speterensure_local_info(struct dir_baton_t *db,
552251881Speter                  apr_pool_t *scratch_pool)
553251881Speter{
554251881Speter  apr_hash_t *conflicts;
555251881Speter
556251881Speter  if (db->local_info)
557251881Speter    return SVN_NO_ERROR;
558251881Speter
559251881Speter  SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
560251881Speter                                        db->eb->db, db->local_abspath,
561289180Speter                                        FALSE /* base_tree_only */,
562251881Speter                                        db->pool, scratch_pool));
563251881Speter
564251881Speter  return SVN_NO_ERROR;
565251881Speter}
566251881Speter
567251881Speter/* Called when the directory is closed to compare any elements that have
568251881Speter * not yet been compared.  This identifies local, working copy only
569251881Speter * changes.  At this stage we are dealing with files/directories that do
570251881Speter * exist in the working copy.
571251881Speter *
572251881Speter * DIR_BATON is the baton for the directory.
573251881Speter */
574251881Speterstatic svn_error_t *
575251881Speterwalk_local_nodes_diff(struct edit_baton_t *eb,
576251881Speter                      const char *local_abspath,
577251881Speter                      const char *path,
578251881Speter                      svn_depth_t depth,
579251881Speter                      apr_hash_t *compared,
580251881Speter                      void *parent_baton,
581251881Speter                      apr_pool_t *scratch_pool)
582251881Speter{
583251881Speter  svn_wc__db_t *db = eb->db;
584251881Speter  svn_boolean_t in_anchor_not_target;
585251881Speter  apr_pool_t *iterpool;
586251881Speter  void *dir_baton = NULL;
587251881Speter  svn_boolean_t skip = FALSE;
588251881Speter  svn_boolean_t skip_children = FALSE;
589251881Speter  svn_revnum_t revision;
590251881Speter  svn_boolean_t props_mod;
591251881Speter  svn_diff_source_t *left_src;
592251881Speter  svn_diff_source_t *right_src;
593251881Speter
594251881Speter  /* Everything we do below is useless if we are comparing to BASE. */
595251881Speter  if (eb->diff_pristine)
596251881Speter    return SVN_NO_ERROR;
597251881Speter
598251881Speter  /* Determine if this is the anchor directory if the anchor is different
599251881Speter     to the target. When the target is a file, the anchor is the parent
600251881Speter     directory and if this is that directory the non-target entries must be
601251881Speter     skipped. */
602251881Speter  in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
603251881Speter
604251881Speter  iterpool = svn_pool_create(scratch_pool);
605251881Speter
606251881Speter  SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
607251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
608251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
609251881Speter                               NULL, &props_mod, NULL, NULL, NULL,
610251881Speter                               db, local_abspath, scratch_pool, scratch_pool));
611251881Speter
612251881Speter  left_src = svn_diff__source_create(revision, scratch_pool);
613251881Speter  right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
614251881Speter
615251881Speter  if (compared)
616251881Speter    {
617251881Speter      dir_baton = parent_baton;
618251881Speter      skip = TRUE;
619251881Speter    }
620251881Speter  else if (!in_anchor_not_target)
621251881Speter    SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
622251881Speter                                      path,
623251881Speter                                      left_src,
624251881Speter                                      right_src,
625251881Speter                                      NULL /* copyfrom_src */,
626251881Speter                                      parent_baton,
627251881Speter                                      eb->processor,
628251881Speter                                      scratch_pool, scratch_pool));
629251881Speter
630251881Speter
631251881Speter  if (!skip_children && depth != svn_depth_empty)
632251881Speter    {
633251881Speter      apr_hash_t *nodes;
634251881Speter      apr_hash_t *conflicts;
635251881Speter      apr_array_header_t *children;
636251881Speter      svn_depth_t depth_below_here = depth;
637251881Speter      svn_boolean_t diff_files;
638251881Speter      svn_boolean_t diff_dirs;
639251881Speter      int i;
640251881Speter
641251881Speter      if (depth_below_here == svn_depth_immediates)
642251881Speter        depth_below_here = svn_depth_empty;
643251881Speter
644251881Speter      diff_files = (depth == svn_depth_unknown
645251881Speter                   || depth >= svn_depth_files);
646251881Speter      diff_dirs = (depth == svn_depth_unknown
647251881Speter                   || depth >= svn_depth_immediates);
648251881Speter
649251881Speter      SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
650251881Speter                                            db, local_abspath,
651289180Speter                                            FALSE /* base_tree_only */,
652251881Speter                                            scratch_pool, iterpool));
653251881Speter
654251881Speter      children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
655251881Speter                            scratch_pool);
656251881Speter
657251881Speter      for (i = 0; i < children->nelts; i++)
658251881Speter        {
659251881Speter          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
660251881Speter                                                  svn_sort__item_t);
661251881Speter          const char *name = item->key;
662251881Speter          struct svn_wc__db_info_t *info = item->value;
663251881Speter
664251881Speter          const char *child_abspath;
665251881Speter          const char *child_relpath;
666251881Speter          svn_boolean_t repos_only;
667251881Speter          svn_boolean_t local_only;
668251881Speter          svn_node_kind_t base_kind;
669251881Speter
670251881Speter          if (eb->cancel_func)
671251881Speter            SVN_ERR(eb->cancel_func(eb->cancel_baton));
672251881Speter
673251881Speter          /* In the anchor directory, if the anchor is not the target then all
674251881Speter             entries other than the target should not be diff'd. Running diff
675251881Speter             on one file in a directory should not diff other files in that
676251881Speter             directory. */
677251881Speter          if (in_anchor_not_target && strcmp(eb->target, name))
678251881Speter            continue;
679251881Speter
680251881Speter          if (compared && svn_hash_gets(compared, name))
681251881Speter            continue;
682251881Speter
683251881Speter          if (NOT_PRESENT(info->status))
684251881Speter            continue;
685251881Speter
686251881Speter          assert(info->status == svn_wc__db_status_normal
687251881Speter                 || info->status == svn_wc__db_status_added
688251881Speter                 || info->status == svn_wc__db_status_deleted);
689251881Speter
690251881Speter          svn_pool_clear(iterpool);
691251881Speter          child_abspath = svn_dirent_join(local_abspath, name, iterpool);
692251881Speter          child_relpath = svn_relpath_join(path, name, iterpool);
693251881Speter
694251881Speter          repos_only = FALSE;
695251881Speter          local_only = FALSE;
696251881Speter
697251881Speter          if (!info->have_base)
698251881Speter            {
699251881Speter              local_only = TRUE; /* Only report additions */
700289180Speter
701289180Speter              if (info->status == svn_wc__db_status_deleted)
702289180Speter                continue; /* Nothing added (deleted copy) */
703251881Speter            }
704251881Speter          else if (info->status == svn_wc__db_status_normal)
705251881Speter            {
706251881Speter              /* Simple diff */
707251881Speter              base_kind = info->kind;
708251881Speter            }
709251881Speter          else if (info->status == svn_wc__db_status_deleted
710251881Speter                   && (!eb->diff_pristine || !info->have_more_work))
711251881Speter            {
712251881Speter              svn_wc__db_status_t base_status;
713251881Speter              repos_only = TRUE;
714251881Speter              SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
715251881Speter                                               NULL, NULL, NULL, NULL, NULL,
716251881Speter                                               NULL, NULL, NULL, NULL, NULL,
717251881Speter                                               NULL, NULL, NULL,
718251881Speter                                               db, child_abspath,
719251881Speter                                               iterpool, iterpool));
720251881Speter
721251881Speter              if (NOT_PRESENT(base_status))
722251881Speter                continue;
723251881Speter            }
724251881Speter          else
725251881Speter            {
726251881Speter              /* working status is either added or deleted */
727251881Speter              svn_wc__db_status_t base_status;
728251881Speter
729251881Speter              SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
730251881Speter                                               NULL, NULL, NULL, NULL, NULL,
731251881Speter                                               NULL, NULL, NULL, NULL, NULL,
732251881Speter                                               NULL, NULL, NULL,
733251881Speter                                               db, child_abspath,
734251881Speter                                               iterpool, iterpool));
735251881Speter
736251881Speter              if (NOT_PRESENT(base_status))
737251881Speter                local_only = TRUE;
738251881Speter              else if (base_kind != info->kind || !eb->ignore_ancestry)
739251881Speter                {
740251881Speter                  repos_only = TRUE;
741251881Speter                  local_only = TRUE;
742251881Speter                }
743251881Speter            }
744251881Speter
745251881Speter          if (eb->local_before_remote && local_only)
746251881Speter            {
747362181Sdim              const char *moved_from_relpath;
748362181Sdim
749362181Sdim              if (info->moved_here)
750362181Sdim                {
751362181Sdim                  const char *moved_from_abspath;
752362181Sdim
753362181Sdim                  SVN_ERR(svn_wc__db_scan_moved(&moved_from_abspath,
754362181Sdim                                                NULL, NULL, NULL,
755362181Sdim                                                db, child_abspath,
756362181Sdim                                                iterpool, iterpool));
757362181Sdim                  SVN_ERR_ASSERT(moved_from_abspath != NULL);
758362181Sdim
759362181Sdim                  moved_from_relpath = svn_dirent_skip_ancestor(
760362181Sdim                                                        eb->anchor_abspath,
761362181Sdim                                                        moved_from_abspath);
762362181Sdim                }
763362181Sdim              else
764362181Sdim                moved_from_relpath = NULL;
765362181Sdim
766251881Speter              if (info->kind == svn_node_file && diff_files)
767251881Speter                SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
768251881Speter                                                     child_relpath,
769362181Sdim                                                     moved_from_relpath,
770251881Speter                                                     eb->processor, dir_baton,
771251881Speter                                                     eb->diff_pristine,
772251881Speter                                                     eb->cancel_func,
773251881Speter                                                     eb->cancel_baton,
774251881Speter                                                     iterpool));
775251881Speter              else if (info->kind == svn_node_dir && diff_dirs)
776251881Speter                SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
777251881Speter                                                    child_relpath,
778251881Speter                                                    depth_below_here,
779362181Sdim                                                    moved_from_relpath,
780251881Speter                                                    eb->processor, dir_baton,
781251881Speter                                                    eb->diff_pristine,
782251881Speter                                                    eb->cancel_func,
783251881Speter                                                    eb->cancel_baton,
784251881Speter                                                    iterpool));
785251881Speter            }
786251881Speter
787251881Speter          if (repos_only)
788251881Speter            {
789251881Speter              /* Report repository form deleted */
790251881Speter              if (base_kind == svn_node_file && diff_files)
791251881Speter                SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
792251881Speter                                                    child_relpath, eb->revnum,
793251881Speter                                                    eb->processor, dir_baton,
794251881Speter                                                    iterpool));
795251881Speter              else if (base_kind == svn_node_dir && diff_dirs)
796251881Speter                SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
797251881Speter                                                   child_relpath, eb->revnum,
798251881Speter                                                   depth_below_here,
799251881Speter                                                   eb->processor, dir_baton,
800251881Speter                                                   eb->cancel_func,
801251881Speter                                                   eb->cancel_baton,
802251881Speter                                                   iterpool));
803251881Speter            }
804251881Speter          else if (!local_only) /* Not local only nor remote only */
805251881Speter            {
806251881Speter              /* Diff base against actual */
807251881Speter              if (info->kind == svn_node_file && diff_files)
808251881Speter                {
809251881Speter                  if (info->status != svn_wc__db_status_normal
810251881Speter                      || !eb->diff_pristine)
811251881Speter                    {
812251881Speter                      SVN_ERR(svn_wc__diff_base_working_diff(
813251881Speter                                                db, child_abspath,
814251881Speter                                                child_relpath,
815251881Speter                                                eb->revnum,
816251881Speter                                                eb->processor, dir_baton,
817251881Speter                                                eb->diff_pristine,
818251881Speter                                                eb->cancel_func,
819251881Speter                                                eb->cancel_baton,
820251881Speter                                                scratch_pool));
821251881Speter                    }
822251881Speter                }
823251881Speter              else if (info->kind == svn_node_dir && diff_dirs)
824251881Speter                SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
825251881Speter                                              child_relpath,
826251881Speter                                              depth_below_here,
827251881Speter                                              NULL /* compared */,
828251881Speter                                              dir_baton,
829251881Speter                                              scratch_pool));
830251881Speter            }
831251881Speter
832251881Speter          if (!eb->local_before_remote && local_only)
833251881Speter            {
834362181Sdim              const char *moved_from_relpath;
835362181Sdim
836362181Sdim              if (info->moved_here)
837362181Sdim                {
838362181Sdim                  const char *moved_from_abspath;
839362181Sdim
840362181Sdim                  SVN_ERR(svn_wc__db_scan_moved(&moved_from_abspath,
841362181Sdim                                                NULL, NULL, NULL,
842362181Sdim                                                db, child_abspath,
843362181Sdim                                                iterpool, iterpool));
844362181Sdim                  SVN_ERR_ASSERT(moved_from_abspath != NULL);
845362181Sdim
846362181Sdim                  moved_from_relpath = svn_dirent_skip_ancestor(
847362181Sdim                                                        eb->anchor_abspath,
848362181Sdim                                                        moved_from_abspath);
849362181Sdim                }
850362181Sdim              else
851362181Sdim                moved_from_relpath = NULL;
852362181Sdim
853251881Speter              if (info->kind == svn_node_file && diff_files)
854251881Speter                SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
855251881Speter                                                     child_relpath,
856362181Sdim                                                     moved_from_relpath,
857251881Speter                                                     eb->processor, dir_baton,
858251881Speter                                                     eb->diff_pristine,
859251881Speter                                                     eb->cancel_func,
860251881Speter                                                     eb->cancel_baton,
861251881Speter                                                     iterpool));
862251881Speter              else if (info->kind == svn_node_dir && diff_dirs)
863251881Speter                SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
864362181Sdim                                                    child_relpath, depth_below_here,
865362181Sdim                                                    moved_from_relpath,
866362181Sdim                                                    eb->processor, dir_baton,
867362181Sdim                                                    eb->diff_pristine,
868362181Sdim                                                    eb->cancel_func,
869362181Sdim                                                    eb->cancel_baton,
870362181Sdim                                                    iterpool));
871251881Speter            }
872251881Speter        }
873251881Speter    }
874251881Speter
875251881Speter  if (compared)
876251881Speter    return SVN_NO_ERROR;
877251881Speter
878286506Speter  /* Check for local property mods on this directory, if we haven't
879286506Speter     already reported them. */
880251881Speter  if (! skip
881251881Speter      && ! in_anchor_not_target
882251881Speter      && props_mod)
883251881Speter    {
884251881Speter      apr_array_header_t *propchanges;
885251881Speter      apr_hash_t *left_props;
886251881Speter      apr_hash_t *right_props;
887251881Speter
888251881Speter      SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
889251881Speter                                        db, local_abspath,
890251881Speter                                        scratch_pool, scratch_pool));
891251881Speter
892251881Speter      right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
893251881Speter
894251881Speter      SVN_ERR(eb->processor->dir_changed(path,
895251881Speter                                         left_src,
896251881Speter                                         right_src,
897251881Speter                                         left_props,
898251881Speter                                         right_props,
899251881Speter                                         propchanges,
900251881Speter                                         dir_baton,
901251881Speter                                         eb->processor,
902251881Speter                                         scratch_pool));
903251881Speter    }
904251881Speter  else if (! skip)
905251881Speter    SVN_ERR(eb->processor->dir_closed(path,
906251881Speter                                      left_src,
907251881Speter                                      right_src,
908251881Speter                                      dir_baton,
909251881Speter                                      eb->processor,
910251881Speter                                      scratch_pool));
911251881Speter
912251881Speter  svn_pool_destroy(iterpool);
913251881Speter
914251881Speter  return SVN_NO_ERROR;
915251881Speter}
916251881Speter
917251881Spetersvn_error_t *
918251881Spetersvn_wc__diff_local_only_file(svn_wc__db_t *db,
919251881Speter                             const char *local_abspath,
920251881Speter                             const char *relpath,
921362181Sdim                             const char *moved_from_relpath,
922251881Speter                             const svn_diff_tree_processor_t *processor,
923251881Speter                             void *processor_parent_baton,
924251881Speter                             svn_boolean_t diff_pristine,
925251881Speter                             svn_cancel_func_t cancel_func,
926251881Speter                             void *cancel_baton,
927251881Speter                             apr_pool_t *scratch_pool)
928251881Speter{
929251881Speter  svn_diff_source_t *right_src;
930251881Speter  svn_diff_source_t *copyfrom_src = NULL;
931251881Speter  svn_wc__db_status_t status;
932251881Speter  svn_node_kind_t kind;
933251881Speter  const svn_checksum_t *checksum;
934251881Speter  const char *original_repos_relpath;
935251881Speter  svn_revnum_t original_revision;
936251881Speter  svn_boolean_t had_props;
937251881Speter  svn_boolean_t props_mod;
938251881Speter  apr_hash_t *pristine_props;
939251881Speter  apr_hash_t *right_props = NULL;
940251881Speter  const char *pristine_file;
941251881Speter  const char *translated_file;
942251881Speter  svn_revnum_t revision;
943251881Speter  void *file_baton = NULL;
944251881Speter  svn_boolean_t skip = FALSE;
945251881Speter  svn_boolean_t file_mod = TRUE;
946251881Speter
947251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
948251881Speter                               NULL, NULL, NULL, NULL, &checksum, NULL,
949251881Speter                               &original_repos_relpath, NULL, NULL,
950251881Speter                               &original_revision, NULL, NULL, NULL,
951286506Speter                               NULL, NULL, NULL, &had_props,
952251881Speter                               &props_mod, NULL, NULL, NULL,
953251881Speter                               db, local_abspath,
954251881Speter                               scratch_pool, scratch_pool));
955251881Speter
956251881Speter  assert(kind == svn_node_file
957251881Speter         && (status == svn_wc__db_status_normal
958251881Speter             || status == svn_wc__db_status_added
959251881Speter             || (status == svn_wc__db_status_deleted && diff_pristine)));
960251881Speter
961251881Speter
962251881Speter  if (status == svn_wc__db_status_deleted)
963251881Speter    {
964251881Speter      assert(diff_pristine);
965251881Speter
966251881Speter      SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
967251881Speter                                            NULL, &checksum, NULL, &had_props,
968251881Speter                                            &pristine_props,
969251881Speter                                            db, local_abspath,
970251881Speter                                            scratch_pool, scratch_pool));
971251881Speter      props_mod = FALSE;
972251881Speter    }
973251881Speter  else if (!had_props)
974251881Speter    pristine_props = apr_hash_make(scratch_pool);
975251881Speter  else
976251881Speter    SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
977251881Speter                                           db, local_abspath,
978251881Speter                                           scratch_pool, scratch_pool));
979251881Speter
980251881Speter  if (original_repos_relpath)
981251881Speter    {
982251881Speter      copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
983251881Speter      copyfrom_src->repos_relpath = original_repos_relpath;
984362181Sdim      copyfrom_src->moved_from_relpath = moved_from_relpath;
985251881Speter    }
986251881Speter
987251881Speter  if (props_mod || !SVN_IS_VALID_REVNUM(revision))
988251881Speter    right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
989251881Speter  else
990251881Speter    {
991251881Speter      if (diff_pristine)
992251881Speter        file_mod = FALSE;
993251881Speter      else
994251881Speter        SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
995251881Speter                                                 FALSE, scratch_pool));
996251881Speter
997251881Speter      if (!file_mod)
998251881Speter        right_src = svn_diff__source_create(revision, scratch_pool);
999251881Speter      else
1000251881Speter        right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
1001251881Speter    }
1002251881Speter
1003251881Speter  SVN_ERR(processor->file_opened(&file_baton, &skip,
1004251881Speter                                 relpath,
1005251881Speter                                 NULL /* left_source */,
1006251881Speter                                 right_src,
1007251881Speter                                 copyfrom_src,
1008251881Speter                                 processor_parent_baton,
1009251881Speter                                 processor,
1010251881Speter                                 scratch_pool, scratch_pool));
1011251881Speter
1012251881Speter  if (skip)
1013251881Speter    return SVN_NO_ERROR;
1014251881Speter
1015251881Speter  if (props_mod && !diff_pristine)
1016251881Speter    SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1017251881Speter                                  scratch_pool, scratch_pool));
1018251881Speter  else
1019251881Speter    right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1020251881Speter
1021251881Speter  if (checksum)
1022251881Speter    SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
1023251881Speter                                         checksum, scratch_pool, scratch_pool));
1024251881Speter  else
1025251881Speter    pristine_file = NULL;
1026251881Speter
1027251881Speter  if (diff_pristine)
1028251881Speter    {
1029251881Speter      translated_file = pristine_file; /* No translation needed */
1030251881Speter    }
1031251881Speter  else
1032251881Speter    {
1033251881Speter      SVN_ERR(svn_wc__internal_translated_file(
1034251881Speter           &translated_file, local_abspath, db, local_abspath,
1035251881Speter           SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
1036251881Speter           cancel_func, cancel_baton,
1037251881Speter           scratch_pool, scratch_pool));
1038251881Speter    }
1039251881Speter
1040251881Speter  SVN_ERR(processor->file_added(relpath,
1041251881Speter                                copyfrom_src,
1042251881Speter                                right_src,
1043251881Speter                                copyfrom_src
1044251881Speter                                  ? pristine_file
1045251881Speter                                  : NULL,
1046251881Speter                                translated_file,
1047251881Speter                                copyfrom_src
1048251881Speter                                  ? pristine_props
1049251881Speter                                  : NULL,
1050251881Speter                                right_props,
1051251881Speter                                file_baton,
1052251881Speter                                processor,
1053251881Speter                                scratch_pool));
1054251881Speter
1055251881Speter  return SVN_NO_ERROR;
1056251881Speter}
1057251881Speter
1058251881Spetersvn_error_t *
1059251881Spetersvn_wc__diff_local_only_dir(svn_wc__db_t *db,
1060251881Speter                            const char *local_abspath,
1061251881Speter                            const char *relpath,
1062251881Speter                            svn_depth_t depth,
1063362181Sdim                            const char *moved_from_relpath,
1064251881Speter                            const svn_diff_tree_processor_t *processor,
1065251881Speter                            void *processor_parent_baton,
1066251881Speter                            svn_boolean_t diff_pristine,
1067251881Speter                            svn_cancel_func_t cancel_func,
1068251881Speter                            void *cancel_baton,
1069251881Speter                            apr_pool_t *scratch_pool)
1070251881Speter{
1071286506Speter  svn_wc__db_status_t status;
1072286506Speter  svn_node_kind_t kind;
1073286506Speter  svn_boolean_t had_props;
1074286506Speter  svn_boolean_t props_mod;
1075286506Speter  const char *original_repos_relpath;
1076286506Speter  svn_revnum_t original_revision;
1077286506Speter  svn_diff_source_t *copyfrom_src = NULL;
1078286506Speter  apr_hash_t *pristine_props;
1079251881Speter  const apr_array_header_t *children;
1080251881Speter  int i;
1081251881Speter  apr_pool_t *iterpool;
1082251881Speter  void *pdb = NULL;
1083251881Speter  svn_boolean_t skip = FALSE;
1084251881Speter  svn_boolean_t skip_children = FALSE;
1085251881Speter  svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
1086251881Speter                                                         scratch_pool);
1087251881Speter
1088286506Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
1089286506Speter                               NULL, NULL, NULL, NULL, NULL, NULL,
1090286506Speter                               &original_repos_relpath, NULL, NULL,
1091286506Speter                               &original_revision, NULL, NULL, NULL,
1092286506Speter                               NULL, NULL, NULL, &had_props,
1093286506Speter                               &props_mod, NULL, NULL, NULL,
1094286506Speter                               db, local_abspath,
1095286506Speter                               scratch_pool, scratch_pool));
1096286506Speter  if (original_repos_relpath)
1097286506Speter    {
1098286506Speter      copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
1099286506Speter      copyfrom_src->repos_relpath = original_repos_relpath;
1100362181Sdim      copyfrom_src->moved_from_relpath = moved_from_relpath;
1101286506Speter    }
1102286506Speter
1103286506Speter  /* svn_wc__db_status_incomplete should never happen, as the result won't be
1104286506Speter     stable or guaranteed related to what is in the repository for this
1105286506Speter     revision, but without this it would be hard to diagnose that status... */
1106286506Speter  assert(kind == svn_node_dir
1107286506Speter         && (status == svn_wc__db_status_normal
1108286506Speter             || status == svn_wc__db_status_incomplete
1109286506Speter             || status == svn_wc__db_status_added
1110286506Speter             || (status == svn_wc__db_status_deleted && diff_pristine)));
1111286506Speter
1112286506Speter  if (status == svn_wc__db_status_deleted)
1113286506Speter    {
1114286506Speter      assert(diff_pristine);
1115286506Speter
1116286506Speter      SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1117286506Speter                                            NULL, NULL, NULL, &had_props,
1118286506Speter                                            &pristine_props,
1119286506Speter                                            db, local_abspath,
1120286506Speter                                            scratch_pool, scratch_pool));
1121286506Speter      props_mod = FALSE;
1122286506Speter    }
1123286506Speter  else if (!had_props)
1124286506Speter    pristine_props = apr_hash_make(scratch_pool);
1125286506Speter  else
1126286506Speter    SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
1127286506Speter                                           db, local_abspath,
1128286506Speter                                           scratch_pool, scratch_pool));
1129286506Speter
1130251881Speter  /* Report the addition of the directory's contents. */
1131251881Speter  iterpool = svn_pool_create(scratch_pool);
1132251881Speter
1133251881Speter  SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
1134251881Speter                                relpath,
1135251881Speter                                NULL,
1136251881Speter                                right_src,
1137286506Speter                                copyfrom_src,
1138251881Speter                                processor_parent_baton,
1139251881Speter                                processor,
1140251881Speter                                scratch_pool, iterpool));
1141251881Speter
1142289180Speter  if ((depth > svn_depth_empty || depth == svn_depth_unknown)
1143289180Speter      && ! skip_children)
1144289180Speter    {
1145289180Speter      svn_depth_t depth_below_here = depth;
1146289180Speter      apr_hash_t *nodes;
1147289180Speter      apr_hash_t *conflicts;
1148251881Speter
1149289180Speter      if (depth_below_here == svn_depth_immediates)
1150289180Speter        depth_below_here = svn_depth_empty;
1151251881Speter
1152289180Speter      SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
1153289180Speter                                            db, local_abspath,
1154289180Speter                                            FALSE /* base_tree_only */,
1155289180Speter                                            scratch_pool, iterpool));
1156251881Speter
1157251881Speter
1158289180Speter      children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1159289180Speter                                scratch_pool);
1160251881Speter
1161289180Speter      for (i = 0; i < children->nelts; i++)
1162289180Speter        {
1163289180Speter          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
1164289180Speter          const char *name = item->key;
1165289180Speter          struct svn_wc__db_info_t *info = item->value;
1166289180Speter          const char *child_abspath;
1167289180Speter          const char *child_relpath;
1168251881Speter
1169289180Speter          svn_pool_clear(iterpool);
1170251881Speter
1171289180Speter          if (cancel_func)
1172289180Speter            SVN_ERR(cancel_func(cancel_baton));
1173251881Speter
1174289180Speter          child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1175251881Speter
1176289180Speter          if (NOT_PRESENT(info->status))
1177289180Speter            {
1178289180Speter              continue;
1179289180Speter            }
1180251881Speter
1181289180Speter          /* If comparing against WORKING, skip entries that are
1182289180Speter             schedule-deleted - they don't really exist. */
1183289180Speter          if (!diff_pristine && info->status == svn_wc__db_status_deleted)
1184289180Speter            continue;
1185251881Speter
1186289180Speter          child_relpath = svn_relpath_join(relpath, name, iterpool);
1187289180Speter
1188362181Sdim          if (info->moved_here)
1189362181Sdim            {
1190362181Sdim              const char *moved_from_abspath;
1191362181Sdim              const char *a_abspath;
1192362181Sdim              const char *a_relpath;
1193362181Sdim
1194362181Sdim              a_relpath = relpath;
1195362181Sdim              a_abspath = local_abspath;
1196362181Sdim              while (*a_relpath)
1197362181Sdim                {
1198362181Sdim                  a_relpath = svn_relpath_dirname(a_relpath, iterpool);
1199362181Sdim                  a_abspath = svn_dirent_dirname(a_abspath, iterpool);
1200362181Sdim                }
1201362181Sdim
1202362181Sdim              SVN_ERR(svn_wc__db_scan_moved(&moved_from_abspath,
1203362181Sdim                                            NULL, NULL, NULL,
1204362181Sdim                                            db, child_abspath,
1205362181Sdim                                            iterpool, iterpool));
1206362181Sdim              SVN_ERR_ASSERT(moved_from_abspath != NULL);
1207362181Sdim
1208362181Sdim              moved_from_relpath = svn_dirent_skip_ancestor(
1209362181Sdim                                                        a_abspath,
1210362181Sdim                                                        moved_from_abspath);
1211362181Sdim            }
1212362181Sdim          else
1213362181Sdim            moved_from_relpath = NULL;
1214362181Sdim
1215289180Speter          switch (info->kind)
1216251881Speter            {
1217289180Speter            case svn_node_file:
1218289180Speter            case svn_node_symlink:
1219289180Speter              SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
1220289180Speter                                                   child_relpath,
1221362181Sdim                                                   moved_from_relpath,
1222289180Speter                                                   processor, pdb,
1223289180Speter                                                   diff_pristine,
1224289180Speter                                                   cancel_func, cancel_baton,
1225289180Speter                                                   scratch_pool));
1226289180Speter              break;
1227289180Speter
1228289180Speter            case svn_node_dir:
1229289180Speter              if (depth > svn_depth_files || depth == svn_depth_unknown)
1230289180Speter                {
1231289180Speter                  SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
1232289180Speter                                                      child_relpath,
1233289180Speter                                                      depth_below_here,
1234362181Sdim                                                      moved_from_relpath,
1235289180Speter                                                      processor, pdb,
1236289180Speter                                                      diff_pristine,
1237289180Speter                                                      cancel_func,
1238289180Speter                                                      cancel_baton,
1239289180Speter                                                      iterpool));
1240289180Speter                }
1241289180Speter              break;
1242289180Speter
1243289180Speter            default:
1244289180Speter              break;
1245251881Speter            }
1246251881Speter        }
1247251881Speter    }
1248251881Speter
1249251881Speter  if (!skip)
1250251881Speter    {
1251251881Speter      apr_hash_t *right_props;
1252286506Speter
1253286506Speter      if (props_mod && !diff_pristine)
1254286506Speter        SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1255286506Speter                                      scratch_pool, scratch_pool));
1256251881Speter      else
1257286506Speter        right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1258251881Speter
1259251881Speter      SVN_ERR(processor->dir_added(relpath,
1260286506Speter                                   copyfrom_src,
1261251881Speter                                   right_src,
1262286506Speter                                   copyfrom_src
1263286506Speter                                     ? pristine_props
1264286506Speter                                     : NULL,
1265251881Speter                                   right_props,
1266251881Speter                                   pdb,
1267251881Speter                                   processor,
1268251881Speter                                   iterpool));
1269251881Speter    }
1270251881Speter  svn_pool_destroy(iterpool);
1271251881Speter
1272251881Speter  return SVN_NO_ERROR;
1273251881Speter}
1274251881Speter
1275251881Speter/* Reports local changes. */
1276251881Speterstatic svn_error_t *
1277251881Speterhandle_local_only(struct dir_baton_t *pb,
1278251881Speter                  const char *name,
1279251881Speter                  apr_pool_t *scratch_pool)
1280251881Speter{
1281251881Speter  struct edit_baton_t *eb = pb->eb;
1282251881Speter  const struct svn_wc__db_info_t *info;
1283362181Sdim  const char *child_abspath;
1284362181Sdim  const char *moved_from_relpath;
1285251881Speter  svn_boolean_t repos_delete = (pb->deletes
1286251881Speter                                && svn_hash_gets(pb->deletes, name));
1287251881Speter
1288251881Speter  assert(!strchr(name, '/'));
1289251881Speter  assert(!pb->added || eb->ignore_ancestry);
1290251881Speter
1291251881Speter  if (pb->skip_children)
1292251881Speter    return SVN_NO_ERROR;
1293251881Speter
1294251881Speter  SVN_ERR(ensure_local_info(pb, scratch_pool));
1295251881Speter
1296251881Speter  info = svn_hash_gets(pb->local_info, name);
1297251881Speter
1298251881Speter  if (info == NULL || NOT_PRESENT(info->status))
1299251881Speter    return SVN_NO_ERROR;
1300251881Speter
1301251881Speter  switch (info->status)
1302251881Speter    {
1303362181Sdim      case svn_wc__db_status_normal:
1304251881Speter      case svn_wc__db_status_incomplete:
1305251881Speter        if (!repos_delete)
1306251881Speter          return SVN_NO_ERROR; /* Local and remote */
1307251881Speter        svn_hash_sets(pb->deletes, name, NULL);
1308251881Speter        break;
1309251881Speter
1310251881Speter      case svn_wc__db_status_deleted:
1311251881Speter        if (!(eb->diff_pristine && repos_delete))
1312251881Speter          return SVN_NO_ERROR;
1313251881Speter        break;
1314251881Speter
1315251881Speter      case svn_wc__db_status_added:
1316251881Speter      default:
1317251881Speter        break;
1318251881Speter    }
1319251881Speter
1320362181Sdim  child_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
1321362181Sdim
1322362181Sdim  if (info->moved_here)
1323362181Sdim    {
1324362181Sdim      const char *moved_from_abspath;
1325362181Sdim
1326362181Sdim      SVN_ERR(svn_wc__db_scan_moved(&moved_from_abspath,
1327362181Sdim                                    NULL, NULL, NULL,
1328362181Sdim                                    eb->db, child_abspath,
1329362181Sdim                                    scratch_pool, scratch_pool));
1330362181Sdim      SVN_ERR_ASSERT(moved_from_abspath != NULL);
1331362181Sdim
1332362181Sdim      moved_from_relpath = svn_dirent_skip_ancestor(
1333362181Sdim                                    eb->anchor_abspath,
1334362181Sdim                                    moved_from_abspath);
1335362181Sdim    }
1336362181Sdim  else
1337362181Sdim    moved_from_relpath = NULL;
1338362181Sdim
1339251881Speter  if (info->kind == svn_node_dir)
1340251881Speter    {
1341251881Speter      svn_depth_t depth ;
1342251881Speter
1343251881Speter      if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
1344251881Speter        depth = pb->depth;
1345251881Speter      else
1346251881Speter        depth = svn_depth_empty;
1347251881Speter
1348251881Speter      SVN_ERR(svn_wc__diff_local_only_dir(
1349251881Speter                      eb->db,
1350362181Sdim                      child_abspath,
1351251881Speter                      svn_relpath_join(pb->relpath, name, scratch_pool),
1352251881Speter                      repos_delete ? svn_depth_infinity : depth,
1353362181Sdim                      moved_from_relpath,
1354251881Speter                      eb->processor, pb->pdb,
1355251881Speter                      eb->diff_pristine,
1356251881Speter                      eb->cancel_func, eb->cancel_baton,
1357251881Speter                      scratch_pool));
1358251881Speter    }
1359251881Speter  else
1360251881Speter    SVN_ERR(svn_wc__diff_local_only_file(
1361251881Speter                      eb->db,
1362362181Sdim                      child_abspath,
1363251881Speter                      svn_relpath_join(pb->relpath, name, scratch_pool),
1364362181Sdim                      moved_from_relpath,
1365251881Speter                      eb->processor, pb->pdb,
1366251881Speter                      eb->diff_pristine,
1367251881Speter                      eb->cancel_func, eb->cancel_baton,
1368251881Speter                      scratch_pool));
1369251881Speter
1370251881Speter  return SVN_NO_ERROR;
1371251881Speter}
1372251881Speter
1373251881Speter/* Reports a file LOCAL_ABSPATH in BASE as deleted */
1374251881Spetersvn_error_t *
1375251881Spetersvn_wc__diff_base_only_file(svn_wc__db_t *db,
1376251881Speter                            const char *local_abspath,
1377251881Speter                            const char *relpath,
1378251881Speter                            svn_revnum_t revision,
1379251881Speter                            const svn_diff_tree_processor_t *processor,
1380251881Speter                            void *processor_parent_baton,
1381251881Speter                            apr_pool_t *scratch_pool)
1382251881Speter{
1383251881Speter  svn_wc__db_status_t status;
1384251881Speter  svn_node_kind_t kind;
1385251881Speter  const svn_checksum_t *checksum;
1386251881Speter  apr_hash_t *props;
1387251881Speter  void *file_baton = NULL;
1388251881Speter  svn_boolean_t skip = FALSE;
1389251881Speter  svn_diff_source_t *left_src;
1390251881Speter  const char *pristine_file;
1391251881Speter
1392251881Speter  SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
1393251881Speter                                   SVN_IS_VALID_REVNUM(revision)
1394251881Speter                                        ? NULL : &revision,
1395251881Speter                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1396251881Speter                                   &checksum, NULL, NULL, NULL, &props, NULL,
1397251881Speter                                   db, local_abspath,
1398251881Speter                                   scratch_pool, scratch_pool));
1399251881Speter
1400251881Speter  SVN_ERR_ASSERT(status == svn_wc__db_status_normal
1401251881Speter                 && kind == svn_node_file
1402251881Speter                 && checksum);
1403251881Speter
1404251881Speter  left_src = svn_diff__source_create(revision, scratch_pool);
1405251881Speter
1406251881Speter  SVN_ERR(processor->file_opened(&file_baton, &skip,
1407251881Speter                                 relpath,
1408251881Speter                                 left_src,
1409251881Speter                                 NULL /* right_src */,
1410251881Speter                                 NULL /* copyfrom_source */,
1411251881Speter                                 processor_parent_baton,
1412251881Speter                                 processor,
1413251881Speter                                 scratch_pool, scratch_pool));
1414251881Speter
1415251881Speter  if (skip)
1416251881Speter    return SVN_NO_ERROR;
1417251881Speter
1418251881Speter  SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
1419251881Speter                                       db, local_abspath, checksum,
1420251881Speter                                       scratch_pool, scratch_pool));
1421251881Speter
1422251881Speter  SVN_ERR(processor->file_deleted(relpath,
1423251881Speter                                  left_src,
1424251881Speter                                  pristine_file,
1425251881Speter                                  props,
1426251881Speter                                  file_baton,
1427251881Speter                                  processor,
1428251881Speter                                  scratch_pool));
1429251881Speter
1430251881Speter  return SVN_NO_ERROR;
1431251881Speter}
1432251881Speter
1433251881Spetersvn_error_t *
1434251881Spetersvn_wc__diff_base_only_dir(svn_wc__db_t *db,
1435251881Speter                           const char *local_abspath,
1436251881Speter                           const char *relpath,
1437251881Speter                           svn_revnum_t revision,
1438251881Speter                           svn_depth_t depth,
1439251881Speter                           const svn_diff_tree_processor_t *processor,
1440251881Speter                           void *processor_parent_baton,
1441251881Speter                           svn_cancel_func_t cancel_func,
1442251881Speter                           void *cancel_baton,
1443251881Speter                           apr_pool_t *scratch_pool)
1444251881Speter{
1445251881Speter  void *dir_baton = NULL;
1446251881Speter  svn_boolean_t skip = FALSE;
1447251881Speter  svn_boolean_t skip_children = FALSE;
1448251881Speter  svn_diff_source_t *left_src;
1449251881Speter  svn_revnum_t report_rev = revision;
1450251881Speter
1451251881Speter  if (!SVN_IS_VALID_REVNUM(report_rev))
1452251881Speter    SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
1453251881Speter                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1454251881Speter                                     NULL, NULL, NULL,
1455251881Speter                                     db, local_abspath,
1456251881Speter                                     scratch_pool, scratch_pool));
1457251881Speter
1458251881Speter  left_src = svn_diff__source_create(report_rev, scratch_pool);
1459251881Speter
1460251881Speter  SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
1461251881Speter                                relpath,
1462251881Speter                                left_src,
1463251881Speter                                NULL /* right_src */,
1464251881Speter                                NULL /* copyfrom_src */,
1465251881Speter                                processor_parent_baton,
1466251881Speter                                processor,
1467251881Speter                                scratch_pool, scratch_pool));
1468251881Speter
1469251881Speter  if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
1470251881Speter    {
1471251881Speter      apr_hash_t *nodes;
1472251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1473251881Speter      apr_array_header_t *children;
1474251881Speter      int i;
1475251881Speter
1476251881Speter      SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
1477251881Speter                                                scratch_pool, iterpool));
1478251881Speter
1479251881Speter      children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1480251881Speter                                scratch_pool);
1481251881Speter
1482251881Speter      for (i = 0; i < children->nelts; i++)
1483251881Speter        {
1484251881Speter          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1485251881Speter                                                  svn_sort__item_t);
1486251881Speter          const char *name = item->key;
1487251881Speter          struct svn_wc__db_base_info_t *info = item->value;
1488251881Speter          const char *child_abspath;
1489251881Speter          const char *child_relpath;
1490251881Speter
1491251881Speter          if (info->status != svn_wc__db_status_normal)
1492251881Speter            continue;
1493251881Speter
1494251881Speter          if (cancel_func)
1495251881Speter            SVN_ERR(cancel_func(cancel_baton));
1496251881Speter
1497251881Speter          svn_pool_clear(iterpool);
1498251881Speter
1499251881Speter          child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1500251881Speter          child_relpath = svn_relpath_join(relpath, name, iterpool);
1501251881Speter
1502251881Speter          switch (info->kind)
1503251881Speter            {
1504251881Speter              case svn_node_file:
1505251881Speter              case svn_node_symlink:
1506251881Speter                SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
1507251881Speter                                                    child_relpath,
1508251881Speter                                                    revision,
1509251881Speter                                                    processor, dir_baton,
1510251881Speter                                                    iterpool));
1511251881Speter                break;
1512251881Speter              case svn_node_dir:
1513251881Speter                if (depth > svn_depth_files || depth == svn_depth_unknown)
1514251881Speter                  {
1515251881Speter                    svn_depth_t depth_below_here = depth;
1516251881Speter
1517251881Speter                    if (depth_below_here == svn_depth_immediates)
1518251881Speter                      depth_below_here = svn_depth_empty;
1519251881Speter
1520251881Speter                    SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
1521251881Speter                                                       child_relpath,
1522251881Speter                                                       revision,
1523251881Speter                                                       depth_below_here,
1524251881Speter                                                       processor, dir_baton,
1525251881Speter                                                       cancel_func,
1526251881Speter                                                       cancel_baton,
1527251881Speter                                                       iterpool));
1528251881Speter                  }
1529251881Speter                break;
1530251881Speter
1531251881Speter              default:
1532251881Speter                break;
1533251881Speter            }
1534251881Speter        }
1535251881Speter    }
1536251881Speter
1537251881Speter  if (!skip)
1538251881Speter    {
1539251881Speter      apr_hash_t *props;
1540251881Speter      SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
1541251881Speter                                        scratch_pool, scratch_pool));
1542251881Speter
1543251881Speter      SVN_ERR(processor->dir_deleted(relpath,
1544251881Speter                                     left_src,
1545251881Speter                                     props,
1546251881Speter                                     dir_baton,
1547251881Speter                                     processor,
1548251881Speter                                     scratch_pool));
1549251881Speter    }
1550251881Speter
1551251881Speter  return SVN_NO_ERROR;
1552251881Speter}
1553251881Speter
1554251881Speter/* An svn_delta_editor_t function. */
1555251881Speterstatic svn_error_t *
1556251881Speterset_target_revision(void *edit_baton,
1557251881Speter                    svn_revnum_t target_revision,
1558251881Speter                    apr_pool_t *pool)
1559251881Speter{
1560251881Speter  struct edit_baton_t *eb = edit_baton;
1561251881Speter  eb->revnum = target_revision;
1562251881Speter
1563251881Speter  return SVN_NO_ERROR;
1564251881Speter}
1565251881Speter
1566251881Speter/* An svn_delta_editor_t function. The root of the comparison hierarchy */
1567251881Speterstatic svn_error_t *
1568251881Speteropen_root(void *edit_baton,
1569251881Speter          svn_revnum_t base_revision,
1570251881Speter          apr_pool_t *dir_pool,
1571251881Speter          void **root_baton)
1572251881Speter{
1573251881Speter  struct edit_baton_t *eb = edit_baton;
1574251881Speter  struct dir_baton_t *db;
1575251881Speter
1576251881Speter  eb->root_opened = TRUE;
1577251881Speter  db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
1578251881Speter  *root_baton = db;
1579251881Speter
1580251881Speter  if (eb->target[0] == '\0')
1581251881Speter    {
1582251881Speter      db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1583251881Speter      db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1584251881Speter
1585251881Speter      SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
1586251881Speter                                        &db->skip_children,
1587251881Speter                                        "",
1588251881Speter                                        db->left_src,
1589251881Speter                                        db->right_src,
1590251881Speter                                        NULL /* copyfrom_source */,
1591251881Speter                                        NULL /* parent_baton */,
1592251881Speter                                        eb->processor,
1593251881Speter                                        db->pool, db->pool));
1594251881Speter    }
1595251881Speter  else
1596251881Speter    db->skip = TRUE; /* Skip this, but not the children */
1597251881Speter
1598251881Speter  return SVN_NO_ERROR;
1599251881Speter}
1600251881Speter
1601251881Speter/* An svn_delta_editor_t function. */
1602251881Speterstatic svn_error_t *
1603251881Speterdelete_entry(const char *path,
1604251881Speter             svn_revnum_t base_revision,
1605251881Speter             void *parent_baton,
1606251881Speter             apr_pool_t *pool)
1607251881Speter{
1608251881Speter  struct dir_baton_t *pb = parent_baton;
1609251881Speter  const char *name = svn_dirent_basename(path, pb->pool);
1610251881Speter
1611251881Speter  if (!pb->deletes)
1612251881Speter    pb->deletes = apr_hash_make(pb->pool);
1613251881Speter
1614251881Speter  svn_hash_sets(pb->deletes, name, "");
1615251881Speter  return SVN_NO_ERROR;
1616251881Speter}
1617251881Speter
1618251881Speter/* An svn_delta_editor_t function. */
1619251881Speterstatic svn_error_t *
1620251881Speteradd_directory(const char *path,
1621251881Speter              void *parent_baton,
1622251881Speter              const char *copyfrom_path,
1623251881Speter              svn_revnum_t copyfrom_revision,
1624251881Speter              apr_pool_t *dir_pool,
1625251881Speter              void **child_baton)
1626251881Speter{
1627251881Speter  struct dir_baton_t *pb = parent_baton;
1628251881Speter  struct edit_baton_t *eb = pb->eb;
1629251881Speter  struct dir_baton_t *db;
1630251881Speter  svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1631251881Speter                              ? svn_depth_empty : pb->depth;
1632251881Speter
1633251881Speter  db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
1634251881Speter                      dir_pool);
1635251881Speter  *child_baton = db;
1636251881Speter
1637251881Speter  if (pb->repos_only || !eb->ignore_ancestry)
1638251881Speter    db->repos_only = TRUE;
1639251881Speter  else
1640251881Speter    {
1641251881Speter      struct svn_wc__db_info_t *info;
1642251881Speter      SVN_ERR(ensure_local_info(pb, dir_pool));
1643251881Speter
1644251881Speter      info = svn_hash_gets(pb->local_info, db->name);
1645251881Speter
1646251881Speter      if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1647251881Speter        db->repos_only = TRUE;
1648251881Speter
1649251881Speter      if (!db->repos_only && info->status != svn_wc__db_status_added)
1650251881Speter        db->repos_only = TRUE;
1651251881Speter
1652251881Speter      if (!db->repos_only)
1653251881Speter        {
1654251881Speter          db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1655251881Speter          db->ignoring_ancestry = TRUE;
1656251881Speter
1657251881Speter          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1658251881Speter        }
1659251881Speter    }
1660251881Speter
1661251881Speter  db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1662251881Speter
1663251881Speter  if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1664251881Speter    SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1665251881Speter
1666251881Speter  SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1667251881Speter                                    db->relpath,
1668251881Speter                                    db->left_src,
1669251881Speter                                    db->right_src,
1670251881Speter                                    NULL /* copyfrom src */,
1671251881Speter                                    pb->pdb,
1672251881Speter                                    eb->processor,
1673251881Speter                                    db->pool, db->pool));
1674251881Speter
1675251881Speter  return SVN_NO_ERROR;
1676251881Speter}
1677251881Speter
1678251881Speter/* An svn_delta_editor_t function. */
1679251881Speterstatic svn_error_t *
1680251881Speteropen_directory(const char *path,
1681251881Speter               void *parent_baton,
1682251881Speter               svn_revnum_t base_revision,
1683251881Speter               apr_pool_t *dir_pool,
1684251881Speter               void **child_baton)
1685251881Speter{
1686251881Speter  struct dir_baton_t *pb = parent_baton;
1687251881Speter  struct edit_baton_t *eb = pb->eb;
1688251881Speter  struct dir_baton_t *db;
1689251881Speter  svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1690251881Speter                              ? svn_depth_empty : pb->depth;
1691251881Speter
1692251881Speter  /* Allocate path from the parent pool since the memory is used in the
1693251881Speter     parent's compared hash */
1694251881Speter  db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
1695251881Speter  *child_baton = db;
1696251881Speter
1697251881Speter  if (pb->repos_only)
1698251881Speter    db->repos_only = TRUE;
1699251881Speter  else
1700251881Speter    {
1701251881Speter      struct svn_wc__db_info_t *info;
1702251881Speter      SVN_ERR(ensure_local_info(pb, dir_pool));
1703251881Speter
1704251881Speter      info = svn_hash_gets(pb->local_info, db->name);
1705251881Speter
1706251881Speter      if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1707251881Speter        db->repos_only = TRUE;
1708251881Speter
1709251881Speter      if (!db->repos_only)
1710362181Sdim        {
1711362181Sdim          switch (info->status)
1712362181Sdim            {
1713362181Sdim              case svn_wc__db_status_normal:
1714362181Sdim              case svn_wc__db_status_incomplete:
1715362181Sdim                db->is_incomplete = (info->status ==
1716362181Sdim                                     svn_wc__db_status_incomplete);
1717362181Sdim                break;
1718362181Sdim              case svn_wc__db_status_deleted:
1719362181Sdim                db->repos_only = TRUE;
1720251881Speter
1721362181Sdim                if (!info->have_more_work)
1722362181Sdim                  svn_hash_sets(pb->compared,
1723362181Sdim                                apr_pstrdup(pb->pool, db->name), "");
1724362181Sdim                break;
1725362181Sdim              case svn_wc__db_status_added:
1726362181Sdim                if (eb->ignore_ancestry)
1727362181Sdim                  db->ignoring_ancestry = TRUE;
1728362181Sdim                else
1729362181Sdim                  db->repos_only = TRUE;
1730362181Sdim                break;
1731362181Sdim              default:
1732362181Sdim                SVN_ERR_MALFUNCTION();
1733362181Sdim          }
1734362181Sdim
1735362181Sdim          if (info->status == svn_wc__db_status_added
1736362181Sdim              || info->status == svn_wc__db_status_deleted)
1737362181Sdim            {
1738362181Sdim              svn_wc__db_status_t base_status;
1739362181Sdim
1740362181Sdim              /* Node is shadowed; check BASE */
1741362181Sdim              SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, NULL,
1742362181Sdim                                               NULL, NULL, NULL, NULL, NULL,
1743362181Sdim                                               NULL, NULL, NULL, NULL, NULL,
1744362181Sdim                                               NULL, NULL, NULL,
1745362181Sdim                                               eb->db, db->local_abspath,
1746362181Sdim                                               dir_pool, dir_pool));
1747362181Sdim
1748362181Sdim              if (base_status == svn_wc__db_status_incomplete)
1749362181Sdim                db->is_incomplete = TRUE;
1750362181Sdim            }
1751251881Speter        }
1752251881Speter
1753251881Speter      if (!db->repos_only)
1754251881Speter        {
1755251881Speter          db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1756251881Speter          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1757251881Speter        }
1758251881Speter    }
1759251881Speter
1760251881Speter  db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1761251881Speter
1762251881Speter  if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1763251881Speter    SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1764251881Speter
1765251881Speter  SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1766251881Speter                                    db->relpath,
1767251881Speter                                    db->left_src,
1768251881Speter                                    db->right_src,
1769251881Speter                                    NULL /* copyfrom src */,
1770251881Speter                                    pb->pdb,
1771251881Speter                                    eb->processor,
1772251881Speter                                    db->pool, db->pool));
1773251881Speter
1774251881Speter  return SVN_NO_ERROR;
1775251881Speter}
1776251881Speter
1777251881Speter
1778251881Speter/* An svn_delta_editor_t function.  When a directory is closed, all the
1779251881Speter * directory elements that have been added or replaced will already have been
1780251881Speter * diff'd. However there may be other elements in the working copy
1781251881Speter * that have not yet been considered.  */
1782251881Speterstatic svn_error_t *
1783251881Speterclose_directory(void *dir_baton,
1784251881Speter                apr_pool_t *pool)
1785251881Speter{
1786251881Speter  struct dir_baton_t *db = dir_baton;
1787251881Speter  struct dir_baton_t *pb = db->parent_baton;
1788251881Speter  struct edit_baton_t *eb = db->eb;
1789251881Speter  apr_pool_t *scratch_pool = db->pool;
1790251881Speter  svn_boolean_t reported_closed = FALSE;
1791251881Speter
1792251881Speter  if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
1793251881Speter    {
1794251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1795251881Speter      apr_array_header_t *children;
1796251881Speter      int i;
1797251881Speter      children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
1798251881Speter                                scratch_pool);
1799251881Speter
1800251881Speter      for (i = 0; i < children->nelts; i++)
1801251881Speter        {
1802251881Speter          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1803251881Speter                                                  svn_sort__item_t);
1804251881Speter          const char *name = item->key;
1805251881Speter
1806251881Speter          svn_pool_clear(iterpool);
1807251881Speter          SVN_ERR(handle_local_only(db, name, iterpool));
1808251881Speter
1809251881Speter          svn_hash_sets(db->compared, name, "");
1810251881Speter        }
1811251881Speter
1812251881Speter      svn_pool_destroy(iterpool);
1813251881Speter    }
1814251881Speter
1815251881Speter  /* Report local modifications for this directory.  Skip added
1816251881Speter     directories since they can only contain added elements, all of
1817251881Speter     which have already been diff'd. */
1818251881Speter  if (!db->repos_only && !db->skip_children)
1819251881Speter  {
1820251881Speter    SVN_ERR(walk_local_nodes_diff(eb,
1821251881Speter                                  db->local_abspath,
1822251881Speter                                  db->relpath,
1823251881Speter                                  db->depth,
1824251881Speter                                  db->compared,
1825251881Speter                                  db->pdb,
1826251881Speter                                  scratch_pool));
1827251881Speter  }
1828251881Speter
1829251881Speter  /* Report the property changes on the directory itself, if necessary. */
1830251881Speter  if (db->skip)
1831251881Speter    {
1832251881Speter      /* Diff processor requested no directory details */
1833251881Speter    }
1834251881Speter  else if (db->propchanges->nelts > 0 || db->repos_only)
1835251881Speter    {
1836251881Speter      apr_hash_t *repos_props;
1837251881Speter
1838362181Sdim      if (db->added || db->is_incomplete)
1839251881Speter        {
1840251881Speter          repos_props = apr_hash_make(scratch_pool);
1841251881Speter        }
1842251881Speter      else
1843251881Speter        {
1844251881Speter          SVN_ERR(svn_wc__db_base_get_props(&repos_props,
1845251881Speter                                            eb->db, db->local_abspath,
1846251881Speter                                            scratch_pool, scratch_pool));
1847251881Speter        }
1848251881Speter
1849251881Speter      /* Add received property changes and entry props */
1850251881Speter      if (db->propchanges->nelts)
1851251881Speter        repos_props = svn_prop__patch(repos_props, db->propchanges,
1852251881Speter                                      scratch_pool);
1853251881Speter
1854251881Speter      if (db->repos_only)
1855251881Speter        {
1856251881Speter          SVN_ERR(eb->processor->dir_deleted(db->relpath,
1857251881Speter                                             db->left_src,
1858251881Speter                                             repos_props,
1859251881Speter                                             db->pdb,
1860251881Speter                                             eb->processor,
1861251881Speter                                             scratch_pool));
1862251881Speter          reported_closed = TRUE;
1863251881Speter        }
1864251881Speter      else
1865251881Speter        {
1866251881Speter          apr_hash_t *local_props;
1867251881Speter          apr_array_header_t *prop_changes;
1868251881Speter
1869251881Speter          if (eb->diff_pristine)
1870251881Speter            SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1871251881Speter                                                  NULL, NULL, NULL, NULL,
1872251881Speter                                                  &local_props,
1873251881Speter                                                  eb->db, db->local_abspath,
1874251881Speter                                                  scratch_pool, scratch_pool));
1875251881Speter          else
1876251881Speter            SVN_ERR(svn_wc__db_read_props(&local_props,
1877251881Speter                                          eb->db, db->local_abspath,
1878251881Speter                                          scratch_pool, scratch_pool));
1879251881Speter
1880251881Speter          SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
1881251881Speter                                 scratch_pool));
1882251881Speter
1883251881Speter          /* ### as a good diff processor we should now only report changes
1884251881Speter                 if there are non-entry changes, but for now we stick to
1885251881Speter                 compatibility */
1886251881Speter
1887251881Speter          if (prop_changes->nelts)
1888251881Speter            {
1889251881Speter              SVN_ERR(eb->processor->dir_changed(db->relpath,
1890251881Speter                                                 db->left_src,
1891251881Speter                                                 db->right_src,
1892251881Speter                                                 repos_props,
1893251881Speter                                                 local_props,
1894251881Speter                                                 prop_changes,
1895251881Speter                                                 db->pdb,
1896251881Speter                                                 eb->processor,
1897251881Speter                                                 scratch_pool));
1898251881Speter              reported_closed = TRUE;
1899251881Speter          }
1900251881Speter        }
1901251881Speter    }
1902251881Speter
1903251881Speter  /* Mark this directory as compared in the parent directory's baton,
1904251881Speter     unless this is the root of the comparison. */
1905251881Speter  if (!reported_closed && !db->skip)
1906251881Speter    SVN_ERR(eb->processor->dir_closed(db->relpath,
1907251881Speter                                      db->left_src,
1908251881Speter                                      db->right_src,
1909251881Speter                                      db->pdb,
1910251881Speter                                      eb->processor,
1911251881Speter                                      scratch_pool));
1912251881Speter
1913251881Speter  if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1914251881Speter    SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
1915251881Speter
1916251881Speter  SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
1917251881Speter
1918251881Speter  return SVN_NO_ERROR;
1919251881Speter}
1920251881Speter
1921251881Speter/* An svn_delta_editor_t function. */
1922251881Speterstatic svn_error_t *
1923251881Speteradd_file(const char *path,
1924251881Speter         void *parent_baton,
1925251881Speter         const char *copyfrom_path,
1926251881Speter         svn_revnum_t copyfrom_revision,
1927251881Speter         apr_pool_t *file_pool,
1928251881Speter         void **file_baton)
1929251881Speter{
1930251881Speter  struct dir_baton_t *pb = parent_baton;
1931251881Speter  struct edit_baton_t *eb = pb->eb;
1932251881Speter  struct file_baton_t *fb;
1933251881Speter
1934251881Speter  fb = make_file_baton(path, TRUE, pb, file_pool);
1935251881Speter  *file_baton = fb;
1936251881Speter
1937251881Speter  if (pb->skip_children)
1938251881Speter    {
1939251881Speter      fb->skip = TRUE;
1940251881Speter      return SVN_NO_ERROR;
1941251881Speter    }
1942251881Speter  else if (pb->repos_only || !eb->ignore_ancestry)
1943251881Speter    fb->repos_only = TRUE;
1944251881Speter  else
1945251881Speter    {
1946251881Speter      struct svn_wc__db_info_t *info;
1947251881Speter      SVN_ERR(ensure_local_info(pb, file_pool));
1948251881Speter
1949251881Speter      info = svn_hash_gets(pb->local_info, fb->name);
1950251881Speter
1951251881Speter      if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1952251881Speter        fb->repos_only = TRUE;
1953251881Speter
1954251881Speter      if (!fb->repos_only && info->status != svn_wc__db_status_added)
1955251881Speter        fb->repos_only = TRUE;
1956251881Speter
1957251881Speter      if (!fb->repos_only)
1958251881Speter        {
1959251881Speter          /* Add this path to the parent directory's list of elements that
1960251881Speter             have been compared. */
1961251881Speter          fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1962251881Speter          fb->ignoring_ancestry = TRUE;
1963251881Speter
1964251881Speter          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1965251881Speter        }
1966251881Speter    }
1967251881Speter
1968251881Speter  fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1969251881Speter
1970251881Speter  SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1971251881Speter                                     fb->relpath,
1972251881Speter                                     fb->left_src,
1973251881Speter                                     fb->right_src,
1974251881Speter                                     NULL /* copyfrom src */,
1975251881Speter                                     pb->pdb,
1976251881Speter                                     eb->processor,
1977251881Speter                                     fb->pool, fb->pool));
1978251881Speter
1979251881Speter  return SVN_NO_ERROR;
1980251881Speter}
1981251881Speter
1982251881Speter/* An svn_delta_editor_t function. */
1983251881Speterstatic svn_error_t *
1984251881Speteropen_file(const char *path,
1985251881Speter          void *parent_baton,
1986251881Speter          svn_revnum_t base_revision,
1987251881Speter          apr_pool_t *file_pool,
1988251881Speter          void **file_baton)
1989251881Speter{
1990251881Speter  struct dir_baton_t *pb = parent_baton;
1991251881Speter  struct edit_baton_t *eb = pb->eb;
1992251881Speter  struct file_baton_t *fb;
1993251881Speter
1994251881Speter  fb = make_file_baton(path, FALSE, pb, file_pool);
1995251881Speter  *file_baton = fb;
1996251881Speter
1997251881Speter  if (pb->skip_children)
1998251881Speter    fb->skip = TRUE;
1999251881Speter  else if (pb->repos_only)
2000251881Speter    fb->repos_only = TRUE;
2001251881Speter  else
2002251881Speter    {
2003251881Speter      struct svn_wc__db_info_t *info;
2004251881Speter      SVN_ERR(ensure_local_info(pb, file_pool));
2005251881Speter
2006251881Speter      info = svn_hash_gets(pb->local_info, fb->name);
2007251881Speter
2008251881Speter      if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
2009251881Speter        fb->repos_only = TRUE;
2010251881Speter
2011251881Speter      if (!fb->repos_only)
2012251881Speter        switch (info->status)
2013251881Speter          {
2014251881Speter            case svn_wc__db_status_normal:
2015362181Sdim            case svn_wc__db_status_incomplete:
2016251881Speter              break;
2017251881Speter            case svn_wc__db_status_deleted:
2018251881Speter              fb->repos_only = TRUE;
2019251881Speter              if (!info->have_more_work)
2020251881Speter                svn_hash_sets(pb->compared,
2021251881Speter                              apr_pstrdup(pb->pool, fb->name), "");
2022251881Speter              break;
2023251881Speter            case svn_wc__db_status_added:
2024251881Speter              if (eb->ignore_ancestry)
2025251881Speter                fb->ignoring_ancestry = TRUE;
2026251881Speter              else
2027251881Speter                fb->repos_only = TRUE;
2028251881Speter              break;
2029251881Speter            default:
2030251881Speter              SVN_ERR_MALFUNCTION();
2031251881Speter        }
2032251881Speter
2033251881Speter      if (!fb->repos_only)
2034251881Speter        {
2035251881Speter          /* Add this path to the parent directory's list of elements that
2036251881Speter             have been compared. */
2037251881Speter          fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
2038251881Speter          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
2039251881Speter        }
2040251881Speter    }
2041251881Speter
2042251881Speter  fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
2043251881Speter
2044251881Speter  SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2045251881Speter                                   NULL, NULL, NULL, &fb->base_checksum, NULL,
2046251881Speter                                   NULL, NULL, &fb->base_props, NULL,
2047251881Speter                                   eb->db, fb->local_abspath,
2048251881Speter                                   fb->pool, fb->pool));
2049251881Speter
2050251881Speter  SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
2051251881Speter                                     fb->relpath,
2052251881Speter                                     fb->left_src,
2053251881Speter                                     fb->right_src,
2054251881Speter                                     NULL /* copyfrom src */,
2055251881Speter                                     pb->pdb,
2056251881Speter                                     eb->processor,
2057251881Speter                                     fb->pool, fb->pool));
2058251881Speter
2059251881Speter  return SVN_NO_ERROR;
2060251881Speter}
2061251881Speter
2062251881Speter/* An svn_delta_editor_t function. */
2063251881Speterstatic svn_error_t *
2064251881Speterapply_textdelta(void *file_baton,
2065251881Speter                const char *base_checksum_hex,
2066251881Speter                apr_pool_t *pool,
2067251881Speter                svn_txdelta_window_handler_t *handler,
2068251881Speter                void **handler_baton)
2069251881Speter{
2070251881Speter  struct file_baton_t *fb = file_baton;
2071251881Speter  struct edit_baton_t *eb = fb->eb;
2072251881Speter  svn_stream_t *source;
2073251881Speter  svn_stream_t *temp_stream;
2074251881Speter  svn_checksum_t *repos_checksum = NULL;
2075251881Speter
2076251881Speter  if (fb->skip)
2077251881Speter    {
2078251881Speter      *handler = svn_delta_noop_window_handler;
2079251881Speter      *handler_baton = NULL;
2080251881Speter      return SVN_NO_ERROR;
2081251881Speter    }
2082251881Speter
2083251881Speter  if (base_checksum_hex && fb->base_checksum)
2084251881Speter    {
2085251881Speter      const svn_checksum_t *base_md5;
2086251881Speter      SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
2087251881Speter                                     base_checksum_hex, pool));
2088251881Speter
2089251881Speter      SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
2090251881Speter                                          eb->db, eb->anchor_abspath,
2091251881Speter                                          fb->base_checksum,
2092251881Speter                                          pool, pool));
2093251881Speter
2094251881Speter      if (! svn_checksum_match(repos_checksum, base_md5))
2095251881Speter        {
2096251881Speter          /* ### I expect that there are some bad drivers out there
2097251881Speter             ### that used to give bad results. We could look in
2098251881Speter             ### working to see if the expected checksum matches and
2099251881Speter             ### then return the pristine of that... But that only moves
2100251881Speter             ### the problem */
2101251881Speter
2102251881Speter          /* If needed: compare checksum obtained via md5 of working.
2103251881Speter             And if they match set fb->base_checksum and fb->base_props */
2104251881Speter
2105251881Speter          return svn_checksum_mismatch_err(
2106251881Speter                        base_md5,
2107251881Speter                        repos_checksum,
2108251881Speter                        pool,
2109251881Speter                        _("Checksum mismatch for '%s'"),
2110251881Speter                        svn_dirent_local_style(fb->local_abspath,
2111251881Speter                                               pool));
2112251881Speter        }
2113251881Speter
2114251881Speter      SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
2115251881Speter                                       eb->db, fb->local_abspath,
2116251881Speter                                       fb->base_checksum,
2117251881Speter                                       pool, pool));
2118251881Speter    }
2119251881Speter  else if (fb->base_checksum)
2120251881Speter    {
2121251881Speter      SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
2122251881Speter                                       eb->db, fb->local_abspath,
2123251881Speter                                       fb->base_checksum,
2124251881Speter                                       pool, pool));
2125251881Speter    }
2126251881Speter  else
2127251881Speter    source = svn_stream_empty(pool);
2128251881Speter
2129251881Speter  /* This is the file that will contain the pristine repository version. */
2130251881Speter  SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
2131251881Speter                                 svn_io_file_del_on_pool_cleanup,
2132251881Speter                                 fb->pool, fb->pool));
2133251881Speter
2134251881Speter  svn_txdelta_apply(source, temp_stream,
2135251881Speter                    fb->result_digest,
2136251881Speter                    fb->local_abspath /* error_info */,
2137251881Speter                    fb->pool,
2138251881Speter                    handler, handler_baton);
2139251881Speter
2140251881Speter  return SVN_NO_ERROR;
2141251881Speter}
2142251881Speter
2143251881Speter/* An svn_delta_editor_t function.  When the file is closed we have a temporary
2144251881Speter * file containing a pristine version of the repository file. This can
2145251881Speter * be compared against the working copy.
2146251881Speter *
2147251881Speter * Ignore TEXT_CHECKSUM.
2148251881Speter */
2149251881Speterstatic svn_error_t *
2150251881Speterclose_file(void *file_baton,
2151251881Speter           const char *expected_md5_digest,
2152251881Speter           apr_pool_t *pool)
2153251881Speter{
2154251881Speter  struct file_baton_t *fb = file_baton;
2155251881Speter  struct dir_baton_t *pb = fb->parent_baton;
2156251881Speter  struct edit_baton_t *eb = fb->eb;
2157251881Speter  apr_pool_t *scratch_pool = fb->pool;
2158251881Speter
2159251881Speter  /* The repository information; constructed from BASE + Changes */
2160251881Speter  const char *repos_file;
2161251881Speter  apr_hash_t *repos_props;
2162251881Speter
2163286506Speter  if (fb->skip)
2164251881Speter    {
2165286506Speter      svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2166286506Speter      SVN_ERR(maybe_done(pb));
2167286506Speter      return SVN_NO_ERROR;
2168286506Speter    }
2169286506Speter
2170286506Speter  if (expected_md5_digest != NULL)
2171286506Speter    {
2172251881Speter      svn_checksum_t *expected_checksum;
2173251881Speter      const svn_checksum_t *result_checksum;
2174251881Speter
2175251881Speter      if (fb->temp_file_path)
2176251881Speter        result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
2177251881Speter                                                        scratch_pool);
2178251881Speter      else
2179251881Speter        result_checksum = fb->base_checksum;
2180251881Speter
2181251881Speter      SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
2182251881Speter                                     expected_md5_digest, scratch_pool));
2183251881Speter
2184251881Speter      if (result_checksum->kind != svn_checksum_md5)
2185251881Speter        SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
2186251881Speter                                            eb->db, fb->local_abspath,
2187251881Speter                                            result_checksum,
2188251881Speter                                            scratch_pool, scratch_pool));
2189251881Speter
2190251881Speter      if (!svn_checksum_match(expected_checksum, result_checksum))
2191251881Speter        return svn_checksum_mismatch_err(
2192251881Speter                            expected_checksum,
2193251881Speter                            result_checksum,
2194251881Speter                            pool,
2195251881Speter                            _("Checksum mismatch for '%s'"),
2196251881Speter                            svn_dirent_local_style(fb->local_abspath,
2197251881Speter                                                   scratch_pool));
2198251881Speter    }
2199251881Speter
2200251881Speter  if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2201251881Speter    SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2202251881Speter
2203251881Speter  {
2204251881Speter    apr_hash_t *prop_base;
2205251881Speter
2206251881Speter    if (fb->added)
2207251881Speter      prop_base = apr_hash_make(scratch_pool);
2208251881Speter    else
2209251881Speter      prop_base = fb->base_props;
2210251881Speter
2211251881Speter    /* includes entry props */
2212251881Speter    repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
2213251881Speter
2214251881Speter    repos_file = fb->temp_file_path;
2215251881Speter    if (! repos_file)
2216251881Speter      {
2217251881Speter        assert(fb->base_checksum);
2218251881Speter        SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
2219251881Speter                                             eb->db, eb->anchor_abspath,
2220251881Speter                                             fb->base_checksum,
2221251881Speter                                             scratch_pool, scratch_pool));
2222251881Speter      }
2223251881Speter  }
2224251881Speter
2225286506Speter  if (fb->repos_only)
2226251881Speter    {
2227251881Speter      SVN_ERR(eb->processor->file_deleted(fb->relpath,
2228251881Speter                                          fb->left_src,
2229251881Speter                                          fb->temp_file_path,
2230251881Speter                                          repos_props,
2231251881Speter                                          fb->pfb,
2232251881Speter                                          eb->processor,
2233251881Speter                                          scratch_pool));
2234251881Speter    }
2235251881Speter  else
2236251881Speter    {
2237251881Speter      /* Produce a diff of actual or pristine against repos */
2238251881Speter      apr_hash_t *local_props;
2239251881Speter      apr_array_header_t *prop_changes;
2240251881Speter      const char *localfile;
2241251881Speter
2242251881Speter      /* pb->local_info contains some information that might allow optimizing
2243251881Speter         this a bit */
2244251881Speter
2245251881Speter      if (eb->diff_pristine)
2246251881Speter        {
2247251881Speter          const svn_checksum_t *checksum;
2248251881Speter          SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
2249251881Speter                                                NULL, &checksum, NULL, NULL,
2250251881Speter                                                &local_props,
2251251881Speter                                                eb->db, fb->local_abspath,
2252251881Speter                                                scratch_pool, scratch_pool));
2253251881Speter          assert(checksum);
2254251881Speter          SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
2255251881Speter                                               eb->db, eb->anchor_abspath,
2256251881Speter                                               checksum,
2257251881Speter                                               scratch_pool, scratch_pool));
2258251881Speter        }
2259251881Speter      else
2260251881Speter        {
2261251881Speter          SVN_ERR(svn_wc__db_read_props(&local_props,
2262251881Speter                                        eb->db, fb->local_abspath,
2263251881Speter                                        scratch_pool, scratch_pool));
2264251881Speter
2265251881Speter          /* a detranslated version of the working file */
2266251881Speter          SVN_ERR(svn_wc__internal_translated_file(
2267251881Speter                    &localfile, fb->local_abspath, eb->db, fb->local_abspath,
2268251881Speter                    SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
2269251881Speter                    eb->cancel_func, eb->cancel_baton,
2270251881Speter                    scratch_pool, scratch_pool));
2271251881Speter        }
2272251881Speter
2273251881Speter      SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
2274251881Speter                             scratch_pool));
2275251881Speter
2276251881Speter
2277251881Speter      /* ### as a good diff processor we should now only report changes, and
2278251881Speter             report file_closed() in other cases */
2279251881Speter      SVN_ERR(eb->processor->file_changed(fb->relpath,
2280251881Speter                                          fb->left_src,
2281251881Speter                                          fb->right_src,
2282251881Speter                                          repos_file /* left file */,
2283251881Speter                                          localfile /* right file */,
2284251881Speter                                          repos_props /* left_props */,
2285251881Speter                                          local_props /* right props */,
2286251881Speter                                          TRUE /* ### file_modified */,
2287251881Speter                                          prop_changes,
2288251881Speter                                          fb->pfb,
2289251881Speter                                          eb->processor,
2290251881Speter                                          scratch_pool));
2291251881Speter    }
2292251881Speter
2293251881Speter  if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2294251881Speter    SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2295251881Speter
2296251881Speter  svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2297251881Speter  SVN_ERR(maybe_done(pb));
2298251881Speter  return SVN_NO_ERROR;
2299251881Speter}
2300251881Speter
2301251881Speter
2302251881Speter/* An svn_delta_editor_t function. */
2303251881Speterstatic svn_error_t *
2304251881Speterchange_file_prop(void *file_baton,
2305251881Speter                 const char *name,
2306251881Speter                 const svn_string_t *value,
2307251881Speter                 apr_pool_t *pool)
2308251881Speter{
2309251881Speter  struct file_baton_t *fb = file_baton;
2310251881Speter  svn_prop_t *propchange;
2311251881Speter  svn_prop_kind_t propkind;
2312251881Speter
2313251881Speter  propkind = svn_property_kind2(name);
2314251881Speter  if (propkind == svn_prop_wc_kind)
2315251881Speter    return SVN_NO_ERROR;
2316251881Speter  else if (propkind == svn_prop_regular_kind)
2317251881Speter    fb->has_propchange = TRUE;
2318251881Speter
2319251881Speter  propchange = apr_array_push(fb->propchanges);
2320251881Speter  propchange->name = apr_pstrdup(fb->pool, name);
2321289180Speter  propchange->value = svn_string_dup(value, fb->pool);
2322251881Speter
2323251881Speter  return SVN_NO_ERROR;
2324251881Speter}
2325251881Speter
2326251881Speter
2327251881Speter/* An svn_delta_editor_t function. */
2328251881Speterstatic svn_error_t *
2329251881Speterchange_dir_prop(void *dir_baton,
2330251881Speter                const char *name,
2331251881Speter                const svn_string_t *value,
2332251881Speter                apr_pool_t *pool)
2333251881Speter{
2334251881Speter  struct dir_baton_t *db = dir_baton;
2335251881Speter  svn_prop_t *propchange;
2336251881Speter  svn_prop_kind_t propkind;
2337251881Speter
2338251881Speter  propkind = svn_property_kind2(name);
2339251881Speter  if (propkind == svn_prop_wc_kind)
2340251881Speter    return SVN_NO_ERROR;
2341251881Speter  else if (propkind == svn_prop_regular_kind)
2342251881Speter    db->has_propchange = TRUE;
2343251881Speter
2344251881Speter  propchange = apr_array_push(db->propchanges);
2345251881Speter  propchange->name = apr_pstrdup(db->pool, name);
2346289180Speter  propchange->value = svn_string_dup(value, db->pool);
2347251881Speter
2348251881Speter  return SVN_NO_ERROR;
2349251881Speter}
2350251881Speter
2351251881Speter
2352251881Speter/* An svn_delta_editor_t function. */
2353251881Speterstatic svn_error_t *
2354251881Speterclose_edit(void *edit_baton,
2355251881Speter           apr_pool_t *pool)
2356251881Speter{
2357251881Speter  struct edit_baton_t *eb = edit_baton;
2358251881Speter
2359251881Speter  if (!eb->root_opened)
2360251881Speter    {
2361251881Speter      SVN_ERR(walk_local_nodes_diff(eb,
2362251881Speter                                    eb->anchor_abspath,
2363251881Speter                                    "",
2364251881Speter                                    eb->depth,
2365251881Speter                                    NULL /* compared */,
2366251881Speter                                    NULL /* No parent_baton */,
2367251881Speter                                    eb->pool));
2368251881Speter    }
2369251881Speter
2370251881Speter  return SVN_NO_ERROR;
2371251881Speter}
2372251881Speter
2373251881Speter/* Public Interface */
2374251881Speter
2375251881Speter
2376251881Speter/* Create a diff editor and baton. */
2377251881Spetersvn_error_t *
2378251881Spetersvn_wc__get_diff_editor(const svn_delta_editor_t **editor,
2379251881Speter                        void **edit_baton,
2380251881Speter                        svn_wc_context_t *wc_ctx,
2381251881Speter                        const char *anchor_abspath,
2382251881Speter                        const char *target,
2383251881Speter                        svn_depth_t depth,
2384251881Speter                        svn_boolean_t ignore_ancestry,
2385251881Speter                        svn_boolean_t use_text_base,
2386251881Speter                        svn_boolean_t reverse_order,
2387251881Speter                        svn_boolean_t server_performs_filtering,
2388251881Speter                        const apr_array_header_t *changelist_filter,
2389289180Speter                        const svn_diff_tree_processor_t *diff_processor,
2390251881Speter                        svn_cancel_func_t cancel_func,
2391251881Speter                        void *cancel_baton,
2392251881Speter                        apr_pool_t *result_pool,
2393251881Speter                        apr_pool_t *scratch_pool)
2394251881Speter{
2395251881Speter  struct edit_baton_t *eb;
2396251881Speter  void *inner_baton;
2397251881Speter  svn_delta_editor_t *tree_editor;
2398251881Speter  const svn_delta_editor_t *inner_editor;
2399251881Speter  struct svn_wc__shim_fetch_baton_t *sfb;
2400251881Speter  svn_delta_shim_callbacks_t *shim_callbacks =
2401251881Speter                                svn_delta_shim_callbacks_default(result_pool);
2402251881Speter
2403251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
2404251881Speter
2405286506Speter  /* Apply changelist filtering to the output */
2406286506Speter  if (changelist_filter && changelist_filter->nelts)
2407286506Speter    {
2408286506Speter      apr_hash_t *changelist_hash;
2409286506Speter
2410286506Speter      SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
2411286506Speter                                         result_pool));
2412286506Speter      diff_processor = svn_wc__changelist_filter_tree_processor_create(
2413286506Speter                         diff_processor, wc_ctx, anchor_abspath,
2414286506Speter                         changelist_hash, result_pool);
2415286506Speter    }
2416286506Speter
2417251881Speter  SVN_ERR(make_edit_baton(&eb,
2418251881Speter                          wc_ctx->db,
2419251881Speter                          anchor_abspath, target,
2420286506Speter                          diff_processor,
2421289180Speter                          depth, ignore_ancestry,
2422286506Speter                          use_text_base, reverse_order,
2423251881Speter                          cancel_func, cancel_baton,
2424251881Speter                          result_pool));
2425251881Speter
2426251881Speter  tree_editor = svn_delta_default_editor(eb->pool);
2427251881Speter
2428251881Speter  tree_editor->set_target_revision = set_target_revision;
2429251881Speter  tree_editor->open_root = open_root;
2430251881Speter  tree_editor->delete_entry = delete_entry;
2431251881Speter  tree_editor->add_directory = add_directory;
2432251881Speter  tree_editor->open_directory = open_directory;
2433251881Speter  tree_editor->close_directory = close_directory;
2434251881Speter  tree_editor->add_file = add_file;
2435251881Speter  tree_editor->open_file = open_file;
2436251881Speter  tree_editor->apply_textdelta = apply_textdelta;
2437251881Speter  tree_editor->change_file_prop = change_file_prop;
2438251881Speter  tree_editor->change_dir_prop = change_dir_prop;
2439251881Speter  tree_editor->close_file = close_file;
2440251881Speter  tree_editor->close_edit = close_edit;
2441251881Speter
2442251881Speter  inner_editor = tree_editor;
2443251881Speter  inner_baton = eb;
2444251881Speter
2445251881Speter  if (!server_performs_filtering
2446251881Speter      && depth == svn_depth_unknown)
2447251881Speter    SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2448251881Speter                                                &inner_baton,
2449251881Speter                                                wc_ctx->db,
2450251881Speter                                                anchor_abspath,
2451251881Speter                                                target,
2452251881Speter                                                inner_editor,
2453251881Speter                                                inner_baton,
2454251881Speter                                                result_pool));
2455251881Speter
2456251881Speter  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
2457251881Speter                                            cancel_baton,
2458251881Speter                                            inner_editor,
2459251881Speter                                            inner_baton,
2460251881Speter                                            editor,
2461251881Speter                                            edit_baton,
2462251881Speter                                            result_pool));
2463251881Speter
2464251881Speter  sfb = apr_palloc(result_pool, sizeof(*sfb));
2465251881Speter  sfb->db = wc_ctx->db;
2466251881Speter  sfb->base_abspath = eb->anchor_abspath;
2467251881Speter  sfb->fetch_base = TRUE;
2468251881Speter
2469251881Speter  shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2470251881Speter  shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2471251881Speter  shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2472251881Speter  shim_callbacks->fetch_baton = sfb;
2473251881Speter
2474251881Speter
2475251881Speter  SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2476251881Speter                                   NULL, NULL, shim_callbacks,
2477251881Speter                                   result_pool, scratch_pool));
2478251881Speter
2479251881Speter  return SVN_NO_ERROR;
2480251881Speter}
2481251881Speter
2482251881Speter/* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
2483251881Speter
2484251881Speter/* baton for the svn_diff_tree_processor_t wrapper */
2485251881Spetertypedef struct wc_diff_wrap_baton_t
2486251881Speter{
2487251881Speter  const svn_wc_diff_callbacks4_t *callbacks;
2488251881Speter  void *callback_baton;
2489251881Speter
2490251881Speter  svn_boolean_t walk_deleted_dirs;
2491251881Speter
2492251881Speter  apr_pool_t *result_pool;
2493251881Speter  const char *empty_file;
2494251881Speter
2495251881Speter} wc_diff_wrap_baton_t;
2496251881Speter
2497251881Speterstatic svn_error_t *
2498251881Speterwrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
2499251881Speter                       apr_pool_t *scratch_pool)
2500251881Speter{
2501251881Speter  if (wb->empty_file)
2502251881Speter    return SVN_NO_ERROR;
2503251881Speter
2504251881Speter  /* Create a unique file in the tempdir */
2505251881Speter  SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
2506251881Speter                                   svn_io_file_del_on_pool_cleanup,
2507251881Speter                                   wb->result_pool, scratch_pool));
2508251881Speter
2509251881Speter  return SVN_NO_ERROR;
2510251881Speter}
2511251881Speter
2512251881Speter/* svn_diff_tree_processor_t function */
2513251881Speterstatic svn_error_t *
2514251881Speterwrap_dir_opened(void **new_dir_baton,
2515251881Speter                svn_boolean_t *skip,
2516251881Speter                svn_boolean_t *skip_children,
2517251881Speter                const char *relpath,
2518251881Speter                const svn_diff_source_t *left_source,
2519251881Speter                const svn_diff_source_t *right_source,
2520251881Speter                const svn_diff_source_t *copyfrom_source,
2521251881Speter                void *parent_dir_baton,
2522251881Speter                const svn_diff_tree_processor_t *processor,
2523251881Speter                apr_pool_t *result_pool,
2524251881Speter                apr_pool_t *scratch_pool)
2525251881Speter{
2526251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2527251881Speter  svn_boolean_t tree_conflicted = FALSE;
2528251881Speter
2529286506Speter  assert(left_source || right_source);      /* Must exist at one point. */
2530286506Speter  assert(!left_source || !copyfrom_source); /* Either existed or added. */
2531251881Speter
2532251881Speter  /* Maybe store state and tree_conflicted in baton? */
2533251881Speter  if (left_source != NULL)
2534251881Speter    {
2535251881Speter      /* Open for change or delete */
2536251881Speter      SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
2537251881Speter                                        relpath,
2538251881Speter                                        right_source
2539251881Speter                                            ? right_source->revision
2540251881Speter                                            : (left_source
2541251881Speter                                                    ? left_source->revision
2542251881Speter                                                    : SVN_INVALID_REVNUM),
2543251881Speter                                        wb->callback_baton,
2544251881Speter                                        scratch_pool));
2545251881Speter
2546251881Speter      if (! right_source && !wb->walk_deleted_dirs)
2547251881Speter        *skip_children = TRUE;
2548251881Speter    }
2549251881Speter  else /* left_source == NULL -> Add */
2550251881Speter    {
2551251881Speter      svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2552251881Speter      SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
2553251881Speter                                       skip, skip_children,
2554251881Speter                                       relpath,
2555251881Speter                                       right_source->revision,
2556251881Speter                                       copyfrom_source
2557251881Speter                                            ? copyfrom_source->repos_relpath
2558251881Speter                                            : NULL,
2559251881Speter                                       copyfrom_source
2560251881Speter                                            ? copyfrom_source->revision
2561251881Speter                                            : SVN_INVALID_REVNUM,
2562251881Speter                                       wb->callback_baton,
2563251881Speter                                       scratch_pool));
2564251881Speter    }
2565251881Speter
2566251881Speter  *new_dir_baton = NULL;
2567251881Speter
2568251881Speter  return SVN_NO_ERROR;
2569251881Speter}
2570251881Speter
2571251881Speter/* svn_diff_tree_processor_t function */
2572251881Speterstatic svn_error_t *
2573251881Speterwrap_dir_added(const char *relpath,
2574289180Speter               const svn_diff_source_t *copyfrom_source,
2575251881Speter               const svn_diff_source_t *right_source,
2576251881Speter               /*const*/ apr_hash_t *copyfrom_props,
2577251881Speter               /*const*/ apr_hash_t *right_props,
2578251881Speter               void *dir_baton,
2579251881Speter               const svn_diff_tree_processor_t *processor,
2580251881Speter               apr_pool_t *scratch_pool)
2581251881Speter{
2582251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2583251881Speter  svn_boolean_t tree_conflicted = FALSE;
2584251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
2585251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2586251881Speter  apr_hash_t *pristine_props = copyfrom_props;
2587251881Speter  apr_array_header_t *prop_changes = NULL;
2588251881Speter
2589251881Speter  if (right_props && apr_hash_count(right_props))
2590251881Speter    {
2591251881Speter      if (!pristine_props)
2592251881Speter        pristine_props = apr_hash_make(scratch_pool);
2593251881Speter
2594251881Speter      SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
2595251881Speter                             scratch_pool));
2596251881Speter
2597251881Speter      SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
2598251881Speter                                               &tree_conflicted,
2599251881Speter                                               relpath,
2600251881Speter                                               TRUE /* dir_was_added */,
2601251881Speter                                               prop_changes, pristine_props,
2602251881Speter                                               wb->callback_baton,
2603251881Speter                                               scratch_pool));
2604251881Speter    }
2605251881Speter
2606251881Speter  SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
2607251881Speter                                   &tree_conflicted,
2608251881Speter                                   relpath,
2609251881Speter                                   TRUE /* dir_was_added */,
2610251881Speter                                   wb->callback_baton,
2611251881Speter                                   scratch_pool));
2612251881Speter  return SVN_NO_ERROR;
2613251881Speter}
2614251881Speter
2615251881Speter/* svn_diff_tree_processor_t function */
2616251881Speterstatic svn_error_t *
2617251881Speterwrap_dir_deleted(const char *relpath,
2618251881Speter                 const svn_diff_source_t *left_source,
2619251881Speter                 /*const*/ apr_hash_t *left_props,
2620251881Speter                 void *dir_baton,
2621251881Speter                 const svn_diff_tree_processor_t *processor,
2622251881Speter                 apr_pool_t *scratch_pool)
2623251881Speter{
2624251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2625251881Speter  svn_boolean_t tree_conflicted = FALSE;
2626251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2627251881Speter
2628251881Speter  SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
2629251881Speter                                     relpath,
2630251881Speter                                     wb->callback_baton,
2631251881Speter                                     scratch_pool));
2632251881Speter
2633251881Speter  return SVN_NO_ERROR;
2634251881Speter}
2635251881Speter
2636251881Speter/* svn_diff_tree_processor_t function */
2637251881Speterstatic svn_error_t *
2638251881Speterwrap_dir_closed(const char *relpath,
2639251881Speter                const svn_diff_source_t *left_source,
2640251881Speter                const svn_diff_source_t *right_source,
2641251881Speter                void *dir_baton,
2642251881Speter                const svn_diff_tree_processor_t *processor,
2643251881Speter                apr_pool_t *scratch_pool)
2644251881Speter{
2645251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2646251881Speter
2647251881Speter  /* No previous implementations provided these arguments, so we
2648251881Speter     are not providing them either */
2649251881Speter  SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
2650251881Speter                                    relpath,
2651251881Speter                                    FALSE /* added */,
2652251881Speter                                    wb->callback_baton,
2653251881Speter                                    scratch_pool));
2654251881Speter
2655251881Speterreturn SVN_NO_ERROR;
2656251881Speter}
2657251881Speter
2658251881Speter/* svn_diff_tree_processor_t function */
2659251881Speterstatic svn_error_t *
2660251881Speterwrap_dir_changed(const char *relpath,
2661251881Speter                 const svn_diff_source_t *left_source,
2662251881Speter                 const svn_diff_source_t *right_source,
2663251881Speter                 /*const*/ apr_hash_t *left_props,
2664251881Speter                 /*const*/ apr_hash_t *right_props,
2665251881Speter                 const apr_array_header_t *prop_changes,
2666251881Speter                 void *dir_baton,
2667251881Speter                 const struct svn_diff_tree_processor_t *processor,
2668251881Speter                 apr_pool_t *scratch_pool)
2669251881Speter{
2670251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2671251881Speter  svn_boolean_t tree_conflicted = FALSE;
2672251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2673251881Speter
2674251881Speter  assert(left_source && right_source);
2675251881Speter
2676251881Speter  SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
2677251881Speter                                           relpath,
2678251881Speter                                           FALSE /* dir_was_added */,
2679251881Speter                                           prop_changes,
2680251881Speter                                           left_props,
2681251881Speter                                           wb->callback_baton,
2682251881Speter                                           scratch_pool));
2683251881Speter
2684251881Speter  /* And call dir_closed, etc */
2685251881Speter  SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
2686251881Speter                          dir_baton, processor,
2687251881Speter                          scratch_pool));
2688251881Speter  return SVN_NO_ERROR;
2689251881Speter}
2690251881Speter
2691251881Speter/* svn_diff_tree_processor_t function */
2692251881Speterstatic svn_error_t *
2693251881Speterwrap_file_opened(void **new_file_baton,
2694251881Speter                 svn_boolean_t *skip,
2695251881Speter                 const char *relpath,
2696251881Speter                 const svn_diff_source_t *left_source,
2697251881Speter                 const svn_diff_source_t *right_source,
2698251881Speter                 const svn_diff_source_t *copyfrom_source,
2699251881Speter                 void *dir_baton,
2700251881Speter                 const svn_diff_tree_processor_t *processor,
2701251881Speter                 apr_pool_t *result_pool,
2702251881Speter                 apr_pool_t *scratch_pool)
2703251881Speter{
2704251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2705251881Speter  svn_boolean_t tree_conflicted = FALSE;
2706251881Speter
2707251881Speter  if (left_source) /* If ! added */
2708251881Speter    SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
2709251881Speter                                       right_source
2710251881Speter                                            ? right_source->revision
2711251881Speter                                            : (left_source
2712251881Speter                                                    ? left_source->revision
2713251881Speter                                                    : SVN_INVALID_REVNUM),
2714251881Speter                                       wb->callback_baton, scratch_pool));
2715251881Speter
2716251881Speter  /* No old implementation used the output arguments for notify */
2717251881Speter
2718251881Speter  *new_file_baton = NULL;
2719251881Speter  return SVN_NO_ERROR;
2720251881Speter}
2721251881Speter
2722251881Speter/* svn_diff_tree_processor_t function */
2723251881Speterstatic svn_error_t *
2724251881Speterwrap_file_added(const char *relpath,
2725251881Speter                const svn_diff_source_t *copyfrom_source,
2726251881Speter                const svn_diff_source_t *right_source,
2727251881Speter                const char *copyfrom_file,
2728251881Speter                const char *right_file,
2729251881Speter                /*const*/ apr_hash_t *copyfrom_props,
2730251881Speter                /*const*/ apr_hash_t *right_props,
2731251881Speter                void *file_baton,
2732251881Speter                const svn_diff_tree_processor_t *processor,
2733251881Speter                apr_pool_t *scratch_pool)
2734251881Speter{
2735251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2736251881Speter  svn_boolean_t tree_conflicted = FALSE;
2737251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2738251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2739251881Speter  apr_array_header_t *prop_changes;
2740251881Speter
2741251881Speter  if (! copyfrom_props)
2742251881Speter    copyfrom_props = apr_hash_make(scratch_pool);
2743251881Speter
2744251881Speter  SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
2745251881Speter                         scratch_pool));
2746251881Speter
2747251881Speter  if (! copyfrom_source)
2748251881Speter    SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2749251881Speter
2750251881Speter  SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
2751251881Speter                                    relpath,
2752251881Speter                                    copyfrom_source
2753251881Speter                                        ? copyfrom_file
2754251881Speter                                        : wb->empty_file,
2755251881Speter                                    right_file,
2756251881Speter                                    0,
2757251881Speter                                    right_source->revision,
2758251881Speter                                    copyfrom_props
2759251881Speter                                     ? svn_prop_get_value(copyfrom_props,
2760251881Speter                                                          SVN_PROP_MIME_TYPE)
2761251881Speter                                     : NULL,
2762251881Speter                                    right_props
2763251881Speter                                     ? svn_prop_get_value(right_props,
2764251881Speter                                                          SVN_PROP_MIME_TYPE)
2765251881Speter                                     : NULL,
2766251881Speter                                    copyfrom_source
2767251881Speter                                            ? copyfrom_source->repos_relpath
2768251881Speter                                            : NULL,
2769251881Speter                                    copyfrom_source
2770251881Speter                                            ? copyfrom_source->revision
2771251881Speter                                            : SVN_INVALID_REVNUM,
2772251881Speter                                    prop_changes, copyfrom_props,
2773251881Speter                                    wb->callback_baton,
2774251881Speter                                    scratch_pool));
2775251881Speter  return SVN_NO_ERROR;
2776251881Speter}
2777251881Speter
2778251881Speterstatic svn_error_t *
2779251881Speterwrap_file_deleted(const char *relpath,
2780251881Speter                  const svn_diff_source_t *left_source,
2781251881Speter                  const char *left_file,
2782251881Speter                  apr_hash_t *left_props,
2783251881Speter                  void *file_baton,
2784251881Speter                  const svn_diff_tree_processor_t *processor,
2785251881Speter                  apr_pool_t *scratch_pool)
2786251881Speter{
2787251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2788251881Speter  svn_boolean_t tree_conflicted = FALSE;
2789251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2790251881Speter
2791251881Speter  SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2792251881Speter
2793251881Speter  SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
2794251881Speter                                      relpath,
2795251881Speter                                      left_file, wb->empty_file,
2796251881Speter                                      left_props
2797251881Speter                                       ? svn_prop_get_value(left_props,
2798251881Speter                                                            SVN_PROP_MIME_TYPE)
2799251881Speter                                       : NULL,
2800251881Speter                                      NULL,
2801251881Speter                                      left_props,
2802251881Speter                                      wb->callback_baton,
2803251881Speter                                      scratch_pool));
2804251881Speter  return SVN_NO_ERROR;
2805251881Speter}
2806251881Speter
2807251881Speter/* svn_diff_tree_processor_t function */
2808251881Speterstatic svn_error_t *
2809251881Speterwrap_file_changed(const char *relpath,
2810251881Speter                  const svn_diff_source_t *left_source,
2811251881Speter                  const svn_diff_source_t *right_source,
2812251881Speter                  const char *left_file,
2813251881Speter                  const char *right_file,
2814251881Speter                  /*const*/ apr_hash_t *left_props,
2815251881Speter                  /*const*/ apr_hash_t *right_props,
2816251881Speter                  svn_boolean_t file_modified,
2817251881Speter                  const apr_array_header_t *prop_changes,
2818251881Speter                  void *file_baton,
2819251881Speter                  const svn_diff_tree_processor_t *processor,
2820251881Speter                  apr_pool_t *scratch_pool)
2821251881Speter{
2822251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2823251881Speter  svn_boolean_t tree_conflicted = FALSE;
2824251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2825251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2826251881Speter
2827251881Speter  SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2828251881Speter
2829251881Speter  assert(left_source && right_source);
2830251881Speter
2831251881Speter  SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
2832251881Speter                                      relpath,
2833251881Speter                                      file_modified ? left_file : NULL,
2834251881Speter                                      file_modified ? right_file : NULL,
2835251881Speter                                      left_source->revision,
2836251881Speter                                      right_source->revision,
2837251881Speter                                      left_props
2838251881Speter                                       ? svn_prop_get_value(left_props,
2839251881Speter                                                            SVN_PROP_MIME_TYPE)
2840251881Speter                                       : NULL,
2841251881Speter                                      right_props
2842251881Speter                                       ? svn_prop_get_value(right_props,
2843251881Speter                                                            SVN_PROP_MIME_TYPE)
2844251881Speter                                       : NULL,
2845251881Speter                                       prop_changes,
2846251881Speter                                      left_props,
2847251881Speter                                      wb->callback_baton,
2848251881Speter                                      scratch_pool));
2849251881Speter  return SVN_NO_ERROR;
2850251881Speter}
2851251881Speter
2852251881Spetersvn_error_t *
2853251881Spetersvn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
2854251881Speter                            const svn_wc_diff_callbacks4_t *callbacks,
2855251881Speter                            void *callback_baton,
2856251881Speter                            svn_boolean_t walk_deleted_dirs,
2857251881Speter                            apr_pool_t *result_pool,
2858251881Speter                            apr_pool_t *scratch_pool)
2859251881Speter{
2860251881Speter  wc_diff_wrap_baton_t *wrap_baton;
2861251881Speter  svn_diff_tree_processor_t *processor;
2862251881Speter
2863251881Speter  wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
2864251881Speter
2865251881Speter  wrap_baton->result_pool = result_pool;
2866251881Speter  wrap_baton->callbacks = callbacks;
2867251881Speter  wrap_baton->callback_baton = callback_baton;
2868251881Speter  wrap_baton->empty_file = NULL;
2869251881Speter  wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
2870251881Speter
2871251881Speter  processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
2872251881Speter
2873251881Speter  processor->dir_opened   = wrap_dir_opened;
2874251881Speter  processor->dir_added    = wrap_dir_added;
2875251881Speter  processor->dir_deleted  = wrap_dir_deleted;
2876251881Speter  processor->dir_changed  = wrap_dir_changed;
2877251881Speter  processor->dir_closed   = wrap_dir_closed;
2878251881Speter
2879251881Speter  processor->file_opened   = wrap_file_opened;
2880251881Speter  processor->file_added    = wrap_file_added;
2881251881Speter  processor->file_deleted  = wrap_file_deleted;
2882251881Speter  processor->file_changed  = wrap_file_changed;
2883251881Speter  /*processor->file_closed   = wrap_file_closed*/; /* Not needed */
2884251881Speter
2885251881Speter  *diff_processor = processor;
2886251881Speter  return SVN_NO_ERROR;
2887251881Speter}
2888286506Speter
2889286506Speter/* =====================================================================
2890286506Speter * A tree processor filter that filters by changelist membership
2891286506Speter * =====================================================================
2892286506Speter *
2893286506Speter * The current implementation queries the WC for the changelist of each
2894286506Speter * file as it comes through, and sets the 'skip' flag for a non-matching
2895286506Speter * file.
2896286506Speter *
2897286506Speter * (It doesn't set the 'skip' flag for a directory, as we need to receive
2898286506Speter * the changed/added/deleted/closed call to know when it is closed, in
2899286506Speter * order to preserve the strict open-close semantics for the wrapped tree
2900286506Speter * processor.)
2901286506Speter *
2902286506Speter * It passes on the opening and closing of every directory, even if there
2903286506Speter * are no file changes to be passed on inside that directory.
2904286506Speter */
2905286506Speter
2906286506Spetertypedef struct filter_tree_baton_t
2907286506Speter{
2908286506Speter  const svn_diff_tree_processor_t *processor;
2909286506Speter  svn_wc_context_t *wc_ctx;
2910286506Speter  /* WC path of the root of the diff (where relpath = "") */
2911286506Speter  const char *root_local_abspath;
2912286506Speter  /* Hash whose keys are const char * changelist names. */
2913286506Speter  apr_hash_t *changelist_hash;
2914286506Speter} filter_tree_baton_t;
2915286506Speter
2916286506Speterstatic svn_error_t *
2917286506Speterfilter_dir_opened(void **new_dir_baton,
2918286506Speter                  svn_boolean_t *skip,
2919286506Speter                  svn_boolean_t *skip_children,
2920286506Speter                  const char *relpath,
2921286506Speter                  const svn_diff_source_t *left_source,
2922286506Speter                  const svn_diff_source_t *right_source,
2923286506Speter                  const svn_diff_source_t *copyfrom_source,
2924286506Speter                  void *parent_dir_baton,
2925286506Speter                  const svn_diff_tree_processor_t *processor,
2926286506Speter                  apr_pool_t *result_pool,
2927286506Speter                  apr_pool_t *scratch_pool)
2928286506Speter{
2929286506Speter  struct filter_tree_baton_t *fb = processor->baton;
2930286506Speter
2931286506Speter  SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
2932286506Speter                                    relpath,
2933286506Speter                                    left_source, right_source,
2934286506Speter                                    copyfrom_source,
2935286506Speter                                    parent_dir_baton,
2936286506Speter                                    fb->processor,
2937286506Speter                                    result_pool, scratch_pool));
2938286506Speter  return SVN_NO_ERROR;
2939286506Speter}
2940286506Speter
2941286506Speterstatic svn_error_t *
2942286506Speterfilter_dir_added(const char *relpath,
2943286506Speter                 const svn_diff_source_t *copyfrom_source,
2944286506Speter                 const svn_diff_source_t *right_source,
2945286506Speter                 /*const*/ apr_hash_t *copyfrom_props,
2946286506Speter                 /*const*/ apr_hash_t *right_props,
2947286506Speter                 void *dir_baton,
2948286506Speter                 const svn_diff_tree_processor_t *processor,
2949286506Speter                 apr_pool_t *scratch_pool)
2950286506Speter{
2951286506Speter  struct filter_tree_baton_t *fb = processor->baton;
2952286506Speter
2953286506Speter  SVN_ERR(fb->processor->dir_closed(relpath,
2954286506Speter                                    NULL,
2955286506Speter                                    right_source,
2956286506Speter                                    dir_baton,
2957286506Speter                                    fb->processor,
2958286506Speter                                    scratch_pool));
2959286506Speter
2960286506Speter  return SVN_NO_ERROR;
2961286506Speter}
2962286506Speter
2963286506Speterstatic svn_error_t *
2964286506Speterfilter_dir_deleted(const char *relpath,
2965286506Speter                   const svn_diff_source_t *left_source,
2966286506Speter                   /*const*/ apr_hash_t *left_props,
2967286506Speter                   void *dir_baton,
2968286506Speter                   const svn_diff_tree_processor_t *processor,
2969286506Speter                   apr_pool_t *scratch_pool)
2970286506Speter{
2971286506Speter  struct filter_tree_baton_t *fb = processor->baton;
2972286506Speter
2973286506Speter  SVN_ERR(fb->processor->dir_closed(relpath,
2974286506Speter                                    left_source,
2975286506Speter                                    NULL,
2976286506Speter                                    dir_baton,
2977286506Speter                                    fb->processor,
2978286506Speter                                    scratch_pool));
2979286506Speter
2980286506Speter  return SVN_NO_ERROR;
2981286506Speter}
2982286506Speter
2983286506Speterstatic svn_error_t *
2984286506Speterfilter_dir_changed(const char *relpath,
2985286506Speter                   const svn_diff_source_t *left_source,
2986286506Speter                   const svn_diff_source_t *right_source,
2987286506Speter                   /*const*/ apr_hash_t *left_props,
2988286506Speter                   /*const*/ apr_hash_t *right_props,
2989286506Speter                   const apr_array_header_t *prop_changes,
2990286506Speter                   void *dir_baton,
2991286506Speter                   const struct svn_diff_tree_processor_t *processor,
2992286506Speter                   apr_pool_t *scratch_pool)
2993286506Speter{
2994286506Speter  struct filter_tree_baton_t *fb = processor->baton;
2995286506Speter
2996286506Speter  SVN_ERR(fb->processor->dir_closed(relpath,
2997286506Speter                                    left_source,
2998286506Speter                                    right_source,
2999286506Speter                                    dir_baton,
3000286506Speter                                    fb->processor,
3001286506Speter                                    scratch_pool));
3002286506Speter  return SVN_NO_ERROR;
3003286506Speter}
3004286506Speter
3005286506Speterstatic svn_error_t *
3006286506Speterfilter_dir_closed(const char *relpath,
3007286506Speter                  const svn_diff_source_t *left_source,
3008286506Speter                  const svn_diff_source_t *right_source,
3009286506Speter                  void *dir_baton,
3010286506Speter                  const svn_diff_tree_processor_t *processor,
3011286506Speter                  apr_pool_t *scratch_pool)
3012286506Speter{
3013286506Speter  struct filter_tree_baton_t *fb = processor->baton;
3014286506Speter
3015286506Speter  SVN_ERR(fb->processor->dir_closed(relpath,
3016286506Speter                                    left_source,
3017286506Speter                                    right_source,
3018286506Speter                                    dir_baton,
3019286506Speter                                    fb->processor,
3020286506Speter                                    scratch_pool));
3021286506Speter  return SVN_NO_ERROR;
3022286506Speter}
3023286506Speter
3024286506Speterstatic svn_error_t *
3025286506Speterfilter_file_opened(void **new_file_baton,
3026286506Speter                   svn_boolean_t *skip,
3027286506Speter                   const char *relpath,
3028286506Speter                   const svn_diff_source_t *left_source,
3029286506Speter                   const svn_diff_source_t *right_source,
3030286506Speter                   const svn_diff_source_t *copyfrom_source,
3031286506Speter                   void *dir_baton,
3032286506Speter                   const svn_diff_tree_processor_t *processor,
3033286506Speter                   apr_pool_t *result_pool,
3034286506Speter                   apr_pool_t *scratch_pool)
3035286506Speter{
3036286506Speter  struct filter_tree_baton_t *fb = processor->baton;
3037286506Speter  const char *local_abspath
3038286506Speter    = svn_dirent_join(fb->root_local_abspath, relpath, scratch_pool);
3039286506Speter
3040286506Speter  /* Skip if not a member of a given changelist */
3041286506Speter  if (! svn_wc__changelist_match(fb->wc_ctx, local_abspath,
3042286506Speter                                 fb->changelist_hash, scratch_pool))
3043286506Speter    {
3044286506Speter      *skip = TRUE;
3045286506Speter      return SVN_NO_ERROR;
3046286506Speter    }
3047286506Speter
3048286506Speter  SVN_ERR(fb->processor->file_opened(new_file_baton,
3049286506Speter                                     skip,
3050286506Speter                                     relpath,
3051286506Speter                                     left_source,
3052286506Speter                                     right_source,
3053286506Speter                                     copyfrom_source,
3054286506Speter                                     dir_baton,
3055286506Speter                                     fb->processor,
3056286506Speter                                     result_pool,
3057286506Speter                                     scratch_pool));
3058286506Speter  return SVN_NO_ERROR;
3059286506Speter}
3060286506Speter
3061286506Speterstatic svn_error_t *
3062286506Speterfilter_file_added(const char *relpath,
3063286506Speter                  const svn_diff_source_t *copyfrom_source,
3064286506Speter                  const svn_diff_source_t *right_source,
3065286506Speter                  const char *copyfrom_file,
3066286506Speter                  const char *right_file,
3067286506Speter                  /*const*/ apr_hash_t *copyfrom_props,
3068286506Speter                  /*const*/ apr_hash_t *right_props,
3069286506Speter                  void *file_baton,
3070286506Speter                  const svn_diff_tree_processor_t *processor,
3071286506Speter                  apr_pool_t *scratch_pool)
3072286506Speter{
3073286506Speter  struct filter_tree_baton_t *fb = processor->baton;
3074286506Speter
3075286506Speter  SVN_ERR(fb->processor->file_added(relpath,
3076286506Speter                                    copyfrom_source,
3077286506Speter                                    right_source,
3078286506Speter                                    copyfrom_file,
3079286506Speter                                    right_file,
3080286506Speter                                    copyfrom_props,
3081286506Speter                                    right_props,
3082286506Speter                                    file_baton,
3083286506Speter                                    fb->processor,
3084286506Speter                                    scratch_pool));
3085286506Speter  return SVN_NO_ERROR;
3086286506Speter}
3087286506Speter
3088286506Speterstatic svn_error_t *
3089286506Speterfilter_file_deleted(const char *relpath,
3090286506Speter                    const svn_diff_source_t *left_source,
3091286506Speter                    const char *left_file,
3092286506Speter                    /*const*/ apr_hash_t *left_props,
3093286506Speter                    void *file_baton,
3094286506Speter                    const svn_diff_tree_processor_t *processor,
3095286506Speter                    apr_pool_t *scratch_pool)
3096286506Speter{
3097286506Speter  struct filter_tree_baton_t *fb = processor->baton;
3098286506Speter
3099286506Speter  SVN_ERR(fb->processor->file_deleted(relpath,
3100286506Speter                                      left_source,
3101286506Speter                                      left_file,
3102286506Speter                                      left_props,
3103286506Speter                                      file_baton,
3104286506Speter                                      fb->processor,
3105286506Speter                                      scratch_pool));
3106286506Speter
3107286506Speter  return SVN_NO_ERROR;
3108286506Speter}
3109286506Speter
3110286506Speterstatic svn_error_t *
3111286506Speterfilter_file_changed(const char *relpath,
3112286506Speter                    const svn_diff_source_t *left_source,
3113286506Speter                    const svn_diff_source_t *right_source,
3114286506Speter                    const char *left_file,
3115286506Speter                    const char *right_file,
3116286506Speter                    /*const*/ apr_hash_t *left_props,
3117286506Speter                    /*const*/ apr_hash_t *right_props,
3118286506Speter                    svn_boolean_t file_modified,
3119286506Speter                    const apr_array_header_t *prop_changes,
3120286506Speter                    void *file_baton,
3121286506Speter                    const svn_diff_tree_processor_t *processor,
3122286506Speter                    apr_pool_t *scratch_pool)
3123286506Speter{
3124286506Speter  struct filter_tree_baton_t *fb = processor->baton;
3125286506Speter
3126286506Speter  SVN_ERR(fb->processor->file_changed(relpath,
3127286506Speter                                      left_source,
3128286506Speter                                      right_source,
3129286506Speter                                      left_file,
3130286506Speter                                      right_file,
3131286506Speter                                      left_props,
3132286506Speter                                      right_props,
3133286506Speter                                      file_modified,
3134286506Speter                                      prop_changes,
3135286506Speter                                      file_baton,
3136286506Speter                                      fb->processor,
3137286506Speter                                      scratch_pool));
3138286506Speter  return SVN_NO_ERROR;
3139286506Speter}
3140286506Speter
3141286506Speterstatic svn_error_t *
3142286506Speterfilter_file_closed(const char *relpath,
3143286506Speter                   const svn_diff_source_t *left_source,
3144286506Speter                   const svn_diff_source_t *right_source,
3145286506Speter                   void *file_baton,
3146286506Speter                   const svn_diff_tree_processor_t *processor,
3147286506Speter                   apr_pool_t *scratch_pool)
3148286506Speter{
3149286506Speter  struct filter_tree_baton_t *fb = processor->baton;
3150286506Speter
3151286506Speter  SVN_ERR(fb->processor->file_closed(relpath,
3152286506Speter                                     left_source,
3153286506Speter                                     right_source,
3154286506Speter                                     file_baton,
3155286506Speter                                     fb->processor,
3156286506Speter                                     scratch_pool));
3157286506Speter
3158286506Speter  return SVN_NO_ERROR;
3159286506Speter}
3160286506Speter
3161286506Speterstatic svn_error_t *
3162286506Speterfilter_node_absent(const char *relpath,
3163286506Speter                   void *dir_baton,
3164286506Speter                   const svn_diff_tree_processor_t *processor,
3165286506Speter                   apr_pool_t *scratch_pool)
3166286506Speter{
3167286506Speter  struct filter_tree_baton_t *fb = processor->baton;
3168286506Speter
3169286506Speter  SVN_ERR(fb->processor->node_absent(relpath,
3170286506Speter                                     dir_baton,
3171286506Speter                                     fb->processor,
3172286506Speter                                     scratch_pool));
3173286506Speter  return SVN_NO_ERROR;
3174286506Speter}
3175286506Speter
3176286506Speterconst svn_diff_tree_processor_t *
3177286506Spetersvn_wc__changelist_filter_tree_processor_create(
3178286506Speter                                const svn_diff_tree_processor_t *processor,
3179286506Speter                                svn_wc_context_t *wc_ctx,
3180286506Speter                                const char *root_local_abspath,
3181286506Speter                                apr_hash_t *changelist_hash,
3182286506Speter                                apr_pool_t *result_pool)
3183286506Speter{
3184286506Speter  struct filter_tree_baton_t *fb;
3185286506Speter  svn_diff_tree_processor_t *filter;
3186286506Speter
3187286506Speter  if (! changelist_hash)
3188286506Speter    return processor;
3189286506Speter
3190286506Speter  fb = apr_pcalloc(result_pool, sizeof(*fb));
3191286506Speter  fb->processor = processor;
3192286506Speter  fb->wc_ctx = wc_ctx;
3193286506Speter  fb->root_local_abspath = root_local_abspath;
3194286506Speter  fb->changelist_hash = changelist_hash;
3195286506Speter
3196286506Speter  filter = svn_diff__tree_processor_create(fb, result_pool);
3197286506Speter  filter->dir_opened   = filter_dir_opened;
3198286506Speter  filter->dir_added    = filter_dir_added;
3199286506Speter  filter->dir_deleted  = filter_dir_deleted;
3200286506Speter  filter->dir_changed  = filter_dir_changed;
3201286506Speter  filter->dir_closed   = filter_dir_closed;
3202286506Speter
3203286506Speter  filter->file_opened   = filter_file_opened;
3204286506Speter  filter->file_added    = filter_file_added;
3205286506Speter  filter->file_deleted  = filter_file_deleted;
3206286506Speter  filter->file_changed  = filter_file_changed;
3207286506Speter  filter->file_closed   = filter_file_closed;
3208286506Speter
3209286506Speter  filter->node_absent   = filter_node_absent;
3210286506Speter
3211286506Speter  return filter;
3212286506Speter}
3213286506Speter
3214