1251881Speter/* 2251881Speter * status.c: the command-line's portion of the "svn status" command 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/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter#include "svn_hash.h" 30251881Speter#include "svn_cmdline.h" 31251881Speter#include "svn_wc.h" 32251881Speter#include "svn_dirent_uri.h" 33251881Speter#include "svn_xml.h" 34251881Speter#include "svn_time.h" 35251881Speter#include "cl.h" 36251881Speter#include "svn_private_config.h" 37251881Speter#include "cl-conflicts.h" 38251881Speter#include "private/svn_wc_private.h" 39251881Speter 40251881Speter/* Return the single character representation of STATUS */ 41251881Speterstatic char 42251881Spetergenerate_status_code(enum svn_wc_status_kind status) 43251881Speter{ 44251881Speter switch (status) 45251881Speter { 46251881Speter case svn_wc_status_none: return ' '; 47251881Speter case svn_wc_status_normal: return ' '; 48251881Speter case svn_wc_status_added: return 'A'; 49251881Speter case svn_wc_status_missing: return '!'; 50251881Speter case svn_wc_status_incomplete: return '!'; 51251881Speter case svn_wc_status_deleted: return 'D'; 52251881Speter case svn_wc_status_replaced: return 'R'; 53251881Speter case svn_wc_status_modified: return 'M'; 54251881Speter case svn_wc_status_conflicted: return 'C'; 55251881Speter case svn_wc_status_obstructed: return '~'; 56251881Speter case svn_wc_status_ignored: return 'I'; 57251881Speter case svn_wc_status_external: return 'X'; 58251881Speter case svn_wc_status_unversioned: return '?'; 59251881Speter default: return '?'; 60251881Speter } 61251881Speter} 62251881Speter 63251881Speter/* Return the combined STATUS as shown in 'svn status' based 64251881Speter on the node status and text status */ 65251881Speterstatic enum svn_wc_status_kind 66251881Spetercombined_status(const svn_client_status_t *status) 67251881Speter{ 68251881Speter enum svn_wc_status_kind new_status = status->node_status; 69251881Speter 70251881Speter switch (status->node_status) 71251881Speter { 72251881Speter case svn_wc_status_conflicted: 73251881Speter if (!status->versioned && status->conflicted) 74251881Speter { 75251881Speter /* Report unversioned tree conflict victims as missing: '!' */ 76251881Speter new_status = svn_wc_status_missing; 77251881Speter break; 78251881Speter } 79251881Speter /* fall through */ 80251881Speter case svn_wc_status_modified: 81251881Speter /* This value might be the property status */ 82251881Speter new_status = status->text_status; 83251881Speter break; 84251881Speter default: 85251881Speter break; 86251881Speter } 87251881Speter 88251881Speter return new_status; 89251881Speter} 90251881Speter 91251881Speter/* Return the combined repository STATUS as shown in 'svn status' based 92251881Speter on the repository node status and repository text status */ 93251881Speterstatic enum svn_wc_status_kind 94251881Spetercombined_repos_status(const svn_client_status_t *status) 95251881Speter{ 96251881Speter if (status->repos_node_status == svn_wc_status_modified) 97251881Speter return status->repos_text_status; 98251881Speter 99251881Speter return status->repos_node_status; 100251881Speter} 101251881Speter 102251881Speter/* Return the single character representation of the switched column 103251881Speter status. */ 104251881Speterstatic char 105251881Spetergenerate_switch_column_code(const svn_client_status_t *status) 106251881Speter{ 107251881Speter if (status->switched) 108251881Speter return 'S'; 109251881Speter else if (status->file_external) 110251881Speter return 'X'; 111251881Speter else 112251881Speter return ' '; 113251881Speter} 114251881Speter 115251881Speter/* Return the detailed string representation of STATUS */ 116251881Speterstatic const char * 117251881Spetergenerate_status_desc(enum svn_wc_status_kind status) 118251881Speter{ 119251881Speter switch (status) 120251881Speter { 121251881Speter case svn_wc_status_none: return "none"; 122251881Speter case svn_wc_status_normal: return "normal"; 123251881Speter case svn_wc_status_added: return "added"; 124251881Speter case svn_wc_status_missing: return "missing"; 125251881Speter case svn_wc_status_incomplete: return "incomplete"; 126251881Speter case svn_wc_status_deleted: return "deleted"; 127251881Speter case svn_wc_status_replaced: return "replaced"; 128251881Speter case svn_wc_status_modified: return "modified"; 129251881Speter case svn_wc_status_conflicted: return "conflicted"; 130251881Speter case svn_wc_status_obstructed: return "obstructed"; 131251881Speter case svn_wc_status_ignored: return "ignored"; 132251881Speter case svn_wc_status_external: return "external"; 133251881Speter case svn_wc_status_unversioned: return "unversioned"; 134251881Speter default: 135251881Speter SVN_ERR_MALFUNCTION_NO_RETURN(); 136251881Speter } 137251881Speter} 138251881Speter 139251881Speter/* Make a relative path containing '..' elements as needed. 140262253Speter TARGET_ABSPATH shall be the absolute version of TARGET_PATH. 141299742Sdim TARGET_ABSPATH, TARGET_PATH and LOCAL_ABSPATH shall be canonical 142251881Speter 143262253Speter If above conditions are met, a relative path that leads to PATH 144262253Speter from TARGET_PATH is returned, but there is no error checking involved. 145251881Speter 146262253Speter The returned path is allocated from RESULT_POOL, all other 147299742Sdim allocations are made in SCRATCH_POOL. 148299742Sdim 149299742Sdim Note that it is not possible to just join the resulting path to 150299742Sdim reconstruct the real path as the "../" paths are relative from 151299742Sdim a different base than the normal relative paths! 152299742Sdim */ 153251881Speterstatic const char * 154262253Spetermake_relpath(const char *target_abspath, 155251881Speter const char *target_path, 156299742Sdim const char *local_abspath, 157251881Speter apr_pool_t *result_pool, 158251881Speter apr_pool_t *scratch_pool) 159251881Speter{ 160251881Speter const char *la; 161251881Speter const char *parent_dir_els = ""; 162299742Sdim const char *t_relpath; 163299742Sdim const char *p_relpath; 164251881Speter 165299742Sdim#ifdef SVN_DEBUG 166299742Sdim SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_abspath)); 167299742Sdim#endif 168262253Speter 169299742Sdim t_relpath = svn_dirent_skip_ancestor(target_abspath, local_abspath); 170262253Speter 171299742Sdim if (t_relpath) 172299742Sdim return svn_dirent_join(target_path, t_relpath, result_pool); 173299742Sdim 174251881Speter /* An example: 175251881Speter * relative_to_path = /a/b/c 176262253Speter * path = /a/x/y/z 177251881Speter * result = ../../x/y/z 178251881Speter * 179251881Speter * Another example (Windows specific): 180251881Speter * relative_to_path = F:/wc 181262253Speter * path = C:/wc 182251881Speter * result = C:/wc 183251881Speter */ 184251881Speter /* Skip the common ancestor of both paths, here '/a'. */ 185299742Sdim la = svn_dirent_get_longest_ancestor(target_abspath, local_abspath, 186251881Speter scratch_pool); 187251881Speter if (*la == '\0') 188251881Speter { 189251881Speter /* Nothing in common: E.g. C:/ vs F:/ on Windows */ 190299742Sdim return apr_pstrdup(result_pool, local_abspath); 191251881Speter } 192299742Sdim t_relpath = svn_dirent_skip_ancestor(la, target_abspath); 193299742Sdim p_relpath = svn_dirent_skip_ancestor(la, local_abspath); 194251881Speter 195251881Speter /* In above example, we'd now have: 196251881Speter * relative_to_path = b/c 197262253Speter * path = x/y/z */ 198251881Speter 199251881Speter /* Count the elements of relative_to_path and prepend as many '..' elements 200262253Speter * to path. */ 201299742Sdim while (*t_relpath) 202251881Speter { 203299742Sdim t_relpath = svn_dirent_dirname(t_relpath, scratch_pool); 204251881Speter parent_dir_els = svn_dirent_join(parent_dir_els, "..", scratch_pool); 205251881Speter } 206251881Speter 207299742Sdim /* This returns a ../ style path relative from the status target */ 208299742Sdim return svn_dirent_join(parent_dir_els, p_relpath, result_pool); 209251881Speter} 210251881Speter 211251881Speter 212251881Speter/* Print STATUS and PATH in a format determined by DETAILED and 213251881Speter SHOW_LAST_COMMITTED. */ 214251881Speterstatic svn_error_t * 215262253Speterprint_status(const char *target_abspath, 216262253Speter const char *target_path, 217262253Speter const char *path, 218251881Speter svn_boolean_t detailed, 219251881Speter svn_boolean_t show_last_committed, 220251881Speter svn_boolean_t repos_locks, 221251881Speter const svn_client_status_t *status, 222251881Speter unsigned int *text_conflicts, 223251881Speter unsigned int *prop_conflicts, 224251881Speter unsigned int *tree_conflicts, 225251881Speter svn_client_ctx_t *ctx, 226251881Speter apr_pool_t *pool) 227251881Speter{ 228251881Speter enum svn_wc_status_kind node_status = status->node_status; 229251881Speter enum svn_wc_status_kind prop_status = status->prop_status; 230251881Speter char tree_status_code = ' '; 231251881Speter const char *tree_desc_line = ""; 232251881Speter const char *moved_from_line = ""; 233251881Speter const char *moved_to_line = ""; 234251881Speter 235251881Speter /* For historic reasons svn ignores the property status for added nodes, even 236251881Speter if these nodes were copied and have local property changes. 237251881Speter 238251881Speter Note that it doesn't do this on replacements, or children of copies. 239251881Speter 240251881Speter ### Our test suite would catch more errors if we reported property 241251881Speter changes on copies. */ 242251881Speter if (node_status == svn_wc_status_added) 243251881Speter prop_status = svn_wc_status_none; 244251881Speter 245251881Speter /* To indicate this node is the victim of a tree conflict, we show 246251881Speter 'C' in the tree-conflict column, overriding any other status. 247251881Speter We also print a separate line describing the nature of the tree 248251881Speter conflict. */ 249251881Speter if (status->conflicted) 250251881Speter { 251251881Speter const char *desc; 252251881Speter const char *local_abspath = status->local_abspath; 253251881Speter svn_boolean_t text_conflicted; 254251881Speter svn_boolean_t prop_conflicted; 255251881Speter svn_boolean_t tree_conflicted; 256251881Speter 257251881Speter if (status->versioned) 258251881Speter { 259251881Speter svn_error_t *err; 260251881Speter 261251881Speter err = svn_wc_conflicted_p3(&text_conflicted, 262251881Speter &prop_conflicted, 263251881Speter &tree_conflicted, ctx->wc_ctx, 264251881Speter local_abspath, pool); 265251881Speter 266251881Speter if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) 267251881Speter { 268251881Speter svn_error_clear(err); 269251881Speter text_conflicted = FALSE; 270251881Speter prop_conflicted = FALSE; 271251881Speter tree_conflicted = FALSE; 272251881Speter } 273251881Speter else 274251881Speter SVN_ERR(err); 275251881Speter } 276251881Speter else 277251881Speter { 278251881Speter text_conflicted = FALSE; 279251881Speter prop_conflicted = FALSE; 280251881Speter tree_conflicted = TRUE; 281251881Speter } 282251881Speter 283251881Speter if (tree_conflicted) 284251881Speter { 285251881Speter const svn_wc_conflict_description2_t *tree_conflict; 286251881Speter SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict, ctx->wc_ctx, 287251881Speter local_abspath, pool, pool)); 288251881Speter SVN_ERR_ASSERT(tree_conflict != NULL); 289251881Speter 290251881Speter tree_status_code = 'C'; 291251881Speter SVN_ERR(svn_cl__get_human_readable_tree_conflict_description( 292251881Speter &desc, tree_conflict, pool)); 293251881Speter tree_desc_line = apr_psprintf(pool, "\n > %s", desc); 294251881Speter (*tree_conflicts)++; 295251881Speter } 296251881Speter else if (text_conflicted) 297251881Speter (*text_conflicts)++; 298251881Speter else if (prop_conflicted) 299251881Speter (*prop_conflicts)++; 300251881Speter } 301251881Speter 302251881Speter /* Note that moved-from and moved-to information is only available in STATUS 303251881Speter * for (op-)roots of a move. Those are exactly the nodes we want to show 304251881Speter * move info for in 'svn status'. See also comments in svn_wc_status3_t. */ 305251881Speter if (status->moved_from_abspath && status->moved_to_abspath && 306251881Speter strcmp(status->moved_from_abspath, status->moved_to_abspath) == 0) 307251881Speter { 308251881Speter const char *relpath; 309251881Speter 310262253Speter relpath = make_relpath(target_abspath, target_path, 311262253Speter status->moved_from_abspath, 312251881Speter pool, pool); 313251881Speter relpath = svn_dirent_local_style(relpath, pool); 314251881Speter moved_from_line = apr_pstrcat(pool, "\n > ", 315251881Speter apr_psprintf(pool, 316251881Speter _("swapped places with %s"), 317251881Speter relpath), 318299742Sdim SVN_VA_NULL); 319251881Speter } 320251881Speter else if (status->moved_from_abspath || status->moved_to_abspath) 321251881Speter { 322251881Speter const char *relpath; 323251881Speter 324251881Speter if (status->moved_from_abspath) 325251881Speter { 326262253Speter relpath = make_relpath(target_abspath, target_path, 327262253Speter status->moved_from_abspath, 328251881Speter pool, pool); 329251881Speter relpath = svn_dirent_local_style(relpath, pool); 330251881Speter moved_from_line = apr_pstrcat(pool, "\n > ", 331251881Speter apr_psprintf(pool, _("moved from %s"), 332251881Speter relpath), 333299742Sdim SVN_VA_NULL); 334251881Speter } 335251881Speter 336251881Speter if (status->moved_to_abspath) 337251881Speter { 338262253Speter relpath = make_relpath(target_abspath, target_path, 339262253Speter status->moved_to_abspath, 340251881Speter pool, pool); 341251881Speter relpath = svn_dirent_local_style(relpath, pool); 342251881Speter moved_to_line = apr_pstrcat(pool, "\n > ", 343251881Speter apr_psprintf(pool, _("moved to %s"), 344251881Speter relpath), 345299742Sdim SVN_VA_NULL); 346251881Speter } 347251881Speter } 348251881Speter 349262253Speter path = svn_dirent_local_style(path, pool); 350262253Speter 351251881Speter if (detailed) 352251881Speter { 353251881Speter char ood_status, lock_status; 354251881Speter const char *working_rev; 355251881Speter 356251881Speter if (! status->versioned) 357251881Speter working_rev = ""; 358251881Speter else if (status->copied 359251881Speter || ! SVN_IS_VALID_REVNUM(status->revision)) 360251881Speter working_rev = "-"; 361251881Speter else 362251881Speter working_rev = apr_psprintf(pool, "%ld", status->revision); 363251881Speter 364251881Speter if (status->repos_node_status != svn_wc_status_none) 365251881Speter ood_status = '*'; 366251881Speter else 367251881Speter ood_status = ' '; 368251881Speter 369251881Speter if (repos_locks) 370251881Speter { 371251881Speter if (status->repos_lock) 372251881Speter { 373251881Speter if (status->lock) 374251881Speter { 375251881Speter if (strcmp(status->repos_lock->token, status->lock->token) 376251881Speter == 0) 377251881Speter lock_status = 'K'; 378251881Speter else 379251881Speter lock_status = 'T'; 380251881Speter } 381251881Speter else 382251881Speter lock_status = 'O'; 383251881Speter } 384251881Speter else if (status->lock) 385251881Speter lock_status = 'B'; 386251881Speter else 387251881Speter lock_status = ' '; 388251881Speter } 389251881Speter else 390251881Speter lock_status = (status->lock) ? 'K' : ' '; 391251881Speter 392251881Speter if (show_last_committed) 393251881Speter { 394251881Speter const char *commit_rev; 395251881Speter const char *commit_author; 396251881Speter 397251881Speter if (SVN_IS_VALID_REVNUM(status->changed_rev)) 398251881Speter commit_rev = apr_psprintf(pool, "%ld", status->changed_rev); 399251881Speter else if (status->versioned) 400251881Speter commit_rev = " ? "; 401251881Speter else 402251881Speter commit_rev = ""; 403251881Speter 404251881Speter if (status->changed_author) 405251881Speter commit_author = status->changed_author; 406251881Speter else if (status->versioned) 407251881Speter commit_author = " ? "; 408251881Speter else 409251881Speter commit_author = ""; 410251881Speter 411251881Speter SVN_ERR 412251881Speter (svn_cmdline_printf(pool, 413251881Speter "%c%c%c%c%c%c%c %c %8s %8s %-12s %s%s%s%s\n", 414251881Speter generate_status_code(combined_status(status)), 415251881Speter generate_status_code(prop_status), 416251881Speter status->wc_is_locked ? 'L' : ' ', 417251881Speter status->copied ? '+' : ' ', 418251881Speter generate_switch_column_code(status), 419251881Speter lock_status, 420251881Speter tree_status_code, 421251881Speter ood_status, 422251881Speter working_rev, 423251881Speter commit_rev, 424251881Speter commit_author, 425251881Speter path, 426251881Speter moved_to_line, 427251881Speter moved_from_line, 428251881Speter tree_desc_line)); 429251881Speter } 430251881Speter else 431251881Speter SVN_ERR( 432251881Speter svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %c %8s %s%s%s%s\n", 433251881Speter generate_status_code(combined_status(status)), 434251881Speter generate_status_code(prop_status), 435251881Speter status->wc_is_locked ? 'L' : ' ', 436251881Speter status->copied ? '+' : ' ', 437251881Speter generate_switch_column_code(status), 438251881Speter lock_status, 439251881Speter tree_status_code, 440251881Speter ood_status, 441251881Speter working_rev, 442251881Speter path, 443251881Speter moved_to_line, 444251881Speter moved_from_line, 445251881Speter tree_desc_line)); 446251881Speter } 447251881Speter else 448251881Speter SVN_ERR( 449251881Speter svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %s%s%s%s\n", 450251881Speter generate_status_code(combined_status(status)), 451251881Speter generate_status_code(prop_status), 452251881Speter status->wc_is_locked ? 'L' : ' ', 453251881Speter status->copied ? '+' : ' ', 454251881Speter generate_switch_column_code(status), 455251881Speter ((status->lock) 456251881Speter ? 'K' : ' '), 457251881Speter tree_status_code, 458251881Speter path, 459251881Speter moved_to_line, 460251881Speter moved_from_line, 461251881Speter tree_desc_line)); 462251881Speter 463251881Speter return svn_cmdline_fflush(stdout); 464251881Speter} 465251881Speter 466251881Speter 467251881Spetersvn_error_t * 468262253Spetersvn_cl__print_status_xml(const char *target_abspath, 469262253Speter const char *target_path, 470251881Speter const char *path, 471251881Speter const svn_client_status_t *status, 472251881Speter svn_client_ctx_t *ctx, 473251881Speter apr_pool_t *pool) 474251881Speter{ 475251881Speter svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); 476251881Speter apr_hash_t *att_hash; 477251881Speter const char *local_abspath = status->local_abspath; 478251881Speter svn_boolean_t tree_conflicted = FALSE; 479251881Speter 480251881Speter if (status->node_status == svn_wc_status_none 481251881Speter && status->repos_node_status == svn_wc_status_none) 482251881Speter return SVN_NO_ERROR; 483251881Speter 484251881Speter if (status->conflicted) 485251881Speter SVN_ERR(svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, 486251881Speter ctx->wc_ctx, local_abspath, pool)); 487251881Speter 488251881Speter svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry", 489299742Sdim "path", svn_dirent_local_style(path, pool), 490299742Sdim SVN_VA_NULL); 491251881Speter 492251881Speter att_hash = apr_hash_make(pool); 493251881Speter svn_hash_sets(att_hash, "item", 494251881Speter generate_status_desc(combined_status(status))); 495251881Speter 496251881Speter svn_hash_sets(att_hash, "props", 497251881Speter generate_status_desc( 498251881Speter (status->node_status != svn_wc_status_deleted) 499251881Speter ? status->prop_status 500251881Speter : svn_wc_status_none)); 501251881Speter if (status->wc_is_locked) 502251881Speter svn_hash_sets(att_hash, "wc-locked", "true"); 503251881Speter if (status->copied) 504251881Speter svn_hash_sets(att_hash, "copied", "true"); 505251881Speter if (status->switched) 506251881Speter svn_hash_sets(att_hash, "switched", "true"); 507251881Speter if (status->file_external) 508251881Speter svn_hash_sets(att_hash, "file-external", "true"); 509251881Speter if (status->versioned && ! status->copied) 510251881Speter svn_hash_sets(att_hash, "revision", 511251881Speter apr_psprintf(pool, "%ld", status->revision)); 512251881Speter if (tree_conflicted) 513251881Speter svn_hash_sets(att_hash, "tree-conflicted", "true"); 514251881Speter if (status->moved_from_abspath || status->moved_to_abspath) 515251881Speter { 516251881Speter const char *relpath; 517251881Speter 518251881Speter if (status->moved_from_abspath) 519251881Speter { 520262253Speter relpath = make_relpath(target_abspath, target_path, 521262253Speter status->moved_from_abspath, 522251881Speter pool, pool); 523251881Speter relpath = svn_dirent_local_style(relpath, pool); 524251881Speter svn_hash_sets(att_hash, "moved-from", relpath); 525251881Speter } 526251881Speter if (status->moved_to_abspath) 527251881Speter { 528262253Speter relpath = make_relpath(target_abspath, target_path, 529262253Speter status->moved_to_abspath, 530251881Speter pool, pool); 531251881Speter relpath = svn_dirent_local_style(relpath, pool); 532251881Speter svn_hash_sets(att_hash, "moved-to", relpath); 533251881Speter } 534251881Speter } 535251881Speter svn_xml_make_open_tag_hash(&sb, pool, svn_xml_normal, "wc-status", 536251881Speter att_hash); 537251881Speter 538251881Speter if (SVN_IS_VALID_REVNUM(status->changed_rev)) 539251881Speter { 540251881Speter svn_cl__print_xml_commit(&sb, status->changed_rev, 541251881Speter status->changed_author, 542251881Speter svn_time_to_cstring(status->changed_date, 543251881Speter pool), 544251881Speter pool); 545251881Speter } 546251881Speter 547251881Speter if (status->lock) 548251881Speter svn_cl__print_xml_lock(&sb, status->lock, pool); 549251881Speter 550251881Speter svn_xml_make_close_tag(&sb, pool, "wc-status"); 551251881Speter 552251881Speter if (status->repos_node_status != svn_wc_status_none 553251881Speter || status->repos_lock) 554251881Speter { 555251881Speter svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repos-status", 556251881Speter "item", 557251881Speter generate_status_desc(combined_repos_status(status)), 558251881Speter "props", 559251881Speter generate_status_desc(status->repos_prop_status), 560299742Sdim SVN_VA_NULL); 561251881Speter if (status->repos_lock) 562251881Speter svn_cl__print_xml_lock(&sb, status->repos_lock, pool); 563251881Speter 564251881Speter svn_xml_make_close_tag(&sb, pool, "repos-status"); 565251881Speter } 566251881Speter 567251881Speter svn_xml_make_close_tag(&sb, pool, "entry"); 568251881Speter 569251881Speter return svn_cl__error_checked_fputs(sb->data, stdout); 570251881Speter} 571251881Speter 572251881Speter/* Called by status-cmd.c */ 573251881Spetersvn_error_t * 574262253Spetersvn_cl__print_status(const char *target_abspath, 575262253Speter const char *target_path, 576251881Speter const char *path, 577251881Speter const svn_client_status_t *status, 578251881Speter svn_boolean_t suppress_externals_placeholders, 579251881Speter svn_boolean_t detailed, 580251881Speter svn_boolean_t show_last_committed, 581251881Speter svn_boolean_t skip_unrecognized, 582251881Speter svn_boolean_t repos_locks, 583251881Speter unsigned int *text_conflicts, 584251881Speter unsigned int *prop_conflicts, 585251881Speter unsigned int *tree_conflicts, 586251881Speter svn_client_ctx_t *ctx, 587251881Speter apr_pool_t *pool) 588251881Speter{ 589251881Speter if (! status 590251881Speter || (skip_unrecognized 591251881Speter && !(status->versioned 592251881Speter || status->conflicted 593251881Speter || status->node_status == svn_wc_status_external)) 594251881Speter || (status->node_status == svn_wc_status_none 595251881Speter && status->repos_node_status == svn_wc_status_none)) 596251881Speter return SVN_NO_ERROR; 597251881Speter 598251881Speter /* If we're trying not to print boring "X /path/to/external" 599251881Speter lines..." */ 600251881Speter if (suppress_externals_placeholders) 601251881Speter { 602251881Speter /* ... skip regular externals unmodified in the repository. */ 603251881Speter if ((status->node_status == svn_wc_status_external) 604251881Speter && (status->repos_node_status == svn_wc_status_none) 605251881Speter && (! status->conflicted)) 606251881Speter return SVN_NO_ERROR; 607251881Speter 608251881Speter /* ... skip file externals that aren't modified locally or 609251881Speter remotely, changelisted, or locked (in either sense of the 610251881Speter word). */ 611251881Speter if ((status->file_external) 612251881Speter && (status->repos_node_status == svn_wc_status_none) 613251881Speter && ((status->node_status == svn_wc_status_normal) 614251881Speter || (status->node_status == svn_wc_status_none)) 615251881Speter && ((status->prop_status == svn_wc_status_normal) 616251881Speter || (status->prop_status == svn_wc_status_none)) 617251881Speter && (! status->changelist) 618251881Speter && (! status->lock) 619251881Speter && (! status->wc_is_locked) 620251881Speter && (! status->conflicted)) 621251881Speter return SVN_NO_ERROR; 622251881Speter } 623251881Speter 624262253Speter return print_status(target_abspath, target_path, path, 625251881Speter detailed, show_last_committed, repos_locks, status, 626251881Speter text_conflicts, prop_conflicts, tree_conflicts, 627251881Speter ctx, pool); 628251881Speter} 629