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