2
* repos_diff.c -- The diff editor for comparing two repository versions
4
* ====================================================================
5
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
7
* This software is licensed as described in the file COPYING, which
8
* you should have received as part of this distribution. The terms
9
* are also available at http://subversion.tigris.org/license-1.html.
10
* If newer versions of this license are posted there, you may use a
11
* newer version instead, at your option.
13
* This software consists of voluntary contributions made by many
14
* individuals. For exact contribution history, see the revision
15
* history and logs, available at http://subversion.tigris.org/.
16
* ====================================================================
19
/* This code uses an editor driven by a tree delta between two
20
* repository revisions (REV1 and REV2). For each file encountered in
21
* the delta the editor constructs two temporary files, one for each
22
* revision. This necessitates a separate request for the REV1 version
23
* of the file when the delta shows the file being modified or
24
* deleted. Files that are added by the delta do not require a
25
* separate request, the REV1 version is empty and the delta is
26
* sufficient to construct the REV2 version. When both versions of
27
* each file have been created the diff callback is invoked to display
28
* the difference between the two files. */
31
#include "svn_pools.h"
34
#include "svn_props.h"
38
/* Overall crawler editor baton. */
40
/* TARGET is a working-copy directory which corresponds to the base
41
URL open in RA_SESSION below. */
44
/* ADM_ACCESS is an access baton that includes the TARGET directory */
45
svn_wc_adm_access_t *adm_access;
47
/* The callback and calback argument that implement the file comparison
49
const svn_wc_diff_callbacks2_t *diff_callbacks;
52
/* RECURSE is TRUE if this is a recursive diff or merge, false otherwise */
53
svn_boolean_t recurse;
55
/* DRY_RUN is TRUE if this is a dry-run diff, false otherwise. */
56
svn_boolean_t dry_run;
58
/* RA_LIB is the vtable for making requests to the RA layer, RA_SESSION
59
is the open session for these requests */
60
svn_ra_session_t *ra_session;
62
/* The rev1 from the '-r Rev1:Rev2' command line option */
63
svn_revnum_t revision;
65
/* The rev2 from the '-r Rev1:Rev2' option, specifically set by
66
set_target_revision(). */
67
svn_revnum_t target_revision;
69
/* A temporary empty file. Used for add/delete differences. This is
70
cached here so that it can be reused, all empty files are the same. */
71
const char *empty_file;
73
/* Empty hash used for adds. */
74
apr_hash_t *empty_hash;
76
/* If the func is non-null, send notifications of actions. */
77
svn_wc_notify_func2_t notify_func;
83
/* Directory level baton.
86
/* Gets set if the directory is added rather than replaced/unchanged. */
89
/* The path of the directory within the repository */
92
/* The path of the directory in the wc, relative to cwd */
95
/* The baton for the parent directory, or null if this is the root of the
96
hierarchy to be compared. */
97
struct dir_baton *dir_baton;
99
/* The overall crawler editor baton. */
100
struct edit_baton *edit_baton;
102
/* A cache of any property changes (svn_prop_t) received for this dir. */
103
apr_array_header_t *propchanges;
105
/* The pristine-property list attached to this directory. */
106
apr_hash_t *pristine_props;
108
/* The pool passed in by add_dir, open_dir, or open_root.
109
Also, the pool this dir baton is allocated in. */
116
/* Gets set if the file is added rather than replaced. */
119
/* The path of the file within the repository */
122
/* The path of the file in the wc, relative to cwd */
125
/* The path and APR file handle to the temporary file that contains the
126
first repository version. Also, the pristine-property list of
128
const char *path_start_revision;
129
apr_file_t *file_start_revision;
130
apr_hash_t *pristine_props;
132
/* The path and APR file handle to the temporary file that contains the
133
second repository version. These fields are set when processing
134
textdelta and file deletion, and will be NULL if there's no
135
textual difference between the two revisions. */
136
const char *path_end_revision;
137
apr_file_t *file_end_revision;
139
/* APPLY_HANDLER/APPLY_BATON represent the delta application baton. */
140
svn_txdelta_window_handler_t apply_handler;
143
/* The overall crawler editor baton. */
144
struct edit_baton *edit_baton;
146
/* A cache of any property changes (svn_prop_t) received for this file. */
147
apr_array_header_t *propchanges;
149
/* The pool passed in by add_file or open_file.
150
Also, the pool this file_baton is allocated in. */
154
/* Data used by the apr pool temp file cleanup handler */
155
struct temp_file_cleanup_s {
156
/* The path to the file to be deleted. NOTE: this path is
157
APR-encoded, _not_ utf8-encoded! */
159
/* The pool to which the deletion of the file is linked. */
163
/* Create a new directory baton for PATH in POOL. ADDED is set if
164
* this directory is being added rather than replaced. PARENT_BATON is
165
* the baton of the parent directory (or NULL if this is the root of
166
* the comparison hierarchy). The directory and its parent may or may
167
* not exist in the working copy. EDIT_BATON is the overall crawler
170
static struct dir_baton *
171
make_dir_baton (const char *path,
172
struct dir_baton *parent_baton,
173
struct edit_baton *edit_baton,
177
struct dir_baton *dir_baton = apr_pcalloc (pool, sizeof (*dir_baton));
179
dir_baton->dir_baton = parent_baton;
180
dir_baton->edit_baton = edit_baton;
181
dir_baton->added = added;
182
dir_baton->pool = pool;
183
dir_baton->path = apr_pstrdup (pool, path);
184
dir_baton->wcpath = svn_path_join (edit_baton->target, path, pool);
185
dir_baton->propchanges = apr_array_make (pool, 1, sizeof (svn_prop_t));
190
/* Create a new file baton for PATH in POOL, which is a child of
191
* directory PARENT_PATH. ADDED is set if this file is being added
192
* rather than replaced. EDIT_BATON is a pointer to the global edit
195
static struct file_baton *
196
make_file_baton (const char *path,
201
struct file_baton *file_baton = apr_pcalloc (pool, sizeof (*file_baton));
202
struct edit_baton *eb = edit_baton;
204
file_baton->edit_baton = edit_baton;
205
file_baton->added = added;
206
file_baton->pool = pool;
207
file_baton->path = apr_pstrdup (pool, path);
208
file_baton->wcpath = svn_path_join (eb->target, path, pool);
209
file_baton->propchanges = apr_array_make (pool, 1, sizeof (svn_prop_t));
215
/* Helper function: return up to two svn:mime-type values buried
216
* within a file baton. Set *MIMETYPE1 to the value within the file's
217
* pristine properties, or NULL if not available. Set *MIMETYPE2 to
218
* the value within the "new" file's propchanges, or NULL if not
222
get_file_mime_types (const char **mimetype1,
223
const char **mimetype2,
224
struct file_baton *b)
230
if (b->pristine_props)
232
svn_string_t *pristine_val;
233
pristine_val = apr_hash_get (b->pristine_props, SVN_PROP_MIME_TYPE,
234
strlen(SVN_PROP_MIME_TYPE));
236
*mimetype1 = pristine_val->data;
242
svn_prop_t *propchange;
244
for (i = 0; i < b->propchanges->nelts; i++)
246
propchange = &APR_ARRAY_IDX(b->propchanges, i, svn_prop_t);
247
if (strcmp (propchange->name, SVN_PROP_MIME_TYPE) == 0)
249
if (propchange->value)
250
*mimetype2 = propchange->value->data;
258
/* An apr pool cleanup handler, this deletes one of the temporary files.
261
temp_file_plain_cleanup_handler (void *arg)
263
struct temp_file_cleanup_s *s = arg;
265
/* Note to UTF-8 watchers: this is ok because the path is already in
266
APR internal encoding. */
267
return apr_file_remove (s->path, s->pool);
270
/* An apr pool cleanup handler, this removes a cleanup handler.
273
temp_file_child_cleanup_handler (void *arg)
275
struct temp_file_cleanup_s *s = arg;
277
apr_pool_cleanup_kill (s->pool, s, temp_file_plain_cleanup_handler);
282
/* Register a pool cleanup to delete PATH when POOL is destroyed.
284
* PATH is not copied; caller should probably ensure that it is
285
* allocated in a pool at least as long-lived as POOL.
287
* The main "gotcha" is that if the process forks a child by calling
288
* apr_proc_create, then the child's copy of the cleanup handler will run
289
* and delete the file while the parent still expects it to be around. To
290
* avoid this a child cleanup handler is also installed to kill the plain
291
* cleanup handler in the child.
293
* ### TODO: This a candidate to be a general utility function.
296
temp_file_cleanup_register (const char *path,
299
struct temp_file_cleanup_s *s = apr_palloc (pool, sizeof (*s));
300
SVN_ERR (svn_path_cstring_from_utf8 (&(s->path), path, pool));
302
apr_pool_cleanup_register (s->pool, s, temp_file_plain_cleanup_handler,
303
temp_file_child_cleanup_handler);
308
/* Get the repository version of a file. This makes an RA request to
309
* retrieve the file contents. A pool cleanup handler is installed to
312
* ### TODO: The editor calls this function to get REV1 of the file. Can we
313
* get the file props as well? Then get_wc_prop() could return them later
314
* on enabling the REV1:REV2 request to send diffs.
317
get_file_from_ra (struct file_baton *b)
320
svn_stream_t *fstream;
321
const char *temp_dir;
323
SVN_ERR (svn_io_temp_dir (&temp_dir, b->pool));
324
SVN_ERR (svn_io_open_unique_file (&file, &(b->path_start_revision),
325
svn_path_join (temp_dir, "tmp", b->pool),
326
"", FALSE, b->pool));
328
/* Install a pool cleanup handler to delete the file */
329
SVN_ERR (temp_file_cleanup_register (b->path_start_revision, b->pool));
331
fstream = svn_stream_from_aprfile (file, b->pool);
332
SVN_ERR (svn_ra_get_file (b->edit_baton->ra_session,
334
b->edit_baton->revision,
336
&(b->pristine_props),
338
SVN_ERR (svn_io_file_close (file, b->pool));
343
/* Get the props attached to a directory in the repository. */
345
get_dirprops_from_ra (struct dir_baton *b)
347
SVN_ERR (svn_ra_get_dir (b->edit_baton->ra_session,
349
b->edit_baton->revision,
351
&(b->pristine_props),
358
/* Create an empty file, the path to the file is returned in EMPTY_FILE.
359
* If HAVE_WRITE_LOCK is true, create the file in the working directory,
360
* otherwise use a system temp dir.
363
create_empty_file (const char **empty_file,
364
svn_boolean_t have_write_lock,
368
const char *temp_path;
376
const char *temp_dir;
377
SVN_ERR (svn_io_temp_dir (&temp_dir, pool));
378
temp_path = svn_path_join (temp_dir, "tmp", pool);
381
SVN_ERR (svn_io_open_unique_file (&file, empty_file, temp_path,
383
SVN_ERR (svn_io_file_close (file, pool));
388
/* Return in *PATH_ACCESS the access baton for the directory PATH by
389
searching the access baton set of ADM_ACCESS. If ADM_ACCESS is NULL
390
then *PATH_ACCESS will be NULL. If LENIENT is TRUE then failure to find
391
an access baton will not return an error but will set *PATH_ACCESS to
394
get_path_access (svn_wc_adm_access_t **path_access,
395
svn_wc_adm_access_t *adm_access,
397
svn_boolean_t lenient,
404
svn_error_t *err = svn_wc_adm_retrieve (path_access, adm_access, path,
410
svn_error_clear (err);
418
/* Like get_path_access except the returned access baton, in
419
*PARENT_ACCESS, is for the parent of PATH rather than for PATH
422
get_parent_access (svn_wc_adm_access_t **parent_access,
423
svn_wc_adm_access_t *adm_access,
425
svn_boolean_t lenient,
429
*parent_access = NULL; /* Avoid messing around with paths */
432
const char *parent_path = svn_path_dirname (path, pool);
433
SVN_ERR (get_path_access (parent_access, adm_access, parent_path,
439
/* Get the empty file associated with the edit baton. This is cached so
440
* that it can be reused, all empty files are the same.
443
get_empty_file (struct edit_baton *b,
444
const char **empty_file)
446
/* Create the file if it does not exist */
449
svn_boolean_t have_lock;
450
have_lock = (b->adm_access && svn_wc_adm_locked (b->adm_access));
451
SVN_ERR (create_empty_file (&(b->empty_file), have_lock, b->pool));
453
/* Install a pool cleanup handler to delete the file */
454
SVN_ERR (temp_file_cleanup_register (b->empty_file, b->pool));
457
*empty_file = b->empty_file;
462
/* An editor function. The root of the comparison hierarchy */
464
set_target_revision (void *edit_baton,
465
svn_revnum_t target_revision,
468
struct edit_baton *eb = edit_baton;
470
eb->target_revision = target_revision;
474
/* An editor function. The root of the comparison hierarchy */
476
open_root (void *edit_baton,
477
svn_revnum_t base_revision,
481
struct edit_baton *eb = edit_baton;
482
struct dir_baton *b = make_dir_baton ("", NULL, eb, FALSE, pool);
484
/* Override the wcpath in our baton. */
485
b->wcpath = eb->target ? apr_pstrdup (pool, eb->target) : "";
487
SVN_ERR (get_dirprops_from_ra (b));
493
/* An editor function. */
495
delete_entry (const char *path,
496
svn_revnum_t base_revision,
500
struct dir_baton *pb = parent_baton;
501
struct edit_baton *eb = pb->edit_baton;
502
svn_node_kind_t kind;
503
svn_wc_adm_access_t *adm_access;
504
svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
505
svn_wc_notify_action_t action = svn_wc_notify_skip;
507
/* We need to know if this is a directory or a file */
508
SVN_ERR (svn_ra_check_path (pb->edit_baton->ra_session,
510
pb->edit_baton->revision,
513
SVN_ERR (get_path_access (&adm_access, eb->adm_access, pb->wcpath,
515
if ((! eb->adm_access) || adm_access)
521
const char *mimetype1, *mimetype2;
522
struct file_baton *b;
524
/* Compare a file being deleted against an empty file */
525
b = make_file_baton (path, FALSE, pb->edit_baton, pool);
526
SVN_ERR (get_file_from_ra (b));
527
SVN_ERR (get_empty_file(b->edit_baton, &(b->path_end_revision)));
529
get_file_mime_types (&mimetype1, &mimetype2, b);
531
SVN_ERR (pb->edit_baton->diff_callbacks->file_deleted
532
(adm_access, &state, b->wcpath,
533
b->path_start_revision,
534
b->path_end_revision,
535
mimetype1, mimetype2,
537
b->edit_baton->diff_cmd_baton));
543
SVN_ERR (pb->edit_baton->diff_callbacks->dir_deleted
545
svn_path_join (eb->target, path, pool),
546
pb->edit_baton->diff_cmd_baton));
553
if ((state != svn_wc_notify_state_missing)
554
&& (state != svn_wc_notify_state_obstructed))
555
action = svn_wc_notify_update_delete;
558
if (pb->edit_baton->notify_func)
560
svn_wc_notify_t *notify
561
= svn_wc_create_notify (svn_path_join (eb->target, path, pool),
564
notify->content_state = notify->prop_state = state;
565
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
566
(*pb->edit_baton->notify_func) (pb->edit_baton->notify_baton, notify,
572
/* An editor function. */
574
add_directory (const char *path,
576
const char *copyfrom_path,
577
svn_revnum_t copyfrom_revision,
581
struct dir_baton *pb = parent_baton;
582
struct edit_baton *eb = pb->edit_baton;
584
svn_wc_adm_access_t *adm_access;
585
svn_wc_notify_state_t state;
586
svn_wc_notify_action_t action;
588
/* ### TODO: support copyfrom? */
590
b = make_dir_baton (path, pb, pb->edit_baton, TRUE, pool);
593
SVN_ERR (get_path_access (&adm_access,
594
pb->edit_baton->adm_access, pb->wcpath,
595
pb->edit_baton->dry_run, pool));
597
SVN_ERR (pb->edit_baton->diff_callbacks->dir_added
598
(adm_access, &state, b->wcpath, eb->target_revision,
599
pb->edit_baton->diff_cmd_baton));
601
if ((state == svn_wc_notify_state_missing)
602
|| (state == svn_wc_notify_state_obstructed))
603
action = svn_wc_notify_skip;
605
action = svn_wc_notify_update_add;
607
if (pb->edit_baton->notify_func)
609
svn_wc_notify_t *notify = svn_wc_create_notify (b->wcpath, action, pool);
610
notify->kind = svn_node_dir;
611
(*pb->edit_baton->notify_func) (pb->edit_baton->notify_baton, notify,
618
/* An editor function. */
620
open_directory (const char *path,
622
svn_revnum_t base_revision,
626
struct dir_baton *pb = parent_baton;
629
b = make_dir_baton (path, pb, pb->edit_baton, FALSE, pool);
632
SVN_ERR (get_dirprops_from_ra (b));
638
/* An editor function. */
640
add_file (const char *path,
642
const char *copyfrom_path,
643
svn_revnum_t copyfrom_revision,
647
struct dir_baton *pb = parent_baton;
648
struct file_baton *b;
650
/* ### TODO: support copyfrom? */
652
b = make_file_baton (path, TRUE, pb->edit_baton, pool);
655
SVN_ERR (get_empty_file (b->edit_baton, &(b->path_start_revision)));
656
b->pristine_props = pb->edit_baton->empty_hash;
661
/* An editor function. */
663
open_file (const char *path,
665
svn_revnum_t base_revision,
669
struct dir_baton *pb = parent_baton;
670
struct file_baton *b;
672
b = make_file_baton (path, FALSE, pb->edit_baton, pool);
675
SVN_ERR (get_file_from_ra (b));
680
/* An editor function. Do the work of applying the text delta. */
682
window_handler (svn_txdelta_window_t *window,
685
struct file_baton *b = window_baton;
687
SVN_ERR (b->apply_handler (window, b->apply_baton));
691
SVN_ERR (svn_io_file_close (b->file_start_revision, b->pool));
692
SVN_ERR (svn_io_file_close (b->file_end_revision, b->pool));
698
/* An editor function. */
700
apply_textdelta (void *file_baton,
701
const char *base_checksum,
703
svn_txdelta_window_handler_t *handler,
704
void **handler_baton)
706
struct file_baton *b = file_baton;
707
svn_boolean_t have_lock;
709
/* Open the file to be used as the base for second revision */
710
SVN_ERR (svn_io_file_open (&(b->file_start_revision),
711
b->path_start_revision,
712
APR_READ, APR_OS_DEFAULT, b->pool));
714
/* Open the file that will become the second revision after applying the
715
text delta, it starts empty */
716
have_lock = (b->edit_baton->adm_access
717
&& svn_wc_adm_locked (b->edit_baton->adm_access));
718
SVN_ERR (create_empty_file (&(b->path_end_revision), have_lock, b->pool));
719
SVN_ERR (temp_file_cleanup_register (b->path_end_revision, b->pool));
720
SVN_ERR (svn_io_file_open (&(b->file_end_revision), b->path_end_revision,
721
APR_WRITE, APR_OS_DEFAULT, b->pool));
723
svn_txdelta_apply (svn_stream_from_aprfile (b->file_start_revision, b->pool),
724
svn_stream_from_aprfile (b->file_end_revision, b->pool),
728
&(b->apply_handler), &(b->apply_baton));
730
*handler = window_handler;
731
*handler_baton = file_baton;
736
/* An editor function. When the file is closed we have a temporary
737
* file containing a pristine version of the repository file. This can
738
* be compared against the working copy.
740
* ### Ignore TEXT_CHECKSUM for now. Someday we can use it to verify
741
* ### the integrity of the file being diffed. Done efficiently, this
742
* ### would probably involve calculating the checksum as the data is
743
* ### received, storing the final checksum in the file_baton, and
744
* ### comparing against it here.
747
close_file (void *file_baton,
748
const char *text_checksum,
751
struct file_baton *b = file_baton;
752
struct edit_baton *eb = b->edit_baton;
753
svn_wc_adm_access_t *adm_access;
755
svn_wc_notify_action_t action;
756
svn_wc_notify_state_t
757
content_state = svn_wc_notify_state_unknown,
758
prop_state = svn_wc_notify_state_unknown;
760
err = get_parent_access (&adm_access, eb->adm_access,
761
b->wcpath, eb->dry_run, b->pool);
763
if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED)
765
/* ### maybe try to stat the local b->wcpath? */
766
/* If the file path doesn't exist, then send a 'skipped' notification. */
769
svn_wc_notify_t *notify = svn_wc_create_notify (b->wcpath,
772
notify->kind = svn_node_file;
773
notify->content_state = svn_wc_notify_state_missing;
774
notify->prop_state = prop_state;
775
(*eb->notify_func) (eb->notify_baton, notify, pool);
778
svn_error_clear (err);
784
if (b->path_end_revision || b->propchanges->nelts > 0)
786
const char *mimetype1, *mimetype2;
787
get_file_mime_types (&mimetype1, &mimetype2, b);
790
SVN_ERR (eb->diff_callbacks->file_added
791
(adm_access, &content_state, &prop_state,
793
b->path_end_revision ? b->path_start_revision : NULL,
794
b->path_end_revision,
796
b->edit_baton->target_revision,
797
mimetype1, mimetype2,
798
b->propchanges, b->pristine_props,
799
b->edit_baton->diff_cmd_baton));
801
SVN_ERR (eb->diff_callbacks->file_changed
802
(adm_access, &content_state, &prop_state,
804
b->path_end_revision ? b->path_start_revision : NULL,
805
b->path_end_revision,
806
b->edit_baton->revision,
807
b->edit_baton->target_revision,
808
mimetype1, mimetype2,
809
b->propchanges, b->pristine_props,
810
b->edit_baton->diff_cmd_baton));
814
/* ### Is b->path the repos path? Probably. This doesn't really
815
matter while issue #748 (svn merge only happens in ".") is
816
outstanding. But when we take a wc_path as an argument to
817
merge, then we'll need to pass around a wc path somehow. */
819
if ((content_state == svn_wc_notify_state_missing)
820
|| (content_state == svn_wc_notify_state_obstructed))
821
action = svn_wc_notify_skip;
823
action = svn_wc_notify_update_add;
825
action = svn_wc_notify_update_update;
829
svn_wc_notify_t *notify = svn_wc_create_notify (b->wcpath, action,
831
notify->kind = svn_node_file;
832
notify->content_state = content_state;
833
notify->prop_state = prop_state;
834
(*eb->notify_func) (eb->notify_baton, notify, pool);
840
/* An editor function. */
842
close_directory (void *dir_baton,
845
struct dir_baton *b = dir_baton;
846
struct edit_baton *eb = b->edit_baton;
847
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
850
if (b->propchanges->nelts > 0)
852
svn_wc_adm_access_t *adm_access;
853
err = get_path_access (&adm_access, eb->adm_access, b->wcpath,
854
eb->dry_run, b->pool);
856
if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED)
858
/* ### maybe try to stat the local b->wcpath? */
859
/* If the path doesn't exist, then send a 'skipped' notification. */
862
svn_wc_notify_t *notify
863
= svn_wc_create_notify (b->wcpath, svn_wc_notify_skip, pool);
864
notify->kind = svn_node_dir;
865
notify->content_state = notify->prop_state
866
= svn_wc_notify_state_missing;
867
(*eb->notify_func) (eb->notify_baton, notify, pool);
869
svn_error_clear (err);
875
/* Don't do the props_changed stuff if this is a dry_run and we don't
876
have an access baton, since in that case the directory will already
877
have been recognised as added, in which case they cannot conflict. */
878
if (! eb->dry_run || adm_access)
879
SVN_ERR (eb->diff_callbacks->dir_props_changed
880
(adm_access, &prop_state,
882
b->propchanges, b->pristine_props,
883
b->edit_baton->diff_cmd_baton));
888
svn_wc_notify_t *notify
889
= svn_wc_create_notify (b->wcpath, svn_wc_notify_update_update, pool);
890
notify->kind = svn_node_dir;
891
notify->content_state = svn_wc_notify_state_inapplicable;
892
notify->prop_state = prop_state;
893
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
894
(*eb->notify_func) (eb->notify_baton, notify, pool);
901
/* An editor function. */
903
change_file_prop (void *file_baton,
905
const svn_string_t *value,
908
struct file_baton *b = file_baton;
909
svn_prop_t *propchange;
911
propchange = apr_array_push (b->propchanges);
912
propchange->name = apr_pstrdup (b->pool, name);
913
propchange->value = value ? svn_string_dup (value, b->pool) : NULL;
918
/* An editor function. */
920
change_dir_prop (void *dir_baton,
922
const svn_string_t *value,
925
struct dir_baton *db = dir_baton;
926
svn_prop_t *propchange;
928
propchange = apr_array_push (db->propchanges);
929
propchange->name = apr_pstrdup (db->pool, name);
930
propchange->value = value ? svn_string_dup (value, db->pool) : NULL;
936
/* An editor function. */
938
close_edit (void *edit_baton,
941
struct edit_baton *eb = edit_baton;
943
svn_pool_destroy (eb->pool);
948
/* Create a repository diff editor and baton. */
950
svn_client__get_diff_editor (const char *target,
951
svn_wc_adm_access_t *adm_access,
952
const svn_wc_diff_callbacks2_t *diff_callbacks,
953
void *diff_cmd_baton,
954
svn_boolean_t recurse,
955
svn_boolean_t dry_run,
956
svn_ra_session_t *ra_session,
957
svn_revnum_t revision,
958
svn_wc_notify_func2_t notify_func,
960
svn_cancel_func_t cancel_func,
962
const svn_delta_editor_t **editor,
966
apr_pool_t *subpool = svn_pool_create (pool);
967
svn_delta_editor_t *tree_editor = svn_delta_default_editor (subpool);
968
struct edit_baton *eb = apr_palloc (subpool, sizeof (*eb));
971
eb->adm_access = adm_access;
972
eb->diff_callbacks = diff_callbacks;
973
eb->diff_cmd_baton = diff_cmd_baton;
974
eb->recurse = recurse;
975
eb->dry_run = dry_run;
976
eb->ra_session = ra_session;
977
eb->revision = revision;
978
eb->empty_file = NULL;
979
eb->empty_hash = apr_hash_make (subpool);
981
eb->notify_func = notify_func;
982
eb->notify_baton = notify_baton;
984
tree_editor->set_target_revision = set_target_revision;
985
tree_editor->open_root = open_root;
986
tree_editor->delete_entry = delete_entry;
987
tree_editor->add_directory = add_directory;
988
tree_editor->open_directory = open_directory;
989
tree_editor->add_file = add_file;
990
tree_editor->open_file = open_file;
991
tree_editor->apply_textdelta = apply_textdelta;
992
tree_editor->close_file = close_file;
993
tree_editor->close_directory = close_directory;
994
tree_editor->change_file_prop = change_file_prop;
995
tree_editor->change_dir_prop = change_dir_prop;
996
tree_editor->close_edit = close_edit;
998
SVN_ERR (svn_delta_get_cancellation_editor (cancel_func,
1006
return SVN_NO_ERROR;