1251881Speter/* 2251881Speter * diff_pristine.c -- A simple diff walker which compares local files against 3251881Speter * their pristine versions. 4251881Speter * 5251881Speter * ==================================================================== 6251881Speter * Licensed to the Apache Software Foundation (ASF) under one 7251881Speter * or more contributor license agreements. See the NOTICE file 8251881Speter * distributed with this work for additional information 9251881Speter * regarding copyright ownership. The ASF licenses this file 10251881Speter * to you under the Apache License, Version 2.0 (the 11251881Speter * "License"); you may not use this file except in compliance 12251881Speter * with the License. You may obtain a copy of the License at 13251881Speter * 14251881Speter * http://www.apache.org/licenses/LICENSE-2.0 15251881Speter * 16251881Speter * Unless required by applicable law or agreed to in writing, 17251881Speter * software distributed under the License is distributed on an 18251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19251881Speter * KIND, either express or implied. See the License for the 20251881Speter * specific language governing permissions and limitations 21251881Speter * under the License. 22251881Speter * ==================================================================== 23251881Speter * 24251881Speter * This is the simple working copy diff algorithm which is used when you 25251881Speter * just use 'svn diff PATH'. It shows what is modified in your working copy 26251881Speter * since a node was checked out or copied but doesn't show most kinds of 27251881Speter * restructuring operations. 28251881Speter * 29251881Speter * You can look at this as another form of the status walker. 30251881Speter */ 31251881Speter 32251881Speter#include <apr_hash.h> 33251881Speter 34251881Speter#include "svn_error.h" 35251881Speter#include "svn_pools.h" 36251881Speter#include "svn_dirent_uri.h" 37251881Speter#include "svn_path.h" 38251881Speter#include "svn_hash.h" 39251881Speter 40251881Speter#include "private/svn_wc_private.h" 41251881Speter#include "private/svn_diff_tree.h" 42251881Speter 43251881Speter#include "wc.h" 44251881Speter#include "props.h" 45251881Speter#include "translate.h" 46251881Speter#include "diff.h" 47251881Speter 48251881Speter#include "svn_private_config.h" 49251881Speter 50251881Speter/*-------------------------------------------------------------------------*/ 51251881Speter 52251881Speter/* Baton containing the state of a directory 53251881Speter reported open via a diff processor */ 54251881Speterstruct node_state_t 55251881Speter{ 56251881Speter struct node_state_t *parent; 57251881Speter 58251881Speter apr_pool_t *pool; 59251881Speter 60251881Speter const char *local_abspath; 61251881Speter const char *relpath; 62251881Speter void *baton; 63251881Speter 64251881Speter svn_diff_source_t *left_src; 65251881Speter svn_diff_source_t *right_src; 66251881Speter svn_diff_source_t *copy_src; 67251881Speter 68251881Speter svn_boolean_t skip; 69251881Speter svn_boolean_t skip_children; 70251881Speter 71251881Speter apr_hash_t *left_props; 72251881Speter apr_hash_t *right_props; 73251881Speter const apr_array_header_t *propchanges; 74251881Speter}; 75251881Speter 76251881Speter/* The diff baton */ 77251881Speterstruct diff_baton 78251881Speter{ 79251881Speter /* A wc db. */ 80251881Speter svn_wc__db_t *db; 81251881Speter 82251881Speter /* Report editor paths relative from this directory */ 83251881Speter const char *anchor_abspath; 84251881Speter 85251881Speter struct node_state_t *cur; 86251881Speter 87251881Speter const svn_diff_tree_processor_t *processor; 88251881Speter 89251881Speter /* Should this diff ignore node ancestry? */ 90251881Speter svn_boolean_t ignore_ancestry; 91251881Speter 92251881Speter /* Should this diff not compare copied files with their source? */ 93251881Speter svn_boolean_t show_copies_as_adds; 94251881Speter 95251881Speter /* Hash whose keys are const char * changelist names. */ 96251881Speter apr_hash_t *changelist_hash; 97251881Speter 98251881Speter /* Cancel function/baton */ 99251881Speter svn_cancel_func_t cancel_func; 100251881Speter void *cancel_baton; 101251881Speter 102251881Speter apr_pool_t *pool; 103251881Speter}; 104251881Speter 105251881Speter/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH 106251881Speter is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself, 107251881Speter but create it marked with skip+skip_children. 108251881Speter */ 109251881Speterstatic svn_error_t * 110251881Speterensure_state(struct diff_baton *eb, 111251881Speter const char *local_abspath, 112251881Speter svn_boolean_t recursive_skip, 113251881Speter apr_pool_t *scratch_pool) 114251881Speter{ 115251881Speter struct node_state_t *ns; 116251881Speter apr_pool_t *ns_pool; 117251881Speter if (!eb->cur) 118251881Speter { 119262253Speter const char *relpath; 120262253Speter 121262253Speter relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath); 122262253Speter if (! relpath) 123251881Speter return SVN_NO_ERROR; 124251881Speter 125262253Speter /* Don't recurse on the anchor, as that might loop infinately because 126262253Speter svn_dirent_dirname("/",...) -> "/" 127262253Speter svn_dirent_dirname("C:/",...) -> "C:/" (Windows) */ 128262253Speter if (*relpath) 129262253Speter SVN_ERR(ensure_state(eb, 130262253Speter svn_dirent_dirname(local_abspath,scratch_pool), 131262253Speter FALSE, 132262253Speter scratch_pool)); 133251881Speter } 134251881Speter else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL)) 135251881Speter SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), 136251881Speter FALSE, 137251881Speter scratch_pool)); 138251881Speter else 139251881Speter return SVN_NO_ERROR; 140251881Speter 141251881Speter if (eb->cur && eb->cur->skip_children) 142251881Speter return SVN_NO_ERROR; 143251881Speter 144251881Speter ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool); 145251881Speter ns = apr_pcalloc(ns_pool, sizeof(*ns)); 146251881Speter 147251881Speter ns->pool = ns_pool; 148251881Speter ns->local_abspath = apr_pstrdup(ns_pool, local_abspath); 149251881Speter ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath); 150251881Speter ns->parent = eb->cur; 151251881Speter eb->cur = ns; 152251881Speter 153251881Speter if (recursive_skip) 154251881Speter { 155251881Speter ns->skip = TRUE; 156251881Speter ns->skip_children = TRUE; 157251881Speter return SVN_NO_ERROR; 158251881Speter } 159251881Speter 160251881Speter { 161251881Speter svn_revnum_t revision; 162251881Speter svn_error_t *err; 163251881Speter 164251881Speter err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL, 165251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 166251881Speter NULL, NULL, NULL, 167251881Speter eb->db, local_abspath, 168251881Speter scratch_pool, scratch_pool); 169251881Speter 170251881Speter if (err) 171251881Speter { 172251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 173251881Speter return svn_error_trace(err); 174251881Speter svn_error_clear(err); 175251881Speter 176251881Speter revision = 0; /* Use original revision? */ 177251881Speter } 178251881Speter ns->left_src = svn_diff__source_create(revision, ns->pool); 179251881Speter ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool); 180251881Speter 181251881Speter SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip, 182251881Speter &ns->skip_children, 183251881Speter ns->relpath, 184251881Speter ns->left_src, 185251881Speter ns->right_src, 186251881Speter NULL /* copyfrom_source */, 187251881Speter ns->parent ? ns->parent->baton : NULL, 188251881Speter eb->processor, 189251881Speter ns->pool, scratch_pool)); 190251881Speter } 191251881Speter 192251881Speter return SVN_NO_ERROR; 193251881Speter} 194251881Speter 195251881Speter/* Implements svn_wc_status_func3_t */ 196251881Speterstatic svn_error_t * 197251881Speterdiff_status_callback(void *baton, 198251881Speter const char *local_abspath, 199251881Speter const svn_wc_status3_t *status, 200251881Speter apr_pool_t *scratch_pool) 201251881Speter{ 202251881Speter struct diff_baton *eb = baton; 203251881Speter svn_wc__db_t *db = eb->db; 204251881Speter 205262253Speter if (! status->versioned) 206262253Speter return SVN_NO_ERROR; /* unversioned (includes dir externals) */ 207262253Speter 208262253Speter if (status->node_status == svn_wc_status_conflicted 209262253Speter && status->text_status == svn_wc_status_none 210262253Speter && status->prop_status == svn_wc_status_none) 211251881Speter { 212262253Speter /* Node is an actual only node describing a tree conflict */ 213262253Speter return SVN_NO_ERROR; 214251881Speter } 215251881Speter 216251881Speter /* Not text/prop modified, not copied. Easy out */ 217251881Speter if (status->node_status == svn_wc_status_normal && !status->copied) 218251881Speter return SVN_NO_ERROR; 219251881Speter 220251881Speter /* Mark all directories where we are no longer inside as closed */ 221251881Speter while (eb->cur 222251881Speter && !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath)) 223251881Speter { 224251881Speter struct node_state_t *ns = eb->cur; 225251881Speter 226251881Speter if (!ns->skip) 227251881Speter { 228251881Speter if (ns->propchanges) 229251881Speter SVN_ERR(eb->processor->dir_changed(ns->relpath, 230251881Speter ns->left_src, 231251881Speter ns->right_src, 232251881Speter ns->left_props, 233251881Speter ns->right_props, 234251881Speter ns->propchanges, 235251881Speter ns->baton, 236251881Speter eb->processor, 237251881Speter ns->pool)); 238251881Speter else 239251881Speter SVN_ERR(eb->processor->dir_closed(ns->relpath, 240251881Speter ns->left_src, 241251881Speter ns->right_src, 242251881Speter ns->baton, 243251881Speter eb->processor, 244251881Speter ns->pool)); 245251881Speter } 246251881Speter eb->cur = ns->parent; 247251881Speter svn_pool_clear(ns->pool); 248251881Speter } 249251881Speter SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool), 250251881Speter FALSE, scratch_pool)); 251251881Speter 252251881Speter if (eb->cur && eb->cur->skip_children) 253251881Speter return SVN_NO_ERROR; 254251881Speter 255251881Speter if (eb->changelist_hash != NULL 256251881Speter && (!status->changelist 257251881Speter || ! svn_hash_gets(eb->changelist_hash, status->changelist))) 258251881Speter return SVN_NO_ERROR; /* Filtered via changelist */ 259251881Speter 260251881Speter /* This code does about the same thing as the inner body of 261251881Speter walk_local_nodes_diff() in diff_editor.c, except that 262251881Speter it is already filtered by the status walker, doesn't have to 263251881Speter account for remote changes (and many tiny other details) */ 264251881Speter 265251881Speter { 266251881Speter svn_boolean_t repos_only; 267251881Speter svn_boolean_t local_only; 268251881Speter svn_wc__db_status_t db_status; 269251881Speter svn_boolean_t have_base; 270251881Speter svn_node_kind_t base_kind; 271251881Speter svn_node_kind_t db_kind = status->kind; 272251881Speter svn_depth_t depth_below_here = svn_depth_unknown; 273251881Speter 274251881Speter const char *child_abspath = local_abspath; 275251881Speter const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, 276251881Speter local_abspath); 277251881Speter 278251881Speter 279251881Speter repos_only = FALSE; 280251881Speter local_only = FALSE; 281251881Speter 282251881Speter /* ### optimize away this call using status info. Should 283251881Speter be possible in almost every case (except conflict, missing, obst.)*/ 284251881Speter SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL, 285251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 286251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 287251881Speter NULL, NULL, NULL, NULL, 288251881Speter &have_base, NULL, NULL, 289251881Speter eb->db, local_abspath, 290251881Speter scratch_pool, scratch_pool)); 291251881Speter if (!have_base) 292251881Speter { 293251881Speter local_only = TRUE; /* Only report additions */ 294251881Speter } 295251881Speter else if (db_status == svn_wc__db_status_normal) 296251881Speter { 297251881Speter /* Simple diff */ 298251881Speter base_kind = db_kind; 299251881Speter } 300251881Speter else if (db_status == svn_wc__db_status_deleted) 301251881Speter { 302251881Speter svn_wc__db_status_t base_status; 303251881Speter repos_only = TRUE; 304251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, 305251881Speter NULL, NULL, NULL, NULL, NULL, 306251881Speter NULL, NULL, NULL, NULL, NULL, 307251881Speter NULL, NULL, NULL, 308251881Speter eb->db, local_abspath, 309251881Speter scratch_pool, scratch_pool)); 310251881Speter 311251881Speter if (base_status != svn_wc__db_status_normal) 312251881Speter return SVN_NO_ERROR; 313251881Speter } 314251881Speter else 315251881Speter { 316251881Speter /* working status is either added or deleted */ 317251881Speter svn_wc__db_status_t base_status; 318251881Speter 319251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, 320251881Speter NULL, NULL, NULL, NULL, NULL, 321251881Speter NULL, NULL, NULL, NULL, NULL, 322251881Speter NULL, NULL, NULL, 323251881Speter eb->db, local_abspath, 324251881Speter scratch_pool, scratch_pool)); 325251881Speter 326251881Speter if (base_status != svn_wc__db_status_normal) 327251881Speter local_only = TRUE; 328251881Speter else if (base_kind != db_kind || !eb->ignore_ancestry) 329251881Speter { 330251881Speter repos_only = TRUE; 331251881Speter local_only = TRUE; 332251881Speter } 333251881Speter } 334251881Speter 335251881Speter if (repos_only) 336251881Speter { 337251881Speter /* Report repository form deleted */ 338251881Speter if (base_kind == svn_node_file) 339251881Speter SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, 340251881Speter child_relpath, 341251881Speter SVN_INVALID_REVNUM, 342251881Speter eb->processor, 343251881Speter eb->cur ? eb->cur->baton : NULL, 344251881Speter scratch_pool)); 345251881Speter else if (base_kind == svn_node_dir) 346251881Speter SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, 347251881Speter child_relpath, 348251881Speter SVN_INVALID_REVNUM, 349251881Speter depth_below_here, 350251881Speter eb->processor, 351251881Speter eb->cur ? eb->cur->baton : NULL, 352251881Speter eb->cancel_func, 353251881Speter eb->cancel_baton, 354251881Speter scratch_pool)); 355251881Speter } 356251881Speter else if (!local_only) 357251881Speter { 358251881Speter /* Diff base against actual */ 359251881Speter if (db_kind == svn_node_file) 360251881Speter { 361251881Speter SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath, 362251881Speter child_relpath, 363251881Speter SVN_INVALID_REVNUM, 364251881Speter eb->changelist_hash, 365251881Speter eb->processor, 366251881Speter eb->cur 367251881Speter ? eb->cur->baton 368251881Speter : NULL, 369251881Speter FALSE, 370251881Speter eb->cancel_func, 371251881Speter eb->cancel_baton, 372251881Speter scratch_pool)); 373251881Speter } 374251881Speter else if (db_kind == svn_node_dir) 375251881Speter { 376251881Speter SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool)); 377251881Speter 378251881Speter if (status->prop_status != svn_wc_status_none 379251881Speter && status->prop_status != svn_wc_status_normal) 380251881Speter { 381251881Speter apr_array_header_t *propchanges; 382251881Speter SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props, 383251881Speter eb->db, local_abspath, 384251881Speter eb->cur->pool, 385251881Speter scratch_pool)); 386251881Speter SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props, 387251881Speter eb->db, local_abspath, 388251881Speter eb->cur->pool, 389251881Speter scratch_pool)); 390251881Speter 391251881Speter SVN_ERR(svn_prop_diffs(&propchanges, 392251881Speter eb->cur->right_props, 393251881Speter eb->cur->left_props, 394251881Speter eb->cur->pool)); 395251881Speter 396251881Speter eb->cur->propchanges = propchanges; 397251881Speter } 398251881Speter } 399251881Speter } 400251881Speter 401262253Speter if (local_only && (db_status != svn_wc__db_status_deleted)) 402251881Speter { 403251881Speter if (db_kind == svn_node_file) 404251881Speter SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, 405251881Speter child_relpath, 406251881Speter eb->processor, 407251881Speter eb->cur ? eb->cur->baton : NULL, 408251881Speter eb->changelist_hash, 409251881Speter FALSE, 410251881Speter eb->cancel_func, 411251881Speter eb->cancel_baton, 412251881Speter scratch_pool)); 413251881Speter else if (db_kind == svn_node_dir) 414251881Speter SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, 415251881Speter child_relpath, depth_below_here, 416251881Speter eb->processor, 417251881Speter eb->cur ? eb->cur->baton : NULL, 418251881Speter eb->changelist_hash, 419251881Speter FALSE, 420251881Speter eb->cancel_func, 421251881Speter eb->cancel_baton, 422251881Speter scratch_pool)); 423251881Speter } 424251881Speter 425251881Speter if (db_kind == svn_node_dir && (local_only || repos_only)) 426251881Speter SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool)); 427251881Speter } 428251881Speter 429251881Speter return SVN_NO_ERROR; 430251881Speter} 431251881Speter 432251881Speter 433251881Speter/* Public Interface */ 434251881Spetersvn_error_t * 435251881Spetersvn_wc_diff6(svn_wc_context_t *wc_ctx, 436251881Speter const char *local_abspath, 437251881Speter const svn_wc_diff_callbacks4_t *callbacks, 438251881Speter void *callback_baton, 439251881Speter svn_depth_t depth, 440251881Speter svn_boolean_t ignore_ancestry, 441251881Speter svn_boolean_t show_copies_as_adds, 442251881Speter svn_boolean_t use_git_diff_format, 443251881Speter const apr_array_header_t *changelist_filter, 444251881Speter svn_cancel_func_t cancel_func, 445251881Speter void *cancel_baton, 446251881Speter apr_pool_t *scratch_pool) 447251881Speter{ 448251881Speter struct diff_baton eb = { 0 }; 449251881Speter svn_node_kind_t kind; 450251881Speter svn_boolean_t get_all; 451251881Speter const svn_diff_tree_processor_t *processor; 452251881Speter 453251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 454251881Speter SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath, 455251881Speter FALSE /* allow_missing */, 456251881Speter TRUE /* show_deleted */, 457251881Speter FALSE /* show_hidden */, 458251881Speter scratch_pool)); 459251881Speter 460251881Speter if (kind == svn_node_dir) 461251881Speter eb.anchor_abspath = local_abspath; 462251881Speter else 463251881Speter eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 464251881Speter 465251881Speter SVN_ERR(svn_wc__wrap_diff_callbacks(&processor, 466251881Speter callbacks, callback_baton, TRUE, 467251881Speter scratch_pool, scratch_pool)); 468251881Speter 469251881Speter if (use_git_diff_format) 470251881Speter show_copies_as_adds = TRUE; 471251881Speter if (show_copies_as_adds) 472251881Speter ignore_ancestry = FALSE; 473251881Speter 474251881Speter 475251881Speter 476251881Speter /* 477251881Speter if (reverse_order) 478251881Speter processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool); 479251881Speter */ 480251881Speter 481251881Speter if (! show_copies_as_adds && !use_git_diff_format) 482251881Speter processor = svn_diff__tree_processor_copy_as_changed_create(processor, 483251881Speter scratch_pool); 484251881Speter 485251881Speter eb.db = wc_ctx->db; 486251881Speter eb.processor = processor; 487251881Speter eb.ignore_ancestry = ignore_ancestry; 488251881Speter eb.show_copies_as_adds = show_copies_as_adds; 489251881Speter eb.pool = scratch_pool; 490251881Speter 491251881Speter if (changelist_filter && changelist_filter->nelts) 492251881Speter SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter, 493251881Speter scratch_pool)); 494251881Speter 495251881Speter if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry) 496251881Speter get_all = TRUE; /* We need unmodified descendants of copies */ 497251881Speter else 498251881Speter get_all = FALSE; 499251881Speter 500251881Speter /* Walk status handles files and directories */ 501251881Speter SVN_ERR(svn_wc__internal_walk_status(wc_ctx->db, local_abspath, depth, 502251881Speter get_all, 503251881Speter TRUE /* no_ignore */, 504251881Speter FALSE /* ignore_text_mods */, 505251881Speter NULL /* ignore_patterns */, 506251881Speter diff_status_callback, &eb, 507251881Speter cancel_func, cancel_baton, 508251881Speter scratch_pool)); 509251881Speter 510251881Speter /* Close the remaining open directories */ 511251881Speter while (eb.cur) 512251881Speter { 513251881Speter struct node_state_t *ns = eb.cur; 514251881Speter 515251881Speter if (!ns->skip) 516251881Speter { 517251881Speter if (ns->propchanges) 518251881Speter SVN_ERR(processor->dir_changed(ns->relpath, 519251881Speter ns->left_src, 520251881Speter ns->right_src, 521251881Speter ns->left_props, 522251881Speter ns->right_props, 523251881Speter ns->propchanges, 524251881Speter ns->baton, 525251881Speter processor, 526251881Speter ns->pool)); 527251881Speter else 528251881Speter SVN_ERR(processor->dir_closed(ns->relpath, 529251881Speter ns->left_src, 530251881Speter ns->right_src, 531251881Speter ns->baton, 532251881Speter processor, 533251881Speter ns->pool)); 534251881Speter } 535251881Speter eb.cur = ns->parent; 536251881Speter svn_pool_clear(ns->pool); 537251881Speter } 538251881Speter 539251881Speter return SVN_NO_ERROR; 540251881Speter} 541