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