~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/diff_local.c

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
#include "svn_subst.h"
46
46
#include "client.h"
47
47
 
 
48
#include "private/svn_sorts_private.h"
48
49
#include "private/svn_wc_private.h"
 
50
#include "private/svn_diff_tree.h"
49
51
 
50
52
#include "svn_private_config.h"
51
53
 
81
83
  return SVN_NO_ERROR;
82
84
}
83
85
 
84
 
/* Produce a diff between two arbitrary files at LOCAL_ABSPATH1 and
85
 
 * LOCAL_ABSPATH2, using the diff callbacks from CALLBACKS.
86
 
 * Use PATH as the name passed to diff callbacks.
87
 
 * FILE1_IS_EMPTY and FILE2_IS_EMPTY are used as hints which diff callback
88
 
 * function to use to compare the files (added/deleted/changed).
89
 
 *
90
 
 * If ORIGINAL_PROPS_OVERRIDE is not NULL, use it as original properties
91
 
 * instead of reading properties from LOCAL_ABSPATH1. This is required when
92
 
 * a file replaces a directory, where LOCAL_ABSPATH1 is an empty file that
93
 
 * file content must be diffed against, but properties to diff against come
94
 
 * from the replaced directory. */
95
 
static svn_error_t *
96
 
do_arbitrary_files_diff(const char *local_abspath1,
97
 
                        const char *local_abspath2,
98
 
                        const char *path,
99
 
                        svn_boolean_t file1_is_empty,
100
 
                        svn_boolean_t file2_is_empty,
101
 
                        apr_hash_t *original_props_override,
102
 
                        const svn_wc_diff_callbacks4_t *callbacks,
103
 
                        void *diff_baton,
104
 
                        svn_client_ctx_t *ctx,
105
 
                        apr_pool_t *scratch_pool)
106
 
{
107
 
  apr_hash_t *original_props;
108
 
  apr_hash_t *modified_props;
109
 
  apr_array_header_t *prop_changes;
110
 
  svn_string_t *original_mime_type = NULL;
111
 
  svn_string_t *modified_mime_type = NULL;
112
 
 
113
 
  if (ctx->cancel_func)
114
 
    SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
115
 
 
116
 
  /* Try to get properties from either file. It's OK if the files do not
117
 
   * have properties, or if they are unversioned. */
118
 
  if (original_props_override)
119
 
    original_props = original_props_override;
120
 
  else
121
 
    SVN_ERR(get_props(&original_props, local_abspath1, ctx->wc_ctx,
122
 
                      scratch_pool, scratch_pool));
123
 
  SVN_ERR(get_props(&modified_props, local_abspath2, ctx->wc_ctx,
124
 
                    scratch_pool, scratch_pool));
125
 
 
126
 
  SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props,
127
 
                         scratch_pool));
128
 
 
129
 
  /* Try to determine the mime-type of each file. */
130
 
  original_mime_type = svn_hash_gets(original_props, SVN_PROP_MIME_TYPE);
131
 
  if (!file1_is_empty && !original_mime_type)
132
 
    {
133
 
      const char *mime_type;
134
 
      SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1,
135
 
                                      ctx->mimetypes_map, scratch_pool));
136
 
 
137
 
      if (mime_type)
138
 
        original_mime_type = svn_string_create(mime_type, scratch_pool);
139
 
    }
140
 
 
141
 
  modified_mime_type = svn_hash_gets(modified_props, SVN_PROP_MIME_TYPE);
142
 
  if (!file2_is_empty && !modified_mime_type)
143
 
    {
144
 
      const char *mime_type;
145
 
      SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1,
146
 
                                      ctx->mimetypes_map, scratch_pool));
147
 
 
148
 
      if (mime_type)
149
 
        modified_mime_type = svn_string_create(mime_type, scratch_pool);
150
 
    }
151
 
 
152
 
  /* Produce the diff. */
153
 
  if (file1_is_empty && !file2_is_empty)
154
 
    SVN_ERR(callbacks->file_added(NULL, NULL, NULL, path,
155
 
                                  local_abspath1, local_abspath2,
156
 
                                  /* ### TODO get real revision info
157
 
                                   * for versioned files? */
158
 
                                  SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
159
 
                                  original_mime_type ?
160
 
                                    original_mime_type->data : NULL,
161
 
                                  modified_mime_type ?
162
 
                                    modified_mime_type->data : NULL,
163
 
                                  /* ### TODO get copyfrom? */
164
 
                                  NULL, SVN_INVALID_REVNUM,
165
 
                                  prop_changes, original_props,
166
 
                                  diff_baton, scratch_pool));
167
 
  else if (!file1_is_empty && file2_is_empty)
168
 
    SVN_ERR(callbacks->file_deleted(NULL, NULL, path,
169
 
                                    local_abspath1, local_abspath2,
170
 
                                    original_mime_type ?
171
 
                                      original_mime_type->data : NULL,
172
 
                                    modified_mime_type ?
173
 
                                      modified_mime_type->data : NULL,
174
 
                                    original_props,
175
 
                                    diff_baton, scratch_pool));
176
 
  else
177
 
    {
178
 
      svn_stream_t *file1;
179
 
      svn_stream_t *file2;
180
 
      svn_boolean_t same;
181
 
      svn_string_t *val;
182
 
      /* We have two files, which may or may not be the same.
183
 
 
184
 
         ### Our caller assumes that we should ignore symlinks here and
185
 
             handle them as normal paths. Perhaps that should change?
186
 
      */
187
 
      SVN_ERR(svn_stream_open_readonly(&file1, local_abspath1, scratch_pool,
188
 
                                       scratch_pool));
189
 
 
190
 
      SVN_ERR(svn_stream_open_readonly(&file2, local_abspath2, scratch_pool,
191
 
                                       scratch_pool));
192
 
 
193
 
      /* Wrap with normalization, etc. if necessary */
194
 
      if (original_props)
195
 
        {
196
 
          val = svn_hash_gets(original_props, SVN_PROP_EOL_STYLE);
197
 
 
198
 
          if (val)
199
 
            {
200
 
              svn_subst_eol_style_t style;
201
 
              const char *eol;
202
 
              svn_subst_eol_style_from_value(&style, &eol, val->data);
203
 
 
204
 
              /* ### Ignoring keywords */
205
 
              if (eol)
206
 
                file1 = svn_subst_stream_translated(file1, eol, TRUE,
207
 
                                                    NULL, FALSE,
208
 
                                                    scratch_pool);
209
 
            }
210
 
        }
211
 
 
212
 
      if (modified_props)
213
 
        {
214
 
          val = svn_hash_gets(modified_props, SVN_PROP_EOL_STYLE);
215
 
 
216
 
          if (val)
217
 
            {
218
 
              svn_subst_eol_style_t style;
219
 
              const char *eol;
220
 
              svn_subst_eol_style_from_value(&style, &eol, val->data);
221
 
 
222
 
              /* ### Ignoring keywords */
223
 
              if (eol)
224
 
                file2 = svn_subst_stream_translated(file2, eol, TRUE,
225
 
                                                    NULL, FALSE,
226
 
                                                    scratch_pool);
227
 
            }
228
 
        }
229
 
 
230
 
      SVN_ERR(svn_stream_contents_same2(&same, file1, file2, scratch_pool));
231
 
 
232
 
      if (! same || prop_changes->nelts > 0)
233
 
        {
234
 
          /* ### We should probably pass the normalized data we created using
235
 
                 the subst streams as that is what diff users expect */
236
 
          SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, path,
237
 
                                          same ? NULL : local_abspath1,
238
 
                                          same ? NULL : local_abspath2,
239
 
                                          /* ### TODO get real revision info
240
 
                                           * for versioned files? */
241
 
                                          SVN_INVALID_REVNUM /* rev1 */,
242
 
                                          SVN_INVALID_REVNUM /* rev2 */,
243
 
                                          original_mime_type ?
244
 
                                            original_mime_type->data : NULL,
245
 
                                          modified_mime_type ?
246
 
                                            modified_mime_type->data : NULL,
247
 
                                          prop_changes, original_props,
248
 
                                          diff_baton, scratch_pool));
249
 
        }
250
 
    }
251
 
 
252
 
  return SVN_NO_ERROR;
253
 
}
254
 
 
255
 
struct arbitrary_diff_walker_baton {
256
 
  /* The root directories of the trees being compared. */
257
 
  const char *root1_abspath;
258
 
  const char *root2_abspath;
259
 
 
260
 
  /* TRUE if recursing within an added subtree of root2_abspath that
261
 
   * does not exist in root1_abspath. */
262
 
  svn_boolean_t recursing_within_added_subtree;
263
 
 
264
 
  /* TRUE if recursing within an administrative (.i.e. .svn) directory. */
265
 
  svn_boolean_t recursing_within_adm_dir;
266
 
 
267
 
  /* The absolute path of the adm dir if RECURSING_WITHIN_ADM_DIR is TRUE.
268
 
   * Else this is NULL.*/
269
 
  const char *adm_dir_abspath;
270
 
 
271
 
  /* A path to an empty file used for diffs that add/delete files. */
272
 
  const char *empty_file_abspath;
273
 
 
274
 
  const svn_wc_diff_callbacks4_t *callbacks;
275
 
  void *diff_baton;
276
 
  svn_client_ctx_t *ctx;
277
 
  apr_pool_t *pool;
278
 
} arbitrary_diff_walker_baton;
279
 
 
280
 
/* Forward declaration needed because this function has a cyclic
281
 
 * dependency with do_arbitrary_dirs_diff(). */
282
 
static svn_error_t *
283
 
arbitrary_diff_walker(void *baton, const char *local_abspath,
284
 
                      const apr_finfo_t *finfo,
285
 
                      apr_pool_t *scratch_pool);
286
 
 
287
 
/* Another forward declaration. */
288
 
static svn_error_t *
289
 
arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b,
290
 
                        const char *local_abspath,
291
 
                        svn_depth_t depth,
292
 
                        apr_pool_t *scratch_pool);
 
86
/* Forward declaration */
 
87
static svn_error_t *
 
88
do_file_diff(const char *left_abspath,
 
89
             const char *right_abspath,
 
90
             const char *left_root_abspath,
 
91
             const char *right_root_abspath,
 
92
             svn_boolean_t left_only,
 
93
             svn_boolean_t right_only,
 
94
             void *parent_baton,
 
95
             const svn_diff_tree_processor_t *diff_processor,
 
96
             svn_client_ctx_t *ctx,
 
97
             apr_pool_t *scratch_pool);
 
98
 
 
99
/* Forward declaration */
 
100
static svn_error_t *
 
101
do_dir_diff(const char *left_abspath,
 
102
            const char *right_abspath,
 
103
            const char *left_root_abspath,
 
104
            const char *right_root_abspath,
 
105
            svn_boolean_t left_only,
 
106
            svn_boolean_t right_only,
 
107
            svn_boolean_t left_before_right,
 
108
            svn_depth_t depth,
 
109
            void *parent_baton,
 
110
            const svn_diff_tree_processor_t *diff_processor,
 
111
            svn_client_ctx_t *ctx,
 
112
            apr_pool_t *scratch_pool);
293
113
 
294
114
/* Produce a diff of depth DEPTH between two arbitrary directories at
295
 
 * LOCAL_ABSPATH1 and LOCAL_ABSPATH2, using the provided diff callbacks
 
115
 * LEFT_ABSPATH1 and RIGHT_ABSPATH2, using the provided diff callbacks
296
116
 * to show file changes and, for versioned nodes, property changes.
297
117
 *
298
 
 * If ROOT_ABSPATH1 and ROOT_ABSPATH2 are not NULL, show paths in diffs
299
 
 * relative to these roots, rather than relative to LOCAL_ABSPATH1 and
300
 
 * LOCAL_ABSPATH2. This is needed when crawling a subtree that exists
301
 
 * only within LOCAL_ABSPATH2. */
302
 
static svn_error_t *
303
 
do_arbitrary_dirs_diff(const char *local_abspath1,
304
 
                       const char *local_abspath2,
305
 
                       const char *root_abspath1,
306
 
                       const char *root_abspath2,
307
 
                       svn_depth_t depth,
308
 
                       const svn_wc_diff_callbacks4_t *callbacks,
309
 
                       void *diff_baton,
310
 
                       svn_client_ctx_t *ctx,
311
 
                       apr_pool_t *scratch_pool)
312
 
{
313
 
  apr_file_t *empty_file;
314
 
  svn_node_kind_t kind1;
315
 
 
316
 
  struct arbitrary_diff_walker_baton b;
317
 
 
318
 
  /* If LOCAL_ABSPATH1 is not a directory, crawl LOCAL_ABSPATH2 instead
319
 
   * and compare it to LOCAL_ABSPATH1, showing only additions.
320
 
   * This case can only happen during recursion from arbitrary_diff_walker(),
321
 
   * because do_arbitrary_nodes_diff() prevents this from happening at
322
 
   * the root of the comparison. */
323
 
  SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
324
 
  b.recursing_within_added_subtree = (kind1 != svn_node_dir);
325
 
 
326
 
  b.root1_abspath = root_abspath1 ? root_abspath1 : local_abspath1;
327
 
  b.root2_abspath = root_abspath2 ? root_abspath2 : local_abspath2;
328
 
  b.recursing_within_adm_dir = FALSE;
329
 
  b.adm_dir_abspath = NULL;
330
 
  b.callbacks = callbacks;
331
 
  b.diff_baton = diff_baton;
332
 
  b.ctx = ctx;
333
 
  b.pool = scratch_pool;
334
 
 
335
 
  SVN_ERR(svn_io_open_unique_file3(&empty_file, &b.empty_file_abspath,
336
 
                                   NULL, svn_io_file_del_on_pool_cleanup,
337
 
                                   scratch_pool, scratch_pool));
338
 
 
339
 
  if (depth <= svn_depth_immediates)
340
 
    SVN_ERR(arbitrary_diff_this_dir(&b, local_abspath1, depth, scratch_pool));
341
 
  else if (depth == svn_depth_infinity)
342
 
    SVN_ERR(svn_io_dir_walk2(b.recursing_within_added_subtree ? local_abspath2
343
 
                                                              : local_abspath1,
344
 
                             0, arbitrary_diff_walker, &b, scratch_pool));
345
 
  return SVN_NO_ERROR;
346
 
}
347
 
 
348
 
/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH,
349
 
 * using information from the arbitrary_diff_walker_baton B.
350
 
 * LOCAL_ABSPATH is the path being crawled and can be on either side
351
 
 * of the diff depending on baton->recursing_within_added_subtree. */
352
 
static svn_error_t *
353
 
arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b,
354
 
                        const char *local_abspath,
355
 
                        svn_depth_t depth,
356
 
                        apr_pool_t *scratch_pool)
357
 
{
358
 
  const char *local_abspath1;
359
 
  const char *local_abspath2;
360
 
  svn_node_kind_t kind1;
361
 
  svn_node_kind_t kind2;
362
 
  const char *child_relpath;
363
 
  apr_hash_t *dirents1;
364
 
  apr_hash_t *dirents2;
365
 
  apr_hash_t *merged_dirents;
 
118
 * Report paths as relative from LEFT_ROOT_ABSPATH/RIGHT_ROOT_ABSPATH.
 
119
 *
 
120
 * If LEFT_ONLY is TRUE, only the left source exists (= everything will
 
121
 * be reported as deleted). If RIGHT_ONLY is TRUE, only the right source
 
122
 * exists (= everything will be reported as added).
 
123
 *
 
124
 * If LEFT_BEFORE_RIGHT is TRUE and left and right are unrelated, left is
 
125
 * reported first. If false, right is reported first. (This is to allow
 
126
 * producing a proper inverse diff).
 
127
 *
 
128
 * Walk the sources according to depth, and report with parent baton
 
129
 * PARENT_BATON. */
 
130
static svn_error_t *
 
131
inner_dir_diff(const char *left_abspath,
 
132
               const char *right_abspath,
 
133
               const char *left_root_abspath,
 
134
               const char *right_root_abspath,
 
135
               svn_boolean_t left_only,
 
136
               svn_boolean_t right_only,
 
137
               svn_boolean_t left_before_right,
 
138
               svn_depth_t depth,
 
139
               void *parent_baton,
 
140
               const svn_diff_tree_processor_t *diff_processor,
 
141
               svn_client_ctx_t *ctx,
 
142
               apr_pool_t *scratch_pool)
 
143
{
 
144
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
145
  apr_hash_t *left_dirents;
 
146
  apr_hash_t *right_dirents;
366
147
  apr_array_header_t *sorted_dirents;
 
148
  svn_error_t *err;
 
149
  svn_depth_t depth_below_here;
367
150
  int i;
368
 
  apr_pool_t *iterpool;
369
 
 
370
 
  if (b->recursing_within_adm_dir)
371
 
    {
372
 
      if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath))
373
 
        return SVN_NO_ERROR;
374
 
      else
375
 
        {
376
 
          b->recursing_within_adm_dir = FALSE;
377
 
          b->adm_dir_abspath = NULL;
378
 
        }
379
 
    }
380
 
  else if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL),
381
 
                             scratch_pool))
382
 
    {
383
 
      b->recursing_within_adm_dir = TRUE;
384
 
      b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath);
385
 
      return SVN_NO_ERROR;
386
 
    }
387
 
 
388
 
  if (b->recursing_within_added_subtree)
389
 
    child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath);
390
 
  else
391
 
    child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath);
392
 
  if (!child_relpath)
393
 
    return SVN_NO_ERROR;
394
 
 
395
 
  local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath,
396
 
                                   scratch_pool);
397
 
  SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
398
 
 
399
 
  local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath,
400
 
                                   scratch_pool);
401
 
  SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool));
402
 
 
403
 
  if (depth > svn_depth_empty)
404
 
    {
405
 
      if (kind1 == svn_node_dir)
406
 
        SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1,
407
 
                                    TRUE, /* only_check_type */
408
 
                                    scratch_pool, scratch_pool));
409
 
      else
410
 
        dirents1 = apr_hash_make(scratch_pool);
411
 
    }
412
 
 
413
 
  if (kind2 == svn_node_dir)
414
 
    {
415
 
      apr_hash_t *original_props;
416
 
      apr_hash_t *modified_props;
417
 
      apr_array_header_t *prop_changes;
418
 
 
419
 
      /* Show any property changes for this directory. */
420
 
      SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx,
421
 
                        scratch_pool, scratch_pool));
422
 
      SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx,
423
 
                        scratch_pool, scratch_pool));
424
 
      SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props,
425
 
                             scratch_pool));
426
 
      if (prop_changes->nelts > 0)
427
 
        SVN_ERR(b->callbacks->dir_props_changed(NULL, NULL, child_relpath,
428
 
                                                FALSE /* was_added */,
429
 
                                                prop_changes, original_props,
430
 
                                                b->diff_baton,
431
 
                                                scratch_pool));
432
 
 
433
 
      if (depth > svn_depth_empty)
434
 
        {
435
 
          /* Read directory entries. */
436
 
          SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2,
437
 
                                      TRUE, /* only_check_type */
438
 
                                      scratch_pool, scratch_pool));
439
 
        }
440
 
    }
441
 
  else if (depth > svn_depth_empty)
442
 
    dirents2 = apr_hash_make(scratch_pool);
443
 
 
444
 
  if (depth <= svn_depth_empty)
445
 
    return SVN_NO_ERROR;
446
 
 
447
 
  /* Compare dirents1 to dirents2 and show added/deleted/changed files. */
448
 
  merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2,
449
 
                                  NULL, NULL);
450
 
  sorted_dirents = svn_sort__hash(merged_dirents,
 
151
 
 
152
  SVN_ERR_ASSERT(depth >= svn_depth_files && depth <= svn_depth_infinity);
 
153
 
 
154
  if (!right_only)
 
155
    {
 
156
      err = svn_io_get_dirents3(&left_dirents, left_abspath, FALSE,
 
157
                                scratch_pool, iterpool);
 
158
 
 
159
      if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
 
160
                  || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
 
161
        {
 
162
          svn_error_clear(err);
 
163
          left_dirents = apr_hash_make(scratch_pool);
 
164
          right_only = TRUE;
 
165
        }
 
166
      else
 
167
        SVN_ERR(err);
 
168
    }
 
169
  else
 
170
    left_dirents = apr_hash_make(scratch_pool);
 
171
 
 
172
  if (!left_only)
 
173
    {
 
174
      err = svn_io_get_dirents3(&right_dirents, right_abspath, FALSE,
 
175
                                scratch_pool, iterpool);
 
176
 
 
177
      if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
 
178
                  || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
 
179
        {
 
180
          svn_error_clear(err);
 
181
          right_dirents = apr_hash_make(scratch_pool);
 
182
          right_only = TRUE;
 
183
        }
 
184
      else
 
185
        SVN_ERR(err);
 
186
    }
 
187
  else
 
188
    right_dirents = apr_hash_make(scratch_pool);
 
189
 
 
190
  if (left_only && right_only)
 
191
    return SVN_NO_ERROR; /* Somebody deleted the directory?? */
 
192
 
 
193
  if (depth != svn_depth_infinity)
 
194
    depth_below_here = svn_depth_empty;
 
195
  else
 
196
    depth_below_here = svn_depth_infinity;
 
197
 
 
198
  sorted_dirents = svn_sort__hash(apr_hash_merge(iterpool, left_dirents,
 
199
                                                 right_dirents, NULL, NULL),
451
200
                                  svn_sort_compare_items_as_paths,
452
201
                                  scratch_pool);
453
 
  iterpool = svn_pool_create(scratch_pool);
 
202
 
454
203
  for (i = 0; i < sorted_dirents->nelts; i++)
455
204
    {
456
 
      svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t);
457
 
      const char *name = elt.key;
458
 
      svn_io_dirent2_t *dirent1;
459
 
      svn_io_dirent2_t *dirent2;
460
 
      const char *child1_abspath;
461
 
      const char *child2_abspath;
 
205
      svn_sort__item_t* elt = &APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t);
 
206
      svn_io_dirent2_t *left_dirent;
 
207
      svn_io_dirent2_t *right_dirent;
 
208
      const char *child_left_abspath;
 
209
      const char *child_right_abspath;
462
210
 
463
211
      svn_pool_clear(iterpool);
464
212
 
465
 
      if (b->ctx->cancel_func)
466
 
        SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
 
213
      if (ctx->cancel_func)
 
214
        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
467
215
 
468
 
      if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0)
 
216
      if (svn_wc_is_adm_dir(elt->key, iterpool))
469
217
        continue;
470
218
 
471
 
      dirent1 = svn_hash_gets(dirents1, name);
472
 
      if (!dirent1)
473
 
        {
474
 
          dirent1 = svn_io_dirent2_create(iterpool);
475
 
          dirent1->kind = svn_node_none;
476
 
        }
477
 
      dirent2 = svn_hash_gets(dirents2, name);
478
 
      if (!dirent2)
479
 
        {
480
 
          dirent2 = svn_io_dirent2_create(iterpool);
481
 
          dirent2->kind = svn_node_none;
482
 
        }
483
 
 
484
 
      child1_abspath = svn_dirent_join(local_abspath1, name, iterpool);
485
 
      child2_abspath = svn_dirent_join(local_abspath2, name, iterpool);
486
 
 
487
 
      if (dirent1->special)
488
 
        SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind,
489
 
                                           iterpool));
490
 
      if (dirent2->special)
491
 
        SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind,
492
 
                                           iterpool));
493
 
 
494
 
      if (dirent1->kind == svn_node_dir &&
495
 
          dirent2->kind == svn_node_dir)
496
 
        {
497
 
          if (depth == svn_depth_immediates)
498
 
            {
499
 
              /* Not using the walker, so show property diffs on these dirs. */
500
 
              SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
501
 
                                             b->root1_abspath, b->root2_abspath,
502
 
                                             svn_depth_empty,
503
 
                                             b->callbacks, b->diff_baton,
504
 
                                             b->ctx, iterpool));
505
 
            }
506
 
          else
507
 
            {
508
 
              /* Either the walker will visit these directories (with
509
 
               * depth=infinity) and they will be processed as 'this dir'
510
 
               * later, or we're showing file children only (depth=files). */
511
 
              continue;
512
 
            }
513
 
 
514
 
        }
515
 
 
516
 
      /* Files that exist only in dirents1. */
517
 
      if (dirent1->kind == svn_node_file &&
518
 
          (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none))
519
 
        SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath,
520
 
                                        svn_relpath_join(child_relpath, name,
521
 
                                                         iterpool),
522
 
                                        FALSE, TRUE, NULL,
523
 
                                        b->callbacks, b->diff_baton,
524
 
                                        b->ctx, iterpool));
525
 
 
526
 
      /* Files that exist only in dirents2. */
527
 
      if (dirent2->kind == svn_node_file &&
528
 
          (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none))
529
 
        {
530
 
          apr_hash_t *original_props;
531
 
 
532
 
          SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx,
533
 
                            scratch_pool, scratch_pool));
534
 
          SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath,
535
 
                                          svn_relpath_join(child_relpath, name,
536
 
                                                           iterpool),
537
 
                                          TRUE, FALSE, original_props,
538
 
                                          b->callbacks, b->diff_baton,
539
 
                                          b->ctx, iterpool));
540
 
        }
541
 
 
542
 
      /* Files that exist in dirents1 and dirents2. */
543
 
      if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file)
544
 
        SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath,
545
 
                                        svn_relpath_join(child_relpath, name,
546
 
                                                         iterpool),
547
 
                                        FALSE, FALSE, NULL,
548
 
                                        b->callbacks, b->diff_baton,
549
 
                                        b->ctx, scratch_pool));
550
 
 
551
 
      /* Directories that only exist in dirents2. These aren't crawled
552
 
       * by this walker so we have to crawl them separately. */
553
 
      if (depth > svn_depth_files &&
554
 
          dirent2->kind == svn_node_dir &&
555
 
          (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none))
556
 
        SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
557
 
                                       b->root1_abspath, b->root2_abspath,
558
 
                                       depth <= svn_depth_immediates
559
 
                                         ? svn_depth_empty
560
 
                                         : svn_depth_infinity ,
561
 
                                       b->callbacks, b->diff_baton,
562
 
                                       b->ctx, iterpool));
563
 
    }
564
 
 
565
 
  svn_pool_destroy(iterpool);
566
 
 
567
 
  return SVN_NO_ERROR;
568
 
}
569
 
 
570
 
/* An implementation of svn_io_walk_func_t.
571
 
 * Note: LOCAL_ABSPATH is the path being crawled and can be on either side
572
 
 * of the diff depending on baton->recursing_within_added_subtree. */
573
 
static svn_error_t *
574
 
arbitrary_diff_walker(void *baton, const char *local_abspath,
575
 
                      const apr_finfo_t *finfo,
576
 
                      apr_pool_t *scratch_pool)
577
 
{
578
 
  struct arbitrary_diff_walker_baton *b = baton;
579
 
 
580
 
  if (b->ctx->cancel_func)
581
 
    SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
582
 
 
583
 
  if (finfo->filetype != APR_DIR)
584
 
    return SVN_NO_ERROR;
585
 
 
586
 
  SVN_ERR(arbitrary_diff_this_dir(b, local_abspath, svn_depth_infinity,
587
 
                                  scratch_pool));
 
219
      left_dirent = right_only ? NULL : svn_hash_gets(left_dirents, elt->key);
 
220
      right_dirent = left_only ? NULL : svn_hash_gets(right_dirents, elt->key);
 
221
 
 
222
      child_left_abspath = svn_dirent_join(left_abspath, elt->key, iterpool);
 
223
      child_right_abspath = svn_dirent_join(right_abspath, elt->key, iterpool);
 
224
 
 
225
      if (((left_dirent == NULL) != (right_dirent == NULL))
 
226
          || (left_dirent->kind != right_dirent->kind))
 
227
        {
 
228
          /* Report delete and/or add */
 
229
          if (left_dirent && left_before_right)
 
230
            {
 
231
              if (left_dirent->kind == svn_node_file)
 
232
                SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
 
233
                                     left_root_abspath, right_root_abspath,
 
234
                                     TRUE, FALSE, parent_baton,
 
235
                                     diff_processor, ctx, iterpool));
 
236
              else if (depth >= svn_depth_immediates)
 
237
                SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
 
238
                                    left_root_abspath, right_root_abspath,
 
239
                                    TRUE, FALSE, left_before_right,
 
240
                                    depth_below_here, parent_baton,
 
241
                                    diff_processor, ctx, iterpool));
 
242
            }
 
243
 
 
244
          if (right_dirent)
 
245
            {
 
246
              if (right_dirent->kind == svn_node_file)
 
247
                SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
 
248
                                     left_root_abspath, right_root_abspath,
 
249
                                     FALSE, TRUE, parent_baton,
 
250
                                     diff_processor, ctx, iterpool));
 
251
              else if (depth >= svn_depth_immediates)
 
252
                SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
 
253
                                    left_root_abspath, right_root_abspath,
 
254
                                    FALSE, TRUE,  left_before_right,
 
255
                                    depth_below_here, parent_baton,
 
256
                                    diff_processor, ctx, iterpool));
 
257
            }
 
258
 
 
259
          if (left_dirent && !left_before_right)
 
260
            {
 
261
              if (left_dirent->kind == svn_node_file)
 
262
                SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
 
263
                                     left_root_abspath, right_root_abspath,
 
264
                                     TRUE, FALSE, parent_baton,
 
265
                                     diff_processor, ctx, iterpool));
 
266
              else if (depth >= svn_depth_immediates)
 
267
                SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
 
268
                                    left_root_abspath, right_root_abspath,
 
269
                                    TRUE, FALSE,  left_before_right,
 
270
                                    depth_below_here, parent_baton,
 
271
                                    diff_processor, ctx, iterpool));
 
272
            }
 
273
        }
 
274
      else if (left_dirent->kind == svn_node_file)
 
275
        {
 
276
          /* Perform file-file diff */
 
277
          SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
 
278
                               left_root_abspath, right_root_abspath,
 
279
                               FALSE, FALSE, parent_baton,
 
280
                               diff_processor, ctx, iterpool));
 
281
        }
 
282
      else if (depth >= svn_depth_immediates)
 
283
        {
 
284
          /* Perform dir-dir diff */
 
285
          SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
 
286
                              left_root_abspath, right_root_abspath,
 
287
                              FALSE, FALSE,  left_before_right,
 
288
                              depth_below_here, parent_baton,
 
289
                              diff_processor, ctx, iterpool));
 
290
        }
 
291
    }
 
292
 
 
293
  return SVN_NO_ERROR;
 
294
}
 
295
 
 
296
/* Translates *LEFT_ABSPATH to a temporary file if PROPS specify that the
 
297
   file needs translation. *LEFT_ABSPATH is updated to point to a file that
 
298
   lives at least as long as RESULT_POOL when translation is necessary.
 
299
   Otherwise the value is not updated */
 
300
static svn_error_t *
 
301
translate_if_necessary(const char **local_abspath,
 
302
                       apr_hash_t *props,
 
303
                       svn_cancel_func_t cancel_func,
 
304
                       void *cancel_baton,
 
305
                       apr_pool_t *result_pool,
 
306
                       apr_pool_t *scratch_pool)
 
307
{
 
308
  const svn_string_t *eol_style_val;
 
309
  const svn_string_t *keywords_val;
 
310
  svn_subst_eol_style_t eol_style;
 
311
  const char *eol;
 
312
  apr_hash_t *keywords;
 
313
  svn_stream_t *contents;
 
314
  svn_stream_t *dst;
 
315
 
 
316
  /* if (svn_hash_gets(props, SVN_PROP_SPECIAL))
 
317
      ### TODO: Implement */
 
318
 
 
319
  eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
 
320
  keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS);
 
321
 
 
322
  if (eol_style_val)
 
323
    svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data);
 
324
  else
 
325
    {
 
326
      eol = NULL;
 
327
      eol_style = svn_subst_eol_style_none;
 
328
    }
 
329
 
 
330
  if (keywords_val)
 
331
    SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data,
 
332
                                      APR_STRINGIFY(SVN_INVALID_REVNUM),
 
333
                                      "", "", 0, "", scratch_pool));
 
334
  else
 
335
    keywords = NULL;
 
336
 
 
337
  if (!svn_subst_translation_required(eol_style, eol, keywords, FALSE, FALSE))
 
338
    return SVN_NO_ERROR;
 
339
 
 
340
  SVN_ERR(svn_stream_open_readonly(&contents, *local_abspath,
 
341
                                    scratch_pool, scratch_pool));
 
342
 
 
343
  SVN_ERR(svn_stream_open_unique(&dst, local_abspath, NULL,
 
344
                                  svn_io_file_del_on_pool_cleanup,
 
345
                                  result_pool, scratch_pool));
 
346
 
 
347
  dst = svn_subst_stream_translated(dst, eol, TRUE /* repair */,
 
348
                                    keywords, FALSE /* expand */,
 
349
                                    scratch_pool);
 
350
 
 
351
  SVN_ERR(svn_stream_copy3(contents, dst, cancel_func, cancel_baton,
 
352
                           scratch_pool));
 
353
 
 
354
  return SVN_NO_ERROR;
 
355
}
 
356
 
 
357
/* Handles reporting of a file for inner_dir_diff */
 
358
static svn_error_t *
 
359
do_file_diff(const char *left_abspath,
 
360
             const char *right_abspath,
 
361
             const char *left_root_abspath,
 
362
             const char *right_root_abspath,
 
363
             svn_boolean_t left_only,
 
364
             svn_boolean_t right_only,
 
365
             void *parent_baton,
 
366
             const svn_diff_tree_processor_t *diff_processor,
 
367
             svn_client_ctx_t *ctx,
 
368
             apr_pool_t *scratch_pool)
 
369
{
 
370
  const char *relpath;
 
371
  svn_diff_source_t *left_source;
 
372
  svn_diff_source_t *right_source;
 
373
  svn_boolean_t skip = FALSE;
 
374
  apr_hash_t *left_props;
 
375
  apr_hash_t *right_props;
 
376
  void *file_baton;
 
377
 
 
378
  relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath);
 
379
 
 
380
 if (! right_only)
 
381
    left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
 
382
  else
 
383
    left_source = NULL;
 
384
 
 
385
  if (! left_only)
 
386
    right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
 
387
  else
 
388
    right_source = NULL;
 
389
 
 
390
  SVN_ERR(diff_processor->file_opened(&file_baton, &skip,
 
391
                                      relpath,
 
392
                                      left_source,
 
393
                                      right_source,
 
394
                                      NULL /* copyfrom_source */,
 
395
                                      parent_baton,
 
396
                                      diff_processor,
 
397
                                      scratch_pool,
 
398
                                      scratch_pool));
 
399
 
 
400
  if (skip)
 
401
    return SVN_NO_ERROR;
 
402
 
 
403
   if (! right_only)
 
404
    {
 
405
      SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx,
 
406
                        scratch_pool, scratch_pool));
 
407
 
 
408
      /* We perform a mimetype detection to avoid diffing binary files
 
409
         for textual changes.*/
 
410
      if (! svn_hash_gets(left_props, SVN_PROP_MIME_TYPE))
 
411
        {
 
412
          const char *mime_type;
 
413
 
 
414
          /* ### Use libmagic magic? */
 
415
          SVN_ERR(svn_io_detect_mimetype2(&mime_type, left_abspath,
 
416
                                          ctx->mimetypes_map, scratch_pool));
 
417
 
 
418
          if (mime_type)
 
419
            svn_hash_sets(left_props, SVN_PROP_MIME_TYPE,
 
420
                          svn_string_create(mime_type, scratch_pool));
 
421
        }
 
422
 
 
423
      SVN_ERR(translate_if_necessary(&left_abspath, left_props,
 
424
                                     ctx->cancel_func, ctx->cancel_baton,
 
425
                                     scratch_pool, scratch_pool));
 
426
    }
 
427
  else
 
428
    left_props = NULL;
 
429
 
 
430
  if (! left_only)
 
431
    {
 
432
      SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx,
 
433
                        scratch_pool, scratch_pool));
 
434
 
 
435
      /* We perform a mimetype detection to avoid diffing binary files
 
436
         for textual changes.*/
 
437
      if (! svn_hash_gets(right_props, SVN_PROP_MIME_TYPE))
 
438
        {
 
439
          const char *mime_type;
 
440
 
 
441
          /* ### Use libmagic magic? */
 
442
          SVN_ERR(svn_io_detect_mimetype2(&mime_type, right_abspath,
 
443
                                          ctx->mimetypes_map, scratch_pool));
 
444
 
 
445
          if (mime_type)
 
446
            svn_hash_sets(right_props, SVN_PROP_MIME_TYPE,
 
447
                          svn_string_create(mime_type, scratch_pool));
 
448
        }
 
449
 
 
450
      SVN_ERR(translate_if_necessary(&right_abspath, right_props,
 
451
                                     ctx->cancel_func, ctx->cancel_baton,
 
452
                                     scratch_pool, scratch_pool));
 
453
 
 
454
    }
 
455
  else
 
456
    right_props = NULL;
 
457
 
 
458
  if (left_only)
 
459
    {
 
460
      SVN_ERR(diff_processor->file_deleted(relpath,
 
461
                                           left_source,
 
462
                                           left_abspath,
 
463
                                           left_props,
 
464
                                           file_baton,
 
465
                                           diff_processor,
 
466
                                           scratch_pool));
 
467
    }
 
468
  else if (right_only)
 
469
    {
 
470
      SVN_ERR(diff_processor->file_added(relpath,
 
471
                                         NULL /* copyfrom_source */,
 
472
                                         right_source,
 
473
                                         NULL /* copyfrom_file */,
 
474
                                         right_abspath,
 
475
                                         NULL /* copyfrom_props */,
 
476
                                         right_props,
 
477
                                         file_baton,
 
478
                                         diff_processor,
 
479
                                         scratch_pool));
 
480
    }
 
481
  else
 
482
    {
 
483
      /* ### Perform diff -> close/changed */
 
484
      svn_boolean_t same;
 
485
      apr_array_header_t *prop_changes;
 
486
 
 
487
      SVN_ERR(svn_io_files_contents_same_p(&same, left_abspath, right_abspath,
 
488
                                           scratch_pool));
 
489
 
 
490
      SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props,
 
491
                             scratch_pool));
 
492
 
 
493
      if (!same || prop_changes->nelts > 0)
 
494
        {
 
495
          SVN_ERR(diff_processor->file_changed(relpath,
 
496
                                               left_source,
 
497
                                               right_source,
 
498
                                               same ? NULL : left_abspath,
 
499
                                               same ? NULL : right_abspath,
 
500
                                               left_props,
 
501
                                               right_props,
 
502
                                               !same,
 
503
                                               prop_changes,
 
504
                                               file_baton,
 
505
                                               diff_processor,
 
506
                                               scratch_pool));
 
507
        }
 
508
      else
 
509
        {
 
510
          SVN_ERR(diff_processor->file_closed(relpath,
 
511
                                            left_source,
 
512
                                            right_source,
 
513
                                            file_baton,
 
514
                                            diff_processor,
 
515
                                            scratch_pool));
 
516
        }
 
517
    }
 
518
  return SVN_NO_ERROR;
 
519
}
 
520
 
 
521
 
 
522
/* Handles reporting of a directory and its children for inner_dir_diff */
 
523
static svn_error_t *
 
524
do_dir_diff(const char *left_abspath,
 
525
            const char *right_abspath,
 
526
            const char *left_root_abspath,
 
527
            const char *right_root_abspath,
 
528
            svn_boolean_t left_only,
 
529
            svn_boolean_t right_only,
 
530
            svn_boolean_t left_before_right,
 
531
            svn_depth_t depth,
 
532
            void *parent_baton,
 
533
            const svn_diff_tree_processor_t *diff_processor,
 
534
            svn_client_ctx_t *ctx,
 
535
            apr_pool_t *scratch_pool)
 
536
{
 
537
  const char *relpath;
 
538
  svn_diff_source_t *left_source;
 
539
  svn_diff_source_t *right_source;
 
540
  svn_boolean_t skip = FALSE;
 
541
  svn_boolean_t skip_children = FALSE;
 
542
  void *dir_baton;
 
543
  apr_hash_t *left_props;
 
544
  apr_hash_t *right_props;
 
545
 
 
546
  relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath);
 
547
 
 
548
  if (! right_only)
 
549
    {
 
550
      left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
 
551
      SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx,
 
552
                        scratch_pool, scratch_pool));
 
553
    }
 
554
  else
 
555
    {
 
556
      left_source = NULL;
 
557
      left_props = NULL;
 
558
    }
 
559
 
 
560
  if (! left_only)
 
561
    {
 
562
      right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
 
563
      SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx,
 
564
                        scratch_pool, scratch_pool));
 
565
    }
 
566
  else
 
567
    {
 
568
      right_source = NULL;
 
569
      right_props = NULL;
 
570
    }
 
571
 
 
572
  SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children,
 
573
                                     relpath,
 
574
                                     left_source,
 
575
                                     right_source,
 
576
                                     NULL /* copyfrom_source */,
 
577
                                     parent_baton,
 
578
                                     diff_processor,
 
579
                                     scratch_pool, scratch_pool));
 
580
 
 
581
  if (!skip_children)
 
582
    {
 
583
      if (depth >= svn_depth_files)
 
584
        SVN_ERR(inner_dir_diff(left_abspath, right_abspath,
 
585
                               left_root_abspath, right_root_abspath,
 
586
                               left_only, right_only,
 
587
                               left_before_right, depth,
 
588
                               dir_baton,
 
589
                               diff_processor, ctx, scratch_pool));
 
590
    }
 
591
  else if (skip)
 
592
    return SVN_NO_ERROR;
 
593
 
 
594
  if (left_props && right_props)
 
595
    {
 
596
      apr_array_header_t *prop_diffs;
 
597
 
 
598
      SVN_ERR(svn_prop_diffs(&prop_diffs, right_props, left_props,
 
599
                             scratch_pool));
 
600
 
 
601
      if (prop_diffs->nelts)
 
602
        {
 
603
          SVN_ERR(diff_processor->dir_changed(relpath,
 
604
                                              left_source,
 
605
                                              right_source,
 
606
                                              left_props,
 
607
                                              right_props,
 
608
                                              prop_diffs,
 
609
                                              dir_baton,
 
610
                                              diff_processor,
 
611
                                              scratch_pool));
 
612
          return SVN_NO_ERROR;
 
613
        }
 
614
    }
 
615
 
 
616
  if (left_source && right_source)
 
617
    {
 
618
      SVN_ERR(diff_processor->dir_closed(relpath,
 
619
                                         left_source,
 
620
                                         right_source,
 
621
                                         dir_baton,
 
622
                                         diff_processor,
 
623
                                         scratch_pool));
 
624
    }
 
625
  else if (left_source)
 
626
    {
 
627
      SVN_ERR(diff_processor->dir_deleted(relpath,
 
628
                                          left_source,
 
629
                                          left_props,
 
630
                                          dir_baton,
 
631
                                          diff_processor,
 
632
                                          scratch_pool));
 
633
    }
 
634
  else
 
635
    {
 
636
      SVN_ERR(diff_processor->dir_added(relpath,
 
637
                                        NULL /* copyfrom_source */,
 
638
                                        right_source,
 
639
                                        NULL /* copyfrom_props */,
 
640
                                        right_props,
 
641
                                        dir_baton,
 
642
                                        diff_processor,
 
643
                                        scratch_pool));
 
644
    }
588
645
 
589
646
  return SVN_NO_ERROR;
590
647
}
591
648
 
592
649
svn_error_t *
593
 
svn_client__arbitrary_nodes_diff(const char *local_abspath1,
594
 
                                 const char *local_abspath2,
 
650
svn_client__arbitrary_nodes_diff(const char **root_relpath,
 
651
                                 svn_boolean_t *root_is_dir,
 
652
                                 const char *left_abspath,
 
653
                                 const char *right_abspath,
595
654
                                 svn_depth_t depth,
596
 
                                 const svn_wc_diff_callbacks4_t *callbacks,
597
 
                                 void *diff_baton,
 
655
                                 const svn_diff_tree_processor_t *diff_processor,
598
656
                                 svn_client_ctx_t *ctx,
 
657
                                 apr_pool_t *result_pool,
599
658
                                 apr_pool_t *scratch_pool)
600
659
{
601
 
  svn_node_kind_t kind1;
602
 
  svn_node_kind_t kind2;
603
 
 
604
 
  SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
605
 
  SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool));
606
 
 
607
 
  if (kind1 != kind2)
608
 
    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
609
 
                             _("'%s' is not the same node kind as '%s'"),
610
 
                             svn_dirent_local_style(local_abspath1,
611
 
                                                    scratch_pool),
612
 
                             svn_dirent_local_style(local_abspath2,
613
 
                                                    scratch_pool));
614
 
 
615
 
  if (depth == svn_depth_unknown)
616
 
    depth = svn_depth_infinity;
617
 
 
618
 
  if (kind1 == svn_node_file)
619
 
    SVN_ERR(do_arbitrary_files_diff(local_abspath1, local_abspath2,
620
 
                                    svn_dirent_basename(local_abspath1,
621
 
                                                        scratch_pool),
622
 
                                    FALSE, FALSE, NULL,
623
 
                                    callbacks, diff_baton,
624
 
                                    ctx, scratch_pool));
625
 
  else if (kind1 == svn_node_dir)
626
 
    SVN_ERR(do_arbitrary_dirs_diff(local_abspath1, local_abspath2,
627
 
                                   NULL, NULL, depth,
628
 
                                   callbacks, diff_baton,
629
 
                                   ctx, scratch_pool));
 
660
  svn_node_kind_t left_kind;
 
661
  svn_node_kind_t right_kind;
 
662
  const char *left_root_abspath;
 
663
  const char *right_root_abspath;
 
664
  svn_boolean_t left_before_right = TRUE; /* Future argument? */
 
665
 
 
666
  if (depth == svn_depth_unknown)
 
667
    depth = svn_depth_infinity;
 
668
 
 
669
  SVN_ERR(svn_io_check_resolved_path(left_abspath, &left_kind, scratch_pool));
 
670
  SVN_ERR(svn_io_check_resolved_path(right_abspath, &right_kind, scratch_pool));
 
671
 
 
672
  if (depth == svn_depth_unknown)
 
673
    depth = svn_depth_infinity;
 
674
 
 
675
  if (left_kind == svn_node_dir && right_kind == svn_node_dir)
 
676
    {
 
677
      left_root_abspath = left_abspath;
 
678
      right_root_abspath = right_abspath;
 
679
 
 
680
      if (root_relpath)
 
681
        *root_relpath = "";
 
682
      if (root_is_dir)
 
683
        *root_is_dir = TRUE;
 
684
    }
 
685
  else
 
686
    {
 
687
      svn_dirent_split(&left_root_abspath, root_relpath, left_abspath,
 
688
                       scratch_pool);
 
689
      right_root_abspath = svn_dirent_dirname(right_abspath, scratch_pool);
 
690
 
 
691
      if (root_relpath)
 
692
        *root_relpath = apr_pstrdup(result_pool, *root_relpath);
 
693
      if (root_is_dir)
 
694
        *root_is_dir = FALSE;
 
695
    }
 
696
 
 
697
  if (left_kind == svn_node_dir && right_kind == svn_node_dir)
 
698
    {
 
699
      SVN_ERR(do_dir_diff(left_abspath, right_abspath,
 
700
                          left_root_abspath, right_root_abspath,
 
701
                          FALSE, FALSE, left_before_right,
 
702
                          depth, NULL /* parent_baton */,
 
703
                          diff_processor, ctx, scratch_pool));
 
704
    }
 
705
  else if (left_kind == svn_node_file && right_kind == svn_node_file)
 
706
    {
 
707
      SVN_ERR(do_file_diff(left_abspath, right_abspath,
 
708
                           left_root_abspath, right_root_abspath,
 
709
                           FALSE, FALSE,
 
710
                           NULL /* parent_baton */,
 
711
                           diff_processor, ctx, scratch_pool));
 
712
    }
 
713
  else if (left_kind == svn_node_file || left_kind == svn_node_dir
 
714
           || right_kind == svn_node_file || right_kind == svn_node_dir)
 
715
    {
 
716
      void *dir_baton;
 
717
      svn_boolean_t skip = FALSE;
 
718
      svn_boolean_t skip_children = FALSE;
 
719
      svn_diff_source_t *left_src;
 
720
      svn_diff_source_t *right_src;
 
721
 
 
722
      left_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
 
723
      right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
 
724
 
 
725
      /* The root is replaced... */
 
726
      /* Report delete and/or add */
 
727
 
 
728
      SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, "",
 
729
                                         left_src,
 
730
                                         right_src,
 
731
                                         NULL /* copyfrom_src */,
 
732
                                         NULL,
 
733
                                         diff_processor,
 
734
                                         scratch_pool, scratch_pool));
 
735
 
 
736
      if (skip)
 
737
        return SVN_NO_ERROR;
 
738
      else if (!skip_children)
 
739
        {
 
740
          if (left_before_right)
 
741
            {
 
742
              if (left_kind == svn_node_file)
 
743
                SVN_ERR(do_file_diff(left_abspath, right_abspath,
 
744
                                     left_root_abspath, right_root_abspath,
 
745
                                     TRUE, FALSE, NULL /* parent_baton */,
 
746
                                     diff_processor, ctx, scratch_pool));
 
747
              else if (left_kind == svn_node_dir)
 
748
                SVN_ERR(do_dir_diff(left_abspath, right_abspath,
 
749
                                    left_root_abspath, right_root_abspath,
 
750
                                    TRUE, FALSE, left_before_right,
 
751
                                    depth, NULL /* parent_baton */,
 
752
                                    diff_processor, ctx, scratch_pool));
 
753
            }
 
754
 
 
755
          if (right_kind == svn_node_file)
 
756
            SVN_ERR(do_file_diff(left_abspath, right_abspath,
 
757
                                 left_root_abspath, right_root_abspath,
 
758
                                 FALSE, TRUE, NULL /* parent_baton */,
 
759
                                 diff_processor, ctx, scratch_pool));
 
760
          else if (right_kind == svn_node_dir)
 
761
            SVN_ERR(do_dir_diff(left_abspath, right_abspath,
 
762
                                left_root_abspath, right_root_abspath,
 
763
                                FALSE, TRUE,  left_before_right,
 
764
                                depth, NULL /* parent_baton */,
 
765
                                diff_processor, ctx, scratch_pool));
 
766
 
 
767
          if (! left_before_right)
 
768
            {
 
769
              if (left_kind == svn_node_file)
 
770
                SVN_ERR(do_file_diff(left_abspath, right_abspath,
 
771
                                     left_root_abspath, right_root_abspath,
 
772
                                     TRUE, FALSE, NULL /* parent_baton */,
 
773
                                     diff_processor, ctx, scratch_pool));
 
774
              else if (left_kind == svn_node_dir)
 
775
                SVN_ERR(do_dir_diff(left_abspath, right_abspath,
 
776
                                    left_root_abspath, right_root_abspath,
 
777
                                    TRUE, FALSE,  left_before_right,
 
778
                                    depth, NULL /* parent_baton */,
 
779
                                    diff_processor, ctx, scratch_pool));
 
780
            }
 
781
        }
 
782
 
 
783
      SVN_ERR(diff_processor->dir_closed("",
 
784
                                         left_src,
 
785
                                         right_src,
 
786
                                         dir_baton,
 
787
                                         diff_processor,
 
788
                                         scratch_pool));
 
789
    }
630
790
  else
631
791
    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
632
792
                             _("'%s' is not a file or directory"),
633
 
                             kind1 == svn_node_none
634
 
                               ? svn_dirent_local_style(local_abspath1,
635
 
                                                        scratch_pool)
636
 
                               : svn_dirent_local_style(local_abspath2,
637
 
                                                        scratch_pool));
 
793
                             svn_dirent_local_style(
 
794
                                    (left_kind == svn_node_none)
 
795
                                        ? left_abspath
 
796
                                        : right_abspath,
 
797
                                    scratch_pool));
 
798
 
638
799
  return SVN_NO_ERROR;
639
800
}