1251881Speter/* 2251881Speter * diff_editor.c -- The diff editor for comparing the working copy against the 3251881Speter * repository. 4251881Speter * 5251881Speter * ==================================================================== 6251881Speter * Licensed to the Apache Software Foundation (ASF) under one 7251881Speter * or more contributor license agreements. See the NOTICE file 8251881Speter * distributed with this work for additional information 9251881Speter * regarding copyright ownership. The ASF licenses this file 10251881Speter * to you under the Apache License, Version 2.0 (the 11251881Speter * "License"); you may not use this file except in compliance 12251881Speter * with the License. You may obtain a copy of the License at 13251881Speter * 14251881Speter * http://www.apache.org/licenses/LICENSE-2.0 15251881Speter * 16251881Speter * Unless required by applicable law or agreed to in writing, 17251881Speter * software distributed under the License is distributed on an 18251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19251881Speter * KIND, either express or implied. See the License for the 20251881Speter * specific language governing permissions and limitations 21251881Speter * under the License. 22251881Speter * ==================================================================== 23251881Speter */ 24251881Speter 25251881Speter/* 26251881Speter * This code uses an svn_delta_editor_t editor driven by 27251881Speter * svn_wc_crawl_revisions (like the update command) to retrieve the 28251881Speter * differences between the working copy and the requested repository 29251881Speter * version. Rather than updating the working copy, this new editor creates 30251881Speter * temporary files that contain the pristine repository versions. When the 31251881Speter * crawler closes the files the editor calls back to a client layer 32251881Speter * function to compare the working copy and the temporary file. There is 33251881Speter * only ever one temporary file in existence at any time. 34251881Speter * 35251881Speter * When the crawler closes a directory, the editor then calls back to the 36251881Speter * client layer to compare any remaining files that may have been modified 37251881Speter * locally. Added directories do not have corresponding temporary 38251881Speter * directories created, as they are not needed. 39251881Speter * 40251881Speter * The diff result from this editor is a combination of the restructuring 41251881Speter * operations from the repository with the local restructurings since checking 42251881Speter * out. 43251881Speter * 44251881Speter * ### TODO: Make sure that we properly support and report multi layered 45251881Speter * operations instead of only simple file replacements. 46251881Speter * 47251881Speter * ### TODO: Replacements where the node kind changes needs support. It 48251881Speter * mostly works when the change is in the repository, but not when it is 49251881Speter * in the working copy. 50251881Speter * 51251881Speter * ### TODO: Do we need to support copyfrom? 52251881Speter * 53251881Speter */ 54251881Speter 55251881Speter#include <apr_hash.h> 56251881Speter#include <apr_md5.h> 57251881Speter 58251881Speter#include <assert.h> 59251881Speter 60251881Speter#include "svn_error.h" 61251881Speter#include "svn_pools.h" 62251881Speter#include "svn_dirent_uri.h" 63251881Speter#include "svn_path.h" 64251881Speter#include "svn_hash.h" 65251881Speter#include "svn_sorts.h" 66251881Speter 67251881Speter#include "private/svn_subr_private.h" 68251881Speter#include "private/svn_wc_private.h" 69251881Speter#include "private/svn_diff_tree.h" 70251881Speter#include "private/svn_editor.h" 71251881Speter 72251881Speter#include "wc.h" 73251881Speter#include "props.h" 74251881Speter#include "adm_files.h" 75251881Speter#include "translate.h" 76251881Speter#include "diff.h" 77251881Speter 78251881Speter#include "svn_private_config.h" 79251881Speter 80251881Speter/*-------------------------------------------------------------------------*/ 81251881Speter 82251881Speter 83251881Speter/* Overall crawler editor baton. 84251881Speter */ 85251881Speterstruct edit_baton_t 86251881Speter{ 87251881Speter /* A wc db. */ 88251881Speter svn_wc__db_t *db; 89251881Speter 90251881Speter /* A diff tree processor, receiving the result of the diff. */ 91251881Speter const svn_diff_tree_processor_t *processor; 92251881Speter 93251881Speter /* A boolean indicating whether local additions should be reported before 94251881Speter remote deletes. The processor can transform adds in deletes and deletes 95251881Speter in adds, but it can't reorder the output. */ 96251881Speter svn_boolean_t local_before_remote; 97251881Speter 98251881Speter /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */ 99251881Speter const char *target; 100251881Speter const char *anchor_abspath; 101251881Speter 102251881Speter /* Target revision */ 103251881Speter svn_revnum_t revnum; 104251881Speter 105251881Speter /* Was the root opened? */ 106251881Speter svn_boolean_t root_opened; 107251881Speter 108251881Speter /* How does this diff descend as seen from target? */ 109251881Speter svn_depth_t depth; 110251881Speter 111251881Speter /* Should this diff ignore node ancestry? */ 112251881Speter svn_boolean_t ignore_ancestry; 113251881Speter 114251881Speter /* Possibly diff repos against text-bases instead of working files. */ 115251881Speter svn_boolean_t diff_pristine; 116251881Speter 117251881Speter /* Hash whose keys are const char * changelist names. */ 118251881Speter apr_hash_t *changelist_hash; 119251881Speter 120251881Speter /* Cancel function/baton */ 121251881Speter svn_cancel_func_t cancel_func; 122251881Speter void *cancel_baton; 123251881Speter 124251881Speter apr_pool_t *pool; 125251881Speter}; 126251881Speter 127251881Speter/* Directory level baton. 128251881Speter */ 129251881Speterstruct dir_baton_t 130251881Speter{ 131251881Speter /* Reference to parent directory baton (or NULL for the root) */ 132251881Speter struct dir_baton_t *parent_baton; 133251881Speter 134251881Speter /* The depth at which this directory should be diffed. */ 135251881Speter svn_depth_t depth; 136251881Speter 137251881Speter /* The name and path of this directory as if they would be/are in the 138251881Speter local working copy. */ 139251881Speter const char *name; 140251881Speter const char *relpath; 141251881Speter const char *local_abspath; 142251881Speter 143251881Speter /* TRUE if the file is added by the editor drive. */ 144251881Speter svn_boolean_t added; 145251881Speter /* TRUE if the node exists only on the repository side (op_depth 0 or added) */ 146251881Speter svn_boolean_t repos_only; 147251881Speter /* TRUE if the node is to be compared with an unrelated node*/ 148251881Speter svn_boolean_t ignoring_ancestry; 149251881Speter 150251881Speter /* Processor state */ 151251881Speter void *pdb; 152251881Speter svn_boolean_t skip; 153251881Speter svn_boolean_t skip_children; 154251881Speter 155251881Speter svn_diff_source_t *left_src; 156251881Speter svn_diff_source_t *right_src; 157251881Speter 158251881Speter apr_hash_t *local_info; 159251881Speter 160251881Speter /* A hash containing the basenames of the nodes reported deleted by the 161251881Speter repository (or NULL for no values). */ 162251881Speter apr_hash_t *deletes; 163251881Speter 164251881Speter /* Identifies those directory elements that get compared while running 165251881Speter the crawler. These elements should not be compared again when 166251881Speter recursively looking for local modifications. 167251881Speter 168251881Speter This hash maps the basename of the node to an unimportant value. 169251881Speter 170251881Speter If the directory's properties have been compared, an item with hash 171251881Speter key of "" will be present in the hash. */ 172251881Speter apr_hash_t *compared; 173251881Speter 174251881Speter /* The list of incoming BASE->repos propchanges. */ 175251881Speter apr_array_header_t *propchanges; 176251881Speter 177251881Speter /* Has a change on regular properties */ 178251881Speter svn_boolean_t has_propchange; 179251881Speter 180251881Speter /* The overall crawler editor baton. */ 181251881Speter struct edit_baton_t *eb; 182251881Speter 183251881Speter apr_pool_t *pool; 184251881Speter int users; 185251881Speter}; 186251881Speter 187251881Speter/* File level baton. 188251881Speter */ 189251881Speterstruct file_baton_t 190251881Speter{ 191251881Speter struct dir_baton_t *parent_baton; 192251881Speter 193251881Speter /* The name and path of this file as if they would be/are in the 194251881Speter parent directory, diff session and local working copy. */ 195251881Speter const char *name; 196251881Speter const char *relpath; 197251881Speter const char *local_abspath; 198251881Speter 199251881Speter /* Processor state */ 200251881Speter void *pfb; 201251881Speter svn_boolean_t skip; 202251881Speter 203251881Speter /* TRUE if the file is added by the editor drive. */ 204251881Speter svn_boolean_t added; 205251881Speter /* TRUE if the node exists only on the repository side (op_depth 0 or added) */ 206251881Speter svn_boolean_t repos_only; 207251881Speter /* TRUE if the node is to be compared with an unrelated node*/ 208251881Speter svn_boolean_t ignoring_ancestry; 209251881Speter 210251881Speter const svn_diff_source_t *left_src; 211251881Speter const svn_diff_source_t *right_src; 212251881Speter 213251881Speter /* The list of incoming BASE->repos propchanges. */ 214251881Speter apr_array_header_t *propchanges; 215251881Speter 216251881Speter /* Has a change on regular properties */ 217251881Speter svn_boolean_t has_propchange; 218251881Speter 219251881Speter /* The current BASE checksum and props */ 220251881Speter const svn_checksum_t *base_checksum; 221251881Speter apr_hash_t *base_props; 222251881Speter 223251881Speter /* The resulting from apply_textdelta */ 224251881Speter const char *temp_file_path; 225251881Speter unsigned char result_digest[APR_MD5_DIGESTSIZE]; 226251881Speter 227251881Speter /* The overall crawler editor baton. */ 228251881Speter struct edit_baton_t *eb; 229251881Speter 230251881Speter apr_pool_t *pool; 231251881Speter}; 232251881Speter 233251881Speter/* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths 234251881Speter * that describe the root of the comparison. CALLBACKS/CALLBACK_BATON 235251881Speter * define the callbacks to compare files. DEPTH defines if and how to 236251881Speter * descend into subdirectories; see public doc string for exactly how. 237251881Speter * IGNORE_ANCESTRY defines whether to utilize node ancestry when 238251881Speter * calculating diffs. USE_TEXT_BASE defines whether to compare 239251881Speter * against working files or text-bases. REVERSE_ORDER defines which 240251881Speter * direction to perform the diff. 241251881Speter * 242251881Speter * CHANGELIST_FILTER is a list of const char * changelist names, used to 243251881Speter * filter diff output responses to only those items in one of the 244251881Speter * specified changelists, empty (or NULL altogether) if no changelist 245251881Speter * filtering is requested. 246251881Speter */ 247251881Speterstatic svn_error_t * 248251881Spetermake_edit_baton(struct edit_baton_t **edit_baton, 249251881Speter svn_wc__db_t *db, 250251881Speter const char *anchor_abspath, 251251881Speter const char *target, 252251881Speter const svn_wc_diff_callbacks4_t *callbacks, 253251881Speter void *callback_baton, 254251881Speter svn_depth_t depth, 255251881Speter svn_boolean_t ignore_ancestry, 256251881Speter svn_boolean_t show_copies_as_adds, 257251881Speter svn_boolean_t use_text_base, 258251881Speter svn_boolean_t reverse_order, 259251881Speter const apr_array_header_t *changelist_filter, 260251881Speter svn_cancel_func_t cancel_func, 261251881Speter void *cancel_baton, 262251881Speter apr_pool_t *pool) 263251881Speter{ 264251881Speter apr_hash_t *changelist_hash = NULL; 265251881Speter struct edit_baton_t *eb; 266251881Speter const svn_diff_tree_processor_t *processor; 267251881Speter 268251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath)); 269251881Speter 270251881Speter if (changelist_filter && changelist_filter->nelts) 271251881Speter SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter, 272251881Speter pool)); 273251881Speter 274251881Speter SVN_ERR(svn_wc__wrap_diff_callbacks(&processor, 275251881Speter callbacks, callback_baton, TRUE, 276251881Speter pool, pool)); 277251881Speter 278251881Speter if (reverse_order) 279251881Speter processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool); 280251881Speter 281251881Speter /* --show-copies-as-adds implies --notice-ancestry */ 282251881Speter if (show_copies_as_adds) 283251881Speter ignore_ancestry = FALSE; 284251881Speter 285251881Speter if (! show_copies_as_adds) 286251881Speter processor = svn_diff__tree_processor_copy_as_changed_create(processor, 287251881Speter pool); 288251881Speter 289251881Speter eb = apr_pcalloc(pool, sizeof(*eb)); 290251881Speter eb->db = db; 291251881Speter eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath); 292251881Speter eb->target = apr_pstrdup(pool, target); 293251881Speter eb->processor = processor; 294251881Speter eb->depth = depth; 295251881Speter eb->ignore_ancestry = ignore_ancestry; 296251881Speter eb->local_before_remote = reverse_order; 297251881Speter eb->diff_pristine = use_text_base; 298251881Speter eb->changelist_hash = changelist_hash; 299251881Speter eb->cancel_func = cancel_func; 300251881Speter eb->cancel_baton = cancel_baton; 301251881Speter eb->pool = pool; 302251881Speter 303251881Speter *edit_baton = eb; 304251881Speter return SVN_NO_ERROR; 305251881Speter} 306251881Speter 307251881Speter/* Create a new directory baton. PATH is the directory path, 308251881Speter * including anchor_path. ADDED is set if this directory is being 309251881Speter * added rather than replaced. PARENT_BATON is the baton of the 310251881Speter * parent directory, it will be null if this is the root of the 311251881Speter * comparison hierarchy. The directory and its parent may or may not 312251881Speter * exist in the working copy. EDIT_BATON is the overall crawler 313251881Speter * editor baton. 314251881Speter */ 315251881Speterstatic struct dir_baton_t * 316251881Spetermake_dir_baton(const char *path, 317251881Speter struct dir_baton_t *parent_baton, 318251881Speter struct edit_baton_t *eb, 319251881Speter svn_boolean_t added, 320251881Speter svn_depth_t depth, 321251881Speter apr_pool_t *result_pool) 322251881Speter{ 323251881Speter apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool 324251881Speter : eb->pool); 325251881Speter struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db)); 326251881Speter 327251881Speter db->parent_baton = parent_baton; 328251881Speter 329251881Speter /* Allocate 1 string for using as 3 strings */ 330251881Speter db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool); 331251881Speter db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath); 332251881Speter db->name = svn_dirent_basename(db->relpath, NULL); 333251881Speter 334251881Speter db->eb = eb; 335251881Speter db->added = added; 336251881Speter db->depth = depth; 337251881Speter db->pool = dir_pool; 338251881Speter db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t)); 339251881Speter db->compared = apr_hash_make(dir_pool); 340251881Speter 341251881Speter if (parent_baton != NULL) 342251881Speter { 343251881Speter parent_baton->users++; 344251881Speter } 345251881Speter 346251881Speter db->users = 1; 347251881Speter 348251881Speter return db; 349251881Speter} 350251881Speter 351251881Speter/* Create a new file baton. PATH is the file path, including 352251881Speter * anchor_path. ADDED is set if this file is being added rather than 353251881Speter * replaced. PARENT_BATON is the baton of the parent directory. 354251881Speter * The directory and its parent may or may not exist in the working copy. 355251881Speter */ 356251881Speterstatic struct file_baton_t * 357251881Spetermake_file_baton(const char *path, 358251881Speter svn_boolean_t added, 359251881Speter struct dir_baton_t *parent_baton, 360251881Speter apr_pool_t *result_pool) 361251881Speter{ 362251881Speter apr_pool_t *file_pool = svn_pool_create(result_pool); 363251881Speter struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb)); 364251881Speter struct edit_baton_t *eb = parent_baton->eb; 365251881Speter 366251881Speter fb->eb = eb; 367251881Speter fb->parent_baton = parent_baton; 368251881Speter fb->parent_baton->users++; 369251881Speter 370251881Speter /* Allocate 1 string for using as 3 strings */ 371251881Speter fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool); 372251881Speter fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath); 373251881Speter fb->name = svn_dirent_basename(fb->relpath, NULL); 374251881Speter 375251881Speter fb->added = added; 376251881Speter fb->pool = file_pool; 377251881Speter fb->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t)); 378251881Speter 379251881Speter return fb; 380251881Speter} 381251881Speter 382251881Speter/* Destroy DB when there are no more registered users */ 383251881Speterstatic svn_error_t * 384251881Spetermaybe_done(struct dir_baton_t *db) 385251881Speter{ 386251881Speter db->users--; 387251881Speter 388251881Speter if (!db->users) 389251881Speter { 390251881Speter struct dir_baton_t *pb = db->parent_baton; 391251881Speter 392251881Speter svn_pool_clear(db->pool); 393251881Speter 394251881Speter if (pb != NULL) 395251881Speter SVN_ERR(maybe_done(pb)); 396251881Speter } 397251881Speter 398251881Speter return SVN_NO_ERROR; 399251881Speter} 400251881Speter 401251881Speter/* Standard check to see if a node is represented in the local working copy */ 402251881Speter#define NOT_PRESENT(status) \ 403251881Speter ((status) == svn_wc__db_status_not_present \ 404251881Speter || (status) == svn_wc__db_status_excluded \ 405251881Speter || (status) == svn_wc__db_status_server_excluded) 406251881Speter 407251881Spetersvn_error_t * 408251881Spetersvn_wc__diff_base_working_diff(svn_wc__db_t *db, 409251881Speter const char *local_abspath, 410251881Speter const char *relpath, 411251881Speter svn_revnum_t revision, 412251881Speter apr_hash_t *changelist_hash, 413251881Speter const svn_diff_tree_processor_t *processor, 414251881Speter void *processor_dir_baton, 415251881Speter svn_boolean_t diff_pristine, 416251881Speter svn_cancel_func_t cancel_func, 417251881Speter void *cancel_baton, 418251881Speter apr_pool_t *scratch_pool) 419251881Speter{ 420251881Speter void *file_baton = NULL; 421251881Speter svn_boolean_t skip = FALSE; 422251881Speter svn_wc__db_status_t status; 423251881Speter svn_revnum_t db_revision; 424251881Speter svn_boolean_t had_props; 425251881Speter svn_boolean_t props_mod; 426251881Speter svn_boolean_t files_same = FALSE; 427251881Speter svn_wc__db_status_t base_status; 428251881Speter const svn_checksum_t *working_checksum; 429251881Speter const svn_checksum_t *checksum; 430251881Speter svn_filesize_t recorded_size; 431251881Speter apr_time_t recorded_time; 432251881Speter const char *pristine_file; 433251881Speter const char *local_file; 434251881Speter svn_diff_source_t *left_src; 435251881Speter svn_diff_source_t *right_src; 436251881Speter apr_hash_t *base_props; 437251881Speter apr_hash_t *local_props; 438251881Speter apr_array_header_t *prop_changes; 439251881Speter const char *changelist; 440251881Speter 441251881Speter SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL, 442251881Speter NULL, NULL, NULL, NULL, &working_checksum, NULL, 443251881Speter NULL, NULL, NULL, NULL, NULL, &recorded_size, 444251881Speter &recorded_time, &changelist, NULL, NULL, 445251881Speter &had_props, &props_mod, NULL, NULL, NULL, 446251881Speter db, local_abspath, scratch_pool, scratch_pool)); 447251881Speter checksum = working_checksum; 448251881Speter 449251881Speter assert(status == svn_wc__db_status_normal 450251881Speter || status == svn_wc__db_status_added 451251881Speter || (status == svn_wc__db_status_deleted && diff_pristine)); 452251881Speter 453251881Speter /* If the item is not a member of a specified changelist (and there are 454251881Speter some specified changelists), skip it. */ 455251881Speter if (changelist_hash && !svn_hash_gets(changelist_hash, changelist)) 456251881Speter return SVN_NO_ERROR; 457251881Speter 458251881Speter 459251881Speter if (status != svn_wc__db_status_normal) 460251881Speter { 461251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision, 462251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 463251881Speter NULL, &checksum, NULL, NULL, &had_props, 464251881Speter NULL, NULL, 465251881Speter db, local_abspath, 466251881Speter scratch_pool, scratch_pool)); 467251881Speter recorded_size = SVN_INVALID_FILESIZE; 468251881Speter recorded_time = 0; 469251881Speter props_mod = TRUE; /* Requires compare */ 470251881Speter } 471251881Speter else if (diff_pristine) 472251881Speter files_same = TRUE; 473251881Speter else 474251881Speter { 475251881Speter const svn_io_dirent2_t *dirent; 476251881Speter 477251881Speter SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, 478251881Speter FALSE /* verify truename */, 479251881Speter TRUE /* ingore_enoent */, 480251881Speter scratch_pool, scratch_pool)); 481251881Speter 482251881Speter if (dirent->kind == svn_node_file 483251881Speter && dirent->filesize == recorded_size 484251881Speter && dirent->mtime == recorded_time) 485251881Speter { 486251881Speter files_same = TRUE; 487251881Speter } 488251881Speter } 489251881Speter 490251881Speter if (files_same && !props_mod) 491251881Speter return SVN_NO_ERROR; /* Cheap exit */ 492251881Speter 493251881Speter assert(checksum); 494251881Speter 495251881Speter if (!SVN_IS_VALID_REVNUM(revision)) 496251881Speter revision = db_revision; 497251881Speter 498251881Speter left_src = svn_diff__source_create(revision, scratch_pool); 499251881Speter right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 500251881Speter 501251881Speter SVN_ERR(processor->file_opened(&file_baton, &skip, relpath, 502251881Speter left_src, 503251881Speter right_src, 504251881Speter NULL /* copyfrom_src */, 505251881Speter processor_dir_baton, 506251881Speter processor, 507251881Speter scratch_pool, scratch_pool)); 508251881Speter 509251881Speter if (skip) 510251881Speter return SVN_NO_ERROR; 511251881Speter 512251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, 513251881Speter db, local_abspath, checksum, 514251881Speter scratch_pool, scratch_pool)); 515251881Speter 516251881Speter if (diff_pristine) 517251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&local_file, 518251881Speter db, local_abspath, 519251881Speter working_checksum, 520251881Speter scratch_pool, scratch_pool)); 521251881Speter else if (! (had_props || props_mod)) 522251881Speter local_file = local_abspath; 523251881Speter else if (files_same) 524251881Speter local_file = pristine_file; 525251881Speter else 526251881Speter SVN_ERR(svn_wc__internal_translated_file( 527251881Speter &local_file, local_abspath, 528251881Speter db, local_abspath, 529251881Speter SVN_WC_TRANSLATE_TO_NF 530251881Speter | SVN_WC_TRANSLATE_USE_GLOBAL_TMP, 531251881Speter cancel_func, cancel_baton, 532251881Speter scratch_pool, scratch_pool)); 533251881Speter 534251881Speter if (! files_same) 535251881Speter SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file, 536251881Speter pristine_file, scratch_pool)); 537251881Speter 538251881Speter if (had_props) 539251881Speter SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath, 540251881Speter scratch_pool, scratch_pool)); 541251881Speter else 542251881Speter base_props = apr_hash_make(scratch_pool); 543251881Speter 544251881Speter if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod)) 545251881Speter local_props = base_props; 546251881Speter else if (diff_pristine) 547251881Speter SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath, 548251881Speter scratch_pool, scratch_pool)); 549251881Speter else 550251881Speter SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath, 551251881Speter scratch_pool, scratch_pool)); 552251881Speter 553251881Speter SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool)); 554251881Speter 555251881Speter if (prop_changes->nelts || !files_same) 556251881Speter { 557251881Speter SVN_ERR(processor->file_changed(relpath, 558251881Speter left_src, 559251881Speter right_src, 560251881Speter pristine_file, 561251881Speter local_file, 562251881Speter base_props, 563251881Speter local_props, 564251881Speter ! files_same, 565251881Speter prop_changes, 566251881Speter file_baton, 567251881Speter processor, 568251881Speter scratch_pool)); 569251881Speter } 570251881Speter else 571251881Speter { 572251881Speter SVN_ERR(processor->file_closed(relpath, 573251881Speter left_src, 574251881Speter right_src, 575251881Speter file_baton, 576251881Speter processor, 577251881Speter scratch_pool)); 578251881Speter } 579251881Speter 580251881Speter return SVN_NO_ERROR; 581251881Speter} 582251881Speter 583251881Speterstatic svn_error_t * 584251881Speterensure_local_info(struct dir_baton_t *db, 585251881Speter apr_pool_t *scratch_pool) 586251881Speter{ 587251881Speter apr_hash_t *conflicts; 588251881Speter 589251881Speter if (db->local_info) 590251881Speter return SVN_NO_ERROR; 591251881Speter 592251881Speter SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts, 593251881Speter db->eb->db, db->local_abspath, 594251881Speter db->pool, scratch_pool)); 595251881Speter 596251881Speter return SVN_NO_ERROR; 597251881Speter} 598251881Speter 599251881Speter/* Called when the directory is closed to compare any elements that have 600251881Speter * not yet been compared. This identifies local, working copy only 601251881Speter * changes. At this stage we are dealing with files/directories that do 602251881Speter * exist in the working copy. 603251881Speter * 604251881Speter * DIR_BATON is the baton for the directory. 605251881Speter */ 606251881Speterstatic svn_error_t * 607251881Speterwalk_local_nodes_diff(struct edit_baton_t *eb, 608251881Speter const char *local_abspath, 609251881Speter const char *path, 610251881Speter svn_depth_t depth, 611251881Speter apr_hash_t *compared, 612251881Speter void *parent_baton, 613251881Speter apr_pool_t *scratch_pool) 614251881Speter{ 615251881Speter svn_wc__db_t *db = eb->db; 616251881Speter svn_boolean_t in_anchor_not_target; 617251881Speter apr_pool_t *iterpool; 618251881Speter void *dir_baton = NULL; 619251881Speter svn_boolean_t skip = FALSE; 620251881Speter svn_boolean_t skip_children = FALSE; 621251881Speter svn_revnum_t revision; 622251881Speter svn_boolean_t props_mod; 623251881Speter svn_diff_source_t *left_src; 624251881Speter svn_diff_source_t *right_src; 625251881Speter 626251881Speter /* Everything we do below is useless if we are comparing to BASE. */ 627251881Speter if (eb->diff_pristine) 628251881Speter return SVN_NO_ERROR; 629251881Speter 630251881Speter /* Determine if this is the anchor directory if the anchor is different 631251881Speter to the target. When the target is a file, the anchor is the parent 632251881Speter directory and if this is that directory the non-target entries must be 633251881Speter skipped. */ 634251881Speter in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0')); 635251881Speter 636251881Speter iterpool = svn_pool_create(scratch_pool); 637251881Speter 638251881Speter SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL, 639251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 640251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 641251881Speter NULL, &props_mod, NULL, NULL, NULL, 642251881Speter db, local_abspath, scratch_pool, scratch_pool)); 643251881Speter 644251881Speter left_src = svn_diff__source_create(revision, scratch_pool); 645251881Speter right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 646251881Speter 647251881Speter if (compared) 648251881Speter { 649251881Speter dir_baton = parent_baton; 650251881Speter skip = TRUE; 651251881Speter } 652251881Speter else if (!in_anchor_not_target) 653251881Speter SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children, 654251881Speter path, 655251881Speter left_src, 656251881Speter right_src, 657251881Speter NULL /* copyfrom_src */, 658251881Speter parent_baton, 659251881Speter eb->processor, 660251881Speter scratch_pool, scratch_pool)); 661251881Speter 662251881Speter 663251881Speter if (!skip_children && depth != svn_depth_empty) 664251881Speter { 665251881Speter apr_hash_t *nodes; 666251881Speter apr_hash_t *conflicts; 667251881Speter apr_array_header_t *children; 668251881Speter svn_depth_t depth_below_here = depth; 669251881Speter svn_boolean_t diff_files; 670251881Speter svn_boolean_t diff_dirs; 671251881Speter int i; 672251881Speter 673251881Speter if (depth_below_here == svn_depth_immediates) 674251881Speter depth_below_here = svn_depth_empty; 675251881Speter 676251881Speter diff_files = (depth == svn_depth_unknown 677251881Speter || depth >= svn_depth_files); 678251881Speter diff_dirs = (depth == svn_depth_unknown 679251881Speter || depth >= svn_depth_immediates); 680251881Speter 681251881Speter SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, 682251881Speter db, local_abspath, 683251881Speter scratch_pool, iterpool)); 684251881Speter 685251881Speter children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, 686251881Speter scratch_pool); 687251881Speter 688251881Speter for (i = 0; i < children->nelts; i++) 689251881Speter { 690251881Speter svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, 691251881Speter svn_sort__item_t); 692251881Speter const char *name = item->key; 693251881Speter struct svn_wc__db_info_t *info = item->value; 694251881Speter 695251881Speter const char *child_abspath; 696251881Speter const char *child_relpath; 697251881Speter svn_boolean_t repos_only; 698251881Speter svn_boolean_t local_only; 699251881Speter svn_node_kind_t base_kind; 700251881Speter 701251881Speter if (eb->cancel_func) 702251881Speter SVN_ERR(eb->cancel_func(eb->cancel_baton)); 703251881Speter 704251881Speter /* In the anchor directory, if the anchor is not the target then all 705251881Speter entries other than the target should not be diff'd. Running diff 706251881Speter on one file in a directory should not diff other files in that 707251881Speter directory. */ 708251881Speter if (in_anchor_not_target && strcmp(eb->target, name)) 709251881Speter continue; 710251881Speter 711251881Speter if (compared && svn_hash_gets(compared, name)) 712251881Speter continue; 713251881Speter 714251881Speter if (NOT_PRESENT(info->status)) 715251881Speter continue; 716251881Speter 717251881Speter assert(info->status == svn_wc__db_status_normal 718251881Speter || info->status == svn_wc__db_status_added 719251881Speter || info->status == svn_wc__db_status_deleted); 720251881Speter 721251881Speter svn_pool_clear(iterpool); 722251881Speter child_abspath = svn_dirent_join(local_abspath, name, iterpool); 723251881Speter child_relpath = svn_relpath_join(path, name, iterpool); 724251881Speter 725251881Speter repos_only = FALSE; 726251881Speter local_only = FALSE; 727251881Speter 728251881Speter if (!info->have_base) 729251881Speter { 730251881Speter local_only = TRUE; /* Only report additions */ 731251881Speter } 732251881Speter else if (info->status == svn_wc__db_status_normal) 733251881Speter { 734251881Speter /* Simple diff */ 735251881Speter base_kind = info->kind; 736251881Speter } 737251881Speter else if (info->status == svn_wc__db_status_deleted 738251881Speter && (!eb->diff_pristine || !info->have_more_work)) 739251881Speter { 740251881Speter svn_wc__db_status_t base_status; 741251881Speter repos_only = TRUE; 742251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, 743251881Speter NULL, NULL, NULL, NULL, NULL, 744251881Speter NULL, NULL, NULL, NULL, NULL, 745251881Speter NULL, NULL, NULL, 746251881Speter db, child_abspath, 747251881Speter iterpool, iterpool)); 748251881Speter 749251881Speter if (NOT_PRESENT(base_status)) 750251881Speter continue; 751251881Speter } 752251881Speter else 753251881Speter { 754251881Speter /* working status is either added or deleted */ 755251881Speter svn_wc__db_status_t base_status; 756251881Speter 757251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, 758251881Speter NULL, NULL, NULL, NULL, NULL, 759251881Speter NULL, NULL, NULL, NULL, NULL, 760251881Speter NULL, NULL, NULL, 761251881Speter db, child_abspath, 762251881Speter iterpool, iterpool)); 763251881Speter 764251881Speter if (NOT_PRESENT(base_status)) 765251881Speter local_only = TRUE; 766251881Speter else if (base_kind != info->kind || !eb->ignore_ancestry) 767251881Speter { 768251881Speter repos_only = TRUE; 769251881Speter local_only = TRUE; 770251881Speter } 771251881Speter } 772251881Speter 773251881Speter if (eb->local_before_remote && local_only) 774251881Speter { 775251881Speter if (info->kind == svn_node_file && diff_files) 776251881Speter SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, 777251881Speter child_relpath, 778251881Speter eb->processor, dir_baton, 779251881Speter eb->changelist_hash, 780251881Speter eb->diff_pristine, 781251881Speter eb->cancel_func, 782251881Speter eb->cancel_baton, 783251881Speter iterpool)); 784251881Speter else if (info->kind == svn_node_dir && diff_dirs) 785251881Speter SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, 786251881Speter child_relpath, 787251881Speter depth_below_here, 788251881Speter eb->processor, dir_baton, 789251881Speter eb->changelist_hash, 790251881Speter eb->diff_pristine, 791251881Speter eb->cancel_func, 792251881Speter eb->cancel_baton, 793251881Speter iterpool)); 794251881Speter } 795251881Speter 796251881Speter if (repos_only) 797251881Speter { 798251881Speter /* Report repository form deleted */ 799251881Speter if (base_kind == svn_node_file && diff_files) 800251881Speter SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, 801251881Speter child_relpath, eb->revnum, 802251881Speter eb->processor, dir_baton, 803251881Speter iterpool)); 804251881Speter else if (base_kind == svn_node_dir && diff_dirs) 805251881Speter SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, 806251881Speter child_relpath, eb->revnum, 807251881Speter depth_below_here, 808251881Speter eb->processor, dir_baton, 809251881Speter eb->cancel_func, 810251881Speter eb->cancel_baton, 811251881Speter iterpool)); 812251881Speter } 813251881Speter else if (!local_only) /* Not local only nor remote only */ 814251881Speter { 815251881Speter /* Diff base against actual */ 816251881Speter if (info->kind == svn_node_file && diff_files) 817251881Speter { 818251881Speter if (info->status != svn_wc__db_status_normal 819251881Speter || !eb->diff_pristine) 820251881Speter { 821251881Speter SVN_ERR(svn_wc__diff_base_working_diff( 822251881Speter db, child_abspath, 823251881Speter child_relpath, 824251881Speter eb->revnum, 825251881Speter eb->changelist_hash, 826251881Speter eb->processor, dir_baton, 827251881Speter eb->diff_pristine, 828251881Speter eb->cancel_func, 829251881Speter eb->cancel_baton, 830251881Speter scratch_pool)); 831251881Speter } 832251881Speter } 833251881Speter else if (info->kind == svn_node_dir && diff_dirs) 834251881Speter SVN_ERR(walk_local_nodes_diff(eb, child_abspath, 835251881Speter child_relpath, 836251881Speter depth_below_here, 837251881Speter NULL /* compared */, 838251881Speter dir_baton, 839251881Speter scratch_pool)); 840251881Speter } 841251881Speter 842251881Speter if (!eb->local_before_remote && local_only) 843251881Speter { 844251881Speter if (info->kind == svn_node_file && diff_files) 845251881Speter SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, 846251881Speter child_relpath, 847251881Speter eb->processor, dir_baton, 848251881Speter eb->changelist_hash, 849251881Speter eb->diff_pristine, 850251881Speter eb->cancel_func, 851251881Speter eb->cancel_baton, 852251881Speter iterpool)); 853251881Speter else if (info->kind == svn_node_dir && diff_dirs) 854251881Speter SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, 855251881Speter child_relpath, depth_below_here, 856251881Speter eb->processor, dir_baton, 857251881Speter eb->changelist_hash, 858251881Speter eb->diff_pristine, 859251881Speter eb->cancel_func, 860251881Speter eb->cancel_baton, 861251881Speter iterpool)); 862251881Speter } 863251881Speter } 864251881Speter } 865251881Speter 866251881Speter if (compared) 867251881Speter return SVN_NO_ERROR; 868251881Speter 869251881Speter /* Check for local property mods on this directory, if we haven't 870251881Speter already reported them and we aren't changelist-filted. 871251881Speter ### it should be noted that we do not currently allow directories 872251881Speter ### to be part of changelists, so if a changelist is provided, the 873251881Speter ### changelist check will always fail. */ 874251881Speter if (! skip 875251881Speter && ! eb->changelist_hash 876251881Speter && ! in_anchor_not_target 877251881Speter && props_mod) 878251881Speter { 879251881Speter apr_array_header_t *propchanges; 880251881Speter apr_hash_t *left_props; 881251881Speter apr_hash_t *right_props; 882251881Speter 883251881Speter SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props, 884251881Speter db, local_abspath, 885251881Speter scratch_pool, scratch_pool)); 886251881Speter 887251881Speter right_props = svn_prop__patch(left_props, propchanges, scratch_pool); 888251881Speter 889251881Speter SVN_ERR(eb->processor->dir_changed(path, 890251881Speter left_src, 891251881Speter right_src, 892251881Speter left_props, 893251881Speter right_props, 894251881Speter propchanges, 895251881Speter dir_baton, 896251881Speter eb->processor, 897251881Speter scratch_pool)); 898251881Speter } 899251881Speter else if (! skip) 900251881Speter SVN_ERR(eb->processor->dir_closed(path, 901251881Speter left_src, 902251881Speter right_src, 903251881Speter dir_baton, 904251881Speter eb->processor, 905251881Speter scratch_pool)); 906251881Speter 907251881Speter svn_pool_destroy(iterpool); 908251881Speter 909251881Speter return SVN_NO_ERROR; 910251881Speter} 911251881Speter 912251881Spetersvn_error_t * 913251881Spetersvn_wc__diff_local_only_file(svn_wc__db_t *db, 914251881Speter const char *local_abspath, 915251881Speter const char *relpath, 916251881Speter const svn_diff_tree_processor_t *processor, 917251881Speter void *processor_parent_baton, 918251881Speter apr_hash_t *changelist_hash, 919251881Speter svn_boolean_t diff_pristine, 920251881Speter svn_cancel_func_t cancel_func, 921251881Speter void *cancel_baton, 922251881Speter apr_pool_t *scratch_pool) 923251881Speter{ 924251881Speter svn_diff_source_t *right_src; 925251881Speter svn_diff_source_t *copyfrom_src = NULL; 926251881Speter svn_wc__db_status_t status; 927251881Speter svn_node_kind_t kind; 928251881Speter const svn_checksum_t *checksum; 929251881Speter const char *original_repos_relpath; 930251881Speter svn_revnum_t original_revision; 931251881Speter const char *changelist; 932251881Speter svn_boolean_t had_props; 933251881Speter svn_boolean_t props_mod; 934251881Speter apr_hash_t *pristine_props; 935251881Speter apr_hash_t *right_props = NULL; 936251881Speter const char *pristine_file; 937251881Speter const char *translated_file; 938251881Speter svn_revnum_t revision; 939251881Speter void *file_baton = NULL; 940251881Speter svn_boolean_t skip = FALSE; 941251881Speter svn_boolean_t file_mod = TRUE; 942251881Speter 943251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL, 944251881Speter NULL, NULL, NULL, NULL, &checksum, NULL, 945251881Speter &original_repos_relpath, NULL, NULL, 946251881Speter &original_revision, NULL, NULL, NULL, 947251881Speter &changelist, NULL, NULL, &had_props, 948251881Speter &props_mod, NULL, NULL, NULL, 949251881Speter db, local_abspath, 950251881Speter scratch_pool, scratch_pool)); 951251881Speter 952251881Speter assert(kind == svn_node_file 953251881Speter && (status == svn_wc__db_status_normal 954251881Speter || status == svn_wc__db_status_added 955251881Speter || (status == svn_wc__db_status_deleted && diff_pristine))); 956251881Speter 957251881Speter 958251881Speter if (changelist && changelist_hash 959251881Speter && !svn_hash_gets(changelist_hash, changelist)) 960251881Speter return SVN_NO_ERROR; 961251881Speter 962251881Speter if (status == svn_wc__db_status_deleted) 963251881Speter { 964251881Speter assert(diff_pristine); 965251881Speter 966251881Speter SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL, 967251881Speter NULL, &checksum, NULL, &had_props, 968251881Speter &pristine_props, 969251881Speter db, local_abspath, 970251881Speter scratch_pool, scratch_pool)); 971251881Speter props_mod = FALSE; 972251881Speter } 973251881Speter else if (!had_props) 974251881Speter pristine_props = apr_hash_make(scratch_pool); 975251881Speter else 976251881Speter SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, 977251881Speter db, local_abspath, 978251881Speter scratch_pool, scratch_pool)); 979251881Speter 980251881Speter if (original_repos_relpath) 981251881Speter { 982251881Speter copyfrom_src = svn_diff__source_create(original_revision, scratch_pool); 983251881Speter copyfrom_src->repos_relpath = original_repos_relpath; 984251881Speter } 985251881Speter 986251881Speter if (props_mod || !SVN_IS_VALID_REVNUM(revision)) 987251881Speter right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 988251881Speter else 989251881Speter { 990251881Speter if (diff_pristine) 991251881Speter file_mod = FALSE; 992251881Speter else 993251881Speter SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath, 994251881Speter FALSE, scratch_pool)); 995251881Speter 996251881Speter if (!file_mod) 997251881Speter right_src = svn_diff__source_create(revision, scratch_pool); 998251881Speter else 999251881Speter right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 1000251881Speter } 1001251881Speter 1002251881Speter SVN_ERR(processor->file_opened(&file_baton, &skip, 1003251881Speter relpath, 1004251881Speter NULL /* left_source */, 1005251881Speter right_src, 1006251881Speter copyfrom_src, 1007251881Speter processor_parent_baton, 1008251881Speter processor, 1009251881Speter scratch_pool, scratch_pool)); 1010251881Speter 1011251881Speter if (skip) 1012251881Speter return SVN_NO_ERROR; 1013251881Speter 1014251881Speter if (props_mod && !diff_pristine) 1015251881Speter SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath, 1016251881Speter scratch_pool, scratch_pool)); 1017251881Speter else 1018251881Speter right_props = svn_prop_hash_dup(pristine_props, scratch_pool); 1019251881Speter 1020251881Speter if (checksum) 1021251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath, 1022251881Speter checksum, scratch_pool, scratch_pool)); 1023251881Speter else 1024251881Speter pristine_file = NULL; 1025251881Speter 1026251881Speter if (diff_pristine) 1027251881Speter { 1028251881Speter translated_file = pristine_file; /* No translation needed */ 1029251881Speter } 1030251881Speter else 1031251881Speter { 1032251881Speter SVN_ERR(svn_wc__internal_translated_file( 1033251881Speter &translated_file, local_abspath, db, local_abspath, 1034251881Speter SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP, 1035251881Speter cancel_func, cancel_baton, 1036251881Speter scratch_pool, scratch_pool)); 1037251881Speter } 1038251881Speter 1039251881Speter SVN_ERR(processor->file_added(relpath, 1040251881Speter copyfrom_src, 1041251881Speter right_src, 1042251881Speter copyfrom_src 1043251881Speter ? pristine_file 1044251881Speter : NULL, 1045251881Speter translated_file, 1046251881Speter copyfrom_src 1047251881Speter ? pristine_props 1048251881Speter : NULL, 1049251881Speter right_props, 1050251881Speter file_baton, 1051251881Speter processor, 1052251881Speter scratch_pool)); 1053251881Speter 1054251881Speter return SVN_NO_ERROR; 1055251881Speter} 1056251881Speter 1057251881Spetersvn_error_t * 1058251881Spetersvn_wc__diff_local_only_dir(svn_wc__db_t *db, 1059251881Speter const char *local_abspath, 1060251881Speter const char *relpath, 1061251881Speter svn_depth_t depth, 1062251881Speter const svn_diff_tree_processor_t *processor, 1063251881Speter void *processor_parent_baton, 1064251881Speter apr_hash_t *changelist_hash, 1065251881Speter svn_boolean_t diff_pristine, 1066251881Speter svn_cancel_func_t cancel_func, 1067251881Speter void *cancel_baton, 1068251881Speter apr_pool_t *scratch_pool) 1069251881Speter{ 1070251881Speter const apr_array_header_t *children; 1071251881Speter int i; 1072251881Speter apr_pool_t *iterpool; 1073251881Speter void *pdb = NULL; 1074251881Speter svn_boolean_t skip = FALSE; 1075251881Speter svn_boolean_t skip_children = FALSE; 1076251881Speter svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM, 1077251881Speter scratch_pool); 1078251881Speter svn_depth_t depth_below_here = depth; 1079251881Speter apr_hash_t *nodes; 1080251881Speter apr_hash_t *conflicts; 1081251881Speter 1082251881Speter /* Report the addition of the directory's contents. */ 1083251881Speter iterpool = svn_pool_create(scratch_pool); 1084251881Speter 1085251881Speter SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children, 1086251881Speter relpath, 1087251881Speter NULL, 1088251881Speter right_src, 1089251881Speter NULL /* copyfrom_src */, 1090251881Speter processor_parent_baton, 1091251881Speter processor, 1092251881Speter scratch_pool, iterpool)); 1093251881Speter 1094251881Speter SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath, 1095251881Speter scratch_pool, iterpool)); 1096251881Speter 1097251881Speter if (depth_below_here == svn_depth_immediates) 1098251881Speter depth_below_here = svn_depth_empty; 1099251881Speter 1100251881Speter children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, 1101251881Speter scratch_pool); 1102251881Speter 1103251881Speter for (i = 0; i < children->nelts; i++) 1104251881Speter { 1105251881Speter svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t); 1106251881Speter const char *name = item->key; 1107251881Speter struct svn_wc__db_info_t *info = item->value; 1108251881Speter const char *child_abspath; 1109251881Speter const char *child_relpath; 1110251881Speter 1111251881Speter svn_pool_clear(iterpool); 1112251881Speter 1113251881Speter if (cancel_func) 1114251881Speter SVN_ERR(cancel_func(cancel_baton)); 1115251881Speter 1116251881Speter child_abspath = svn_dirent_join(local_abspath, name, iterpool); 1117251881Speter 1118251881Speter if (NOT_PRESENT(info->status)) 1119251881Speter { 1120251881Speter continue; 1121251881Speter } 1122251881Speter 1123251881Speter /* If comparing against WORKING, skip entries that are 1124251881Speter schedule-deleted - they don't really exist. */ 1125251881Speter if (!diff_pristine && info->status == svn_wc__db_status_deleted) 1126251881Speter continue; 1127251881Speter 1128251881Speter child_relpath = svn_relpath_join(relpath, name, iterpool); 1129251881Speter 1130251881Speter switch (info->kind) 1131251881Speter { 1132251881Speter case svn_node_file: 1133251881Speter case svn_node_symlink: 1134251881Speter SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, 1135251881Speter child_relpath, 1136251881Speter processor, pdb, 1137251881Speter changelist_hash, 1138251881Speter diff_pristine, 1139251881Speter cancel_func, cancel_baton, 1140251881Speter scratch_pool)); 1141251881Speter break; 1142251881Speter 1143251881Speter case svn_node_dir: 1144251881Speter if (depth > svn_depth_files || depth == svn_depth_unknown) 1145251881Speter { 1146251881Speter SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, 1147251881Speter child_relpath, depth_below_here, 1148251881Speter processor, pdb, 1149251881Speter changelist_hash, 1150251881Speter diff_pristine, 1151251881Speter cancel_func, cancel_baton, 1152251881Speter iterpool)); 1153251881Speter } 1154251881Speter break; 1155251881Speter 1156251881Speter default: 1157251881Speter break; 1158251881Speter } 1159251881Speter } 1160251881Speter 1161251881Speter if (!skip) 1162251881Speter { 1163251881Speter apr_hash_t *right_props; 1164251881Speter if (diff_pristine) 1165251881Speter SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath, 1166251881Speter scratch_pool, scratch_pool)); 1167251881Speter else 1168251881Speter SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath, 1169251881Speter scratch_pool, scratch_pool)); 1170251881Speter 1171251881Speter SVN_ERR(processor->dir_added(relpath, 1172251881Speter NULL /* copyfrom_src */, 1173251881Speter right_src, 1174251881Speter NULL, 1175251881Speter right_props, 1176251881Speter pdb, 1177251881Speter processor, 1178251881Speter iterpool)); 1179251881Speter } 1180251881Speter svn_pool_destroy(iterpool); 1181251881Speter 1182251881Speter return SVN_NO_ERROR; 1183251881Speter} 1184251881Speter 1185251881Speter/* Reports local changes. */ 1186251881Speterstatic svn_error_t * 1187251881Speterhandle_local_only(struct dir_baton_t *pb, 1188251881Speter const char *name, 1189251881Speter apr_pool_t *scratch_pool) 1190251881Speter{ 1191251881Speter struct edit_baton_t *eb = pb->eb; 1192251881Speter const struct svn_wc__db_info_t *info; 1193251881Speter svn_boolean_t repos_delete = (pb->deletes 1194251881Speter && svn_hash_gets(pb->deletes, name)); 1195251881Speter 1196251881Speter assert(!strchr(name, '/')); 1197251881Speter assert(!pb->added || eb->ignore_ancestry); 1198251881Speter 1199251881Speter if (pb->skip_children) 1200251881Speter return SVN_NO_ERROR; 1201251881Speter 1202251881Speter SVN_ERR(ensure_local_info(pb, scratch_pool)); 1203251881Speter 1204251881Speter info = svn_hash_gets(pb->local_info, name); 1205251881Speter 1206251881Speter if (info == NULL || NOT_PRESENT(info->status)) 1207251881Speter return SVN_NO_ERROR; 1208251881Speter 1209251881Speter switch (info->status) 1210251881Speter { 1211251881Speter case svn_wc__db_status_incomplete: 1212251881Speter return SVN_NO_ERROR; /* Not local only */ 1213251881Speter 1214251881Speter case svn_wc__db_status_normal: 1215251881Speter if (!repos_delete) 1216251881Speter return SVN_NO_ERROR; /* Local and remote */ 1217251881Speter svn_hash_sets(pb->deletes, name, NULL); 1218251881Speter break; 1219251881Speter 1220251881Speter case svn_wc__db_status_deleted: 1221251881Speter if (!(eb->diff_pristine && repos_delete)) 1222251881Speter return SVN_NO_ERROR; 1223251881Speter break; 1224251881Speter 1225251881Speter case svn_wc__db_status_added: 1226251881Speter default: 1227251881Speter break; 1228251881Speter } 1229251881Speter 1230251881Speter if (info->kind == svn_node_dir) 1231251881Speter { 1232251881Speter svn_depth_t depth ; 1233251881Speter 1234251881Speter if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown) 1235251881Speter depth = pb->depth; 1236251881Speter else 1237251881Speter depth = svn_depth_empty; 1238251881Speter 1239251881Speter SVN_ERR(svn_wc__diff_local_only_dir( 1240251881Speter eb->db, 1241251881Speter svn_dirent_join(pb->local_abspath, name, scratch_pool), 1242251881Speter svn_relpath_join(pb->relpath, name, scratch_pool), 1243251881Speter repos_delete ? svn_depth_infinity : depth, 1244251881Speter eb->processor, pb->pdb, 1245251881Speter eb->changelist_hash, 1246251881Speter eb->diff_pristine, 1247251881Speter eb->cancel_func, eb->cancel_baton, 1248251881Speter scratch_pool)); 1249251881Speter } 1250251881Speter else 1251251881Speter SVN_ERR(svn_wc__diff_local_only_file( 1252251881Speter eb->db, 1253251881Speter svn_dirent_join(pb->local_abspath, name, scratch_pool), 1254251881Speter svn_relpath_join(pb->relpath, name, scratch_pool), 1255251881Speter eb->processor, pb->pdb, 1256251881Speter eb->changelist_hash, 1257251881Speter eb->diff_pristine, 1258251881Speter eb->cancel_func, eb->cancel_baton, 1259251881Speter scratch_pool)); 1260251881Speter 1261251881Speter return SVN_NO_ERROR; 1262251881Speter} 1263251881Speter 1264251881Speter/* Reports a file LOCAL_ABSPATH in BASE as deleted */ 1265251881Spetersvn_error_t * 1266251881Spetersvn_wc__diff_base_only_file(svn_wc__db_t *db, 1267251881Speter const char *local_abspath, 1268251881Speter const char *relpath, 1269251881Speter svn_revnum_t revision, 1270251881Speter const svn_diff_tree_processor_t *processor, 1271251881Speter void *processor_parent_baton, 1272251881Speter apr_pool_t *scratch_pool) 1273251881Speter{ 1274251881Speter svn_wc__db_status_t status; 1275251881Speter svn_node_kind_t kind; 1276251881Speter const svn_checksum_t *checksum; 1277251881Speter apr_hash_t *props; 1278251881Speter void *file_baton = NULL; 1279251881Speter svn_boolean_t skip = FALSE; 1280251881Speter svn_diff_source_t *left_src; 1281251881Speter const char *pristine_file; 1282251881Speter 1283251881Speter SVN_ERR(svn_wc__db_base_get_info(&status, &kind, 1284251881Speter SVN_IS_VALID_REVNUM(revision) 1285251881Speter ? NULL : &revision, 1286251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1287251881Speter &checksum, NULL, NULL, NULL, &props, NULL, 1288251881Speter db, local_abspath, 1289251881Speter scratch_pool, scratch_pool)); 1290251881Speter 1291251881Speter SVN_ERR_ASSERT(status == svn_wc__db_status_normal 1292251881Speter && kind == svn_node_file 1293251881Speter && checksum); 1294251881Speter 1295251881Speter left_src = svn_diff__source_create(revision, scratch_pool); 1296251881Speter 1297251881Speter SVN_ERR(processor->file_opened(&file_baton, &skip, 1298251881Speter relpath, 1299251881Speter left_src, 1300251881Speter NULL /* right_src */, 1301251881Speter NULL /* copyfrom_source */, 1302251881Speter processor_parent_baton, 1303251881Speter processor, 1304251881Speter scratch_pool, scratch_pool)); 1305251881Speter 1306251881Speter if (skip) 1307251881Speter return SVN_NO_ERROR; 1308251881Speter 1309251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, 1310251881Speter db, local_abspath, checksum, 1311251881Speter scratch_pool, scratch_pool)); 1312251881Speter 1313251881Speter SVN_ERR(processor->file_deleted(relpath, 1314251881Speter left_src, 1315251881Speter pristine_file, 1316251881Speter props, 1317251881Speter file_baton, 1318251881Speter processor, 1319251881Speter scratch_pool)); 1320251881Speter 1321251881Speter return SVN_NO_ERROR; 1322251881Speter} 1323251881Speter 1324251881Spetersvn_error_t * 1325251881Spetersvn_wc__diff_base_only_dir(svn_wc__db_t *db, 1326251881Speter const char *local_abspath, 1327251881Speter const char *relpath, 1328251881Speter svn_revnum_t revision, 1329251881Speter svn_depth_t depth, 1330251881Speter const svn_diff_tree_processor_t *processor, 1331251881Speter void *processor_parent_baton, 1332251881Speter svn_cancel_func_t cancel_func, 1333251881Speter void *cancel_baton, 1334251881Speter apr_pool_t *scratch_pool) 1335251881Speter{ 1336251881Speter void *dir_baton = NULL; 1337251881Speter svn_boolean_t skip = FALSE; 1338251881Speter svn_boolean_t skip_children = FALSE; 1339251881Speter svn_diff_source_t *left_src; 1340251881Speter svn_revnum_t report_rev = revision; 1341251881Speter 1342251881Speter if (!SVN_IS_VALID_REVNUM(report_rev)) 1343251881Speter SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL, 1344251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1345251881Speter NULL, NULL, NULL, 1346251881Speter db, local_abspath, 1347251881Speter scratch_pool, scratch_pool)); 1348251881Speter 1349251881Speter left_src = svn_diff__source_create(report_rev, scratch_pool); 1350251881Speter 1351251881Speter SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children, 1352251881Speter relpath, 1353251881Speter left_src, 1354251881Speter NULL /* right_src */, 1355251881Speter NULL /* copyfrom_src */, 1356251881Speter processor_parent_baton, 1357251881Speter processor, 1358251881Speter scratch_pool, scratch_pool)); 1359251881Speter 1360251881Speter if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty)) 1361251881Speter { 1362251881Speter apr_hash_t *nodes; 1363251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1364251881Speter apr_array_header_t *children; 1365251881Speter int i; 1366251881Speter 1367251881Speter SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath, 1368251881Speter scratch_pool, iterpool)); 1369251881Speter 1370251881Speter children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, 1371251881Speter scratch_pool); 1372251881Speter 1373251881Speter for (i = 0; i < children->nelts; i++) 1374251881Speter { 1375251881Speter svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, 1376251881Speter svn_sort__item_t); 1377251881Speter const char *name = item->key; 1378251881Speter struct svn_wc__db_base_info_t *info = item->value; 1379251881Speter const char *child_abspath; 1380251881Speter const char *child_relpath; 1381251881Speter 1382251881Speter if (info->status != svn_wc__db_status_normal) 1383251881Speter continue; 1384251881Speter 1385251881Speter if (cancel_func) 1386251881Speter SVN_ERR(cancel_func(cancel_baton)); 1387251881Speter 1388251881Speter svn_pool_clear(iterpool); 1389251881Speter 1390251881Speter child_abspath = svn_dirent_join(local_abspath, name, iterpool); 1391251881Speter child_relpath = svn_relpath_join(relpath, name, iterpool); 1392251881Speter 1393251881Speter switch (info->kind) 1394251881Speter { 1395251881Speter case svn_node_file: 1396251881Speter case svn_node_symlink: 1397251881Speter SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, 1398251881Speter child_relpath, 1399251881Speter revision, 1400251881Speter processor, dir_baton, 1401251881Speter iterpool)); 1402251881Speter break; 1403251881Speter case svn_node_dir: 1404251881Speter if (depth > svn_depth_files || depth == svn_depth_unknown) 1405251881Speter { 1406251881Speter svn_depth_t depth_below_here = depth; 1407251881Speter 1408251881Speter if (depth_below_here == svn_depth_immediates) 1409251881Speter depth_below_here = svn_depth_empty; 1410251881Speter 1411251881Speter SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, 1412251881Speter child_relpath, 1413251881Speter revision, 1414251881Speter depth_below_here, 1415251881Speter processor, dir_baton, 1416251881Speter cancel_func, 1417251881Speter cancel_baton, 1418251881Speter iterpool)); 1419251881Speter } 1420251881Speter break; 1421251881Speter 1422251881Speter default: 1423251881Speter break; 1424251881Speter } 1425251881Speter } 1426251881Speter } 1427251881Speter 1428251881Speter if (!skip) 1429251881Speter { 1430251881Speter apr_hash_t *props; 1431251881Speter SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath, 1432251881Speter scratch_pool, scratch_pool)); 1433251881Speter 1434251881Speter SVN_ERR(processor->dir_deleted(relpath, 1435251881Speter left_src, 1436251881Speter props, 1437251881Speter dir_baton, 1438251881Speter processor, 1439251881Speter scratch_pool)); 1440251881Speter } 1441251881Speter 1442251881Speter return SVN_NO_ERROR; 1443251881Speter} 1444251881Speter 1445251881Speter/* An svn_delta_editor_t function. */ 1446251881Speterstatic svn_error_t * 1447251881Speterset_target_revision(void *edit_baton, 1448251881Speter svn_revnum_t target_revision, 1449251881Speter apr_pool_t *pool) 1450251881Speter{ 1451251881Speter struct edit_baton_t *eb = edit_baton; 1452251881Speter eb->revnum = target_revision; 1453251881Speter 1454251881Speter return SVN_NO_ERROR; 1455251881Speter} 1456251881Speter 1457251881Speter/* An svn_delta_editor_t function. The root of the comparison hierarchy */ 1458251881Speterstatic svn_error_t * 1459251881Speteropen_root(void *edit_baton, 1460251881Speter svn_revnum_t base_revision, 1461251881Speter apr_pool_t *dir_pool, 1462251881Speter void **root_baton) 1463251881Speter{ 1464251881Speter struct edit_baton_t *eb = edit_baton; 1465251881Speter struct dir_baton_t *db; 1466251881Speter 1467251881Speter eb->root_opened = TRUE; 1468251881Speter db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool); 1469251881Speter *root_baton = db; 1470251881Speter 1471251881Speter if (eb->target[0] == '\0') 1472251881Speter { 1473251881Speter db->left_src = svn_diff__source_create(eb->revnum, db->pool); 1474251881Speter db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool); 1475251881Speter 1476251881Speter SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, 1477251881Speter &db->skip_children, 1478251881Speter "", 1479251881Speter db->left_src, 1480251881Speter db->right_src, 1481251881Speter NULL /* copyfrom_source */, 1482251881Speter NULL /* parent_baton */, 1483251881Speter eb->processor, 1484251881Speter db->pool, db->pool)); 1485251881Speter } 1486251881Speter else 1487251881Speter db->skip = TRUE; /* Skip this, but not the children */ 1488251881Speter 1489251881Speter return SVN_NO_ERROR; 1490251881Speter} 1491251881Speter 1492251881Speter/* An svn_delta_editor_t function. */ 1493251881Speterstatic svn_error_t * 1494251881Speterdelete_entry(const char *path, 1495251881Speter svn_revnum_t base_revision, 1496251881Speter void *parent_baton, 1497251881Speter apr_pool_t *pool) 1498251881Speter{ 1499251881Speter struct dir_baton_t *pb = parent_baton; 1500251881Speter const char *name = svn_dirent_basename(path, pb->pool); 1501251881Speter 1502251881Speter if (!pb->deletes) 1503251881Speter pb->deletes = apr_hash_make(pb->pool); 1504251881Speter 1505251881Speter svn_hash_sets(pb->deletes, name, ""); 1506251881Speter return SVN_NO_ERROR; 1507251881Speter} 1508251881Speter 1509251881Speter/* An svn_delta_editor_t function. */ 1510251881Speterstatic svn_error_t * 1511251881Speteradd_directory(const char *path, 1512251881Speter void *parent_baton, 1513251881Speter const char *copyfrom_path, 1514251881Speter svn_revnum_t copyfrom_revision, 1515251881Speter apr_pool_t *dir_pool, 1516251881Speter void **child_baton) 1517251881Speter{ 1518251881Speter struct dir_baton_t *pb = parent_baton; 1519251881Speter struct edit_baton_t *eb = pb->eb; 1520251881Speter struct dir_baton_t *db; 1521251881Speter svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates) 1522251881Speter ? svn_depth_empty : pb->depth; 1523251881Speter 1524251881Speter db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth, 1525251881Speter dir_pool); 1526251881Speter *child_baton = db; 1527251881Speter 1528251881Speter if (pb->repos_only || !eb->ignore_ancestry) 1529251881Speter db->repos_only = TRUE; 1530251881Speter else 1531251881Speter { 1532251881Speter struct svn_wc__db_info_t *info; 1533251881Speter SVN_ERR(ensure_local_info(pb, dir_pool)); 1534251881Speter 1535251881Speter info = svn_hash_gets(pb->local_info, db->name); 1536251881Speter 1537251881Speter if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status)) 1538251881Speter db->repos_only = TRUE; 1539251881Speter 1540251881Speter if (!db->repos_only && info->status != svn_wc__db_status_added) 1541251881Speter db->repos_only = TRUE; 1542251881Speter 1543251881Speter if (!db->repos_only) 1544251881Speter { 1545251881Speter db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool); 1546251881Speter db->ignoring_ancestry = TRUE; 1547251881Speter 1548251881Speter svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), ""); 1549251881Speter } 1550251881Speter } 1551251881Speter 1552251881Speter db->left_src = svn_diff__source_create(eb->revnum, db->pool); 1553251881Speter 1554251881Speter if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry) 1555251881Speter SVN_ERR(handle_local_only(pb, db->name, dir_pool)); 1556251881Speter 1557251881Speter SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children, 1558251881Speter db->relpath, 1559251881Speter db->left_src, 1560251881Speter db->right_src, 1561251881Speter NULL /* copyfrom src */, 1562251881Speter pb->pdb, 1563251881Speter eb->processor, 1564251881Speter db->pool, db->pool)); 1565251881Speter 1566251881Speter return SVN_NO_ERROR; 1567251881Speter} 1568251881Speter 1569251881Speter/* An svn_delta_editor_t function. */ 1570251881Speterstatic svn_error_t * 1571251881Speteropen_directory(const char *path, 1572251881Speter void *parent_baton, 1573251881Speter svn_revnum_t base_revision, 1574251881Speter apr_pool_t *dir_pool, 1575251881Speter void **child_baton) 1576251881Speter{ 1577251881Speter struct dir_baton_t *pb = parent_baton; 1578251881Speter struct edit_baton_t *eb = pb->eb; 1579251881Speter struct dir_baton_t *db; 1580251881Speter svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates) 1581251881Speter ? svn_depth_empty : pb->depth; 1582251881Speter 1583251881Speter /* Allocate path from the parent pool since the memory is used in the 1584251881Speter parent's compared hash */ 1585251881Speter db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool); 1586251881Speter *child_baton = db; 1587251881Speter 1588251881Speter if (pb->repos_only) 1589251881Speter db->repos_only = TRUE; 1590251881Speter else 1591251881Speter { 1592251881Speter struct svn_wc__db_info_t *info; 1593251881Speter SVN_ERR(ensure_local_info(pb, dir_pool)); 1594251881Speter 1595251881Speter info = svn_hash_gets(pb->local_info, db->name); 1596251881Speter 1597251881Speter if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status)) 1598251881Speter db->repos_only = TRUE; 1599251881Speter 1600251881Speter if (!db->repos_only) 1601251881Speter switch (info->status) 1602251881Speter { 1603251881Speter case svn_wc__db_status_normal: 1604251881Speter break; 1605251881Speter case svn_wc__db_status_deleted: 1606251881Speter db->repos_only = TRUE; 1607251881Speter 1608251881Speter if (!info->have_more_work) 1609251881Speter svn_hash_sets(pb->compared, 1610251881Speter apr_pstrdup(pb->pool, db->name), ""); 1611251881Speter break; 1612251881Speter case svn_wc__db_status_added: 1613251881Speter if (eb->ignore_ancestry) 1614251881Speter db->ignoring_ancestry = TRUE; 1615251881Speter else 1616251881Speter db->repos_only = TRUE; 1617251881Speter break; 1618251881Speter default: 1619251881Speter SVN_ERR_MALFUNCTION(); 1620251881Speter } 1621251881Speter 1622251881Speter if (!db->repos_only) 1623251881Speter { 1624251881Speter db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool); 1625251881Speter svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), ""); 1626251881Speter } 1627251881Speter } 1628251881Speter 1629251881Speter db->left_src = svn_diff__source_create(eb->revnum, db->pool); 1630251881Speter 1631251881Speter if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry) 1632251881Speter SVN_ERR(handle_local_only(pb, db->name, dir_pool)); 1633251881Speter 1634251881Speter SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children, 1635251881Speter db->relpath, 1636251881Speter db->left_src, 1637251881Speter db->right_src, 1638251881Speter NULL /* copyfrom src */, 1639251881Speter pb->pdb, 1640251881Speter eb->processor, 1641251881Speter db->pool, db->pool)); 1642251881Speter 1643251881Speter return SVN_NO_ERROR; 1644251881Speter} 1645251881Speter 1646251881Speter 1647251881Speter/* An svn_delta_editor_t function. When a directory is closed, all the 1648251881Speter * directory elements that have been added or replaced will already have been 1649251881Speter * diff'd. However there may be other elements in the working copy 1650251881Speter * that have not yet been considered. */ 1651251881Speterstatic svn_error_t * 1652251881Speterclose_directory(void *dir_baton, 1653251881Speter apr_pool_t *pool) 1654251881Speter{ 1655251881Speter struct dir_baton_t *db = dir_baton; 1656251881Speter struct dir_baton_t *pb = db->parent_baton; 1657251881Speter struct edit_baton_t *eb = db->eb; 1658251881Speter apr_pool_t *scratch_pool = db->pool; 1659251881Speter svn_boolean_t reported_closed = FALSE; 1660251881Speter 1661251881Speter if (!db->skip_children && db->deletes && apr_hash_count(db->deletes)) 1662251881Speter { 1663251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1664251881Speter apr_array_header_t *children; 1665251881Speter int i; 1666251881Speter children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically, 1667251881Speter scratch_pool); 1668251881Speter 1669251881Speter for (i = 0; i < children->nelts; i++) 1670251881Speter { 1671251881Speter svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, 1672251881Speter svn_sort__item_t); 1673251881Speter const char *name = item->key; 1674251881Speter 1675251881Speter svn_pool_clear(iterpool); 1676251881Speter SVN_ERR(handle_local_only(db, name, iterpool)); 1677251881Speter 1678251881Speter svn_hash_sets(db->compared, name, ""); 1679251881Speter } 1680251881Speter 1681251881Speter svn_pool_destroy(iterpool); 1682251881Speter } 1683251881Speter 1684251881Speter /* Report local modifications for this directory. Skip added 1685251881Speter directories since they can only contain added elements, all of 1686251881Speter which have already been diff'd. */ 1687251881Speter if (!db->repos_only && !db->skip_children) 1688251881Speter { 1689251881Speter SVN_ERR(walk_local_nodes_diff(eb, 1690251881Speter db->local_abspath, 1691251881Speter db->relpath, 1692251881Speter db->depth, 1693251881Speter db->compared, 1694251881Speter db->pdb, 1695251881Speter scratch_pool)); 1696251881Speter } 1697251881Speter 1698251881Speter /* Report the property changes on the directory itself, if necessary. */ 1699251881Speter if (db->skip) 1700251881Speter { 1701251881Speter /* Diff processor requested no directory details */ 1702251881Speter } 1703251881Speter else if (db->propchanges->nelts > 0 || db->repos_only) 1704251881Speter { 1705251881Speter apr_hash_t *repos_props; 1706251881Speter 1707251881Speter if (db->added) 1708251881Speter { 1709251881Speter repos_props = apr_hash_make(scratch_pool); 1710251881Speter } 1711251881Speter else 1712251881Speter { 1713251881Speter SVN_ERR(svn_wc__db_base_get_props(&repos_props, 1714251881Speter eb->db, db->local_abspath, 1715251881Speter scratch_pool, scratch_pool)); 1716251881Speter } 1717251881Speter 1718251881Speter /* Add received property changes and entry props */ 1719251881Speter if (db->propchanges->nelts) 1720251881Speter repos_props = svn_prop__patch(repos_props, db->propchanges, 1721251881Speter scratch_pool); 1722251881Speter 1723251881Speter if (db->repos_only) 1724251881Speter { 1725251881Speter SVN_ERR(eb->processor->dir_deleted(db->relpath, 1726251881Speter db->left_src, 1727251881Speter repos_props, 1728251881Speter db->pdb, 1729251881Speter eb->processor, 1730251881Speter scratch_pool)); 1731251881Speter reported_closed = TRUE; 1732251881Speter } 1733251881Speter else 1734251881Speter { 1735251881Speter apr_hash_t *local_props; 1736251881Speter apr_array_header_t *prop_changes; 1737251881Speter 1738251881Speter if (eb->diff_pristine) 1739251881Speter SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL, 1740251881Speter NULL, NULL, NULL, NULL, 1741251881Speter &local_props, 1742251881Speter eb->db, db->local_abspath, 1743251881Speter scratch_pool, scratch_pool)); 1744251881Speter else 1745251881Speter SVN_ERR(svn_wc__db_read_props(&local_props, 1746251881Speter eb->db, db->local_abspath, 1747251881Speter scratch_pool, scratch_pool)); 1748251881Speter 1749251881Speter SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props, 1750251881Speter scratch_pool)); 1751251881Speter 1752251881Speter /* ### as a good diff processor we should now only report changes 1753251881Speter if there are non-entry changes, but for now we stick to 1754251881Speter compatibility */ 1755251881Speter 1756251881Speter if (prop_changes->nelts) 1757251881Speter { 1758251881Speter SVN_ERR(eb->processor->dir_changed(db->relpath, 1759251881Speter db->left_src, 1760251881Speter db->right_src, 1761251881Speter repos_props, 1762251881Speter local_props, 1763251881Speter prop_changes, 1764251881Speter db->pdb, 1765251881Speter eb->processor, 1766251881Speter scratch_pool)); 1767251881Speter reported_closed = TRUE; 1768251881Speter } 1769251881Speter } 1770251881Speter } 1771251881Speter 1772251881Speter /* Mark this directory as compared in the parent directory's baton, 1773251881Speter unless this is the root of the comparison. */ 1774251881Speter if (!reported_closed && !db->skip) 1775251881Speter SVN_ERR(eb->processor->dir_closed(db->relpath, 1776251881Speter db->left_src, 1777251881Speter db->right_src, 1778251881Speter db->pdb, 1779251881Speter eb->processor, 1780251881Speter scratch_pool)); 1781251881Speter 1782251881Speter if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry) 1783251881Speter SVN_ERR(handle_local_only(pb, db->name, scratch_pool)); 1784251881Speter 1785251881Speter SVN_ERR(maybe_done(db)); /* destroys scratch_pool */ 1786251881Speter 1787251881Speter return SVN_NO_ERROR; 1788251881Speter} 1789251881Speter 1790251881Speter/* An svn_delta_editor_t function. */ 1791251881Speterstatic svn_error_t * 1792251881Speteradd_file(const char *path, 1793251881Speter void *parent_baton, 1794251881Speter const char *copyfrom_path, 1795251881Speter svn_revnum_t copyfrom_revision, 1796251881Speter apr_pool_t *file_pool, 1797251881Speter void **file_baton) 1798251881Speter{ 1799251881Speter struct dir_baton_t *pb = parent_baton; 1800251881Speter struct edit_baton_t *eb = pb->eb; 1801251881Speter struct file_baton_t *fb; 1802251881Speter 1803251881Speter fb = make_file_baton(path, TRUE, pb, file_pool); 1804251881Speter *file_baton = fb; 1805251881Speter 1806251881Speter if (pb->skip_children) 1807251881Speter { 1808251881Speter fb->skip = TRUE; 1809251881Speter return SVN_NO_ERROR; 1810251881Speter } 1811251881Speter else if (pb->repos_only || !eb->ignore_ancestry) 1812251881Speter fb->repos_only = TRUE; 1813251881Speter else 1814251881Speter { 1815251881Speter struct svn_wc__db_info_t *info; 1816251881Speter SVN_ERR(ensure_local_info(pb, file_pool)); 1817251881Speter 1818251881Speter info = svn_hash_gets(pb->local_info, fb->name); 1819251881Speter 1820251881Speter if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status)) 1821251881Speter fb->repos_only = TRUE; 1822251881Speter 1823251881Speter if (!fb->repos_only && info->status != svn_wc__db_status_added) 1824251881Speter fb->repos_only = TRUE; 1825251881Speter 1826251881Speter if (!fb->repos_only) 1827251881Speter { 1828251881Speter /* Add this path to the parent directory's list of elements that 1829251881Speter have been compared. */ 1830251881Speter fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool); 1831251881Speter fb->ignoring_ancestry = TRUE; 1832251881Speter 1833251881Speter svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), ""); 1834251881Speter } 1835251881Speter } 1836251881Speter 1837251881Speter fb->left_src = svn_diff__source_create(eb->revnum, fb->pool); 1838251881Speter 1839251881Speter SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip, 1840251881Speter fb->relpath, 1841251881Speter fb->left_src, 1842251881Speter fb->right_src, 1843251881Speter NULL /* copyfrom src */, 1844251881Speter pb->pdb, 1845251881Speter eb->processor, 1846251881Speter fb->pool, fb->pool)); 1847251881Speter 1848251881Speter return SVN_NO_ERROR; 1849251881Speter} 1850251881Speter 1851251881Speter/* An svn_delta_editor_t function. */ 1852251881Speterstatic svn_error_t * 1853251881Speteropen_file(const char *path, 1854251881Speter void *parent_baton, 1855251881Speter svn_revnum_t base_revision, 1856251881Speter apr_pool_t *file_pool, 1857251881Speter void **file_baton) 1858251881Speter{ 1859251881Speter struct dir_baton_t *pb = parent_baton; 1860251881Speter struct edit_baton_t *eb = pb->eb; 1861251881Speter struct file_baton_t *fb; 1862251881Speter 1863251881Speter fb = make_file_baton(path, FALSE, pb, file_pool); 1864251881Speter *file_baton = fb; 1865251881Speter 1866251881Speter if (pb->skip_children) 1867251881Speter fb->skip = TRUE; 1868251881Speter else if (pb->repos_only) 1869251881Speter fb->repos_only = TRUE; 1870251881Speter else 1871251881Speter { 1872251881Speter struct svn_wc__db_info_t *info; 1873251881Speter SVN_ERR(ensure_local_info(pb, file_pool)); 1874251881Speter 1875251881Speter info = svn_hash_gets(pb->local_info, fb->name); 1876251881Speter 1877251881Speter if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status)) 1878251881Speter fb->repos_only = TRUE; 1879251881Speter 1880251881Speter if (!fb->repos_only) 1881251881Speter switch (info->status) 1882251881Speter { 1883251881Speter case svn_wc__db_status_normal: 1884251881Speter break; 1885251881Speter case svn_wc__db_status_deleted: 1886251881Speter fb->repos_only = TRUE; 1887251881Speter if (!info->have_more_work) 1888251881Speter svn_hash_sets(pb->compared, 1889251881Speter apr_pstrdup(pb->pool, fb->name), ""); 1890251881Speter break; 1891251881Speter case svn_wc__db_status_added: 1892251881Speter if (eb->ignore_ancestry) 1893251881Speter fb->ignoring_ancestry = TRUE; 1894251881Speter else 1895251881Speter fb->repos_only = TRUE; 1896251881Speter break; 1897251881Speter default: 1898251881Speter SVN_ERR_MALFUNCTION(); 1899251881Speter } 1900251881Speter 1901251881Speter if (!fb->repos_only) 1902251881Speter { 1903251881Speter /* Add this path to the parent directory's list of elements that 1904251881Speter have been compared. */ 1905251881Speter fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool); 1906251881Speter svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), ""); 1907251881Speter } 1908251881Speter } 1909251881Speter 1910251881Speter fb->left_src = svn_diff__source_create(eb->revnum, fb->pool); 1911251881Speter 1912251881Speter SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1913251881Speter NULL, NULL, NULL, &fb->base_checksum, NULL, 1914251881Speter NULL, NULL, &fb->base_props, NULL, 1915251881Speter eb->db, fb->local_abspath, 1916251881Speter fb->pool, fb->pool)); 1917251881Speter 1918251881Speter SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip, 1919251881Speter fb->relpath, 1920251881Speter fb->left_src, 1921251881Speter fb->right_src, 1922251881Speter NULL /* copyfrom src */, 1923251881Speter pb->pdb, 1924251881Speter eb->processor, 1925251881Speter fb->pool, fb->pool)); 1926251881Speter 1927251881Speter return SVN_NO_ERROR; 1928251881Speter} 1929251881Speter 1930251881Speter/* An svn_delta_editor_t function. */ 1931251881Speterstatic svn_error_t * 1932251881Speterapply_textdelta(void *file_baton, 1933251881Speter const char *base_checksum_hex, 1934251881Speter apr_pool_t *pool, 1935251881Speter svn_txdelta_window_handler_t *handler, 1936251881Speter void **handler_baton) 1937251881Speter{ 1938251881Speter struct file_baton_t *fb = file_baton; 1939251881Speter struct edit_baton_t *eb = fb->eb; 1940251881Speter svn_stream_t *source; 1941251881Speter svn_stream_t *temp_stream; 1942251881Speter svn_checksum_t *repos_checksum = NULL; 1943251881Speter 1944251881Speter if (fb->skip) 1945251881Speter { 1946251881Speter *handler = svn_delta_noop_window_handler; 1947251881Speter *handler_baton = NULL; 1948251881Speter return SVN_NO_ERROR; 1949251881Speter } 1950251881Speter 1951251881Speter if (base_checksum_hex && fb->base_checksum) 1952251881Speter { 1953251881Speter const svn_checksum_t *base_md5; 1954251881Speter SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5, 1955251881Speter base_checksum_hex, pool)); 1956251881Speter 1957251881Speter SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5, 1958251881Speter eb->db, eb->anchor_abspath, 1959251881Speter fb->base_checksum, 1960251881Speter pool, pool)); 1961251881Speter 1962251881Speter if (! svn_checksum_match(repos_checksum, base_md5)) 1963251881Speter { 1964251881Speter /* ### I expect that there are some bad drivers out there 1965251881Speter ### that used to give bad results. We could look in 1966251881Speter ### working to see if the expected checksum matches and 1967251881Speter ### then return the pristine of that... But that only moves 1968251881Speter ### the problem */ 1969251881Speter 1970251881Speter /* If needed: compare checksum obtained via md5 of working. 1971251881Speter And if they match set fb->base_checksum and fb->base_props */ 1972251881Speter 1973251881Speter return svn_checksum_mismatch_err( 1974251881Speter base_md5, 1975251881Speter repos_checksum, 1976251881Speter pool, 1977251881Speter _("Checksum mismatch for '%s'"), 1978251881Speter svn_dirent_local_style(fb->local_abspath, 1979251881Speter pool)); 1980251881Speter } 1981251881Speter 1982251881Speter SVN_ERR(svn_wc__db_pristine_read(&source, NULL, 1983251881Speter eb->db, fb->local_abspath, 1984251881Speter fb->base_checksum, 1985251881Speter pool, pool)); 1986251881Speter } 1987251881Speter else if (fb->base_checksum) 1988251881Speter { 1989251881Speter SVN_ERR(svn_wc__db_pristine_read(&source, NULL, 1990251881Speter eb->db, fb->local_abspath, 1991251881Speter fb->base_checksum, 1992251881Speter pool, pool)); 1993251881Speter } 1994251881Speter else 1995251881Speter source = svn_stream_empty(pool); 1996251881Speter 1997251881Speter /* This is the file that will contain the pristine repository version. */ 1998251881Speter SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL, 1999251881Speter svn_io_file_del_on_pool_cleanup, 2000251881Speter fb->pool, fb->pool)); 2001251881Speter 2002251881Speter svn_txdelta_apply(source, temp_stream, 2003251881Speter fb->result_digest, 2004251881Speter fb->local_abspath /* error_info */, 2005251881Speter fb->pool, 2006251881Speter handler, handler_baton); 2007251881Speter 2008251881Speter return SVN_NO_ERROR; 2009251881Speter} 2010251881Speter 2011251881Speter/* An svn_delta_editor_t function. When the file is closed we have a temporary 2012251881Speter * file containing a pristine version of the repository file. This can 2013251881Speter * be compared against the working copy. 2014251881Speter * 2015251881Speter * Ignore TEXT_CHECKSUM. 2016251881Speter */ 2017251881Speterstatic svn_error_t * 2018251881Speterclose_file(void *file_baton, 2019251881Speter const char *expected_md5_digest, 2020251881Speter apr_pool_t *pool) 2021251881Speter{ 2022251881Speter struct file_baton_t *fb = file_baton; 2023251881Speter struct dir_baton_t *pb = fb->parent_baton; 2024251881Speter struct edit_baton_t *eb = fb->eb; 2025251881Speter apr_pool_t *scratch_pool = fb->pool; 2026251881Speter 2027251881Speter /* The repository information; constructed from BASE + Changes */ 2028251881Speter const char *repos_file; 2029251881Speter apr_hash_t *repos_props; 2030251881Speter 2031251881Speter if (!fb->skip && expected_md5_digest != NULL) 2032251881Speter { 2033251881Speter svn_checksum_t *expected_checksum; 2034251881Speter const svn_checksum_t *result_checksum; 2035251881Speter 2036251881Speter if (fb->temp_file_path) 2037251881Speter result_checksum = svn_checksum__from_digest_md5(fb->result_digest, 2038251881Speter scratch_pool); 2039251881Speter else 2040251881Speter result_checksum = fb->base_checksum; 2041251881Speter 2042251881Speter SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5, 2043251881Speter expected_md5_digest, scratch_pool)); 2044251881Speter 2045251881Speter if (result_checksum->kind != svn_checksum_md5) 2046251881Speter SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum, 2047251881Speter eb->db, fb->local_abspath, 2048251881Speter result_checksum, 2049251881Speter scratch_pool, scratch_pool)); 2050251881Speter 2051251881Speter if (!svn_checksum_match(expected_checksum, result_checksum)) 2052251881Speter return svn_checksum_mismatch_err( 2053251881Speter expected_checksum, 2054251881Speter result_checksum, 2055251881Speter pool, 2056251881Speter _("Checksum mismatch for '%s'"), 2057251881Speter svn_dirent_local_style(fb->local_abspath, 2058251881Speter scratch_pool)); 2059251881Speter } 2060251881Speter 2061251881Speter if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry) 2062251881Speter SVN_ERR(handle_local_only(pb, fb->name, scratch_pool)); 2063251881Speter 2064251881Speter { 2065251881Speter apr_hash_t *prop_base; 2066251881Speter 2067251881Speter if (fb->added) 2068251881Speter prop_base = apr_hash_make(scratch_pool); 2069251881Speter else 2070251881Speter prop_base = fb->base_props; 2071251881Speter 2072251881Speter /* includes entry props */ 2073251881Speter repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool); 2074251881Speter 2075251881Speter repos_file = fb->temp_file_path; 2076251881Speter if (! repos_file) 2077251881Speter { 2078251881Speter assert(fb->base_checksum); 2079251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&repos_file, 2080251881Speter eb->db, eb->anchor_abspath, 2081251881Speter fb->base_checksum, 2082251881Speter scratch_pool, scratch_pool)); 2083251881Speter } 2084251881Speter } 2085251881Speter 2086251881Speter if (fb->skip) 2087251881Speter { 2088251881Speter /* Diff processor requested skipping information */ 2089251881Speter } 2090251881Speter else if (fb->repos_only) 2091251881Speter { 2092251881Speter SVN_ERR(eb->processor->file_deleted(fb->relpath, 2093251881Speter fb->left_src, 2094251881Speter fb->temp_file_path, 2095251881Speter repos_props, 2096251881Speter fb->pfb, 2097251881Speter eb->processor, 2098251881Speter scratch_pool)); 2099251881Speter } 2100251881Speter else 2101251881Speter { 2102251881Speter /* Produce a diff of actual or pristine against repos */ 2103251881Speter apr_hash_t *local_props; 2104251881Speter apr_array_header_t *prop_changes; 2105251881Speter const char *localfile; 2106251881Speter 2107251881Speter /* pb->local_info contains some information that might allow optimizing 2108251881Speter this a bit */ 2109251881Speter 2110251881Speter if (eb->diff_pristine) 2111251881Speter { 2112251881Speter const svn_checksum_t *checksum; 2113251881Speter SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL, 2114251881Speter NULL, &checksum, NULL, NULL, 2115251881Speter &local_props, 2116251881Speter eb->db, fb->local_abspath, 2117251881Speter scratch_pool, scratch_pool)); 2118251881Speter assert(checksum); 2119251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&localfile, 2120251881Speter eb->db, eb->anchor_abspath, 2121251881Speter checksum, 2122251881Speter scratch_pool, scratch_pool)); 2123251881Speter } 2124251881Speter else 2125251881Speter { 2126251881Speter SVN_ERR(svn_wc__db_read_props(&local_props, 2127251881Speter eb->db, fb->local_abspath, 2128251881Speter scratch_pool, scratch_pool)); 2129251881Speter 2130251881Speter /* a detranslated version of the working file */ 2131251881Speter SVN_ERR(svn_wc__internal_translated_file( 2132251881Speter &localfile, fb->local_abspath, eb->db, fb->local_abspath, 2133251881Speter SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP, 2134251881Speter eb->cancel_func, eb->cancel_baton, 2135251881Speter scratch_pool, scratch_pool)); 2136251881Speter } 2137251881Speter 2138251881Speter SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props, 2139251881Speter scratch_pool)); 2140251881Speter 2141251881Speter 2142251881Speter /* ### as a good diff processor we should now only report changes, and 2143251881Speter report file_closed() in other cases */ 2144251881Speter SVN_ERR(eb->processor->file_changed(fb->relpath, 2145251881Speter fb->left_src, 2146251881Speter fb->right_src, 2147251881Speter repos_file /* left file */, 2148251881Speter localfile /* right file */, 2149251881Speter repos_props /* left_props */, 2150251881Speter local_props /* right props */, 2151251881Speter TRUE /* ### file_modified */, 2152251881Speter prop_changes, 2153251881Speter fb->pfb, 2154251881Speter eb->processor, 2155251881Speter scratch_pool)); 2156251881Speter } 2157251881Speter 2158251881Speter if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry) 2159251881Speter SVN_ERR(handle_local_only(pb, fb->name, scratch_pool)); 2160251881Speter 2161251881Speter svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */ 2162251881Speter SVN_ERR(maybe_done(pb)); 2163251881Speter return SVN_NO_ERROR; 2164251881Speter} 2165251881Speter 2166251881Speter 2167251881Speter/* An svn_delta_editor_t function. */ 2168251881Speterstatic svn_error_t * 2169251881Speterchange_file_prop(void *file_baton, 2170251881Speter const char *name, 2171251881Speter const svn_string_t *value, 2172251881Speter apr_pool_t *pool) 2173251881Speter{ 2174251881Speter struct file_baton_t *fb = file_baton; 2175251881Speter svn_prop_t *propchange; 2176251881Speter svn_prop_kind_t propkind; 2177251881Speter 2178251881Speter propkind = svn_property_kind2(name); 2179251881Speter if (propkind == svn_prop_wc_kind) 2180251881Speter return SVN_NO_ERROR; 2181251881Speter else if (propkind == svn_prop_regular_kind) 2182251881Speter fb->has_propchange = TRUE; 2183251881Speter 2184251881Speter propchange = apr_array_push(fb->propchanges); 2185251881Speter propchange->name = apr_pstrdup(fb->pool, name); 2186251881Speter propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; 2187251881Speter 2188251881Speter return SVN_NO_ERROR; 2189251881Speter} 2190251881Speter 2191251881Speter 2192251881Speter/* An svn_delta_editor_t function. */ 2193251881Speterstatic svn_error_t * 2194251881Speterchange_dir_prop(void *dir_baton, 2195251881Speter const char *name, 2196251881Speter const svn_string_t *value, 2197251881Speter apr_pool_t *pool) 2198251881Speter{ 2199251881Speter struct dir_baton_t *db = dir_baton; 2200251881Speter svn_prop_t *propchange; 2201251881Speter svn_prop_kind_t propkind; 2202251881Speter 2203251881Speter propkind = svn_property_kind2(name); 2204251881Speter if (propkind == svn_prop_wc_kind) 2205251881Speter return SVN_NO_ERROR; 2206251881Speter else if (propkind == svn_prop_regular_kind) 2207251881Speter db->has_propchange = TRUE; 2208251881Speter 2209251881Speter propchange = apr_array_push(db->propchanges); 2210251881Speter propchange->name = apr_pstrdup(db->pool, name); 2211251881Speter propchange->value = value ? svn_string_dup(value, db->pool) : NULL; 2212251881Speter 2213251881Speter return SVN_NO_ERROR; 2214251881Speter} 2215251881Speter 2216251881Speter 2217251881Speter/* An svn_delta_editor_t function. */ 2218251881Speterstatic svn_error_t * 2219251881Speterclose_edit(void *edit_baton, 2220251881Speter apr_pool_t *pool) 2221251881Speter{ 2222251881Speter struct edit_baton_t *eb = edit_baton; 2223251881Speter 2224251881Speter if (!eb->root_opened) 2225251881Speter { 2226251881Speter SVN_ERR(walk_local_nodes_diff(eb, 2227251881Speter eb->anchor_abspath, 2228251881Speter "", 2229251881Speter eb->depth, 2230251881Speter NULL /* compared */, 2231251881Speter NULL /* No parent_baton */, 2232251881Speter eb->pool)); 2233251881Speter } 2234251881Speter 2235251881Speter return SVN_NO_ERROR; 2236251881Speter} 2237251881Speter 2238251881Speter/* Public Interface */ 2239251881Speter 2240251881Speter 2241251881Speter/* Create a diff editor and baton. */ 2242251881Spetersvn_error_t * 2243251881Spetersvn_wc__get_diff_editor(const svn_delta_editor_t **editor, 2244251881Speter void **edit_baton, 2245251881Speter svn_wc_context_t *wc_ctx, 2246251881Speter const char *anchor_abspath, 2247251881Speter const char *target, 2248251881Speter svn_depth_t depth, 2249251881Speter svn_boolean_t ignore_ancestry, 2250251881Speter svn_boolean_t show_copies_as_adds, 2251251881Speter svn_boolean_t use_git_diff_format, 2252251881Speter svn_boolean_t use_text_base, 2253251881Speter svn_boolean_t reverse_order, 2254251881Speter svn_boolean_t server_performs_filtering, 2255251881Speter const apr_array_header_t *changelist_filter, 2256251881Speter const svn_wc_diff_callbacks4_t *callbacks, 2257251881Speter void *callback_baton, 2258251881Speter svn_cancel_func_t cancel_func, 2259251881Speter void *cancel_baton, 2260251881Speter apr_pool_t *result_pool, 2261251881Speter apr_pool_t *scratch_pool) 2262251881Speter{ 2263251881Speter struct edit_baton_t *eb; 2264251881Speter void *inner_baton; 2265251881Speter svn_delta_editor_t *tree_editor; 2266251881Speter const svn_delta_editor_t *inner_editor; 2267251881Speter struct svn_wc__shim_fetch_baton_t *sfb; 2268251881Speter svn_delta_shim_callbacks_t *shim_callbacks = 2269251881Speter svn_delta_shim_callbacks_default(result_pool); 2270251881Speter 2271251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath)); 2272251881Speter 2273251881Speter /* --git implies --show-copies-as-adds */ 2274251881Speter if (use_git_diff_format) 2275251881Speter show_copies_as_adds = TRUE; 2276251881Speter 2277251881Speter SVN_ERR(make_edit_baton(&eb, 2278251881Speter wc_ctx->db, 2279251881Speter anchor_abspath, target, 2280251881Speter callbacks, callback_baton, 2281251881Speter depth, ignore_ancestry, show_copies_as_adds, 2282251881Speter use_text_base, reverse_order, changelist_filter, 2283251881Speter cancel_func, cancel_baton, 2284251881Speter result_pool)); 2285251881Speter 2286251881Speter tree_editor = svn_delta_default_editor(eb->pool); 2287251881Speter 2288251881Speter tree_editor->set_target_revision = set_target_revision; 2289251881Speter tree_editor->open_root = open_root; 2290251881Speter tree_editor->delete_entry = delete_entry; 2291251881Speter tree_editor->add_directory = add_directory; 2292251881Speter tree_editor->open_directory = open_directory; 2293251881Speter tree_editor->close_directory = close_directory; 2294251881Speter tree_editor->add_file = add_file; 2295251881Speter tree_editor->open_file = open_file; 2296251881Speter tree_editor->apply_textdelta = apply_textdelta; 2297251881Speter tree_editor->change_file_prop = change_file_prop; 2298251881Speter tree_editor->change_dir_prop = change_dir_prop; 2299251881Speter tree_editor->close_file = close_file; 2300251881Speter tree_editor->close_edit = close_edit; 2301251881Speter 2302251881Speter inner_editor = tree_editor; 2303251881Speter inner_baton = eb; 2304251881Speter 2305251881Speter if (!server_performs_filtering 2306251881Speter && depth == svn_depth_unknown) 2307251881Speter SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor, 2308251881Speter &inner_baton, 2309251881Speter wc_ctx->db, 2310251881Speter anchor_abspath, 2311251881Speter target, 2312251881Speter inner_editor, 2313251881Speter inner_baton, 2314251881Speter result_pool)); 2315251881Speter 2316251881Speter SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, 2317251881Speter cancel_baton, 2318251881Speter inner_editor, 2319251881Speter inner_baton, 2320251881Speter editor, 2321251881Speter edit_baton, 2322251881Speter result_pool)); 2323251881Speter 2324251881Speter sfb = apr_palloc(result_pool, sizeof(*sfb)); 2325251881Speter sfb->db = wc_ctx->db; 2326251881Speter sfb->base_abspath = eb->anchor_abspath; 2327251881Speter sfb->fetch_base = TRUE; 2328251881Speter 2329251881Speter shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func; 2330251881Speter shim_callbacks->fetch_props_func = svn_wc__fetch_props_func; 2331251881Speter shim_callbacks->fetch_base_func = svn_wc__fetch_base_func; 2332251881Speter shim_callbacks->fetch_baton = sfb; 2333251881Speter 2334251881Speter 2335251881Speter SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, 2336251881Speter NULL, NULL, shim_callbacks, 2337251881Speter result_pool, scratch_pool)); 2338251881Speter 2339251881Speter return SVN_NO_ERROR; 2340251881Speter} 2341251881Speter 2342251881Speter/* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */ 2343251881Speter 2344251881Speter/* baton for the svn_diff_tree_processor_t wrapper */ 2345251881Spetertypedef struct wc_diff_wrap_baton_t 2346251881Speter{ 2347251881Speter const svn_wc_diff_callbacks4_t *callbacks; 2348251881Speter void *callback_baton; 2349251881Speter 2350251881Speter svn_boolean_t walk_deleted_dirs; 2351251881Speter 2352251881Speter apr_pool_t *result_pool; 2353251881Speter const char *empty_file; 2354251881Speter 2355251881Speter} wc_diff_wrap_baton_t; 2356251881Speter 2357251881Speterstatic svn_error_t * 2358251881Speterwrap_ensure_empty_file(wc_diff_wrap_baton_t *wb, 2359251881Speter apr_pool_t *scratch_pool) 2360251881Speter{ 2361251881Speter if (wb->empty_file) 2362251881Speter return SVN_NO_ERROR; 2363251881Speter 2364251881Speter /* Create a unique file in the tempdir */ 2365251881Speter SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL, 2366251881Speter svn_io_file_del_on_pool_cleanup, 2367251881Speter wb->result_pool, scratch_pool)); 2368251881Speter 2369251881Speter return SVN_NO_ERROR; 2370251881Speter} 2371251881Speter 2372251881Speter/* svn_diff_tree_processor_t function */ 2373251881Speterstatic svn_error_t * 2374251881Speterwrap_dir_opened(void **new_dir_baton, 2375251881Speter svn_boolean_t *skip, 2376251881Speter svn_boolean_t *skip_children, 2377251881Speter const char *relpath, 2378251881Speter const svn_diff_source_t *left_source, 2379251881Speter const svn_diff_source_t *right_source, 2380251881Speter const svn_diff_source_t *copyfrom_source, 2381251881Speter void *parent_dir_baton, 2382251881Speter const svn_diff_tree_processor_t *processor, 2383251881Speter apr_pool_t *result_pool, 2384251881Speter apr_pool_t *scratch_pool) 2385251881Speter{ 2386251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2387251881Speter svn_boolean_t tree_conflicted = FALSE; 2388251881Speter 2389251881Speter assert(left_source || right_source); 2390251881Speter assert(!copyfrom_source || !right_source); 2391251881Speter 2392251881Speter /* Maybe store state and tree_conflicted in baton? */ 2393251881Speter if (left_source != NULL) 2394251881Speter { 2395251881Speter /* Open for change or delete */ 2396251881Speter SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children, 2397251881Speter relpath, 2398251881Speter right_source 2399251881Speter ? right_source->revision 2400251881Speter : (left_source 2401251881Speter ? left_source->revision 2402251881Speter : SVN_INVALID_REVNUM), 2403251881Speter wb->callback_baton, 2404251881Speter scratch_pool)); 2405251881Speter 2406251881Speter if (! right_source && !wb->walk_deleted_dirs) 2407251881Speter *skip_children = TRUE; 2408251881Speter } 2409251881Speter else /* left_source == NULL -> Add */ 2410251881Speter { 2411251881Speter svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2412251881Speter SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted, 2413251881Speter skip, skip_children, 2414251881Speter relpath, 2415251881Speter right_source->revision, 2416251881Speter copyfrom_source 2417251881Speter ? copyfrom_source->repos_relpath 2418251881Speter : NULL, 2419251881Speter copyfrom_source 2420251881Speter ? copyfrom_source->revision 2421251881Speter : SVN_INVALID_REVNUM, 2422251881Speter wb->callback_baton, 2423251881Speter scratch_pool)); 2424251881Speter } 2425251881Speter 2426251881Speter *new_dir_baton = NULL; 2427251881Speter 2428251881Speter return SVN_NO_ERROR; 2429251881Speter} 2430251881Speter 2431251881Speter/* svn_diff_tree_processor_t function */ 2432251881Speterstatic svn_error_t * 2433251881Speterwrap_dir_added(const char *relpath, 2434251881Speter const svn_diff_source_t *right_source, 2435251881Speter const svn_diff_source_t *copyfrom_source, 2436251881Speter /*const*/ apr_hash_t *copyfrom_props, 2437251881Speter /*const*/ apr_hash_t *right_props, 2438251881Speter void *dir_baton, 2439251881Speter const svn_diff_tree_processor_t *processor, 2440251881Speter apr_pool_t *scratch_pool) 2441251881Speter{ 2442251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2443251881Speter svn_boolean_t tree_conflicted = FALSE; 2444251881Speter svn_wc_notify_state_t state = svn_wc_notify_state_unknown; 2445251881Speter svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown; 2446251881Speter apr_hash_t *pristine_props = copyfrom_props; 2447251881Speter apr_array_header_t *prop_changes = NULL; 2448251881Speter 2449251881Speter if (right_props && apr_hash_count(right_props)) 2450251881Speter { 2451251881Speter if (!pristine_props) 2452251881Speter pristine_props = apr_hash_make(scratch_pool); 2453251881Speter 2454251881Speter SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props, 2455251881Speter scratch_pool)); 2456251881Speter 2457251881Speter SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, 2458251881Speter &tree_conflicted, 2459251881Speter relpath, 2460251881Speter TRUE /* dir_was_added */, 2461251881Speter prop_changes, pristine_props, 2462251881Speter wb->callback_baton, 2463251881Speter scratch_pool)); 2464251881Speter } 2465251881Speter 2466251881Speter SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state, 2467251881Speter &tree_conflicted, 2468251881Speter relpath, 2469251881Speter TRUE /* dir_was_added */, 2470251881Speter wb->callback_baton, 2471251881Speter scratch_pool)); 2472251881Speter return SVN_NO_ERROR; 2473251881Speter} 2474251881Speter 2475251881Speter/* svn_diff_tree_processor_t function */ 2476251881Speterstatic svn_error_t * 2477251881Speterwrap_dir_deleted(const char *relpath, 2478251881Speter const svn_diff_source_t *left_source, 2479251881Speter /*const*/ apr_hash_t *left_props, 2480251881Speter void *dir_baton, 2481251881Speter const svn_diff_tree_processor_t *processor, 2482251881Speter apr_pool_t *scratch_pool) 2483251881Speter{ 2484251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2485251881Speter svn_boolean_t tree_conflicted = FALSE; 2486251881Speter svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2487251881Speter 2488251881Speter SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted, 2489251881Speter relpath, 2490251881Speter wb->callback_baton, 2491251881Speter scratch_pool)); 2492251881Speter 2493251881Speter return SVN_NO_ERROR; 2494251881Speter} 2495251881Speter 2496251881Speter/* svn_diff_tree_processor_t function */ 2497251881Speterstatic svn_error_t * 2498251881Speterwrap_dir_closed(const char *relpath, 2499251881Speter const svn_diff_source_t *left_source, 2500251881Speter const svn_diff_source_t *right_source, 2501251881Speter void *dir_baton, 2502251881Speter const svn_diff_tree_processor_t *processor, 2503251881Speter apr_pool_t *scratch_pool) 2504251881Speter{ 2505251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2506251881Speter 2507251881Speter /* No previous implementations provided these arguments, so we 2508251881Speter are not providing them either */ 2509251881Speter SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL, 2510251881Speter relpath, 2511251881Speter FALSE /* added */, 2512251881Speter wb->callback_baton, 2513251881Speter scratch_pool)); 2514251881Speter 2515251881Speterreturn SVN_NO_ERROR; 2516251881Speter} 2517251881Speter 2518251881Speter/* svn_diff_tree_processor_t function */ 2519251881Speterstatic svn_error_t * 2520251881Speterwrap_dir_changed(const char *relpath, 2521251881Speter const svn_diff_source_t *left_source, 2522251881Speter const svn_diff_source_t *right_source, 2523251881Speter /*const*/ apr_hash_t *left_props, 2524251881Speter /*const*/ apr_hash_t *right_props, 2525251881Speter const apr_array_header_t *prop_changes, 2526251881Speter void *dir_baton, 2527251881Speter const struct svn_diff_tree_processor_t *processor, 2528251881Speter apr_pool_t *scratch_pool) 2529251881Speter{ 2530251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2531251881Speter svn_boolean_t tree_conflicted = FALSE; 2532251881Speter svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable; 2533251881Speter 2534251881Speter assert(left_source && right_source); 2535251881Speter 2536251881Speter SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted, 2537251881Speter relpath, 2538251881Speter FALSE /* dir_was_added */, 2539251881Speter prop_changes, 2540251881Speter left_props, 2541251881Speter wb->callback_baton, 2542251881Speter scratch_pool)); 2543251881Speter 2544251881Speter /* And call dir_closed, etc */ 2545251881Speter SVN_ERR(wrap_dir_closed(relpath, left_source, right_source, 2546251881Speter dir_baton, processor, 2547251881Speter scratch_pool)); 2548251881Speter return SVN_NO_ERROR; 2549251881Speter} 2550251881Speter 2551251881Speter/* svn_diff_tree_processor_t function */ 2552251881Speterstatic svn_error_t * 2553251881Speterwrap_file_opened(void **new_file_baton, 2554251881Speter svn_boolean_t *skip, 2555251881Speter const char *relpath, 2556251881Speter const svn_diff_source_t *left_source, 2557251881Speter const svn_diff_source_t *right_source, 2558251881Speter const svn_diff_source_t *copyfrom_source, 2559251881Speter void *dir_baton, 2560251881Speter const svn_diff_tree_processor_t *processor, 2561251881Speter apr_pool_t *result_pool, 2562251881Speter apr_pool_t *scratch_pool) 2563251881Speter{ 2564251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2565251881Speter svn_boolean_t tree_conflicted = FALSE; 2566251881Speter 2567251881Speter if (left_source) /* If ! added */ 2568251881Speter SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath, 2569251881Speter right_source 2570251881Speter ? right_source->revision 2571251881Speter : (left_source 2572251881Speter ? left_source->revision 2573251881Speter : SVN_INVALID_REVNUM), 2574251881Speter wb->callback_baton, scratch_pool)); 2575251881Speter 2576251881Speter /* No old implementation used the output arguments for notify */ 2577251881Speter 2578251881Speter *new_file_baton = NULL; 2579251881Speter return SVN_NO_ERROR; 2580251881Speter} 2581251881Speter 2582251881Speter/* svn_diff_tree_processor_t function */ 2583251881Speterstatic svn_error_t * 2584251881Speterwrap_file_added(const char *relpath, 2585251881Speter const svn_diff_source_t *copyfrom_source, 2586251881Speter const svn_diff_source_t *right_source, 2587251881Speter const char *copyfrom_file, 2588251881Speter const char *right_file, 2589251881Speter /*const*/ apr_hash_t *copyfrom_props, 2590251881Speter /*const*/ apr_hash_t *right_props, 2591251881Speter void *file_baton, 2592251881Speter const svn_diff_tree_processor_t *processor, 2593251881Speter apr_pool_t *scratch_pool) 2594251881Speter{ 2595251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2596251881Speter svn_boolean_t tree_conflicted = FALSE; 2597251881Speter svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2598251881Speter svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable; 2599251881Speter apr_array_header_t *prop_changes; 2600251881Speter 2601251881Speter if (! copyfrom_props) 2602251881Speter copyfrom_props = apr_hash_make(scratch_pool); 2603251881Speter 2604251881Speter SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props, 2605251881Speter scratch_pool)); 2606251881Speter 2607251881Speter if (! copyfrom_source) 2608251881Speter SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool)); 2609251881Speter 2610251881Speter SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted, 2611251881Speter relpath, 2612251881Speter copyfrom_source 2613251881Speter ? copyfrom_file 2614251881Speter : wb->empty_file, 2615251881Speter right_file, 2616251881Speter 0, 2617251881Speter right_source->revision, 2618251881Speter copyfrom_props 2619251881Speter ? svn_prop_get_value(copyfrom_props, 2620251881Speter SVN_PROP_MIME_TYPE) 2621251881Speter : NULL, 2622251881Speter right_props 2623251881Speter ? svn_prop_get_value(right_props, 2624251881Speter SVN_PROP_MIME_TYPE) 2625251881Speter : NULL, 2626251881Speter copyfrom_source 2627251881Speter ? copyfrom_source->repos_relpath 2628251881Speter : NULL, 2629251881Speter copyfrom_source 2630251881Speter ? copyfrom_source->revision 2631251881Speter : SVN_INVALID_REVNUM, 2632251881Speter prop_changes, copyfrom_props, 2633251881Speter wb->callback_baton, 2634251881Speter scratch_pool)); 2635251881Speter return SVN_NO_ERROR; 2636251881Speter} 2637251881Speter 2638251881Speterstatic svn_error_t * 2639251881Speterwrap_file_deleted(const char *relpath, 2640251881Speter const svn_diff_source_t *left_source, 2641251881Speter const char *left_file, 2642251881Speter apr_hash_t *left_props, 2643251881Speter void *file_baton, 2644251881Speter const svn_diff_tree_processor_t *processor, 2645251881Speter apr_pool_t *scratch_pool) 2646251881Speter{ 2647251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2648251881Speter svn_boolean_t tree_conflicted = FALSE; 2649251881Speter svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2650251881Speter 2651251881Speter SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool)); 2652251881Speter 2653251881Speter SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted, 2654251881Speter relpath, 2655251881Speter left_file, wb->empty_file, 2656251881Speter left_props 2657251881Speter ? svn_prop_get_value(left_props, 2658251881Speter SVN_PROP_MIME_TYPE) 2659251881Speter : NULL, 2660251881Speter NULL, 2661251881Speter left_props, 2662251881Speter wb->callback_baton, 2663251881Speter scratch_pool)); 2664251881Speter return SVN_NO_ERROR; 2665251881Speter} 2666251881Speter 2667251881Speter/* svn_diff_tree_processor_t function */ 2668251881Speterstatic svn_error_t * 2669251881Speterwrap_file_changed(const char *relpath, 2670251881Speter const svn_diff_source_t *left_source, 2671251881Speter const svn_diff_source_t *right_source, 2672251881Speter const char *left_file, 2673251881Speter const char *right_file, 2674251881Speter /*const*/ apr_hash_t *left_props, 2675251881Speter /*const*/ apr_hash_t *right_props, 2676251881Speter svn_boolean_t file_modified, 2677251881Speter const apr_array_header_t *prop_changes, 2678251881Speter void *file_baton, 2679251881Speter const svn_diff_tree_processor_t *processor, 2680251881Speter apr_pool_t *scratch_pool) 2681251881Speter{ 2682251881Speter wc_diff_wrap_baton_t *wb = processor->baton; 2683251881Speter svn_boolean_t tree_conflicted = FALSE; 2684251881Speter svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; 2685251881Speter svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable; 2686251881Speter 2687251881Speter SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool)); 2688251881Speter 2689251881Speter assert(left_source && right_source); 2690251881Speter 2691251881Speter SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted, 2692251881Speter relpath, 2693251881Speter file_modified ? left_file : NULL, 2694251881Speter file_modified ? right_file : NULL, 2695251881Speter left_source->revision, 2696251881Speter right_source->revision, 2697251881Speter left_props 2698251881Speter ? svn_prop_get_value(left_props, 2699251881Speter SVN_PROP_MIME_TYPE) 2700251881Speter : NULL, 2701251881Speter right_props 2702251881Speter ? svn_prop_get_value(right_props, 2703251881Speter SVN_PROP_MIME_TYPE) 2704251881Speter : NULL, 2705251881Speter prop_changes, 2706251881Speter left_props, 2707251881Speter wb->callback_baton, 2708251881Speter scratch_pool)); 2709251881Speter return SVN_NO_ERROR; 2710251881Speter} 2711251881Speter 2712251881Spetersvn_error_t * 2713251881Spetersvn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor, 2714251881Speter const svn_wc_diff_callbacks4_t *callbacks, 2715251881Speter void *callback_baton, 2716251881Speter svn_boolean_t walk_deleted_dirs, 2717251881Speter apr_pool_t *result_pool, 2718251881Speter apr_pool_t *scratch_pool) 2719251881Speter{ 2720251881Speter wc_diff_wrap_baton_t *wrap_baton; 2721251881Speter svn_diff_tree_processor_t *processor; 2722251881Speter 2723251881Speter wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton)); 2724251881Speter 2725251881Speter wrap_baton->result_pool = result_pool; 2726251881Speter wrap_baton->callbacks = callbacks; 2727251881Speter wrap_baton->callback_baton = callback_baton; 2728251881Speter wrap_baton->empty_file = NULL; 2729251881Speter wrap_baton->walk_deleted_dirs = walk_deleted_dirs; 2730251881Speter 2731251881Speter processor = svn_diff__tree_processor_create(wrap_baton, result_pool); 2732251881Speter 2733251881Speter processor->dir_opened = wrap_dir_opened; 2734251881Speter processor->dir_added = wrap_dir_added; 2735251881Speter processor->dir_deleted = wrap_dir_deleted; 2736251881Speter processor->dir_changed = wrap_dir_changed; 2737251881Speter processor->dir_closed = wrap_dir_closed; 2738251881Speter 2739251881Speter processor->file_opened = wrap_file_opened; 2740251881Speter processor->file_added = wrap_file_added; 2741251881Speter processor->file_deleted = wrap_file_deleted; 2742251881Speter processor->file_changed = wrap_file_changed; 2743251881Speter /*processor->file_closed = wrap_file_closed*/; /* Not needed */ 2744251881Speter 2745251881Speter *diff_processor = processor; 2746251881Speter return SVN_NO_ERROR; 2747251881Speter} 2748