diff_editor.c revision 251881
1/* 2 * diff_editor.c -- The diff editor for comparing the working copy against the 3 * repository. 4 * 5 * ==================================================================== 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 * ==================================================================== 23 */ 24 25/* 26 * This code uses an svn_delta_editor_t editor driven by 27 * svn_wc_crawl_revisions (like the update command) to retrieve the 28 * differences between the working copy and the requested repository 29 * version. Rather than updating the working copy, this new editor creates 30 * temporary files that contain the pristine repository versions. When the 31 * crawler closes the files the editor calls back to a client layer 32 * function to compare the working copy and the temporary file. There is 33 * only ever one temporary file in existence at any time. 34 * 35 * When the crawler closes a directory, the editor then calls back to the 36 * client layer to compare any remaining files that may have been modified 37 * locally. Added directories do not have corresponding temporary 38 * directories created, as they are not needed. 39 * 40 * The diff result from this editor is a combination of the restructuring 41 * operations from the repository with the local restructurings since checking 42 * out. 43 * 44 * ### TODO: Make sure that we properly support and report multi layered 45 * operations instead of only simple file replacements. 46 * 47 * ### TODO: Replacements where the node kind changes needs support. It 48 * mostly works when the change is in the repository, but not when it is 49 * in the working copy. 50 * 51 * ### TODO: Do we need to support copyfrom? 52 * 53 */ 54 55#include <apr_hash.h> 56#include <apr_md5.h> 57 58#include <assert.h> 59 60#include "svn_error.h" 61#include "svn_pools.h" 62#include "svn_dirent_uri.h" 63#include "svn_path.h" 64#include "svn_hash.h" 65#include "svn_sorts.h" 66 67#include "private/svn_subr_private.h" 68#include "private/svn_wc_private.h" 69#include "private/svn_diff_tree.h" 70#include "private/svn_editor.h" 71 72#include "wc.h" 73#include "props.h" 74#include "adm_files.h" 75#include "translate.h" 76#include "diff.h" 77 78#include "svn_private_config.h" 79 80/*-------------------------------------------------------------------------*/ 81 82 83/* Overall crawler editor baton. 84 */ 85struct edit_baton_t 86{ 87 /* A wc db. */ 88 svn_wc__db_t *db; 89 90 /* A diff tree processor, receiving the result of the diff. */ 91 const svn_diff_tree_processor_t *processor; 92 93 /* A boolean indicating whether local additions should be reported before 94 remote deletes. The processor can transform adds in deletes and deletes 95 in adds, but it can't reorder the output. */ 96 svn_boolean_t local_before_remote; 97 98 /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */ 99 const char *target; 100 const char *anchor_abspath; 101 102 /* Target revision */ 103 svn_revnum_t revnum; 104 105 /* Was the root opened? */ 106 svn_boolean_t root_opened; 107 108 /* How does this diff descend as seen from target? */ 109 svn_depth_t depth; 110 111 /* Should this diff ignore node ancestry? */ 112 svn_boolean_t ignore_ancestry; 113 114 /* Possibly diff repos against text-bases instead of working files. */ 115 svn_boolean_t diff_pristine; 116 117 /* Hash whose keys are const char * changelist names. */ 118 apr_hash_t *changelist_hash; 119 120 /* Cancel function/baton */ 121 svn_cancel_func_t cancel_func; 122 void *cancel_baton; 123 124 apr_pool_t *pool; 125}; 126 127/* Directory level baton. 128 */ 129struct dir_baton_t 130{ 131 /* Reference to parent directory baton (or NULL for the root) */ 132 struct dir_baton_t *parent_baton; 133 134 /* The depth at which this directory should be diffed. */ 135 svn_depth_t depth; 136 137 /* The name and path of this directory as if they would be/are in the 138 local working copy. */ 139 const char *name; 140 const char *relpath; 141 const char *local_abspath; 142 143 /* TRUE if the file is added by the editor drive. */ 144 svn_boolean_t added; 145 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */ 146 svn_boolean_t repos_only; 147 /* TRUE if the node is to be compared with an unrelated node*/ 148 svn_boolean_t ignoring_ancestry; 149 150 /* Processor state */ 151 void *pdb; 152 svn_boolean_t skip; 153 svn_boolean_t skip_children; 154 155 svn_diff_source_t *left_src; 156 svn_diff_source_t *right_src; 157 158 apr_hash_t *local_info; 159 160 /* A hash containing the basenames of the nodes reported deleted by the 161 repository (or NULL for no values). */ 162 apr_hash_t *deletes; 163 164 /* Identifies those directory elements that get compared while running 165 the crawler. These elements should not be compared again when 166 recursively looking for local modifications. 167 168 This hash maps the basename of the node to an unimportant value. 169 170 If the directory's properties have been compared, an item with hash 171 key of "" will be present in the hash. */ 172 apr_hash_t *compared; 173 174 /* The list of incoming BASE->repos propchanges. */ 175 apr_array_header_t *propchanges; 176 177 /* Has a change on regular properties */ 178 svn_boolean_t has_propchange; 179 180 /* The overall crawler editor baton. */ 181 struct edit_baton_t *eb; 182 183 apr_pool_t *pool; 184 int users; 185}; 186 187/* File level baton. 188 */ 189struct file_baton_t 190{ 191 struct dir_baton_t *parent_baton; 192 193 /* The name and path of this file as if they would be/are in the 194 parent directory, diff session and local working copy. */ 195 const char *name; 196 const char *relpath; 197 const char *local_abspath; 198 199 /* Processor state */ 200 void *pfb; 201 svn_boolean_t skip; 202 203 /* TRUE if the file is added by the editor drive. */ 204 svn_boolean_t added; 205 /* TRUE if the node exists only on the repository side (op_depth 0 or added) */ 206 svn_boolean_t repos_only; 207 /* TRUE if the node is to be compared with an unrelated node*/ 208 svn_boolean_t ignoring_ancestry; 209 210 const svn_diff_source_t *left_src; 211 const svn_diff_source_t *right_src; 212 213 /* The list of incoming BASE->repos propchanges. */ 214 apr_array_header_t *propchanges; 215 216 /* Has a change on regular properties */ 217 svn_boolean_t has_propchange; 218 219 /* The current BASE checksum and props */ 220 const svn_checksum_t *base_checksum; 221 apr_hash_t *base_props; 222 223 /* The resulting from apply_textdelta */ 224 const char *temp_file_path; 225 unsigned char result_digest[APR_MD5_DIGESTSIZE]; 226 227 /* The overall crawler editor baton. */ 228 struct edit_baton_t *eb; 229 230 apr_pool_t *pool; 231}; 232 233/* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths 234 * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON 235 * define the callbacks to compare files. DEPTH defines if and how to 236 * descend into subdirectories; see public doc string for exactly how. 237 * IGNORE_ANCESTRY defines whether to utilize node ancestry when 238 * calculating diffs. USE_TEXT_BASE defines whether to compare 239 * against working files or text-bases. REVERSE_ORDER defines which 240 * direction to perform the diff. 241 * 242 * CHANGELIST_FILTER is a list of const char * changelist names, used to 243 * filter diff output responses to only those items in one of the 244 * specified changelists, empty (or NULL altogether) if no changelist 245 * filtering is requested. 246 */ 247static svn_error_t * 248make_edit_baton(struct edit_baton_t **edit_baton, 249 svn_wc__db_t *db, 250 const char *anchor_abspath, 251 const char *target, 252 const svn_wc_diff_callbacks4_t *callbacks, 253 void *callback_baton, 254 svn_depth_t depth, 255 svn_boolean_t ignore_ancestry, 256 svn_boolean_t show_copies_as_adds, 257 svn_boolean_t use_text_base, 258 svn_boolean_t reverse_order, 259 const apr_array_header_t *changelist_filter, 260 svn_cancel_func_t cancel_func, 261 void *cancel_baton, 262 apr_pool_t *pool) 263{ 264 apr_hash_t *changelist_hash = NULL; 265 struct edit_baton_t *eb; 266 const svn_diff_tree_processor_t *processor; 267 268 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath)); 269 270 if (changelist_filter && changelist_filter->nelts) 271 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter, 272 pool)); 273 274 SVN_ERR(svn_wc__wrap_diff_callbacks(&processor, 275 callbacks, callback_baton, TRUE, 276 pool, pool)); 277 278 if (reverse_order) 279 processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool); 280 281 /* --show-copies-as-adds implies --notice-ancestry */ 282 if (show_copies_as_adds) 283 ignore_ancestry = FALSE; 284 285 if (! show_copies_as_adds) 286 processor = svn_diff__tree_processor_copy_as_changed_create(processor, 287 pool); 288 289 eb = apr_pcalloc(pool, sizeof(*eb)); 290 eb->db = db; 291 eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath); 292 eb->target = apr_pstrdup(pool, target); 293 eb->processor = processor; 294 eb->depth = depth; 295 eb->ignore_ancestry = ignore_ancestry; 296 eb->local_before_remote = reverse_order; 297 eb->diff_pristine = use_text_base; 298 eb->changelist_hash = changelist_hash; 299 eb->cancel_func = cancel_func; 300 eb->cancel_baton = cancel_baton; 301 eb->pool = pool; 302 303 *edit_baton = eb; 304 return SVN_NO_ERROR; 305} 306 307/* Create a new directory baton. PATH is the directory path, 308 * including anchor_path. ADDED is set if this directory is being 309 * added rather than replaced. PARENT_BATON is the baton of the 310 * parent directory, it will be null if this is the root of the 311 * comparison hierarchy. The directory and its parent may or may not 312 * exist in the working copy. EDIT_BATON is the overall crawler 313 * editor baton. 314 */ 315static struct dir_baton_t * 316make_dir_baton(const char *path, 317 struct dir_baton_t *parent_baton, 318 struct edit_baton_t *eb, 319 svn_boolean_t added, 320 svn_depth_t depth, 321 apr_pool_t *result_pool) 322{ 323 apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool 324 : eb->pool); 325 struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db)); 326 327 db->parent_baton = parent_baton; 328 329 /* Allocate 1 string for using as 3 strings */ 330 db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool); 331 db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath); 332 db->name = svn_dirent_basename(db->relpath, NULL); 333 334 db->eb = eb; 335 db->added = added; 336 db->depth = depth; 337 db->pool = dir_pool; 338 db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t)); 339 db->compared = apr_hash_make(dir_pool); 340 341 if (parent_baton != NULL) 342 { 343 parent_baton->users++; 344 } 345 346 db->users = 1; 347 348 return db; 349} 350 351/* Create a new file baton. PATH is the file path, including 352 * anchor_path. ADDED is set if this file is being added rather than 353 * replaced. PARENT_BATON is the baton of the parent directory. 354 * The directory and its parent may or may not exist in the working copy. 355 */ 356static struct file_baton_t * 357make_file_baton(const char *path, 358 svn_boolean_t added, 359 struct dir_baton_t *parent_baton, 360 apr_pool_t *result_pool) 361{ 362 apr_pool_t *file_pool = svn_pool_create(result_pool); 363 struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb)); 364 struct edit_baton_t *eb = parent_baton->eb; 365 366 fb->eb = eb; 367 fb->parent_baton = parent_baton; 368 fb->parent_baton->users++; 369 370 /* Allocate 1 string for using as 3 strings */ 371 fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool); 372 fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath); 373 fb->name = svn_dirent_basename(fb->relpath, NULL); 374 375 fb->added = added; 376 fb->pool = file_pool; 377 fb->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t)); 378 379 return fb; 380} 381 382/* Destroy DB when there are no more registered users */ 383static svn_error_t * 384maybe_done(struct dir_baton_t *db) 385{ 386 db->users--; 387 388 if (!db->users) 389 { 390 struct dir_baton_t *pb = db->parent_baton; 391 392 svn_pool_clear(db->pool); 393 394 if (pb != NULL) 395 SVN_ERR(maybe_done(pb)); 396 } 397 398 return SVN_NO_ERROR; 399} 400 401/* Standard check to see if a node is represented in the local working copy */ 402#define NOT_PRESENT(status) \ 403 ((status) == svn_wc__db_status_not_present \ 404 || (status) == svn_wc__db_status_excluded \ 405 || (status) == svn_wc__db_status_server_excluded) 406 407svn_error_t * 408svn_wc__diff_base_working_diff(svn_wc__db_t *db, 409 const char *local_abspath, 410 const char *relpath, 411 svn_revnum_t revision, 412 apr_hash_t *changelist_hash, 413 const svn_diff_tree_processor_t *processor, 414 void *processor_dir_baton, 415 svn_boolean_t diff_pristine, 416 svn_cancel_func_t cancel_func, 417 void *cancel_baton, 418 apr_pool_t *scratch_pool) 419{ 420 void *file_baton = NULL; 421 svn_boolean_t skip = FALSE; 422 svn_wc__db_status_t status; 423 svn_revnum_t db_revision; 424 svn_boolean_t had_props; 425 svn_boolean_t props_mod; 426 svn_boolean_t files_same = FALSE; 427 svn_wc__db_status_t base_status; 428 const svn_checksum_t *working_checksum; 429 const svn_checksum_t *checksum; 430 svn_filesize_t recorded_size; 431 apr_time_t recorded_time; 432 const char *pristine_file; 433 const char *local_file; 434 svn_diff_source_t *left_src; 435 svn_diff_source_t *right_src; 436 apr_hash_t *base_props; 437 apr_hash_t *local_props; 438 apr_array_header_t *prop_changes; 439 const char *changelist; 440 441 SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL, 442 NULL, NULL, NULL, NULL, &working_checksum, NULL, 443 NULL, NULL, NULL, NULL, NULL, &recorded_size, 444 &recorded_time, &changelist, NULL, NULL, 445 &had_props, &props_mod, NULL, NULL, NULL, 446 db, local_abspath, scratch_pool, scratch_pool)); 447 checksum = working_checksum; 448 449 assert(status == svn_wc__db_status_normal 450 || status == svn_wc__db_status_added 451 || (status == svn_wc__db_status_deleted && diff_pristine)); 452 453 /* If the item is not a member of a specified changelist (and there are 454 some specified changelists), skip it. */ 455 if (changelist_hash && !svn_hash_gets(changelist_hash, changelist)) 456 return SVN_NO_ERROR; 457 458 459 if (status != svn_wc__db_status_normal) 460 { 461 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision, 462 NULL, NULL, NULL, NULL, NULL, NULL, 463 NULL, &checksum, NULL, NULL, &had_props, 464 NULL, NULL, 465 db, local_abspath, 466 scratch_pool, scratch_pool)); 467 recorded_size = SVN_INVALID_FILESIZE; 468 recorded_time = 0; 469 props_mod = TRUE; /* Requires compare */ 470 } 471 else if (diff_pristine) 472 files_same = TRUE; 473 else 474 { 475 const svn_io_dirent2_t *dirent; 476 477 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, 478 FALSE /* verify truename */, 479 TRUE /* ingore_enoent */, 480 scratch_pool, scratch_pool)); 481 482 if (dirent->kind == svn_node_file 483 && dirent->filesize == recorded_size 484 && dirent->mtime == recorded_time) 485 { 486 files_same = TRUE; 487 } 488 } 489 490 if (files_same && !props_mod) 491 return SVN_NO_ERROR; /* Cheap exit */ 492 493 assert(checksum); 494 495 if (!SVN_IS_VALID_REVNUM(revision)) 496 revision = db_revision; 497 498 left_src = svn_diff__source_create(revision, scratch_pool); 499 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 500 501 SVN_ERR(processor->file_opened(&file_baton, &skip, relpath, 502 left_src, 503 right_src, 504 NULL /* copyfrom_src */, 505 processor_dir_baton, 506 processor, 507 scratch_pool, scratch_pool)); 508 509 if (skip) 510 return SVN_NO_ERROR; 511 512 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, 513 db, local_abspath, checksum, 514 scratch_pool, scratch_pool)); 515 516 if (diff_pristine) 517 SVN_ERR(svn_wc__db_pristine_get_path(&local_file, 518 db, local_abspath, 519 working_checksum, 520 scratch_pool, scratch_pool)); 521 else if (! (had_props || props_mod)) 522 local_file = local_abspath; 523 else if (files_same) 524 local_file = pristine_file; 525 else 526 SVN_ERR(svn_wc__internal_translated_file( 527 &local_file, local_abspath, 528 db, local_abspath, 529 SVN_WC_TRANSLATE_TO_NF 530 | SVN_WC_TRANSLATE_USE_GLOBAL_TMP, 531 cancel_func, cancel_baton, 532 scratch_pool, scratch_pool)); 533 534 if (! files_same) 535 SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file, 536 pristine_file, scratch_pool)); 537 538 if (had_props) 539 SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath, 540 scratch_pool, scratch_pool)); 541 else 542 base_props = apr_hash_make(scratch_pool); 543 544 if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod)) 545 local_props = base_props; 546 else if (diff_pristine) 547 SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath, 548 scratch_pool, scratch_pool)); 549 else 550 SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath, 551 scratch_pool, scratch_pool)); 552 553 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool)); 554 555 if (prop_changes->nelts || !files_same) 556 { 557 SVN_ERR(processor->file_changed(relpath, 558 left_src, 559 right_src, 560 pristine_file, 561 local_file, 562 base_props, 563 local_props, 564 ! files_same, 565 prop_changes, 566 file_baton, 567 processor, 568 scratch_pool)); 569 } 570 else 571 { 572 SVN_ERR(processor->file_closed(relpath, 573 left_src, 574 right_src, 575 file_baton, 576 processor, 577 scratch_pool)); 578 } 579 580 return SVN_NO_ERROR; 581} 582 583static svn_error_t * 584ensure_local_info(struct dir_baton_t *db, 585 apr_pool_t *scratch_pool) 586{ 587 apr_hash_t *conflicts; 588 589 if (db->local_info) 590 return SVN_NO_ERROR; 591 592 SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts, 593 db->eb->db, db->local_abspath, 594 db->pool, scratch_pool)); 595 596 return SVN_NO_ERROR; 597} 598 599/* Called when the directory is closed to compare any elements that have 600 * not yet been compared. This identifies local, working copy only 601 * changes. At this stage we are dealing with files/directories that do 602 * exist in the working copy. 603 * 604 * DIR_BATON is the baton for the directory. 605 */ 606static svn_error_t * 607walk_local_nodes_diff(struct edit_baton_t *eb, 608 const char *local_abspath, 609 const char *path, 610 svn_depth_t depth, 611 apr_hash_t *compared, 612 void *parent_baton, 613 apr_pool_t *scratch_pool) 614{ 615 svn_wc__db_t *db = eb->db; 616 svn_boolean_t in_anchor_not_target; 617 apr_pool_t *iterpool; 618 void *dir_baton = NULL; 619 svn_boolean_t skip = FALSE; 620 svn_boolean_t skip_children = FALSE; 621 svn_revnum_t revision; 622 svn_boolean_t props_mod; 623 svn_diff_source_t *left_src; 624 svn_diff_source_t *right_src; 625 626 /* Everything we do below is useless if we are comparing to BASE. */ 627 if (eb->diff_pristine) 628 return SVN_NO_ERROR; 629 630 /* Determine if this is the anchor directory if the anchor is different 631 to the target. When the target is a file, the anchor is the parent 632 directory and if this is that directory the non-target entries must be 633 skipped. */ 634 in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0')); 635 636 iterpool = svn_pool_create(scratch_pool); 637 638 SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL, 639 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 640 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 641 NULL, &props_mod, NULL, NULL, NULL, 642 db, local_abspath, scratch_pool, scratch_pool)); 643 644 left_src = svn_diff__source_create(revision, scratch_pool); 645 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 646 647 if (compared) 648 { 649 dir_baton = parent_baton; 650 skip = TRUE; 651 } 652 else if (!in_anchor_not_target) 653 SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children, 654 path, 655 left_src, 656 right_src, 657 NULL /* copyfrom_src */, 658 parent_baton, 659 eb->processor, 660 scratch_pool, scratch_pool)); 661 662 663 if (!skip_children && depth != svn_depth_empty) 664 { 665 apr_hash_t *nodes; 666 apr_hash_t *conflicts; 667 apr_array_header_t *children; 668 svn_depth_t depth_below_here = depth; 669 svn_boolean_t diff_files; 670 svn_boolean_t diff_dirs; 671 int i; 672 673 if (depth_below_here == svn_depth_immediates) 674 depth_below_here = svn_depth_empty; 675 676 diff_files = (depth == svn_depth_unknown 677 || depth >= svn_depth_files); 678 diff_dirs = (depth == svn_depth_unknown 679 || depth >= svn_depth_immediates); 680 681 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, 682 db, local_abspath, 683 scratch_pool, iterpool)); 684 685 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, 686 scratch_pool); 687 688 for (i = 0; i < children->nelts; i++) 689 { 690 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, 691 svn_sort__item_t); 692 const char *name = item->key; 693 struct svn_wc__db_info_t *info = item->value; 694 695 const char *child_abspath; 696 const char *child_relpath; 697 svn_boolean_t repos_only; 698 svn_boolean_t local_only; 699 svn_node_kind_t base_kind; 700 701 if (eb->cancel_func) 702 SVN_ERR(eb->cancel_func(eb->cancel_baton)); 703 704 /* In the anchor directory, if the anchor is not the target then all 705 entries other than the target should not be diff'd. Running diff 706 on one file in a directory should not diff other files in that 707 directory. */ 708 if (in_anchor_not_target && strcmp(eb->target, name)) 709 continue; 710 711 if (compared && svn_hash_gets(compared, name)) 712 continue; 713 714 if (NOT_PRESENT(info->status)) 715 continue; 716 717 assert(info->status == svn_wc__db_status_normal 718 || info->status == svn_wc__db_status_added 719 || info->status == svn_wc__db_status_deleted); 720 721 svn_pool_clear(iterpool); 722 child_abspath = svn_dirent_join(local_abspath, name, iterpool); 723 child_relpath = svn_relpath_join(path, name, iterpool); 724 725 repos_only = FALSE; 726 local_only = FALSE; 727 728 if (!info->have_base) 729 { 730 local_only = TRUE; /* Only report additions */ 731 } 732 else if (info->status == svn_wc__db_status_normal) 733 { 734 /* Simple diff */ 735 base_kind = info->kind; 736 } 737 else if (info->status == svn_wc__db_status_deleted 738 && (!eb->diff_pristine || !info->have_more_work)) 739 { 740 svn_wc__db_status_t base_status; 741 repos_only = TRUE; 742 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, 743 NULL, NULL, NULL, NULL, NULL, 744 NULL, NULL, NULL, NULL, NULL, 745 NULL, NULL, NULL, 746 db, child_abspath, 747 iterpool, iterpool)); 748 749 if (NOT_PRESENT(base_status)) 750 continue; 751 } 752 else 753 { 754 /* working status is either added or deleted */ 755 svn_wc__db_status_t base_status; 756 757 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, 758 NULL, NULL, NULL, NULL, NULL, 759 NULL, NULL, NULL, NULL, NULL, 760 NULL, NULL, NULL, 761 db, child_abspath, 762 iterpool, iterpool)); 763 764 if (NOT_PRESENT(base_status)) 765 local_only = TRUE; 766 else if (base_kind != info->kind || !eb->ignore_ancestry) 767 { 768 repos_only = TRUE; 769 local_only = TRUE; 770 } 771 } 772 773 if (eb->local_before_remote && local_only) 774 { 775 if (info->kind == svn_node_file && diff_files) 776 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, 777 child_relpath, 778 eb->processor, dir_baton, 779 eb->changelist_hash, 780 eb->diff_pristine, 781 eb->cancel_func, 782 eb->cancel_baton, 783 iterpool)); 784 else if (info->kind == svn_node_dir && diff_dirs) 785 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, 786 child_relpath, 787 depth_below_here, 788 eb->processor, dir_baton, 789 eb->changelist_hash, 790 eb->diff_pristine, 791 eb->cancel_func, 792 eb->cancel_baton, 793 iterpool)); 794 } 795 796 if (repos_only) 797 { 798 /* Report repository form deleted */ 799 if (base_kind == svn_node_file && diff_files) 800 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, 801 child_relpath, eb->revnum, 802 eb->processor, dir_baton, 803 iterpool)); 804 else if (base_kind == svn_node_dir && diff_dirs) 805 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, 806 child_relpath, eb->revnum, 807 depth_below_here, 808 eb->processor, dir_baton, 809 eb->cancel_func, 810 eb->cancel_baton, 811 iterpool)); 812 } 813 else if (!local_only) /* Not local only nor remote only */ 814 { 815 /* Diff base against actual */ 816 if (info->kind == svn_node_file && diff_files) 817 { 818 if (info->status != svn_wc__db_status_normal 819 || !eb->diff_pristine) 820 { 821 SVN_ERR(svn_wc__diff_base_working_diff( 822 db, child_abspath, 823 child_relpath, 824 eb->revnum, 825 eb->changelist_hash, 826 eb->processor, dir_baton, 827 eb->diff_pristine, 828 eb->cancel_func, 829 eb->cancel_baton, 830 scratch_pool)); 831 } 832 } 833 else if (info->kind == svn_node_dir && diff_dirs) 834 SVN_ERR(walk_local_nodes_diff(eb, child_abspath, 835 child_relpath, 836 depth_below_here, 837 NULL /* compared */, 838 dir_baton, 839 scratch_pool)); 840 } 841 842 if (!eb->local_before_remote && local_only) 843 { 844 if (info->kind == svn_node_file && diff_files) 845 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, 846 child_relpath, 847 eb->processor, dir_baton, 848 eb->changelist_hash, 849 eb->diff_pristine, 850 eb->cancel_func, 851 eb->cancel_baton, 852 iterpool)); 853 else if (info->kind == svn_node_dir && diff_dirs) 854 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, 855 child_relpath, depth_below_here, 856 eb->processor, dir_baton, 857 eb->changelist_hash, 858 eb->diff_pristine, 859 eb->cancel_func, 860 eb->cancel_baton, 861 iterpool)); 862 } 863 } 864 } 865 866 if (compared) 867 return SVN_NO_ERROR; 868 869 /* Check for local property mods on this directory, if we haven't 870 already reported them and we aren't changelist-filted. 871 ### it should be noted that we do not currently allow directories 872 ### to be part of changelists, so if a changelist is provided, the 873 ### changelist check will always fail. */ 874 if (! skip 875 && ! eb->changelist_hash 876 && ! in_anchor_not_target 877 && props_mod) 878 { 879 apr_array_header_t *propchanges; 880 apr_hash_t *left_props; 881 apr_hash_t *right_props; 882 883 SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props, 884 db, local_abspath, 885 scratch_pool, scratch_pool)); 886 887 right_props = svn_prop__patch(left_props, propchanges, scratch_pool); 888 889 SVN_ERR(eb->processor->dir_changed(path, 890 left_src, 891 right_src, 892 left_props, 893 right_props, 894 propchanges, 895 dir_baton, 896 eb->processor, 897 scratch_pool)); 898 } 899 else if (! skip) 900 SVN_ERR(eb->processor->dir_closed(path, 901 left_src, 902 right_src, 903 dir_baton, 904 eb->processor, 905 scratch_pool)); 906 907 svn_pool_destroy(iterpool); 908 909 return SVN_NO_ERROR; 910} 911 912svn_error_t * 913svn_wc__diff_local_only_file(svn_wc__db_t *db, 914 const char *local_abspath, 915 const char *relpath, 916 const svn_diff_tree_processor_t *processor, 917 void *processor_parent_baton, 918 apr_hash_t *changelist_hash, 919 svn_boolean_t diff_pristine, 920 svn_cancel_func_t cancel_func, 921 void *cancel_baton, 922 apr_pool_t *scratch_pool) 923{ 924 svn_diff_source_t *right_src; 925 svn_diff_source_t *copyfrom_src = NULL; 926 svn_wc__db_status_t status; 927 svn_node_kind_t kind; 928 const svn_checksum_t *checksum; 929 const char *original_repos_relpath; 930 svn_revnum_t original_revision; 931 const char *changelist; 932 svn_boolean_t had_props; 933 svn_boolean_t props_mod; 934 apr_hash_t *pristine_props; 935 apr_hash_t *right_props = NULL; 936 const char *pristine_file; 937 const char *translated_file; 938 svn_revnum_t revision; 939 void *file_baton = NULL; 940 svn_boolean_t skip = FALSE; 941 svn_boolean_t file_mod = TRUE; 942 943 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL, 944 NULL, NULL, NULL, NULL, &checksum, NULL, 945 &original_repos_relpath, NULL, NULL, 946 &original_revision, NULL, NULL, NULL, 947 &changelist, NULL, NULL, &had_props, 948 &props_mod, NULL, NULL, NULL, 949 db, local_abspath, 950 scratch_pool, scratch_pool)); 951 952 assert(kind == svn_node_file 953 && (status == svn_wc__db_status_normal 954 || status == svn_wc__db_status_added 955 || (status == svn_wc__db_status_deleted && diff_pristine))); 956 957 958 if (changelist && changelist_hash 959 && !svn_hash_gets(changelist_hash, changelist)) 960 return SVN_NO_ERROR; 961 962 if (status == svn_wc__db_status_deleted) 963 { 964 assert(diff_pristine); 965 966 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL, 967 NULL, &checksum, NULL, &had_props, 968 &pristine_props, 969 db, local_abspath, 970 scratch_pool, scratch_pool)); 971 props_mod = FALSE; 972 } 973 else if (!had_props) 974 pristine_props = apr_hash_make(scratch_pool); 975 else 976 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, 977 db, local_abspath, 978 scratch_pool, scratch_pool)); 979 980 if (original_repos_relpath) 981 { 982 copyfrom_src = svn_diff__source_create(original_revision, scratch_pool); 983 copyfrom_src->repos_relpath = original_repos_relpath; 984 } 985 986 if (props_mod || !SVN_IS_VALID_REVNUM(revision)) 987 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 988 else 989 { 990 if (diff_pristine) 991 file_mod = FALSE; 992 else 993 SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath, 994 FALSE, scratch_pool)); 995 996 if (!file_mod) 997 right_src = svn_diff__source_create(revision, scratch_pool); 998 else 999 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 1000 } 1001 1002 SVN_ERR(processor->file_opened(&file_baton, &skip, 1003 relpath, 1004 NULL /* left_source */, 1005 right_src, 1006 copyfrom_src, 1007 processor_parent_baton, 1008 processor, 1009 scratch_pool, scratch_pool)); 1010 1011 if (skip) 1012 return SVN_NO_ERROR; 1013 1014 if (props_mod && !diff_pristine) 1015 SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath, 1016 scratch_pool, scratch_pool)); 1017 else 1018 right_props = svn_prop_hash_dup(pristine_props, scratch_pool); 1019 1020 if (checksum) 1021 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath, 1022 checksum, scratch_pool, scratch_pool)); 1023 else 1024 pristine_file = NULL; 1025 1026 if (diff_pristine) 1027 { 1028 translated_file = pristine_file; /* No translation needed */ 1029 } 1030 else 1031 { 1032 SVN_ERR(svn_wc__internal_translated_file( 1033 &translated_file, local_abspath, db, local_abspath, 1034 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP, 1035 cancel_func, cancel_baton, 1036 scratch_pool, scratch_pool)); 1037 } 1038 1039 SVN_ERR(processor->file_added(relpath, 1040 copyfrom_src, 1041 right_src, 1042 copyfrom_src 1043 ? pristine_file 1044 : NULL, 1045 translated_file, 1046 copyfrom_src 1047 ? pristine_props 1048 : NULL, 1049 right_props, 1050 file_baton, 1051 processor, 1052 scratch_pool)); 1053 1054 return SVN_NO_ERROR; 1055} 1056 1057svn_error_t * 1058svn_wc__diff_local_only_dir(svn_wc__db_t *db, 1059 const char *local_abspath, 1060 const char *relpath, 1061 svn_depth_t depth, 1062 const svn_diff_tree_processor_t *processor, 1063 void *processor_parent_baton, 1064 apr_hash_t *changelist_hash, 1065 svn_boolean_t diff_pristine, 1066 svn_cancel_func_t cancel_func, 1067 void *cancel_baton, 1068 apr_pool_t *scratch_pool) 1069{ 1070 const apr_array_header_t *children; 1071 int i; 1072 apr_pool_t *iterpool; 1073 void *pdb = NULL; 1074 svn_boolean_t skip = FALSE; 1075 svn_boolean_t skip_children = FALSE; 1076 svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM, 1077 scratch_pool); 1078 svn_depth_t depth_below_here = depth; 1079 apr_hash_t *nodes; 1080 apr_hash_t *conflicts; 1081 1082 /* Report the addition of the directory's contents. */ 1083 iterpool = svn_pool_create(scratch_pool); 1084 1085 SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children, 1086 relpath, 1087 NULL, 1088 right_src, 1089 NULL /* copyfrom_src */, 1090 processor_parent_baton, 1091 processor, 1092 scratch_pool, iterpool)); 1093 1094 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath, 1095 scratch_pool, iterpool)); 1096 1097 if (depth_below_here == svn_depth_immediates) 1098 depth_below_here = svn_depth_empty; 1099 1100 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, 1101 scratch_pool); 1102 1103 for (i = 0; i < children->nelts; i++) 1104 { 1105 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t); 1106 const char *name = item->key; 1107 struct svn_wc__db_info_t *info = item->value; 1108 const char *child_abspath; 1109 const char *child_relpath; 1110 1111 svn_pool_clear(iterpool); 1112 1113 if (cancel_func) 1114 SVN_ERR(cancel_func(cancel_baton)); 1115 1116 child_abspath = svn_dirent_join(local_abspath, name, iterpool); 1117 1118 if (NOT_PRESENT(info->status)) 1119 { 1120 continue; 1121 } 1122 1123 /* If comparing against WORKING, skip entries that are 1124 schedule-deleted - they don't really exist. */ 1125 if (!diff_pristine && info->status == svn_wc__db_status_deleted) 1126 continue; 1127 1128 child_relpath = svn_relpath_join(relpath, name, iterpool); 1129 1130 switch (info->kind) 1131 { 1132 case svn_node_file: 1133 case svn_node_symlink: 1134 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, 1135 child_relpath, 1136 processor, pdb, 1137 changelist_hash, 1138 diff_pristine, 1139 cancel_func, cancel_baton, 1140 scratch_pool)); 1141 break; 1142 1143 case svn_node_dir: 1144 if (depth > svn_depth_files || depth == svn_depth_unknown) 1145 { 1146 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, 1147 child_relpath, depth_below_here, 1148 processor, pdb, 1149 changelist_hash, 1150 diff_pristine, 1151 cancel_func, cancel_baton, 1152 iterpool)); 1153 } 1154 break; 1155 1156 default: 1157 break; 1158 } 1159 } 1160 1161 if (!skip) 1162 { 1163 apr_hash_t *right_props; 1164 if (diff_pristine) 1165 SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath, 1166 scratch_pool, scratch_pool)); 1167 else 1168 SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath, 1169 scratch_pool, scratch_pool)); 1170 1171 SVN_ERR(processor->dir_added(relpath, 1172 NULL /* copyfrom_src */, 1173 right_src, 1174 NULL, 1175 right_props, 1176 pdb, 1177 processor, 1178 iterpool)); 1179 } 1180 svn_pool_destroy(iterpool); 1181 1182 return SVN_NO_ERROR; 1183} 1184 1185/* Reports local changes. */ 1186static svn_error_t * 1187handle_local_only(struct dir_baton_t *pb, 1188 const char *name, 1189 apr_pool_t *scratch_pool) 1190{ 1191 struct edit_baton_t *eb = pb->eb; 1192 const struct svn_wc__db_info_t *info; 1193 svn_boolean_t repos_delete = (pb->deletes 1194 && svn_hash_gets(pb->deletes, name)); 1195 1196 assert(!strchr(name, '/')); 1197 assert(!pb->added || eb->ignore_ancestry); 1198 1199 if (pb->skip_children) 1200 return SVN_NO_ERROR; 1201 1202 SVN_ERR(ensure_local_info(pb, scratch_pool)); 1203 1204 info = svn_hash_gets(pb->local_info, name); 1205 1206 if (info == NULL || NOT_PRESENT(info->status)) 1207 return SVN_NO_ERROR; 1208 1209 switch (info->status) 1210 { 1211 case svn_wc__db_status_incomplete: 1212 return SVN_NO_ERROR; /* Not local only */ 1213 1214 case svn_wc__db_status_normal: 1215 if (!repos_delete) 1216 return SVN_NO_ERROR; /* Local and remote */ 1217 svn_hash_sets(pb->deletes, name, NULL); 1218 break; 1219 1220 case svn_wc__db_status_deleted: 1221 if (!(eb->diff_pristine && repos_delete)) 1222 return SVN_NO_ERROR; 1223 break; 1224 1225 case svn_wc__db_status_added: 1226 default: 1227 break; 1228 } 1229 1230 if (info->kind == svn_node_dir) 1231 { 1232 svn_depth_t depth ; 1233 1234 if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown) 1235 depth = pb->depth; 1236 else 1237 depth = svn_depth_empty; 1238 1239 SVN_ERR(svn_wc__diff_local_only_dir( 1240 eb->db, 1241 svn_dirent_join(pb->local_abspath, name, scratch_pool), 1242 svn_relpath_join(pb->relpath, name, scratch_pool), 1243 repos_delete ? svn_depth_infinity : depth, 1244 eb->processor, pb->pdb, 1245 eb->changelist_hash, 1246 eb->diff_pristine, 1247 eb->cancel_func, eb->cancel_baton, 1248 scratch_pool)); 1249 } 1250 else 1251 SVN_ERR(svn_wc__diff_local_only_file( 1252 eb->db, 1253 svn_dirent_join(pb->local_abspath, name, scratch_pool), 1254 svn_relpath_join(pb->relpath, name, scratch_pool), 1255 eb->processor, pb->pdb, 1256 eb->changelist_hash, 1257 eb->diff_pristine, 1258 eb->cancel_func, eb->cancel_baton, 1259 scratch_pool)); 1260 1261 return SVN_NO_ERROR; 1262} 1263 1264/* Reports a file LOCAL_ABSPATH in BASE as deleted */ 1265svn_error_t * 1266svn_wc__diff_base_only_file(svn_wc__db_t *db, 1267 const char *local_abspath, 1268 const char *relpath, 1269 svn_revnum_t revision, 1270 const svn_diff_tree_processor_t *processor, 1271 void *processor_parent_baton, 1272 apr_pool_t *scratch_pool) 1273{ 1274 svn_wc__db_status_t status; 1275 svn_node_kind_t kind; 1276 const svn_checksum_t *checksum; 1277 apr_hash_t *props; 1278 void *file_baton = NULL; 1279 svn_boolean_t skip = FALSE; 1280 svn_diff_source_t *left_src; 1281 const char *pristine_file; 1282 1283 SVN_ERR(svn_wc__db_base_get_info(&status, &kind, 1284 SVN_IS_VALID_REVNUM(revision) 1285 ? NULL : &revision, 1286 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1287 &checksum, NULL, NULL, NULL, &props, NULL, 1288 db, local_abspath, 1289 scratch_pool, scratch_pool)); 1290 1291 SVN_ERR_ASSERT(status == svn_wc__db_status_normal 1292 && kind == svn_node_file 1293 && checksum); 1294 1295 left_src = svn_diff__source_create(revision, scratch_pool); 1296 1297 SVN_ERR(processor->file_opened(&file_baton, &skip, 1298 relpath, 1299 left_src, 1300 NULL /* right_src */, 1301 NULL /* copyfrom_source */, 1302 processor_parent_baton, 1303 processor, 1304 scratch_pool, scratch_pool)); 1305 1306 if (skip) 1307 return SVN_NO_ERROR; 1308 1309 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, 1310 db, local_abspath, checksum, 1311 scratch_pool, scratch_pool)); 1312 1313 SVN_ERR(processor->file_deleted(relpath, 1314 left_src, 1315 pristine_file, 1316 props, 1317 file_baton, 1318 processor, 1319 scratch_pool)); 1320 1321 return SVN_NO_ERROR; 1322} 1323 1324svn_error_t * 1325svn_wc__diff_base_only_dir(svn_wc__db_t *db, 1326 const char *local_abspath, 1327 const char *relpath, 1328 svn_revnum_t revision, 1329 svn_depth_t depth, 1330 const svn_diff_tree_processor_t *processor, 1331 void *processor_parent_baton, 1332 svn_cancel_func_t cancel_func, 1333 void *cancel_baton, 1334 apr_pool_t *scratch_pool) 1335{ 1336 void *dir_baton = NULL; 1337 svn_boolean_t skip = FALSE; 1338 svn_boolean_t skip_children = FALSE; 1339 svn_diff_source_t *left_src; 1340 svn_revnum_t report_rev = revision; 1341 1342 if (!SVN_IS_VALID_REVNUM(report_rev)) 1343 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL, 1344 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1345 NULL, NULL, NULL, 1346 db, local_abspath, 1347 scratch_pool, scratch_pool)); 1348 1349 left_src = svn_diff__source_create(report_rev, scratch_pool); 1350 1351 SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children, 1352 relpath, 1353 left_src, 1354 NULL /* right_src */, 1355 NULL /* copyfrom_src */, 1356 processor_parent_baton, 1357 processor, 1358 scratch_pool, scratch_pool)); 1359 1360 if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty)) 1361 { 1362 apr_hash_t *nodes; 1363 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1364 apr_array_header_t *children; 1365 int i; 1366 1367 SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath, 1368 scratch_pool, iterpool)); 1369 1370 children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, 1371 scratch_pool); 1372 1373 for (i = 0; i < children->nelts; i++) 1374 { 1375 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, 1376 svn_sort__item_t); 1377 const char *name = item->key; 1378 struct svn_wc__db_base_info_t *info = item->value; 1379 const char *child_abspath; 1380 const char *child_relpath; 1381 1382 if (info->status != svn_wc__db_status_normal) 1383 continue; 1384 1385 if (cancel_func) 1386 SVN_ERR(cancel_func(cancel_baton)); 1387 1388 svn_pool_clear(iterpool); 1389 1390 child_abspath = svn_dirent_join(local_abspath, name, iterpool); 1391 child_relpath = svn_relpath_join(relpath, name, iterpool); 1392 1393 switch (info->kind) 1394 { 1395 case svn_node_file: 1396 case svn_node_symlink: 1397 SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, 1398 child_relpath, 1399 revision, 1400 processor, dir_baton, 1401 iterpool)); 1402 break; 1403 case svn_node_dir: 1404 if (depth > svn_depth_files || depth == svn_depth_unknown) 1405 { 1406 svn_depth_t depth_below_here = depth; 1407 1408 if (depth_below_here == svn_depth_immediates) 1409 depth_below_here = svn_depth_empty; 1410 1411 SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, 1412 child_relpath, 1413 revision, 1414 depth_below_here, 1415 processor, dir_baton, 1416 cancel_func, 1417 cancel_baton, 1418 iterpool)); 1419 } 1420 break; 1421 1422 default: 1423 break; 1424 } 1425 } 1426 } 1427 1428 if (!skip) 1429 { 1430 apr_hash_t *props; 1431 SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath, 1432 scratch_pool, scratch_pool)); 1433 1434 SVN_ERR(processor->dir_deleted(relpath, 1435 left_src, 1436 props, 1437 dir_baton, 1438 processor, 1439 scratch_pool)); 1440 } 1441 1442 return SVN_NO_ERROR; 1443} 1444 1445/* An svn_delta_editor_t function. */ 1446static svn_error_t * 1447set_target_revision(void *edit_baton, 1448 svn_revnum_t target_revision, 1449 apr_pool_t *pool) 1450{ 1451 struct edit_baton_t *eb = edit_baton; 1452 eb->revnum = target_revision; 1453 1454 return SVN_NO_ERROR; 1455} 1456 1457/* An svn_delta_editor_t function. The root of the comparison hierarchy */ 1458static svn_error_t * 1459open_root(void *edit_baton, 1460 svn_revnum_t base_revision, 1461 apr_pool_t *dir_pool, 1462 void **root_baton) 1463{ 1464 struct edit_baton_t *eb = edit_baton; 1465 struct dir_baton_t *db; 1466 1467 eb->root_opened = TRUE; 1468 db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool); 1469 *root_baton = db; 1470 1471 if (eb->target[0] == '\0') 1472 { 1473 db->left_src = svn_diff__source_create(eb->revnum, db->pool); 1474 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool); 1475 1476 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, 1477 &db->skip_children, 1478 "", 1479 db->left_src, 1480 db->right_src, 1481 NULL /* copyfrom_source */, 1482 NULL /* parent_baton */, 1483 eb->processor, 1484 db->pool, db->pool)); 1485 } 1486 else 1487 db->skip = TRUE; /* Skip this, but not the children */ 1488 1489 return SVN_NO_ERROR; 1490} 1491 1492/* An svn_delta_editor_t function. */ 1493static svn_error_t * 1494delete_entry(const char *path, 1495 svn_revnum_t base_revision, 1496 void *parent_baton, 1497 apr_pool_t *pool) 1498{ 1499 struct dir_baton_t *pb = parent_baton; 1500 const char *name = svn_dirent_basename(path, pb->pool); 1501 1502 if (!pb->deletes) 1503 pb->deletes = apr_hash_make(pb->pool); 1504 1505 svn_hash_sets(pb->deletes, name, ""); 1506 return SVN_NO_ERROR; 1507} 1508 1509/* An svn_delta_editor_t function. */ 1510static svn_error_t * 1511add_directory(const char *path, 1512 void *parent_baton, 1513 const char *copyfrom_path, 1514 svn_revnum_t copyfrom_revision, 1515 apr_pool_t *dir_pool, 1516 void **child_baton) 1517{ 1518 struct dir_baton_t *pb = parent_baton; 1519 struct edit_baton_t *eb = pb->eb; 1520 struct dir_baton_t *db; 1521 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates) 1522 ? svn_depth_empty : pb->depth; 1523 1524 db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth, 1525 dir_pool); 1526 *child_baton = db; 1527 1528 if (pb->repos_only || !eb->ignore_ancestry) 1529 db->repos_only = TRUE; 1530 else 1531 { 1532 struct svn_wc__db_info_t *info; 1533 SVN_ERR(ensure_local_info(pb, dir_pool)); 1534 1535 info = svn_hash_gets(pb->local_info, db->name); 1536 1537 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status)) 1538 db->repos_only = TRUE; 1539 1540 if (!db->repos_only && info->status != svn_wc__db_status_added) 1541 db->repos_only = TRUE; 1542 1543 if (!db->repos_only) 1544 { 1545 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool); 1546 db->ignoring_ancestry = TRUE; 1547 1548 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), ""); 1549 } 1550 } 1551 1552 db->left_src = svn_diff__source_create(eb->revnum, db->pool); 1553 1554 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry) 1555 SVN_ERR(handle_local_only(pb, db->name, dir_pool)); 1556 1557 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children, 1558 db->relpath, 1559 db->left_src, 1560 db->right_src, 1561 NULL /* copyfrom src */, 1562 pb->pdb, 1563 eb->processor, 1564 db->pool, db->pool)); 1565 1566 return SVN_NO_ERROR; 1567} 1568 1569/* An svn_delta_editor_t function. */ 1570static svn_error_t * 1571open_directory(const char *path, 1572 void *parent_baton, 1573 svn_revnum_t base_revision, 1574 apr_pool_t *dir_pool, 1575 void **child_baton) 1576{ 1577 struct dir_baton_t *pb = parent_baton; 1578 struct edit_baton_t *eb = pb->eb; 1579 struct dir_baton_t *db; 1580 svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates) 1581 ? svn_depth_empty : pb->depth; 1582 1583 /* Allocate path from the parent pool since the memory is used in the 1584 parent's compared hash */ 1585 db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool); 1586 *child_baton = db; 1587 1588 if (pb->repos_only) 1589 db->repos_only = TRUE; 1590 else 1591 { 1592 struct svn_wc__db_info_t *info; 1593 SVN_ERR(ensure_local_info(pb, dir_pool)); 1594 1595 info = svn_hash_gets(pb->local_info, db->name); 1596 1597 if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status)) 1598 db->repos_only = TRUE; 1599 1600 if (!db->repos_only) 1601 switch (info->status) 1602 { 1603 case svn_wc__db_status_normal: 1604 break; 1605 case svn_wc__db_status_deleted: 1606 db->repos_only = TRUE; 1607 1608 if (!info->have_more_work) 1609 svn_hash_sets(pb->compared, 1610 apr_pstrdup(pb->pool, db->name), ""); 1611 break; 1612 case svn_wc__db_status_added: 1613 if (eb->ignore_ancestry) 1614 db->ignoring_ancestry = TRUE; 1615 else 1616 db->repos_only = TRUE; 1617 break; 1618 default: 1619 SVN_ERR_MALFUNCTION(); 1620 } 1621 1622 if (!db->repos_only) 1623 { 1624 db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool); 1625 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), ""); 1626 } 1627 } 1628 1629 db->left_src = svn_diff__source_create(eb->revnum, db->pool); 1630 1631 if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry) 1632 SVN_ERR(handle_local_only(pb, db->name, dir_pool)); 1633 1634 SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children, 1635 db->relpath, 1636 db->left_src, 1637 db->right_src, 1638 NULL /* copyfrom src */, 1639 pb->pdb, 1640 eb->processor, 1641 db->pool, db->pool)); 1642 1643 return SVN_NO_ERROR; 1644} 1645 1646 1647/* An svn_delta_editor_t function. When a directory is closed, all the 1648 * directory elements that have been added or replaced will already have been 1649 * diff'd. However there may be other elements in the working copy 1650 * that have not yet been considered. */ 1651static svn_error_t * 1652close_directory(void *dir_baton, 1653 apr_pool_t *pool) 1654{ 1655 struct dir_baton_t *db = dir_baton; 1656 struct dir_baton_t *pb = db->parent_baton; 1657 struct edit_baton_t *eb = db->eb; 1658 apr_pool_t *scratch_pool = db->pool; 1659 svn_boolean_t reported_closed = FALSE; 1660 1661 if (!db->skip_children && db->deletes && apr_hash_count(db->deletes)) 1662 { 1663 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1664 apr_array_header_t *children; 1665 int i; 1666 children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically, 1667 scratch_pool); 1668 1669 for (i = 0; i < children->nelts; i++) 1670 { 1671 svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, 1672 svn_sort__item_t); 1673 const char *name = item->key; 1674 1675 svn_pool_clear(iterpool); 1676 SVN_ERR(handle_local_only(db, name, iterpool)); 1677 1678 svn_hash_sets(db->compared, name, ""); 1679 } 1680 1681 svn_pool_destroy(iterpool); 1682 } 1683 1684 /* Report local modifications for this directory. Skip added 1685 directories since they can only contain added elements, all of 1686 which have already been diff'd. */ 1687 if (!db->repos_only && !db->skip_children) 1688 { 1689 SVN_ERR(walk_local_nodes_diff(eb, 1690 db->local_abspath, 1691 db->relpath, 1692 db->depth, 1693 db->compared, 1694 db->pdb, 1695 scratch_pool)); 1696 } 1697 1698 /* Report the property changes on the directory itself, if necessary. */ 1699 if (db->skip) 1700 { 1701 /* Diff processor requested no directory details */ 1702 } 1703 else if (db->propchanges->nelts > 0 || db->repos_only) 1704 { 1705 apr_hash_t *repos_props; 1706 1707 if (db->added) 1708 { 1709 repos_props = apr_hash_make(scratch_pool); 1710 } 1711 else 1712 { 1713 SVN_ERR(svn_wc__db_base_get_props(&repos_props, 1714 eb->db, db->local_abspath, 1715 scratch_pool, scratch_pool)); 1716 } 1717 1718 /* Add received property changes and entry props */ 1719 if (db->propchanges->nelts) 1720 repos_props = svn_prop__patch(repos_props, db->propchanges, 1721 scratch_pool); 1722 1723 if (db->repos_only) 1724 { 1725 SVN_ERR(eb->processor->dir_deleted(db->relpath, 1726 db->left_src, 1727 repos_props, 1728 db->pdb, 1729 eb->processor, 1730 scratch_pool)); 1731 reported_closed = TRUE; 1732 } 1733 else 1734 { 1735 apr_hash_t *local_props; 1736 apr_array_header_t *prop_changes; 1737 1738 if (eb->diff_pristine) 1739 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL, 1740 NULL, NULL, NULL, NULL, 1741 &local_props, 1742 eb->db, db->local_abspath, 1743 scratch_pool, scratch_pool)); 1744 else 1745 SVN_ERR(svn_wc__db_read_props(&local_props, 1746 eb->db, db->local_abspath, 1747 scratch_pool, scratch_pool)); 1748 1749 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props, 1750 scratch_pool)); 1751 1752 /* ### as a good diff processor we should now only report changes 1753 if there are non-entry changes, but for now we stick to 1754 compatibility */ 1755 1756 if (prop_changes->nelts) 1757 { 1758 SVN_ERR(eb->processor->dir_changed(db->relpath, 1759 db->left_src, 1760 db->right_src, 1761 repos_props, 1762 local_props, 1763 prop_changes, 1764 db->pdb, 1765 eb->processor, 1766 scratch_pool)); 1767 reported_closed = TRUE; 1768 } 1769 } 1770 } 1771 1772 /* Mark this directory as compared in the parent directory's baton, 1773 unless this is the root of the comparison. */ 1774 if (!reported_closed && !db->skip) 1775 SVN_ERR(eb->processor->dir_closed(db->relpath, 1776 db->left_src, 1777 db->right_src, 1778 db->pdb, 1779 eb->processor, 1780 scratch_pool)); 1781 1782 if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry) 1783 SVN_ERR(handle_local_only(pb, db->name, scratch_pool)); 1784 1785 SVN_ERR(maybe_done(db)); /* destroys scratch_pool */ 1786 1787 return SVN_NO_ERROR; 1788} 1789 1790/* An svn_delta_editor_t function. */ 1791static svn_error_t * 1792add_file(const char *path, 1793 void *parent_baton, 1794 const char *copyfrom_path, 1795 svn_revnum_t copyfrom_revision, 1796 apr_pool_t *file_pool, 1797 void **file_baton) 1798{ 1799 struct dir_baton_t *pb = parent_baton; 1800 struct edit_baton_t *eb = pb->eb; 1801 struct file_baton_t *fb; 1802 1803 fb = make_file_baton(path, TRUE, pb, file_pool); 1804 *file_baton = fb; 1805 1806 if (pb->skip_children) 1807 { 1808 fb->skip = TRUE; 1809 return SVN_NO_ERROR; 1810 } 1811 else if (pb->repos_only || !eb->ignore_ancestry) 1812 fb->repos_only = TRUE; 1813 else 1814 { 1815 struct svn_wc__db_info_t *info; 1816 SVN_ERR(ensure_local_info(pb, file_pool)); 1817 1818 info = svn_hash_gets(pb->local_info, fb->name); 1819 1820 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status)) 1821 fb->repos_only = TRUE; 1822 1823 if (!fb->repos_only && info->status != svn_wc__db_status_added) 1824 fb->repos_only = TRUE; 1825 1826 if (!fb->repos_only) 1827 { 1828 /* Add this path to the parent directory's list of elements that 1829 have been compared. */ 1830 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool); 1831 fb->ignoring_ancestry = TRUE; 1832 1833 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), ""); 1834 } 1835 } 1836 1837 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool); 1838 1839 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip, 1840 fb->relpath, 1841 fb->left_src, 1842 fb->right_src, 1843 NULL /* copyfrom src */, 1844 pb->pdb, 1845 eb->processor, 1846 fb->pool, fb->pool)); 1847 1848 return SVN_NO_ERROR; 1849} 1850 1851/* An svn_delta_editor_t function. */ 1852static svn_error_t * 1853open_file(const char *path, 1854 void *parent_baton, 1855 svn_revnum_t base_revision, 1856 apr_pool_t *file_pool, 1857 void **file_baton) 1858{ 1859 struct dir_baton_t *pb = parent_baton; 1860 struct edit_baton_t *eb = pb->eb; 1861 struct file_baton_t *fb; 1862 1863 fb = make_file_baton(path, FALSE, pb, file_pool); 1864 *file_baton = fb; 1865 1866 if (pb->skip_children) 1867 fb->skip = TRUE; 1868 else if (pb->repos_only) 1869 fb->repos_only = TRUE; 1870 else 1871 { 1872 struct svn_wc__db_info_t *info; 1873 SVN_ERR(ensure_local_info(pb, file_pool)); 1874 1875 info = svn_hash_gets(pb->local_info, fb->name); 1876 1877 if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status)) 1878 fb->repos_only = TRUE; 1879 1880 if (!fb->repos_only) 1881 switch (info->status) 1882 { 1883 case svn_wc__db_status_normal: 1884 break; 1885 case svn_wc__db_status_deleted: 1886 fb->repos_only = TRUE; 1887 if (!info->have_more_work) 1888 svn_hash_sets(pb->compared, 1889 apr_pstrdup(pb->pool, fb->name), ""); 1890 break; 1891 case svn_wc__db_status_added: 1892 if (eb->ignore_ancestry) 1893 fb->ignoring_ancestry = TRUE; 1894 else 1895 fb->repos_only = TRUE; 1896 break; 1897 default: 1898 SVN_ERR_MALFUNCTION(); 1899 } 1900 1901 if (!fb->repos_only) 1902 { 1903 /* Add this path to the parent directory's list of elements that 1904 have been compared. */ 1905 fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool); 1906 svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), ""); 1907 } 1908 } 1909 1910 fb->left_src = svn_diff__source_create(eb->revnum, fb->pool); 1911 1912 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1913 NULL, NULL, NULL, &fb->base_checksum, NULL, 1914 NULL, NULL, &fb->base_props, NULL, 1915 eb->db, fb->local_abspath, 1916 fb->pool, fb->pool)); 1917 1918 SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip, 1919 fb->relpath, 1920 fb->left_src, 1921 fb->right_src, 1922 NULL /* copyfrom src */, 1923 pb->pdb, 1924 eb->processor, 1925 fb->pool, fb->pool)); 1926 1927 return SVN_NO_ERROR; 1928} 1929 1930/* An svn_delta_editor_t function. */ 1931static svn_error_t * 1932apply_textdelta(void *file_baton, 1933 const char *base_checksum_hex, 1934 apr_pool_t *pool, 1935 svn_txdelta_window_handler_t *handler, 1936 void **handler_baton) 1937{ 1938 struct file_baton_t *fb = file_baton; 1939 struct edit_baton_t *eb = fb->eb; 1940 svn_stream_t *source; 1941 svn_stream_t *temp_stream; 1942 svn_checksum_t *repos_checksum = NULL; 1943 1944 if (fb->skip) 1945 { 1946 *handler = svn_delta_noop_window_handler; 1947 *handler_baton = NULL; 1948 return SVN_NO_ERROR; 1949 } 1950 1951 if (base_checksum_hex && fb->base_checksum) 1952 { 1953 const svn_checksum_t *base_md5; 1954 SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5, 1955 base_checksum_hex, pool)); 1956 1957 SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5, 1958 eb->db, eb->anchor_abspath, 1959 fb->base_checksum, 1960 pool, pool)); 1961 1962 if (! svn_checksum_match(repos_checksum, base_md5)) 1963 { 1964 /* ### I expect that there are some bad drivers out there 1965 ### that used to give bad results. We could look in 1966 ### working to see if the expected checksum matches and 1967 ### then return the pristine of that... But that only moves 1968 ### the problem */ 1969 1970 /* If needed: compare checksum obtained via md5 of working. 1971 And if they match set fb->base_checksum and fb->base_props */ 1972 1973 return svn_checksum_mismatch_err( 1974 base_md5, 1975 repos_checksum, 1976 pool, 1977 _("Checksum mismatch for '%s'"), 1978 svn_dirent_local_style(fb->local_abspath, 1979 pool)); 1980 } 1981 1982 SVN_ERR(svn_wc__db_pristine_read(&source, NULL, 1983 eb->db, fb->local_abspath, 1984 fb->base_checksum, 1985 pool, pool)); 1986 } 1987 else if (fb->base_checksum) 1988 { 1989 SVN_ERR(svn_wc__db_pristine_read(&source, NULL, 1990 eb->db, fb->local_abspath, 1991 fb->base_checksum, 1992 pool, pool)); 1993 } 1994 else 1995 source = svn_stream_empty(pool); 1996 1997 /* This is the file that will contain the pristine repository version. */ 1998 SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL, 1999 svn_io_file_del_on_pool_cleanup, 2000 fb->pool, fb->pool)); 2001 2002 svn_txdelta_apply(source, temp_stream, 2003 fb->result_digest, 2004 fb->local_abspath /* error_info */, 2005 fb->pool, 2006 handler, handler_baton); 2007 2008 return SVN_NO_ERROR; 2009} 2010 2011/* An svn_delta_editor_t function. When the file is closed we have a temporary 2012 * file containing a pristine version of the repository file. This can 2013 * be compared against the working copy. 2014 * 2015 * Ignore TEXT_CHECKSUM. 2016 */ 2017static svn_error_t * 2018close_file(void *file_baton, 2019 const char *expected_md5_digest, 2020 apr_pool_t *pool) 2021{ 2022 struct file_baton_t *fb = file_baton; 2023 struct dir_baton_t *pb = fb->parent_baton; 2024 struct edit_baton_t *eb = fb->eb; 2025 apr_pool_t *scratch_pool = fb->pool; 2026 2027 /* The repository information; constructed from BASE + Changes */ 2028 const char *repos_file; 2029 apr_hash_t *repos_props; 2030 2031 if (!fb->skip && expected_md5_digest != NULL) 2032 { 2033 svn_checksum_t *expected_checksum; 2034 const svn_checksum_t *result_checksum; 2035 2036 if (fb->temp_file_path) 2037 result_checksum = svn_checksum__from_digest_md5(fb->result_digest, 2038 scratch_pool); 2039 else 2040 result_checksum = fb->base_checksum; 2041 2042 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5, 2043 expected_md5_digest, scratch_pool)); 2044 2045 if (result_checksum->kind != svn_checksum_md5) 2046 SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum, 2047 eb->db, fb->local_abspath, 2048 result_checksum, 2049 scratch_pool, scratch_pool)); 2050 2051 if (!svn_checksum_match(expected_checksum, result_checksum)) 2052 return svn_checksum_mismatch_err( 2053 expected_checksum, 2054 result_checksum, 2055 pool, 2056 _("Checksum mismatch for '%s'"), 2057 svn_dirent_local_style(fb->local_abspath, 2058 scratch_pool)); 2059 } 2060 2061 if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry) 2062 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool)); 2063 2064 { 2065 apr_hash_t *prop_base; 2066 2067 if (fb->added) 2068 prop_base = apr_hash_make(scratch_pool); 2069 else 2070 prop_base = fb->base_props; 2071 2072 /* includes entry props */ 2073 repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool); 2074 2075 repos_file = fb->temp_file_path; 2076 if (! repos_file) 2077 { 2078 assert(fb->base_checksum); 2079 SVN_ERR(svn_wc__db_pristine_get_path(&repos_file, 2080 eb->db, eb->anchor_abspath, 2081 fb->base_checksum, 2082 scratch_pool, scratch_pool)); 2083 } 2084 } 2085 2086 if (fb->skip) 2087 { 2088 /* Diff processor requested skipping information */ 2089 } 2090 else if (fb->repos_only) 2091 { 2092 SVN_ERR(eb->processor->file_deleted(fb->relpath, 2093 fb->left_src, 2094 fb->temp_file_path, 2095 repos_props, 2096 fb->pfb, 2097 eb->processor, 2098 scratch_pool)); 2099 } 2100 else 2101 { 2102 /* Produce a diff of actual or pristine against repos */ 2103 apr_hash_t *local_props; 2104 apr_array_header_t *prop_changes; 2105 const char *localfile; 2106 2107 /* pb->local_info contains some information that might allow optimizing 2108 this a bit */ 2109 2110 if (eb->diff_pristine) 2111 { 2112 const svn_checksum_t *checksum; 2113 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL, 2114 NULL, &checksum, NULL, NULL, 2115 &local_props, 2116 eb->db, fb->local_abspath, 2117 scratch_pool, scratch_pool)); 2118 assert(checksum); 2119 SVN_ERR(svn_wc__db_pristine_get_path(&localfile, 2120 eb->db, eb->anchor_abspath, 2121 checksum, 2122 scratch_pool, scratch_pool)); 2123 } 2124 else 2125 { 2126 SVN_ERR(svn_wc__db_read_props(&local_props, 2127 eb->db, fb->local_abspath, 2128 scratch_pool, scratch_pool)); 2129 2130 /* a detranslated version of the working file */ 2131 SVN_ERR(svn_wc__internal_translated_file( 2132 &localfile, fb->local_abspath, eb->db, fb->local_abspath, 2133 SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP, 2134 eb->cancel_func, eb->cancel_baton, 2135 scratch_pool, scratch_pool)); 2136 } 2137 2138 SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props, 2139 scratch_pool)); 2140 2141 2142 /* ### as a good diff processor we should now only report changes, and 2143 report file_closed() in other cases */ 2144 SVN_ERR(eb->processor->file_changed(fb->relpath, 2145 fb->left_src, 2146 fb->right_src, 2147 repos_file /* left file */, 2148 localfile /* right file */, 2149 repos_props /* left_props */, 2150 local_props /* right props */, 2151 TRUE /* ### file_modified */, 2152 prop_changes, 2153 fb->pfb, 2154 eb->processor, 2155 scratch_pool)); 2156 } 2157 2158 if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry) 2159 SVN_ERR(handle_local_only(pb, fb->name, scratch_pool)); 2160 2161 svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */ 2162 SVN_ERR(maybe_done(pb)); 2163 return SVN_NO_ERROR; 2164} 2165 2166 2167/* An svn_delta_editor_t function. */ 2168static svn_error_t * 2169change_file_prop(void *file_baton, 2170 const char *name, 2171 const svn_string_t *value, 2172 apr_pool_t *pool) 2173{ 2174 struct file_baton_t *fb = file_baton; 2175 svn_prop_t *propchange; 2176 svn_prop_kind_t propkind; 2177 2178 propkind = svn_property_kind2(name); 2179 if (propkind == svn_prop_wc_kind) 2180 return SVN_NO_ERROR; 2181 else if (propkind == svn_prop_regular_kind) 2182 fb->has_propchange = TRUE; 2183 2184 propchange = apr_array_push(fb->propchanges); 2185 propchange->name = apr_pstrdup(fb->pool, name); 2186 propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; 2187 2188 return SVN_NO_ERROR; 2189} 2190 2191 2192/* An svn_delta_editor_t function. */ 2193static svn_error_t * 2194change_dir_prop(void *dir_baton, 2195 const char *name, 2196 const svn_string_t *value, 2197 apr_pool_t *pool) 2198{ 2199 struct dir_baton_t *db = dir_baton; 2200 svn_prop_t *propchange; 2201 svn_prop_kind_t propkind; 2202 2203 propkind = svn_property_kind2(name); 2204 if (propkind == svn_prop_wc_kind) 2205 return SVN_NO_ERROR; 2206 else if (propkind == svn_prop_regular_kind) 2207 db->has_propchange = TRUE; 2208 2209 propchange = apr_array_push(db->propchanges); 2210 propchange->name = apr_pstrdup(db->pool, name); 2211 propchange->value = value ? svn_string_dup(value, db->pool) : NULL; 2212 2213 return SVN_NO_ERROR; 2214} 2215 2216 2217/* An svn_delta_editor_t function. */ 2218static svn_error_t * 2219close_edit(void *edit_baton, 2220 apr_pool_t *pool) 2221{ 2222 struct edit_baton_t *eb = edit_baton; 2223 2224 if (!eb->root_opened) 2225 { 2226 SVN_ERR(walk_local_nodes_diff(eb, 2227 eb->anchor_abspath, 2228 "", 2229 eb->depth, 2230 NULL /* compared */, 2231 NULL /* No parent_baton */, 2232 eb->pool)); 2233 } 2234 2235 return SVN_NO_ERROR; 2236} 2237 2238/* Public Interface */ 2239 2240 2241/* Create a diff editor and baton. */ 2242svn_error_t * 2243svn_wc__get_diff_editor(const svn_delta_editor_t **editor, 2244 void **edit_baton, 2245 svn_wc_context_t *wc_ctx, 2246 const char *anchor_abspath, 2247 const char *target, 2248 svn_depth_t depth, 2249 svn_boolean_t ignore_ancestry, 2250 svn_boolean_t show_copies_as_adds, 2251 svn_boolean_t use_git_diff_format, 2252 svn_boolean_t use_text_base, 2253 svn_boolean_t reverse_order, 2254 svn_boolean_t server_performs_filtering, 2255 const apr_array_header_t *changelist_filter, 2256 const svn_wc_diff_callbacks4_t *callbacks, 2257 void *callback_baton, 2258 svn_cancel_func_t cancel_func, 2259 void *cancel_baton, 2260 apr_pool_t *result_pool, 2261 apr_pool_t *scratch_pool) 2262{ 2263 struct edit_baton_t *eb; 2264 void *inner_baton; 2265 svn_delta_editor_t *tree_editor; 2266 const svn_delta_editor_t *inner_editor; 2267 struct svn_wc__shim_fetch_baton_t *sfb; 2268 svn_delta_shim_callbacks_t *shim_callbacks = 2269 svn_delta_shim_callbacks_default(result_pool); 2270 2271 SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath)); 2272 2273 /* --git implies --show-copies-as-adds */ 2274 if (use_git_diff_format) 2275 show_copies_as_adds = TRUE; 2276 2277 SVN_ERR(make_edit_baton(&eb, 2278 wc_ctx->db, 2279 anchor_abspath, target, 2280 callbacks, callback_baton, 2281 depth, ignore_ancestry, show_copies_as_adds, 2282 use_text_base, reverse_order, changelist_filter, 2283 cancel_func, cancel_baton, 2284 result_pool)); 2285 2286 tree_editor = svn_delta_default_editor(eb->pool); 2287 2288 tree_editor->set_target_revision = set_target_revision; 2289 tree_editor->open_root = open_root; 2290 tree_editor->delete_entry = delete_entry; 2291 tree_editor->add_directory = add_directory; 2292 tree_editor->open_directory = open_directory; 2293 tree_editor->close_directory = close_directory; 2294 tree_editor->add_file = add_file; 2295 tree_editor->open_file = open_file; 2296 tree_editor->apply_textdelta = apply_textdelta; 2297 tree_editor->change_file_prop = change_file_prop; 2298 tree_editor->change_dir_prop = change_dir_prop; 2299 tree_editor->close_file = close_file; 2300 tree_editor->close_edit = close_edit; 2301 2302 inner_editor = tree_editor; 2303 inner_baton = eb; 2304 2305 if (!server_performs_filtering 2306 && depth == svn_depth_unknown) 2307 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor, 2308 &inner_baton, 2309 wc_ctx->db, 2310 anchor_abspath, 2311 target, 2312 inner_editor, 2313 inner_baton, 2314 result_pool)); 2315 2316 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, 2317 cancel_baton, 2318 inner_editor, 2319 inner_baton, 2320 editor, 2321 edit_baton, 2322 result_pool)); 2323 2324 sfb = apr_palloc(result_pool, sizeof(*sfb)); 2325 sfb->db = wc_ctx->db; 2326 sfb->base_abspath = eb->anchor_abspath; 2327 sfb->fetch_base = TRUE; 2328 2329 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func; 2330 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func; 2331 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func; 2332 shim_callbacks->fetch_baton = sfb; 2333 2334 2335 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, 2336 NULL, NULL, shim_callbacks, 2337 result_pool, scratch_pool)); 2338 2339 return SVN_NO_ERROR; 2340} 2341 2342/* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */ 2343 2344/* baton for the svn_diff_tree_processor_t wrapper */ 2345typedef struct wc_diff_wrap_baton_t 2346{ 2347 const svn_wc_diff_callbacks4_t *callbacks; 2348 void *callback_baton; 2349 2350 svn_boolean_t walk_deleted_dirs; 2351 2352 apr_pool_t *result_pool; 2353 const char *empty_file; 2354 2355} wc_diff_wrap_baton_t; 2356 2357static svn_error_t * 2358wrap_ensure_empty_file(wc_diff_wrap_baton_t *wb, 2359 apr_pool_t *scratch_pool) 2360{ 2361 if (wb->empty_file) 2362 return SVN_NO_ERROR; 2363 2364 /* Create a unique file in the tempdir */ 2365 SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL, 2366 svn_io_file_del_on_pool_cleanup, 2367 wb->result_pool, scratch_pool)); 2368 2369 return SVN_NO_ERROR; 2370} 2371 2372/* svn_diff_tree_processor_t function */ 2373static svn_error_t * 2374wrap_dir_opened(void **new_dir_baton, 2375 svn_boolean_t *skip, 2376 svn_boolean_t *skip_children, 2377 const char *relpath, 2378 const svn_diff_source_t *left_source, 2379 const svn_diff_source_t *right_source, 2380 const svn_diff_source_t *copyfrom_source, 2381 void *parent_dir_baton, 2382 const svn_diff_tree_processor_t *processor, 2383 apr_pool_t *result_pool, 2384 apr_pool_t *scratch_pool) 2385{ 2386 wc_diff_wrap_baton_t *wb = processor->baton; 2387 svn_boolean_t tree_conflicted = FALSE; 2388 2389 assert(left_source || right_source); 2390 assert(!copyfrom_source || !right_source); 2391 2392 /* Maybe store state and tree_conflicted in baton? */ 2393 if (left_source != NULL) 2394 { 2395 /* Open for change or delete */ 2396 SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children, 2397 relpath, 2398 right_source 2399 ? right_source->revision 2400 : (left_source 2401 ? left_source->revision 2402 : SVN_INVALID_REVNUM), 2403 wb->callback_baton, 2404 scratch_pool)); 2405 2406 if (! right_source && !wb->walk_deleted_dirs) 2407 *skip_children = TRUE; 2408 } 2409 else /* left_source == NULL -> Add */ 2410 { 2411 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2412 SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted, 2413 skip, skip_children, 2414 relpath, 2415 right_source->revision, 2416 copyfrom_source 2417 ? copyfrom_source->repos_relpath 2418 : NULL, 2419 copyfrom_source 2420 ? copyfrom_source->revision 2421 : SVN_INVALID_REVNUM, 2422 wb->callback_baton, 2423 scratch_pool)); 2424 } 2425 2426 *new_dir_baton = NULL; 2427 2428 return SVN_NO_ERROR; 2429} 2430 2431/* svn_diff_tree_processor_t function */ 2432static svn_error_t * 2433wrap_dir_added(const char *relpath, 2434 const svn_diff_source_t *right_source, 2435 const svn_diff_source_t *copyfrom_source, 2436 /*const*/ apr_hash_t *copyfrom_props, 2437 /*const*/ apr_hash_t *right_props, 2438 void *dir_baton, 2439 const svn_diff_tree_processor_t *processor, 2440 apr_pool_t *scratch_pool) 2441{ 2442 wc_diff_wrap_baton_t *wb = processor->baton; 2443 svn_boolean_t tree_conflicted = FALSE; 2444 svn_wc_notify_state_t state = svn_wc_notify_state_unknown; 2445 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown; 2446 apr_hash_t *pristine_props = copyfrom_props; 2447 apr_array_header_t *prop_changes = NULL; 2448 2449 if (right_props && apr_hash_count(right_props)) 2450 { 2451 if (!pristine_props) 2452 pristine_props = apr_hash_make(scratch_pool); 2453 2454 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props, 2455 scratch_pool)); 2456 2457 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, 2458 &tree_conflicted, 2459 relpath, 2460 TRUE /* dir_was_added */, 2461 prop_changes, pristine_props, 2462 wb->callback_baton, 2463 scratch_pool)); 2464 } 2465 2466 SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state, 2467 &tree_conflicted, 2468 relpath, 2469 TRUE /* dir_was_added */, 2470 wb->callback_baton, 2471 scratch_pool)); 2472 return SVN_NO_ERROR; 2473} 2474 2475/* svn_diff_tree_processor_t function */ 2476static svn_error_t * 2477wrap_dir_deleted(const char *relpath, 2478 const svn_diff_source_t *left_source, 2479 /*const*/ apr_hash_t *left_props, 2480 void *dir_baton, 2481 const svn_diff_tree_processor_t *processor, 2482 apr_pool_t *scratch_pool) 2483{ 2484 wc_diff_wrap_baton_t *wb = processor->baton; 2485 svn_boolean_t tree_conflicted = FALSE; 2486 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2487 2488 SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted, 2489 relpath, 2490 wb->callback_baton, 2491 scratch_pool)); 2492 2493 return SVN_NO_ERROR; 2494} 2495 2496/* svn_diff_tree_processor_t function */ 2497static svn_error_t * 2498wrap_dir_closed(const char *relpath, 2499 const svn_diff_source_t *left_source, 2500 const svn_diff_source_t *right_source, 2501 void *dir_baton, 2502 const svn_diff_tree_processor_t *processor, 2503 apr_pool_t *scratch_pool) 2504{ 2505 wc_diff_wrap_baton_t *wb = processor->baton; 2506 2507 /* No previous implementations provided these arguments, so we 2508 are not providing them either */ 2509 SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL, 2510 relpath, 2511 FALSE /* added */, 2512 wb->callback_baton, 2513 scratch_pool)); 2514 2515return SVN_NO_ERROR; 2516} 2517 2518/* svn_diff_tree_processor_t function */ 2519static svn_error_t * 2520wrap_dir_changed(const char *relpath, 2521 const svn_diff_source_t *left_source, 2522 const svn_diff_source_t *right_source, 2523 /*const*/ apr_hash_t *left_props, 2524 /*const*/ apr_hash_t *right_props, 2525 const apr_array_header_t *prop_changes, 2526 void *dir_baton, 2527 const struct svn_diff_tree_processor_t *processor, 2528 apr_pool_t *scratch_pool) 2529{ 2530 wc_diff_wrap_baton_t *wb = processor->baton; 2531 svn_boolean_t tree_conflicted = FALSE; 2532 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable; 2533 2534 assert(left_source && right_source); 2535 2536 SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted, 2537 relpath, 2538 FALSE /* dir_was_added */, 2539 prop_changes, 2540 left_props, 2541 wb->callback_baton, 2542 scratch_pool)); 2543 2544 /* And call dir_closed, etc */ 2545 SVN_ERR(wrap_dir_closed(relpath, left_source, right_source, 2546 dir_baton, processor, 2547 scratch_pool)); 2548 return SVN_NO_ERROR; 2549} 2550 2551/* svn_diff_tree_processor_t function */ 2552static svn_error_t * 2553wrap_file_opened(void **new_file_baton, 2554 svn_boolean_t *skip, 2555 const char *relpath, 2556 const svn_diff_source_t *left_source, 2557 const svn_diff_source_t *right_source, 2558 const svn_diff_source_t *copyfrom_source, 2559 void *dir_baton, 2560 const svn_diff_tree_processor_t *processor, 2561 apr_pool_t *result_pool, 2562 apr_pool_t *scratch_pool) 2563{ 2564 wc_diff_wrap_baton_t *wb = processor->baton; 2565 svn_boolean_t tree_conflicted = FALSE; 2566 2567 if (left_source) /* If ! added */ 2568 SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath, 2569 right_source 2570 ? right_source->revision 2571 : (left_source 2572 ? left_source->revision 2573 : SVN_INVALID_REVNUM), 2574 wb->callback_baton, scratch_pool)); 2575 2576 /* No old implementation used the output arguments for notify */ 2577 2578 *new_file_baton = NULL; 2579 return SVN_NO_ERROR; 2580} 2581 2582/* svn_diff_tree_processor_t function */ 2583static svn_error_t * 2584wrap_file_added(const char *relpath, 2585 const svn_diff_source_t *copyfrom_source, 2586 const svn_diff_source_t *right_source, 2587 const char *copyfrom_file, 2588 const char *right_file, 2589 /*const*/ apr_hash_t *copyfrom_props, 2590 /*const*/ apr_hash_t *right_props, 2591 void *file_baton, 2592 const svn_diff_tree_processor_t *processor, 2593 apr_pool_t *scratch_pool) 2594{ 2595 wc_diff_wrap_baton_t *wb = processor->baton; 2596 svn_boolean_t tree_conflicted = FALSE; 2597 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2598 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable; 2599 apr_array_header_t *prop_changes; 2600 2601 if (! copyfrom_props) 2602 copyfrom_props = apr_hash_make(scratch_pool); 2603 2604 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props, 2605 scratch_pool)); 2606 2607 if (! copyfrom_source) 2608 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool)); 2609 2610 SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted, 2611 relpath, 2612 copyfrom_source 2613 ? copyfrom_file 2614 : wb->empty_file, 2615 right_file, 2616 0, 2617 right_source->revision, 2618 copyfrom_props 2619 ? svn_prop_get_value(copyfrom_props, 2620 SVN_PROP_MIME_TYPE) 2621 : NULL, 2622 right_props 2623 ? svn_prop_get_value(right_props, 2624 SVN_PROP_MIME_TYPE) 2625 : NULL, 2626 copyfrom_source 2627 ? copyfrom_source->repos_relpath 2628 : NULL, 2629 copyfrom_source 2630 ? copyfrom_source->revision 2631 : SVN_INVALID_REVNUM, 2632 prop_changes, copyfrom_props, 2633 wb->callback_baton, 2634 scratch_pool)); 2635 return SVN_NO_ERROR; 2636} 2637 2638static svn_error_t * 2639wrap_file_deleted(const char *relpath, 2640 const svn_diff_source_t *left_source, 2641 const char *left_file, 2642 apr_hash_t *left_props, 2643 void *file_baton, 2644 const svn_diff_tree_processor_t *processor, 2645 apr_pool_t *scratch_pool) 2646{ 2647 wc_diff_wrap_baton_t *wb = processor->baton; 2648 svn_boolean_t tree_conflicted = FALSE; 2649 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2650 2651 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool)); 2652 2653 SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted, 2654 relpath, 2655 left_file, wb->empty_file, 2656 left_props 2657 ? svn_prop_get_value(left_props, 2658 SVN_PROP_MIME_TYPE) 2659 : NULL, 2660 NULL, 2661 left_props, 2662 wb->callback_baton, 2663 scratch_pool)); 2664 return SVN_NO_ERROR; 2665} 2666 2667/* svn_diff_tree_processor_t function */ 2668static svn_error_t * 2669wrap_file_changed(const char *relpath, 2670 const svn_diff_source_t *left_source, 2671 const svn_diff_source_t *right_source, 2672 const char *left_file, 2673 const char *right_file, 2674 /*const*/ apr_hash_t *left_props, 2675 /*const*/ apr_hash_t *right_props, 2676 svn_boolean_t file_modified, 2677 const apr_array_header_t *prop_changes, 2678 void *file_baton, 2679 const svn_diff_tree_processor_t *processor, 2680 apr_pool_t *scratch_pool) 2681{ 2682 wc_diff_wrap_baton_t *wb = processor->baton; 2683 svn_boolean_t tree_conflicted = FALSE; 2684 svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2685 svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable; 2686 2687 SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool)); 2688 2689 assert(left_source && right_source); 2690 2691 SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted, 2692 relpath, 2693 file_modified ? left_file : NULL, 2694 file_modified ? right_file : NULL, 2695 left_source->revision, 2696 right_source->revision, 2697 left_props 2698 ? svn_prop_get_value(left_props, 2699 SVN_PROP_MIME_TYPE) 2700 : NULL, 2701 right_props 2702 ? svn_prop_get_value(right_props, 2703 SVN_PROP_MIME_TYPE) 2704 : NULL, 2705 prop_changes, 2706 left_props, 2707 wb->callback_baton, 2708 scratch_pool)); 2709 return SVN_NO_ERROR; 2710} 2711 2712svn_error_t * 2713svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor, 2714 const svn_wc_diff_callbacks4_t *callbacks, 2715 void *callback_baton, 2716 svn_boolean_t walk_deleted_dirs, 2717 apr_pool_t *result_pool, 2718 apr_pool_t *scratch_pool) 2719{ 2720 wc_diff_wrap_baton_t *wrap_baton; 2721 svn_diff_tree_processor_t *processor; 2722 2723 wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton)); 2724 2725 wrap_baton->result_pool = result_pool; 2726 wrap_baton->callbacks = callbacks; 2727 wrap_baton->callback_baton = callback_baton; 2728 wrap_baton->empty_file = NULL; 2729 wrap_baton->walk_deleted_dirs = walk_deleted_dirs; 2730 2731 processor = svn_diff__tree_processor_create(wrap_baton, result_pool); 2732 2733 processor->dir_opened = wrap_dir_opened; 2734 processor->dir_added = wrap_dir_added; 2735 processor->dir_deleted = wrap_dir_deleted; 2736 processor->dir_changed = wrap_dir_changed; 2737 processor->dir_closed = wrap_dir_closed; 2738 2739 processor->file_opened = wrap_file_opened; 2740 processor->file_added = wrap_file_added; 2741 processor->file_deleted = wrap_file_deleted; 2742 processor->file_changed = wrap_file_changed; 2743 /*processor->file_closed = wrap_file_closed*/; /* Not needed */ 2744 2745 *diff_processor = processor; 2746 return SVN_NO_ERROR; 2747} 2748