diff_local.c revision 272461
1168404Spjd/* 2168404Spjd * diff_pristine.c -- A simple diff walker which compares local files against 3168404Spjd * their pristine versions. 4168404Spjd * 5185029Spjd * ==================================================================== 6185029Spjd * Licensed to the Apache Software Foundation (ASF) under one 7168404Spjd * or more contributor license agreements. See the NOTICE file 8168404Spjd * distributed with this work for additional information 9168404Spjd * regarding copyright ownership. The ASF licenses this file 10168404Spjd * to you under the Apache License, Version 2.0 (the 11168404Spjd * "License"); you may not use this file except in compliance 12168404Spjd * with the License. You may obtain a copy of the License at 13168404Spjd * 14168404Spjd * http://www.apache.org/licenses/LICENSE-2.0 15168404Spjd * 16168404Spjd * Unless required by applicable law or agreed to in writing, 17168404Spjd * software distributed under the License is distributed on an 18168404Spjd * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19168404Spjd * KIND, either express or implied. See the License for the 20168404Spjd * specific language governing permissions and limitations 21168404Spjd * under the License. 22168404Spjd * ==================================================================== 23168404Spjd * 24168404Spjd * This is the simple working copy diff algorithm which is used when you 25168404Spjd * just use 'svn diff PATH'. It shows what is modified in your working copy 26185029Spjd * since a node was checked out or copied but doesn't show most kinds of 27168404Spjd * restructuring operations. 28168404Spjd * 29168404Spjd * You can look at this as another form of the status walker. 30168404Spjd */ 31168404Spjd 32168404Spjd#include <apr_hash.h> 33168404Spjd 34219089Spjd#include "svn_error.h" 35168404Spjd#include "svn_pools.h" 36168404Spjd#include "svn_dirent_uri.h" 37168404Spjd#include "svn_path.h" 38168404Spjd#include "svn_hash.h" 39168404Spjd 40168404Spjd#include "private/svn_wc_private.h" 41168404Spjd#include "private/svn_diff_tree.h" 42168404Spjd 43168404Spjd#include "wc.h" 44168404Spjd#include "props.h" 45168404Spjd#include "translate.h" 46168404Spjd#include "diff.h" 47168404Spjd 48168404Spjd#include "svn_private_config.h" 49168404Spjd 50168404Spjd/*-------------------------------------------------------------------------*/ 51168404Spjd 52168404Spjd/* Baton containing the state of a directory 53168404Spjd reported open via a diff processor */ 54168404Spjdstruct node_state_t 55168404Spjd{ 56168404Spjd struct node_state_t *parent; 57168404Spjd 58168404Spjd apr_pool_t *pool; 59168404Spjd 60168404Spjd const char *local_abspath; 61219089Spjd const char *relpath; 62219089Spjd void *baton; 63219089Spjd 64168404Spjd svn_diff_source_t *left_src; 65168404Spjd svn_diff_source_t *right_src; 66168404Spjd svn_diff_source_t *copy_src; 67168404Spjd 68168404Spjd svn_boolean_t skip; 69168404Spjd svn_boolean_t skip_children; 70168404Spjd 71168404Spjd apr_hash_t *left_props; 72168404Spjd apr_hash_t *right_props; 73168404Spjd const apr_array_header_t *propchanges; 74168404Spjd}; 75168404Spjd 76168404Spjd/* The diff baton */ 77168404Spjdstruct diff_baton 78168404Spjd{ 79168404Spjd /* A wc db. */ 80168404Spjd svn_wc__db_t *db; 81168404Spjd 82168404Spjd /* Report editor paths relative from this directory */ 83168404Spjd const char *anchor_abspath; 84168404Spjd 85168404Spjd struct node_state_t *cur; 86168404Spjd 87168404Spjd const svn_diff_tree_processor_t *processor; 88168404Spjd 89168404Spjd /* Should this diff ignore node ancestry? */ 90168404Spjd svn_boolean_t ignore_ancestry; 91168404Spjd 92168404Spjd /* Should this diff not compare copied files with their source? */ 93168404Spjd svn_boolean_t show_copies_as_adds; 94168404Spjd 95168404Spjd /* Hash whose keys are const char * changelist names. */ 96168404Spjd apr_hash_t *changelist_hash; 97168404Spjd 98168404Spjd /* Cancel function/baton */ 99168404Spjd svn_cancel_func_t cancel_func; 100168404Spjd void *cancel_baton; 101168404Spjd 102168404Spjd apr_pool_t *pool; 103168404Spjd}; 104168404Spjd 105168404Spjd/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH 106168404Spjd is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself, 107168404Spjd but create it marked with skip+skip_children. 108168404Spjd */ 109168404Spjdstatic svn_error_t * 110168404Spjdensure_state(struct diff_baton *eb, 111168404Spjd const char *local_abspath, 112168404Spjd svn_boolean_t recursive_skip, 113168404Spjd apr_pool_t *scratch_pool) 114168404Spjd{ 115219089Spjd struct node_state_t *ns; 116168404Spjd apr_pool_t *ns_pool; 117168404Spjd if (!eb->cur) 118168404Spjd { 119168404Spjd const char *relpath; 120168404Spjd 121168404Spjd relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath); 122168404Spjd if (! relpath) 123168404Spjd return SVN_NO_ERROR; 124168404Spjd 125168404Spjd /* Don't recurse on the anchor, as that might loop infinately because 126168404Spjd svn_dirent_dirname("/",...) -> "/" 127168404Spjd svn_dirent_dirname("C:/",...) -> "C:/" (Windows) */ 128168404Spjd if (*relpath) 129168404Spjd SVN_ERR(ensure_state(eb, 130168404Spjd svn_dirent_dirname(local_abspath,scratch_pool), 131168404Spjd FALSE, 132168404Spjd scratch_pool)); 133168404Spjd } 134168404Spjd else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL)) 135168404Spjd SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), 136168404Spjd FALSE, 137168404Spjd scratch_pool)); 138168404Spjd else 139168404Spjd return SVN_NO_ERROR; 140168404Spjd 141168404Spjd if (eb->cur && eb->cur->skip_children) 142168404Spjd return SVN_NO_ERROR; 143168404Spjd 144168404Spjd ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool); 145168404Spjd ns = apr_pcalloc(ns_pool, sizeof(*ns)); 146168404Spjd 147168404Spjd ns->pool = ns_pool; 148168404Spjd ns->local_abspath = apr_pstrdup(ns_pool, local_abspath); 149168404Spjd ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath); 150168404Spjd ns->parent = eb->cur; 151168404Spjd eb->cur = ns; 152168404Spjd 153168404Spjd if (recursive_skip) 154168404Spjd { 155168404Spjd ns->skip = TRUE; 156168404Spjd ns->skip_children = TRUE; 157168404Spjd return SVN_NO_ERROR; 158168404Spjd } 159168404Spjd 160168404Spjd { 161168404Spjd svn_revnum_t revision; 162168404Spjd svn_error_t *err; 163168404Spjd 164168404Spjd err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL, 165168404Spjd NULL, NULL, NULL, NULL, NULL, NULL, NULL, 166168404Spjd NULL, NULL, NULL, 167168404Spjd eb->db, local_abspath, 168168404Spjd scratch_pool, scratch_pool); 169168404Spjd 170168404Spjd if (err) 171168404Spjd { 172168404Spjd if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 173168404Spjd return svn_error_trace(err); 174168404Spjd svn_error_clear(err); 175174049Sjb 176174049Sjb revision = 0; /* Use original revision? */ 177168404Spjd } 178168404Spjd ns->left_src = svn_diff__source_create(revision, ns->pool); 179168404Spjd ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool); 180168404Spjd 181168404Spjd SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip, 182168404Spjd &ns->skip_children, 183168404Spjd ns->relpath, 184168404Spjd ns->left_src, 185168404Spjd ns->right_src, 186168404Spjd NULL /* copyfrom_source */, 187168404Spjd ns->parent ? ns->parent->baton : NULL, 188168404Spjd eb->processor, 189168404Spjd ns->pool, scratch_pool)); 190168404Spjd } 191168404Spjd 192168404Spjd return SVN_NO_ERROR; 193168404Spjd} 194168404Spjd 195168404Spjd/* Implements svn_wc_status_func3_t */ 196168404Spjdstatic svn_error_t * 197168404Spjddiff_status_callback(void *baton, 198168404Spjd const char *local_abspath, 199168404Spjd const svn_wc_status3_t *status, 200168404Spjd apr_pool_t *scratch_pool) 201168404Spjd{ 202168404Spjd struct diff_baton *eb = baton; 203168404Spjd svn_wc__db_t *db = eb->db; 204168404Spjd 205168404Spjd if (! status->versioned) 206168404Spjd return SVN_NO_ERROR; /* unversioned (includes dir externals) */ 207168404Spjd 208168404Spjd if (status->node_status == svn_wc_status_conflicted 209168404Spjd && status->text_status == svn_wc_status_none 210168404Spjd && status->prop_status == svn_wc_status_none) 211168404Spjd { 212168404Spjd /* Node is an actual only node describing a tree conflict */ 213168404Spjd return SVN_NO_ERROR; 214168404Spjd } 215168404Spjd 216168404Spjd /* Not text/prop modified, not copied. Easy out */ 217168404Spjd if (status->node_status == svn_wc_status_normal && !status->copied) 218168404Spjd return SVN_NO_ERROR; 219168404Spjd 220168404Spjd /* Mark all directories where we are no longer inside as closed */ 221168404Spjd while (eb->cur 222168404Spjd && !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath)) 223168404Spjd { 224168404Spjd struct node_state_t *ns = eb->cur; 225168404Spjd 226168404Spjd if (!ns->skip) 227168404Spjd { 228168404Spjd if (ns->propchanges) 229185029Spjd SVN_ERR(eb->processor->dir_changed(ns->relpath, 230185029Spjd ns->left_src, 231168404Spjd ns->right_src, 232185029Spjd ns->left_props, 233185029Spjd ns->right_props, 234185029Spjd ns->propchanges, 235185029Spjd ns->baton, 236185029Spjd eb->processor, 237185029Spjd ns->pool)); 238185029Spjd else 239185029Spjd SVN_ERR(eb->processor->dir_closed(ns->relpath, 240168404Spjd ns->left_src, 241185029Spjd ns->right_src, 242185029Spjd ns->baton, 243185029Spjd eb->processor, 244185029Spjd ns->pool)); 245185029Spjd } 246185029Spjd eb->cur = ns->parent; 247168404Spjd svn_pool_clear(ns->pool); 248185029Spjd } 249185029Spjd SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool), 250185029Spjd FALSE, scratch_pool)); 251185029Spjd 252185029Spjd if (eb->cur && eb->cur->skip_children) 253185029Spjd return SVN_NO_ERROR; 254185029Spjd 255168404Spjd if (eb->changelist_hash != NULL 256185029Spjd && (!status->changelist 257185029Spjd || ! svn_hash_gets(eb->changelist_hash, status->changelist))) 258185029Spjd return SVN_NO_ERROR; /* Filtered via changelist */ 259185029Spjd 260185029Spjd /* This code does about the same thing as the inner body of 261185029Spjd walk_local_nodes_diff() in diff_editor.c, except that 262168404Spjd it is already filtered by the status walker, doesn't have to 263185029Spjd account for remote changes (and many tiny other details) */ 264185029Spjd 265185029Spjd { 266185029Spjd svn_boolean_t repos_only; 267185029Spjd svn_boolean_t local_only; 268185029Spjd svn_wc__db_status_t db_status; 269168404Spjd svn_boolean_t have_base; 270185029Spjd svn_node_kind_t base_kind; 271185029Spjd svn_node_kind_t db_kind = status->kind; 272185029Spjd svn_depth_t depth_below_here = svn_depth_unknown; 273185029Spjd 274185029Spjd const char *child_abspath = local_abspath; 275185029Spjd const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, 276185029Spjd local_abspath); 277168404Spjd 278185029Spjd 279168404Spjd repos_only = FALSE; 280185029Spjd local_only = FALSE; 281185029Spjd 282185029Spjd /* ### optimize away this call using status info. Should 283185029Spjd be possible in almost every case (except conflict, missing, obst.)*/ 284168404Spjd SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL, 285185029Spjd NULL, NULL, NULL, NULL, NULL, NULL, NULL, 286185029Spjd NULL, NULL, NULL, NULL, NULL, NULL, NULL, 287185029Spjd NULL, NULL, NULL, NULL, 288185029Spjd &have_base, NULL, NULL, 289185029Spjd eb->db, local_abspath, 290185029Spjd scratch_pool, scratch_pool)); 291185029Spjd if (!have_base) 292185029Spjd { 293168404Spjd local_only = TRUE; /* Only report additions */ 294168404Spjd } 295168404Spjd else if (db_status == svn_wc__db_status_normal) 296168404Spjd { 297168404Spjd /* Simple diff */ 298168404Spjd base_kind = db_kind; 299168404Spjd } 300168404Spjd else if (db_status == svn_wc__db_status_deleted) 301168404Spjd { 302168404Spjd svn_wc__db_status_t base_status; 303168404Spjd repos_only = TRUE; 304168404Spjd SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, 305168404Spjd NULL, NULL, NULL, NULL, NULL, 306168404Spjd NULL, NULL, NULL, NULL, NULL, 307168404Spjd NULL, NULL, NULL, 308168404Spjd eb->db, local_abspath, 309168404Spjd scratch_pool, scratch_pool)); 310168404Spjd 311168404Spjd if (base_status != svn_wc__db_status_normal) 312168404Spjd return SVN_NO_ERROR; 313168404Spjd } 314168404Spjd else 315168404Spjd { 316168404Spjd /* working status is either added or deleted */ 317168404Spjd svn_wc__db_status_t base_status; 318168404Spjd 319168404Spjd SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, 320168404Spjd NULL, NULL, NULL, NULL, NULL, 321168404Spjd NULL, NULL, NULL, NULL, NULL, 322168404Spjd NULL, NULL, NULL, 323168404Spjd eb->db, local_abspath, 324168404Spjd scratch_pool, scratch_pool)); 325168404Spjd 326168404Spjd if (base_status != svn_wc__db_status_normal) 327168404Spjd local_only = TRUE; 328168404Spjd else if (base_kind != db_kind || !eb->ignore_ancestry) 329168404Spjd { 330185029Spjd repos_only = TRUE; 331185029Spjd local_only = TRUE; 332185029Spjd } 333185029Spjd } 334185029Spjd 335185029Spjd if (repos_only) 336185029Spjd { 337185029Spjd /* Report repository form deleted */ 338185029Spjd if (base_kind == svn_node_file) 339185029Spjd SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, 340185029Spjd child_relpath, 341185029Spjd SVN_INVALID_REVNUM, 342185029Spjd eb->processor, 343185029Spjd eb->cur ? eb->cur->baton : NULL, 344185029Spjd scratch_pool)); 345185029Spjd else if (base_kind == svn_node_dir) 346185029Spjd SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, 347185029Spjd child_relpath, 348185029Spjd SVN_INVALID_REVNUM, 349185029Spjd depth_below_here, 350185029Spjd eb->processor, 351185029Spjd eb->cur ? eb->cur->baton : NULL, 352185029Spjd eb->cancel_func, 353185029Spjd eb->cancel_baton, 354185029Spjd scratch_pool)); 355185029Spjd } 356185029Spjd else if (!local_only) 357185029Spjd { 358185029Spjd /* Diff base against actual */ 359185029Spjd if (db_kind == svn_node_file) 360185029Spjd { 361185029Spjd SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath, 362185029Spjd child_relpath, 363185029Spjd SVN_INVALID_REVNUM, 364185029Spjd eb->changelist_hash, 365185029Spjd eb->processor, 366185029Spjd eb->cur 367185029Spjd ? eb->cur->baton 368185029Spjd : NULL, 369185029Spjd FALSE, 370168404Spjd eb->cancel_func, 371168404Spjd eb->cancel_baton, 372168404Spjd scratch_pool)); 373168404Spjd } 374168404Spjd else if (db_kind == svn_node_dir) 375168404Spjd { 376168404Spjd SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool)); 377219089Spjd 378219089Spjd if (status->prop_status != svn_wc_status_none 379219089Spjd && status->prop_status != svn_wc_status_normal) 380219089Spjd { 381219089Spjd apr_array_header_t *propchanges; 382219089Spjd SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props, 383219089Spjd eb->db, local_abspath, 384219089Spjd eb->cur->pool, 385219089Spjd scratch_pool)); 386219089Spjd SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props, 387219089Spjd eb->db, local_abspath, 388219089Spjd eb->cur->pool, 389219089Spjd scratch_pool)); 390219089Spjd 391219089Spjd SVN_ERR(svn_prop_diffs(&propchanges, 392219089Spjd eb->cur->right_props, 393219089Spjd eb->cur->left_props, 394219089Spjd eb->cur->pool)); 395219089Spjd 396219089Spjd eb->cur->propchanges = propchanges; 397219089Spjd } 398219089Spjd } 399219089Spjd } 400219089Spjd 401219089Spjd if (local_only && (db_status != svn_wc__db_status_deleted)) 402219089Spjd { 403219089Spjd if (db_kind == svn_node_file) 404219089Spjd SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, 405219089Spjd child_relpath, 406219089Spjd eb->processor, 407219089Spjd eb->cur ? eb->cur->baton : NULL, 408219089Spjd eb->changelist_hash, 409219089Spjd FALSE, 410219089Spjd eb->cancel_func, 411219089Spjd eb->cancel_baton, 412265751Sdelphij scratch_pool)); 413265751Sdelphij else if (db_kind == svn_node_dir) 414265751Sdelphij SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, 415265751Sdelphij child_relpath, depth_below_here, 416265751Sdelphij eb->processor, 417265751Sdelphij eb->cur ? eb->cur->baton : NULL, 418265751Sdelphij eb->changelist_hash, 419265751Sdelphij FALSE, 420265751Sdelphij eb->cancel_func, 421265751Sdelphij eb->cancel_baton, 422265751Sdelphij scratch_pool)); 423265751Sdelphij } 424265751Sdelphij 425265751Sdelphij if (db_kind == svn_node_dir && (local_only || repos_only)) 426265751Sdelphij SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool)); 427265751Sdelphij } 428265751Sdelphij 429265751Sdelphij return SVN_NO_ERROR; 430265751Sdelphij} 431265751Sdelphij 432265751Sdelphij 433265751Sdelphij/* Public Interface */ 434265751Sdelphijsvn_error_t * 435265751Sdelphijsvn_wc_diff6(svn_wc_context_t *wc_ctx, 436265751Sdelphij const char *local_abspath, 437265751Sdelphij const svn_wc_diff_callbacks4_t *callbacks, 438265751Sdelphij void *callback_baton, 439265751Sdelphij svn_depth_t depth, 440265751Sdelphij svn_boolean_t ignore_ancestry, 441265751Sdelphij svn_boolean_t show_copies_as_adds, 442265751Sdelphij svn_boolean_t use_git_diff_format, 443265751Sdelphij const apr_array_header_t *changelist_filter, 444168404Spjd svn_cancel_func_t cancel_func, 445168404Spjd void *cancel_baton, 446168404Spjd apr_pool_t *scratch_pool) 447168404Spjd{ 448168404Spjd struct diff_baton eb = { 0 }; 449 svn_node_kind_t kind; 450 svn_boolean_t get_all; 451 const svn_diff_tree_processor_t *processor; 452 453 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 454 SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath, 455 FALSE /* allow_missing */, 456 TRUE /* show_deleted */, 457 FALSE /* show_hidden */, 458 scratch_pool)); 459 460 if (kind == svn_node_dir) 461 eb.anchor_abspath = local_abspath; 462 else 463 eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 464 465 SVN_ERR(svn_wc__wrap_diff_callbacks(&processor, 466 callbacks, callback_baton, TRUE, 467 scratch_pool, scratch_pool)); 468 469 if (use_git_diff_format) 470 show_copies_as_adds = TRUE; 471 if (show_copies_as_adds) 472 ignore_ancestry = FALSE; 473 474 475 476 /* 477 if (reverse_order) 478 processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool); 479 */ 480 481 if (! show_copies_as_adds && !use_git_diff_format) 482 processor = svn_diff__tree_processor_copy_as_changed_create(processor, 483 scratch_pool); 484 485 eb.db = wc_ctx->db; 486 eb.processor = processor; 487 eb.ignore_ancestry = ignore_ancestry; 488 eb.show_copies_as_adds = show_copies_as_adds; 489 eb.pool = scratch_pool; 490 491 if (changelist_filter && changelist_filter->nelts) 492 SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter, 493 scratch_pool)); 494 495 if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry) 496 get_all = TRUE; /* We need unmodified descendants of copies */ 497 else 498 get_all = FALSE; 499 500 /* Walk status handles files and directories */ 501 SVN_ERR(svn_wc__internal_walk_status(wc_ctx->db, local_abspath, depth, 502 get_all, 503 TRUE /* no_ignore */, 504 FALSE /* ignore_text_mods */, 505 NULL /* ignore_patterns */, 506 diff_status_callback, &eb, 507 cancel_func, cancel_baton, 508 scratch_pool)); 509 510 /* Close the remaining open directories */ 511 while (eb.cur) 512 { 513 struct node_state_t *ns = eb.cur; 514 515 if (!ns->skip) 516 { 517 if (ns->propchanges) 518 SVN_ERR(processor->dir_changed(ns->relpath, 519 ns->left_src, 520 ns->right_src, 521 ns->left_props, 522 ns->right_props, 523 ns->propchanges, 524 ns->baton, 525 processor, 526 ns->pool)); 527 else 528 SVN_ERR(processor->dir_closed(ns->relpath, 529 ns->left_src, 530 ns->right_src, 531 ns->baton, 532 processor, 533 ns->pool)); 534 } 535 eb.cur = ns->parent; 536 svn_pool_clear(ns->pool); 537 } 538 539 return SVN_NO_ERROR; 540} 541