tree_conflicts.c revision 362181
1141296Sdas/* 2141296Sdas * tree_conflicts.c: Storage of tree conflict descriptions in the WC. 32116Sjkh * 42116Sjkh * ==================================================================== 52116Sjkh * Licensed to the Apache Software Foundation (ASF) under one 62116Sjkh * or more contributor license agreements. See the NOTICE file 7141296Sdas * distributed with this work for additional information 82116Sjkh * regarding copyright ownership. The ASF licenses this file 9141296Sdas * to you under the Apache License, Version 2.0 (the 102116Sjkh * "License"); you may not use this file except in compliance 112116Sjkh * with the License. You may obtain a copy of the License at 122116Sjkh * 132116Sjkh * http://www.apache.org/licenses/LICENSE-2.0 14176451Sdas * 15176451Sdas * Unless required by applicable law or agreed to in writing, 162116Sjkh * software distributed under the License is distributed on an 172116Sjkh * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18141296Sdas * KIND, either express or implied. See the License for the 192116Sjkh * specific language governing permissions and limitations 20141296Sdas * under the License. 21141296Sdas * ==================================================================== 222116Sjkh */ 232116Sjkh 242116Sjkh#include "svn_dirent_uri.h" 252116Sjkh#include "svn_path.h" 26141296Sdas#include "svn_types.h" 272116Sjkh#include "svn_pools.h" 282116Sjkh 292116Sjkh#include "tree_conflicts.h" 302116Sjkh#include "conflicts.h" 312116Sjkh#include "wc.h" 322116Sjkh 332116Sjkh#include "private/svn_skel.h" 342116Sjkh#include "private/svn_wc_private.h" 35260067Skargl#include "private/svn_token.h" 36260067Skargl 372116Sjkh#include "svn_private_config.h" 382116Sjkh 392116Sjkh/* ### this should move to a more general location... */ 402116Sjkh/* A map for svn_node_kind_t values. */ 412116Sjkh/* FIXME: this mapping defines a different representation of 4297413Salfred svn_node_unknown than the one defined in token-map.h */ 4397413Salfredstatic const svn_token_map_t node_kind_map[] = 448870Srgrimes{ 45226598Sdas { "none", svn_node_none }, 462116Sjkh { "file", svn_node_file }, 472116Sjkh { "dir", svn_node_dir }, 482116Sjkh { "", svn_node_unknown }, 492116Sjkh /* ### should also map svn_node_symlink */ 502116Sjkh { NULL } 512116Sjkh}; 522116Sjkh 53141296Sdas/* A map for svn_wc_operation_t values. */ 542116Sjkhconst svn_token_map_t svn_wc__operation_map[] = 552116Sjkh{ 562116Sjkh { "none", svn_wc_operation_none }, 572116Sjkh { "update", svn_wc_operation_update }, 582116Sjkh { "switch", svn_wc_operation_switch }, 592116Sjkh { "merge", svn_wc_operation_merge }, 602116Sjkh { NULL } 612116Sjkh}; 622116Sjkh 632116Sjkh/* A map for svn_wc_conflict_action_t values. */ 642116Sjkhconst svn_token_map_t svn_wc__conflict_action_map[] = 652116Sjkh{ 662116Sjkh { "edited", svn_wc_conflict_action_edit }, 672116Sjkh { "deleted", svn_wc_conflict_action_delete }, 682116Sjkh { "added", svn_wc_conflict_action_add }, 692116Sjkh { "replaced", svn_wc_conflict_action_replace }, 70226598Sdas { NULL } 71226598Sdas}; 722116Sjkh 732116Sjkh/* A map for svn_wc_conflict_reason_t values. */ 742116Sjkhconst svn_token_map_t svn_wc__conflict_reason_map[] = 752116Sjkh{ 76260067Skargl { "edited", svn_wc_conflict_reason_edited }, 77260067Skargl { "deleted", svn_wc_conflict_reason_deleted }, 78260067Skargl { "missing", svn_wc_conflict_reason_missing }, 79260067Skargl { "obstructed", svn_wc_conflict_reason_obstructed }, 80 { "added", svn_wc_conflict_reason_added }, 81 { "replaced", svn_wc_conflict_reason_replaced }, 82 { "unversioned", svn_wc_conflict_reason_unversioned }, 83 { "moved-away", svn_wc_conflict_reason_moved_away }, 84 { "moved-here", svn_wc_conflict_reason_moved_here }, 85 { NULL } 86}; 87 88 89/* */ 90static svn_boolean_t 91is_valid_version_info_skel(const svn_skel_t *skel) 92{ 93 return (svn_skel__list_length(skel) == 5 94 && svn_skel__matches_atom(skel->children, "version") 95 && skel->children->next->is_atom 96 && skel->children->next->next->is_atom 97 && skel->children->next->next->next->is_atom 98 && skel->children->next->next->next->next->is_atom); 99} 100 101 102/* */ 103static svn_boolean_t 104is_valid_conflict_skel(const svn_skel_t *skel) 105{ 106 int i; 107 108 if (svn_skel__list_length(skel) != 8 109 || !svn_skel__matches_atom(skel->children, "conflict")) 110 return FALSE; 111 112 /* 5 atoms ... */ 113 skel = skel->children->next; 114 for (i = 5; i--; skel = skel->next) 115 if (!skel->is_atom) 116 return FALSE; 117 118 /* ... and 2 version info skels. */ 119 return (is_valid_version_info_skel(skel) 120 && is_valid_version_info_skel(skel->next)); 121} 122 123 124/* Parse the enumeration value in VALUE into a plain 125 * 'int', using MAP to convert from strings to enumeration values. 126 * In MAP, a null .str field marks the end of the map. 127 */ 128static svn_error_t * 129read_enum_field(int *result, 130 const svn_token_map_t *map, 131 const svn_skel_t *skel) 132{ 133 int value = svn_token__from_mem(map, skel->data, skel->len); 134 135 if (value == SVN_TOKEN_UNKNOWN) 136 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 137 _("Unknown enumeration value in tree conflict " 138 "description")); 139 140 *result = value; 141 142 return SVN_NO_ERROR; 143} 144 145 146/* Parse the conflict info fields from SKEL into *VERSION_INFO. */ 147static svn_error_t * 148read_node_version_info(const svn_wc_conflict_version_t **version_info, 149 const svn_skel_t *skel, 150 apr_pool_t *result_pool, 151 apr_pool_t *scratch_pool) 152{ 153 int n; 154 const char *repos_root; 155 const char *repos_relpath; 156 svn_revnum_t peg_rev; 157 svn_node_kind_t kind; 158 159 if (!is_valid_version_info_skel(skel)) 160 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 161 _("Invalid version info in tree conflict " 162 "description")); 163 164 repos_root = apr_pstrmemdup(scratch_pool, 165 skel->children->next->data, 166 skel->children->next->len); 167 if (*repos_root == '\0') 168 { 169 *version_info = NULL; 170 return SVN_NO_ERROR; 171 } 172 173 /* Apply the Subversion 1.7+ url canonicalization rules to a pre 1.7 url */ 174 repos_root = svn_uri_canonicalize(repos_root, result_pool); 175 176 peg_rev = SVN_STR_TO_REV(apr_pstrmemdup(scratch_pool, 177 skel->children->next->next->data, 178 skel->children->next->next->len)); 179 180 repos_relpath = apr_pstrmemdup(result_pool, 181 skel->children->next->next->next->data, 182 skel->children->next->next->next->len); 183 184 SVN_ERR(read_enum_field(&n, node_kind_map, 185 skel->children->next->next->next->next)); 186 kind = (svn_node_kind_t)n; 187 188 *version_info = svn_wc_conflict_version_create2(repos_root, 189 NULL, 190 repos_relpath, 191 peg_rev, 192 kind, 193 result_pool); 194 195 return SVN_NO_ERROR; 196} 197 198 199svn_error_t * 200svn_wc__deserialize_conflict(const svn_wc_conflict_description2_t **conflict, 201 const svn_skel_t *skel, 202 const char *dir_path, 203 apr_pool_t *result_pool, 204 apr_pool_t *scratch_pool) 205{ 206 const char *victim_basename; 207 const char *victim_abspath; 208 svn_node_kind_t node_kind; 209 svn_wc_operation_t operation; 210 svn_wc_conflict_action_t action; 211 svn_wc_conflict_reason_t reason; 212 const svn_wc_conflict_version_t *src_left_version; 213 const svn_wc_conflict_version_t *src_right_version; 214 int n; 215 svn_wc_conflict_description2_t *new_conflict; 216 217 if (!is_valid_conflict_skel(skel)) 218 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 219 _("Invalid conflict info '%s' in tree conflict " 220 "description"), 221 skel ? svn_skel__unparse(skel, scratch_pool)->data 222 : "(null)"); 223 224 /* victim basename */ 225 victim_basename = apr_pstrmemdup(scratch_pool, 226 skel->children->next->data, 227 skel->children->next->len); 228 if (victim_basename[0] == '\0') 229 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 230 _("Empty 'victim' field in tree conflict " 231 "description")); 232 233 /* node_kind */ 234 SVN_ERR(read_enum_field(&n, node_kind_map, skel->children->next->next)); 235 node_kind = (svn_node_kind_t)n; 236 if (node_kind != svn_node_file && node_kind != svn_node_dir) 237 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 238 _("Invalid 'node_kind' field in tree conflict description")); 239 240 /* operation */ 241 SVN_ERR(read_enum_field(&n, svn_wc__operation_map, 242 skel->children->next->next->next)); 243 operation = (svn_wc_operation_t)n; 244 245 SVN_ERR(svn_dirent_get_absolute(&victim_abspath, 246 svn_dirent_join(dir_path, victim_basename, scratch_pool), 247 scratch_pool)); 248 249 /* action */ 250 SVN_ERR(read_enum_field(&n, svn_wc__conflict_action_map, 251 skel->children->next->next->next->next)); 252 action = n; 253 254 /* reason */ 255 SVN_ERR(read_enum_field(&n, svn_wc__conflict_reason_map, 256 skel->children->next->next->next->next->next)); 257 reason = n; 258 259 /* Let's just make it a bit easier on ourself here... */ 260 skel = skel->children->next->next->next->next->next->next; 261 262 /* src_left_version */ 263 SVN_ERR(read_node_version_info(&src_left_version, skel, 264 result_pool, scratch_pool)); 265 266 /* src_right_version */ 267 SVN_ERR(read_node_version_info(&src_right_version, skel->next, 268 result_pool, scratch_pool)); 269 270 new_conflict = svn_wc_conflict_description_create_tree2(victim_abspath, 271 node_kind, operation, src_left_version, src_right_version, 272 result_pool); 273 new_conflict->action = action; 274 new_conflict->reason = reason; 275 276 *conflict = new_conflict; 277 278 return SVN_NO_ERROR; 279} 280 281 282/* Prepend to SKEL the string corresponding to enumeration value N, as found 283 * in MAP. */ 284static void 285skel_prepend_enum(svn_skel_t *skel, 286 const svn_token_map_t *map, 287 int n, 288 apr_pool_t *result_pool) 289{ 290 svn_skel__prepend(svn_skel__str_atom(svn_token__to_word(map, n), 291 result_pool), skel); 292} 293 294 295/* Prepend to PARENT_SKEL the several fields that represent VERSION_INFO, */ 296static svn_error_t * 297prepend_version_info_skel(svn_skel_t *parent_skel, 298 const svn_wc_conflict_version_t *version_info, 299 apr_pool_t *pool) 300{ 301 svn_skel_t *skel = svn_skel__make_empty_list(pool); 302 303 /* node_kind */ 304 skel_prepend_enum(skel, node_kind_map, version_info->node_kind, pool); 305 306 /* path_in_repos */ 307 svn_skel__prepend(svn_skel__str_atom(version_info->path_in_repos 308 ? version_info->path_in_repos 309 : "", pool), skel); 310 311 /* peg_rev */ 312 svn_skel__prepend(svn_skel__str_atom(apr_psprintf(pool, "%ld", 313 version_info->peg_rev), 314 pool), skel); 315 316 /* repos_url */ 317 svn_skel__prepend(svn_skel__str_atom(version_info->repos_url 318 ? version_info->repos_url 319 : "", pool), skel); 320 321 svn_skel__prepend(svn_skel__str_atom("version", pool), skel); 322 323 SVN_ERR_ASSERT(is_valid_version_info_skel(skel)); 324 325 svn_skel__prepend(skel, parent_skel); 326 327 return SVN_NO_ERROR; 328} 329 330 331svn_error_t * 332svn_wc__serialize_conflict(svn_skel_t **skel, 333 const svn_wc_conflict_description2_t *conflict, 334 apr_pool_t *result_pool, 335 apr_pool_t *scratch_pool) 336{ 337 /* A conflict version struct with all fields null/invalid. */ 338 static const svn_wc_conflict_version_t null_version = { 339 NULL, SVN_INVALID_REVNUM, NULL, svn_node_unknown }; 340 svn_skel_t *c_skel = svn_skel__make_empty_list(result_pool); 341 const char *victim_basename; 342 343 /* src_right_version */ 344 if (conflict->src_right_version) 345 SVN_ERR(prepend_version_info_skel(c_skel, conflict->src_right_version, 346 result_pool)); 347 else 348 SVN_ERR(prepend_version_info_skel(c_skel, &null_version, result_pool)); 349 350 /* src_left_version */ 351 if (conflict->src_left_version) 352 SVN_ERR(prepend_version_info_skel(c_skel, conflict->src_left_version, 353 result_pool)); 354 else 355 SVN_ERR(prepend_version_info_skel(c_skel, &null_version, result_pool)); 356 357 /* local change */ 358 skel_prepend_enum(c_skel, svn_wc__conflict_reason_map, 359 conflict->reason, result_pool); 360 361 /* incoming change */ 362 skel_prepend_enum(c_skel, svn_wc__conflict_action_map, 363 conflict->action, result_pool); 364 365 /* operation */ 366 skel_prepend_enum(c_skel, svn_wc__operation_map, conflict->operation, 367 result_pool); 368 369 /* node_kind */ 370 SVN_ERR_ASSERT(conflict->node_kind == svn_node_dir 371 || conflict->node_kind == svn_node_file 372 || conflict->node_kind == svn_node_none); 373 skel_prepend_enum(c_skel, node_kind_map, conflict->node_kind, 374 result_pool); 375 376 /* Victim path (escaping separator chars). */ 377 victim_basename = svn_dirent_basename(conflict->local_abspath, result_pool); 378 SVN_ERR_ASSERT(victim_basename[0]); 379 svn_skel__prepend(svn_skel__str_atom(victim_basename, result_pool), c_skel); 380 381 svn_skel__prepend(svn_skel__str_atom("conflict", result_pool), c_skel); 382 383 SVN_ERR_ASSERT(is_valid_conflict_skel(c_skel)); 384 385 *skel = c_skel; 386 387 return SVN_NO_ERROR; 388} 389 390 391svn_error_t * 392svn_wc__del_tree_conflict(svn_wc_context_t *wc_ctx, 393 const char *victim_abspath, 394 apr_pool_t *scratch_pool) 395{ 396 SVN_ERR_ASSERT(svn_dirent_is_absolute(victim_abspath)); 397 398 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, victim_abspath, 399 FALSE, FALSE, TRUE, NULL, 400 scratch_pool)); 401 402 return SVN_NO_ERROR; 403 } 404 405svn_error_t * 406svn_wc__add_tree_conflict(svn_wc_context_t *wc_ctx, 407 const svn_wc_conflict_description2_t *conflict, 408 apr_pool_t *scratch_pool) 409{ 410 svn_boolean_t existing_conflict; 411 svn_skel_t *conflict_skel; 412 svn_error_t *err; 413 414 SVN_ERR_ASSERT(conflict != NULL); 415 SVN_ERR_ASSERT(conflict->operation == svn_wc_operation_merge || 416 (conflict->reason != svn_wc_conflict_reason_moved_away && 417 conflict->reason != svn_wc_conflict_reason_moved_here)); 418 419 /* Re-adding an existing tree conflict victim is an error. */ 420 err = svn_wc__internal_conflicted_p(NULL, NULL, &existing_conflict, 421 wc_ctx->db, conflict->local_abspath, 422 scratch_pool); 423 if (err) 424 { 425 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 426 return svn_error_trace(err); 427 428 svn_error_clear(err); 429 } 430 else if (existing_conflict) 431 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 432 _("Attempt to add tree conflict that already " 433 "exists at '%s'"), 434 svn_dirent_local_style(conflict->local_abspath, 435 scratch_pool)); 436 else if (!conflict) 437 return SVN_NO_ERROR; 438 439 conflict_skel = svn_wc__conflict_skel_create(scratch_pool); 440 441 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(conflict_skel, wc_ctx->db, 442 conflict->local_abspath, 443 conflict->reason, 444 conflict->action, 445 NULL, NULL, 446 scratch_pool, scratch_pool)); 447 448 switch (conflict->operation) 449 { 450 case svn_wc_operation_update: 451 default: 452 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, 453 conflict->src_left_version, 454 conflict->src_right_version, 455 scratch_pool, scratch_pool)); 456 break; 457 case svn_wc_operation_switch: 458 SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict_skel, 459 conflict->src_left_version, 460 conflict->src_right_version, 461 scratch_pool, scratch_pool)); 462 break; 463 case svn_wc_operation_merge: 464 SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_skel, 465 conflict->src_left_version, 466 conflict->src_right_version, 467 scratch_pool, scratch_pool)); 468 break; 469 } 470 471 return svn_error_trace( 472 svn_wc__db_op_mark_conflict(wc_ctx->db, conflict->local_abspath, 473 conflict_skel, NULL, scratch_pool)); 474} 475 476 477svn_error_t * 478svn_wc__get_tree_conflict(const svn_wc_conflict_description2_t **tree_conflict, 479 svn_wc_context_t *wc_ctx, 480 const char *local_abspath, 481 apr_pool_t *result_pool, 482 apr_pool_t *scratch_pool) 483{ 484 const apr_array_header_t *conflicts; 485 int i; 486 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 487 488 SVN_ERR(svn_wc__read_conflicts(&conflicts, NULL, 489 wc_ctx->db, local_abspath, 490 FALSE /* temp files */, 491 TRUE /* only tree conflicts */, 492 scratch_pool, scratch_pool)); 493 494 if (!conflicts || conflicts->nelts == 0) 495 { 496 *tree_conflict = NULL; 497 return SVN_NO_ERROR; 498 } 499 500 for (i = 0; i < conflicts->nelts; i++) 501 { 502 const svn_wc_conflict_description2_t *desc; 503 504 desc = APR_ARRAY_IDX(conflicts, i, svn_wc_conflict_description2_t *); 505 506 if (desc->kind == svn_wc_conflict_kind_tree) 507 { 508 *tree_conflict = svn_wc_conflict_description2_dup(desc, result_pool); 509 return SVN_NO_ERROR; 510 } 511 } 512 513 *tree_conflict = NULL; 514 return SVN_NO_ERROR; 515} 516 517