1251881Speter/* 2251881Speter * repos_diff.c -- The diff editor for comparing two repository versions 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* This code uses an editor driven by a tree delta between two 25251881Speter * repository revisions (REV1 and REV2). For each file encountered in 26251881Speter * the delta the editor constructs two temporary files, one for each 27251881Speter * revision. This necessitates a separate request for the REV1 version 28251881Speter * of the file when the delta shows the file being modified or 29251881Speter * deleted. Files that are added by the delta do not require a 30251881Speter * separate request, the REV1 version is empty and the delta is 31251881Speter * sufficient to construct the REV2 version. When both versions of 32251881Speter * each file have been created the diff callback is invoked to display 33251881Speter * the difference between the two files. */ 34251881Speter 35251881Speter#include <apr_uri.h> 36251881Speter#include <apr_md5.h> 37251881Speter#include <assert.h> 38251881Speter 39251881Speter#include "svn_checksum.h" 40251881Speter#include "svn_hash.h" 41251881Speter#include "svn_wc.h" 42251881Speter#include "svn_pools.h" 43251881Speter#include "svn_dirent_uri.h" 44251881Speter#include "svn_path.h" 45251881Speter#include "svn_io.h" 46251881Speter#include "svn_props.h" 47251881Speter#include "svn_private_config.h" 48251881Speter 49251881Speter#include "client.h" 50251881Speter 51251881Speter#include "private/svn_subr_private.h" 52251881Speter#include "private/svn_wc_private.h" 53251881Speter#include "private/svn_editor.h" 54362181Sdim#include "private/svn_sorts_private.h" 55251881Speter 56251881Speter/* Overall crawler editor baton. */ 57251881Speterstruct edit_baton { 58251881Speter /* The passed depth */ 59251881Speter svn_depth_t depth; 60251881Speter 61251881Speter /* The result processor */ 62251881Speter const svn_diff_tree_processor_t *processor; 63251881Speter 64251881Speter /* RA_SESSION is the open session for making requests to the RA layer */ 65251881Speter svn_ra_session_t *ra_session; 66251881Speter 67251881Speter /* The rev1 from the '-r Rev1:Rev2' command line option */ 68251881Speter svn_revnum_t revision; 69251881Speter 70251881Speter /* The rev2 from the '-r Rev1:Rev2' option, specifically set by 71251881Speter set_target_revision(). */ 72251881Speter svn_revnum_t target_revision; 73251881Speter 74251881Speter /* The path to a temporary empty file used for add/delete 75251881Speter differences. The path is cached here so that it can be reused, 76251881Speter since all empty files are the same. */ 77251881Speter const char *empty_file; 78251881Speter 79251881Speter /* Empty hash used for adds. */ 80251881Speter apr_hash_t *empty_hash; 81251881Speter 82251881Speter /* Whether to report text deltas */ 83251881Speter svn_boolean_t text_deltas; 84251881Speter 85251881Speter /* A callback used to see if the client wishes to cancel the running 86251881Speter operation. */ 87251881Speter svn_cancel_func_t cancel_func; 88251881Speter 89251881Speter /* A baton to pass to the cancellation callback. */ 90251881Speter void *cancel_baton; 91251881Speter 92251881Speter apr_pool_t *pool; 93251881Speter}; 94251881Speter 95251881Spetertypedef struct deleted_path_notify_t 96251881Speter{ 97251881Speter svn_node_kind_t kind; 98251881Speter svn_wc_notify_action_t action; 99251881Speter svn_wc_notify_state_t state; 100251881Speter svn_boolean_t tree_conflicted; 101251881Speter} deleted_path_notify_t; 102251881Speter 103251881Speter/* Directory level baton. 104251881Speter */ 105251881Speterstruct dir_baton { 106251881Speter /* Gets set if the directory is added rather than replaced/unchanged. */ 107251881Speter svn_boolean_t added; 108251881Speter 109251881Speter /* Gets set if this operation caused a tree-conflict on this directory 110251881Speter * (does not show tree-conflicts persisting from before this operation). */ 111251881Speter svn_boolean_t tree_conflicted; 112251881Speter 113251881Speter /* If TRUE, this node is skipped entirely. 114251881Speter * This is used to skip all children of a tree-conflicted 115251881Speter * directory without setting TREE_CONFLICTED to TRUE everywhere. */ 116251881Speter svn_boolean_t skip; 117251881Speter 118251881Speter /* If TRUE, all children of this directory are skipped. */ 119251881Speter svn_boolean_t skip_children; 120251881Speter 121251881Speter /* The path of the directory within the repository */ 122251881Speter const char *path; 123251881Speter 124251881Speter /* The baton for the parent directory, or null if this is the root of the 125251881Speter hierarchy to be compared. */ 126251881Speter struct dir_baton *parent_baton; 127251881Speter 128251881Speter /* The overall crawler editor baton. */ 129251881Speter struct edit_baton *edit_baton; 130251881Speter 131251881Speter /* A cache of any property changes (svn_prop_t) received for this dir. */ 132251881Speter apr_array_header_t *propchanges; 133251881Speter 134251881Speter /* Boolean indicating whether a node property was changed */ 135251881Speter svn_boolean_t has_propchange; 136251881Speter 137251881Speter /* Baton for svn_diff_tree_processor_t */ 138251881Speter void *pdb; 139251881Speter svn_diff_source_t *left_source; 140251881Speter svn_diff_source_t *right_source; 141251881Speter 142251881Speter /* The pool passed in by add_dir, open_dir, or open_root. 143251881Speter Also, the pool this dir baton is allocated in. */ 144251881Speter apr_pool_t *pool; 145251881Speter 146251881Speter /* Base revision of directory. */ 147251881Speter svn_revnum_t base_revision; 148251881Speter 149251881Speter /* Number of users of baton. Its pool will be destroyed 0 */ 150251881Speter int users; 151251881Speter}; 152251881Speter 153251881Speter/* File level baton. 154251881Speter */ 155251881Speterstruct file_baton { 156251881Speter /* Reference to parent baton */ 157251881Speter struct dir_baton *parent_baton; 158251881Speter 159251881Speter /* Gets set if the file is added rather than replaced. */ 160251881Speter svn_boolean_t added; 161251881Speter 162251881Speter /* Gets set if this operation caused a tree-conflict on this file 163251881Speter * (does not show tree-conflicts persisting from before this operation). */ 164251881Speter svn_boolean_t tree_conflicted; 165251881Speter 166251881Speter /* If TRUE, this node is skipped entirely. 167251881Speter * This is currently used to skip all children of a tree-conflicted 168251881Speter * directory. */ 169251881Speter svn_boolean_t skip; 170251881Speter 171251881Speter /* The path of the file within the repository */ 172251881Speter const char *path; 173251881Speter 174251881Speter /* The path and APR file handle to the temporary file that contains the 175251881Speter first repository version. Also, the pristine-property list of 176251881Speter this file. */ 177251881Speter const char *path_start_revision; 178251881Speter apr_hash_t *pristine_props; 179251881Speter svn_revnum_t base_revision; 180251881Speter 181251881Speter /* The path and APR file handle to the temporary file that contains the 182251881Speter second repository version. These fields are set when processing 183251881Speter textdelta and file deletion, and will be NULL if there's no 184251881Speter textual difference between the two revisions. */ 185251881Speter const char *path_end_revision; 186251881Speter 187251881Speter /* APPLY_HANDLER/APPLY_BATON represent the delta application baton. */ 188251881Speter svn_txdelta_window_handler_t apply_handler; 189251881Speter void *apply_baton; 190251881Speter 191251881Speter /* The overall crawler editor baton. */ 192251881Speter struct edit_baton *edit_baton; 193251881Speter 194251881Speter /* Holds the checksum of the start revision file */ 195251881Speter svn_checksum_t *start_md5_checksum; 196251881Speter 197251881Speter /* Holds the resulting md5 digest of a textdelta transform */ 198251881Speter unsigned char result_digest[APR_MD5_DIGESTSIZE]; 199251881Speter svn_checksum_t *result_md5_checksum; 200251881Speter 201251881Speter /* A cache of any property changes (svn_prop_t) received for this file. */ 202251881Speter apr_array_header_t *propchanges; 203251881Speter 204251881Speter /* Boolean indicating whether a node property was changed */ 205251881Speter svn_boolean_t has_propchange; 206251881Speter 207251881Speter /* Baton for svn_diff_tree_processor_t */ 208251881Speter void *pfb; 209251881Speter svn_diff_source_t *left_source; 210251881Speter svn_diff_source_t *right_source; 211251881Speter 212251881Speter /* The pool passed in by add_file or open_file. 213251881Speter Also, the pool this file_baton is allocated in. */ 214251881Speter apr_pool_t *pool; 215251881Speter}; 216251881Speter 217251881Speter/* Create a new directory baton for PATH in POOL. ADDED is set if 218251881Speter * this directory is being added rather than replaced. PARENT_BATON is 219251881Speter * the baton of the parent directory (or NULL if this is the root of 220251881Speter * the comparison hierarchy). The directory and its parent may or may 221251881Speter * not exist in the working copy. EDIT_BATON is the overall crawler 222251881Speter * editor baton. 223251881Speter */ 224251881Speterstatic struct dir_baton * 225251881Spetermake_dir_baton(const char *path, 226251881Speter struct dir_baton *parent_baton, 227251881Speter struct edit_baton *edit_baton, 228251881Speter svn_boolean_t added, 229251881Speter svn_revnum_t base_revision, 230251881Speter apr_pool_t *result_pool) 231251881Speter{ 232251881Speter apr_pool_t *dir_pool = svn_pool_create(result_pool); 233251881Speter struct dir_baton *dir_baton = apr_pcalloc(dir_pool, sizeof(*dir_baton)); 234251881Speter 235251881Speter dir_baton->parent_baton = parent_baton; 236251881Speter dir_baton->edit_baton = edit_baton; 237251881Speter dir_baton->added = added; 238251881Speter dir_baton->tree_conflicted = FALSE; 239251881Speter dir_baton->skip = FALSE; 240251881Speter dir_baton->skip_children = FALSE; 241251881Speter dir_baton->pool = dir_pool; 242251881Speter dir_baton->path = apr_pstrdup(dir_pool, path); 243251881Speter dir_baton->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t)); 244251881Speter dir_baton->base_revision = base_revision; 245251881Speter dir_baton->users++; 246251881Speter 247251881Speter if (parent_baton) 248251881Speter parent_baton->users++; 249251881Speter 250251881Speter return dir_baton; 251251881Speter} 252251881Speter 253251881Speter/* New function. Called by everyone who has a reference when done */ 254251881Speterstatic svn_error_t * 255251881Speterrelease_dir(struct dir_baton *db) 256251881Speter{ 257251881Speter assert(db->users > 0); 258251881Speter 259251881Speter db->users--; 260251881Speter if (db->users) 261251881Speter return SVN_NO_ERROR; 262251881Speter 263251881Speter { 264251881Speter struct dir_baton *pb = db->parent_baton; 265251881Speter 266251881Speter svn_pool_destroy(db->pool); 267251881Speter 268251881Speter if (pb != NULL) 269251881Speter SVN_ERR(release_dir(pb)); 270251881Speter } 271251881Speter 272251881Speter return SVN_NO_ERROR; 273251881Speter} 274251881Speter 275251881Speter/* Create a new file baton for PATH in POOL, which is a child of 276251881Speter * directory PARENT_PATH. ADDED is set if this file is being added 277251881Speter * rather than replaced. EDIT_BATON is a pointer to the global edit 278251881Speter * baton. 279251881Speter */ 280251881Speterstatic struct file_baton * 281251881Spetermake_file_baton(const char *path, 282251881Speter struct dir_baton *parent_baton, 283251881Speter svn_boolean_t added, 284251881Speter apr_pool_t *result_pool) 285251881Speter{ 286251881Speter apr_pool_t *file_pool = svn_pool_create(result_pool); 287251881Speter struct file_baton *file_baton = apr_pcalloc(file_pool, sizeof(*file_baton)); 288251881Speter 289251881Speter file_baton->parent_baton = parent_baton; 290251881Speter file_baton->edit_baton = parent_baton->edit_baton; 291251881Speter file_baton->added = added; 292251881Speter file_baton->tree_conflicted = FALSE; 293251881Speter file_baton->skip = FALSE; 294251881Speter file_baton->pool = file_pool; 295251881Speter file_baton->path = apr_pstrdup(file_pool, path); 296251881Speter file_baton->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t)); 297251881Speter file_baton->base_revision = parent_baton->edit_baton->revision; 298251881Speter 299251881Speter parent_baton->users++; 300251881Speter 301251881Speter return file_baton; 302251881Speter} 303251881Speter 304251881Speter/* Get revision FB->base_revision of the file described by FB from the 305251881Speter * repository, through FB->edit_baton->ra_session. 306251881Speter * 307251881Speter * Unless PROPS_ONLY is true: 308251881Speter * Set FB->path_start_revision to the path of a new temporary file containing 309251881Speter * the file's text. 310251881Speter * Set FB->start_md5_checksum to that file's MD-5 checksum. 311251881Speter * Install a pool cleanup handler on FB->pool to delete the file. 312251881Speter * 313251881Speter * Always: 314251881Speter * Set FB->pristine_props to a new hash containing the file's properties. 315251881Speter * 316251881Speter * Allocate all results in FB->pool. 317251881Speter */ 318251881Speterstatic svn_error_t * 319251881Speterget_file_from_ra(struct file_baton *fb, 320251881Speter svn_boolean_t props_only, 321251881Speter apr_pool_t *scratch_pool) 322251881Speter{ 323251881Speter if (! props_only) 324251881Speter { 325251881Speter svn_stream_t *fstream; 326251881Speter 327251881Speter SVN_ERR(svn_stream_open_unique(&fstream, &(fb->path_start_revision), 328251881Speter NULL, svn_io_file_del_on_pool_cleanup, 329251881Speter fb->pool, scratch_pool)); 330251881Speter 331251881Speter fstream = svn_stream_checksummed2(fstream, NULL, &fb->start_md5_checksum, 332289180Speter svn_checksum_md5, TRUE, fb->pool); 333251881Speter 334251881Speter /* Retrieve the file and its properties */ 335251881Speter SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session, 336251881Speter fb->path, 337251881Speter fb->base_revision, 338251881Speter fstream, NULL, 339251881Speter &(fb->pristine_props), 340251881Speter fb->pool)); 341251881Speter SVN_ERR(svn_stream_close(fstream)); 342251881Speter } 343251881Speter else 344251881Speter { 345251881Speter SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session, 346251881Speter fb->path, 347251881Speter fb->base_revision, 348251881Speter NULL, NULL, 349251881Speter &(fb->pristine_props), 350251881Speter fb->pool)); 351251881Speter } 352251881Speter 353251881Speter return SVN_NO_ERROR; 354251881Speter} 355251881Speter 356251881Speter/* Remove every no-op property change from CHANGES: that is, remove every 357251881Speter entry in which the target value is the same as the value of the 358251881Speter corresponding property in PRISTINE_PROPS. 359251881Speter 360251881Speter Issue #3657 'dav update report handler in skelta mode can cause 361251881Speter spurious conflicts'. When communicating with the repository via ra_serf, 362251881Speter the change_dir_prop and change_file_prop svn_delta_editor_t 363251881Speter callbacks are called (obviously) when a directory or file property has 364251881Speter changed between the start and end of the edit. Less obvious however, 365251881Speter is that these callbacks may be made describing *all* of the properties 366251881Speter on FILE_BATON->PATH when using the DAV providers, not just the change(s). 367251881Speter (Specifically ra_serf does it for diff/merge/update/switch). 368251881Speter 369251881Speter This means that the change_[file|dir]_prop svn_delta_editor_t callbacks 370251881Speter may be made where there are no property changes (i.e. a noop change of 371251881Speter NAME from VALUE to VALUE). Normally this is harmless, but during a 372251881Speter merge it can result in spurious conflicts if the WC's pristine property 373251881Speter NAME has a value other than VALUE. In an ideal world the mod_dav_svn 374251881Speter update report handler, when in 'skelta' mode and describing changes to 375251881Speter a path on which a property has changed, wouldn't ask the client to later 376251881Speter fetch all properties and figure out what has changed itself. The server 377251881Speter already knows which properties have changed! 378251881Speter 379251881Speter Regardless, such a change is not yet implemented, and even when it is, 380251881Speter the client should DTRT with regard to older servers which behave this 381251881Speter way. Hence this little hack: We populate FILE_BATON->PROPCHANGES only 382251881Speter with *actual* property changes. 383251881Speter 384362181Sdim See https://issues.apache.org/jira/browse/SVN-3657#desc9 and 385251881Speter http://svn.haxx.se/dev/archive-2010-08/0351.shtml for more details. 386251881Speter */ 387362181Sdimstatic svn_error_t * 388251881Speterremove_non_prop_changes(apr_hash_t *pristine_props, 389251881Speter apr_array_header_t *changes) 390251881Speter{ 391251881Speter int i; 392251881Speter 393289180Speter /* For added nodes, there is nothing to filter. */ 394289180Speter if (apr_hash_count(pristine_props) == 0) 395362181Sdim return SVN_NO_ERROR; 396289180Speter 397251881Speter for (i = 0; i < changes->nelts; i++) 398251881Speter { 399251881Speter svn_prop_t *change = &APR_ARRAY_IDX(changes, i, svn_prop_t); 400251881Speter 401251881Speter if (change->value) 402251881Speter { 403251881Speter const svn_string_t *old_val = svn_hash_gets(pristine_props, 404251881Speter change->name); 405251881Speter 406251881Speter if (old_val && svn_string_compare(old_val, change->value)) 407251881Speter { 408362181Sdim /* Remove the matching change and re-check the current index */ 409362181Sdim SVN_ERR(svn_sort__array_delete2(changes, i, 1)); 410362181Sdim i--; 411251881Speter } 412251881Speter } 413251881Speter } 414362181Sdim return SVN_NO_ERROR; 415251881Speter} 416251881Speter 417251881Speter/* Get the empty file associated with the edit baton. This is cached so 418251881Speter * that it can be reused, all empty files are the same. 419251881Speter */ 420251881Speterstatic svn_error_t * 421251881Speterget_empty_file(struct edit_baton *eb, 422251881Speter const char **empty_file_path) 423251881Speter{ 424251881Speter /* Create the file if it does not exist */ 425251881Speter /* Note that we tried to use /dev/null in r857294, but 426251881Speter that won't work on Windows: it's impossible to stat NUL */ 427251881Speter if (!eb->empty_file) 428251881Speter SVN_ERR(svn_io_open_unique_file3(NULL, &(eb->empty_file), NULL, 429251881Speter svn_io_file_del_on_pool_cleanup, 430251881Speter eb->pool, eb->pool)); 431251881Speter 432251881Speter *empty_file_path = eb->empty_file; 433251881Speter 434251881Speter return SVN_NO_ERROR; 435251881Speter} 436251881Speter 437251881Speter/* An svn_delta_editor_t function. */ 438251881Speterstatic svn_error_t * 439251881Speterset_target_revision(void *edit_baton, 440251881Speter svn_revnum_t target_revision, 441251881Speter apr_pool_t *pool) 442251881Speter{ 443251881Speter struct edit_baton *eb = edit_baton; 444251881Speter 445251881Speter eb->target_revision = target_revision; 446251881Speter return SVN_NO_ERROR; 447251881Speter} 448251881Speter 449251881Speter/* An svn_delta_editor_t function. The root of the comparison hierarchy */ 450251881Speterstatic svn_error_t * 451251881Speteropen_root(void *edit_baton, 452251881Speter svn_revnum_t base_revision, 453251881Speter apr_pool_t *pool, 454251881Speter void **root_baton) 455251881Speter{ 456251881Speter struct edit_baton *eb = edit_baton; 457251881Speter struct dir_baton *db = make_dir_baton("", NULL, eb, FALSE, base_revision, 458251881Speter eb->pool); 459251881Speter 460251881Speter db->left_source = svn_diff__source_create(eb->revision, db->pool); 461251881Speter db->right_source = svn_diff__source_create(eb->target_revision, db->pool); 462251881Speter 463251881Speter SVN_ERR(eb->processor->dir_opened(&db->pdb, 464251881Speter &db->skip, 465251881Speter &db->skip_children, 466251881Speter "", 467251881Speter db->left_source, 468251881Speter db->right_source, 469251881Speter NULL, 470251881Speter NULL, 471251881Speter eb->processor, 472251881Speter db->pool, 473251881Speter db->pool /* scratch_pool */)); 474251881Speter 475251881Speter *root_baton = db; 476251881Speter return SVN_NO_ERROR; 477251881Speter} 478251881Speter 479251881Speter/* Compare a file being deleted against an empty file. 480251881Speter */ 481251881Speterstatic svn_error_t * 482251881Speterdiff_deleted_file(const char *path, 483251881Speter struct dir_baton *db, 484251881Speter apr_pool_t *scratch_pool) 485251881Speter{ 486251881Speter struct edit_baton *eb = db->edit_baton; 487251881Speter struct file_baton *fb = make_file_baton(path, db, FALSE, scratch_pool); 488251881Speter svn_boolean_t skip = FALSE; 489251881Speter svn_diff_source_t *left_source = svn_diff__source_create(eb->revision, 490251881Speter scratch_pool); 491251881Speter 492251881Speter if (eb->cancel_func) 493251881Speter SVN_ERR(eb->cancel_func(eb->cancel_baton)); 494251881Speter 495251881Speter SVN_ERR(eb->processor->file_opened(&fb->pfb, &skip, path, 496251881Speter left_source, 497251881Speter NULL /* right_source */, 498251881Speter NULL /* copyfrom_source */, 499251881Speter db->pdb, 500251881Speter eb->processor, 501251881Speter scratch_pool, scratch_pool)); 502251881Speter 503251881Speter if (eb->cancel_func) 504251881Speter SVN_ERR(eb->cancel_func(eb->cancel_baton)); 505251881Speter 506251881Speter if (skip) 507251881Speter return SVN_NO_ERROR; 508251881Speter 509251881Speter SVN_ERR(get_file_from_ra(fb, ! eb->text_deltas, scratch_pool)); 510251881Speter 511251881Speter SVN_ERR(eb->processor->file_deleted(fb->path, 512251881Speter left_source, 513251881Speter fb->path_start_revision, 514251881Speter fb->pristine_props, 515251881Speter fb->pfb, 516251881Speter eb->processor, 517251881Speter scratch_pool)); 518251881Speter 519251881Speter return SVN_NO_ERROR; 520251881Speter} 521251881Speter 522251881Speter/* Recursively walk tree rooted at DIR (at EB->revision) in the repository, 523251881Speter * reporting all children as deleted. Part of a workaround for issue 2333. 524251881Speter * 525251881Speter * DIR is a repository path relative to the URL in EB->ra_session. EB is 526251881Speter * the overall crawler editor baton. EB->revision must be a valid revision 527251881Speter * number, not SVN_INVALID_REVNUM. Use EB->cancel_func (if not null) with 528251881Speter * EB->cancel_baton for cancellation. 529251881Speter */ 530251881Speter/* ### TODO: Handle depth. */ 531251881Speterstatic svn_error_t * 532251881Speterdiff_deleted_dir(const char *path, 533251881Speter struct dir_baton *pb, 534251881Speter apr_pool_t *scratch_pool) 535251881Speter{ 536251881Speter struct edit_baton *eb = pb->edit_baton; 537251881Speter struct dir_baton *db; 538251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 539251881Speter svn_boolean_t skip = FALSE; 540251881Speter svn_boolean_t skip_children = FALSE; 541251881Speter apr_hash_t *dirents = NULL; 542251881Speter apr_hash_t *left_props = NULL; 543251881Speter svn_diff_source_t *left_source = svn_diff__source_create(eb->revision, 544251881Speter scratch_pool); 545251881Speter db = make_dir_baton(path, pb, pb->edit_baton, FALSE, SVN_INVALID_REVNUM, 546251881Speter scratch_pool); 547251881Speter 548251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(eb->revision)); 549251881Speter 550251881Speter if (eb->cancel_func) 551251881Speter SVN_ERR(eb->cancel_func(eb->cancel_baton)); 552251881Speter 553251881Speter SVN_ERR(eb->processor->dir_opened(&db->pdb, &skip, &skip_children, 554251881Speter path, 555251881Speter left_source, 556251881Speter NULL /* right_source */, 557251881Speter NULL /* copyfrom_source */, 558251881Speter pb->pdb, 559251881Speter eb->processor, 560251881Speter scratch_pool, iterpool)); 561251881Speter 562251881Speter if (!skip || !skip_children) 563251881Speter SVN_ERR(svn_ra_get_dir2(eb->ra_session, 564251881Speter skip_children ? NULL : &dirents, 565251881Speter NULL, 566251881Speter skip ? NULL : &left_props, 567251881Speter path, 568251881Speter eb->revision, 569251881Speter SVN_DIRENT_KIND, 570251881Speter scratch_pool)); 571251881Speter 572251881Speter /* The "old" dir will be skipped by the repository report. If required, 573251881Speter * crawl it recursively, diffing each file against the empty file. This 574251881Speter * is a workaround for issue 2333 "'svn diff URL1 URL2' not reverse of 575251881Speter * 'svn diff URL2 URL1'". */ 576251881Speter if (! skip_children) 577251881Speter { 578251881Speter apr_hash_index_t *hi; 579251881Speter 580251881Speter for (hi = apr_hash_first(scratch_pool, dirents); hi; 581251881Speter hi = apr_hash_next(hi)) 582251881Speter { 583251881Speter const char *child_path; 584289180Speter const char *name = apr_hash_this_key(hi); 585289180Speter svn_dirent_t *dirent = apr_hash_this_val(hi); 586251881Speter 587251881Speter svn_pool_clear(iterpool); 588251881Speter 589251881Speter child_path = svn_relpath_join(path, name, iterpool); 590251881Speter 591251881Speter if (dirent->kind == svn_node_file) 592251881Speter { 593251881Speter SVN_ERR(diff_deleted_file(child_path, db, iterpool)); 594251881Speter } 595251881Speter else if (dirent->kind == svn_node_dir) 596251881Speter { 597251881Speter SVN_ERR(diff_deleted_dir(child_path, db, iterpool)); 598251881Speter } 599251881Speter } 600251881Speter } 601251881Speter 602251881Speter if (! skip) 603251881Speter { 604251881Speter SVN_ERR(eb->processor->dir_deleted(path, 605251881Speter left_source, 606251881Speter left_props, 607251881Speter db->pdb, 608251881Speter eb->processor, 609251881Speter scratch_pool)); 610251881Speter } 611251881Speter 612251881Speter SVN_ERR(release_dir(db)); 613251881Speter 614251881Speter svn_pool_destroy(iterpool); 615251881Speter return SVN_NO_ERROR; 616251881Speter} 617251881Speter 618251881Speter/* An svn_delta_editor_t function. */ 619251881Speterstatic svn_error_t * 620251881Speterdelete_entry(const char *path, 621251881Speter svn_revnum_t base_revision, 622251881Speter void *parent_baton, 623251881Speter apr_pool_t *pool) 624251881Speter{ 625251881Speter struct dir_baton *pb = parent_baton; 626251881Speter struct edit_baton *eb = pb->edit_baton; 627251881Speter svn_node_kind_t kind; 628251881Speter apr_pool_t *scratch_pool; 629251881Speter 630251881Speter /* Process skips. */ 631251881Speter if (pb->skip_children) 632251881Speter return SVN_NO_ERROR; 633251881Speter 634251881Speter scratch_pool = svn_pool_create(eb->pool); 635251881Speter 636251881Speter /* We need to know if this is a directory or a file */ 637251881Speter SVN_ERR(svn_ra_check_path(eb->ra_session, path, eb->revision, &kind, 638251881Speter scratch_pool)); 639251881Speter 640251881Speter switch (kind) 641251881Speter { 642251881Speter case svn_node_file: 643251881Speter { 644251881Speter SVN_ERR(diff_deleted_file(path, pb, scratch_pool)); 645251881Speter break; 646251881Speter } 647251881Speter case svn_node_dir: 648251881Speter { 649251881Speter SVN_ERR(diff_deleted_dir(path, pb, scratch_pool)); 650251881Speter break; 651251881Speter } 652251881Speter default: 653251881Speter break; 654251881Speter } 655251881Speter 656251881Speter svn_pool_destroy(scratch_pool); 657251881Speter 658251881Speter return SVN_NO_ERROR; 659251881Speter} 660251881Speter 661251881Speter/* An svn_delta_editor_t function. */ 662251881Speterstatic svn_error_t * 663251881Speteradd_directory(const char *path, 664251881Speter void *parent_baton, 665251881Speter const char *copyfrom_path, 666251881Speter svn_revnum_t copyfrom_revision, 667251881Speter apr_pool_t *pool, 668251881Speter void **child_baton) 669251881Speter{ 670251881Speter struct dir_baton *pb = parent_baton; 671251881Speter struct edit_baton *eb = pb->edit_baton; 672251881Speter struct dir_baton *db; 673251881Speter 674251881Speter /* ### TODO: support copyfrom? */ 675251881Speter 676251881Speter db = make_dir_baton(path, pb, eb, TRUE, SVN_INVALID_REVNUM, pb->pool); 677251881Speter *child_baton = db; 678251881Speter 679251881Speter /* Skip *everything* within a newly tree-conflicted directory, 680251881Speter * and directories the children of which should be skipped. */ 681251881Speter if (pb->skip_children) 682251881Speter { 683251881Speter db->skip = TRUE; 684251881Speter db->skip_children = TRUE; 685251881Speter return SVN_NO_ERROR; 686251881Speter } 687251881Speter 688251881Speter db->right_source = svn_diff__source_create(eb->target_revision, 689251881Speter db->pool); 690251881Speter 691251881Speter SVN_ERR(eb->processor->dir_opened(&db->pdb, 692251881Speter &db->skip, 693251881Speter &db->skip_children, 694251881Speter db->path, 695251881Speter NULL, 696251881Speter db->right_source, 697251881Speter NULL /* copyfrom_source */, 698251881Speter pb->pdb, 699251881Speter eb->processor, 700251881Speter db->pool, db->pool)); 701251881Speter 702251881Speter return SVN_NO_ERROR; 703251881Speter} 704251881Speter 705251881Speter/* An svn_delta_editor_t function. */ 706251881Speterstatic svn_error_t * 707251881Speteropen_directory(const char *path, 708251881Speter void *parent_baton, 709251881Speter svn_revnum_t base_revision, 710251881Speter apr_pool_t *pool, 711251881Speter void **child_baton) 712251881Speter{ 713251881Speter struct dir_baton *pb = parent_baton; 714251881Speter struct edit_baton *eb = pb->edit_baton; 715251881Speter struct dir_baton *db; 716251881Speter 717251881Speter db = make_dir_baton(path, pb, eb, FALSE, base_revision, pb->pool); 718251881Speter 719251881Speter *child_baton = db; 720251881Speter 721251881Speter /* Process Skips. */ 722251881Speter if (pb->skip_children) 723251881Speter { 724251881Speter db->skip = TRUE; 725251881Speter db->skip_children = TRUE; 726251881Speter return SVN_NO_ERROR; 727251881Speter } 728251881Speter 729251881Speter db->left_source = svn_diff__source_create(eb->revision, db->pool); 730251881Speter db->right_source = svn_diff__source_create(eb->target_revision, db->pool); 731251881Speter 732251881Speter SVN_ERR(eb->processor->dir_opened(&db->pdb, 733251881Speter &db->skip, &db->skip_children, 734251881Speter path, 735251881Speter db->left_source, 736251881Speter db->right_source, 737251881Speter NULL /* copyfrom */, 738251881Speter pb ? pb->pdb : NULL, 739251881Speter eb->processor, 740251881Speter db->pool, db->pool)); 741251881Speter 742251881Speter return SVN_NO_ERROR; 743251881Speter} 744251881Speter 745251881Speter 746251881Speter/* An svn_delta_editor_t function. */ 747251881Speterstatic svn_error_t * 748251881Speteradd_file(const char *path, 749251881Speter void *parent_baton, 750251881Speter const char *copyfrom_path, 751251881Speter svn_revnum_t copyfrom_revision, 752251881Speter apr_pool_t *pool, 753251881Speter void **file_baton) 754251881Speter{ 755251881Speter struct dir_baton *pb = parent_baton; 756251881Speter struct edit_baton *eb = pb->edit_baton; 757251881Speter struct file_baton *fb; 758251881Speter 759251881Speter /* ### TODO: support copyfrom? */ 760251881Speter 761251881Speter fb = make_file_baton(path, pb, TRUE, pb->pool); 762251881Speter *file_baton = fb; 763251881Speter 764251881Speter /* Process Skips. */ 765251881Speter if (pb->skip_children) 766251881Speter { 767251881Speter fb->skip = TRUE; 768251881Speter return SVN_NO_ERROR; 769251881Speter } 770251881Speter 771251881Speter fb->pristine_props = pb->edit_baton->empty_hash; 772251881Speter 773251881Speter fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool); 774251881Speter 775251881Speter SVN_ERR(eb->processor->file_opened(&fb->pfb, 776251881Speter &fb->skip, 777251881Speter path, 778251881Speter NULL, 779251881Speter fb->right_source, 780251881Speter NULL /* copy source */, 781251881Speter pb->pdb, 782251881Speter eb->processor, 783251881Speter fb->pool, fb->pool)); 784251881Speter 785251881Speter return SVN_NO_ERROR; 786251881Speter} 787251881Speter 788251881Speter/* An svn_delta_editor_t function. */ 789251881Speterstatic svn_error_t * 790251881Speteropen_file(const char *path, 791251881Speter void *parent_baton, 792251881Speter svn_revnum_t base_revision, 793251881Speter apr_pool_t *pool, 794251881Speter void **file_baton) 795251881Speter{ 796251881Speter struct dir_baton *pb = parent_baton; 797251881Speter struct file_baton *fb; 798251881Speter struct edit_baton *eb = pb->edit_baton; 799251881Speter fb = make_file_baton(path, pb, FALSE, pb->pool); 800251881Speter *file_baton = fb; 801251881Speter 802251881Speter /* Process Skips. */ 803251881Speter if (pb->skip_children) 804251881Speter { 805251881Speter fb->skip = TRUE; 806251881Speter return SVN_NO_ERROR; 807251881Speter } 808251881Speter 809251881Speter fb->base_revision = base_revision; 810251881Speter 811251881Speter fb->left_source = svn_diff__source_create(eb->revision, fb->pool); 812251881Speter fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool); 813251881Speter 814251881Speter SVN_ERR(eb->processor->file_opened(&fb->pfb, 815251881Speter &fb->skip, 816251881Speter path, 817251881Speter fb->left_source, 818251881Speter fb->right_source, 819251881Speter NULL /* copy source */, 820251881Speter pb->pdb, 821251881Speter eb->processor, 822251881Speter fb->pool, fb->pool)); 823251881Speter 824251881Speter return SVN_NO_ERROR; 825251881Speter} 826251881Speter 827251881Speter/* Do the work of applying the text delta. */ 828251881Speterstatic svn_error_t * 829251881Speterwindow_handler(svn_txdelta_window_t *window, 830251881Speter void *window_baton) 831251881Speter{ 832251881Speter struct file_baton *fb = window_baton; 833251881Speter 834251881Speter SVN_ERR(fb->apply_handler(window, fb->apply_baton)); 835251881Speter 836251881Speter if (!window) 837251881Speter { 838251881Speter fb->result_md5_checksum = svn_checksum__from_digest_md5( 839251881Speter fb->result_digest, 840251881Speter fb->pool); 841251881Speter } 842251881Speter 843251881Speter return SVN_NO_ERROR; 844251881Speter} 845251881Speter 846251881Speter/* Implements svn_stream_lazyopen_func_t. */ 847251881Speterstatic svn_error_t * 848251881Speterlazy_open_source(svn_stream_t **stream, 849251881Speter void *baton, 850251881Speter apr_pool_t *result_pool, 851251881Speter apr_pool_t *scratch_pool) 852251881Speter{ 853251881Speter struct file_baton *fb = baton; 854251881Speter 855251881Speter SVN_ERR(svn_stream_open_readonly(stream, fb->path_start_revision, 856251881Speter result_pool, scratch_pool)); 857251881Speter 858251881Speter return SVN_NO_ERROR; 859251881Speter} 860251881Speter 861251881Speter/* Implements svn_stream_lazyopen_func_t. */ 862251881Speterstatic svn_error_t * 863251881Speterlazy_open_result(svn_stream_t **stream, 864251881Speter void *baton, 865251881Speter apr_pool_t *result_pool, 866251881Speter apr_pool_t *scratch_pool) 867251881Speter{ 868251881Speter struct file_baton *fb = baton; 869251881Speter 870251881Speter SVN_ERR(svn_stream_open_unique(stream, &fb->path_end_revision, NULL, 871251881Speter svn_io_file_del_on_pool_cleanup, 872251881Speter result_pool, scratch_pool)); 873251881Speter 874251881Speter return SVN_NO_ERROR; 875251881Speter} 876251881Speter 877251881Speter/* An svn_delta_editor_t function. */ 878251881Speterstatic svn_error_t * 879251881Speterapply_textdelta(void *file_baton, 880251881Speter const char *base_md5_digest, 881251881Speter apr_pool_t *pool, 882251881Speter svn_txdelta_window_handler_t *handler, 883251881Speter void **handler_baton) 884251881Speter{ 885251881Speter struct file_baton *fb = file_baton; 886251881Speter svn_stream_t *src_stream; 887251881Speter svn_stream_t *result_stream; 888251881Speter apr_pool_t *scratch_pool = fb->pool; 889251881Speter 890251881Speter /* Skip *everything* within a newly tree-conflicted directory. */ 891251881Speter if (fb->skip) 892251881Speter { 893251881Speter *handler = svn_delta_noop_window_handler; 894251881Speter *handler_baton = NULL; 895251881Speter return SVN_NO_ERROR; 896251881Speter } 897251881Speter 898251881Speter /* If we're not sending file text, then ignore any that we receive. */ 899251881Speter if (! fb->edit_baton->text_deltas) 900251881Speter { 901251881Speter /* Supply valid paths to indicate there is a text change. */ 902251881Speter SVN_ERR(get_empty_file(fb->edit_baton, &fb->path_start_revision)); 903251881Speter SVN_ERR(get_empty_file(fb->edit_baton, &fb->path_end_revision)); 904251881Speter 905251881Speter *handler = svn_delta_noop_window_handler; 906251881Speter *handler_baton = NULL; 907251881Speter 908251881Speter return SVN_NO_ERROR; 909251881Speter } 910251881Speter 911251881Speter /* We need the expected pristine file, so go get it */ 912251881Speter if (!fb->added) 913251881Speter SVN_ERR(get_file_from_ra(fb, FALSE, scratch_pool)); 914251881Speter else 915251881Speter SVN_ERR(get_empty_file(fb->edit_baton, &(fb->path_start_revision))); 916251881Speter 917251881Speter SVN_ERR_ASSERT(fb->path_start_revision != NULL); 918251881Speter 919251881Speter if (base_md5_digest != NULL) 920251881Speter { 921251881Speter svn_checksum_t *base_md5_checksum; 922251881Speter 923251881Speter SVN_ERR(svn_checksum_parse_hex(&base_md5_checksum, svn_checksum_md5, 924251881Speter base_md5_digest, scratch_pool)); 925251881Speter 926251881Speter if (!svn_checksum_match(base_md5_checksum, fb->start_md5_checksum)) 927251881Speter return svn_error_trace(svn_checksum_mismatch_err( 928251881Speter base_md5_checksum, 929251881Speter fb->start_md5_checksum, 930251881Speter scratch_pool, 931251881Speter _("Base checksum mismatch for '%s'"), 932251881Speter fb->path)); 933251881Speter } 934251881Speter 935251881Speter /* Open the file to be used as the base for second revision */ 936251881Speter src_stream = svn_stream_lazyopen_create(lazy_open_source, fb, TRUE, 937251881Speter scratch_pool); 938251881Speter 939251881Speter /* Open the file that will become the second revision after applying the 940251881Speter text delta, it starts empty */ 941251881Speter result_stream = svn_stream_lazyopen_create(lazy_open_result, fb, TRUE, 942251881Speter scratch_pool); 943251881Speter 944251881Speter svn_txdelta_apply(src_stream, 945251881Speter result_stream, 946251881Speter fb->result_digest, 947251881Speter fb->path, fb->pool, 948251881Speter &(fb->apply_handler), &(fb->apply_baton)); 949251881Speter 950251881Speter *handler = window_handler; 951251881Speter *handler_baton = file_baton; 952251881Speter 953251881Speter return SVN_NO_ERROR; 954251881Speter} 955251881Speter 956251881Speter/* An svn_delta_editor_t function. When the file is closed we have a temporary 957251881Speter * file containing a pristine version of the repository file. This can 958251881Speter * be compared against the working copy. 959251881Speter * 960251881Speter * ### Ignore TEXT_CHECKSUM for now. Someday we can use it to verify 961251881Speter * ### the integrity of the file being diffed. Done efficiently, this 962251881Speter * ### would probably involve calculating the checksum as the data is 963251881Speter * ### received, storing the final checksum in the file_baton, and 964251881Speter * ### comparing against it here. 965251881Speter */ 966251881Speterstatic svn_error_t * 967251881Speterclose_file(void *file_baton, 968251881Speter const char *expected_md5_digest, 969251881Speter apr_pool_t *pool) 970251881Speter{ 971251881Speter struct file_baton *fb = file_baton; 972251881Speter struct dir_baton *pb = fb->parent_baton; 973251881Speter struct edit_baton *eb = fb->edit_baton; 974251881Speter apr_pool_t *scratch_pool; 975251881Speter 976251881Speter /* Skip *everything* within a newly tree-conflicted directory. */ 977251881Speter if (fb->skip) 978251881Speter { 979251881Speter svn_pool_destroy(fb->pool); 980251881Speter SVN_ERR(release_dir(pb)); 981251881Speter return SVN_NO_ERROR; 982251881Speter } 983251881Speter 984251881Speter scratch_pool = fb->pool; 985251881Speter 986251881Speter if (expected_md5_digest && eb->text_deltas) 987251881Speter { 988251881Speter svn_checksum_t *expected_md5_checksum; 989251881Speter 990251881Speter SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5, 991251881Speter expected_md5_digest, scratch_pool)); 992251881Speter 993251881Speter if (!svn_checksum_match(expected_md5_checksum, fb->result_md5_checksum)) 994251881Speter return svn_error_trace(svn_checksum_mismatch_err( 995251881Speter expected_md5_checksum, 996251881Speter fb->result_md5_checksum, 997251881Speter pool, 998251881Speter _("Checksum mismatch for '%s'"), 999251881Speter fb->path)); 1000251881Speter } 1001251881Speter 1002251881Speter if (fb->added || fb->path_end_revision || fb->has_propchange) 1003251881Speter { 1004251881Speter apr_hash_t *right_props; 1005251881Speter 1006251881Speter if (!fb->added && !fb->pristine_props) 1007251881Speter { 1008251881Speter /* We didn't receive a text change, so we have no pristine props. 1009251881Speter Retrieve just the props now. */ 1010251881Speter SVN_ERR(get_file_from_ra(fb, TRUE, scratch_pool)); 1011251881Speter } 1012251881Speter 1013251881Speter if (fb->pristine_props) 1014362181Sdim SVN_ERR(remove_non_prop_changes(fb->pristine_props, fb->propchanges)); 1015251881Speter 1016251881Speter right_props = svn_prop__patch(fb->pristine_props, fb->propchanges, 1017251881Speter fb->pool); 1018251881Speter 1019251881Speter if (fb->added) 1020251881Speter SVN_ERR(eb->processor->file_added(fb->path, 1021251881Speter NULL /* copyfrom_src */, 1022251881Speter fb->right_source, 1023251881Speter NULL /* copyfrom_file */, 1024251881Speter fb->path_end_revision, 1025251881Speter NULL /* copyfrom_props */, 1026251881Speter right_props, 1027251881Speter fb->pfb, 1028251881Speter eb->processor, 1029251881Speter fb->pool)); 1030251881Speter else 1031251881Speter SVN_ERR(eb->processor->file_changed(fb->path, 1032251881Speter fb->left_source, 1033251881Speter fb->right_source, 1034251881Speter fb->path_end_revision 1035251881Speter ? fb->path_start_revision 1036251881Speter : NULL, 1037251881Speter fb->path_end_revision, 1038251881Speter fb->pristine_props, 1039251881Speter right_props, 1040251881Speter (fb->path_end_revision != NULL), 1041251881Speter fb->propchanges, 1042251881Speter fb->pfb, 1043251881Speter eb->processor, 1044251881Speter fb->pool)); 1045251881Speter } 1046251881Speter 1047251881Speter svn_pool_destroy(fb->pool); /* Destroy file and scratch pool */ 1048251881Speter 1049251881Speter SVN_ERR(release_dir(pb)); 1050251881Speter 1051251881Speter return SVN_NO_ERROR; 1052251881Speter} 1053251881Speter 1054251881Speter/* Report any accumulated prop changes via the 'dir_props_changed' callback, 1055251881Speter * and then call the 'dir_closed' callback. Notify about any deleted paths 1056251881Speter * within this directory that have not already been notified, and then about 1057251881Speter * this directory itself (unless it was added, in which case the notification 1058251881Speter * was done at that time). 1059251881Speter * 1060251881Speter * An svn_delta_editor_t function. */ 1061251881Speterstatic svn_error_t * 1062251881Speterclose_directory(void *dir_baton, 1063251881Speter apr_pool_t *pool) 1064251881Speter{ 1065251881Speter struct dir_baton *db = dir_baton; 1066251881Speter struct edit_baton *eb = db->edit_baton; 1067251881Speter apr_pool_t *scratch_pool; 1068251881Speter apr_hash_t *pristine_props; 1069251881Speter svn_boolean_t send_changed = FALSE; 1070251881Speter 1071251881Speter scratch_pool = db->pool; 1072251881Speter 1073251881Speter if ((db->has_propchange || db->added) && !db->skip) 1074251881Speter { 1075251881Speter if (db->added) 1076251881Speter { 1077251881Speter pristine_props = eb->empty_hash; 1078251881Speter } 1079251881Speter else 1080251881Speter { 1081251881Speter SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, &pristine_props, 1082251881Speter db->path, db->base_revision, 0, scratch_pool)); 1083251881Speter } 1084251881Speter 1085251881Speter if (db->propchanges->nelts > 0) 1086251881Speter { 1087362181Sdim SVN_ERR(remove_non_prop_changes(pristine_props, db->propchanges)); 1088251881Speter } 1089251881Speter 1090251881Speter if (db->propchanges->nelts > 0 || db->added) 1091251881Speter { 1092251881Speter apr_hash_t *right_props; 1093251881Speter 1094251881Speter right_props = svn_prop__patch(pristine_props, db->propchanges, 1095251881Speter scratch_pool); 1096251881Speter 1097251881Speter if (db->added) 1098251881Speter { 1099251881Speter SVN_ERR(eb->processor->dir_added(db->path, 1100251881Speter NULL /* copyfrom */, 1101251881Speter db->right_source, 1102251881Speter NULL /* copyfrom props */, 1103251881Speter right_props, 1104251881Speter db->pdb, 1105251881Speter eb->processor, 1106251881Speter db->pool)); 1107251881Speter } 1108251881Speter else 1109251881Speter { 1110251881Speter SVN_ERR(eb->processor->dir_changed(db->path, 1111251881Speter db->left_source, 1112251881Speter db->right_source, 1113251881Speter pristine_props, 1114251881Speter right_props, 1115251881Speter db->propchanges, 1116251881Speter db->pdb, 1117251881Speter eb->processor, 1118251881Speter db->pool)); 1119251881Speter } 1120251881Speter 1121251881Speter send_changed = TRUE; /* Skip dir_closed */ 1122251881Speter } 1123251881Speter } 1124251881Speter 1125251881Speter if (! db->skip && !send_changed) 1126251881Speter { 1127251881Speter SVN_ERR(eb->processor->dir_closed(db->path, 1128251881Speter db->left_source, 1129251881Speter db->right_source, 1130251881Speter db->pdb, 1131251881Speter eb->processor, 1132251881Speter db->pool)); 1133251881Speter } 1134251881Speter SVN_ERR(release_dir(db)); 1135251881Speter 1136251881Speter return SVN_NO_ERROR; 1137251881Speter} 1138251881Speter 1139251881Speter 1140251881Speter/* Record a prop change, which we will report later in close_file(). 1141251881Speter * 1142251881Speter * An svn_delta_editor_t function. */ 1143251881Speterstatic svn_error_t * 1144251881Speterchange_file_prop(void *file_baton, 1145251881Speter const char *name, 1146251881Speter const svn_string_t *value, 1147251881Speter apr_pool_t *pool) 1148251881Speter{ 1149251881Speter struct file_baton *fb = file_baton; 1150251881Speter svn_prop_t *propchange; 1151251881Speter svn_prop_kind_t propkind; 1152251881Speter 1153251881Speter /* Skip *everything* within a newly tree-conflicted directory. */ 1154251881Speter if (fb->skip) 1155251881Speter return SVN_NO_ERROR; 1156251881Speter 1157251881Speter propkind = svn_property_kind2(name); 1158251881Speter if (propkind == svn_prop_wc_kind) 1159251881Speter return SVN_NO_ERROR; 1160251881Speter else if (propkind == svn_prop_regular_kind) 1161251881Speter fb->has_propchange = TRUE; 1162251881Speter 1163251881Speter propchange = apr_array_push(fb->propchanges); 1164251881Speter propchange->name = apr_pstrdup(fb->pool, name); 1165289180Speter propchange->value = svn_string_dup(value, fb->pool); 1166251881Speter 1167251881Speter return SVN_NO_ERROR; 1168251881Speter} 1169251881Speter 1170251881Speter/* Make a note of this prop change, to be reported when the dir is closed. 1171251881Speter * 1172251881Speter * An svn_delta_editor_t function. */ 1173251881Speterstatic svn_error_t * 1174251881Speterchange_dir_prop(void *dir_baton, 1175251881Speter const char *name, 1176251881Speter const svn_string_t *value, 1177251881Speter apr_pool_t *pool) 1178251881Speter{ 1179251881Speter struct dir_baton *db = dir_baton; 1180251881Speter svn_prop_t *propchange; 1181251881Speter svn_prop_kind_t propkind; 1182251881Speter 1183251881Speter /* Skip *everything* within a newly tree-conflicted directory. */ 1184251881Speter if (db->skip) 1185251881Speter return SVN_NO_ERROR; 1186251881Speter 1187251881Speter propkind = svn_property_kind2(name); 1188251881Speter if (propkind == svn_prop_wc_kind) 1189251881Speter return SVN_NO_ERROR; 1190251881Speter else if (propkind == svn_prop_regular_kind) 1191251881Speter db->has_propchange = TRUE; 1192251881Speter 1193251881Speter propchange = apr_array_push(db->propchanges); 1194251881Speter propchange->name = apr_pstrdup(db->pool, name); 1195289180Speter propchange->value = svn_string_dup(value, db->pool); 1196251881Speter 1197251881Speter return SVN_NO_ERROR; 1198251881Speter} 1199251881Speter 1200251881Speter 1201251881Speter/* An svn_delta_editor_t function. */ 1202251881Speterstatic svn_error_t * 1203251881Speterclose_edit(void *edit_baton, 1204251881Speter apr_pool_t *pool) 1205251881Speter{ 1206251881Speter struct edit_baton *eb = edit_baton; 1207251881Speter 1208251881Speter svn_pool_destroy(eb->pool); 1209251881Speter 1210251881Speter return SVN_NO_ERROR; 1211251881Speter} 1212251881Speter 1213251881Speter/* Notify that the node at PATH is 'missing'. 1214251881Speter * An svn_delta_editor_t function. */ 1215251881Speterstatic svn_error_t * 1216251881Speterabsent_directory(const char *path, 1217251881Speter void *parent_baton, 1218251881Speter apr_pool_t *pool) 1219251881Speter{ 1220251881Speter struct dir_baton *pb = parent_baton; 1221251881Speter struct edit_baton *eb = pb->edit_baton; 1222251881Speter 1223251881Speter SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool)); 1224251881Speter 1225251881Speter return SVN_NO_ERROR; 1226251881Speter} 1227251881Speter 1228251881Speter 1229251881Speter/* Notify that the node at PATH is 'missing'. 1230251881Speter * An svn_delta_editor_t function. */ 1231251881Speterstatic svn_error_t * 1232251881Speterabsent_file(const char *path, 1233251881Speter void *parent_baton, 1234251881Speter apr_pool_t *pool) 1235251881Speter{ 1236251881Speter struct dir_baton *pb = parent_baton; 1237251881Speter struct edit_baton *eb = pb->edit_baton; 1238251881Speter 1239251881Speter SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool)); 1240251881Speter 1241251881Speter return SVN_NO_ERROR; 1242251881Speter} 1243251881Speter 1244251881Speterstatic svn_error_t * 1245251881Speterfetch_kind_func(svn_node_kind_t *kind, 1246251881Speter void *baton, 1247251881Speter const char *path, 1248251881Speter svn_revnum_t base_revision, 1249251881Speter apr_pool_t *scratch_pool) 1250251881Speter{ 1251251881Speter struct edit_baton *eb = baton; 1252251881Speter 1253251881Speter if (!SVN_IS_VALID_REVNUM(base_revision)) 1254251881Speter base_revision = eb->revision; 1255251881Speter 1256251881Speter SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, kind, 1257251881Speter scratch_pool)); 1258251881Speter 1259251881Speter return SVN_NO_ERROR; 1260251881Speter} 1261251881Speter 1262251881Speterstatic svn_error_t * 1263251881Speterfetch_props_func(apr_hash_t **props, 1264251881Speter void *baton, 1265251881Speter const char *path, 1266251881Speter svn_revnum_t base_revision, 1267251881Speter apr_pool_t *result_pool, 1268251881Speter apr_pool_t *scratch_pool) 1269251881Speter{ 1270251881Speter struct edit_baton *eb = baton; 1271251881Speter svn_node_kind_t node_kind; 1272251881Speter 1273251881Speter if (!SVN_IS_VALID_REVNUM(base_revision)) 1274251881Speter base_revision = eb->revision; 1275251881Speter 1276251881Speter SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind, 1277251881Speter scratch_pool)); 1278251881Speter 1279251881Speter if (node_kind == svn_node_file) 1280251881Speter { 1281251881Speter SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision, 1282251881Speter NULL, NULL, props, result_pool)); 1283251881Speter } 1284251881Speter else if (node_kind == svn_node_dir) 1285251881Speter { 1286251881Speter apr_array_header_t *tmp_props; 1287251881Speter 1288251881Speter SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path, 1289251881Speter base_revision, 0 /* Dirent fields */, 1290251881Speter result_pool)); 1291251881Speter tmp_props = svn_prop_hash_to_array(*props, result_pool); 1292251881Speter SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props, 1293251881Speter result_pool)); 1294251881Speter *props = svn_prop_array_to_hash(tmp_props, result_pool); 1295251881Speter } 1296251881Speter else 1297251881Speter { 1298251881Speter *props = apr_hash_make(result_pool); 1299251881Speter } 1300251881Speter 1301251881Speter return SVN_NO_ERROR; 1302251881Speter} 1303251881Speter 1304251881Speterstatic svn_error_t * 1305251881Speterfetch_base_func(const char **filename, 1306251881Speter void *baton, 1307251881Speter const char *path, 1308251881Speter svn_revnum_t base_revision, 1309251881Speter apr_pool_t *result_pool, 1310251881Speter apr_pool_t *scratch_pool) 1311251881Speter{ 1312251881Speter struct edit_baton *eb = baton; 1313251881Speter svn_stream_t *fstream; 1314251881Speter svn_error_t *err; 1315251881Speter 1316251881Speter if (!SVN_IS_VALID_REVNUM(base_revision)) 1317251881Speter base_revision = eb->revision; 1318251881Speter 1319251881Speter SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL, 1320251881Speter svn_io_file_del_on_pool_cleanup, 1321251881Speter result_pool, scratch_pool)); 1322251881Speter 1323251881Speter err = svn_ra_get_file(eb->ra_session, path, base_revision, 1324251881Speter fstream, NULL, NULL, scratch_pool); 1325251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 1326251881Speter { 1327251881Speter svn_error_clear(err); 1328251881Speter SVN_ERR(svn_stream_close(fstream)); 1329251881Speter 1330251881Speter *filename = NULL; 1331251881Speter return SVN_NO_ERROR; 1332251881Speter } 1333251881Speter else if (err) 1334251881Speter return svn_error_trace(err); 1335251881Speter 1336251881Speter SVN_ERR(svn_stream_close(fstream)); 1337251881Speter 1338251881Speter return SVN_NO_ERROR; 1339251881Speter} 1340251881Speter 1341251881Speter/* Create a repository diff editor and baton. */ 1342251881Spetersvn_error_t * 1343251881Spetersvn_client__get_diff_editor2(const svn_delta_editor_t **editor, 1344251881Speter void **edit_baton, 1345251881Speter svn_ra_session_t *ra_session, 1346251881Speter svn_depth_t depth, 1347251881Speter svn_revnum_t revision, 1348251881Speter svn_boolean_t text_deltas, 1349251881Speter const svn_diff_tree_processor_t *processor, 1350251881Speter svn_cancel_func_t cancel_func, 1351251881Speter void *cancel_baton, 1352251881Speter apr_pool_t *result_pool) 1353251881Speter{ 1354251881Speter apr_pool_t *editor_pool = svn_pool_create(result_pool); 1355251881Speter svn_delta_editor_t *tree_editor = svn_delta_default_editor(editor_pool); 1356251881Speter struct edit_baton *eb = apr_pcalloc(editor_pool, sizeof(*eb)); 1357251881Speter svn_delta_shim_callbacks_t *shim_callbacks = 1358251881Speter svn_delta_shim_callbacks_default(editor_pool); 1359251881Speter 1360251881Speter eb->pool = editor_pool; 1361251881Speter eb->depth = depth; 1362251881Speter 1363251881Speter eb->processor = processor; 1364251881Speter 1365251881Speter eb->ra_session = ra_session; 1366251881Speter 1367251881Speter eb->revision = revision; 1368289180Speter eb->target_revision = SVN_INVALID_REVNUM; 1369251881Speter eb->empty_file = NULL; 1370251881Speter eb->empty_hash = apr_hash_make(eb->pool); 1371251881Speter eb->text_deltas = text_deltas; 1372251881Speter eb->cancel_func = cancel_func; 1373251881Speter eb->cancel_baton = cancel_baton; 1374251881Speter 1375251881Speter tree_editor->set_target_revision = set_target_revision; 1376251881Speter tree_editor->open_root = open_root; 1377251881Speter tree_editor->delete_entry = delete_entry; 1378251881Speter tree_editor->add_directory = add_directory; 1379251881Speter tree_editor->open_directory = open_directory; 1380251881Speter tree_editor->add_file = add_file; 1381251881Speter tree_editor->open_file = open_file; 1382251881Speter tree_editor->apply_textdelta = apply_textdelta; 1383251881Speter tree_editor->close_file = close_file; 1384251881Speter tree_editor->close_directory = close_directory; 1385251881Speter tree_editor->change_file_prop = change_file_prop; 1386251881Speter tree_editor->change_dir_prop = change_dir_prop; 1387251881Speter tree_editor->close_edit = close_edit; 1388251881Speter tree_editor->absent_directory = absent_directory; 1389251881Speter tree_editor->absent_file = absent_file; 1390251881Speter 1391251881Speter SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton, 1392251881Speter tree_editor, eb, 1393251881Speter editor, edit_baton, 1394251881Speter eb->pool)); 1395251881Speter 1396251881Speter shim_callbacks->fetch_kind_func = fetch_kind_func; 1397251881Speter shim_callbacks->fetch_props_func = fetch_props_func; 1398251881Speter shim_callbacks->fetch_base_func = fetch_base_func; 1399251881Speter shim_callbacks->fetch_baton = eb; 1400251881Speter 1401251881Speter SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, 1402251881Speter NULL, NULL, shim_callbacks, 1403251881Speter result_pool, result_pool)); 1404251881Speter 1405251881Speter return SVN_NO_ERROR; 1406251881Speter} 1407