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

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/repos_diff.c

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
 
35
35
#include <apr_uri.h>
36
36
#include <apr_md5.h>
 
37
#include <assert.h>
37
38
 
38
39
#include "svn_checksum.h"
39
40
#include "svn_hash.h"
47
48
 
48
49
#include "client.h"
49
50
 
 
51
#include "private/svn_subr_private.h"
50
52
#include "private/svn_wc_private.h"
 
53
#include "private/svn_editor.h"
51
54
 
52
55
/* Overall crawler editor baton.  */
53
56
struct edit_baton {
54
 
  /* TARGET is a working-copy directory which corresponds to the base
55
 
     URL open in RA_SESSION below. */
56
 
  const char *target;
57
 
 
58
 
  /* A working copy context for TARGET, NULL if this is purely a
59
 
     repository operation. */
60
 
  svn_wc_context_t *wc_ctx;
61
 
 
62
57
  /* The passed depth */
63
58
  svn_depth_t depth;
64
59
 
65
 
  /* The callback and calback argument that implement the file comparison
66
 
     function */
67
 
  const svn_wc_diff_callbacks4_t *diff_callbacks;
68
 
  void *diff_cmd_baton;
69
 
 
70
 
  /* DRY_RUN is TRUE if this is a dry-run diff, false otherwise. */
71
 
  svn_boolean_t dry_run;
 
60
  /* The result processor */
 
61
  const svn_diff_tree_processor_t *processor;
72
62
 
73
63
  /* RA_SESSION is the open session for making requests to the RA layer */
74
64
  svn_ra_session_t *ra_session;
88
78
  /* Empty hash used for adds. */
89
79
  apr_hash_t *empty_hash;
90
80
 
91
 
  /* Hash used to check replaced paths. Key is path relative CWD,
92
 
   * Value is *deleted_path_notify_t.
93
 
   * All allocations are from edit_baton's pool. */
94
 
  apr_hash_t *deleted_paths;
95
 
 
96
 
  /* If the func is non-null, send notifications of actions. */
97
 
  svn_wc_notify_func2_t notify_func;
98
 
  void *notify_baton;
99
 
 
100
 
  /* TRUE if the operation needs to walk deleted dirs on the "old" side.
101
 
     FALSE otherwise. */
102
 
  svn_boolean_t walk_deleted_repos_dirs;
 
81
  /* Whether to report text deltas */
 
82
  svn_boolean_t text_deltas;
103
83
 
104
84
  /* A callback used to see if the client wishes to cancel the running
105
85
     operation. */
140
120
  /* The path of the directory within the repository */
141
121
  const char *path;
142
122
 
143
 
  /* The path of the directory in the wc, relative to cwd */
144
 
  const char *wcpath;
145
 
 
146
123
  /* The baton for the parent directory, or null if this is the root of the
147
124
     hierarchy to be compared. */
148
 
  struct dir_baton *dir_baton;
 
125
  struct dir_baton *parent_baton;
149
126
 
150
127
  /* The overall crawler editor baton. */
151
128
  struct edit_baton *edit_baton;
153
130
  /* A cache of any property changes (svn_prop_t) received for this dir. */
154
131
  apr_array_header_t *propchanges;
155
132
 
156
 
  /* The pristine-property list attached to this directory. */
157
 
  apr_hash_t *pristine_props;
 
133
  /* Boolean indicating whether a node property was changed */
 
134
  svn_boolean_t has_propchange;
 
135
 
 
136
  /* Baton for svn_diff_tree_processor_t */
 
137
  void *pdb;
 
138
  svn_diff_source_t *left_source;
 
139
  svn_diff_source_t *right_source;
158
140
 
159
141
  /* The pool passed in by add_dir, open_dir, or open_root.
160
142
     Also, the pool this dir baton is allocated in. */
161
143
  apr_pool_t *pool;
 
144
 
 
145
  /* Base revision of directory. */
 
146
  svn_revnum_t base_revision;
 
147
 
 
148
  /* Number of users of baton. Its pool will be destroyed 0 */
 
149
  int users;
162
150
};
163
151
 
164
152
/* File level baton.
165
153
 */
166
154
struct file_baton {
 
155
  /* Reference to parent baton */
 
156
  struct dir_baton *parent_baton;
 
157
 
167
158
  /* Gets set if the file is added rather than replaced. */
168
159
  svn_boolean_t added;
169
160
 
179
170
  /* The path of the file within the repository */
180
171
  const char *path;
181
172
 
182
 
  /* The path of the file in the wc, relative to cwd */
183
 
  const char *wcpath;
184
 
 
185
173
  /* The path and APR file handle to the temporary file that contains the
186
174
     first repository version.  Also, the pristine-property list of
187
175
     this file. */
212
200
  /* A cache of any property changes (svn_prop_t) received for this file. */
213
201
  apr_array_header_t *propchanges;
214
202
 
 
203
  /* Boolean indicating whether a node property was changed */
 
204
  svn_boolean_t has_propchange;
 
205
 
 
206
  /* Baton for svn_diff_tree_processor_t */
 
207
  void *pfb;
 
208
  svn_diff_source_t *left_source;
 
209
  svn_diff_source_t *right_source;
 
210
 
215
211
  /* The pool passed in by add_file or open_file.
216
212
     Also, the pool this file_baton is allocated in. */
217
213
  apr_pool_t *pool;
229
225
               struct dir_baton *parent_baton,
230
226
               struct edit_baton *edit_baton,
231
227
               svn_boolean_t added,
232
 
               apr_pool_t *pool)
 
228
               svn_revnum_t base_revision,
 
229
               apr_pool_t *result_pool)
233
230
{
234
 
  apr_pool_t *dir_pool = svn_pool_create(pool);
 
231
  apr_pool_t *dir_pool = svn_pool_create(result_pool);
235
232
  struct dir_baton *dir_baton = apr_pcalloc(dir_pool, sizeof(*dir_baton));
236
233
 
237
 
  dir_baton->dir_baton = parent_baton;
 
234
  dir_baton->parent_baton = parent_baton;
238
235
  dir_baton->edit_baton = edit_baton;
239
236
  dir_baton->added = added;
240
237
  dir_baton->tree_conflicted = FALSE;
242
239
  dir_baton->skip_children = FALSE;
243
240
  dir_baton->pool = dir_pool;
244
241
  dir_baton->path = apr_pstrdup(dir_pool, path);
245
 
  dir_baton->wcpath = svn_dirent_join(edit_baton->target, path, dir_pool);
246
 
  dir_baton->propchanges  = apr_array_make(pool, 1, sizeof(svn_prop_t));
 
242
  dir_baton->propchanges  = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
 
243
  dir_baton->base_revision = base_revision;
 
244
  dir_baton->users++;
 
245
 
 
246
  if (parent_baton)
 
247
    parent_baton->users++;
247
248
 
248
249
  return dir_baton;
249
250
}
250
251
 
 
252
/* New function. Called by everyone who has a reference when done */
 
253
static svn_error_t *
 
254
release_dir(struct dir_baton *db)
 
255
{
 
256
  assert(db->users > 0);
 
257
 
 
258
  db->users--;
 
259
  if (db->users)
 
260
     return SVN_NO_ERROR;
 
261
 
 
262
  {
 
263
    struct dir_baton *pb = db->parent_baton;
 
264
 
 
265
    svn_pool_destroy(db->pool);
 
266
 
 
267
    if (pb != NULL)
 
268
      SVN_ERR(release_dir(pb));
 
269
  }
 
270
 
 
271
  return SVN_NO_ERROR;
 
272
}
 
273
 
251
274
/* Create a new file baton for PATH in POOL, which is a child of
252
275
 * directory PARENT_PATH. ADDED is set if this file is being added
253
276
 * rather than replaced.  EDIT_BATON is a pointer to the global edit
255
278
 */
256
279
static struct file_baton *
257
280
make_file_baton(const char *path,
 
281
                struct dir_baton *parent_baton,
258
282
                svn_boolean_t added,
259
 
                struct edit_baton *edit_baton,
260
 
                apr_pool_t *pool)
 
283
                apr_pool_t *result_pool)
261
284
{
262
 
  apr_pool_t *file_pool = svn_pool_create(pool);
 
285
  apr_pool_t *file_pool = svn_pool_create(result_pool);
263
286
  struct file_baton *file_baton = apr_pcalloc(file_pool, sizeof(*file_baton));
264
287
 
265
 
  file_baton->edit_baton = edit_baton;
 
288
  file_baton->parent_baton = parent_baton;
 
289
  file_baton->edit_baton = parent_baton->edit_baton;
266
290
  file_baton->added = added;
267
291
  file_baton->tree_conflicted = FALSE;
268
292
  file_baton->skip = FALSE;
269
293
  file_baton->pool = file_pool;
270
294
  file_baton->path = apr_pstrdup(file_pool, path);
271
 
  file_baton->wcpath = svn_dirent_join(edit_baton->target, path, file_pool);
272
 
  file_baton->propchanges  = apr_array_make(pool, 1, sizeof(svn_prop_t));
273
 
  file_baton->base_revision = edit_baton->revision;
 
295
  file_baton->propchanges  = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
 
296
  file_baton->base_revision = parent_baton->edit_baton->revision;
 
297
 
 
298
  parent_baton->users++;
274
299
 
275
300
  return file_baton;
276
301
}
277
302
 
278
 
/* Helper function: return up to two svn:mime-type values buried
279
 
 * within a file baton.  Set *MIMETYPE1 to the value within the file's
280
 
 * pristine properties, or NULL if not available.  Set *MIMETYPE2 to
281
 
 * the value within the "new" file's propchanges, or NULL if not
282
 
 * available.
283
 
 */
284
 
static void
285
 
get_file_mime_types(const char **mimetype1,
286
 
                    const char **mimetype2,
287
 
                    struct file_baton *b)
288
 
{
289
 
  /* Defaults */
290
 
  *mimetype1 = NULL;
291
 
  *mimetype2 = NULL;
292
 
 
293
 
  if (b->pristine_props)
294
 
    {
295
 
      svn_string_t *pristine_val;
296
 
      pristine_val = apr_hash_get(b->pristine_props, SVN_PROP_MIME_TYPE,
297
 
                                  strlen(SVN_PROP_MIME_TYPE));
298
 
      if (pristine_val)
299
 
        *mimetype2 = *mimetype1 = pristine_val->data;
300
 
    }
301
 
 
302
 
  if (b->propchanges)
303
 
    {
304
 
      int i;
305
 
      svn_prop_t *propchange;
306
 
 
307
 
      for (i = 0; i < b->propchanges->nelts; i++)
308
 
        {
309
 
          propchange = &APR_ARRAY_IDX(b->propchanges, i, svn_prop_t);
310
 
          if (strcmp(propchange->name, SVN_PROP_MIME_TYPE) == 0)
311
 
            {
312
 
              if (propchange->value)
313
 
                *mimetype2 = propchange->value->data;
314
 
              break;
315
 
            }
316
 
        }
317
 
    }
318
 
}
319
 
 
320
 
 
321
 
/* Get revision REVISION of the file described by B from the repository.
322
 
 * Set B->path_start_revision to the path of a new temporary file containing
323
 
 * the file's text.  Set B->pristine_props to a new hash containing the
324
 
 * file's properties.  Install a pool cleanup handler on B->pool to delete
325
 
 * the file.
 
303
/* Get revision FB->base_revision of the file described by FB from the
 
304
 * repository, through FB->edit_baton->ra_session.
 
305
 *
 
306
 * Unless PROPS_ONLY is true:
 
307
 *   Set FB->path_start_revision to the path of a new temporary file containing
 
308
 *   the file's text.
 
309
 *   Set FB->start_md5_checksum to that file's MD-5 checksum.
 
310
 *   Install a pool cleanup handler on FB->pool to delete the file.
 
311
 *
 
312
 * Always:
 
313
 *   Set FB->pristine_props to a new hash containing the file's properties.
 
314
 *
 
315
 * Allocate all results in FB->pool.
326
316
 */
327
317
static svn_error_t *
328
 
get_file_from_ra(struct file_baton *b,
 
318
get_file_from_ra(struct file_baton *fb,
329
319
                 svn_boolean_t props_only,
330
320
                 apr_pool_t *scratch_pool)
331
321
{
333
323
    {
334
324
      svn_stream_t *fstream;
335
325
 
336
 
      SVN_ERR(svn_stream_open_unique(&fstream, &(b->path_start_revision), NULL,
337
 
                                     svn_io_file_del_on_pool_cleanup, b->pool,
338
 
                                     b->pool));
 
326
      SVN_ERR(svn_stream_open_unique(&fstream, &(fb->path_start_revision),
 
327
                                     NULL, svn_io_file_del_on_pool_cleanup,
 
328
                                     fb->pool, scratch_pool));
339
329
 
340
 
      fstream = svn_stream_checksummed2(fstream, NULL, &b->start_md5_checksum,
341
 
                                        svn_checksum_md5, TRUE, b->pool);
 
330
      fstream = svn_stream_checksummed2(fstream, NULL, &fb->start_md5_checksum,
 
331
                                        svn_checksum_md5, TRUE, scratch_pool);
342
332
 
343
333
      /* Retrieve the file and its properties */
344
 
      SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
345
 
                              b->path,
346
 
                              b->base_revision,
 
334
      SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
 
335
                              fb->path,
 
336
                              fb->base_revision,
347
337
                              fstream, NULL,
348
 
                              &(b->pristine_props),
349
 
                              b->pool));
 
338
                              &(fb->pristine_props),
 
339
                              fb->pool));
350
340
      SVN_ERR(svn_stream_close(fstream));
351
341
    }
352
342
  else
353
343
    {
354
 
      SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
355
 
                              b->path,
356
 
                              b->base_revision,
 
344
      SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
 
345
                              fb->path,
 
346
                              fb->base_revision,
357
347
                              NULL, NULL,
358
 
                              &(b->pristine_props),
359
 
                              b->pool));
 
348
                              &(fb->pristine_props),
 
349
                              fb->pool));
360
350
    }
361
351
 
362
352
  return SVN_NO_ERROR;
363
353
}
364
354
 
365
 
/* Issue #3657 'dav update report handler in skelta mode can cause
366
 
     spurious conflicts'.  When communicating with the repository via ra_serf
367
 
     and ra_neon, the change_dir_prop and change_file_prop svn_delta_editor_t
 
355
/* Remove every no-op property change from CHANGES: that is, remove every
 
356
   entry in which the target value is the same as the value of the
 
357
   corresponding property in PRISTINE_PROPS.
 
358
 
 
359
     Issue #3657 'dav update report handler in skelta mode can cause
 
360
     spurious conflicts'.  When communicating with the repository via ra_serf,
 
361
     the change_dir_prop and change_file_prop svn_delta_editor_t
368
362
     callbacks are called (obviously) when a directory or file property has
369
363
     changed between the start and end of the edit.  Less obvious however,
370
364
     is that these callbacks may be made describing *all* of the properties
371
365
     on FILE_BATON->PATH when using the DAV providers, not just the change(s).
372
 
     (Specifically ra_neon does this for diff/merge and ra_serf does it
373
 
     for diff/merge/update/switch).
 
366
     (Specifically ra_serf does it for diff/merge/update/switch).
374
367
 
375
368
     This means that the change_[file|dir]_prop svn_delta_editor_t callbacks
376
369
     may be made where there are no property changes (i.e. a noop change of
389
382
 
390
383
     See http://subversion.tigris.org/issues/show_bug.cgi?id=3657#desc9 and
391
384
     http://svn.haxx.se/dev/archive-2010-08/0351.shtml for more details.
392
 
 
393
 
     This function filters these property changes from the change hash
394
385
 */
395
386
static void
396
387
remove_non_prop_changes(apr_hash_t *pristine_props,
404
395
 
405
396
      if (change->value)
406
397
        {
407
 
          const svn_string_t *old_val = apr_hash_get(pristine_props,
408
 
                                                     change->name,
409
 
                                                     APR_HASH_KEY_STRING);
 
398
          const svn_string_t *old_val = svn_hash_gets(pristine_props,
 
399
                                                      change->name);
410
400
 
411
401
          if (old_val && svn_string_compare(old_val, change->value))
412
402
            {
424
414
    }
425
415
}
426
416
 
427
 
/* Get the props attached to a directory in the repository at BASE_REVISION. */
428
 
static svn_error_t *
429
 
get_dirprops_from_ra(struct dir_baton *b, svn_revnum_t base_revision)
430
 
{
431
 
  return svn_ra_get_dir2(b->edit_baton->ra_session,
432
 
                         NULL, NULL, &(b->pristine_props),
433
 
                         b->path,
434
 
                         base_revision,
435
 
                         0,
436
 
                         b->pool);
437
 
}
438
 
 
439
417
/* Get the empty file associated with the edit baton. This is cached so
440
418
 * that it can be reused, all empty files are the same.
441
419
 */
456
434
  return SVN_NO_ERROR;
457
435
}
458
436
 
459
 
/* An editor function. The root of the comparison hierarchy */
 
437
/* An svn_delta_editor_t function.  */
460
438
static svn_error_t *
461
439
set_target_revision(void *edit_baton,
462
440
                    svn_revnum_t target_revision,
468
446
  return SVN_NO_ERROR;
469
447
}
470
448
 
471
 
/* An editor function. The root of the comparison hierarchy */
 
449
/* An svn_delta_editor_t function. The root of the comparison hierarchy */
472
450
static svn_error_t *
473
451
open_root(void *edit_baton,
474
452
          svn_revnum_t base_revision,
476
454
          void **root_baton)
477
455
{
478
456
  struct edit_baton *eb = edit_baton;
479
 
  struct dir_baton *b = make_dir_baton("", NULL, eb, FALSE, pool);
480
 
 
481
 
  /* Override the wcpath in our baton. */
482
 
  b->wcpath = apr_pstrdup(pool, eb->target);
483
 
 
484
 
  SVN_ERR(get_dirprops_from_ra(b, base_revision));
485
 
 
486
 
  *root_baton = b;
487
 
  return SVN_NO_ERROR;
488
 
}
489
 
 
490
 
/* Recursively walk tree rooted at DIR (at REVISION) in the repository,
491
 
 * reporting all files as deleted.  Part of a workaround for issue 2333.
 
457
  struct dir_baton *db = make_dir_baton("", NULL, eb, FALSE, base_revision,
 
458
                                        eb->pool);
 
459
 
 
460
  db->left_source = svn_diff__source_create(eb->revision, db->pool);
 
461
  db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
 
462
 
 
463
  SVN_ERR(eb->processor->dir_opened(&db->pdb,
 
464
                                    &db->skip,
 
465
                                    &db->skip_children,
 
466
                                    "",
 
467
                                    db->left_source,
 
468
                                    db->right_source,
 
469
                                    NULL,
 
470
                                    NULL,
 
471
                                    eb->processor,
 
472
                                    db->pool,
 
473
                                    db->pool /* scratch_pool */));
 
474
 
 
475
  *root_baton = db;
 
476
  return SVN_NO_ERROR;
 
477
}
 
478
 
 
479
/* Compare a file being deleted against an empty file.
 
480
 */
 
481
static svn_error_t *
 
482
diff_deleted_file(const char *path,
 
483
                  struct dir_baton *db,
 
484
                  apr_pool_t *scratch_pool)
 
485
{
 
486
  struct edit_baton *eb = db->edit_baton;
 
487
  struct file_baton *fb = make_file_baton(path, db, FALSE, scratch_pool);
 
488
  svn_boolean_t skip = FALSE;
 
489
  svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
 
490
                                                           scratch_pool);
 
491
 
 
492
  if (eb->cancel_func)
 
493
    SVN_ERR(eb->cancel_func(eb->cancel_baton));
 
494
 
 
495
  SVN_ERR(eb->processor->file_opened(&fb->pfb, &skip, path,
 
496
                                     left_source,
 
497
                                     NULL /* right_source */,
 
498
                                     NULL /* copyfrom_source */,
 
499
                                     db->pdb,
 
500
                                     eb->processor,
 
501
                                     scratch_pool, scratch_pool));
 
502
 
 
503
  if (eb->cancel_func)
 
504
    SVN_ERR(eb->cancel_func(eb->cancel_baton));
 
505
 
 
506
  if (skip)
 
507
    return SVN_NO_ERROR;
 
508
 
 
509
  SVN_ERR(get_file_from_ra(fb, ! eb->text_deltas, scratch_pool));
 
510
 
 
511
  SVN_ERR(eb->processor->file_deleted(fb->path,
 
512
                                      left_source,
 
513
                                      fb->path_start_revision,
 
514
                                      fb->pristine_props,
 
515
                                      fb->pfb,
 
516
                                      eb->processor,
 
517
                                      scratch_pool));
 
518
 
 
519
  return SVN_NO_ERROR;
 
520
}
 
521
 
 
522
/* Recursively walk tree rooted at DIR (at EB->revision) in the repository,
 
523
 * reporting all children as deleted.  Part of a workaround for issue 2333.
492
524
 *
493
 
 * DIR is a repository path relative to the URL in RA_SESSION.  REVISION
494
 
 * must be a valid revision number, not SVN_INVALID_REVNUM.  EB is the
495
 
 * overall crawler editor baton.  If CANCEL_FUNC is not NULL, then it
496
 
 * should refer to a cancellation function (along with CANCEL_BATON).
 
525
 * DIR is a repository path relative to the URL in EB->ra_session.  EB is
 
526
 * the overall crawler editor baton.  EB->revision must be a valid revision
 
527
 * number, not SVN_INVALID_REVNUM.  Use EB->cancel_func (if not null) with
 
528
 * EB->cancel_baton for cancellation.
497
529
 */
498
530
/* ### TODO: Handle depth. */
499
531
static svn_error_t *
500
 
diff_deleted_dir(const char *dir,
501
 
                 svn_revnum_t revision,
502
 
                 svn_ra_session_t *ra_session,
503
 
                 struct edit_baton *eb,
504
 
                 svn_cancel_func_t cancel_func,
505
 
                 void *cancel_baton,
506
 
                 apr_pool_t *pool)
 
532
diff_deleted_dir(const char *path,
 
533
                 struct dir_baton *pb,
 
534
                 apr_pool_t *scratch_pool)
507
535
{
508
 
  apr_hash_t *dirents;
509
 
  apr_pool_t *iterpool = svn_pool_create(pool);
510
 
  apr_hash_index_t *hi;
511
 
 
512
 
  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
513
 
 
514
 
  if (cancel_func)
515
 
    SVN_ERR(cancel_func(cancel_baton));
516
 
 
517
 
  SVN_ERR(svn_ra_get_dir2(ra_session,
518
 
                          &dirents,
519
 
                          NULL, NULL,
520
 
                          dir,
521
 
                          revision,
522
 
                          SVN_DIRENT_KIND,
523
 
                          pool));
524
 
 
525
 
  for (hi = apr_hash_first(pool, dirents); hi;
526
 
       hi = apr_hash_next(hi))
 
536
  struct edit_baton *eb = pb->edit_baton;
 
537
  struct dir_baton *db;
 
538
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
539
  svn_boolean_t skip = FALSE;
 
540
  svn_boolean_t skip_children = FALSE;
 
541
  apr_hash_t *dirents = NULL;
 
542
  apr_hash_t *left_props = NULL;
 
543
  svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
 
544
                                                           scratch_pool);
 
545
  db = make_dir_baton(path, pb, pb->edit_baton, FALSE, SVN_INVALID_REVNUM,
 
546
                      scratch_pool);
 
547
 
 
548
  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(eb->revision));
 
549
 
 
550
  if (eb->cancel_func)
 
551
    SVN_ERR(eb->cancel_func(eb->cancel_baton));
 
552
 
 
553
  SVN_ERR(eb->processor->dir_opened(&db->pdb, &skip, &skip_children,
 
554
                                    path,
 
555
                                    left_source,
 
556
                                    NULL /* right_source */,
 
557
                                    NULL /* copyfrom_source */,
 
558
                                    pb->pdb,
 
559
                                    eb->processor,
 
560
                                    scratch_pool, iterpool));
 
561
 
 
562
  if (!skip || !skip_children)
 
563
    SVN_ERR(svn_ra_get_dir2(eb->ra_session,
 
564
                            skip_children ? NULL : &dirents,
 
565
                            NULL,
 
566
                            skip ? NULL : &left_props,
 
567
                            path,
 
568
                            eb->revision,
 
569
                            SVN_DIRENT_KIND,
 
570
                            scratch_pool));
 
571
 
 
572
  /* The "old" dir will be skipped by the repository report.  If required,
 
573
   * crawl it recursively, diffing each file against the empty file.  This
 
574
   * is a workaround for issue 2333 "'svn diff URL1 URL2' not reverse of
 
575
   * 'svn diff URL2 URL1'". */
 
576
  if (! skip_children)
527
577
    {
528
 
      const char *path;
529
 
      const char *name = svn__apr_hash_index_key(hi);
530
 
      svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
531
 
 
532
 
      svn_pool_clear(iterpool);
533
 
 
534
 
      path = svn_relpath_join(dir, name, iterpool);
535
 
 
536
 
      if (dirent->kind == svn_node_file)
 
578
      apr_hash_index_t *hi;
 
579
 
 
580
      for (hi = apr_hash_first(scratch_pool, dirents); hi;
 
581
           hi = apr_hash_next(hi))
537
582
        {
538
 
          struct file_baton *b;
539
 
          const char *mimetype1, *mimetype2;
540
 
 
541
 
          /* Compare a file being deleted against an empty file */
542
 
          b = make_file_baton(path, FALSE, eb, iterpool);
543
 
          SVN_ERR(get_file_from_ra(b, FALSE, iterpool));
544
 
 
545
 
          SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
546
 
 
547
 
          get_file_mime_types(&mimetype1, &mimetype2, b);
548
 
 
549
 
          SVN_ERR(eb->diff_callbacks->file_deleted(
550
 
                                NULL, NULL, b->wcpath,
551
 
                                b->path_start_revision,
552
 
                                b->path_end_revision,
553
 
                                mimetype1, mimetype2,
554
 
                                b->pristine_props,
555
 
                                b->edit_baton->diff_cmd_baton,
556
 
                                iterpool));
 
583
          const char *child_path;
 
584
          const char *name = svn__apr_hash_index_key(hi);
 
585
          svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
 
586
 
 
587
          svn_pool_clear(iterpool);
 
588
 
 
589
          child_path = svn_relpath_join(path, name, iterpool);
 
590
 
 
591
          if (dirent->kind == svn_node_file)
 
592
            {
 
593
              SVN_ERR(diff_deleted_file(child_path, db, iterpool));
 
594
            }
 
595
          else if (dirent->kind == svn_node_dir)
 
596
            {
 
597
              SVN_ERR(diff_deleted_dir(child_path, db, iterpool));
 
598
            }
557
599
        }
558
 
 
559
 
      if (dirent->kind == svn_node_dir)
560
 
        SVN_ERR(diff_deleted_dir(path,
561
 
                                 revision,
562
 
                                 ra_session,
563
 
                                 eb,
564
 
                                 cancel_func,
565
 
                                 cancel_baton,
566
 
                                 iterpool));
567
 
    }
 
600
    }
 
601
 
 
602
  if (! skip)
 
603
    {
 
604
      SVN_ERR(eb->processor->dir_deleted(path,
 
605
                                         left_source,
 
606
                                         left_props,
 
607
                                         db->pdb,
 
608
                                         eb->processor,
 
609
                                         scratch_pool));
 
610
    }
 
611
 
 
612
  SVN_ERR(release_dir(db));
568
613
 
569
614
  svn_pool_destroy(iterpool);
570
615
  return SVN_NO_ERROR;
571
616
}
572
617
 
573
 
/* An editor function.  */
 
618
/* An svn_delta_editor_t function.  */
574
619
static svn_error_t *
575
620
delete_entry(const char *path,
576
621
             svn_revnum_t base_revision,
580
625
  struct dir_baton *pb = parent_baton;
581
626
  struct edit_baton *eb = pb->edit_baton;
582
627
  svn_node_kind_t kind;
583
 
  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
584
 
  svn_wc_notify_action_t action = svn_wc_notify_skip;
585
 
  svn_boolean_t tree_conflicted = FALSE;
586
628
  apr_pool_t *scratch_pool;
587
629
 
588
 
  /* Skip *everything* within a newly tree-conflicted directory,
589
 
   * and directories the children of which should be skipped. */
590
 
  if (pb->skip || pb->tree_conflicted || pb->skip_children)
 
630
  /* Process skips. */
 
631
  if (pb->skip_children)
591
632
    return SVN_NO_ERROR;
592
633
 
593
634
  scratch_pool = svn_pool_create(eb->pool);
600
641
    {
601
642
    case svn_node_file:
602
643
      {
603
 
        const char *mimetype1, *mimetype2;
604
 
        struct file_baton *b;
605
 
 
606
 
        /* Compare a file being deleted against an empty file */
607
 
        b = make_file_baton(path, FALSE, eb, scratch_pool);
608
 
        SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool));
609
 
        SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
610
 
 
611
 
        get_file_mime_types(&mimetype1, &mimetype2, b);
612
 
 
613
 
        SVN_ERR(eb->diff_callbacks->file_deleted(
614
 
                     &state, &tree_conflicted, b->wcpath,
615
 
                     b->path_start_revision,
616
 
                     b->path_end_revision,
617
 
                     mimetype1, mimetype2,
618
 
                     b->pristine_props,
619
 
                     b->edit_baton->diff_cmd_baton,
620
 
                     scratch_pool));
621
 
 
 
644
        SVN_ERR(diff_deleted_file(path, pb, scratch_pool));
622
645
        break;
623
646
      }
624
647
    case svn_node_dir:
625
648
      {
626
 
        SVN_ERR(eb->diff_callbacks->dir_deleted(
627
 
                     &state, &tree_conflicted,
628
 
                     svn_dirent_join(eb->target, path, pool),
629
 
                     eb->diff_cmd_baton, scratch_pool));
630
 
 
631
 
        if (eb->walk_deleted_repos_dirs)
632
 
          {
633
 
            /* A workaround for issue 2333.  The "old" dir will be
634
 
            skipped by the repository report.  Crawl it recursively,
635
 
            diffing each file against the empty file. */
636
 
            SVN_ERR(diff_deleted_dir(path,
637
 
                                     eb->revision,
638
 
                                     eb->ra_session,
639
 
                                     eb,
640
 
                                     eb->cancel_func,
641
 
                                     eb->cancel_baton,
642
 
                                     scratch_pool));
643
 
          }
 
649
        SVN_ERR(diff_deleted_dir(path, pb, scratch_pool));
644
650
        break;
645
651
      }
646
652
    default:
647
653
      break;
648
654
    }
649
655
 
650
 
  if ((state != svn_wc_notify_state_missing)
651
 
      && (state != svn_wc_notify_state_obstructed)
652
 
      && !tree_conflicted)
653
 
    {
654
 
      action = svn_wc_notify_update_delete;
655
 
    }
656
 
 
657
 
  if (eb->notify_func)
658
 
    {
659
 
      const char* deleted_path;
660
 
      deleted_path_notify_t *dpn = apr_pcalloc(eb->pool, sizeof(*dpn));
661
 
      deleted_path = svn_dirent_join(eb->target, path, eb->pool);
662
 
      dpn->kind = kind;
663
 
      dpn->action = tree_conflicted ? svn_wc_notify_tree_conflict : action;
664
 
      dpn->state = state;
665
 
      dpn->tree_conflicted = tree_conflicted;
666
 
      apr_hash_set(eb->deleted_paths, deleted_path, APR_HASH_KEY_STRING, dpn);
667
 
    }
668
 
 
669
656
  svn_pool_destroy(scratch_pool);
670
657
 
671
658
  return SVN_NO_ERROR;
672
659
}
673
660
 
674
 
/* An editor function.  */
 
661
/* An svn_delta_editor_t function.  */
675
662
static svn_error_t *
676
663
add_directory(const char *path,
677
664
              void *parent_baton,
682
669
{
683
670
  struct dir_baton *pb = parent_baton;
684
671
  struct edit_baton *eb = pb->edit_baton;
685
 
  struct dir_baton *b;
686
 
  svn_wc_notify_state_t state;
 
672
  struct dir_baton *db;
687
673
 
688
674
  /* ### TODO: support copyfrom? */
689
675
 
690
 
  b = make_dir_baton(path, pb, eb, TRUE, pool);
691
 
  b->pristine_props = eb->empty_hash;
692
 
  *child_baton = b;
 
676
  db = make_dir_baton(path, pb, eb, TRUE, SVN_INVALID_REVNUM, pb->pool);
 
677
  *child_baton = db;
693
678
 
694
679
  /* Skip *everything* within a newly tree-conflicted directory,
695
680
   * and directories the children of which should be skipped. */
696
 
  if (pb->skip || pb->tree_conflicted || pb->skip_children)
 
681
  if (pb->skip_children)
697
682
    {
698
 
      b->skip = TRUE;
 
683
      db->skip = TRUE;
 
684
      db->skip_children = TRUE;
699
685
      return SVN_NO_ERROR;
700
686
    }
701
687
 
702
 
 
703
 
  SVN_ERR(eb->diff_callbacks->dir_added(
704
 
                &state, &b->tree_conflicted,
705
 
                &b->skip, &b->skip_children, b->wcpath,
706
 
                eb->target_revision, copyfrom_path, copyfrom_revision,
707
 
                eb->diff_cmd_baton, pool));
708
 
 
709
 
  /* Notifications for directories are done at close_directory time.
710
 
   * But for paths at which the editor drive adds directories, we make an
711
 
   * exception to this rule, so that the path appears in the output before
712
 
   * any children of the newly added directory. Since a deletion at this path
713
 
   * must have happened before this addition, we can safely notify about
714
 
   * replaced directories here, too. */
715
 
  if (eb->notify_func)
716
 
    {
717
 
      deleted_path_notify_t *dpn;
718
 
      svn_wc_notify_t *notify;
719
 
      svn_wc_notify_action_t action;
720
 
      svn_node_kind_t kind = svn_node_dir;
721
 
 
722
 
      /* Find out if a pending delete notification for this path is
723
 
       * still around. */
724
 
      dpn = apr_hash_get(eb->deleted_paths, b->wcpath, APR_HASH_KEY_STRING);
725
 
      if (dpn)
726
 
        {
727
 
          /* If any was found, we will handle the pending 'deleted path
728
 
           * notification' (DPN) here. Remove it from the list. */
729
 
          apr_hash_set(eb->deleted_paths, b->wcpath,
730
 
                       APR_HASH_KEY_STRING, NULL);
731
 
 
732
 
          /* the pending delete might be on a different node kind. */
733
 
          kind = dpn->kind;
734
 
          state = dpn->state;
735
 
        }
736
 
 
737
 
      /* Determine what the notification (ACTION) should be.
738
 
       * In case of a pending 'delete', this might become a 'replace'. */
739
 
      if (b->tree_conflicted)
740
 
        action = svn_wc_notify_tree_conflict;
741
 
      else if (dpn)
742
 
        {
743
 
          if (dpn->action == svn_wc_notify_update_delete)
744
 
            action = svn_wc_notify_update_replace;
745
 
          else
746
 
            /* Note: dpn->action might be svn_wc_notify_tree_conflict */
747
 
            action = dpn->action;
748
 
        }
749
 
      else if (state == svn_wc_notify_state_missing ||
750
 
               state == svn_wc_notify_state_obstructed)
751
 
        action = svn_wc_notify_skip;
752
 
      else
753
 
        action = svn_wc_notify_update_add;
754
 
 
755
 
      notify = svn_wc_create_notify(b->wcpath, action, pool);
756
 
      notify->kind = kind;
757
 
      notify->content_state = notify->prop_state = state;
758
 
      (*eb->notify_func)(eb->notify_baton, notify, pool);
759
 
    }
 
688
  db->right_source = svn_diff__source_create(eb->target_revision,
 
689
                                             db->pool);
 
690
 
 
691
  SVN_ERR(eb->processor->dir_opened(&db->pdb,
 
692
                                    &db->skip,
 
693
                                    &db->skip_children,
 
694
                                    db->path,
 
695
                                    NULL,
 
696
                                    db->right_source,
 
697
                                    NULL /* copyfrom_source */,
 
698
                                    pb->pdb,
 
699
                                    eb->processor,
 
700
                                    db->pool, db->pool));
760
701
 
761
702
  return SVN_NO_ERROR;
762
703
}
763
704
 
764
 
/* An editor function.  */
 
705
/* An svn_delta_editor_t function.  */
765
706
static svn_error_t *
766
707
open_directory(const char *path,
767
708
               void *parent_baton,
771
712
{
772
713
  struct dir_baton *pb = parent_baton;
773
714
  struct edit_baton *eb = pb->edit_baton;
774
 
  struct dir_baton *b;
775
 
 
776
 
  b = make_dir_baton(path, pb, pb->edit_baton, FALSE, pool);
777
 
  *child_baton = b;
778
 
 
779
 
  /* Skip *everything* within a newly tree-conflicted directory
780
 
   * and directories the children of which should be skipped. */
781
 
  if (pb->skip || pb->tree_conflicted || pb->skip_children)
 
715
  struct dir_baton *db;
 
716
 
 
717
  db = make_dir_baton(path, pb, eb, FALSE, base_revision, pb->pool);
 
718
 
 
719
  *child_baton = db;
 
720
 
 
721
  /* Process Skips. */
 
722
  if (pb->skip_children)
782
723
    {
783
 
      b->skip = TRUE;
 
724
      db->skip = TRUE;
 
725
      db->skip_children = TRUE;
784
726
      return SVN_NO_ERROR;
785
727
    }
786
728
 
787
 
  SVN_ERR(get_dirprops_from_ra(b, base_revision));
 
729
  db->left_source = svn_diff__source_create(eb->revision, db->pool);
 
730
  db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
788
731
 
789
 
  SVN_ERR(eb->diff_callbacks->dir_opened(
790
 
                &b->tree_conflicted, &b->skip,
791
 
                &b->skip_children, b->wcpath, base_revision,
792
 
                b->edit_baton->diff_cmd_baton, pool));
 
732
  SVN_ERR(eb->processor->dir_opened(&db->pdb,
 
733
                                    &db->skip, &db->skip_children,
 
734
                                    path,
 
735
                                    db->left_source,
 
736
                                    db->right_source,
 
737
                                    NULL /* copyfrom */,
 
738
                                    pb ? pb->pdb : NULL,
 
739
                                    eb->processor,
 
740
                                    db->pool, db->pool));
793
741
 
794
742
  return SVN_NO_ERROR;
795
743
}
796
744
 
797
745
 
798
 
/* An editor function.  */
 
746
/* An svn_delta_editor_t function.  */
799
747
static svn_error_t *
800
748
add_file(const char *path,
801
749
         void *parent_baton,
805
753
         void **file_baton)
806
754
{
807
755
  struct dir_baton *pb = parent_baton;
808
 
  struct file_baton *b;
 
756
  struct edit_baton *eb = pb->edit_baton;
 
757
  struct file_baton *fb;
809
758
 
810
759
  /* ### TODO: support copyfrom? */
811
760
 
812
 
  b = make_file_baton(path, TRUE, pb->edit_baton, pool);
813
 
  *file_baton = b;
 
761
  fb = make_file_baton(path, pb, TRUE, pb->pool);
 
762
  *file_baton = fb;
814
763
 
815
 
  /* Skip *everything* within a newly tree-conflicted directory.
816
 
   * and directories the children of which should be skipped. */
817
 
  if (pb->skip || pb->tree_conflicted || pb->skip_children)
 
764
  /* Process Skips. */
 
765
  if (pb->skip_children)
818
766
    {
819
 
      b->skip = TRUE;
 
767
      fb->skip = TRUE;
820
768
      return SVN_NO_ERROR;
821
769
    }
822
770
 
823
 
  b->pristine_props = pb->edit_baton->empty_hash;
 
771
  fb->pristine_props = pb->edit_baton->empty_hash;
 
772
 
 
773
  fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool);
 
774
 
 
775
  SVN_ERR(eb->processor->file_opened(&fb->pfb,
 
776
                                     &fb->skip,
 
777
                                     path,
 
778
                                     NULL,
 
779
                                     fb->right_source,
 
780
                                     NULL /* copy source */,
 
781
                                     pb->pdb,
 
782
                                     eb->processor,
 
783
                                     fb->pool, fb->pool));
824
784
 
825
785
  return SVN_NO_ERROR;
826
786
}
827
787
 
828
 
/* An editor function.  */
 
788
/* An svn_delta_editor_t function.  */
829
789
static svn_error_t *
830
790
open_file(const char *path,
831
791
          void *parent_baton,
834
794
          void **file_baton)
835
795
{
836
796
  struct dir_baton *pb = parent_baton;
837
 
  struct file_baton *b;
 
797
  struct file_baton *fb;
838
798
  struct edit_baton *eb = pb->edit_baton;
839
 
  b = make_file_baton(path, FALSE, pb->edit_baton, pool);
840
 
  *file_baton = b;
 
799
  fb = make_file_baton(path, pb, FALSE, pb->pool);
 
800
  *file_baton = fb;
841
801
 
842
 
  /* Skip *everything* within a newly tree-conflicted directory
843
 
   * and directories the children of which should be skipped. */
844
 
  if (pb->skip || pb->tree_conflicted || pb->skip_children)
 
802
  /* Process Skips. */
 
803
  if (pb->skip_children)
845
804
    {
846
 
      b->skip = TRUE;
 
805
      fb->skip = TRUE;
847
806
      return SVN_NO_ERROR;
848
807
    }
849
808
 
850
 
  b->base_revision = base_revision;
851
 
 
852
 
  SVN_ERR(eb->diff_callbacks->file_opened(
853
 
                   &b->tree_conflicted, &b->skip,
854
 
                   b->wcpath, base_revision, eb->diff_cmd_baton, pool));
 
809
  fb->base_revision = base_revision;
 
810
 
 
811
  fb->left_source = svn_diff__source_create(eb->revision, fb->pool);
 
812
  fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool);
 
813
 
 
814
  SVN_ERR(eb->processor->file_opened(&fb->pfb,
 
815
                                     &fb->skip,
 
816
                                     path,
 
817
                                     fb->left_source,
 
818
                                     fb->right_source,
 
819
                                     NULL /* copy source */,
 
820
                                     pb->pdb,
 
821
                                     eb->processor,
 
822
                                     fb->pool, fb->pool));
855
823
 
856
824
  return SVN_NO_ERROR;
857
825
}
861
829
window_handler(svn_txdelta_window_t *window,
862
830
               void *window_baton)
863
831
{
864
 
  struct file_baton *b = window_baton;
 
832
  struct file_baton *fb = window_baton;
865
833
 
866
 
  SVN_ERR(b->apply_handler(window, b->apply_baton));
 
834
  SVN_ERR(fb->apply_handler(window, fb->apply_baton));
867
835
 
868
836
  if (!window)
869
837
    {
870
 
      b->result_md5_checksum = svn_checksum__from_digest(b->result_digest,
871
 
                                                         svn_checksum_md5,
872
 
                                                         b->pool);
 
838
      fb->result_md5_checksum = svn_checksum__from_digest_md5(
 
839
                                        fb->result_digest,
 
840
                                        fb->pool);
873
841
    }
874
842
 
875
843
  return SVN_NO_ERROR;
876
844
}
877
845
 
878
 
/* An editor function.  */
 
846
/* Implements svn_stream_lazyopen_func_t. */
 
847
static svn_error_t *
 
848
lazy_open_source(svn_stream_t **stream,
 
849
                 void *baton,
 
850
                 apr_pool_t *result_pool,
 
851
                 apr_pool_t *scratch_pool)
 
852
{
 
853
  struct file_baton *fb = baton;
 
854
 
 
855
  SVN_ERR(svn_stream_open_readonly(stream, fb->path_start_revision,
 
856
                                   result_pool, scratch_pool));
 
857
 
 
858
  return SVN_NO_ERROR;
 
859
}
 
860
 
 
861
/* Implements svn_stream_lazyopen_func_t. */
 
862
static svn_error_t *
 
863
lazy_open_result(svn_stream_t **stream,
 
864
                 void *baton,
 
865
                 apr_pool_t *result_pool,
 
866
                 apr_pool_t *scratch_pool)
 
867
{
 
868
  struct file_baton *fb = baton;
 
869
 
 
870
  SVN_ERR(svn_stream_open_unique(stream, &fb->path_end_revision, NULL,
 
871
                                 svn_io_file_del_on_pool_cleanup,
 
872
                                 result_pool, scratch_pool));
 
873
 
 
874
  return SVN_NO_ERROR;
 
875
}
 
876
 
 
877
/* An svn_delta_editor_t function.  */
879
878
static svn_error_t *
880
879
apply_textdelta(void *file_baton,
881
880
                const char *base_md5_digest,
883
882
                svn_txdelta_window_handler_t *handler,
884
883
                void **handler_baton)
885
884
{
886
 
  struct file_baton *b = file_baton;
 
885
  struct file_baton *fb = file_baton;
887
886
  svn_stream_t *src_stream;
888
887
  svn_stream_t *result_stream;
889
 
  apr_pool_t *scratch_pool = b->pool;
 
888
  apr_pool_t *scratch_pool = fb->pool;
890
889
 
891
890
  /* Skip *everything* within a newly tree-conflicted directory. */
892
 
  if (b->skip)
893
 
    {
894
 
      *handler = svn_delta_noop_window_handler;
895
 
      *handler_baton = NULL;
 
891
  if (fb->skip)
 
892
    {
 
893
      *handler = svn_delta_noop_window_handler;
 
894
      *handler_baton = NULL;
 
895
      return SVN_NO_ERROR;
 
896
    }
 
897
 
 
898
  /* If we're not sending file text, then ignore any that we receive. */
 
899
  if (! fb->edit_baton->text_deltas)
 
900
    {
 
901
      /* Supply valid paths to indicate there is a text change. */
 
902
      SVN_ERR(get_empty_file(fb->edit_baton, &fb->path_start_revision));
 
903
      SVN_ERR(get_empty_file(fb->edit_baton, &fb->path_end_revision));
 
904
 
 
905
      *handler = svn_delta_noop_window_handler;
 
906
      *handler_baton = NULL;
 
907
 
896
908
      return SVN_NO_ERROR;
897
909
    }
898
910
 
899
911
  /* We need the expected pristine file, so go get it */
900
 
  if (!b->added)
901
 
    SVN_ERR(get_file_from_ra(b, FALSE, b->pool));
 
912
  if (!fb->added)
 
913
    SVN_ERR(get_file_from_ra(fb, FALSE, scratch_pool));
902
914
  else
903
 
    SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision)));
 
915
    SVN_ERR(get_empty_file(fb->edit_baton, &(fb->path_start_revision)));
904
916
 
905
 
  SVN_ERR_ASSERT(b->path_start_revision != NULL);
 
917
  SVN_ERR_ASSERT(fb->path_start_revision != NULL);
906
918
 
907
919
  if (base_md5_digest != NULL)
908
920
    {
911
923
      SVN_ERR(svn_checksum_parse_hex(&base_md5_checksum, svn_checksum_md5,
912
924
                                     base_md5_digest, scratch_pool));
913
925
 
914
 
      if (!svn_checksum_match(base_md5_checksum, b->start_md5_checksum))
 
926
      if (!svn_checksum_match(base_md5_checksum, fb->start_md5_checksum))
915
927
        return svn_error_trace(svn_checksum_mismatch_err(
916
928
                                      base_md5_checksum,
917
 
                                      b->start_md5_checksum,
 
929
                                      fb->start_md5_checksum,
918
930
                                      scratch_pool,
919
931
                                      _("Base checksum mismatch for '%s'"),
920
 
                                      b->path));
 
932
                                      fb->path));
921
933
    }
922
934
 
923
935
  /* Open the file to be used as the base for second revision */
924
 
  SVN_ERR(svn_stream_open_readonly(&src_stream, b->path_start_revision,
925
 
                                   scratch_pool, scratch_pool));
 
936
  src_stream = svn_stream_lazyopen_create(lazy_open_source, fb, TRUE,
 
937
                                          scratch_pool);
926
938
 
927
939
  /* Open the file that will become the second revision after applying the
928
940
     text delta, it starts empty */
929
 
  SVN_ERR(svn_stream_open_unique(&result_stream, &b->path_end_revision, NULL,
930
 
                                 svn_io_file_del_on_pool_cleanup,
931
 
                                 scratch_pool, scratch_pool));
 
941
  result_stream = svn_stream_lazyopen_create(lazy_open_result, fb, TRUE,
 
942
                                             scratch_pool);
932
943
 
933
944
  svn_txdelta_apply(src_stream,
934
945
                    result_stream,
935
 
                    b->result_digest,
936
 
                    b->path, b->pool,
937
 
                    &(b->apply_handler), &(b->apply_baton));
 
946
                    fb->result_digest,
 
947
                    fb->path, fb->pool,
 
948
                    &(fb->apply_handler), &(fb->apply_baton));
938
949
 
939
950
  *handler = window_handler;
940
951
  *handler_baton = file_baton;
942
953
  return SVN_NO_ERROR;
943
954
}
944
955
 
945
 
/* An editor function.  When the file is closed we have a temporary
 
956
/* An svn_delta_editor_t function.  When the file is closed we have a temporary
946
957
 * file containing a pristine version of the repository file. This can
947
958
 * be compared against the working copy.
948
959
 *
957
968
           const char *expected_md5_digest,
958
969
           apr_pool_t *pool)
959
970
{
960
 
  struct file_baton *b = file_baton;
961
 
  struct edit_baton *eb = b->edit_baton;
962
 
  svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
963
 
  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
 
971
  struct file_baton *fb = file_baton;
 
972
  struct dir_baton *pb = fb->parent_baton;
 
973
  struct edit_baton *eb = fb->edit_baton;
964
974
  apr_pool_t *scratch_pool;
965
975
 
966
976
  /* Skip *everything* within a newly tree-conflicted directory. */
967
 
  if (b->skip)
 
977
  if (fb->skip)
968
978
    {
969
 
      svn_pool_destroy(b->pool);
 
979
      svn_pool_destroy(fb->pool);
 
980
      SVN_ERR(release_dir(pb));
970
981
      return SVN_NO_ERROR;
971
982
    }
972
983
 
973
 
  scratch_pool = b->pool;
 
984
  scratch_pool = fb->pool;
974
985
 
975
 
  if (expected_md5_digest)
 
986
  if (expected_md5_digest && eb->text_deltas)
976
987
    {
977
988
      svn_checksum_t *expected_md5_checksum;
978
989
 
979
990
      SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
980
991
                                     expected_md5_digest, scratch_pool));
981
992
 
982
 
      if (!svn_checksum_match(expected_md5_checksum, b->result_md5_checksum))
 
993
      if (!svn_checksum_match(expected_md5_checksum, fb->result_md5_checksum))
983
994
        return svn_error_trace(svn_checksum_mismatch_err(
984
995
                                      expected_md5_checksum,
985
 
                                      b->result_md5_checksum,
 
996
                                      fb->result_md5_checksum,
986
997
                                      pool,
987
998
                                      _("Checksum mismatch for '%s'"),
988
 
                                      b->path));
 
999
                                      fb->path));
989
1000
    }
990
1001
 
991
 
  if (!b->added && b->propchanges->nelts > 0)
 
1002
  if (fb->added || fb->path_end_revision || fb->has_propchange)
992
1003
    {
993
 
      if (!b->pristine_props)
 
1004
      apr_hash_t *right_props;
 
1005
 
 
1006
      if (!fb->added && !fb->pristine_props)
994
1007
        {
995
1008
          /* We didn't receive a text change, so we have no pristine props.
996
1009
             Retrieve just the props now. */
997
 
          SVN_ERR(get_file_from_ra(b, TRUE, scratch_pool));
998
 
        }
999
 
 
1000
 
      remove_non_prop_changes(b->pristine_props, b->propchanges);
1001
 
    }
1002
 
 
1003
 
  if (b->path_end_revision || b->propchanges->nelts > 0)
1004
 
    {
1005
 
      const char *mimetype1, *mimetype2;
1006
 
      get_file_mime_types(&mimetype1, &mimetype2, b);
1007
 
 
1008
 
 
1009
 
      if (b->added)
1010
 
        SVN_ERR(eb->diff_callbacks->file_added(
1011
 
                 &content_state, &prop_state, &b->tree_conflicted,
1012
 
                 b->wcpath,
1013
 
                 b->path_end_revision ? b->path_start_revision : NULL,
1014
 
                 b->path_end_revision,
1015
 
                 0,
1016
 
                 b->edit_baton->target_revision,
1017
 
                 mimetype1, mimetype2,
1018
 
                 NULL, SVN_INVALID_REVNUM,
1019
 
                 b->propchanges, b->pristine_props,
1020
 
                 b->edit_baton->diff_cmd_baton,
1021
 
                 scratch_pool));
1022
 
      else
1023
 
        SVN_ERR(eb->diff_callbacks->file_changed(
1024
 
                 &content_state, &prop_state,
1025
 
                 &b->tree_conflicted, b->wcpath,
1026
 
                 b->path_end_revision ? b->path_start_revision : NULL,
1027
 
                 b->path_end_revision,
1028
 
                 b->edit_baton->revision,
1029
 
                 b->edit_baton->target_revision,
1030
 
                 mimetype1, mimetype2,
1031
 
                 b->propchanges, b->pristine_props,
1032
 
                 b->edit_baton->diff_cmd_baton,
1033
 
                 scratch_pool));
1034
 
    }
1035
 
 
1036
 
 
1037
 
  if (eb->notify_func)
1038
 
    {
1039
 
      deleted_path_notify_t *dpn;
1040
 
      svn_wc_notify_t *notify;
1041
 
      svn_wc_notify_action_t action;
1042
 
      svn_node_kind_t kind = svn_node_file;
1043
 
 
1044
 
      /* Find out if a pending delete notification for this path is
1045
 
       * still around. */
1046
 
      dpn = apr_hash_get(eb->deleted_paths, b->wcpath, APR_HASH_KEY_STRING);
1047
 
      if (dpn)
1048
 
        {
1049
 
          /* If any was found, we will handle the pending 'deleted path
1050
 
           * notification' (DPN) here. Remove it from the list. */
1051
 
          apr_hash_set(eb->deleted_paths, b->wcpath,
1052
 
                       APR_HASH_KEY_STRING, NULL);
1053
 
 
1054
 
          /* the pending delete might be on a different node kind. */
1055
 
          kind = dpn->kind;
1056
 
          content_state = prop_state = dpn->state;
1057
 
        }
1058
 
 
1059
 
      /* Determine what the notification (ACTION) should be.
1060
 
       * In case of a pending 'delete', this might become a 'replace'. */
1061
 
      if (b->tree_conflicted)
1062
 
        action = svn_wc_notify_tree_conflict;
1063
 
      else if (dpn)
1064
 
        {
1065
 
          if (dpn->action == svn_wc_notify_update_delete
1066
 
              && b->added)
1067
 
            action = svn_wc_notify_update_replace;
1068
 
          else
1069
 
            /* Note: dpn->action might be svn_wc_notify_tree_conflict */
1070
 
            action = dpn->action;
1071
 
        }
1072
 
      else if ((content_state == svn_wc_notify_state_missing)
1073
 
                || (content_state == svn_wc_notify_state_obstructed))
1074
 
        action = svn_wc_notify_skip;
1075
 
      else if (b->added)
1076
 
        action = svn_wc_notify_update_add;
1077
 
      else
1078
 
        action = svn_wc_notify_update_update;
1079
 
 
1080
 
      notify = svn_wc_create_notify(b->wcpath, action, scratch_pool);
1081
 
      notify->kind = kind;
1082
 
      notify->content_state = content_state;
1083
 
      notify->prop_state = prop_state;
1084
 
      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
1085
 
    }
1086
 
 
1087
 
  svn_pool_destroy(b->pool); /* Destroy file and scratch pool */
 
1010
          SVN_ERR(get_file_from_ra(fb, TRUE, scratch_pool));
 
1011
        }
 
1012
 
 
1013
      if (fb->pristine_props)
 
1014
        remove_non_prop_changes(fb->pristine_props, fb->propchanges);
 
1015
 
 
1016
      right_props = svn_prop__patch(fb->pristine_props, fb->propchanges,
 
1017
                                    fb->pool);
 
1018
 
 
1019
      if (fb->added)
 
1020
        SVN_ERR(eb->processor->file_added(fb->path,
 
1021
                                          NULL /* copyfrom_src */,
 
1022
                                          fb->right_source,
 
1023
                                          NULL /* copyfrom_file */,
 
1024
                                          fb->path_end_revision,
 
1025
                                          NULL /* copyfrom_props */,
 
1026
                                          right_props,
 
1027
                                          fb->pfb,
 
1028
                                          eb->processor,
 
1029
                                          fb->pool));
 
1030
      else
 
1031
        SVN_ERR(eb->processor->file_changed(fb->path,
 
1032
                                            fb->left_source,
 
1033
                                            fb->right_source,
 
1034
                                            fb->path_end_revision
 
1035
                                                    ? fb->path_start_revision
 
1036
                                                    : NULL,
 
1037
                                            fb->path_end_revision,
 
1038
                                            fb->pristine_props,
 
1039
                                            right_props,
 
1040
                                            (fb->path_end_revision != NULL),
 
1041
                                            fb->propchanges,
 
1042
                                            fb->pfb,
 
1043
                                            eb->processor,
 
1044
                                            fb->pool));
 
1045
    }
 
1046
 
 
1047
  svn_pool_destroy(fb->pool); /* Destroy file and scratch pool */
 
1048
 
 
1049
  SVN_ERR(release_dir(pb));
1088
1050
 
1089
1051
  return SVN_NO_ERROR;
1090
1052
}
1091
1053
 
1092
 
/* An editor function.  */
 
1054
/* Report any accumulated prop changes via the 'dir_props_changed' callback,
 
1055
 * and then call the 'dir_closed' callback.  Notify about any deleted paths
 
1056
 * within this directory that have not already been notified, and then about
 
1057
 * this directory itself (unless it was added, in which case the notification
 
1058
 * was done at that time).
 
1059
 *
 
1060
 * An svn_delta_editor_t function.  */
1093
1061
static svn_error_t *
1094
1062
close_directory(void *dir_baton,
1095
1063
                apr_pool_t *pool)
1096
1064
{
1097
 
  struct dir_baton *b = dir_baton;
1098
 
  struct edit_baton *eb = b->edit_baton;
1099
 
  svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
1100
 
  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
1101
 
  svn_boolean_t skipped = FALSE;
 
1065
  struct dir_baton *db = dir_baton;
 
1066
  struct edit_baton *eb = db->edit_baton;
1102
1067
  apr_pool_t *scratch_pool;
1103
 
 
1104
 
  /* Skip *everything* within a newly tree-conflicted directory. */
1105
 
  if (b->skip)
1106
 
    {
1107
 
      svn_pool_destroy(b->pool);
1108
 
      return SVN_NO_ERROR;
1109
 
    }
1110
 
 
1111
 
  scratch_pool = b->pool;
1112
 
 
1113
 
  if (!b->added && b->propchanges->nelts > 0)
1114
 
    remove_non_prop_changes(b->pristine_props, b->propchanges);
1115
 
 
1116
 
  /* Don't do the props_changed stuff if this is a dry_run and we don't
1117
 
     have an access baton, since in that case the directory will already
1118
 
     have been recognised as added, in which case they cannot conflict. */
1119
 
  if (b->propchanges->nelts > 0)
1120
 
    {
1121
 
      svn_boolean_t tree_conflicted = FALSE;
1122
 
      SVN_ERR(eb->diff_callbacks->dir_props_changed(
1123
 
               &prop_state, &tree_conflicted,
1124
 
               b->wcpath, b->added,
1125
 
               b->propchanges, b->pristine_props,
1126
 
               b->edit_baton->diff_cmd_baton, scratch_pool));
1127
 
      if (tree_conflicted)
1128
 
        b->tree_conflicted = TRUE;
1129
 
 
1130
 
      if (prop_state == svn_wc_notify_state_obstructed
1131
 
          || prop_state == svn_wc_notify_state_missing)
1132
 
        {
1133
 
          content_state = prop_state;
1134
 
          skipped = TRUE;
1135
 
        }
1136
 
    }
1137
 
 
1138
 
  SVN_ERR(eb->diff_callbacks->dir_closed(NULL, NULL, NULL,
1139
 
                                         b->wcpath, b->added,
1140
 
                                         b->edit_baton->diff_cmd_baton,
1141
 
                                         scratch_pool));
1142
 
 
1143
 
  /* Don't notify added directories as they triggered notification
1144
 
     in add_directory. */
1145
 
  if (!skipped && !b->added && eb->notify_func)
1146
 
    {
1147
 
      apr_hash_index_t *hi;
1148
 
 
1149
 
      for (hi = apr_hash_first(pool, eb->deleted_paths); hi;
1150
 
           hi = apr_hash_next(hi))
1151
 
        {
1152
 
          svn_wc_notify_t *notify;
1153
 
          const char *deleted_path = svn__apr_hash_index_key(hi);
1154
 
          deleted_path_notify_t *dpn = svn__apr_hash_index_val(hi);
1155
 
 
1156
 
          notify = svn_wc_create_notify(deleted_path, dpn->action, pool);
1157
 
          notify->kind = dpn->kind;
1158
 
          notify->content_state = notify->prop_state = dpn->state;
1159
 
          notify->lock_state = svn_wc_notify_lock_state_inapplicable;
1160
 
          (*eb->notify_func)(eb->notify_baton, notify, pool);
1161
 
          apr_hash_set(eb->deleted_paths, deleted_path,
1162
 
                       APR_HASH_KEY_STRING, NULL);
1163
 
        }
1164
 
    }
1165
 
 
1166
 
  if (!b->added && eb->notify_func)
1167
 
    {
1168
 
      svn_wc_notify_t *notify;
1169
 
      svn_wc_notify_action_t action;
1170
 
 
1171
 
      if (b->tree_conflicted)
1172
 
        action = svn_wc_notify_tree_conflict;
1173
 
      else if (skipped)
1174
 
        action = svn_wc_notify_skip;
 
1068
  apr_hash_t *pristine_props;
 
1069
  svn_boolean_t send_changed = FALSE;
 
1070
 
 
1071
  scratch_pool = db->pool;
 
1072
 
 
1073
  if ((db->has_propchange || db->added) && !db->skip)
 
1074
    {
 
1075
      if (db->added)
 
1076
        {
 
1077
          pristine_props = eb->empty_hash;
 
1078
        }
1175
1079
      else
1176
 
        action = svn_wc_notify_update_update;
1177
 
 
1178
 
      notify = svn_wc_create_notify(b->wcpath, action, pool);
1179
 
      notify->kind = svn_node_dir;
1180
 
 
1181
 
      /* In case of a tree conflict during merge, the diff callback
1182
 
       * sets content_state appropriately. So copy the state into the
1183
 
       * notify_t to make sure conflicts get displayed. */
1184
 
      notify->content_state = content_state;
1185
 
 
1186
 
      notify->prop_state = prop_state;
1187
 
      notify->lock_state = svn_wc_notify_lock_state_inapplicable;
1188
 
      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
1189
 
    }
1190
 
 
1191
 
  svn_pool_destroy(b->pool); /* Destroy baton and scratch_pool */
 
1080
        {
 
1081
          SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, &pristine_props,
 
1082
                                  db->path, db->base_revision, 0, scratch_pool));
 
1083
        }
 
1084
 
 
1085
      if (db->propchanges->nelts > 0)
 
1086
        {
 
1087
          remove_non_prop_changes(pristine_props, db->propchanges);
 
1088
        }
 
1089
 
 
1090
      if (db->propchanges->nelts > 0 || db->added)
 
1091
        {
 
1092
          apr_hash_t *right_props;
 
1093
 
 
1094
          right_props = svn_prop__patch(pristine_props, db->propchanges,
 
1095
                                        scratch_pool);
 
1096
 
 
1097
          if (db->added)
 
1098
            {
 
1099
              SVN_ERR(eb->processor->dir_added(db->path,
 
1100
                                           NULL /* copyfrom */,
 
1101
                                           db->right_source,
 
1102
                                           NULL /* copyfrom props */,
 
1103
                                           right_props,
 
1104
                                           db->pdb,
 
1105
                                           eb->processor,
 
1106
                                           db->pool));
 
1107
            }
 
1108
          else
 
1109
            {
 
1110
              SVN_ERR(eb->processor->dir_changed(db->path,
 
1111
                                                 db->left_source,
 
1112
                                                 db->right_source,
 
1113
                                                 pristine_props,
 
1114
                                                 right_props,
 
1115
                                                 db->propchanges,
 
1116
                                                 db->pdb,
 
1117
                                                 eb->processor,
 
1118
                                                 db->pool));
 
1119
            }
 
1120
 
 
1121
          send_changed = TRUE; /* Skip dir_closed */
 
1122
        }
 
1123
    }
 
1124
 
 
1125
  if (! db->skip && !send_changed)
 
1126
    {
 
1127
      SVN_ERR(eb->processor->dir_closed(db->path,
 
1128
                                        db->left_source,
 
1129
                                        db->right_source,
 
1130
                                        db->pdb,
 
1131
                                        eb->processor,
 
1132
                                        db->pool));
 
1133
    }
 
1134
  SVN_ERR(release_dir(db));
1192
1135
 
1193
1136
  return SVN_NO_ERROR;
1194
1137
}
1195
1138
 
1196
1139
 
1197
 
/* An editor function.  */
 
1140
/* Record a prop change, which we will report later in close_file().
 
1141
 *
 
1142
 * An svn_delta_editor_t function.  */
1198
1143
static svn_error_t *
1199
1144
change_file_prop(void *file_baton,
1200
1145
                 const char *name,
1201
1146
                 const svn_string_t *value,
1202
1147
                 apr_pool_t *pool)
1203
1148
{
1204
 
  struct file_baton *b = file_baton;
 
1149
  struct file_baton *fb = file_baton;
1205
1150
  svn_prop_t *propchange;
 
1151
  svn_prop_kind_t propkind;
1206
1152
 
1207
1153
  /* Skip *everything* within a newly tree-conflicted directory. */
1208
 
  if (b->skip)
1209
 
    return SVN_NO_ERROR;
1210
 
 
1211
 
  propchange = apr_array_push(b->propchanges);
1212
 
  propchange->name = apr_pstrdup(b->pool, name);
1213
 
  propchange->value = value ? svn_string_dup(value, b->pool) : NULL;
 
1154
  if (fb->skip)
 
1155
    return SVN_NO_ERROR;
 
1156
 
 
1157
  propkind = svn_property_kind2(name);
 
1158
  if (propkind == svn_prop_wc_kind)
 
1159
    return SVN_NO_ERROR;
 
1160
  else if (propkind == svn_prop_regular_kind)
 
1161
    fb->has_propchange = TRUE;
 
1162
 
 
1163
  propchange = apr_array_push(fb->propchanges);
 
1164
  propchange->name = apr_pstrdup(fb->pool, name);
 
1165
  propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
1214
1166
 
1215
1167
  return SVN_NO_ERROR;
1216
1168
}
1217
1169
 
1218
 
/* An editor function.  */
 
1170
/* Make a note of this prop change, to be reported when the dir is closed.
 
1171
 *
 
1172
 * An svn_delta_editor_t function.  */
1219
1173
static svn_error_t *
1220
1174
change_dir_prop(void *dir_baton,
1221
1175
                const char *name,
1224
1178
{
1225
1179
  struct dir_baton *db = dir_baton;
1226
1180
  svn_prop_t *propchange;
 
1181
  svn_prop_kind_t propkind;
1227
1182
 
1228
1183
  /* Skip *everything* within a newly tree-conflicted directory. */
1229
1184
  if (db->skip)
1230
1185
    return SVN_NO_ERROR;
1231
1186
 
 
1187
  propkind = svn_property_kind2(name);
 
1188
  if (propkind == svn_prop_wc_kind)
 
1189
    return SVN_NO_ERROR;
 
1190
  else if (propkind == svn_prop_regular_kind)
 
1191
    db->has_propchange = TRUE;
 
1192
 
1232
1193
  propchange = apr_array_push(db->propchanges);
1233
1194
  propchange->name = apr_pstrdup(db->pool, name);
1234
1195
  propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
1237
1198
}
1238
1199
 
1239
1200
 
1240
 
/* An editor function.  */
 
1201
/* An svn_delta_editor_t function.  */
1241
1202
static svn_error_t *
1242
1203
close_edit(void *edit_baton,
1243
1204
           apr_pool_t *pool)
1249
1210
  return SVN_NO_ERROR;
1250
1211
}
1251
1212
 
1252
 
/* An editor function.  */
 
1213
/* Notify that the node at PATH is 'missing'.
 
1214
 * An svn_delta_editor_t function.  */
1253
1215
static svn_error_t *
1254
1216
absent_directory(const char *path,
1255
1217
                 void *parent_baton,
1258
1220
  struct dir_baton *pb = parent_baton;
1259
1221
  struct edit_baton *eb = pb->edit_baton;
1260
1222
 
1261
 
  /* ### TODO: Raise a tree-conflict?? I sure hope not.*/
1262
 
 
1263
 
  if (eb->notify_func)
1264
 
    {
1265
 
      svn_wc_notify_t *notify
1266
 
        = svn_wc_create_notify(svn_dirent_join(pb->wcpath,
1267
 
                                               svn_relpath_basename(path,
1268
 
                                                                    NULL),
1269
 
                                               pool),
1270
 
                               svn_wc_notify_skip, pool);
1271
 
      notify->kind = svn_node_dir;
1272
 
      notify->content_state = notify->prop_state
1273
 
        = svn_wc_notify_state_missing;
1274
 
      (*eb->notify_func)(eb->notify_baton, notify, pool);
1275
 
    }
 
1223
  SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
1276
1224
 
1277
1225
  return SVN_NO_ERROR;
1278
1226
}
1279
1227
 
1280
1228
 
1281
 
/* An editor function.  */
 
1229
/* Notify that the node at PATH is 'missing'.
 
1230
 * An svn_delta_editor_t function.  */
1282
1231
static svn_error_t *
1283
1232
absent_file(const char *path,
1284
1233
            void *parent_baton,
1287
1236
  struct dir_baton *pb = parent_baton;
1288
1237
  struct edit_baton *eb = pb->edit_baton;
1289
1238
 
1290
 
  /* ### TODO: Raise a tree-conflict?? I sure hope not.*/
1291
 
 
1292
 
  if (eb->notify_func)
1293
 
    {
1294
 
      svn_wc_notify_t *notify
1295
 
        = svn_wc_create_notify(svn_dirent_join(pb->wcpath,
1296
 
                                               svn_relpath_basename(path,
1297
 
                                                                    pool),
1298
 
                                               pool),
1299
 
                               svn_wc_notify_skip, pool);
1300
 
      notify->kind = svn_node_file;
1301
 
      notify->content_state = notify->prop_state
1302
 
        = svn_wc_notify_state_missing;
1303
 
      (*eb->notify_func)(eb->notify_baton, notify, pool);
1304
 
    }
 
1239
  SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
 
1240
 
 
1241
  return SVN_NO_ERROR;
 
1242
}
 
1243
 
 
1244
static svn_error_t *
 
1245
fetch_kind_func(svn_node_kind_t *kind,
 
1246
                void *baton,
 
1247
                const char *path,
 
1248
                svn_revnum_t base_revision,
 
1249
                apr_pool_t *scratch_pool)
 
1250
{
 
1251
  struct edit_baton *eb = baton;
 
1252
 
 
1253
  if (!SVN_IS_VALID_REVNUM(base_revision))
 
1254
    base_revision = eb->revision;
 
1255
 
 
1256
  SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, kind,
 
1257
                            scratch_pool));
 
1258
 
 
1259
  return SVN_NO_ERROR;
 
1260
}
 
1261
 
 
1262
static svn_error_t *
 
1263
fetch_props_func(apr_hash_t **props,
 
1264
                 void *baton,
 
1265
                 const char *path,
 
1266
                 svn_revnum_t base_revision,
 
1267
                 apr_pool_t *result_pool,
 
1268
                 apr_pool_t *scratch_pool)
 
1269
{
 
1270
  struct edit_baton *eb = baton;
 
1271
  svn_node_kind_t node_kind;
 
1272
 
 
1273
  if (!SVN_IS_VALID_REVNUM(base_revision))
 
1274
    base_revision = eb->revision;
 
1275
 
 
1276
  SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
 
1277
                            scratch_pool));
 
1278
 
 
1279
  if (node_kind == svn_node_file)
 
1280
    {
 
1281
      SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision,
 
1282
                              NULL, NULL, props, result_pool));
 
1283
    }
 
1284
  else if (node_kind == svn_node_dir)
 
1285
    {
 
1286
      apr_array_header_t *tmp_props;
 
1287
 
 
1288
      SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
 
1289
                              base_revision, 0 /* Dirent fields */,
 
1290
                              result_pool));
 
1291
      tmp_props = svn_prop_hash_to_array(*props, result_pool);
 
1292
      SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
 
1293
                                   result_pool));
 
1294
      *props = svn_prop_array_to_hash(tmp_props, result_pool);
 
1295
    }
 
1296
  else
 
1297
    {
 
1298
      *props = apr_hash_make(result_pool);
 
1299
    }
 
1300
 
 
1301
  return SVN_NO_ERROR;
 
1302
}
 
1303
 
 
1304
static svn_error_t *
 
1305
fetch_base_func(const char **filename,
 
1306
                void *baton,
 
1307
                const char *path,
 
1308
                svn_revnum_t base_revision,
 
1309
                apr_pool_t *result_pool,
 
1310
                apr_pool_t *scratch_pool)
 
1311
{
 
1312
  struct edit_baton *eb = baton;
 
1313
  svn_stream_t *fstream;
 
1314
  svn_error_t *err;
 
1315
 
 
1316
  if (!SVN_IS_VALID_REVNUM(base_revision))
 
1317
    base_revision = eb->revision;
 
1318
 
 
1319
  SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
 
1320
                                 svn_io_file_del_on_pool_cleanup,
 
1321
                                 result_pool, scratch_pool));
 
1322
 
 
1323
  err = svn_ra_get_file(eb->ra_session, path, base_revision,
 
1324
                        fstream, NULL, NULL, scratch_pool);
 
1325
  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
 
1326
    {
 
1327
      svn_error_clear(err);
 
1328
      SVN_ERR(svn_stream_close(fstream));
 
1329
 
 
1330
      *filename = NULL;
 
1331
      return SVN_NO_ERROR;
 
1332
    }
 
1333
  else if (err)
 
1334
    return svn_error_trace(err);
 
1335
 
 
1336
  SVN_ERR(svn_stream_close(fstream));
1305
1337
 
1306
1338
  return SVN_NO_ERROR;
1307
1339
}
1308
1340
 
1309
1341
/* Create a repository diff editor and baton.  */
1310
1342
svn_error_t *
1311
 
svn_client__get_diff_editor(const svn_delta_editor_t **editor,
1312
 
                            void **edit_baton,
1313
 
                            svn_wc_context_t *wc_ctx,
1314
 
                            const char *target,
1315
 
                            svn_depth_t depth,
1316
 
                            svn_ra_session_t *ra_session,
1317
 
                            svn_revnum_t revision,
1318
 
                            svn_boolean_t walk_deleted_dirs,
1319
 
                            svn_boolean_t dry_run,
1320
 
                            const svn_wc_diff_callbacks4_t *diff_callbacks,
1321
 
                            void *diff_cmd_baton,
1322
 
                            svn_cancel_func_t cancel_func,
1323
 
                            void *cancel_baton,
1324
 
                            svn_wc_notify_func2_t notify_func,
1325
 
                            void *notify_baton,
1326
 
                            apr_pool_t *result_pool,
1327
 
                            apr_pool_t *scratch_pool)
 
1343
svn_client__get_diff_editor2(const svn_delta_editor_t **editor,
 
1344
                             void **edit_baton,
 
1345
                             svn_ra_session_t *ra_session,
 
1346
                             svn_depth_t depth,
 
1347
                             svn_revnum_t revision,
 
1348
                             svn_boolean_t text_deltas,
 
1349
                             const svn_diff_tree_processor_t *processor,
 
1350
                             svn_cancel_func_t cancel_func,
 
1351
                             void *cancel_baton,
 
1352
                             apr_pool_t *result_pool)
1328
1353
{
1329
1354
  apr_pool_t *editor_pool = svn_pool_create(result_pool);
1330
1355
  svn_delta_editor_t *tree_editor = svn_delta_default_editor(editor_pool);
1331
1356
  struct edit_baton *eb = apr_pcalloc(editor_pool, sizeof(*eb));
1332
 
  const char *target_abspath;
1333
 
 
1334
 
  SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, editor_pool));
 
1357
  svn_delta_shim_callbacks_t *shim_callbacks =
 
1358
                                svn_delta_shim_callbacks_default(editor_pool);
1335
1359
 
1336
1360
  eb->pool = editor_pool;
1337
 
  eb->target = target;
1338
 
  eb->wc_ctx = wc_ctx;
1339
1361
  eb->depth = depth;
1340
 
  eb->diff_callbacks = diff_callbacks;
1341
 
  eb->diff_cmd_baton = diff_cmd_baton;
1342
 
  eb->dry_run = dry_run;
 
1362
 
 
1363
  eb->processor = processor;
 
1364
 
1343
1365
  eb->ra_session = ra_session;
1344
1366
 
1345
1367
  eb->revision = revision;
1346
1368
  eb->empty_file = NULL;
1347
1369
  eb->empty_hash = apr_hash_make(eb->pool);
1348
 
  eb->deleted_paths = apr_hash_make(eb->pool);
1349
 
  eb->notify_func = notify_func;
1350
 
  eb->notify_baton = notify_baton;
1351
 
  eb->walk_deleted_repos_dirs = walk_deleted_dirs;
 
1370
  eb->text_deltas = text_deltas;
1352
1371
  eb->cancel_func = cancel_func;
1353
1372
  eb->cancel_baton = cancel_baton;
1354
1373
 
1368
1387
  tree_editor->absent_directory = absent_directory;
1369
1388
  tree_editor->absent_file = absent_file;
1370
1389
 
1371
 
  return svn_delta_get_cancellation_editor(cancel_func,
1372
 
                                           cancel_baton,
1373
 
                                           tree_editor,
1374
 
                                           eb,
1375
 
                                           editor,
1376
 
                                           edit_baton,
1377
 
                                           eb->pool);
 
1390
  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
 
1391
                                            tree_editor, eb,
 
1392
                                            editor, edit_baton,
 
1393
                                            eb->pool));
 
1394
 
 
1395
  shim_callbacks->fetch_kind_func = fetch_kind_func;
 
1396
  shim_callbacks->fetch_props_func = fetch_props_func;
 
1397
  shim_callbacks->fetch_base_func = fetch_base_func;
 
1398
  shim_callbacks->fetch_baton = eb;
 
1399
 
 
1400
  SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
 
1401
                                   NULL, NULL, shim_callbacks,
 
1402
                                   result_pool, result_pool));
 
1403
 
 
1404
  return SVN_NO_ERROR;
1378
1405
}