diff_local.c (253734) | diff_local.c (289180) |
---|---|
1/* 2 * diff_local.c: comparing local trees with each other 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file --- 31 unchanged lines hidden (view full) --- 40#include "svn_dirent_uri.h" 41#include "svn_io.h" 42#include "svn_pools.h" 43#include "svn_props.h" 44#include "svn_sorts.h" 45#include "svn_subst.h" 46#include "client.h" 47 | 1/* 2 * diff_local.c: comparing local trees with each other 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file --- 31 unchanged lines hidden (view full) --- 40#include "svn_dirent_uri.h" 41#include "svn_io.h" 42#include "svn_pools.h" 43#include "svn_props.h" 44#include "svn_sorts.h" 45#include "svn_subst.h" 46#include "client.h" 47 |
48#include "private/svn_sorts_private.h" |
|
48#include "private/svn_wc_private.h" | 49#include "private/svn_wc_private.h" |
50#include "private/svn_diff_tree.h" |
|
49 50#include "svn_private_config.h" 51 52 53/* Try to get properties for LOCAL_ABSPATH and return them in the property 54 * hash *PROPS. If there are no properties because LOCAL_ABSPATH is not 55 * versioned, return an empty property hash. */ 56static svn_error_t * --- 19 unchanged lines hidden (view full) --- 76 } 77 else 78 return svn_error_trace(err); 79 } 80 81 return SVN_NO_ERROR; 82} 83 | 51 52#include "svn_private_config.h" 53 54 55/* Try to get properties for LOCAL_ABSPATH and return them in the property 56 * hash *PROPS. If there are no properties because LOCAL_ABSPATH is not 57 * versioned, return an empty property hash. */ 58static svn_error_t * --- 19 unchanged lines hidden (view full) --- 78 } 79 else 80 return svn_error_trace(err); 81 } 82 83 return SVN_NO_ERROR; 84} 85 |
84/* Produce a diff between two arbitrary files at LOCAL_ABSPATH1 and 85 * LOCAL_ABSPATH2, using the diff callbacks from CALLBACKS. 86 * Use PATH as the name passed to diff callbacks. 87 * FILE1_IS_EMPTY and FILE2_IS_EMPTY are used as hints which diff callback 88 * function to use to compare the files (added/deleted/changed). | 86/* Forward declaration */ 87static svn_error_t * 88do_file_diff(const char *left_abspath, 89 const char *right_abspath, 90 const char *left_root_abspath, 91 const char *right_root_abspath, 92 svn_boolean_t left_only, 93 svn_boolean_t right_only, 94 void *parent_baton, 95 const svn_diff_tree_processor_t *diff_processor, 96 svn_client_ctx_t *ctx, 97 apr_pool_t *scratch_pool); 98 99/* Forward declaration */ 100static svn_error_t * 101do_dir_diff(const char *left_abspath, 102 const char *right_abspath, 103 const char *left_root_abspath, 104 const char *right_root_abspath, 105 svn_boolean_t left_only, 106 svn_boolean_t right_only, 107 svn_boolean_t left_before_right, 108 svn_depth_t depth, 109 void *parent_baton, 110 const svn_diff_tree_processor_t *diff_processor, 111 svn_client_ctx_t *ctx, 112 apr_pool_t *scratch_pool); 113 114/* Produce a diff of depth DEPTH between two arbitrary directories at 115 * LEFT_ABSPATH1 and RIGHT_ABSPATH2, using the provided diff callbacks 116 * to show file changes and, for versioned nodes, property changes. |
89 * | 117 * |
90 * If ORIGINAL_PROPS_OVERRIDE is not NULL, use it as original properties 91 * instead of reading properties from LOCAL_ABSPATH1. This is required when 92 * a file replaces a directory, where LOCAL_ABSPATH1 is an empty file that 93 * file content must be diffed against, but properties to diff against come 94 * from the replaced directory. */ | 118 * Report paths as relative from LEFT_ROOT_ABSPATH/RIGHT_ROOT_ABSPATH. 119 * 120 * If LEFT_ONLY is TRUE, only the left source exists (= everything will 121 * be reported as deleted). If RIGHT_ONLY is TRUE, only the right source 122 * exists (= everything will be reported as added). 123 * 124 * If LEFT_BEFORE_RIGHT is TRUE and left and right are unrelated, left is 125 * reported first. If false, right is reported first. (This is to allow 126 * producing a proper inverse diff). 127 * 128 * Walk the sources according to depth, and report with parent baton 129 * PARENT_BATON. */ |
95static svn_error_t * | 130static svn_error_t * |
96do_arbitrary_files_diff(const char *local_abspath1, 97 const char *local_abspath2, 98 const char *path, 99 svn_boolean_t file1_is_empty, 100 svn_boolean_t file2_is_empty, 101 apr_hash_t *original_props_override, 102 const svn_wc_diff_callbacks4_t *callbacks, 103 void *diff_baton, 104 svn_client_ctx_t *ctx, 105 apr_pool_t *scratch_pool) | 131inner_dir_diff(const char *left_abspath, 132 const char *right_abspath, 133 const char *left_root_abspath, 134 const char *right_root_abspath, 135 svn_boolean_t left_only, 136 svn_boolean_t right_only, 137 svn_boolean_t left_before_right, 138 svn_depth_t depth, 139 void *parent_baton, 140 const svn_diff_tree_processor_t *diff_processor, 141 svn_client_ctx_t *ctx, 142 apr_pool_t *scratch_pool) |
106{ | 143{ |
107 apr_hash_t *original_props; 108 apr_hash_t *modified_props; 109 apr_array_header_t *prop_changes; 110 svn_string_t *original_mime_type = NULL; 111 svn_string_t *modified_mime_type = NULL; | 144 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 145 apr_hash_t *left_dirents; 146 apr_hash_t *right_dirents; 147 apr_array_header_t *sorted_dirents; 148 svn_error_t *err; 149 svn_depth_t depth_below_here; 150 int i; |
112 | 151 |
113 if (ctx->cancel_func) 114 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); | 152 SVN_ERR_ASSERT(depth >= svn_depth_files && depth <= svn_depth_infinity); |
115 | 153 |
116 /* Try to get properties from either file. It's OK if the files do not 117 * have properties, or if they are unversioned. */ 118 if (original_props_override) 119 original_props = original_props_override; 120 else 121 SVN_ERR(get_props(&original_props, local_abspath1, ctx->wc_ctx, 122 scratch_pool, scratch_pool)); 123 SVN_ERR(get_props(&modified_props, local_abspath2, ctx->wc_ctx, 124 scratch_pool, scratch_pool)); 125 126 SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props, 127 scratch_pool)); 128 129 /* Try to determine the mime-type of each file. */ 130 original_mime_type = svn_hash_gets(original_props, SVN_PROP_MIME_TYPE); 131 if (!file1_is_empty && !original_mime_type) | 154 if (!right_only) |
132 { | 155 { |
133 const char *mime_type; 134 SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1, 135 ctx->mimetypes_map, scratch_pool)); | 156 err = svn_io_get_dirents3(&left_dirents, left_abspath, FALSE, 157 scratch_pool, iterpool); |
136 | 158 |
137 if (mime_type) 138 original_mime_type = svn_string_create(mime_type, scratch_pool); | 159 if (err && (APR_STATUS_IS_ENOENT(err->apr_err) 160 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 161 { 162 svn_error_clear(err); 163 left_dirents = apr_hash_make(scratch_pool); 164 right_only = TRUE; 165 } 166 else 167 SVN_ERR(err); |
139 } | 168 } |
169 else 170 left_dirents = apr_hash_make(scratch_pool); |
|
140 | 171 |
141 modified_mime_type = svn_hash_gets(modified_props, SVN_PROP_MIME_TYPE); 142 if (!file2_is_empty && !modified_mime_type) | 172 if (!left_only) |
143 { | 173 { |
144 const char *mime_type; 145 SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1, 146 ctx->mimetypes_map, scratch_pool)); | 174 err = svn_io_get_dirents3(&right_dirents, right_abspath, FALSE, 175 scratch_pool, iterpool); |
147 | 176 |
148 if (mime_type) 149 modified_mime_type = svn_string_create(mime_type, scratch_pool); | 177 if (err && (APR_STATUS_IS_ENOENT(err->apr_err) 178 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 179 { 180 svn_error_clear(err); 181 right_dirents = apr_hash_make(scratch_pool); 182 right_only = TRUE; 183 } 184 else 185 SVN_ERR(err); |
150 } | 186 } |
187 else 188 right_dirents = apr_hash_make(scratch_pool); |
|
151 | 189 |
152 /* Produce the diff. */ 153 if (file1_is_empty && !file2_is_empty) 154 SVN_ERR(callbacks->file_added(NULL, NULL, NULL, path, 155 local_abspath1, local_abspath2, 156 /* ### TODO get real revision info 157 * for versioned files? */ 158 SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, 159 original_mime_type ? 160 original_mime_type->data : NULL, 161 modified_mime_type ? 162 modified_mime_type->data : NULL, 163 /* ### TODO get copyfrom? */ 164 NULL, SVN_INVALID_REVNUM, 165 prop_changes, original_props, 166 diff_baton, scratch_pool)); 167 else if (!file1_is_empty && file2_is_empty) 168 SVN_ERR(callbacks->file_deleted(NULL, NULL, path, 169 local_abspath1, local_abspath2, 170 original_mime_type ? 171 original_mime_type->data : NULL, 172 modified_mime_type ? 173 modified_mime_type->data : NULL, 174 original_props, 175 diff_baton, scratch_pool)); | 190 if (left_only && right_only) 191 return SVN_NO_ERROR; /* Somebody deleted the directory?? */ 192 193 if (depth != svn_depth_infinity) 194 depth_below_here = svn_depth_empty; |
176 else | 195 else |
196 depth_below_here = svn_depth_infinity; 197 198 sorted_dirents = svn_sort__hash(apr_hash_merge(iterpool, left_dirents, 199 right_dirents, NULL, NULL), 200 svn_sort_compare_items_as_paths, 201 scratch_pool); 202 203 for (i = 0; i < sorted_dirents->nelts; i++) |
|
177 { | 204 { |
178 svn_stream_t *file1; 179 svn_stream_t *file2; 180 svn_boolean_t same; 181 svn_string_t *val; 182 /* We have two files, which may or may not be the same. | 205 svn_sort__item_t* elt = &APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t); 206 svn_io_dirent2_t *left_dirent; 207 svn_io_dirent2_t *right_dirent; 208 const char *child_left_abspath; 209 const char *child_right_abspath; |
183 | 210 |
184 ### Our caller assumes that we should ignore symlinks here and 185 handle them as normal paths. Perhaps that should change? 186 */ 187 SVN_ERR(svn_stream_open_readonly(&file1, local_abspath1, scratch_pool, 188 scratch_pool)); | 211 svn_pool_clear(iterpool); |
189 | 212 |
190 SVN_ERR(svn_stream_open_readonly(&file2, local_abspath2, scratch_pool, 191 scratch_pool)); | 213 if (ctx->cancel_func) 214 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); |
192 | 215 |
193 /* Wrap with normalization, etc. if necessary */ 194 if (original_props) 195 { 196 val = svn_hash_gets(original_props, SVN_PROP_EOL_STYLE); | 216 if (svn_wc_is_adm_dir(elt->key, iterpool)) 217 continue; |
197 | 218 |
198 if (val) 199 { 200 svn_subst_eol_style_t style; 201 const char *eol; 202 svn_subst_eol_style_from_value(&style, &eol, val->data); | 219 left_dirent = right_only ? NULL : svn_hash_gets(left_dirents, elt->key); 220 right_dirent = left_only ? NULL : svn_hash_gets(right_dirents, elt->key); |
203 | 221 |
204 /* ### Ignoring keywords */ 205 if (eol) 206 file1 = svn_subst_stream_translated(file1, eol, TRUE, 207 NULL, FALSE, 208 scratch_pool); 209 } 210 } | 222 child_left_abspath = svn_dirent_join(left_abspath, elt->key, iterpool); 223 child_right_abspath = svn_dirent_join(right_abspath, elt->key, iterpool); |
211 | 224 |
212 if (modified_props) | 225 if (((left_dirent == NULL) != (right_dirent == NULL)) 226 || (left_dirent->kind != right_dirent->kind)) |
213 { | 227 { |
214 val = svn_hash_gets(modified_props, SVN_PROP_EOL_STYLE); | 228 /* Report delete and/or add */ 229 if (left_dirent && left_before_right) 230 { 231 if (left_dirent->kind == svn_node_file) 232 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, 233 left_root_abspath, right_root_abspath, 234 TRUE, FALSE, parent_baton, 235 diff_processor, ctx, iterpool)); 236 else if (depth >= svn_depth_immediates) 237 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, 238 left_root_abspath, right_root_abspath, 239 TRUE, FALSE, left_before_right, 240 depth_below_here, parent_baton, 241 diff_processor, ctx, iterpool)); 242 } |
215 | 243 |
216 if (val) | 244 if (right_dirent) |
217 { | 245 { |
218 svn_subst_eol_style_t style; 219 const char *eol; 220 svn_subst_eol_style_from_value(&style, &eol, val->data); | 246 if (right_dirent->kind == svn_node_file) 247 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, 248 left_root_abspath, right_root_abspath, 249 FALSE, TRUE, parent_baton, 250 diff_processor, ctx, iterpool)); 251 else if (depth >= svn_depth_immediates) 252 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, 253 left_root_abspath, right_root_abspath, 254 FALSE, TRUE, left_before_right, 255 depth_below_here, parent_baton, 256 diff_processor, ctx, iterpool)); 257 } |
221 | 258 |
222 /* ### Ignoring keywords */ 223 if (eol) 224 file2 = svn_subst_stream_translated(file2, eol, TRUE, 225 NULL, FALSE, 226 scratch_pool); | 259 if (left_dirent && !left_before_right) 260 { 261 if (left_dirent->kind == svn_node_file) 262 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, 263 left_root_abspath, right_root_abspath, 264 TRUE, FALSE, parent_baton, 265 diff_processor, ctx, iterpool)); 266 else if (depth >= svn_depth_immediates) 267 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, 268 left_root_abspath, right_root_abspath, 269 TRUE, FALSE, left_before_right, 270 depth_below_here, parent_baton, 271 diff_processor, ctx, iterpool)); |
227 } 228 } | 272 } 273 } |
229 230 SVN_ERR(svn_stream_contents_same2(&same, file1, file2, scratch_pool)); 231 232 if (! same || prop_changes->nelts > 0) | 274 else if (left_dirent->kind == svn_node_file) |
233 { | 275 { |
234 /* ### We should probably pass the normalized data we created using 235 the subst streams as that is what diff users expect */ 236 SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, path, 237 same ? NULL : local_abspath1, 238 same ? NULL : local_abspath2, 239 /* ### TODO get real revision info 240 * for versioned files? */ 241 SVN_INVALID_REVNUM /* rev1 */, 242 SVN_INVALID_REVNUM /* rev2 */, 243 original_mime_type ? 244 original_mime_type->data : NULL, 245 modified_mime_type ? 246 modified_mime_type->data : NULL, 247 prop_changes, original_props, 248 diff_baton, scratch_pool)); | 276 /* Perform file-file diff */ 277 SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, 278 left_root_abspath, right_root_abspath, 279 FALSE, FALSE, parent_baton, 280 diff_processor, ctx, iterpool)); |
249 } | 281 } |
282 else if (depth >= svn_depth_immediates) 283 { 284 /* Perform dir-dir diff */ 285 SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, 286 left_root_abspath, right_root_abspath, 287 FALSE, FALSE, left_before_right, 288 depth_below_here, parent_baton, 289 diff_processor, ctx, iterpool)); 290 } |
|
250 } 251 252 return SVN_NO_ERROR; 253} 254 | 291 } 292 293 return SVN_NO_ERROR; 294} 295 |
255struct arbitrary_diff_walker_baton { 256 /* The root directories of the trees being compared. */ 257 const char *root1_abspath; 258 const char *root2_abspath; | 296/* Translates *LEFT_ABSPATH to a temporary file if PROPS specify that the 297 file needs translation. *LEFT_ABSPATH is updated to point to a file that 298 lives at least as long as RESULT_POOL when translation is necessary. 299 Otherwise the value is not updated */ 300static svn_error_t * 301translate_if_necessary(const char **local_abspath, 302 apr_hash_t *props, 303 svn_cancel_func_t cancel_func, 304 void *cancel_baton, 305 apr_pool_t *result_pool, 306 apr_pool_t *scratch_pool) 307{ 308 const svn_string_t *eol_style_val; 309 const svn_string_t *keywords_val; 310 svn_subst_eol_style_t eol_style; 311 const char *eol; 312 apr_hash_t *keywords; 313 svn_stream_t *contents; 314 svn_stream_t *dst; |
259 | 315 |
260 /* TRUE if recursing within an added subtree of root2_abspath that 261 * does not exist in root1_abspath. */ 262 svn_boolean_t recursing_within_added_subtree; | 316 /* if (svn_hash_gets(props, SVN_PROP_SPECIAL)) 317 ### TODO: Implement */ |
263 | 318 |
264 /* TRUE if recursing within an administrative (.i.e. .svn) directory. */ 265 svn_boolean_t recursing_within_adm_dir; | 319 eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE); 320 keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS); |
266 | 321 |
267 /* The absolute path of the adm dir if RECURSING_WITHIN_ADM_DIR is TRUE. 268 * Else this is NULL.*/ 269 const char *adm_dir_abspath; | 322 if (eol_style_val) 323 svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data); 324 else 325 { 326 eol = NULL; 327 eol_style = svn_subst_eol_style_none; 328 } |
270 | 329 |
271 /* A path to an empty file used for diffs that add/delete files. */ 272 const char *empty_file_abspath; | 330 if (keywords_val) 331 SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data, 332 APR_STRINGIFY(SVN_INVALID_REVNUM), 333 "", "", 0, "", scratch_pool)); 334 else 335 keywords = NULL; |
273 | 336 |
274 const svn_wc_diff_callbacks4_t *callbacks; 275 void *diff_baton; 276 svn_client_ctx_t *ctx; 277 apr_pool_t *pool; 278} arbitrary_diff_walker_baton; | 337 if (!svn_subst_translation_required(eol_style, eol, keywords, FALSE, FALSE)) 338 return SVN_NO_ERROR; |
279 | 339 |
280/* Forward declaration needed because this function has a cyclic 281 * dependency with do_arbitrary_dirs_diff(). */ 282static svn_error_t * 283arbitrary_diff_walker(void *baton, const char *local_abspath, 284 const apr_finfo_t *finfo, 285 apr_pool_t *scratch_pool); | 340 SVN_ERR(svn_stream_open_readonly(&contents, *local_abspath, 341 scratch_pool, scratch_pool)); |
286 | 342 |
287/* Another forward declaration. */ 288static svn_error_t * 289arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b, 290 const char *local_abspath, 291 svn_depth_t depth, 292 apr_pool_t *scratch_pool); | 343 SVN_ERR(svn_stream_open_unique(&dst, local_abspath, NULL, 344 svn_io_file_del_on_pool_cleanup, 345 result_pool, scratch_pool)); |
293 | 346 |
294/* Produce a diff of depth DEPTH between two arbitrary directories at 295 * LOCAL_ABSPATH1 and LOCAL_ABSPATH2, using the provided diff callbacks 296 * to show file changes and, for versioned nodes, property changes. 297 * 298 * If ROOT_ABSPATH1 and ROOT_ABSPATH2 are not NULL, show paths in diffs 299 * relative to these roots, rather than relative to LOCAL_ABSPATH1 and 300 * LOCAL_ABSPATH2. This is needed when crawling a subtree that exists 301 * only within LOCAL_ABSPATH2. */ 302static svn_error_t * 303do_arbitrary_dirs_diff(const char *local_abspath1, 304 const char *local_abspath2, 305 const char *root_abspath1, 306 const char *root_abspath2, 307 svn_depth_t depth, 308 const svn_wc_diff_callbacks4_t *callbacks, 309 void *diff_baton, 310 svn_client_ctx_t *ctx, 311 apr_pool_t *scratch_pool) 312{ 313 apr_file_t *empty_file; 314 svn_node_kind_t kind1; | 347 dst = svn_subst_stream_translated(dst, eol, TRUE /* repair */, 348 keywords, FALSE /* expand */, 349 scratch_pool); |
315 | 350 |
316 struct arbitrary_diff_walker_baton b; | 351 SVN_ERR(svn_stream_copy3(contents, dst, cancel_func, cancel_baton, 352 scratch_pool)); |
317 | 353 |
318 /* If LOCAL_ABSPATH1 is not a directory, crawl LOCAL_ABSPATH2 instead 319 * and compare it to LOCAL_ABSPATH1, showing only additions. 320 * This case can only happen during recursion from arbitrary_diff_walker(), 321 * because do_arbitrary_nodes_diff() prevents this from happening at 322 * the root of the comparison. */ 323 SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); 324 b.recursing_within_added_subtree = (kind1 != svn_node_dir); 325 326 b.root1_abspath = root_abspath1 ? root_abspath1 : local_abspath1; 327 b.root2_abspath = root_abspath2 ? root_abspath2 : local_abspath2; 328 b.recursing_within_adm_dir = FALSE; 329 b.adm_dir_abspath = NULL; 330 b.callbacks = callbacks; 331 b.diff_baton = diff_baton; 332 b.ctx = ctx; 333 b.pool = scratch_pool; 334 335 SVN_ERR(svn_io_open_unique_file3(&empty_file, &b.empty_file_abspath, 336 NULL, svn_io_file_del_on_pool_cleanup, 337 scratch_pool, scratch_pool)); 338 339 if (depth <= svn_depth_immediates) 340 SVN_ERR(arbitrary_diff_this_dir(&b, local_abspath1, depth, scratch_pool)); 341 else if (depth == svn_depth_infinity) 342 SVN_ERR(svn_io_dir_walk2(b.recursing_within_added_subtree ? local_abspath2 343 : local_abspath1, 344 0, arbitrary_diff_walker, &b, scratch_pool)); | |
345 return SVN_NO_ERROR; 346} 347 | 354 return SVN_NO_ERROR; 355} 356 |
348/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH, 349 * using information from the arbitrary_diff_walker_baton B. 350 * LOCAL_ABSPATH is the path being crawled and can be on either side 351 * of the diff depending on baton->recursing_within_added_subtree. */ | 357/* Handles reporting of a file for inner_dir_diff */ |
352static svn_error_t * | 358static svn_error_t * |
353arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b, 354 const char *local_abspath, 355 svn_depth_t depth, 356 apr_pool_t *scratch_pool) | 359do_file_diff(const char *left_abspath, 360 const char *right_abspath, 361 const char *left_root_abspath, 362 const char *right_root_abspath, 363 svn_boolean_t left_only, 364 svn_boolean_t right_only, 365 void *parent_baton, 366 const svn_diff_tree_processor_t *diff_processor, 367 svn_client_ctx_t *ctx, 368 apr_pool_t *scratch_pool) |
357{ | 369{ |
358 const char *local_abspath1; 359 const char *local_abspath2; 360 svn_node_kind_t kind1; 361 svn_node_kind_t kind2; 362 const char *child_relpath; 363 apr_hash_t *dirents1; 364 apr_hash_t *dirents2; 365 apr_hash_t *merged_dirents; 366 apr_array_header_t *sorted_dirents; 367 int i; 368 apr_pool_t *iterpool; | 370 const char *relpath; 371 svn_diff_source_t *left_source; 372 svn_diff_source_t *right_source; 373 svn_boolean_t skip = FALSE; 374 apr_hash_t *left_props; 375 apr_hash_t *right_props; 376 void *file_baton; |
369 | 377 |
370 if (b->recursing_within_adm_dir) 371 { 372 if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath)) 373 return SVN_NO_ERROR; 374 else 375 { 376 b->recursing_within_adm_dir = FALSE; 377 b->adm_dir_abspath = NULL; 378 } 379 } 380 else if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL), 381 scratch_pool)) 382 { 383 b->recursing_within_adm_dir = TRUE; 384 b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath); 385 return SVN_NO_ERROR; 386 } | 378 relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath); |
387 | 379 |
388 if (b->recursing_within_added_subtree) 389 child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath); | 380 if (! right_only) 381 left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); |
390 else | 382 else |
391 child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath); 392 if (!child_relpath) 393 return SVN_NO_ERROR; | 383 left_source = NULL; |
394 | 384 |
395 local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath, 396 scratch_pool); 397 SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); | 385 if (! left_only) 386 right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 387 else 388 right_source = NULL; |
398 | 389 |
399 local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath, 400 scratch_pool); 401 SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool)); | 390 SVN_ERR(diff_processor->file_opened(&file_baton, &skip, 391 relpath, 392 left_source, 393 right_source, 394 NULL /* copyfrom_source */, 395 parent_baton, 396 diff_processor, 397 scratch_pool, 398 scratch_pool)); |
402 | 399 |
403 if (depth > svn_depth_empty) 404 { 405 if (kind1 == svn_node_dir) 406 SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1, 407 TRUE, /* only_check_type */ 408 scratch_pool, scratch_pool)); 409 else 410 dirents1 = apr_hash_make(scratch_pool); 411 } | 400 if (skip) 401 return SVN_NO_ERROR; |
412 | 402 |
413 if (kind2 == svn_node_dir) | 403 if (! right_only) |
414 { | 404 { |
415 apr_hash_t *original_props; 416 apr_hash_t *modified_props; 417 apr_array_header_t *prop_changes; 418 419 /* Show any property changes for this directory. */ 420 SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx, | 405 SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx, |
421 scratch_pool, scratch_pool)); | 406 scratch_pool, scratch_pool)); |
422 SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx, 423 scratch_pool, scratch_pool)); 424 SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props, 425 scratch_pool)); 426 if (prop_changes->nelts > 0) 427 SVN_ERR(b->callbacks->dir_props_changed(NULL, NULL, child_relpath, 428 FALSE /* was_added */, 429 prop_changes, original_props, 430 b->diff_baton, 431 scratch_pool)); | |
432 | 407 |
433 if (depth > svn_depth_empty) | 408 /* We perform a mimetype detection to avoid diffing binary files 409 for textual changes.*/ 410 if (! svn_hash_gets(left_props, SVN_PROP_MIME_TYPE)) |
434 { | 411 { |
435 /* Read directory entries. */ 436 SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2, 437 TRUE, /* only_check_type */ 438 scratch_pool, scratch_pool)); | 412 const char *mime_type; 413 414 /* ### Use libmagic magic? */ 415 SVN_ERR(svn_io_detect_mimetype2(&mime_type, left_abspath, 416 ctx->mimetypes_map, scratch_pool)); 417 418 if (mime_type) 419 svn_hash_sets(left_props, SVN_PROP_MIME_TYPE, 420 svn_string_create(mime_type, scratch_pool)); |
439 } | 421 } |
422 423 SVN_ERR(translate_if_necessary(&left_abspath, left_props, 424 ctx->cancel_func, ctx->cancel_baton, 425 scratch_pool, scratch_pool)); |
|
440 } | 426 } |
441 else if (depth > svn_depth_empty) 442 dirents2 = apr_hash_make(scratch_pool); | 427 else 428 left_props = NULL; |
443 | 429 |
444 if (depth <= svn_depth_empty) 445 return SVN_NO_ERROR; 446 447 /* Compare dirents1 to dirents2 and show added/deleted/changed files. */ 448 merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2, 449 NULL, NULL); 450 sorted_dirents = svn_sort__hash(merged_dirents, 451 svn_sort_compare_items_as_paths, 452 scratch_pool); 453 iterpool = svn_pool_create(scratch_pool); 454 for (i = 0; i < sorted_dirents->nelts; i++) | 430 if (! left_only) |
455 { | 431 { |
456 svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t); 457 const char *name = elt.key; 458 svn_io_dirent2_t *dirent1; 459 svn_io_dirent2_t *dirent2; 460 const char *child1_abspath; 461 const char *child2_abspath; | 432 SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx, 433 scratch_pool, scratch_pool)); |
462 | 434 |
463 svn_pool_clear(iterpool); | 435 /* We perform a mimetype detection to avoid diffing binary files 436 for textual changes.*/ 437 if (! svn_hash_gets(right_props, SVN_PROP_MIME_TYPE)) 438 { 439 const char *mime_type; |
464 | 440 |
465 if (b->ctx->cancel_func) 466 SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton)); | 441 /* ### Use libmagic magic? */ 442 SVN_ERR(svn_io_detect_mimetype2(&mime_type, right_abspath, 443 ctx->mimetypes_map, scratch_pool)); |
467 | 444 |
468 if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0) 469 continue; 470 471 dirent1 = svn_hash_gets(dirents1, name); 472 if (!dirent1) 473 { 474 dirent1 = svn_io_dirent2_create(iterpool); 475 dirent1->kind = svn_node_none; | 445 if (mime_type) 446 svn_hash_sets(right_props, SVN_PROP_MIME_TYPE, 447 svn_string_create(mime_type, scratch_pool)); |
476 } | 448 } |
477 dirent2 = svn_hash_gets(dirents2, name); 478 if (!dirent2) 479 { 480 dirent2 = svn_io_dirent2_create(iterpool); 481 dirent2->kind = svn_node_none; 482 } | |
483 | 449 |
484 child1_abspath = svn_dirent_join(local_abspath1, name, iterpool); 485 child2_abspath = svn_dirent_join(local_abspath2, name, iterpool); | 450 SVN_ERR(translate_if_necessary(&right_abspath, right_props, 451 ctx->cancel_func, ctx->cancel_baton, 452 scratch_pool, scratch_pool)); |
486 | 453 |
487 if (dirent1->special) 488 SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind, 489 iterpool)); 490 if (dirent2->special) 491 SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind, 492 iterpool)); | 454 } 455 else 456 right_props = NULL; |
493 | 457 |
494 if (dirent1->kind == svn_node_dir && 495 dirent2->kind == svn_node_dir) 496 { 497 if (depth == svn_depth_immediates) 498 { 499 /* Not using the walker, so show property diffs on these dirs. */ 500 SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath, 501 b->root1_abspath, b->root2_abspath, 502 svn_depth_empty, 503 b->callbacks, b->diff_baton, 504 b->ctx, iterpool)); 505 } 506 else 507 { 508 /* Either the walker will visit these directories (with 509 * depth=infinity) and they will be processed as 'this dir' 510 * later, or we're showing file children only (depth=files). */ 511 continue; 512 } | 458 if (left_only) 459 { 460 SVN_ERR(diff_processor->file_deleted(relpath, 461 left_source, 462 left_abspath, 463 left_props, 464 file_baton, 465 diff_processor, 466 scratch_pool)); 467 } 468 else if (right_only) 469 { 470 SVN_ERR(diff_processor->file_added(relpath, 471 NULL /* copyfrom_source */, 472 right_source, 473 NULL /* copyfrom_file */, 474 right_abspath, 475 NULL /* copyfrom_props */, 476 right_props, 477 file_baton, 478 diff_processor, 479 scratch_pool)); 480 } 481 else 482 { 483 /* ### Perform diff -> close/changed */ 484 svn_boolean_t same; 485 apr_array_header_t *prop_changes; |
513 | 486 |
514 } | 487 SVN_ERR(svn_io_files_contents_same_p(&same, left_abspath, right_abspath, 488 scratch_pool)); |
515 | 489 |
516 /* Files that exist only in dirents1. */ 517 if (dirent1->kind == svn_node_file && 518 (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none)) 519 SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath, 520 svn_relpath_join(child_relpath, name, 521 iterpool), 522 FALSE, TRUE, NULL, 523 b->callbacks, b->diff_baton, 524 b->ctx, iterpool)); | 490 SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, 491 scratch_pool)); |
525 | 492 |
526 /* Files that exist only in dirents2. */ 527 if (dirent2->kind == svn_node_file && 528 (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none)) | 493 if (!same || prop_changes->nelts > 0) |
529 { | 494 { |
530 apr_hash_t *original_props; 531 532 SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx, 533 scratch_pool, scratch_pool)); 534 SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath, 535 svn_relpath_join(child_relpath, name, 536 iterpool), 537 TRUE, FALSE, original_props, 538 b->callbacks, b->diff_baton, 539 b->ctx, iterpool)); | 495 SVN_ERR(diff_processor->file_changed(relpath, 496 left_source, 497 right_source, 498 same ? NULL : left_abspath, 499 same ? NULL : right_abspath, 500 left_props, 501 right_props, 502 !same, 503 prop_changes, 504 file_baton, 505 diff_processor, 506 scratch_pool)); |
540 } | 507 } |
541 542 /* Files that exist in dirents1 and dirents2. */ 543 if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file) 544 SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath, 545 svn_relpath_join(child_relpath, name, 546 iterpool), 547 FALSE, FALSE, NULL, 548 b->callbacks, b->diff_baton, 549 b->ctx, scratch_pool)); 550 551 /* Directories that only exist in dirents2. These aren't crawled 552 * by this walker so we have to crawl them separately. */ 553 if (depth > svn_depth_files && 554 dirent2->kind == svn_node_dir && 555 (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none)) 556 SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath, 557 b->root1_abspath, b->root2_abspath, 558 depth <= svn_depth_immediates 559 ? svn_depth_empty 560 : svn_depth_infinity , 561 b->callbacks, b->diff_baton, 562 b->ctx, iterpool)); | 508 else 509 { 510 SVN_ERR(diff_processor->file_closed(relpath, 511 left_source, 512 right_source, 513 file_baton, 514 diff_processor, 515 scratch_pool)); 516 } |
563 } | 517 } |
564 565 svn_pool_destroy(iterpool); 566 | |
567 return SVN_NO_ERROR; 568} 569 | 518 return SVN_NO_ERROR; 519} 520 |
570/* An implementation of svn_io_walk_func_t. 571 * Note: LOCAL_ABSPATH is the path being crawled and can be on either side 572 * of the diff depending on baton->recursing_within_added_subtree. */ | 521 522/* Handles reporting of a directory and its children for inner_dir_diff */ |
573static svn_error_t * | 523static svn_error_t * |
574arbitrary_diff_walker(void *baton, const char *local_abspath, 575 const apr_finfo_t *finfo, 576 apr_pool_t *scratch_pool) | 524do_dir_diff(const char *left_abspath, 525 const char *right_abspath, 526 const char *left_root_abspath, 527 const char *right_root_abspath, 528 svn_boolean_t left_only, 529 svn_boolean_t right_only, 530 svn_boolean_t left_before_right, 531 svn_depth_t depth, 532 void *parent_baton, 533 const svn_diff_tree_processor_t *diff_processor, 534 svn_client_ctx_t *ctx, 535 apr_pool_t *scratch_pool) |
577{ | 536{ |
578 struct arbitrary_diff_walker_baton *b = baton; | 537 const char *relpath; 538 svn_diff_source_t *left_source; 539 svn_diff_source_t *right_source; 540 svn_boolean_t skip = FALSE; 541 svn_boolean_t skip_children = FALSE; 542 void *dir_baton; 543 apr_hash_t *left_props; 544 apr_hash_t *right_props; |
579 | 545 |
580 if (b->ctx->cancel_func) 581 SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton)); | 546 relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath); |
582 | 547 |
583 if (finfo->filetype != APR_DIR) | 548 if (! right_only) 549 { 550 left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 551 SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx, 552 scratch_pool, scratch_pool)); 553 } 554 else 555 { 556 left_source = NULL; 557 left_props = NULL; 558 } 559 560 if (! left_only) 561 { 562 right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 563 SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx, 564 scratch_pool, scratch_pool)); 565 } 566 else 567 { 568 right_source = NULL; 569 right_props = NULL; 570 } 571 572 SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, 573 relpath, 574 left_source, 575 right_source, 576 NULL /* copyfrom_source */, 577 parent_baton, 578 diff_processor, 579 scratch_pool, scratch_pool)); 580 581 if (!skip_children) 582 { 583 if (depth >= svn_depth_files) 584 SVN_ERR(inner_dir_diff(left_abspath, right_abspath, 585 left_root_abspath, right_root_abspath, 586 left_only, right_only, 587 left_before_right, depth, 588 dir_baton, 589 diff_processor, ctx, scratch_pool)); 590 } 591 else if (skip) |
584 return SVN_NO_ERROR; 585 | 592 return SVN_NO_ERROR; 593 |
586 SVN_ERR(arbitrary_diff_this_dir(b, local_abspath, svn_depth_infinity, 587 scratch_pool)); | 594 if (left_props && right_props) 595 { 596 apr_array_header_t *prop_diffs; |
588 | 597 |
598 SVN_ERR(svn_prop_diffs(&prop_diffs, right_props, left_props, 599 scratch_pool)); 600 601 if (prop_diffs->nelts) 602 { 603 SVN_ERR(diff_processor->dir_changed(relpath, 604 left_source, 605 right_source, 606 left_props, 607 right_props, 608 prop_diffs, 609 dir_baton, 610 diff_processor, 611 scratch_pool)); 612 return SVN_NO_ERROR; 613 } 614 } 615 616 if (left_source && right_source) 617 { 618 SVN_ERR(diff_processor->dir_closed(relpath, 619 left_source, 620 right_source, 621 dir_baton, 622 diff_processor, 623 scratch_pool)); 624 } 625 else if (left_source) 626 { 627 SVN_ERR(diff_processor->dir_deleted(relpath, 628 left_source, 629 left_props, 630 dir_baton, 631 diff_processor, 632 scratch_pool)); 633 } 634 else 635 { 636 SVN_ERR(diff_processor->dir_added(relpath, 637 NULL /* copyfrom_source */, 638 right_source, 639 NULL /* copyfrom_props */, 640 right_props, 641 dir_baton, 642 diff_processor, 643 scratch_pool)); 644 } 645 |
|
589 return SVN_NO_ERROR; 590} 591 592svn_error_t * | 646 return SVN_NO_ERROR; 647} 648 649svn_error_t * |
593svn_client__arbitrary_nodes_diff(const char *local_abspath1, 594 const char *local_abspath2, | 650svn_client__arbitrary_nodes_diff(const char **root_relpath, 651 svn_boolean_t *root_is_dir, 652 const char *left_abspath, 653 const char *right_abspath, |
595 svn_depth_t depth, | 654 svn_depth_t depth, |
596 const svn_wc_diff_callbacks4_t *callbacks, 597 void *diff_baton, | 655 const svn_diff_tree_processor_t *diff_processor, |
598 svn_client_ctx_t *ctx, | 656 svn_client_ctx_t *ctx, |
657 apr_pool_t *result_pool, |
|
599 apr_pool_t *scratch_pool) 600{ | 658 apr_pool_t *scratch_pool) 659{ |
601 svn_node_kind_t kind1; 602 svn_node_kind_t kind2; | 660 svn_node_kind_t left_kind; 661 svn_node_kind_t right_kind; 662 const char *left_root_abspath; 663 const char *right_root_abspath; 664 svn_boolean_t left_before_right = TRUE; /* Future argument? */ |
603 | 665 |
604 SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); 605 SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool)); | 666 if (depth == svn_depth_unknown) 667 depth = svn_depth_infinity; |
606 | 668 |
607 if (kind1 != kind2) 608 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 609 _("'%s' is not the same node kind as '%s'"), 610 svn_dirent_local_style(local_abspath1, 611 scratch_pool), 612 svn_dirent_local_style(local_abspath2, 613 scratch_pool)); | 669 SVN_ERR(svn_io_check_resolved_path(left_abspath, &left_kind, scratch_pool)); 670 SVN_ERR(svn_io_check_resolved_path(right_abspath, &right_kind, scratch_pool)); |
614 615 if (depth == svn_depth_unknown) 616 depth = svn_depth_infinity; 617 | 671 672 if (depth == svn_depth_unknown) 673 depth = svn_depth_infinity; 674 |
618 if (kind1 == svn_node_file) 619 SVN_ERR(do_arbitrary_files_diff(local_abspath1, local_abspath2, 620 svn_dirent_basename(local_abspath1, 621 scratch_pool), 622 FALSE, FALSE, NULL, 623 callbacks, diff_baton, 624 ctx, scratch_pool)); 625 else if (kind1 == svn_node_dir) 626 SVN_ERR(do_arbitrary_dirs_diff(local_abspath1, local_abspath2, 627 NULL, NULL, depth, 628 callbacks, diff_baton, 629 ctx, scratch_pool)); | 675 if (left_kind == svn_node_dir && right_kind == svn_node_dir) 676 { 677 left_root_abspath = left_abspath; 678 right_root_abspath = right_abspath; 679 680 if (root_relpath) 681 *root_relpath = ""; 682 if (root_is_dir) 683 *root_is_dir = TRUE; 684 } |
630 else | 685 else |
686 { 687 svn_dirent_split(&left_root_abspath, root_relpath, left_abspath, 688 scratch_pool); 689 right_root_abspath = svn_dirent_dirname(right_abspath, scratch_pool); 690 691 if (root_relpath) 692 *root_relpath = apr_pstrdup(result_pool, *root_relpath); 693 if (root_is_dir) 694 *root_is_dir = FALSE; 695 } 696 697 if (left_kind == svn_node_dir && right_kind == svn_node_dir) 698 { 699 SVN_ERR(do_dir_diff(left_abspath, right_abspath, 700 left_root_abspath, right_root_abspath, 701 FALSE, FALSE, left_before_right, 702 depth, NULL /* parent_baton */, 703 diff_processor, ctx, scratch_pool)); 704 } 705 else if (left_kind == svn_node_file && right_kind == svn_node_file) 706 { 707 SVN_ERR(do_file_diff(left_abspath, right_abspath, 708 left_root_abspath, right_root_abspath, 709 FALSE, FALSE, 710 NULL /* parent_baton */, 711 diff_processor, ctx, scratch_pool)); 712 } 713 else if (left_kind == svn_node_file || left_kind == svn_node_dir 714 || right_kind == svn_node_file || right_kind == svn_node_dir) 715 { 716 void *dir_baton; 717 svn_boolean_t skip = FALSE; 718 svn_boolean_t skip_children = FALSE; 719 svn_diff_source_t *left_src; 720 svn_diff_source_t *right_src; 721 722 left_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 723 right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); 724 725 /* The root is replaced... */ 726 /* Report delete and/or add */ 727 728 SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, "", 729 left_src, 730 right_src, 731 NULL /* copyfrom_src */, 732 NULL, 733 diff_processor, 734 scratch_pool, scratch_pool)); 735 736 if (skip) 737 return SVN_NO_ERROR; 738 else if (!skip_children) 739 { 740 if (left_before_right) 741 { 742 if (left_kind == svn_node_file) 743 SVN_ERR(do_file_diff(left_abspath, right_abspath, 744 left_root_abspath, right_root_abspath, 745 TRUE, FALSE, NULL /* parent_baton */, 746 diff_processor, ctx, scratch_pool)); 747 else if (left_kind == svn_node_dir) 748 SVN_ERR(do_dir_diff(left_abspath, right_abspath, 749 left_root_abspath, right_root_abspath, 750 TRUE, FALSE, left_before_right, 751 depth, NULL /* parent_baton */, 752 diff_processor, ctx, scratch_pool)); 753 } 754 755 if (right_kind == svn_node_file) 756 SVN_ERR(do_file_diff(left_abspath, right_abspath, 757 left_root_abspath, right_root_abspath, 758 FALSE, TRUE, NULL /* parent_baton */, 759 diff_processor, ctx, scratch_pool)); 760 else if (right_kind == svn_node_dir) 761 SVN_ERR(do_dir_diff(left_abspath, right_abspath, 762 left_root_abspath, right_root_abspath, 763 FALSE, TRUE, left_before_right, 764 depth, NULL /* parent_baton */, 765 diff_processor, ctx, scratch_pool)); 766 767 if (! left_before_right) 768 { 769 if (left_kind == svn_node_file) 770 SVN_ERR(do_file_diff(left_abspath, right_abspath, 771 left_root_abspath, right_root_abspath, 772 TRUE, FALSE, NULL /* parent_baton */, 773 diff_processor, ctx, scratch_pool)); 774 else if (left_kind == svn_node_dir) 775 SVN_ERR(do_dir_diff(left_abspath, right_abspath, 776 left_root_abspath, right_root_abspath, 777 TRUE, FALSE, left_before_right, 778 depth, NULL /* parent_baton */, 779 diff_processor, ctx, scratch_pool)); 780 } 781 } 782 783 SVN_ERR(diff_processor->dir_closed("", 784 left_src, 785 right_src, 786 dir_baton, 787 diff_processor, 788 scratch_pool)); 789 } 790 else |
|
631 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 632 _("'%s' is not a file or directory"), | 791 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 792 _("'%s' is not a file or directory"), |
633 kind1 == svn_node_none 634 ? svn_dirent_local_style(local_abspath1, 635 scratch_pool) 636 : svn_dirent_local_style(local_abspath2, 637 scratch_pool)); | 793 svn_dirent_local_style( 794 (left_kind == svn_node_none) 795 ? left_abspath 796 : right_abspath, 797 scratch_pool)); 798 |
638 return SVN_NO_ERROR; 639} | 799 return SVN_NO_ERROR; 800} |