2 * tree_conflicts.c: Storage of tree conflict descriptions in the WC.
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
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 #include "svn_dirent_uri.h"
26 #include "svn_types.h"
27 #include "svn_pools.h"
29 #include "tree_conflicts.h"
30 #include "conflicts.h"
33 #include "private/svn_skel.h"
34 #include "private/svn_wc_private.h"
35 #include "private/svn_token.h"
37 #include "svn_private_config.h"
39 /* ### this should move to a more general location... */
40 /* A map for svn_node_kind_t values. */
41 /* FIXME: this mapping defines a different representation of
42 svn_node_unknown than the one defined in token-map.h */
43 static const svn_token_map_t node_kind_map[] =
45 { "none", svn_node_none },
46 { "file", svn_node_file },
47 { "dir", svn_node_dir },
48 { "", svn_node_unknown },
52 /* A map for svn_wc_operation_t values. */
53 const svn_token_map_t svn_wc__operation_map[] =
55 { "none", svn_wc_operation_none },
56 { "update", svn_wc_operation_update },
57 { "switch", svn_wc_operation_switch },
58 { "merge", svn_wc_operation_merge },
62 /* A map for svn_wc_conflict_action_t values. */
63 const svn_token_map_t svn_wc__conflict_action_map[] =
65 { "edited", svn_wc_conflict_action_edit },
66 { "deleted", svn_wc_conflict_action_delete },
67 { "added", svn_wc_conflict_action_add },
68 { "replaced", svn_wc_conflict_action_replace },
72 /* A map for svn_wc_conflict_reason_t values. */
73 const svn_token_map_t svn_wc__conflict_reason_map[] =
75 { "edited", svn_wc_conflict_reason_edited },
76 { "deleted", svn_wc_conflict_reason_deleted },
77 { "missing", svn_wc_conflict_reason_missing },
78 { "obstructed", svn_wc_conflict_reason_obstructed },
79 { "added", svn_wc_conflict_reason_added },
80 { "replaced", svn_wc_conflict_reason_replaced },
81 { "unversioned", svn_wc_conflict_reason_unversioned },
82 { "moved-away", svn_wc_conflict_reason_moved_away },
83 { "moved-here", svn_wc_conflict_reason_moved_here },
90 is_valid_version_info_skel(const svn_skel_t *skel)
92 return (svn_skel__list_length(skel) == 5
93 && svn_skel__matches_atom(skel->children, "version")
94 && skel->children->next->is_atom
95 && skel->children->next->next->is_atom
96 && skel->children->next->next->next->is_atom
97 && skel->children->next->next->next->next->is_atom);
103 is_valid_conflict_skel(const svn_skel_t *skel)
107 if (svn_skel__list_length(skel) != 8
108 || !svn_skel__matches_atom(skel->children, "conflict"))
112 skel = skel->children->next;
113 for (i = 5; i--; skel = skel->next)
117 /* ... and 2 version info skels. */
118 return (is_valid_version_info_skel(skel)
119 && is_valid_version_info_skel(skel->next));
123 /* Parse the enumeration value in VALUE into a plain
124 * 'int', using MAP to convert from strings to enumeration values.
125 * In MAP, a null .str field marks the end of the map.
128 read_enum_field(int *result,
129 const svn_token_map_t *map,
130 const svn_skel_t *skel)
132 int value = svn_token__from_mem(map, skel->data, skel->len);
134 if (value == SVN_TOKEN_UNKNOWN)
135 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
136 _("Unknown enumeration value in tree conflict "
145 /* Parse the conflict info fields from SKEL into *VERSION_INFO. */
147 read_node_version_info(const svn_wc_conflict_version_t **version_info,
148 const svn_skel_t *skel,
149 apr_pool_t *result_pool,
150 apr_pool_t *scratch_pool)
153 const char *repos_root;
154 const char *repos_relpath;
155 svn_revnum_t peg_rev;
156 svn_node_kind_t kind;
158 if (!is_valid_version_info_skel(skel))
159 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
160 _("Invalid version info in tree conflict "
163 repos_root = apr_pstrmemdup(scratch_pool,
164 skel->children->next->data,
165 skel->children->next->len);
166 if (*repos_root == '\0')
168 *version_info = NULL;
172 /* Apply the Subversion 1.7+ url canonicalization rules to a pre 1.7 url */
173 repos_root = svn_uri_canonicalize(repos_root, result_pool);
175 peg_rev = SVN_STR_TO_REV(apr_pstrmemdup(scratch_pool,
176 skel->children->next->next->data,
177 skel->children->next->next->len));
179 repos_relpath = apr_pstrmemdup(result_pool,
180 skel->children->next->next->next->data,
181 skel->children->next->next->next->len);
183 SVN_ERR(read_enum_field(&n, node_kind_map,
184 skel->children->next->next->next->next));
185 kind = (svn_node_kind_t)n;
187 *version_info = svn_wc_conflict_version_create2(repos_root,
199 svn_wc__deserialize_conflict(const svn_wc_conflict_description2_t **conflict,
200 const svn_skel_t *skel,
201 const char *dir_path,
202 apr_pool_t *result_pool,
203 apr_pool_t *scratch_pool)
205 const char *victim_basename;
206 const char *victim_abspath;
207 svn_node_kind_t node_kind;
208 svn_wc_operation_t operation;
209 svn_wc_conflict_action_t action;
210 svn_wc_conflict_reason_t reason;
211 const svn_wc_conflict_version_t *src_left_version;
212 const svn_wc_conflict_version_t *src_right_version;
214 svn_wc_conflict_description2_t *new_conflict;
216 if (!is_valid_conflict_skel(skel))
217 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
218 _("Invalid conflict info '%s' in tree conflict "
220 skel ? svn_skel__unparse(skel, scratch_pool)->data
223 /* victim basename */
224 victim_basename = apr_pstrmemdup(scratch_pool,
225 skel->children->next->data,
226 skel->children->next->len);
227 if (victim_basename[0] == '\0')
228 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
229 _("Empty 'victim' field in tree conflict "
233 SVN_ERR(read_enum_field(&n, node_kind_map, skel->children->next->next));
234 node_kind = (svn_node_kind_t)n;
235 if (node_kind != svn_node_file && node_kind != svn_node_dir)
236 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
237 _("Invalid 'node_kind' field in tree conflict description"));
240 SVN_ERR(read_enum_field(&n, svn_wc__operation_map,
241 skel->children->next->next->next));
242 operation = (svn_wc_operation_t)n;
244 SVN_ERR(svn_dirent_get_absolute(&victim_abspath,
245 svn_dirent_join(dir_path, victim_basename, scratch_pool),
249 SVN_ERR(read_enum_field(&n, svn_wc__conflict_action_map,
250 skel->children->next->next->next->next));
254 SVN_ERR(read_enum_field(&n, svn_wc__conflict_reason_map,
255 skel->children->next->next->next->next->next));
258 /* Let's just make it a bit easier on ourself here... */
259 skel = skel->children->next->next->next->next->next->next;
261 /* src_left_version */
262 SVN_ERR(read_node_version_info(&src_left_version, skel,
263 result_pool, scratch_pool));
265 /* src_right_version */
266 SVN_ERR(read_node_version_info(&src_right_version, skel->next,
267 result_pool, scratch_pool));
269 new_conflict = svn_wc_conflict_description_create_tree2(victim_abspath,
270 node_kind, operation, src_left_version, src_right_version,
272 new_conflict->action = action;
273 new_conflict->reason = reason;
275 *conflict = new_conflict;
281 /* Prepend to SKEL the string corresponding to enumeration value N, as found
284 skel_prepend_enum(svn_skel_t *skel,
285 const svn_token_map_t *map,
287 apr_pool_t *result_pool)
289 svn_skel__prepend(svn_skel__str_atom(svn_token__to_word(map, n),
294 /* Prepend to PARENT_SKEL the several fields that represent VERSION_INFO, */
296 prepend_version_info_skel(svn_skel_t *parent_skel,
297 const svn_wc_conflict_version_t *version_info,
300 svn_skel_t *skel = svn_skel__make_empty_list(pool);
303 skel_prepend_enum(skel, node_kind_map, version_info->node_kind, pool);
306 svn_skel__prepend(svn_skel__str_atom(version_info->path_in_repos
307 ? version_info->path_in_repos
311 svn_skel__prepend(svn_skel__str_atom(apr_psprintf(pool, "%ld",
312 version_info->peg_rev),
316 svn_skel__prepend(svn_skel__str_atom(version_info->repos_url
317 ? version_info->repos_url
320 svn_skel__prepend(svn_skel__str_atom("version", pool), skel);
322 SVN_ERR_ASSERT(is_valid_version_info_skel(skel));
324 svn_skel__prepend(skel, parent_skel);
331 svn_wc__serialize_conflict(svn_skel_t **skel,
332 const svn_wc_conflict_description2_t *conflict,
333 apr_pool_t *result_pool,
334 apr_pool_t *scratch_pool)
336 /* A conflict version struct with all fields null/invalid. */
337 static const svn_wc_conflict_version_t null_version = {
338 NULL, SVN_INVALID_REVNUM, NULL, svn_node_unknown };
339 svn_skel_t *c_skel = svn_skel__make_empty_list(result_pool);
340 const char *victim_basename;
342 /* src_right_version */
343 if (conflict->src_right_version)
344 SVN_ERR(prepend_version_info_skel(c_skel, conflict->src_right_version,
347 SVN_ERR(prepend_version_info_skel(c_skel, &null_version, result_pool));
349 /* src_left_version */
350 if (conflict->src_left_version)
351 SVN_ERR(prepend_version_info_skel(c_skel, conflict->src_left_version,
354 SVN_ERR(prepend_version_info_skel(c_skel, &null_version, result_pool));
357 skel_prepend_enum(c_skel, svn_wc__conflict_reason_map, conflict->reason,
361 skel_prepend_enum(c_skel, svn_wc__conflict_action_map, conflict->action,
365 skel_prepend_enum(c_skel, svn_wc__operation_map, conflict->operation,
369 SVN_ERR_ASSERT(conflict->node_kind == svn_node_dir
370 || conflict->node_kind == svn_node_file);
371 skel_prepend_enum(c_skel, node_kind_map, conflict->node_kind, result_pool);
373 /* Victim path (escaping separator chars). */
374 victim_basename = svn_dirent_basename(conflict->local_abspath, result_pool);
375 SVN_ERR_ASSERT(victim_basename[0]);
376 svn_skel__prepend(svn_skel__str_atom(victim_basename, result_pool), c_skel);
378 svn_skel__prepend(svn_skel__str_atom("conflict", result_pool), c_skel);
380 SVN_ERR_ASSERT(is_valid_conflict_skel(c_skel));
389 svn_wc__del_tree_conflict(svn_wc_context_t *wc_ctx,
390 const char *victim_abspath,
391 apr_pool_t *scratch_pool)
393 SVN_ERR_ASSERT(svn_dirent_is_absolute(victim_abspath));
395 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, victim_abspath,
396 FALSE, FALSE, TRUE, NULL,
403 svn_wc__add_tree_conflict(svn_wc_context_t *wc_ctx,
404 const svn_wc_conflict_description2_t *conflict,
405 apr_pool_t *scratch_pool)
407 svn_boolean_t existing_conflict;
408 svn_skel_t *conflict_skel;
411 SVN_ERR_ASSERT(conflict != NULL);
412 SVN_ERR_ASSERT(conflict->operation == svn_wc_operation_merge
413 || (conflict->reason != svn_wc_conflict_reason_moved_away
414 && conflict->reason != svn_wc_conflict_reason_moved_here)
417 /* Re-adding an existing tree conflict victim is an error. */
418 err = svn_wc__internal_conflicted_p(NULL, NULL, &existing_conflict,
419 wc_ctx->db, conflict->local_abspath,
423 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
424 return svn_error_trace(err);
426 svn_error_clear(err);
428 else if (existing_conflict)
429 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
430 _("Attempt to add tree conflict that already "
432 svn_dirent_local_style(conflict->local_abspath,
437 conflict_skel = svn_wc__conflict_skel_create(scratch_pool);
439 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(conflict_skel, wc_ctx->db,
440 conflict->local_abspath,
444 scratch_pool, scratch_pool));
446 switch(conflict->operation)
448 case svn_wc_operation_update:
450 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel,
451 conflict->src_left_version,
452 conflict->src_right_version,
453 scratch_pool, scratch_pool));
455 case svn_wc_operation_switch:
456 SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict_skel,
457 conflict->src_left_version,
458 conflict->src_right_version,
459 scratch_pool, scratch_pool));
461 case svn_wc_operation_merge:
462 SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_skel,
463 conflict->src_left_version,
464 conflict->src_right_version,
465 scratch_pool, scratch_pool));
469 return svn_error_trace(
470 svn_wc__db_op_mark_conflict(wc_ctx->db, conflict->local_abspath,
471 conflict_skel, NULL, scratch_pool));
476 svn_wc__get_tree_conflict(const svn_wc_conflict_description2_t **tree_conflict,
477 svn_wc_context_t *wc_ctx,
478 const char *local_abspath,
479 apr_pool_t *result_pool,
480 apr_pool_t *scratch_pool)
482 const apr_array_header_t *conflicts;
484 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
486 SVN_ERR(svn_wc__read_conflicts(&conflicts,
487 wc_ctx->db, local_abspath, FALSE,
488 scratch_pool, scratch_pool));
490 if (!conflicts || conflicts->nelts == 0)
492 *tree_conflict = NULL;
496 for (i = 0; i < conflicts->nelts; i++)
498 const svn_wc_conflict_description2_t *desc;
500 desc = APR_ARRAY_IDX(conflicts, i, svn_wc_conflict_description2_t *);
502 if (desc->kind == svn_wc_conflict_kind_tree)
504 *tree_conflict = svn_wc__conflict_description2_dup(desc,
510 *tree_conflict = NULL;