diff_editor.c revision 257936
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
67251881Speter#include "private/svn_subr_private.h"
68251881Speter#include "private/svn_wc_private.h"
69251881Speter#include "private/svn_diff_tree.h"
70251881Speter#include "private/svn_editor.h"
71251881Speter
72251881Speter#include "wc.h"
73251881Speter#include "props.h"
74251881Speter#include "adm_files.h"
75251881Speter#include "translate.h"
76251881Speter#include "diff.h"
77251881Speter
78251881Speter#include "svn_private_config.h"
79251881Speter
80251881Speter/*-------------------------------------------------------------------------*/
81251881Speter
82251881Speter
83251881Speter/* Overall crawler editor baton.
84251881Speter */
85251881Speterstruct edit_baton_t
86251881Speter{
87251881Speter  /* A wc db. */
88251881Speter  svn_wc__db_t *db;
89251881Speter
90251881Speter  /* A diff tree processor, receiving the result of the diff. */
91251881Speter  const svn_diff_tree_processor_t *processor;
92251881Speter
93251881Speter  /* A boolean indicating whether local additions should be reported before
94251881Speter     remote deletes. The processor can transform adds in deletes and deletes
95251881Speter     in adds, but it can't reorder the output. */
96251881Speter  svn_boolean_t local_before_remote;
97251881Speter
98251881Speter  /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */
99251881Speter  const char *target;
100251881Speter  const char *anchor_abspath;
101251881Speter
102251881Speter  /* Target revision */
103251881Speter  svn_revnum_t revnum;
104251881Speter
105251881Speter  /* Was the root opened? */
106251881Speter  svn_boolean_t root_opened;
107251881Speter
108251881Speter  /* How does this diff descend as seen from target? */
109251881Speter  svn_depth_t depth;
110251881Speter
111251881Speter  /* Should this diff ignore node ancestry? */
112251881Speter  svn_boolean_t ignore_ancestry;
113251881Speter
114251881Speter  /* Possibly diff repos against text-bases instead of working files. */
115251881Speter  svn_boolean_t diff_pristine;
116251881Speter
117251881Speter  /* Hash whose keys are const char * changelist names. */
118251881Speter  apr_hash_t *changelist_hash;
119251881Speter
120251881Speter  /* Cancel function/baton */
121251881Speter  svn_cancel_func_t cancel_func;
122251881Speter  void *cancel_baton;
123251881Speter
124251881Speter  apr_pool_t *pool;
125251881Speter};
126251881Speter
127251881Speter/* Directory level baton.
128251881Speter */
129251881Speterstruct dir_baton_t
130251881Speter{
131251881Speter  /* Reference to parent directory baton (or NULL for the root) */
132251881Speter  struct dir_baton_t *parent_baton;
133251881Speter
134251881Speter  /* The depth at which this directory should be diffed. */
135251881Speter  svn_depth_t depth;
136251881Speter
137251881Speter  /* The name and path of this directory as if they would be/are in the
138251881Speter      local working copy. */
139251881Speter  const char *name;
140251881Speter  const char *relpath;
141251881Speter  const char *local_abspath;
142251881Speter
143251881Speter  /* TRUE if the file is added by the editor drive. */
144251881Speter  svn_boolean_t added;
145251881Speter  /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
146251881Speter  svn_boolean_t repos_only;
147251881Speter  /* TRUE if the node is to be compared with an unrelated node*/
148251881Speter  svn_boolean_t ignoring_ancestry;
149251881Speter
150251881Speter  /* Processor state */
151251881Speter  void *pdb;
152251881Speter  svn_boolean_t skip;
153251881Speter  svn_boolean_t skip_children;
154251881Speter
155251881Speter  svn_diff_source_t *left_src;
156251881Speter  svn_diff_source_t *right_src;
157251881Speter
158251881Speter  apr_hash_t *local_info;
159251881Speter
160251881Speter  /* A hash containing the basenames of the nodes reported deleted by the
161251881Speter     repository (or NULL for no values). */
162251881Speter  apr_hash_t *deletes;
163251881Speter
164251881Speter  /* Identifies those directory elements that get compared while running
165251881Speter     the crawler.  These elements should not be compared again when
166251881Speter     recursively looking for local modifications.
167251881Speter
168251881Speter     This hash maps the basename of the node to an unimportant value.
169251881Speter
170251881Speter     If the directory's properties have been compared, an item with hash
171251881Speter     key of "" will be present in the hash. */
172251881Speter  apr_hash_t *compared;
173251881Speter
174251881Speter  /* The list of incoming BASE->repos propchanges. */
175251881Speter  apr_array_header_t *propchanges;
176251881Speter
177251881Speter  /* Has a change on regular properties */
178251881Speter  svn_boolean_t has_propchange;
179251881Speter
180251881Speter  /* The overall crawler editor baton. */
181251881Speter  struct edit_baton_t *eb;
182251881Speter
183251881Speter  apr_pool_t *pool;
184251881Speter  int users;
185251881Speter};
186251881Speter
187251881Speter/* File level baton.
188251881Speter */
189251881Speterstruct file_baton_t
190251881Speter{
191251881Speter  struct dir_baton_t *parent_baton;
192251881Speter
193251881Speter  /* The name and path of this file as if they would be/are in the
194251881Speter     parent directory, diff session and local working copy. */
195251881Speter  const char *name;
196251881Speter  const char *relpath;
197251881Speter  const char *local_abspath;
198251881Speter
199251881Speter  /* Processor state */
200251881Speter  void *pfb;
201251881Speter  svn_boolean_t skip;
202251881Speter
203251881Speter  /* TRUE if the file is added by the editor drive. */
204251881Speter  svn_boolean_t added;
205251881Speter  /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
206251881Speter  svn_boolean_t repos_only;
207251881Speter  /* TRUE if the node is to be compared with an unrelated node*/
208251881Speter  svn_boolean_t ignoring_ancestry;
209251881Speter
210251881Speter  const svn_diff_source_t *left_src;
211251881Speter  const svn_diff_source_t *right_src;
212251881Speter
213251881Speter  /* The list of incoming BASE->repos propchanges. */
214251881Speter  apr_array_header_t *propchanges;
215251881Speter
216251881Speter  /* Has a change on regular properties */
217251881Speter  svn_boolean_t has_propchange;
218251881Speter
219251881Speter  /* The current BASE checksum and props */
220251881Speter  const svn_checksum_t *base_checksum;
221251881Speter  apr_hash_t *base_props;
222251881Speter
223251881Speter  /* The resulting from apply_textdelta */
224251881Speter  const char *temp_file_path;
225251881Speter  unsigned char result_digest[APR_MD5_DIGESTSIZE];
226251881Speter
227251881Speter  /* The overall crawler editor baton. */
228251881Speter  struct edit_baton_t *eb;
229251881Speter
230251881Speter  apr_pool_t *pool;
231251881Speter};
232251881Speter
233251881Speter/* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths
234251881Speter * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON
235251881Speter * define the callbacks to compare files. DEPTH defines if and how to
236251881Speter * descend into subdirectories; see public doc string for exactly how.
237251881Speter * IGNORE_ANCESTRY defines whether to utilize node ancestry when
238251881Speter * calculating diffs.  USE_TEXT_BASE defines whether to compare
239251881Speter * against working files or text-bases.  REVERSE_ORDER defines which
240251881Speter * direction to perform the diff.
241251881Speter *
242251881Speter * CHANGELIST_FILTER is a list of const char * changelist names, used to
243251881Speter * filter diff output responses to only those items in one of the
244251881Speter * specified changelists, empty (or NULL altogether) if no changelist
245251881Speter * filtering is requested.
246251881Speter */
247251881Speterstatic svn_error_t *
248251881Spetermake_edit_baton(struct edit_baton_t **edit_baton,
249251881Speter                svn_wc__db_t *db,
250251881Speter                const char *anchor_abspath,
251251881Speter                const char *target,
252251881Speter                const svn_wc_diff_callbacks4_t *callbacks,
253251881Speter                void *callback_baton,
254251881Speter                svn_depth_t depth,
255251881Speter                svn_boolean_t ignore_ancestry,
256251881Speter                svn_boolean_t show_copies_as_adds,
257251881Speter                svn_boolean_t use_text_base,
258251881Speter                svn_boolean_t reverse_order,
259251881Speter                const apr_array_header_t *changelist_filter,
260251881Speter                svn_cancel_func_t cancel_func,
261251881Speter                void *cancel_baton,
262251881Speter                apr_pool_t *pool)
263251881Speter{
264251881Speter  apr_hash_t *changelist_hash = NULL;
265251881Speter  struct edit_baton_t *eb;
266251881Speter  const svn_diff_tree_processor_t *processor;
267251881Speter
268251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
269251881Speter
270251881Speter  if (changelist_filter && changelist_filter->nelts)
271251881Speter    SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
272251881Speter                                       pool));
273251881Speter
274251881Speter  SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
275251881Speter                                      callbacks, callback_baton, TRUE,
276251881Speter                                      pool, pool));
277251881Speter
278251881Speter  if (reverse_order)
279251881Speter    processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
280251881Speter
281251881Speter  /* --show-copies-as-adds implies --notice-ancestry */
282251881Speter  if (show_copies_as_adds)
283251881Speter    ignore_ancestry = FALSE;
284251881Speter
285251881Speter  if (! show_copies_as_adds)
286251881Speter    processor = svn_diff__tree_processor_copy_as_changed_create(processor,
287251881Speter                                                                pool);
288251881Speter
289251881Speter  eb = apr_pcalloc(pool, sizeof(*eb));
290251881Speter  eb->db = db;
291251881Speter  eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
292251881Speter  eb->target = apr_pstrdup(pool, target);
293251881Speter  eb->processor = processor;
294251881Speter  eb->depth = depth;
295251881Speter  eb->ignore_ancestry = ignore_ancestry;
296251881Speter  eb->local_before_remote = reverse_order;
297251881Speter  eb->diff_pristine = use_text_base;
298251881Speter  eb->changelist_hash = changelist_hash;
299251881Speter  eb->cancel_func = cancel_func;
300251881Speter  eb->cancel_baton = cancel_baton;
301251881Speter  eb->pool = pool;
302251881Speter
303251881Speter  *edit_baton = eb;
304251881Speter  return SVN_NO_ERROR;
305251881Speter}
306251881Speter
307251881Speter/* Create a new directory baton.  PATH is the directory path,
308251881Speter * including anchor_path.  ADDED is set if this directory is being
309251881Speter * added rather than replaced.  PARENT_BATON is the baton of the
310251881Speter * parent directory, it will be null if this is the root of the
311251881Speter * comparison hierarchy.  The directory and its parent may or may not
312251881Speter * exist in the working copy.  EDIT_BATON is the overall crawler
313251881Speter * editor baton.
314251881Speter */
315251881Speterstatic struct dir_baton_t *
316251881Spetermake_dir_baton(const char *path,
317251881Speter               struct dir_baton_t *parent_baton,
318251881Speter               struct edit_baton_t *eb,
319251881Speter               svn_boolean_t added,
320251881Speter               svn_depth_t depth,
321251881Speter               apr_pool_t *result_pool)
322251881Speter{
323251881Speter  apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
324251881Speter                                                      : eb->pool);
325251881Speter  struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
326251881Speter
327251881Speter  db->parent_baton = parent_baton;
328251881Speter
329251881Speter  /* Allocate 1 string for using as 3 strings */
330251881Speter  db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
331251881Speter  db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
332251881Speter  db->name = svn_dirent_basename(db->relpath, NULL);
333251881Speter
334251881Speter  db->eb = eb;
335251881Speter  db->added = added;
336251881Speter  db->depth = depth;
337251881Speter  db->pool = dir_pool;
338251881Speter  db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
339251881Speter  db->compared = apr_hash_make(dir_pool);
340251881Speter
341251881Speter  if (parent_baton != NULL)
342251881Speter    {
343251881Speter      parent_baton->users++;
344251881Speter    }
345251881Speter
346251881Speter  db->users = 1;
347251881Speter
348251881Speter  return db;
349251881Speter}
350251881Speter
351251881Speter/* Create a new file baton.  PATH is the file path, including
352251881Speter * anchor_path.  ADDED is set if this file is being added rather than
353251881Speter * replaced.  PARENT_BATON is the baton of the parent directory.
354251881Speter * The directory and its parent may or may not exist in the working copy.
355251881Speter */
356251881Speterstatic struct file_baton_t *
357251881Spetermake_file_baton(const char *path,
358251881Speter                svn_boolean_t added,
359251881Speter                struct dir_baton_t *parent_baton,
360251881Speter                apr_pool_t *result_pool)
361251881Speter{
362251881Speter  apr_pool_t *file_pool = svn_pool_create(result_pool);
363251881Speter  struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
364251881Speter  struct edit_baton_t *eb = parent_baton->eb;
365251881Speter
366251881Speter  fb->eb = eb;
367251881Speter  fb->parent_baton = parent_baton;
368251881Speter  fb->parent_baton->users++;
369251881Speter
370251881Speter  /* Allocate 1 string for using as 3 strings */
371251881Speter  fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
372251881Speter  fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
373251881Speter  fb->name = svn_dirent_basename(fb->relpath, NULL);
374251881Speter
375251881Speter  fb->added = added;
376251881Speter  fb->pool = file_pool;
377251881Speter  fb->propchanges  = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
378251881Speter
379251881Speter  return fb;
380251881Speter}
381251881Speter
382251881Speter/* Destroy DB when there are no more registered users */
383251881Speterstatic svn_error_t *
384251881Spetermaybe_done(struct dir_baton_t *db)
385251881Speter{
386251881Speter  db->users--;
387251881Speter
388251881Speter  if (!db->users)
389251881Speter    {
390251881Speter      struct dir_baton_t *pb = db->parent_baton;
391251881Speter
392251881Speter      svn_pool_clear(db->pool);
393251881Speter
394251881Speter      if (pb != NULL)
395251881Speter        SVN_ERR(maybe_done(pb));
396251881Speter    }
397251881Speter
398251881Speter  return SVN_NO_ERROR;
399251881Speter}
400251881Speter
401251881Speter/* Standard check to see if a node is represented in the local working copy */
402251881Speter#define NOT_PRESENT(status)                                    \
403251881Speter            ((status) == svn_wc__db_status_not_present          \
404251881Speter             || (status) == svn_wc__db_status_excluded          \
405251881Speter             || (status) == svn_wc__db_status_server_excluded)
406251881Speter
407251881Spetersvn_error_t *
408251881Spetersvn_wc__diff_base_working_diff(svn_wc__db_t *db,
409251881Speter                               const char *local_abspath,
410251881Speter                               const char *relpath,
411251881Speter                               svn_revnum_t revision,
412251881Speter                               apr_hash_t *changelist_hash,
413251881Speter                               const svn_diff_tree_processor_t *processor,
414251881Speter                               void *processor_dir_baton,
415251881Speter                               svn_boolean_t diff_pristine,
416251881Speter                               svn_cancel_func_t cancel_func,
417251881Speter                               void *cancel_baton,
418251881Speter                               apr_pool_t *scratch_pool)
419251881Speter{
420251881Speter  void *file_baton = NULL;
421251881Speter  svn_boolean_t skip = FALSE;
422251881Speter  svn_wc__db_status_t status;
423251881Speter  svn_revnum_t db_revision;
424251881Speter  svn_boolean_t had_props;
425251881Speter  svn_boolean_t props_mod;
426251881Speter  svn_boolean_t files_same = FALSE;
427251881Speter  svn_wc__db_status_t base_status;
428251881Speter  const svn_checksum_t *working_checksum;
429251881Speter  const svn_checksum_t *checksum;
430251881Speter  svn_filesize_t recorded_size;
431251881Speter  apr_time_t recorded_time;
432251881Speter  const char *pristine_file;
433251881Speter  const char *local_file;
434251881Speter  svn_diff_source_t *left_src;
435251881Speter  svn_diff_source_t *right_src;
436251881Speter  apr_hash_t *base_props;
437251881Speter  apr_hash_t *local_props;
438251881Speter  apr_array_header_t *prop_changes;
439251881Speter  const char *changelist;
440251881Speter
441251881Speter  SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
442251881Speter                               NULL, NULL, NULL, NULL, &working_checksum, NULL,
443251881Speter                               NULL, NULL, NULL, NULL, NULL, &recorded_size,
444251881Speter                               &recorded_time, &changelist, NULL, NULL,
445251881Speter                               &had_props, &props_mod, NULL, NULL, NULL,
446251881Speter                               db, local_abspath, scratch_pool, scratch_pool));
447251881Speter  checksum = working_checksum;
448251881Speter
449251881Speter  assert(status == svn_wc__db_status_normal
450251881Speter         || status == svn_wc__db_status_added
451251881Speter         || (status == svn_wc__db_status_deleted && diff_pristine));
452251881Speter
453251881Speter  /* If the item is not a member of a specified changelist (and there are
454251881Speter     some specified changelists), skip it. */
455251881Speter  if (changelist_hash && !svn_hash_gets(changelist_hash, changelist))
456251881Speter    return SVN_NO_ERROR;
457251881Speter
458251881Speter
459251881Speter  if (status != svn_wc__db_status_normal)
460251881Speter    {
461251881Speter      SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
462251881Speter                                       NULL, NULL, NULL, NULL, NULL, NULL,
463251881Speter                                       NULL, &checksum, NULL, NULL, &had_props,
464251881Speter                                       NULL, NULL,
465251881Speter                                       db, local_abspath,
466251881Speter                                       scratch_pool, scratch_pool));
467251881Speter      recorded_size = SVN_INVALID_FILESIZE;
468251881Speter      recorded_time = 0;
469251881Speter      props_mod = TRUE; /* Requires compare */
470251881Speter    }
471251881Speter  else if (diff_pristine)
472251881Speter    files_same = TRUE;
473251881Speter  else
474251881Speter    {
475251881Speter      const svn_io_dirent2_t *dirent;
476251881Speter
477257936Speter      /* Verify truename to mimic status for iota/IOTA difference on Windows */
478251881Speter      SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
479257936Speter                                  TRUE /* verify truename */,
480251881Speter                                  TRUE /* ingore_enoent */,
481251881Speter                                  scratch_pool, scratch_pool));
482251881Speter
483257936Speter      /* If a file does not exist on disk (missing/obstructed) then we
484257936Speter         can't provide a text diff */
485257936Speter      if (dirent->kind != svn_node_file
486257936Speter          || (dirent->kind == svn_node_file
487257936Speter              && dirent->filesize == recorded_size
488257936Speter              && dirent->mtime == recorded_time))
489251881Speter        {
490251881Speter          files_same = TRUE;
491251881Speter        }
492251881Speter    }
493251881Speter
494251881Speter  if (files_same && !props_mod)
495251881Speter    return SVN_NO_ERROR; /* Cheap exit */
496251881Speter
497251881Speter  assert(checksum);
498251881Speter
499251881Speter  if (!SVN_IS_VALID_REVNUM(revision))
500251881Speter    revision = db_revision;
501251881Speter
502251881Speter  left_src = svn_diff__source_create(revision, scratch_pool);
503251881Speter  right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
504251881Speter
505251881Speter  SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
506251881Speter                                 left_src,
507251881Speter                                 right_src,
508251881Speter                                 NULL /* copyfrom_src */,
509251881Speter                                 processor_dir_baton,
510251881Speter                                 processor,
511251881Speter                                 scratch_pool, scratch_pool));
512251881Speter
513251881Speter  if (skip)
514251881Speter    return SVN_NO_ERROR;
515251881Speter
516251881Speter  SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
517251881Speter                                       db, local_abspath, checksum,
518251881Speter                                       scratch_pool, scratch_pool));
519251881Speter
520251881Speter  if (diff_pristine)
521251881Speter    SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
522251881Speter                                         db, local_abspath,
523251881Speter                                         working_checksum,
524251881Speter                                         scratch_pool, scratch_pool));
525251881Speter  else if (! (had_props || props_mod))
526251881Speter    local_file = local_abspath;
527251881Speter  else if (files_same)
528251881Speter    local_file = pristine_file;
529251881Speter  else
530251881Speter    SVN_ERR(svn_wc__internal_translated_file(
531251881Speter                            &local_file, local_abspath,
532251881Speter                            db, local_abspath,
533251881Speter                            SVN_WC_TRANSLATE_TO_NF
534251881Speter                                | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
535251881Speter                            cancel_func, cancel_baton,
536251881Speter                            scratch_pool, scratch_pool));
537251881Speter
538251881Speter  if (! files_same)
539251881Speter    SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
540251881Speter                                         pristine_file, scratch_pool));
541251881Speter
542251881Speter  if (had_props)
543251881Speter    SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
544251881Speter                                      scratch_pool, scratch_pool));
545251881Speter  else
546251881Speter    base_props = apr_hash_make(scratch_pool);
547251881Speter
548251881Speter  if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
549251881Speter    local_props = base_props;
550251881Speter  else if (diff_pristine)
551251881Speter    SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
552251881Speter                                           scratch_pool, scratch_pool));
553251881Speter  else
554251881Speter    SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
555251881Speter                                  scratch_pool, scratch_pool));
556251881Speter
557251881Speter  SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
558251881Speter
559251881Speter  if (prop_changes->nelts || !files_same)
560251881Speter    {
561251881Speter      SVN_ERR(processor->file_changed(relpath,
562251881Speter                                      left_src,
563251881Speter                                      right_src,
564251881Speter                                      pristine_file,
565251881Speter                                      local_file,
566251881Speter                                      base_props,
567251881Speter                                      local_props,
568251881Speter                                      ! files_same,
569251881Speter                                      prop_changes,
570251881Speter                                      file_baton,
571251881Speter                                      processor,
572251881Speter                                      scratch_pool));
573251881Speter    }
574251881Speter  else
575251881Speter    {
576251881Speter      SVN_ERR(processor->file_closed(relpath,
577251881Speter                                     left_src,
578251881Speter                                     right_src,
579251881Speter                                     file_baton,
580251881Speter                                     processor,
581251881Speter                                     scratch_pool));
582251881Speter    }
583251881Speter
584251881Speter  return SVN_NO_ERROR;
585251881Speter}
586251881Speter
587251881Speterstatic svn_error_t *
588251881Speterensure_local_info(struct dir_baton_t *db,
589251881Speter                  apr_pool_t *scratch_pool)
590251881Speter{
591251881Speter  apr_hash_t *conflicts;
592251881Speter
593251881Speter  if (db->local_info)
594251881Speter    return SVN_NO_ERROR;
595251881Speter
596251881Speter  SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
597251881Speter                                        db->eb->db, db->local_abspath,
598251881Speter                                        db->pool, scratch_pool));
599251881Speter
600251881Speter  return SVN_NO_ERROR;
601251881Speter}
602251881Speter
603251881Speter/* Called when the directory is closed to compare any elements that have
604251881Speter * not yet been compared.  This identifies local, working copy only
605251881Speter * changes.  At this stage we are dealing with files/directories that do
606251881Speter * exist in the working copy.
607251881Speter *
608251881Speter * DIR_BATON is the baton for the directory.
609251881Speter */
610251881Speterstatic svn_error_t *
611251881Speterwalk_local_nodes_diff(struct edit_baton_t *eb,
612251881Speter                      const char *local_abspath,
613251881Speter                      const char *path,
614251881Speter                      svn_depth_t depth,
615251881Speter                      apr_hash_t *compared,
616251881Speter                      void *parent_baton,
617251881Speter                      apr_pool_t *scratch_pool)
618251881Speter{
619251881Speter  svn_wc__db_t *db = eb->db;
620251881Speter  svn_boolean_t in_anchor_not_target;
621251881Speter  apr_pool_t *iterpool;
622251881Speter  void *dir_baton = NULL;
623251881Speter  svn_boolean_t skip = FALSE;
624251881Speter  svn_boolean_t skip_children = FALSE;
625251881Speter  svn_revnum_t revision;
626251881Speter  svn_boolean_t props_mod;
627251881Speter  svn_diff_source_t *left_src;
628251881Speter  svn_diff_source_t *right_src;
629251881Speter
630251881Speter  /* Everything we do below is useless if we are comparing to BASE. */
631251881Speter  if (eb->diff_pristine)
632251881Speter    return SVN_NO_ERROR;
633251881Speter
634251881Speter  /* Determine if this is the anchor directory if the anchor is different
635251881Speter     to the target. When the target is a file, the anchor is the parent
636251881Speter     directory and if this is that directory the non-target entries must be
637251881Speter     skipped. */
638251881Speter  in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
639251881Speter
640251881Speter  iterpool = svn_pool_create(scratch_pool);
641251881Speter
642251881Speter  SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
643251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
644251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
645251881Speter                               NULL, &props_mod, NULL, NULL, NULL,
646251881Speter                               db, local_abspath, scratch_pool, scratch_pool));
647251881Speter
648251881Speter  left_src = svn_diff__source_create(revision, scratch_pool);
649251881Speter  right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
650251881Speter
651251881Speter  if (compared)
652251881Speter    {
653251881Speter      dir_baton = parent_baton;
654251881Speter      skip = TRUE;
655251881Speter    }
656251881Speter  else if (!in_anchor_not_target)
657251881Speter    SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
658251881Speter                                      path,
659251881Speter                                      left_src,
660251881Speter                                      right_src,
661251881Speter                                      NULL /* copyfrom_src */,
662251881Speter                                      parent_baton,
663251881Speter                                      eb->processor,
664251881Speter                                      scratch_pool, scratch_pool));
665251881Speter
666251881Speter
667251881Speter  if (!skip_children && depth != svn_depth_empty)
668251881Speter    {
669251881Speter      apr_hash_t *nodes;
670251881Speter      apr_hash_t *conflicts;
671251881Speter      apr_array_header_t *children;
672251881Speter      svn_depth_t depth_below_here = depth;
673251881Speter      svn_boolean_t diff_files;
674251881Speter      svn_boolean_t diff_dirs;
675251881Speter      int i;
676251881Speter
677251881Speter      if (depth_below_here == svn_depth_immediates)
678251881Speter        depth_below_here = svn_depth_empty;
679251881Speter
680251881Speter      diff_files = (depth == svn_depth_unknown
681251881Speter                   || depth >= svn_depth_files);
682251881Speter      diff_dirs = (depth == svn_depth_unknown
683251881Speter                   || depth >= svn_depth_immediates);
684251881Speter
685251881Speter      SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
686251881Speter                                            db, local_abspath,
687251881Speter                                            scratch_pool, iterpool));
688251881Speter
689251881Speter      children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
690251881Speter                            scratch_pool);
691251881Speter
692251881Speter      for (i = 0; i < children->nelts; i++)
693251881Speter        {
694251881Speter          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
695251881Speter                                                  svn_sort__item_t);
696251881Speter          const char *name = item->key;
697251881Speter          struct svn_wc__db_info_t *info = item->value;
698251881Speter
699251881Speter          const char *child_abspath;
700251881Speter          const char *child_relpath;
701251881Speter          svn_boolean_t repos_only;
702251881Speter          svn_boolean_t local_only;
703251881Speter          svn_node_kind_t base_kind;
704251881Speter
705251881Speter          if (eb->cancel_func)
706251881Speter            SVN_ERR(eb->cancel_func(eb->cancel_baton));
707251881Speter
708251881Speter          /* In the anchor directory, if the anchor is not the target then all
709251881Speter             entries other than the target should not be diff'd. Running diff
710251881Speter             on one file in a directory should not diff other files in that
711251881Speter             directory. */
712251881Speter          if (in_anchor_not_target && strcmp(eb->target, name))
713251881Speter            continue;
714251881Speter
715251881Speter          if (compared && svn_hash_gets(compared, name))
716251881Speter            continue;
717251881Speter
718251881Speter          if (NOT_PRESENT(info->status))
719251881Speter            continue;
720251881Speter
721251881Speter          assert(info->status == svn_wc__db_status_normal
722251881Speter                 || info->status == svn_wc__db_status_added
723251881Speter                 || info->status == svn_wc__db_status_deleted);
724251881Speter
725251881Speter          svn_pool_clear(iterpool);
726251881Speter          child_abspath = svn_dirent_join(local_abspath, name, iterpool);
727251881Speter          child_relpath = svn_relpath_join(path, name, iterpool);
728251881Speter
729251881Speter          repos_only = FALSE;
730251881Speter          local_only = FALSE;
731251881Speter
732251881Speter          if (!info->have_base)
733251881Speter            {
734251881Speter              local_only = TRUE; /* Only report additions */
735251881Speter            }
736251881Speter          else if (info->status == svn_wc__db_status_normal)
737251881Speter            {
738251881Speter              /* Simple diff */
739251881Speter              base_kind = info->kind;
740251881Speter            }
741251881Speter          else if (info->status == svn_wc__db_status_deleted
742251881Speter                   && (!eb->diff_pristine || !info->have_more_work))
743251881Speter            {
744251881Speter              svn_wc__db_status_t base_status;
745251881Speter              repos_only = TRUE;
746251881Speter              SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
747251881Speter                                               NULL, NULL, NULL, NULL, NULL,
748251881Speter                                               NULL, NULL, NULL, NULL, NULL,
749251881Speter                                               NULL, NULL, NULL,
750251881Speter                                               db, child_abspath,
751251881Speter                                               iterpool, iterpool));
752251881Speter
753251881Speter              if (NOT_PRESENT(base_status))
754251881Speter                continue;
755251881Speter            }
756251881Speter          else
757251881Speter            {
758251881Speter              /* working status is either added or deleted */
759251881Speter              svn_wc__db_status_t base_status;
760251881Speter
761251881Speter              SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
762251881Speter                                               NULL, NULL, NULL, NULL, NULL,
763251881Speter                                               NULL, NULL, NULL, NULL, NULL,
764251881Speter                                               NULL, NULL, NULL,
765251881Speter                                               db, child_abspath,
766251881Speter                                               iterpool, iterpool));
767251881Speter
768251881Speter              if (NOT_PRESENT(base_status))
769251881Speter                local_only = TRUE;
770251881Speter              else if (base_kind != info->kind || !eb->ignore_ancestry)
771251881Speter                {
772251881Speter                  repos_only = TRUE;
773251881Speter                  local_only = TRUE;
774251881Speter                }
775251881Speter            }
776251881Speter
777251881Speter          if (eb->local_before_remote && local_only)
778251881Speter            {
779251881Speter              if (info->kind == svn_node_file && diff_files)
780251881Speter                SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
781251881Speter                                                     child_relpath,
782251881Speter                                                     eb->processor, dir_baton,
783251881Speter                                                     eb->changelist_hash,
784251881Speter                                                     eb->diff_pristine,
785251881Speter                                                     eb->cancel_func,
786251881Speter                                                     eb->cancel_baton,
787251881Speter                                                     iterpool));
788251881Speter              else if (info->kind == svn_node_dir && diff_dirs)
789251881Speter                SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
790251881Speter                                                    child_relpath,
791251881Speter                                                    depth_below_here,
792251881Speter                                                    eb->processor, dir_baton,
793251881Speter                                                    eb->changelist_hash,
794251881Speter                                                    eb->diff_pristine,
795251881Speter                                                    eb->cancel_func,
796251881Speter                                                    eb->cancel_baton,
797251881Speter                                                    iterpool));
798251881Speter            }
799251881Speter
800251881Speter          if (repos_only)
801251881Speter            {
802251881Speter              /* Report repository form deleted */
803251881Speter              if (base_kind == svn_node_file && diff_files)
804251881Speter                SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
805251881Speter                                                    child_relpath, eb->revnum,
806251881Speter                                                    eb->processor, dir_baton,
807251881Speter                                                    iterpool));
808251881Speter              else if (base_kind == svn_node_dir && diff_dirs)
809251881Speter                SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
810251881Speter                                                   child_relpath, eb->revnum,
811251881Speter                                                   depth_below_here,
812251881Speter                                                   eb->processor, dir_baton,
813251881Speter                                                   eb->cancel_func,
814251881Speter                                                   eb->cancel_baton,
815251881Speter                                                   iterpool));
816251881Speter            }
817251881Speter          else if (!local_only) /* Not local only nor remote only */
818251881Speter            {
819251881Speter              /* Diff base against actual */
820251881Speter              if (info->kind == svn_node_file && diff_files)
821251881Speter                {
822251881Speter                  if (info->status != svn_wc__db_status_normal
823251881Speter                      || !eb->diff_pristine)
824251881Speter                    {
825251881Speter                      SVN_ERR(svn_wc__diff_base_working_diff(
826251881Speter                                                db, child_abspath,
827251881Speter                                                child_relpath,
828251881Speter                                                eb->revnum,
829251881Speter                                                eb->changelist_hash,
830251881Speter                                                eb->processor, dir_baton,
831251881Speter                                                eb->diff_pristine,
832251881Speter                                                eb->cancel_func,
833251881Speter                                                eb->cancel_baton,
834251881Speter                                                scratch_pool));
835251881Speter                    }
836251881Speter                }
837251881Speter              else if (info->kind == svn_node_dir && diff_dirs)
838251881Speter                SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
839251881Speter                                              child_relpath,
840251881Speter                                              depth_below_here,
841251881Speter                                              NULL /* compared */,
842251881Speter                                              dir_baton,
843251881Speter                                              scratch_pool));
844251881Speter            }
845251881Speter
846251881Speter          if (!eb->local_before_remote && local_only)
847251881Speter            {
848251881Speter              if (info->kind == svn_node_file && diff_files)
849251881Speter                SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
850251881Speter                                                     child_relpath,
851251881Speter                                                     eb->processor, dir_baton,
852251881Speter                                                     eb->changelist_hash,
853251881Speter                                                     eb->diff_pristine,
854251881Speter                                                     eb->cancel_func,
855251881Speter                                                     eb->cancel_baton,
856251881Speter                                                     iterpool));
857251881Speter              else if (info->kind == svn_node_dir && diff_dirs)
858251881Speter                SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
859251881Speter                                                     child_relpath, depth_below_here,
860251881Speter                                                     eb->processor, dir_baton,
861251881Speter                                                     eb->changelist_hash,
862251881Speter                                                     eb->diff_pristine,
863251881Speter                                                     eb->cancel_func,
864251881Speter                                                     eb->cancel_baton,
865251881Speter                                                     iterpool));
866251881Speter            }
867251881Speter        }
868251881Speter    }
869251881Speter
870251881Speter  if (compared)
871251881Speter    return SVN_NO_ERROR;
872251881Speter
873251881Speter    /* Check for local property mods on this directory, if we haven't
874251881Speter     already reported them and we aren't changelist-filted.
875251881Speter     ### it should be noted that we do not currently allow directories
876251881Speter     ### to be part of changelists, so if a changelist is provided, the
877251881Speter     ### changelist check will always fail. */
878251881Speter  if (! skip
879251881Speter      && ! eb->changelist_hash
880251881Speter      && ! in_anchor_not_target
881251881Speter      && props_mod)
882251881Speter    {
883251881Speter      apr_array_header_t *propchanges;
884251881Speter      apr_hash_t *left_props;
885251881Speter      apr_hash_t *right_props;
886251881Speter
887251881Speter      SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
888251881Speter                                        db, local_abspath,
889251881Speter                                        scratch_pool, scratch_pool));
890251881Speter
891251881Speter      right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
892251881Speter
893251881Speter      SVN_ERR(eb->processor->dir_changed(path,
894251881Speter                                         left_src,
895251881Speter                                         right_src,
896251881Speter                                         left_props,
897251881Speter                                         right_props,
898251881Speter                                         propchanges,
899251881Speter                                         dir_baton,
900251881Speter                                         eb->processor,
901251881Speter                                         scratch_pool));
902251881Speter    }
903251881Speter  else if (! skip)
904251881Speter    SVN_ERR(eb->processor->dir_closed(path,
905251881Speter                                      left_src,
906251881Speter                                      right_src,
907251881Speter                                      dir_baton,
908251881Speter                                      eb->processor,
909251881Speter                                      scratch_pool));
910251881Speter
911251881Speter  svn_pool_destroy(iterpool);
912251881Speter
913251881Speter  return SVN_NO_ERROR;
914251881Speter}
915251881Speter
916251881Spetersvn_error_t *
917251881Spetersvn_wc__diff_local_only_file(svn_wc__db_t *db,
918251881Speter                             const char *local_abspath,
919251881Speter                             const char *relpath,
920251881Speter                             const svn_diff_tree_processor_t *processor,
921251881Speter                             void *processor_parent_baton,
922251881Speter                             apr_hash_t *changelist_hash,
923251881Speter                             svn_boolean_t diff_pristine,
924251881Speter                             svn_cancel_func_t cancel_func,
925251881Speter                             void *cancel_baton,
926251881Speter                             apr_pool_t *scratch_pool)
927251881Speter{
928251881Speter  svn_diff_source_t *right_src;
929251881Speter  svn_diff_source_t *copyfrom_src = NULL;
930251881Speter  svn_wc__db_status_t status;
931251881Speter  svn_node_kind_t kind;
932251881Speter  const svn_checksum_t *checksum;
933251881Speter  const char *original_repos_relpath;
934251881Speter  svn_revnum_t original_revision;
935251881Speter  const char *changelist;
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,
951251881Speter                               &changelist, 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 (changelist && changelist_hash
963251881Speter      && !svn_hash_gets(changelist_hash, changelist))
964251881Speter    return SVN_NO_ERROR;
965251881Speter
966251881Speter  if (status == svn_wc__db_status_deleted)
967251881Speter    {
968251881Speter      assert(diff_pristine);
969251881Speter
970251881Speter      SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
971251881Speter                                            NULL, &checksum, NULL, &had_props,
972251881Speter                                            &pristine_props,
973251881Speter                                            db, local_abspath,
974251881Speter                                            scratch_pool, scratch_pool));
975251881Speter      props_mod = FALSE;
976251881Speter    }
977251881Speter  else if (!had_props)
978251881Speter    pristine_props = apr_hash_make(scratch_pool);
979251881Speter  else
980251881Speter    SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
981251881Speter                                           db, local_abspath,
982251881Speter                                           scratch_pool, scratch_pool));
983251881Speter
984251881Speter  if (original_repos_relpath)
985251881Speter    {
986251881Speter      copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
987251881Speter      copyfrom_src->repos_relpath = original_repos_relpath;
988251881Speter    }
989251881Speter
990251881Speter  if (props_mod || !SVN_IS_VALID_REVNUM(revision))
991251881Speter    right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
992251881Speter  else
993251881Speter    {
994251881Speter      if (diff_pristine)
995251881Speter        file_mod = FALSE;
996251881Speter      else
997251881Speter        SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
998251881Speter                                                 FALSE, scratch_pool));
999251881Speter
1000251881Speter      if (!file_mod)
1001251881Speter        right_src = svn_diff__source_create(revision, scratch_pool);
1002251881Speter      else
1003251881Speter        right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
1004251881Speter    }
1005251881Speter
1006251881Speter  SVN_ERR(processor->file_opened(&file_baton, &skip,
1007251881Speter                                 relpath,
1008251881Speter                                 NULL /* left_source */,
1009251881Speter                                 right_src,
1010251881Speter                                 copyfrom_src,
1011251881Speter                                 processor_parent_baton,
1012251881Speter                                 processor,
1013251881Speter                                 scratch_pool, scratch_pool));
1014251881Speter
1015251881Speter  if (skip)
1016251881Speter    return SVN_NO_ERROR;
1017251881Speter
1018251881Speter  if (props_mod && !diff_pristine)
1019251881Speter    SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
1020251881Speter                                  scratch_pool, scratch_pool));
1021251881Speter  else
1022251881Speter    right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
1023251881Speter
1024251881Speter  if (checksum)
1025251881Speter    SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
1026251881Speter                                         checksum, scratch_pool, scratch_pool));
1027251881Speter  else
1028251881Speter    pristine_file = NULL;
1029251881Speter
1030251881Speter  if (diff_pristine)
1031251881Speter    {
1032251881Speter      translated_file = pristine_file; /* No translation needed */
1033251881Speter    }
1034251881Speter  else
1035251881Speter    {
1036251881Speter      SVN_ERR(svn_wc__internal_translated_file(
1037251881Speter           &translated_file, local_abspath, db, local_abspath,
1038251881Speter           SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
1039251881Speter           cancel_func, cancel_baton,
1040251881Speter           scratch_pool, scratch_pool));
1041251881Speter    }
1042251881Speter
1043251881Speter  SVN_ERR(processor->file_added(relpath,
1044251881Speter                                copyfrom_src,
1045251881Speter                                right_src,
1046251881Speter                                copyfrom_src
1047251881Speter                                  ? pristine_file
1048251881Speter                                  : NULL,
1049251881Speter                                translated_file,
1050251881Speter                                copyfrom_src
1051251881Speter                                  ? pristine_props
1052251881Speter                                  : NULL,
1053251881Speter                                right_props,
1054251881Speter                                file_baton,
1055251881Speter                                processor,
1056251881Speter                                scratch_pool));
1057251881Speter
1058251881Speter  return SVN_NO_ERROR;
1059251881Speter}
1060251881Speter
1061251881Spetersvn_error_t *
1062251881Spetersvn_wc__diff_local_only_dir(svn_wc__db_t *db,
1063251881Speter                            const char *local_abspath,
1064251881Speter                            const char *relpath,
1065251881Speter                            svn_depth_t depth,
1066251881Speter                            const svn_diff_tree_processor_t *processor,
1067251881Speter                            void *processor_parent_baton,
1068251881Speter                            apr_hash_t *changelist_hash,
1069251881Speter                            svn_boolean_t diff_pristine,
1070251881Speter                            svn_cancel_func_t cancel_func,
1071251881Speter                            void *cancel_baton,
1072251881Speter                            apr_pool_t *scratch_pool)
1073251881Speter{
1074251881Speter  const apr_array_header_t *children;
1075251881Speter  int i;
1076251881Speter  apr_pool_t *iterpool;
1077251881Speter  void *pdb = NULL;
1078251881Speter  svn_boolean_t skip = FALSE;
1079251881Speter  svn_boolean_t skip_children = FALSE;
1080251881Speter  svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
1081251881Speter                                                         scratch_pool);
1082251881Speter  svn_depth_t depth_below_here = depth;
1083251881Speter  apr_hash_t *nodes;
1084251881Speter  apr_hash_t *conflicts;
1085251881Speter
1086251881Speter  /* Report the addition of the directory's contents. */
1087251881Speter  iterpool = svn_pool_create(scratch_pool);
1088251881Speter
1089251881Speter  SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
1090251881Speter                                relpath,
1091251881Speter                                NULL,
1092251881Speter                                right_src,
1093251881Speter                                NULL /* copyfrom_src */,
1094251881Speter                                processor_parent_baton,
1095251881Speter                                processor,
1096251881Speter                                scratch_pool, iterpool));
1097251881Speter
1098251881Speter  SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath,
1099251881Speter                                        scratch_pool, iterpool));
1100251881Speter
1101251881Speter  if (depth_below_here == svn_depth_immediates)
1102251881Speter    depth_below_here = svn_depth_empty;
1103251881Speter
1104251881Speter  children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1105251881Speter                            scratch_pool);
1106251881Speter
1107251881Speter  for (i = 0; i < children->nelts; i++)
1108251881Speter    {
1109251881Speter      svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
1110251881Speter      const char *name = item->key;
1111251881Speter      struct svn_wc__db_info_t *info = item->value;
1112251881Speter      const char *child_abspath;
1113251881Speter      const char *child_relpath;
1114251881Speter
1115251881Speter      svn_pool_clear(iterpool);
1116251881Speter
1117251881Speter      if (cancel_func)
1118251881Speter        SVN_ERR(cancel_func(cancel_baton));
1119251881Speter
1120251881Speter      child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1121251881Speter
1122251881Speter      if (NOT_PRESENT(info->status))
1123251881Speter        {
1124251881Speter          continue;
1125251881Speter        }
1126251881Speter
1127251881Speter      /* If comparing against WORKING, skip entries that are
1128251881Speter         schedule-deleted - they don't really exist. */
1129251881Speter      if (!diff_pristine && info->status == svn_wc__db_status_deleted)
1130251881Speter        continue;
1131251881Speter
1132251881Speter      child_relpath = svn_relpath_join(relpath, name, iterpool);
1133251881Speter
1134251881Speter      switch (info->kind)
1135251881Speter        {
1136251881Speter        case svn_node_file:
1137251881Speter        case svn_node_symlink:
1138251881Speter          SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
1139251881Speter                                               child_relpath,
1140251881Speter                                               processor, pdb,
1141251881Speter                                               changelist_hash,
1142251881Speter                                               diff_pristine,
1143251881Speter                                               cancel_func, cancel_baton,
1144251881Speter                                               scratch_pool));
1145251881Speter          break;
1146251881Speter
1147251881Speter        case svn_node_dir:
1148251881Speter          if (depth > svn_depth_files || depth == svn_depth_unknown)
1149251881Speter            {
1150251881Speter              SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
1151251881Speter                                                  child_relpath, depth_below_here,
1152251881Speter                                                  processor, pdb,
1153251881Speter                                                  changelist_hash,
1154251881Speter                                                  diff_pristine,
1155251881Speter                                                  cancel_func, cancel_baton,
1156251881Speter                                                  iterpool));
1157251881Speter            }
1158251881Speter          break;
1159251881Speter
1160251881Speter        default:
1161251881Speter          break;
1162251881Speter        }
1163251881Speter    }
1164251881Speter
1165251881Speter  if (!skip)
1166251881Speter    {
1167251881Speter      apr_hash_t *right_props;
1168251881Speter      if (diff_pristine)
1169251881Speter        SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath,
1170251881Speter                                               scratch_pool, scratch_pool));
1171251881Speter      else
1172251881Speter        SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath,
1173251881Speter                                         scratch_pool, scratch_pool));
1174251881Speter
1175251881Speter      SVN_ERR(processor->dir_added(relpath,
1176251881Speter                                   NULL /* copyfrom_src */,
1177251881Speter                                   right_src,
1178251881Speter                                   NULL,
1179251881Speter                                   right_props,
1180251881Speter                                   pdb,
1181251881Speter                                   processor,
1182251881Speter                                   iterpool));
1183251881Speter    }
1184251881Speter  svn_pool_destroy(iterpool);
1185251881Speter
1186251881Speter  return SVN_NO_ERROR;
1187251881Speter}
1188251881Speter
1189251881Speter/* Reports local changes. */
1190251881Speterstatic svn_error_t *
1191251881Speterhandle_local_only(struct dir_baton_t *pb,
1192251881Speter                  const char *name,
1193251881Speter                  apr_pool_t *scratch_pool)
1194251881Speter{
1195251881Speter  struct edit_baton_t *eb = pb->eb;
1196251881Speter  const struct svn_wc__db_info_t *info;
1197251881Speter  svn_boolean_t repos_delete = (pb->deletes
1198251881Speter                                && svn_hash_gets(pb->deletes, name));
1199251881Speter
1200251881Speter  assert(!strchr(name, '/'));
1201251881Speter  assert(!pb->added || eb->ignore_ancestry);
1202251881Speter
1203251881Speter  if (pb->skip_children)
1204251881Speter    return SVN_NO_ERROR;
1205251881Speter
1206251881Speter  SVN_ERR(ensure_local_info(pb, scratch_pool));
1207251881Speter
1208251881Speter  info = svn_hash_gets(pb->local_info, name);
1209251881Speter
1210251881Speter  if (info == NULL || NOT_PRESENT(info->status))
1211251881Speter    return SVN_NO_ERROR;
1212251881Speter
1213251881Speter  switch (info->status)
1214251881Speter    {
1215251881Speter      case svn_wc__db_status_incomplete:
1216251881Speter        return SVN_NO_ERROR; /* Not local only */
1217251881Speter
1218251881Speter      case svn_wc__db_status_normal:
1219251881Speter        if (!repos_delete)
1220251881Speter          return SVN_NO_ERROR; /* Local and remote */
1221251881Speter        svn_hash_sets(pb->deletes, name, NULL);
1222251881Speter        break;
1223251881Speter
1224251881Speter      case svn_wc__db_status_deleted:
1225251881Speter        if (!(eb->diff_pristine && repos_delete))
1226251881Speter          return SVN_NO_ERROR;
1227251881Speter        break;
1228251881Speter
1229251881Speter      case svn_wc__db_status_added:
1230251881Speter      default:
1231251881Speter        break;
1232251881Speter    }
1233251881Speter
1234251881Speter  if (info->kind == svn_node_dir)
1235251881Speter    {
1236251881Speter      svn_depth_t depth ;
1237251881Speter
1238251881Speter      if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
1239251881Speter        depth = pb->depth;
1240251881Speter      else
1241251881Speter        depth = svn_depth_empty;
1242251881Speter
1243251881Speter      SVN_ERR(svn_wc__diff_local_only_dir(
1244251881Speter                      eb->db,
1245251881Speter                      svn_dirent_join(pb->local_abspath, name, scratch_pool),
1246251881Speter                      svn_relpath_join(pb->relpath, name, scratch_pool),
1247251881Speter                      repos_delete ? svn_depth_infinity : depth,
1248251881Speter                      eb->processor, pb->pdb,
1249251881Speter                      eb->changelist_hash,
1250251881Speter                      eb->diff_pristine,
1251251881Speter                      eb->cancel_func, eb->cancel_baton,
1252251881Speter                      scratch_pool));
1253251881Speter    }
1254251881Speter  else
1255251881Speter    SVN_ERR(svn_wc__diff_local_only_file(
1256251881Speter                      eb->db,
1257251881Speter                      svn_dirent_join(pb->local_abspath, name, scratch_pool),
1258251881Speter                      svn_relpath_join(pb->relpath, name, scratch_pool),
1259251881Speter                      eb->processor, pb->pdb,
1260251881Speter                      eb->changelist_hash,
1261251881Speter                      eb->diff_pristine,
1262251881Speter                      eb->cancel_func, eb->cancel_baton,
1263251881Speter                      scratch_pool));
1264251881Speter
1265251881Speter  return SVN_NO_ERROR;
1266251881Speter}
1267251881Speter
1268251881Speter/* Reports a file LOCAL_ABSPATH in BASE as deleted */
1269251881Spetersvn_error_t *
1270251881Spetersvn_wc__diff_base_only_file(svn_wc__db_t *db,
1271251881Speter                            const char *local_abspath,
1272251881Speter                            const char *relpath,
1273251881Speter                            svn_revnum_t revision,
1274251881Speter                            const svn_diff_tree_processor_t *processor,
1275251881Speter                            void *processor_parent_baton,
1276251881Speter                            apr_pool_t *scratch_pool)
1277251881Speter{
1278251881Speter  svn_wc__db_status_t status;
1279251881Speter  svn_node_kind_t kind;
1280251881Speter  const svn_checksum_t *checksum;
1281251881Speter  apr_hash_t *props;
1282251881Speter  void *file_baton = NULL;
1283251881Speter  svn_boolean_t skip = FALSE;
1284251881Speter  svn_diff_source_t *left_src;
1285251881Speter  const char *pristine_file;
1286251881Speter
1287251881Speter  SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
1288251881Speter                                   SVN_IS_VALID_REVNUM(revision)
1289251881Speter                                        ? NULL : &revision,
1290251881Speter                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1291251881Speter                                   &checksum, NULL, NULL, NULL, &props, NULL,
1292251881Speter                                   db, local_abspath,
1293251881Speter                                   scratch_pool, scratch_pool));
1294251881Speter
1295251881Speter  SVN_ERR_ASSERT(status == svn_wc__db_status_normal
1296251881Speter                 && kind == svn_node_file
1297251881Speter                 && checksum);
1298251881Speter
1299251881Speter  left_src = svn_diff__source_create(revision, scratch_pool);
1300251881Speter
1301251881Speter  SVN_ERR(processor->file_opened(&file_baton, &skip,
1302251881Speter                                 relpath,
1303251881Speter                                 left_src,
1304251881Speter                                 NULL /* right_src */,
1305251881Speter                                 NULL /* copyfrom_source */,
1306251881Speter                                 processor_parent_baton,
1307251881Speter                                 processor,
1308251881Speter                                 scratch_pool, scratch_pool));
1309251881Speter
1310251881Speter  if (skip)
1311251881Speter    return SVN_NO_ERROR;
1312251881Speter
1313251881Speter  SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
1314251881Speter                                       db, local_abspath, checksum,
1315251881Speter                                       scratch_pool, scratch_pool));
1316251881Speter
1317251881Speter  SVN_ERR(processor->file_deleted(relpath,
1318251881Speter                                  left_src,
1319251881Speter                                  pristine_file,
1320251881Speter                                  props,
1321251881Speter                                  file_baton,
1322251881Speter                                  processor,
1323251881Speter                                  scratch_pool));
1324251881Speter
1325251881Speter  return SVN_NO_ERROR;
1326251881Speter}
1327251881Speter
1328251881Spetersvn_error_t *
1329251881Spetersvn_wc__diff_base_only_dir(svn_wc__db_t *db,
1330251881Speter                           const char *local_abspath,
1331251881Speter                           const char *relpath,
1332251881Speter                           svn_revnum_t revision,
1333251881Speter                           svn_depth_t depth,
1334251881Speter                           const svn_diff_tree_processor_t *processor,
1335251881Speter                           void *processor_parent_baton,
1336251881Speter                           svn_cancel_func_t cancel_func,
1337251881Speter                           void *cancel_baton,
1338251881Speter                           apr_pool_t *scratch_pool)
1339251881Speter{
1340251881Speter  void *dir_baton = NULL;
1341251881Speter  svn_boolean_t skip = FALSE;
1342251881Speter  svn_boolean_t skip_children = FALSE;
1343251881Speter  svn_diff_source_t *left_src;
1344251881Speter  svn_revnum_t report_rev = revision;
1345251881Speter
1346251881Speter  if (!SVN_IS_VALID_REVNUM(report_rev))
1347251881Speter    SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
1348251881Speter                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1349251881Speter                                     NULL, NULL, NULL,
1350251881Speter                                     db, local_abspath,
1351251881Speter                                     scratch_pool, scratch_pool));
1352251881Speter
1353251881Speter  left_src = svn_diff__source_create(report_rev, scratch_pool);
1354251881Speter
1355251881Speter  SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
1356251881Speter                                relpath,
1357251881Speter                                left_src,
1358251881Speter                                NULL /* right_src */,
1359251881Speter                                NULL /* copyfrom_src */,
1360251881Speter                                processor_parent_baton,
1361251881Speter                                processor,
1362251881Speter                                scratch_pool, scratch_pool));
1363251881Speter
1364251881Speter  if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
1365251881Speter    {
1366251881Speter      apr_hash_t *nodes;
1367251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1368251881Speter      apr_array_header_t *children;
1369251881Speter      int i;
1370251881Speter
1371251881Speter      SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
1372251881Speter                                                scratch_pool, iterpool));
1373251881Speter
1374251881Speter      children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
1375251881Speter                                scratch_pool);
1376251881Speter
1377251881Speter      for (i = 0; i < children->nelts; i++)
1378251881Speter        {
1379251881Speter          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1380251881Speter                                                  svn_sort__item_t);
1381251881Speter          const char *name = item->key;
1382251881Speter          struct svn_wc__db_base_info_t *info = item->value;
1383251881Speter          const char *child_abspath;
1384251881Speter          const char *child_relpath;
1385251881Speter
1386251881Speter          if (info->status != svn_wc__db_status_normal)
1387251881Speter            continue;
1388251881Speter
1389251881Speter          if (cancel_func)
1390251881Speter            SVN_ERR(cancel_func(cancel_baton));
1391251881Speter
1392251881Speter          svn_pool_clear(iterpool);
1393251881Speter
1394251881Speter          child_abspath = svn_dirent_join(local_abspath, name, iterpool);
1395251881Speter          child_relpath = svn_relpath_join(relpath, name, iterpool);
1396251881Speter
1397251881Speter          switch (info->kind)
1398251881Speter            {
1399251881Speter              case svn_node_file:
1400251881Speter              case svn_node_symlink:
1401251881Speter                SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
1402251881Speter                                                    child_relpath,
1403251881Speter                                                    revision,
1404251881Speter                                                    processor, dir_baton,
1405251881Speter                                                    iterpool));
1406251881Speter                break;
1407251881Speter              case svn_node_dir:
1408251881Speter                if (depth > svn_depth_files || depth == svn_depth_unknown)
1409251881Speter                  {
1410251881Speter                    svn_depth_t depth_below_here = depth;
1411251881Speter
1412251881Speter                    if (depth_below_here == svn_depth_immediates)
1413251881Speter                      depth_below_here = svn_depth_empty;
1414251881Speter
1415251881Speter                    SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
1416251881Speter                                                       child_relpath,
1417251881Speter                                                       revision,
1418251881Speter                                                       depth_below_here,
1419251881Speter                                                       processor, dir_baton,
1420251881Speter                                                       cancel_func,
1421251881Speter                                                       cancel_baton,
1422251881Speter                                                       iterpool));
1423251881Speter                  }
1424251881Speter                break;
1425251881Speter
1426251881Speter              default:
1427251881Speter                break;
1428251881Speter            }
1429251881Speter        }
1430251881Speter    }
1431251881Speter
1432251881Speter  if (!skip)
1433251881Speter    {
1434251881Speter      apr_hash_t *props;
1435251881Speter      SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
1436251881Speter                                        scratch_pool, scratch_pool));
1437251881Speter
1438251881Speter      SVN_ERR(processor->dir_deleted(relpath,
1439251881Speter                                     left_src,
1440251881Speter                                     props,
1441251881Speter                                     dir_baton,
1442251881Speter                                     processor,
1443251881Speter                                     scratch_pool));
1444251881Speter    }
1445251881Speter
1446251881Speter  return SVN_NO_ERROR;
1447251881Speter}
1448251881Speter
1449251881Speter/* An svn_delta_editor_t function. */
1450251881Speterstatic svn_error_t *
1451251881Speterset_target_revision(void *edit_baton,
1452251881Speter                    svn_revnum_t target_revision,
1453251881Speter                    apr_pool_t *pool)
1454251881Speter{
1455251881Speter  struct edit_baton_t *eb = edit_baton;
1456251881Speter  eb->revnum = target_revision;
1457251881Speter
1458251881Speter  return SVN_NO_ERROR;
1459251881Speter}
1460251881Speter
1461251881Speter/* An svn_delta_editor_t function. The root of the comparison hierarchy */
1462251881Speterstatic svn_error_t *
1463251881Speteropen_root(void *edit_baton,
1464251881Speter          svn_revnum_t base_revision,
1465251881Speter          apr_pool_t *dir_pool,
1466251881Speter          void **root_baton)
1467251881Speter{
1468251881Speter  struct edit_baton_t *eb = edit_baton;
1469251881Speter  struct dir_baton_t *db;
1470251881Speter
1471251881Speter  eb->root_opened = TRUE;
1472251881Speter  db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
1473251881Speter  *root_baton = db;
1474251881Speter
1475251881Speter  if (eb->target[0] == '\0')
1476251881Speter    {
1477251881Speter      db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1478251881Speter      db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1479251881Speter
1480251881Speter      SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
1481251881Speter                                        &db->skip_children,
1482251881Speter                                        "",
1483251881Speter                                        db->left_src,
1484251881Speter                                        db->right_src,
1485251881Speter                                        NULL /* copyfrom_source */,
1486251881Speter                                        NULL /* parent_baton */,
1487251881Speter                                        eb->processor,
1488251881Speter                                        db->pool, db->pool));
1489251881Speter    }
1490251881Speter  else
1491251881Speter    db->skip = TRUE; /* Skip this, but not the children */
1492251881Speter
1493251881Speter  return SVN_NO_ERROR;
1494251881Speter}
1495251881Speter
1496251881Speter/* An svn_delta_editor_t function. */
1497251881Speterstatic svn_error_t *
1498251881Speterdelete_entry(const char *path,
1499251881Speter             svn_revnum_t base_revision,
1500251881Speter             void *parent_baton,
1501251881Speter             apr_pool_t *pool)
1502251881Speter{
1503251881Speter  struct dir_baton_t *pb = parent_baton;
1504251881Speter  const char *name = svn_dirent_basename(path, pb->pool);
1505251881Speter
1506251881Speter  if (!pb->deletes)
1507251881Speter    pb->deletes = apr_hash_make(pb->pool);
1508251881Speter
1509251881Speter  svn_hash_sets(pb->deletes, name, "");
1510251881Speter  return SVN_NO_ERROR;
1511251881Speter}
1512251881Speter
1513251881Speter/* An svn_delta_editor_t function. */
1514251881Speterstatic svn_error_t *
1515251881Speteradd_directory(const char *path,
1516251881Speter              void *parent_baton,
1517251881Speter              const char *copyfrom_path,
1518251881Speter              svn_revnum_t copyfrom_revision,
1519251881Speter              apr_pool_t *dir_pool,
1520251881Speter              void **child_baton)
1521251881Speter{
1522251881Speter  struct dir_baton_t *pb = parent_baton;
1523251881Speter  struct edit_baton_t *eb = pb->eb;
1524251881Speter  struct dir_baton_t *db;
1525251881Speter  svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1526251881Speter                              ? svn_depth_empty : pb->depth;
1527251881Speter
1528251881Speter  db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
1529251881Speter                      dir_pool);
1530251881Speter  *child_baton = db;
1531251881Speter
1532251881Speter  if (pb->repos_only || !eb->ignore_ancestry)
1533251881Speter    db->repos_only = TRUE;
1534251881Speter  else
1535251881Speter    {
1536251881Speter      struct svn_wc__db_info_t *info;
1537251881Speter      SVN_ERR(ensure_local_info(pb, dir_pool));
1538251881Speter
1539251881Speter      info = svn_hash_gets(pb->local_info, db->name);
1540251881Speter
1541251881Speter      if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1542251881Speter        db->repos_only = TRUE;
1543251881Speter
1544251881Speter      if (!db->repos_only && info->status != svn_wc__db_status_added)
1545251881Speter        db->repos_only = TRUE;
1546251881Speter
1547251881Speter      if (!db->repos_only)
1548251881Speter        {
1549251881Speter          db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1550251881Speter          db->ignoring_ancestry = TRUE;
1551251881Speter
1552251881Speter          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1553251881Speter        }
1554251881Speter    }
1555251881Speter
1556251881Speter  db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1557251881Speter
1558251881Speter  if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1559251881Speter    SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1560251881Speter
1561251881Speter  SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1562251881Speter                                    db->relpath,
1563251881Speter                                    db->left_src,
1564251881Speter                                    db->right_src,
1565251881Speter                                    NULL /* copyfrom src */,
1566251881Speter                                    pb->pdb,
1567251881Speter                                    eb->processor,
1568251881Speter                                    db->pool, db->pool));
1569251881Speter
1570251881Speter  return SVN_NO_ERROR;
1571251881Speter}
1572251881Speter
1573251881Speter/* An svn_delta_editor_t function. */
1574251881Speterstatic svn_error_t *
1575251881Speteropen_directory(const char *path,
1576251881Speter               void *parent_baton,
1577251881Speter               svn_revnum_t base_revision,
1578251881Speter               apr_pool_t *dir_pool,
1579251881Speter               void **child_baton)
1580251881Speter{
1581251881Speter  struct dir_baton_t *pb = parent_baton;
1582251881Speter  struct edit_baton_t *eb = pb->eb;
1583251881Speter  struct dir_baton_t *db;
1584251881Speter  svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
1585251881Speter                              ? svn_depth_empty : pb->depth;
1586251881Speter
1587251881Speter  /* Allocate path from the parent pool since the memory is used in the
1588251881Speter     parent's compared hash */
1589251881Speter  db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
1590251881Speter  *child_baton = db;
1591251881Speter
1592251881Speter  if (pb->repos_only)
1593251881Speter    db->repos_only = TRUE;
1594251881Speter  else
1595251881Speter    {
1596251881Speter      struct svn_wc__db_info_t *info;
1597251881Speter      SVN_ERR(ensure_local_info(pb, dir_pool));
1598251881Speter
1599251881Speter      info = svn_hash_gets(pb->local_info, db->name);
1600251881Speter
1601251881Speter      if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
1602251881Speter        db->repos_only = TRUE;
1603251881Speter
1604251881Speter      if (!db->repos_only)
1605251881Speter        switch (info->status)
1606251881Speter          {
1607251881Speter            case svn_wc__db_status_normal:
1608251881Speter              break;
1609251881Speter            case svn_wc__db_status_deleted:
1610251881Speter              db->repos_only = TRUE;
1611251881Speter
1612251881Speter              if (!info->have_more_work)
1613251881Speter                svn_hash_sets(pb->compared,
1614251881Speter                              apr_pstrdup(pb->pool, db->name), "");
1615251881Speter              break;
1616251881Speter            case svn_wc__db_status_added:
1617251881Speter              if (eb->ignore_ancestry)
1618251881Speter                db->ignoring_ancestry = TRUE;
1619251881Speter              else
1620251881Speter                db->repos_only = TRUE;
1621251881Speter              break;
1622251881Speter            default:
1623251881Speter              SVN_ERR_MALFUNCTION();
1624251881Speter        }
1625251881Speter
1626251881Speter      if (!db->repos_only)
1627251881Speter        {
1628251881Speter          db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
1629251881Speter          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
1630251881Speter        }
1631251881Speter    }
1632251881Speter
1633251881Speter  db->left_src = svn_diff__source_create(eb->revnum, db->pool);
1634251881Speter
1635251881Speter  if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1636251881Speter    SVN_ERR(handle_local_only(pb, db->name, dir_pool));
1637251881Speter
1638251881Speter  SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
1639251881Speter                                    db->relpath,
1640251881Speter                                    db->left_src,
1641251881Speter                                    db->right_src,
1642251881Speter                                    NULL /* copyfrom src */,
1643251881Speter                                    pb->pdb,
1644251881Speter                                    eb->processor,
1645251881Speter                                    db->pool, db->pool));
1646251881Speter
1647251881Speter  return SVN_NO_ERROR;
1648251881Speter}
1649251881Speter
1650251881Speter
1651251881Speter/* An svn_delta_editor_t function.  When a directory is closed, all the
1652251881Speter * directory elements that have been added or replaced will already have been
1653251881Speter * diff'd. However there may be other elements in the working copy
1654251881Speter * that have not yet been considered.  */
1655251881Speterstatic svn_error_t *
1656251881Speterclose_directory(void *dir_baton,
1657251881Speter                apr_pool_t *pool)
1658251881Speter{
1659251881Speter  struct dir_baton_t *db = dir_baton;
1660251881Speter  struct dir_baton_t *pb = db->parent_baton;
1661251881Speter  struct edit_baton_t *eb = db->eb;
1662251881Speter  apr_pool_t *scratch_pool = db->pool;
1663251881Speter  svn_boolean_t reported_closed = FALSE;
1664251881Speter
1665251881Speter  if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
1666251881Speter    {
1667251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1668251881Speter      apr_array_header_t *children;
1669251881Speter      int i;
1670251881Speter      children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
1671251881Speter                                scratch_pool);
1672251881Speter
1673251881Speter      for (i = 0; i < children->nelts; i++)
1674251881Speter        {
1675251881Speter          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
1676251881Speter                                                  svn_sort__item_t);
1677251881Speter          const char *name = item->key;
1678251881Speter
1679251881Speter          svn_pool_clear(iterpool);
1680251881Speter          SVN_ERR(handle_local_only(db, name, iterpool));
1681251881Speter
1682251881Speter          svn_hash_sets(db->compared, name, "");
1683251881Speter        }
1684251881Speter
1685251881Speter      svn_pool_destroy(iterpool);
1686251881Speter    }
1687251881Speter
1688251881Speter  /* Report local modifications for this directory.  Skip added
1689251881Speter     directories since they can only contain added elements, all of
1690251881Speter     which have already been diff'd. */
1691251881Speter  if (!db->repos_only && !db->skip_children)
1692251881Speter  {
1693251881Speter    SVN_ERR(walk_local_nodes_diff(eb,
1694251881Speter                                  db->local_abspath,
1695251881Speter                                  db->relpath,
1696251881Speter                                  db->depth,
1697251881Speter                                  db->compared,
1698251881Speter                                  db->pdb,
1699251881Speter                                  scratch_pool));
1700251881Speter  }
1701251881Speter
1702251881Speter  /* Report the property changes on the directory itself, if necessary. */
1703251881Speter  if (db->skip)
1704251881Speter    {
1705251881Speter      /* Diff processor requested no directory details */
1706251881Speter    }
1707251881Speter  else if (db->propchanges->nelts > 0 || db->repos_only)
1708251881Speter    {
1709251881Speter      apr_hash_t *repos_props;
1710251881Speter
1711251881Speter      if (db->added)
1712251881Speter        {
1713251881Speter          repos_props = apr_hash_make(scratch_pool);
1714251881Speter        }
1715251881Speter      else
1716251881Speter        {
1717251881Speter          SVN_ERR(svn_wc__db_base_get_props(&repos_props,
1718251881Speter                                            eb->db, db->local_abspath,
1719251881Speter                                            scratch_pool, scratch_pool));
1720251881Speter        }
1721251881Speter
1722251881Speter      /* Add received property changes and entry props */
1723251881Speter      if (db->propchanges->nelts)
1724251881Speter        repos_props = svn_prop__patch(repos_props, db->propchanges,
1725251881Speter                                      scratch_pool);
1726251881Speter
1727251881Speter      if (db->repos_only)
1728251881Speter        {
1729251881Speter          SVN_ERR(eb->processor->dir_deleted(db->relpath,
1730251881Speter                                             db->left_src,
1731251881Speter                                             repos_props,
1732251881Speter                                             db->pdb,
1733251881Speter                                             eb->processor,
1734251881Speter                                             scratch_pool));
1735251881Speter          reported_closed = TRUE;
1736251881Speter        }
1737251881Speter      else
1738251881Speter        {
1739251881Speter          apr_hash_t *local_props;
1740251881Speter          apr_array_header_t *prop_changes;
1741251881Speter
1742251881Speter          if (eb->diff_pristine)
1743251881Speter            SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
1744251881Speter                                                  NULL, NULL, NULL, NULL,
1745251881Speter                                                  &local_props,
1746251881Speter                                                  eb->db, db->local_abspath,
1747251881Speter                                                  scratch_pool, scratch_pool));
1748251881Speter          else
1749251881Speter            SVN_ERR(svn_wc__db_read_props(&local_props,
1750251881Speter                                          eb->db, db->local_abspath,
1751251881Speter                                          scratch_pool, scratch_pool));
1752251881Speter
1753251881Speter          SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
1754251881Speter                                 scratch_pool));
1755251881Speter
1756251881Speter          /* ### as a good diff processor we should now only report changes
1757251881Speter                 if there are non-entry changes, but for now we stick to
1758251881Speter                 compatibility */
1759251881Speter
1760251881Speter          if (prop_changes->nelts)
1761251881Speter            {
1762251881Speter              SVN_ERR(eb->processor->dir_changed(db->relpath,
1763251881Speter                                                 db->left_src,
1764251881Speter                                                 db->right_src,
1765251881Speter                                                 repos_props,
1766251881Speter                                                 local_props,
1767251881Speter                                                 prop_changes,
1768251881Speter                                                 db->pdb,
1769251881Speter                                                 eb->processor,
1770251881Speter                                                 scratch_pool));
1771251881Speter              reported_closed = TRUE;
1772251881Speter          }
1773251881Speter        }
1774251881Speter    }
1775251881Speter
1776251881Speter  /* Mark this directory as compared in the parent directory's baton,
1777251881Speter     unless this is the root of the comparison. */
1778251881Speter  if (!reported_closed && !db->skip)
1779251881Speter    SVN_ERR(eb->processor->dir_closed(db->relpath,
1780251881Speter                                      db->left_src,
1781251881Speter                                      db->right_src,
1782251881Speter                                      db->pdb,
1783251881Speter                                      eb->processor,
1784251881Speter                                      scratch_pool));
1785251881Speter
1786251881Speter  if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
1787251881Speter    SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
1788251881Speter
1789251881Speter  SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
1790251881Speter
1791251881Speter  return SVN_NO_ERROR;
1792251881Speter}
1793251881Speter
1794251881Speter/* An svn_delta_editor_t function. */
1795251881Speterstatic svn_error_t *
1796251881Speteradd_file(const char *path,
1797251881Speter         void *parent_baton,
1798251881Speter         const char *copyfrom_path,
1799251881Speter         svn_revnum_t copyfrom_revision,
1800251881Speter         apr_pool_t *file_pool,
1801251881Speter         void **file_baton)
1802251881Speter{
1803251881Speter  struct dir_baton_t *pb = parent_baton;
1804251881Speter  struct edit_baton_t *eb = pb->eb;
1805251881Speter  struct file_baton_t *fb;
1806251881Speter
1807251881Speter  fb = make_file_baton(path, TRUE, pb, file_pool);
1808251881Speter  *file_baton = fb;
1809251881Speter
1810251881Speter  if (pb->skip_children)
1811251881Speter    {
1812251881Speter      fb->skip = TRUE;
1813251881Speter      return SVN_NO_ERROR;
1814251881Speter    }
1815251881Speter  else if (pb->repos_only || !eb->ignore_ancestry)
1816251881Speter    fb->repos_only = TRUE;
1817251881Speter  else
1818251881Speter    {
1819251881Speter      struct svn_wc__db_info_t *info;
1820251881Speter      SVN_ERR(ensure_local_info(pb, file_pool));
1821251881Speter
1822251881Speter      info = svn_hash_gets(pb->local_info, fb->name);
1823251881Speter
1824251881Speter      if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1825251881Speter        fb->repos_only = TRUE;
1826251881Speter
1827251881Speter      if (!fb->repos_only && info->status != svn_wc__db_status_added)
1828251881Speter        fb->repos_only = TRUE;
1829251881Speter
1830251881Speter      if (!fb->repos_only)
1831251881Speter        {
1832251881Speter          /* Add this path to the parent directory's list of elements that
1833251881Speter             have been compared. */
1834251881Speter          fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1835251881Speter          fb->ignoring_ancestry = TRUE;
1836251881Speter
1837251881Speter          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1838251881Speter        }
1839251881Speter    }
1840251881Speter
1841251881Speter  fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1842251881Speter
1843251881Speter  SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1844251881Speter                                     fb->relpath,
1845251881Speter                                     fb->left_src,
1846251881Speter                                     fb->right_src,
1847251881Speter                                     NULL /* copyfrom src */,
1848251881Speter                                     pb->pdb,
1849251881Speter                                     eb->processor,
1850251881Speter                                     fb->pool, fb->pool));
1851251881Speter
1852251881Speter  return SVN_NO_ERROR;
1853251881Speter}
1854251881Speter
1855251881Speter/* An svn_delta_editor_t function. */
1856251881Speterstatic svn_error_t *
1857251881Speteropen_file(const char *path,
1858251881Speter          void *parent_baton,
1859251881Speter          svn_revnum_t base_revision,
1860251881Speter          apr_pool_t *file_pool,
1861251881Speter          void **file_baton)
1862251881Speter{
1863251881Speter  struct dir_baton_t *pb = parent_baton;
1864251881Speter  struct edit_baton_t *eb = pb->eb;
1865251881Speter  struct file_baton_t *fb;
1866251881Speter
1867251881Speter  fb = make_file_baton(path, FALSE, pb, file_pool);
1868251881Speter  *file_baton = fb;
1869251881Speter
1870251881Speter  if (pb->skip_children)
1871251881Speter    fb->skip = TRUE;
1872251881Speter  else if (pb->repos_only)
1873251881Speter    fb->repos_only = TRUE;
1874251881Speter  else
1875251881Speter    {
1876251881Speter      struct svn_wc__db_info_t *info;
1877251881Speter      SVN_ERR(ensure_local_info(pb, file_pool));
1878251881Speter
1879251881Speter      info = svn_hash_gets(pb->local_info, fb->name);
1880251881Speter
1881251881Speter      if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
1882251881Speter        fb->repos_only = TRUE;
1883251881Speter
1884251881Speter      if (!fb->repos_only)
1885251881Speter        switch (info->status)
1886251881Speter          {
1887251881Speter            case svn_wc__db_status_normal:
1888251881Speter              break;
1889251881Speter            case svn_wc__db_status_deleted:
1890251881Speter              fb->repos_only = TRUE;
1891251881Speter              if (!info->have_more_work)
1892251881Speter                svn_hash_sets(pb->compared,
1893251881Speter                              apr_pstrdup(pb->pool, fb->name), "");
1894251881Speter              break;
1895251881Speter            case svn_wc__db_status_added:
1896251881Speter              if (eb->ignore_ancestry)
1897251881Speter                fb->ignoring_ancestry = TRUE;
1898251881Speter              else
1899251881Speter                fb->repos_only = TRUE;
1900251881Speter              break;
1901251881Speter            default:
1902251881Speter              SVN_ERR_MALFUNCTION();
1903251881Speter        }
1904251881Speter
1905251881Speter      if (!fb->repos_only)
1906251881Speter        {
1907251881Speter          /* Add this path to the parent directory's list of elements that
1908251881Speter             have been compared. */
1909251881Speter          fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
1910251881Speter          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
1911251881Speter        }
1912251881Speter    }
1913251881Speter
1914251881Speter  fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
1915251881Speter
1916251881Speter  SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1917251881Speter                                   NULL, NULL, NULL, &fb->base_checksum, NULL,
1918251881Speter                                   NULL, NULL, &fb->base_props, NULL,
1919251881Speter                                   eb->db, fb->local_abspath,
1920251881Speter                                   fb->pool, fb->pool));
1921251881Speter
1922251881Speter  SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
1923251881Speter                                     fb->relpath,
1924251881Speter                                     fb->left_src,
1925251881Speter                                     fb->right_src,
1926251881Speter                                     NULL /* copyfrom src */,
1927251881Speter                                     pb->pdb,
1928251881Speter                                     eb->processor,
1929251881Speter                                     fb->pool, fb->pool));
1930251881Speter
1931251881Speter  return SVN_NO_ERROR;
1932251881Speter}
1933251881Speter
1934251881Speter/* An svn_delta_editor_t function. */
1935251881Speterstatic svn_error_t *
1936251881Speterapply_textdelta(void *file_baton,
1937251881Speter                const char *base_checksum_hex,
1938251881Speter                apr_pool_t *pool,
1939251881Speter                svn_txdelta_window_handler_t *handler,
1940251881Speter                void **handler_baton)
1941251881Speter{
1942251881Speter  struct file_baton_t *fb = file_baton;
1943251881Speter  struct edit_baton_t *eb = fb->eb;
1944251881Speter  svn_stream_t *source;
1945251881Speter  svn_stream_t *temp_stream;
1946251881Speter  svn_checksum_t *repos_checksum = NULL;
1947251881Speter
1948251881Speter  if (fb->skip)
1949251881Speter    {
1950251881Speter      *handler = svn_delta_noop_window_handler;
1951251881Speter      *handler_baton = NULL;
1952251881Speter      return SVN_NO_ERROR;
1953251881Speter    }
1954251881Speter
1955251881Speter  if (base_checksum_hex && fb->base_checksum)
1956251881Speter    {
1957251881Speter      const svn_checksum_t *base_md5;
1958251881Speter      SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
1959251881Speter                                     base_checksum_hex, pool));
1960251881Speter
1961251881Speter      SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
1962251881Speter                                          eb->db, eb->anchor_abspath,
1963251881Speter                                          fb->base_checksum,
1964251881Speter                                          pool, pool));
1965251881Speter
1966251881Speter      if (! svn_checksum_match(repos_checksum, base_md5))
1967251881Speter        {
1968251881Speter          /* ### I expect that there are some bad drivers out there
1969251881Speter             ### that used to give bad results. We could look in
1970251881Speter             ### working to see if the expected checksum matches and
1971251881Speter             ### then return the pristine of that... But that only moves
1972251881Speter             ### the problem */
1973251881Speter
1974251881Speter          /* If needed: compare checksum obtained via md5 of working.
1975251881Speter             And if they match set fb->base_checksum and fb->base_props */
1976251881Speter
1977251881Speter          return svn_checksum_mismatch_err(
1978251881Speter                        base_md5,
1979251881Speter                        repos_checksum,
1980251881Speter                        pool,
1981251881Speter                        _("Checksum mismatch for '%s'"),
1982251881Speter                        svn_dirent_local_style(fb->local_abspath,
1983251881Speter                                               pool));
1984251881Speter        }
1985251881Speter
1986251881Speter      SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1987251881Speter                                       eb->db, fb->local_abspath,
1988251881Speter                                       fb->base_checksum,
1989251881Speter                                       pool, pool));
1990251881Speter    }
1991251881Speter  else if (fb->base_checksum)
1992251881Speter    {
1993251881Speter      SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
1994251881Speter                                       eb->db, fb->local_abspath,
1995251881Speter                                       fb->base_checksum,
1996251881Speter                                       pool, pool));
1997251881Speter    }
1998251881Speter  else
1999251881Speter    source = svn_stream_empty(pool);
2000251881Speter
2001251881Speter  /* This is the file that will contain the pristine repository version. */
2002251881Speter  SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
2003251881Speter                                 svn_io_file_del_on_pool_cleanup,
2004251881Speter                                 fb->pool, fb->pool));
2005251881Speter
2006251881Speter  svn_txdelta_apply(source, temp_stream,
2007251881Speter                    fb->result_digest,
2008251881Speter                    fb->local_abspath /* error_info */,
2009251881Speter                    fb->pool,
2010251881Speter                    handler, handler_baton);
2011251881Speter
2012251881Speter  return SVN_NO_ERROR;
2013251881Speter}
2014251881Speter
2015251881Speter/* An svn_delta_editor_t function.  When the file is closed we have a temporary
2016251881Speter * file containing a pristine version of the repository file. This can
2017251881Speter * be compared against the working copy.
2018251881Speter *
2019251881Speter * Ignore TEXT_CHECKSUM.
2020251881Speter */
2021251881Speterstatic svn_error_t *
2022251881Speterclose_file(void *file_baton,
2023251881Speter           const char *expected_md5_digest,
2024251881Speter           apr_pool_t *pool)
2025251881Speter{
2026251881Speter  struct file_baton_t *fb = file_baton;
2027251881Speter  struct dir_baton_t *pb = fb->parent_baton;
2028251881Speter  struct edit_baton_t *eb = fb->eb;
2029251881Speter  apr_pool_t *scratch_pool = fb->pool;
2030251881Speter
2031251881Speter  /* The repository information; constructed from BASE + Changes */
2032251881Speter  const char *repos_file;
2033251881Speter  apr_hash_t *repos_props;
2034251881Speter
2035251881Speter  if (!fb->skip && expected_md5_digest != NULL)
2036251881Speter    {
2037251881Speter      svn_checksum_t *expected_checksum;
2038251881Speter      const svn_checksum_t *result_checksum;
2039251881Speter
2040251881Speter      if (fb->temp_file_path)
2041251881Speter        result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
2042251881Speter                                                        scratch_pool);
2043251881Speter      else
2044251881Speter        result_checksum = fb->base_checksum;
2045251881Speter
2046251881Speter      SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
2047251881Speter                                     expected_md5_digest, scratch_pool));
2048251881Speter
2049251881Speter      if (result_checksum->kind != svn_checksum_md5)
2050251881Speter        SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
2051251881Speter                                            eb->db, fb->local_abspath,
2052251881Speter                                            result_checksum,
2053251881Speter                                            scratch_pool, scratch_pool));
2054251881Speter
2055251881Speter      if (!svn_checksum_match(expected_checksum, result_checksum))
2056251881Speter        return svn_checksum_mismatch_err(
2057251881Speter                            expected_checksum,
2058251881Speter                            result_checksum,
2059251881Speter                            pool,
2060251881Speter                            _("Checksum mismatch for '%s'"),
2061251881Speter                            svn_dirent_local_style(fb->local_abspath,
2062251881Speter                                                   scratch_pool));
2063251881Speter    }
2064251881Speter
2065251881Speter  if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2066251881Speter    SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2067251881Speter
2068251881Speter  {
2069251881Speter    apr_hash_t *prop_base;
2070251881Speter
2071251881Speter    if (fb->added)
2072251881Speter      prop_base = apr_hash_make(scratch_pool);
2073251881Speter    else
2074251881Speter      prop_base = fb->base_props;
2075251881Speter
2076251881Speter    /* includes entry props */
2077251881Speter    repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
2078251881Speter
2079251881Speter    repos_file = fb->temp_file_path;
2080251881Speter    if (! repos_file)
2081251881Speter      {
2082251881Speter        assert(fb->base_checksum);
2083251881Speter        SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
2084251881Speter                                             eb->db, eb->anchor_abspath,
2085251881Speter                                             fb->base_checksum,
2086251881Speter                                             scratch_pool, scratch_pool));
2087251881Speter      }
2088251881Speter  }
2089251881Speter
2090251881Speter  if (fb->skip)
2091251881Speter    {
2092251881Speter      /* Diff processor requested skipping information */
2093251881Speter    }
2094251881Speter  else if (fb->repos_only)
2095251881Speter    {
2096251881Speter      SVN_ERR(eb->processor->file_deleted(fb->relpath,
2097251881Speter                                          fb->left_src,
2098251881Speter                                          fb->temp_file_path,
2099251881Speter                                          repos_props,
2100251881Speter                                          fb->pfb,
2101251881Speter                                          eb->processor,
2102251881Speter                                          scratch_pool));
2103251881Speter    }
2104251881Speter  else
2105251881Speter    {
2106251881Speter      /* Produce a diff of actual or pristine against repos */
2107251881Speter      apr_hash_t *local_props;
2108251881Speter      apr_array_header_t *prop_changes;
2109251881Speter      const char *localfile;
2110251881Speter
2111251881Speter      /* pb->local_info contains some information that might allow optimizing
2112251881Speter         this a bit */
2113251881Speter
2114251881Speter      if (eb->diff_pristine)
2115251881Speter        {
2116251881Speter          const svn_checksum_t *checksum;
2117251881Speter          SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
2118251881Speter                                                NULL, &checksum, NULL, NULL,
2119251881Speter                                                &local_props,
2120251881Speter                                                eb->db, fb->local_abspath,
2121251881Speter                                                scratch_pool, scratch_pool));
2122251881Speter          assert(checksum);
2123251881Speter          SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
2124251881Speter                                               eb->db, eb->anchor_abspath,
2125251881Speter                                               checksum,
2126251881Speter                                               scratch_pool, scratch_pool));
2127251881Speter        }
2128251881Speter      else
2129251881Speter        {
2130251881Speter          SVN_ERR(svn_wc__db_read_props(&local_props,
2131251881Speter                                        eb->db, fb->local_abspath,
2132251881Speter                                        scratch_pool, scratch_pool));
2133251881Speter
2134251881Speter          /* a detranslated version of the working file */
2135251881Speter          SVN_ERR(svn_wc__internal_translated_file(
2136251881Speter                    &localfile, fb->local_abspath, eb->db, fb->local_abspath,
2137251881Speter                    SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
2138251881Speter                    eb->cancel_func, eb->cancel_baton,
2139251881Speter                    scratch_pool, scratch_pool));
2140251881Speter        }
2141251881Speter
2142251881Speter      SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
2143251881Speter                             scratch_pool));
2144251881Speter
2145251881Speter
2146251881Speter      /* ### as a good diff processor we should now only report changes, and
2147251881Speter             report file_closed() in other cases */
2148251881Speter      SVN_ERR(eb->processor->file_changed(fb->relpath,
2149251881Speter                                          fb->left_src,
2150251881Speter                                          fb->right_src,
2151251881Speter                                          repos_file /* left file */,
2152251881Speter                                          localfile /* right file */,
2153251881Speter                                          repos_props /* left_props */,
2154251881Speter                                          local_props /* right props */,
2155251881Speter                                          TRUE /* ### file_modified */,
2156251881Speter                                          prop_changes,
2157251881Speter                                          fb->pfb,
2158251881Speter                                          eb->processor,
2159251881Speter                                          scratch_pool));
2160251881Speter    }
2161251881Speter
2162251881Speter  if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
2163251881Speter    SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
2164251881Speter
2165251881Speter  svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
2166251881Speter  SVN_ERR(maybe_done(pb));
2167251881Speter  return SVN_NO_ERROR;
2168251881Speter}
2169251881Speter
2170251881Speter
2171251881Speter/* An svn_delta_editor_t function. */
2172251881Speterstatic svn_error_t *
2173251881Speterchange_file_prop(void *file_baton,
2174251881Speter                 const char *name,
2175251881Speter                 const svn_string_t *value,
2176251881Speter                 apr_pool_t *pool)
2177251881Speter{
2178251881Speter  struct file_baton_t *fb = file_baton;
2179251881Speter  svn_prop_t *propchange;
2180251881Speter  svn_prop_kind_t propkind;
2181251881Speter
2182251881Speter  propkind = svn_property_kind2(name);
2183251881Speter  if (propkind == svn_prop_wc_kind)
2184251881Speter    return SVN_NO_ERROR;
2185251881Speter  else if (propkind == svn_prop_regular_kind)
2186251881Speter    fb->has_propchange = TRUE;
2187251881Speter
2188251881Speter  propchange = apr_array_push(fb->propchanges);
2189251881Speter  propchange->name = apr_pstrdup(fb->pool, name);
2190251881Speter  propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
2191251881Speter
2192251881Speter  return SVN_NO_ERROR;
2193251881Speter}
2194251881Speter
2195251881Speter
2196251881Speter/* An svn_delta_editor_t function. */
2197251881Speterstatic svn_error_t *
2198251881Speterchange_dir_prop(void *dir_baton,
2199251881Speter                const char *name,
2200251881Speter                const svn_string_t *value,
2201251881Speter                apr_pool_t *pool)
2202251881Speter{
2203251881Speter  struct dir_baton_t *db = dir_baton;
2204251881Speter  svn_prop_t *propchange;
2205251881Speter  svn_prop_kind_t propkind;
2206251881Speter
2207251881Speter  propkind = svn_property_kind2(name);
2208251881Speter  if (propkind == svn_prop_wc_kind)
2209251881Speter    return SVN_NO_ERROR;
2210251881Speter  else if (propkind == svn_prop_regular_kind)
2211251881Speter    db->has_propchange = TRUE;
2212251881Speter
2213251881Speter  propchange = apr_array_push(db->propchanges);
2214251881Speter  propchange->name = apr_pstrdup(db->pool, name);
2215251881Speter  propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2216251881Speter
2217251881Speter  return SVN_NO_ERROR;
2218251881Speter}
2219251881Speter
2220251881Speter
2221251881Speter/* An svn_delta_editor_t function. */
2222251881Speterstatic svn_error_t *
2223251881Speterclose_edit(void *edit_baton,
2224251881Speter           apr_pool_t *pool)
2225251881Speter{
2226251881Speter  struct edit_baton_t *eb = edit_baton;
2227251881Speter
2228251881Speter  if (!eb->root_opened)
2229251881Speter    {
2230251881Speter      SVN_ERR(walk_local_nodes_diff(eb,
2231251881Speter                                    eb->anchor_abspath,
2232251881Speter                                    "",
2233251881Speter                                    eb->depth,
2234251881Speter                                    NULL /* compared */,
2235251881Speter                                    NULL /* No parent_baton */,
2236251881Speter                                    eb->pool));
2237251881Speter    }
2238251881Speter
2239251881Speter  return SVN_NO_ERROR;
2240251881Speter}
2241251881Speter
2242251881Speter/* Public Interface */
2243251881Speter
2244251881Speter
2245251881Speter/* Create a diff editor and baton. */
2246251881Spetersvn_error_t *
2247251881Spetersvn_wc__get_diff_editor(const svn_delta_editor_t **editor,
2248251881Speter                        void **edit_baton,
2249251881Speter                        svn_wc_context_t *wc_ctx,
2250251881Speter                        const char *anchor_abspath,
2251251881Speter                        const char *target,
2252251881Speter                        svn_depth_t depth,
2253251881Speter                        svn_boolean_t ignore_ancestry,
2254251881Speter                        svn_boolean_t show_copies_as_adds,
2255251881Speter                        svn_boolean_t use_git_diff_format,
2256251881Speter                        svn_boolean_t use_text_base,
2257251881Speter                        svn_boolean_t reverse_order,
2258251881Speter                        svn_boolean_t server_performs_filtering,
2259251881Speter                        const apr_array_header_t *changelist_filter,
2260251881Speter                        const svn_wc_diff_callbacks4_t *callbacks,
2261251881Speter                        void *callback_baton,
2262251881Speter                        svn_cancel_func_t cancel_func,
2263251881Speter                        void *cancel_baton,
2264251881Speter                        apr_pool_t *result_pool,
2265251881Speter                        apr_pool_t *scratch_pool)
2266251881Speter{
2267251881Speter  struct edit_baton_t *eb;
2268251881Speter  void *inner_baton;
2269251881Speter  svn_delta_editor_t *tree_editor;
2270251881Speter  const svn_delta_editor_t *inner_editor;
2271251881Speter  struct svn_wc__shim_fetch_baton_t *sfb;
2272251881Speter  svn_delta_shim_callbacks_t *shim_callbacks =
2273251881Speter                                svn_delta_shim_callbacks_default(result_pool);
2274251881Speter
2275251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
2276251881Speter
2277251881Speter  /* --git implies --show-copies-as-adds */
2278251881Speter  if (use_git_diff_format)
2279251881Speter    show_copies_as_adds = TRUE;
2280251881Speter
2281251881Speter  SVN_ERR(make_edit_baton(&eb,
2282251881Speter                          wc_ctx->db,
2283251881Speter                          anchor_abspath, target,
2284251881Speter                          callbacks, callback_baton,
2285251881Speter                          depth, ignore_ancestry, show_copies_as_adds,
2286251881Speter                          use_text_base, reverse_order, changelist_filter,
2287251881Speter                          cancel_func, cancel_baton,
2288251881Speter                          result_pool));
2289251881Speter
2290251881Speter  tree_editor = svn_delta_default_editor(eb->pool);
2291251881Speter
2292251881Speter  tree_editor->set_target_revision = set_target_revision;
2293251881Speter  tree_editor->open_root = open_root;
2294251881Speter  tree_editor->delete_entry = delete_entry;
2295251881Speter  tree_editor->add_directory = add_directory;
2296251881Speter  tree_editor->open_directory = open_directory;
2297251881Speter  tree_editor->close_directory = close_directory;
2298251881Speter  tree_editor->add_file = add_file;
2299251881Speter  tree_editor->open_file = open_file;
2300251881Speter  tree_editor->apply_textdelta = apply_textdelta;
2301251881Speter  tree_editor->change_file_prop = change_file_prop;
2302251881Speter  tree_editor->change_dir_prop = change_dir_prop;
2303251881Speter  tree_editor->close_file = close_file;
2304251881Speter  tree_editor->close_edit = close_edit;
2305251881Speter
2306251881Speter  inner_editor = tree_editor;
2307251881Speter  inner_baton = eb;
2308251881Speter
2309251881Speter  if (!server_performs_filtering
2310251881Speter      && depth == svn_depth_unknown)
2311251881Speter    SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2312251881Speter                                                &inner_baton,
2313251881Speter                                                wc_ctx->db,
2314251881Speter                                                anchor_abspath,
2315251881Speter                                                target,
2316251881Speter                                                inner_editor,
2317251881Speter                                                inner_baton,
2318251881Speter                                                result_pool));
2319251881Speter
2320251881Speter  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
2321251881Speter                                            cancel_baton,
2322251881Speter                                            inner_editor,
2323251881Speter                                            inner_baton,
2324251881Speter                                            editor,
2325251881Speter                                            edit_baton,
2326251881Speter                                            result_pool));
2327251881Speter
2328251881Speter  sfb = apr_palloc(result_pool, sizeof(*sfb));
2329251881Speter  sfb->db = wc_ctx->db;
2330251881Speter  sfb->base_abspath = eb->anchor_abspath;
2331251881Speter  sfb->fetch_base = TRUE;
2332251881Speter
2333251881Speter  shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2334251881Speter  shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2335251881Speter  shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2336251881Speter  shim_callbacks->fetch_baton = sfb;
2337251881Speter
2338251881Speter
2339251881Speter  SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2340251881Speter                                   NULL, NULL, shim_callbacks,
2341251881Speter                                   result_pool, scratch_pool));
2342251881Speter
2343251881Speter  return SVN_NO_ERROR;
2344251881Speter}
2345251881Speter
2346251881Speter/* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
2347251881Speter
2348251881Speter/* baton for the svn_diff_tree_processor_t wrapper */
2349251881Spetertypedef struct wc_diff_wrap_baton_t
2350251881Speter{
2351251881Speter  const svn_wc_diff_callbacks4_t *callbacks;
2352251881Speter  void *callback_baton;
2353251881Speter
2354251881Speter  svn_boolean_t walk_deleted_dirs;
2355251881Speter
2356251881Speter  apr_pool_t *result_pool;
2357251881Speter  const char *empty_file;
2358251881Speter
2359251881Speter} wc_diff_wrap_baton_t;
2360251881Speter
2361251881Speterstatic svn_error_t *
2362251881Speterwrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
2363251881Speter                       apr_pool_t *scratch_pool)
2364251881Speter{
2365251881Speter  if (wb->empty_file)
2366251881Speter    return SVN_NO_ERROR;
2367251881Speter
2368251881Speter  /* Create a unique file in the tempdir */
2369251881Speter  SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
2370251881Speter                                   svn_io_file_del_on_pool_cleanup,
2371251881Speter                                   wb->result_pool, scratch_pool));
2372251881Speter
2373251881Speter  return SVN_NO_ERROR;
2374251881Speter}
2375251881Speter
2376251881Speter/* svn_diff_tree_processor_t function */
2377251881Speterstatic svn_error_t *
2378251881Speterwrap_dir_opened(void **new_dir_baton,
2379251881Speter                svn_boolean_t *skip,
2380251881Speter                svn_boolean_t *skip_children,
2381251881Speter                const char *relpath,
2382251881Speter                const svn_diff_source_t *left_source,
2383251881Speter                const svn_diff_source_t *right_source,
2384251881Speter                const svn_diff_source_t *copyfrom_source,
2385251881Speter                void *parent_dir_baton,
2386251881Speter                const svn_diff_tree_processor_t *processor,
2387251881Speter                apr_pool_t *result_pool,
2388251881Speter                apr_pool_t *scratch_pool)
2389251881Speter{
2390251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2391251881Speter  svn_boolean_t tree_conflicted = FALSE;
2392251881Speter
2393251881Speter  assert(left_source || right_source);
2394251881Speter  assert(!copyfrom_source || !right_source);
2395251881Speter
2396251881Speter  /* Maybe store state and tree_conflicted in baton? */
2397251881Speter  if (left_source != NULL)
2398251881Speter    {
2399251881Speter      /* Open for change or delete */
2400251881Speter      SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
2401251881Speter                                        relpath,
2402251881Speter                                        right_source
2403251881Speter                                            ? right_source->revision
2404251881Speter                                            : (left_source
2405251881Speter                                                    ? left_source->revision
2406251881Speter                                                    : SVN_INVALID_REVNUM),
2407251881Speter                                        wb->callback_baton,
2408251881Speter                                        scratch_pool));
2409251881Speter
2410251881Speter      if (! right_source && !wb->walk_deleted_dirs)
2411251881Speter        *skip_children = TRUE;
2412251881Speter    }
2413251881Speter  else /* left_source == NULL -> Add */
2414251881Speter    {
2415251881Speter      svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2416251881Speter      SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
2417251881Speter                                       skip, skip_children,
2418251881Speter                                       relpath,
2419251881Speter                                       right_source->revision,
2420251881Speter                                       copyfrom_source
2421251881Speter                                            ? copyfrom_source->repos_relpath
2422251881Speter                                            : NULL,
2423251881Speter                                       copyfrom_source
2424251881Speter                                            ? copyfrom_source->revision
2425251881Speter                                            : SVN_INVALID_REVNUM,
2426251881Speter                                       wb->callback_baton,
2427251881Speter                                       scratch_pool));
2428251881Speter    }
2429251881Speter
2430251881Speter  *new_dir_baton = NULL;
2431251881Speter
2432251881Speter  return SVN_NO_ERROR;
2433251881Speter}
2434251881Speter
2435251881Speter/* svn_diff_tree_processor_t function */
2436251881Speterstatic svn_error_t *
2437251881Speterwrap_dir_added(const char *relpath,
2438251881Speter               const svn_diff_source_t *right_source,
2439251881Speter               const svn_diff_source_t *copyfrom_source,
2440251881Speter               /*const*/ apr_hash_t *copyfrom_props,
2441251881Speter               /*const*/ apr_hash_t *right_props,
2442251881Speter               void *dir_baton,
2443251881Speter               const svn_diff_tree_processor_t *processor,
2444251881Speter               apr_pool_t *scratch_pool)
2445251881Speter{
2446251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2447251881Speter  svn_boolean_t tree_conflicted = FALSE;
2448251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
2449251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2450251881Speter  apr_hash_t *pristine_props = copyfrom_props;
2451251881Speter  apr_array_header_t *prop_changes = NULL;
2452251881Speter
2453251881Speter  if (right_props && apr_hash_count(right_props))
2454251881Speter    {
2455251881Speter      if (!pristine_props)
2456251881Speter        pristine_props = apr_hash_make(scratch_pool);
2457251881Speter
2458251881Speter      SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
2459251881Speter                             scratch_pool));
2460251881Speter
2461251881Speter      SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
2462251881Speter                                               &tree_conflicted,
2463251881Speter                                               relpath,
2464251881Speter                                               TRUE /* dir_was_added */,
2465251881Speter                                               prop_changes, pristine_props,
2466251881Speter                                               wb->callback_baton,
2467251881Speter                                               scratch_pool));
2468251881Speter    }
2469251881Speter
2470251881Speter  SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
2471251881Speter                                   &tree_conflicted,
2472251881Speter                                   relpath,
2473251881Speter                                   TRUE /* dir_was_added */,
2474251881Speter                                   wb->callback_baton,
2475251881Speter                                   scratch_pool));
2476251881Speter  return SVN_NO_ERROR;
2477251881Speter}
2478251881Speter
2479251881Speter/* svn_diff_tree_processor_t function */
2480251881Speterstatic svn_error_t *
2481251881Speterwrap_dir_deleted(const char *relpath,
2482251881Speter                 const svn_diff_source_t *left_source,
2483251881Speter                 /*const*/ apr_hash_t *left_props,
2484251881Speter                 void *dir_baton,
2485251881Speter                 const svn_diff_tree_processor_t *processor,
2486251881Speter                 apr_pool_t *scratch_pool)
2487251881Speter{
2488251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2489251881Speter  svn_boolean_t tree_conflicted = FALSE;
2490251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2491251881Speter
2492251881Speter  SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
2493251881Speter                                     relpath,
2494251881Speter                                     wb->callback_baton,
2495251881Speter                                     scratch_pool));
2496251881Speter
2497251881Speter  return SVN_NO_ERROR;
2498251881Speter}
2499251881Speter
2500251881Speter/* svn_diff_tree_processor_t function */
2501251881Speterstatic svn_error_t *
2502251881Speterwrap_dir_closed(const char *relpath,
2503251881Speter                const svn_diff_source_t *left_source,
2504251881Speter                const svn_diff_source_t *right_source,
2505251881Speter                void *dir_baton,
2506251881Speter                const svn_diff_tree_processor_t *processor,
2507251881Speter                apr_pool_t *scratch_pool)
2508251881Speter{
2509251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2510251881Speter
2511251881Speter  /* No previous implementations provided these arguments, so we
2512251881Speter     are not providing them either */
2513251881Speter  SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
2514251881Speter                                    relpath,
2515251881Speter                                    FALSE /* added */,
2516251881Speter                                    wb->callback_baton,
2517251881Speter                                    scratch_pool));
2518251881Speter
2519251881Speterreturn SVN_NO_ERROR;
2520251881Speter}
2521251881Speter
2522251881Speter/* svn_diff_tree_processor_t function */
2523251881Speterstatic svn_error_t *
2524251881Speterwrap_dir_changed(const char *relpath,
2525251881Speter                 const svn_diff_source_t *left_source,
2526251881Speter                 const svn_diff_source_t *right_source,
2527251881Speter                 /*const*/ apr_hash_t *left_props,
2528251881Speter                 /*const*/ apr_hash_t *right_props,
2529251881Speter                 const apr_array_header_t *prop_changes,
2530251881Speter                 void *dir_baton,
2531251881Speter                 const struct svn_diff_tree_processor_t *processor,
2532251881Speter                 apr_pool_t *scratch_pool)
2533251881Speter{
2534251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2535251881Speter  svn_boolean_t tree_conflicted = FALSE;
2536251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2537251881Speter
2538251881Speter  assert(left_source && right_source);
2539251881Speter
2540251881Speter  SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
2541251881Speter                                           relpath,
2542251881Speter                                           FALSE /* dir_was_added */,
2543251881Speter                                           prop_changes,
2544251881Speter                                           left_props,
2545251881Speter                                           wb->callback_baton,
2546251881Speter                                           scratch_pool));
2547251881Speter
2548251881Speter  /* And call dir_closed, etc */
2549251881Speter  SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
2550251881Speter                          dir_baton, processor,
2551251881Speter                          scratch_pool));
2552251881Speter  return SVN_NO_ERROR;
2553251881Speter}
2554251881Speter
2555251881Speter/* svn_diff_tree_processor_t function */
2556251881Speterstatic svn_error_t *
2557251881Speterwrap_file_opened(void **new_file_baton,
2558251881Speter                 svn_boolean_t *skip,
2559251881Speter                 const char *relpath,
2560251881Speter                 const svn_diff_source_t *left_source,
2561251881Speter                 const svn_diff_source_t *right_source,
2562251881Speter                 const svn_diff_source_t *copyfrom_source,
2563251881Speter                 void *dir_baton,
2564251881Speter                 const svn_diff_tree_processor_t *processor,
2565251881Speter                 apr_pool_t *result_pool,
2566251881Speter                 apr_pool_t *scratch_pool)
2567251881Speter{
2568251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2569251881Speter  svn_boolean_t tree_conflicted = FALSE;
2570251881Speter
2571251881Speter  if (left_source) /* If ! added */
2572251881Speter    SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
2573251881Speter                                       right_source
2574251881Speter                                            ? right_source->revision
2575251881Speter                                            : (left_source
2576251881Speter                                                    ? left_source->revision
2577251881Speter                                                    : SVN_INVALID_REVNUM),
2578251881Speter                                       wb->callback_baton, scratch_pool));
2579251881Speter
2580251881Speter  /* No old implementation used the output arguments for notify */
2581251881Speter
2582251881Speter  *new_file_baton = NULL;
2583251881Speter  return SVN_NO_ERROR;
2584251881Speter}
2585251881Speter
2586251881Speter/* svn_diff_tree_processor_t function */
2587251881Speterstatic svn_error_t *
2588251881Speterwrap_file_added(const char *relpath,
2589251881Speter                const svn_diff_source_t *copyfrom_source,
2590251881Speter                const svn_diff_source_t *right_source,
2591251881Speter                const char *copyfrom_file,
2592251881Speter                const char *right_file,
2593251881Speter                /*const*/ apr_hash_t *copyfrom_props,
2594251881Speter                /*const*/ apr_hash_t *right_props,
2595251881Speter                void *file_baton,
2596251881Speter                const svn_diff_tree_processor_t *processor,
2597251881Speter                apr_pool_t *scratch_pool)
2598251881Speter{
2599251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2600251881Speter  svn_boolean_t tree_conflicted = FALSE;
2601251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2602251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2603251881Speter  apr_array_header_t *prop_changes;
2604251881Speter
2605251881Speter  if (! copyfrom_props)
2606251881Speter    copyfrom_props = apr_hash_make(scratch_pool);
2607251881Speter
2608251881Speter  SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
2609251881Speter                         scratch_pool));
2610251881Speter
2611251881Speter  if (! copyfrom_source)
2612251881Speter    SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2613251881Speter
2614251881Speter  SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
2615251881Speter                                    relpath,
2616251881Speter                                    copyfrom_source
2617251881Speter                                        ? copyfrom_file
2618251881Speter                                        : wb->empty_file,
2619251881Speter                                    right_file,
2620251881Speter                                    0,
2621251881Speter                                    right_source->revision,
2622251881Speter                                    copyfrom_props
2623251881Speter                                     ? svn_prop_get_value(copyfrom_props,
2624251881Speter                                                          SVN_PROP_MIME_TYPE)
2625251881Speter                                     : NULL,
2626251881Speter                                    right_props
2627251881Speter                                     ? svn_prop_get_value(right_props,
2628251881Speter                                                          SVN_PROP_MIME_TYPE)
2629251881Speter                                     : NULL,
2630251881Speter                                    copyfrom_source
2631251881Speter                                            ? copyfrom_source->repos_relpath
2632251881Speter                                            : NULL,
2633251881Speter                                    copyfrom_source
2634251881Speter                                            ? copyfrom_source->revision
2635251881Speter                                            : SVN_INVALID_REVNUM,
2636251881Speter                                    prop_changes, copyfrom_props,
2637251881Speter                                    wb->callback_baton,
2638251881Speter                                    scratch_pool));
2639251881Speter  return SVN_NO_ERROR;
2640251881Speter}
2641251881Speter
2642251881Speterstatic svn_error_t *
2643251881Speterwrap_file_deleted(const char *relpath,
2644251881Speter                  const svn_diff_source_t *left_source,
2645251881Speter                  const char *left_file,
2646251881Speter                  apr_hash_t *left_props,
2647251881Speter                  void *file_baton,
2648251881Speter                  const svn_diff_tree_processor_t *processor,
2649251881Speter                  apr_pool_t *scratch_pool)
2650251881Speter{
2651251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2652251881Speter  svn_boolean_t tree_conflicted = FALSE;
2653251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2654251881Speter
2655251881Speter  SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2656251881Speter
2657251881Speter  SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
2658251881Speter                                      relpath,
2659251881Speter                                      left_file, wb->empty_file,
2660251881Speter                                      left_props
2661251881Speter                                       ? svn_prop_get_value(left_props,
2662251881Speter                                                            SVN_PROP_MIME_TYPE)
2663251881Speter                                       : NULL,
2664251881Speter                                      NULL,
2665251881Speter                                      left_props,
2666251881Speter                                      wb->callback_baton,
2667251881Speter                                      scratch_pool));
2668251881Speter  return SVN_NO_ERROR;
2669251881Speter}
2670251881Speter
2671251881Speter/* svn_diff_tree_processor_t function */
2672251881Speterstatic svn_error_t *
2673251881Speterwrap_file_changed(const char *relpath,
2674251881Speter                  const svn_diff_source_t *left_source,
2675251881Speter                  const svn_diff_source_t *right_source,
2676251881Speter                  const char *left_file,
2677251881Speter                  const char *right_file,
2678251881Speter                  /*const*/ apr_hash_t *left_props,
2679251881Speter                  /*const*/ apr_hash_t *right_props,
2680251881Speter                  svn_boolean_t file_modified,
2681251881Speter                  const apr_array_header_t *prop_changes,
2682251881Speter                  void *file_baton,
2683251881Speter                  const svn_diff_tree_processor_t *processor,
2684251881Speter                  apr_pool_t *scratch_pool)
2685251881Speter{
2686251881Speter  wc_diff_wrap_baton_t *wb = processor->baton;
2687251881Speter  svn_boolean_t tree_conflicted = FALSE;
2688251881Speter  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
2689251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
2690251881Speter
2691251881Speter  SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
2692251881Speter
2693251881Speter  assert(left_source && right_source);
2694251881Speter
2695251881Speter  SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
2696251881Speter                                      relpath,
2697251881Speter                                      file_modified ? left_file : NULL,
2698251881Speter                                      file_modified ? right_file : NULL,
2699251881Speter                                      left_source->revision,
2700251881Speter                                      right_source->revision,
2701251881Speter                                      left_props
2702251881Speter                                       ? svn_prop_get_value(left_props,
2703251881Speter                                                            SVN_PROP_MIME_TYPE)
2704251881Speter                                       : NULL,
2705251881Speter                                      right_props
2706251881Speter                                       ? svn_prop_get_value(right_props,
2707251881Speter                                                            SVN_PROP_MIME_TYPE)
2708251881Speter                                       : NULL,
2709251881Speter                                       prop_changes,
2710251881Speter                                      left_props,
2711251881Speter                                      wb->callback_baton,
2712251881Speter                                      scratch_pool));
2713251881Speter  return SVN_NO_ERROR;
2714251881Speter}
2715251881Speter
2716251881Spetersvn_error_t *
2717251881Spetersvn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
2718251881Speter                            const svn_wc_diff_callbacks4_t *callbacks,
2719251881Speter                            void *callback_baton,
2720251881Speter                            svn_boolean_t walk_deleted_dirs,
2721251881Speter                            apr_pool_t *result_pool,
2722251881Speter                            apr_pool_t *scratch_pool)
2723251881Speter{
2724251881Speter  wc_diff_wrap_baton_t *wrap_baton;
2725251881Speter  svn_diff_tree_processor_t *processor;
2726251881Speter
2727251881Speter  wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
2728251881Speter
2729251881Speter  wrap_baton->result_pool = result_pool;
2730251881Speter  wrap_baton->callbacks = callbacks;
2731251881Speter  wrap_baton->callback_baton = callback_baton;
2732251881Speter  wrap_baton->empty_file = NULL;
2733251881Speter  wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
2734251881Speter
2735251881Speter  processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
2736251881Speter
2737251881Speter  processor->dir_opened   = wrap_dir_opened;
2738251881Speter  processor->dir_added    = wrap_dir_added;
2739251881Speter  processor->dir_deleted  = wrap_dir_deleted;
2740251881Speter  processor->dir_changed  = wrap_dir_changed;
2741251881Speter  processor->dir_closed   = wrap_dir_closed;
2742251881Speter
2743251881Speter  processor->file_opened   = wrap_file_opened;
2744251881Speter  processor->file_added    = wrap_file_added;
2745251881Speter  processor->file_deleted  = wrap_file_deleted;
2746251881Speter  processor->file_changed  = wrap_file_changed;
2747251881Speter  /*processor->file_closed   = wrap_file_closed*/; /* Not needed */
2748251881Speter
2749251881Speter  *diff_processor = processor;
2750251881Speter  return SVN_NO_ERROR;
2751251881Speter}
2752