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