diff_local.c revision 299742
1/* 2 * diff_local.c: comparing local trees with each other 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* ==================================================================== */ 25 26 27 28/*** Includes. ***/ 29 30#include <apr_strings.h> 31#include <apr_pools.h> 32#include <apr_hash.h> 33#include "svn_hash.h" 34#include "svn_types.h" 35#include "svn_wc.h" 36#include "svn_diff.h" 37#include "svn_client.h" 38#include "svn_string.h" 39#include "svn_error.h" 40#include "svn_dirent_uri.h" 41#include "svn_io.h" 42#include "svn_pools.h" 43#include "svn_props.h" 44#include "svn_sorts.h" 45#include "svn_subst.h" 46#include "client.h" 47 48#include "private/svn_sorts_private.h" 49#include "private/svn_wc_private.h" 50#include "private/svn_diff_tree.h" 51 52#include "svn_private_config.h" 53 54 55/* Try to get properties for LOCAL_ABSPATH and return them in the property 56 * hash *PROPS. If there are no properties because LOCAL_ABSPATH is not 57 * versioned, return an empty property hash. */ 58static svn_error_t * 59get_props(apr_hash_t **props, 60 const char *local_abspath, 61 svn_wc_context_t *wc_ctx, 62 apr_pool_t *result_pool, 63 apr_pool_t *scratch_pool) 64{ 65 svn_error_t *err; 66 67 err = svn_wc_prop_list2(props, wc_ctx, local_abspath, result_pool, 68 scratch_pool); 69 if (err) 70 { 71 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND || 72 err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) 73 { 74 svn_error_clear(err); 75 *props = apr_hash_make(result_pool); 76 77 /* ### Apply autoprops, like 'svn add' would? */ 78 } 79 else 80 return svn_error_trace(err); 81 } 82 83 return SVN_NO_ERROR; 84} 85 86/* Forward declaration */ 87static svn_error_t * 88do_file_diff(const char *left_abspath, 89 const char *right_abspath, 90 const char *left_root_abspath, 91 const char *right_root_abspath, 92 svn_boolean_t left_only, 93 svn_boolean_t right_only, 94 void *parent_baton, 95 const svn_diff_tree_processor_t *diff_processor, 96 svn_client_ctx_t *ctx, 97 apr_pool_t *scratch_pool); 98 99/* Forward declaration */ 100static svn_error_t * 101do_dir_diff(const char *left_abspath, 102 const char *right_abspath, 103 const char *left_root_abspath, 104 const char *right_root_abspath, 105 svn_boolean_t left_only, 106 svn_boolean_t right_only, 107 svn_boolean_t left_before_right, 108 svn_depth_t depth, 109 void *parent_baton, 110 const svn_diff_tree_processor_t *diff_processor, 111 svn_client_ctx_t *ctx, 112 apr_pool_t *scratch_pool); 113 114/* Produce a diff of depth DEPTH between two arbitrary directories at 115 * LEFT_ABSPATH1 and RIGHT_ABSPATH2, using the provided diff callbacks 116 * to show file changes and, for versioned nodes, property changes. 117 * 118 * Report paths as relative from LEFT_ROOT_ABSPATH/RIGHT_ROOT_ABSPATH. 119 * 120 * If LEFT_ONLY is TRUE, only the left source exists (= everything will 121 * be reported as deleted). If RIGHT_ONLY is TRUE, only the right source 122 * exists (= everything will be reported as added). 123 * 124 * If LEFT_BEFORE_RIGHT is TRUE and left and right are unrelated, left is 125 * reported first. If false, right is reported first. (This is to allow 126 * producing a proper inverse diff). 127 * 128 * Walk the sources according to depth, and report with parent baton 129 * PARENT_BATON. */ 130static svn_error_t * 131inner_dir_diff(const char *left_abspath, 132 const char *right_abspath, 133 const char *left_root_abspath, 134 const char *right_root_abspath, 135 svn_boolean_t left_only, 136 svn_boolean_t right_only, 137 svn_boolean_t left_before_right, 138 svn_depth_t depth, 139 void *parent_baton, 140 const svn_diff_tree_processor_t *diff_processor, 141 svn_client_ctx_t *ctx, 142 apr_pool_t *scratch_pool) 143{ 144 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 145 apr_hash_t *left_dirents; 146 apr_hash_t *right_dirents; 147 apr_array_header_t *sorted_dirents; 148 svn_error_t *err; 149 svn_depth_t depth_below_here; 150 int i; 151 152 SVN_ERR_ASSERT(depth >= svn_depth_files && depth <= svn_depth_infinity); 153 154 if (!right_only) 155 { 156 err = svn_io_get_dirents3(&left_dirents, left_abspath, FALSE, 157 scratch_pool, iterpool); 158 159 if (err && (APR_STATUS_IS_ENOENT(err->apr_err) 160 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 161 { 162 svn_error_clear(err); 163 left_dirents = apr_hash_make(scratch_pool); 164 right_only = TRUE; 165 } 166 else 167 SVN_ERR(err); 168 } 169 else 170 left_dirents = apr_hash_make(scratch_pool); 171 172 if (!left_only) 173 { 174 err = svn_io_get_dirents3(&right_dirents, right_abspath, FALSE, 175 scratch_pool, iterpool); 176 177 if (err && (APR_STATUS_IS_ENOENT(err->apr_err) 178 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 179 { 180 svn_error_clear(err); 181 right_dirents = apr_hash_make(scratch_pool); 182 right_only = TRUE; 183 } 184 else 185 SVN_ERR(err); 186 } 187 else 188 right_dirents = apr_hash_make(scratch_pool); 189 190 if (left_only && right_only) 191 return SVN_NO_ERROR; /* Somebody deleted the directory?? */ 192 193 if (depth != svn_depth_infinity) 194 depth_below_here = svn_depth_empty; 195 else 196 depth_below_here = svn_depth_infinity; 197 198 sorted_dirents = svn_sort__hash(apr_hash_merge(iterpool, left_dirents, 199 right_dirents, NULL, NULL), 200 svn_sort_compare_items_as_paths, 201 scratch_pool); 202 203 for (i = 0; i < sorted_dirents->nelts; i++) 204 { 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; 210 211 svn_pool_clear(iterpool); 212 213 if (ctx->cancel_func) 214 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 215 216 if (svn_wc_is_adm_dir(elt->key, iterpool)) 217 continue; 218 219 left_dirent = right_only ? NULL : svn_hash_gets(left_dirents, elt->key); 220 right_dirent = left_only ? NULL : svn_hash_gets(right_dirents, elt->key); 221 222 child_left_abspath = svn_dirent_join(left_abspath, elt->key, iterpool); 223 child_right_abspath = svn_dirent_join(right_abspath, elt->key, iterpool); 224 225 if (((left_dirent == NULL) != (right_dirent == NULL)) 226 || (left_dirent->kind != right_dirent->kind)) 227 { 228 /* Report delete and/or add */ 229 if (left_dirent && left_before_right) 230 { 231 if (left_dirent->kind == svn_node_file) 232 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, 233 left_root_abspath, right_root_abspath, 234 TRUE, FALSE, parent_baton, 235 diff_processor, ctx, iterpool)); 236 else if (depth >= svn_depth_immediates) 237 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, 238 left_root_abspath, right_root_abspath, 239 TRUE, FALSE, left_before_right, 240 depth_below_here, parent_baton, 241 diff_processor, ctx, iterpool)); 242 } 243 244 if (right_dirent) 245 { 246 if (right_dirent->kind == svn_node_file) 247 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, 248 left_root_abspath, right_root_abspath, 249 FALSE, TRUE, parent_baton, 250 diff_processor, ctx, iterpool)); 251 else if (depth >= svn_depth_immediates) 252 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, 253 left_root_abspath, right_root_abspath, 254 FALSE, TRUE, left_before_right, 255 depth_below_here, parent_baton, 256 diff_processor, ctx, iterpool)); 257 } 258 259 if (left_dirent && !left_before_right) 260 { 261 if (left_dirent->kind == svn_node_file) 262 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, 263 left_root_abspath, right_root_abspath, 264 TRUE, FALSE, parent_baton, 265 diff_processor, ctx, iterpool)); 266 else if (depth >= svn_depth_immediates) 267 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, 268 left_root_abspath, right_root_abspath, 269 TRUE, FALSE, left_before_right, 270 depth_below_here, parent_baton, 271 diff_processor, ctx, iterpool)); 272 } 273 } 274 else if (left_dirent->kind == svn_node_file) 275 { 276 /* Perform file-file diff */ 277 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, 278 left_root_abspath, right_root_abspath, 279 FALSE, FALSE, parent_baton, 280 diff_processor, ctx, iterpool)); 281 } 282 else if (depth >= svn_depth_immediates) 283 { 284 /* Perform dir-dir diff */ 285 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, 286 left_root_abspath, right_root_abspath, 287 FALSE, FALSE, left_before_right, 288 depth_below_here, parent_baton, 289 diff_processor, ctx, iterpool)); 290 } 291 } 292 293 return SVN_NO_ERROR; 294} 295 296/* Translates *LEFT_ABSPATH to a temporary file if PROPS specify that the 297 file needs translation. *LEFT_ABSPATH is updated to point to a file that 298 lives at least as long as RESULT_POOL when translation is necessary. 299 Otherwise the value is not updated */ 300static svn_error_t * 301translate_if_necessary(const char **local_abspath, 302 apr_hash_t *props, 303 svn_cancel_func_t cancel_func, 304 void *cancel_baton, 305 apr_pool_t *result_pool, 306 apr_pool_t *scratch_pool) 307{ 308 const svn_string_t *eol_style_val; 309 const svn_string_t *keywords_val; 310 svn_subst_eol_style_t eol_style; 311 const char *eol; 312 apr_hash_t *keywords; 313 svn_stream_t *contents; 314 svn_stream_t *dst; 315 316 /* if (svn_hash_gets(props, SVN_PROP_SPECIAL)) 317 ### TODO: Implement */ 318 319 eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE); 320 keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS); 321 322 if (eol_style_val) 323 svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data); 324 else 325 { 326 eol = NULL; 327 eol_style = svn_subst_eol_style_none; 328 } 329 330 if (keywords_val) 331 SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data, 332 APR_STRINGIFY(SVN_INVALID_REVNUM), 333 "", "", 0, "", scratch_pool)); 334 else 335 keywords = NULL; 336 337 if (!svn_subst_translation_required(eol_style, eol, keywords, FALSE, FALSE)) 338 return SVN_NO_ERROR; 339 340 SVN_ERR(svn_stream_open_readonly(&contents, *local_abspath, 341 scratch_pool, scratch_pool)); 342 343 SVN_ERR(svn_stream_open_unique(&dst, local_abspath, NULL, 344 svn_io_file_del_on_pool_cleanup, 345 result_pool, scratch_pool)); 346 347 dst = svn_subst_stream_translated(dst, eol, TRUE /* repair */, 348 keywords, FALSE /* expand */, 349 scratch_pool); 350 351 SVN_ERR(svn_stream_copy3(contents, dst, cancel_func, cancel_baton, 352 scratch_pool)); 353 354 return SVN_NO_ERROR; 355} 356 357/* Handles reporting of a file for inner_dir_diff */ 358static svn_error_t * 359do_file_diff(const char *left_abspath, 360 const char *right_abspath, 361 const char *left_root_abspath, 362 const char *right_root_abspath, 363 svn_boolean_t left_only, 364 svn_boolean_t right_only, 365 void *parent_baton, 366 const svn_diff_tree_processor_t *diff_processor, 367 svn_client_ctx_t *ctx, 368 apr_pool_t *scratch_pool) 369{ 370 const char *relpath; 371 svn_diff_source_t *left_source; 372 svn_diff_source_t *right_source; 373 svn_boolean_t skip = FALSE; 374 apr_hash_t *left_props; 375 apr_hash_t *right_props; 376 void *file_baton; 377 378 relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath); 379 380 if (! right_only) 381 left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 382 else 383 left_source = NULL; 384 385 if (! left_only) 386 right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 387 else 388 right_source = NULL; 389 390 SVN_ERR(diff_processor->file_opened(&file_baton, &skip, 391 relpath, 392 left_source, 393 right_source, 394 NULL /* copyfrom_source */, 395 parent_baton, 396 diff_processor, 397 scratch_pool, 398 scratch_pool)); 399 400 if (skip) 401 return SVN_NO_ERROR; 402 403 if (! right_only) 404 { 405 SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx, 406 scratch_pool, scratch_pool)); 407 408 /* We perform a mimetype detection to avoid diffing binary files 409 for textual changes.*/ 410 if (! svn_hash_gets(left_props, SVN_PROP_MIME_TYPE)) 411 { 412 const char *mime_type; 413 414 /* ### Use libmagic magic? */ 415 SVN_ERR(svn_io_detect_mimetype2(&mime_type, left_abspath, 416 ctx->mimetypes_map, scratch_pool)); 417 418 if (mime_type) 419 svn_hash_sets(left_props, SVN_PROP_MIME_TYPE, 420 svn_string_create(mime_type, scratch_pool)); 421 } 422 423 SVN_ERR(translate_if_necessary(&left_abspath, left_props, 424 ctx->cancel_func, ctx->cancel_baton, 425 scratch_pool, scratch_pool)); 426 } 427 else 428 left_props = NULL; 429 430 if (! left_only) 431 { 432 SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx, 433 scratch_pool, scratch_pool)); 434 435 /* We perform a mimetype detection to avoid diffing binary files 436 for textual changes.*/ 437 if (! svn_hash_gets(right_props, SVN_PROP_MIME_TYPE)) 438 { 439 const char *mime_type; 440 441 /* ### Use libmagic magic? */ 442 SVN_ERR(svn_io_detect_mimetype2(&mime_type, right_abspath, 443 ctx->mimetypes_map, scratch_pool)); 444 445 if (mime_type) 446 svn_hash_sets(right_props, SVN_PROP_MIME_TYPE, 447 svn_string_create(mime_type, scratch_pool)); 448 } 449 450 SVN_ERR(translate_if_necessary(&right_abspath, right_props, 451 ctx->cancel_func, ctx->cancel_baton, 452 scratch_pool, scratch_pool)); 453 454 } 455 else 456 right_props = NULL; 457 458 if (left_only) 459 { 460 SVN_ERR(diff_processor->file_deleted(relpath, 461 left_source, 462 left_abspath, 463 left_props, 464 file_baton, 465 diff_processor, 466 scratch_pool)); 467 } 468 else if (right_only) 469 { 470 SVN_ERR(diff_processor->file_added(relpath, 471 NULL /* copyfrom_source */, 472 right_source, 473 NULL /* copyfrom_file */, 474 right_abspath, 475 NULL /* copyfrom_props */, 476 right_props, 477 file_baton, 478 diff_processor, 479 scratch_pool)); 480 } 481 else 482 { 483 /* ### Perform diff -> close/changed */ 484 svn_boolean_t same; 485 apr_array_header_t *prop_changes; 486 487 SVN_ERR(svn_io_files_contents_same_p(&same, left_abspath, right_abspath, 488 scratch_pool)); 489 490 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, 491 scratch_pool)); 492 493 if (!same || prop_changes->nelts > 0) 494 { 495 SVN_ERR(diff_processor->file_changed(relpath, 496 left_source, 497 right_source, 498 same ? NULL : left_abspath, 499 same ? NULL : right_abspath, 500 left_props, 501 right_props, 502 !same, 503 prop_changes, 504 file_baton, 505 diff_processor, 506 scratch_pool)); 507 } 508 else 509 { 510 SVN_ERR(diff_processor->file_closed(relpath, 511 left_source, 512 right_source, 513 file_baton, 514 diff_processor, 515 scratch_pool)); 516 } 517 } 518 return SVN_NO_ERROR; 519} 520 521 522/* Handles reporting of a directory and its children for inner_dir_diff */ 523static svn_error_t * 524do_dir_diff(const char *left_abspath, 525 const char *right_abspath, 526 const char *left_root_abspath, 527 const char *right_root_abspath, 528 svn_boolean_t left_only, 529 svn_boolean_t right_only, 530 svn_boolean_t left_before_right, 531 svn_depth_t depth, 532 void *parent_baton, 533 const svn_diff_tree_processor_t *diff_processor, 534 svn_client_ctx_t *ctx, 535 apr_pool_t *scratch_pool) 536{ 537 const char *relpath; 538 svn_diff_source_t *left_source; 539 svn_diff_source_t *right_source; 540 svn_boolean_t skip = FALSE; 541 svn_boolean_t skip_children = FALSE; 542 void *dir_baton; 543 apr_hash_t *left_props; 544 apr_hash_t *right_props; 545 546 relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath); 547 548 if (! right_only) 549 { 550 left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 551 SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx, 552 scratch_pool, scratch_pool)); 553 } 554 else 555 { 556 left_source = NULL; 557 left_props = NULL; 558 } 559 560 if (! left_only) 561 { 562 right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 563 SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx, 564 scratch_pool, scratch_pool)); 565 } 566 else 567 { 568 right_source = NULL; 569 right_props = NULL; 570 } 571 572 SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, 573 relpath, 574 left_source, 575 right_source, 576 NULL /* copyfrom_source */, 577 parent_baton, 578 diff_processor, 579 scratch_pool, scratch_pool)); 580 581 if (!skip_children) 582 { 583 if (depth >= svn_depth_files) 584 SVN_ERR(inner_dir_diff(left_abspath, right_abspath, 585 left_root_abspath, right_root_abspath, 586 left_only, right_only, 587 left_before_right, depth, 588 dir_baton, 589 diff_processor, ctx, scratch_pool)); 590 } 591 else if (skip) 592 return SVN_NO_ERROR; 593 594 if (left_props && right_props) 595 { 596 apr_array_header_t *prop_diffs; 597 598 SVN_ERR(svn_prop_diffs(&prop_diffs, right_props, left_props, 599 scratch_pool)); 600 601 if (prop_diffs->nelts) 602 { 603 SVN_ERR(diff_processor->dir_changed(relpath, 604 left_source, 605 right_source, 606 left_props, 607 right_props, 608 prop_diffs, 609 dir_baton, 610 diff_processor, 611 scratch_pool)); 612 return SVN_NO_ERROR; 613 } 614 } 615 616 if (left_source && right_source) 617 { 618 SVN_ERR(diff_processor->dir_closed(relpath, 619 left_source, 620 right_source, 621 dir_baton, 622 diff_processor, 623 scratch_pool)); 624 } 625 else if (left_source) 626 { 627 SVN_ERR(diff_processor->dir_deleted(relpath, 628 left_source, 629 left_props, 630 dir_baton, 631 diff_processor, 632 scratch_pool)); 633 } 634 else 635 { 636 SVN_ERR(diff_processor->dir_added(relpath, 637 NULL /* copyfrom_source */, 638 right_source, 639 NULL /* copyfrom_props */, 640 right_props, 641 dir_baton, 642 diff_processor, 643 scratch_pool)); 644 } 645 646 return SVN_NO_ERROR; 647} 648 649svn_error_t * 650svn_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, 654 svn_depth_t depth, 655 const svn_diff_tree_processor_t *diff_processor, 656 svn_client_ctx_t *ctx, 657 apr_pool_t *result_pool, 658 apr_pool_t *scratch_pool) 659{ 660 svn_node_kind_t left_kind; 661 svn_node_kind_t right_kind; 662 const char *left_root_abspath; 663 const char *right_root_abspath; 664 svn_boolean_t left_before_right = TRUE; /* Future argument? */ 665 666 if (depth == svn_depth_unknown) 667 depth = svn_depth_infinity; 668 669 SVN_ERR(svn_io_check_resolved_path(left_abspath, &left_kind, scratch_pool)); 670 SVN_ERR(svn_io_check_resolved_path(right_abspath, &right_kind, scratch_pool)); 671 672 if (depth == svn_depth_unknown) 673 depth = svn_depth_infinity; 674 675 if (left_kind == svn_node_dir && right_kind == svn_node_dir) 676 { 677 left_root_abspath = left_abspath; 678 right_root_abspath = right_abspath; 679 680 if (root_relpath) 681 *root_relpath = ""; 682 if (root_is_dir) 683 *root_is_dir = TRUE; 684 } 685 else 686 { 687 svn_dirent_split(&left_root_abspath, root_relpath, left_abspath, 688 scratch_pool); 689 right_root_abspath = svn_dirent_dirname(right_abspath, scratch_pool); 690 691 if (root_relpath) 692 *root_relpath = apr_pstrdup(result_pool, *root_relpath); 693 if (root_is_dir) 694 *root_is_dir = FALSE; 695 } 696 697 if (left_kind == svn_node_dir && right_kind == svn_node_dir) 698 { 699 SVN_ERR(do_dir_diff(left_abspath, right_abspath, 700 left_root_abspath, right_root_abspath, 701 FALSE, FALSE, left_before_right, 702 depth, NULL /* parent_baton */, 703 diff_processor, ctx, scratch_pool)); 704 } 705 else if (left_kind == svn_node_file && right_kind == svn_node_file) 706 { 707 SVN_ERR(do_file_diff(left_abspath, right_abspath, 708 left_root_abspath, right_root_abspath, 709 FALSE, FALSE, 710 NULL /* parent_baton */, 711 diff_processor, ctx, scratch_pool)); 712 } 713 else if (left_kind == svn_node_file || left_kind == svn_node_dir 714 || right_kind == svn_node_file || right_kind == svn_node_dir) 715 { 716 void *dir_baton; 717 svn_boolean_t skip = FALSE; 718 svn_boolean_t skip_children = FALSE; 719 svn_diff_source_t *left_src; 720 svn_diff_source_t *right_src; 721 722 left_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 723 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 724 725 /* The root is replaced... */ 726 /* Report delete and/or add */ 727 728 SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, "", 729 left_src, 730 right_src, 731 NULL /* copyfrom_src */, 732 NULL, 733 diff_processor, 734 scratch_pool, scratch_pool)); 735 736 if (skip) 737 return SVN_NO_ERROR; 738 else if (!skip_children) 739 { 740 if (left_before_right) 741 { 742 if (left_kind == svn_node_file) 743 SVN_ERR(do_file_diff(left_abspath, right_abspath, 744 left_root_abspath, right_root_abspath, 745 TRUE, FALSE, NULL /* parent_baton */, 746 diff_processor, ctx, scratch_pool)); 747 else if (left_kind == svn_node_dir) 748 SVN_ERR(do_dir_diff(left_abspath, right_abspath, 749 left_root_abspath, right_root_abspath, 750 TRUE, FALSE, left_before_right, 751 depth, NULL /* parent_baton */, 752 diff_processor, ctx, scratch_pool)); 753 } 754 755 if (right_kind == svn_node_file) 756 SVN_ERR(do_file_diff(left_abspath, right_abspath, 757 left_root_abspath, right_root_abspath, 758 FALSE, TRUE, NULL /* parent_baton */, 759 diff_processor, ctx, scratch_pool)); 760 else if (right_kind == svn_node_dir) 761 SVN_ERR(do_dir_diff(left_abspath, right_abspath, 762 left_root_abspath, right_root_abspath, 763 FALSE, TRUE, left_before_right, 764 depth, NULL /* parent_baton */, 765 diff_processor, ctx, scratch_pool)); 766 767 if (! left_before_right) 768 { 769 if (left_kind == svn_node_file) 770 SVN_ERR(do_file_diff(left_abspath, right_abspath, 771 left_root_abspath, right_root_abspath, 772 TRUE, FALSE, NULL /* parent_baton */, 773 diff_processor, ctx, scratch_pool)); 774 else if (left_kind == svn_node_dir) 775 SVN_ERR(do_dir_diff(left_abspath, right_abspath, 776 left_root_abspath, right_root_abspath, 777 TRUE, FALSE, left_before_right, 778 depth, NULL /* parent_baton */, 779 diff_processor, ctx, scratch_pool)); 780 } 781 } 782 783 SVN_ERR(diff_processor->dir_closed("", 784 left_src, 785 right_src, 786 dir_baton, 787 diff_processor, 788 scratch_pool)); 789 } 790 else 791 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 792 _("'%s' is not a file or directory"), 793 svn_dirent_local_style( 794 (left_kind == svn_node_none) 795 ? left_abspath 796 : right_abspath, 797 scratch_pool)); 798 799 return SVN_NO_ERROR; 800} 801