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