📄 tree.c
字号:
/* Determine if the properties of two path/root combinations are different. Set *CHANGED_P to TRUE if the properties at PATH1 under ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise. Both roots must be in the same filesystem. */static svn_error_t *fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, const char *path1, svn_fs_root_t *root2, const char *path2, apr_pool_t *pool){ dag_node_t *node1, *node2; /* Check that roots are in the same fs. */ if (root1->fs != root2->fs) return svn_error_create (SVN_ERR_FS_GENERAL, NULL, _("Cannot compare property value between two different filesystems")); SVN_ERR(get_dag(&node1, root1, path1, pool)); SVN_ERR(get_dag(&node2, root2, path2, pool)); SVN_ERR(svn_fs_fs__things_different(changed_p, NULL, node1, node2, pool)); return SVN_NO_ERROR;}/* Merges and commits. *//* Set ARGS->node to the root node of ARGS->root. */static svn_error_t *get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool){ SVN_ERR(get_dag(node, root, "", pool)); return SVN_NO_ERROR;}static svn_error_t *update_ancestry(svn_fs_t *fs, const svn_fs_id_t *source_id, const svn_fs_id_t *target_id, const char *txn_id, const char *target_path, int source_pred_count, apr_pool_t *pool){ node_revision_t *noderev; if (svn_fs_fs__id_txn_id(target_id) == NULL) return svn_error_createf (SVN_ERR_FS_NOT_MUTABLE, NULL, _("Unexpected immutable node at '%s'"), target_path); SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, target_id, pool)); noderev->predecessor_id = source_id; noderev->predecessor_count = source_pred_count; if (noderev->predecessor_count != -1) noderev->predecessor_count++; SVN_ERR(svn_fs_fs__put_node_revision(fs, target_id, noderev, pool)); return SVN_NO_ERROR;}/* Set the contents of CONFLICT_PATH to PATH, and return an SVN_ERR_FS_CONFLICT error that indicates that there was a conflict at PATH. Perform all allocations in POOL (except the allocation of CONFLICT_PATH, which should be handled outside this function). */static svn_error_t *conflict_err(svn_stringbuf_t *conflict_path, const char *path){ svn_stringbuf_set(conflict_path, path); return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, _("Conflict at '%s'"), path);}/* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR * and TARGET must be distinct node revisions. TARGET_PATH should * correspond to TARGET's full path in its filesystem, and is used for * reporting conflict location. * * SOURCE, TARGET, and ANCESTOR are generally directories; this * function recursively merges the directories' contents. If any are * files, this function simply returns an error whenever SOURCE, * TARGET, and ANCESTOR are all distinct node revisions. * * If there are differences between ANCESTOR and SOURCE that conflict * with changes between ANCESTOR and TARGET, this function returns an * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the * conflicting node in TARGET, with TARGET_PATH prepended as a path. * * If there are no conflicting differences, CONFLICT_P is updated to * the empty string. * * CONFLICT_P must point to a valid svn_stringbuf_t. * * Do any necessary temporary allocation in POOL. */static svn_error_t *merge(svn_stringbuf_t *conflict_p, const char *target_path, dag_node_t *target, dag_node_t *source, dag_node_t *ancestor, const char *txn_id, apr_pool_t *pool){ const svn_fs_id_t *source_id, *target_id, *ancestor_id; apr_hash_t *s_entries, *t_entries, *a_entries; apr_hash_index_t *hi; svn_fs_t *fs; apr_pool_t *iterpool; int pred_count; /* Make sure everyone comes from the same filesystem. */ fs = svn_fs_fs__dag_get_fs(ancestor); if ((fs != svn_fs_fs__dag_get_fs(source)) || (fs != svn_fs_fs__dag_get_fs(target))) { return svn_error_create (SVN_ERR_FS_CORRUPT, NULL, _("Bad merge; ancestor, source, and target not all in same fs")); } /* We have the same fs, now check it. */ SVN_ERR(svn_fs_fs__check_fs(fs)); source_id = svn_fs_fs__dag_get_id(source); target_id = svn_fs_fs__dag_get_id(target); ancestor_id = svn_fs_fs__dag_get_id(ancestor); /* It's improper to call this function with ancestor == target. */ if (svn_fs_fs__id_eq(ancestor_id, target_id)) { svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool); return svn_error_createf (SVN_ERR_FS_GENERAL, NULL, _("Bad merge; target '%s' has id '%s', same as ancestor"), target_path, id_str->data); } svn_stringbuf_setempty(conflict_p); /* Base cases: * Either no change made in source, or same change as made in target. * Both mean nothing to merge here. */ if (svn_fs_fs__id_eq(ancestor_id, source_id) || (svn_fs_fs__id_eq(source_id, target_id))) return SVN_NO_ERROR; /* Else proceed, knowing all three are distinct node revisions. * * How to merge from this point: * * if (not all 3 are directories) * { * early exit with conflict; * } * * // Property changes may only be made to up-to-date * // directories, because once the client commits the prop * // change, it bumps the directory's revision, and therefore * // must be able to depend on there being no other changes to * // that directory in the repository. * if (target's property list differs from ancestor's) * conflict; * * For each entry NAME in the directory ANCESTOR: * * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of * the name within ANCESTOR, SOURCE, and TARGET respectively. * (Possibly null if NAME does not exist in SOURCE or TARGET.) * * If ANCESTOR-ENTRY == SOURCE-ENTRY, then: * No changes were made to this entry while the transaction was in * progress, so do nothing to the target. * * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then: * A change was made to this entry while the transaction was in * process, but the transaction did not touch this entry. Replace * TARGET-ENTRY with SOURCE-ENTRY. * * Else: * Changes were made to this entry both within the transaction and * to the repository while the transaction was in progress. They * must be merged or declared to be in conflict. * * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a * double delete; flag a conflict. * * If any of the three entries is of type file, declare a conflict. * * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct * modification of ANCESTOR-ENTRY (determine by comparing the * node-id fields), declare a conflict. A replacement is * incompatible with a modification or other replacement--even * an identical replacement. * * Direct modifications were made to the directory ANCESTOR-ENTRY * in both SOURCE and TARGET. Recursively merge these * modifications. * * For each leftover entry NAME in the directory SOURCE: * * If NAME exists in TARGET, declare a conflict. Even if SOURCE and * TARGET are adding exactly the same thing, two additions are not * auto-mergeable with each other. * * Add NAME to TARGET with the entry from SOURCE. * * Now that we are done merging the changes from SOURCE into the * directory TARGET, update TARGET's predecessor to be SOURCE. */ if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir) || (svn_fs_fs__dag_node_kind(target) != svn_node_dir) || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir)) { return conflict_err(conflict_p, target_path); } /* Possible early merge failure: if target and ancestor have different property lists, then the merge should fail. Propchanges can *only* be committed on an up-to-date directory. ### TODO: Please see issue #418 about the inelegance of this. */ { node_revision_t *tgt_nr, *anc_nr; /* Get node revisions for our id's. */ SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool)); SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool)); /* Now compare the prop-keys of the skels. Note that just because the keys are different -doesn't- mean the proplists have different contents. But merge() isn't concerned with contents; it doesn't do a brute-force comparison on textual contents, so it won't do that here either. Checking to see if the propkey atoms are `equal' is enough. */ if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep)) { return conflict_err(conflict_p, target_path); } } /* ### todo: it would be more efficient to simply check for a NULL entries hash where necessary below than to allocate an empty hash here, but another day, another day... */ SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool)); s_entries = svn_fs_fs__copy_dir_entries(s_entries, pool); SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool)); t_entries = svn_fs_fs__copy_dir_entries(t_entries, pool); SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool)); a_entries = svn_fs_fs__copy_dir_entries(a_entries, pool); /* for each entry E in a_entries... */ iterpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, a_entries); hi; hi = apr_hash_next(hi)) { svn_fs_dirent_t *s_entry, *t_entry, *a_entry; const void *key; void *val; apr_ssize_t klen; svn_pool_clear(iterpool); /* KEY will be the entry name in ancestor, VAL the dirent */ apr_hash_this(hi, &key, &klen, &val); a_entry = val; s_entry = apr_hash_get(s_entries, key, klen); t_entry = apr_hash_get(t_entries, key, klen); /* No changes were made to this entry while the transaction was in progress, so do nothing to the target. */ if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id)) goto end; /* A change was made to this entry while the transaction was in process, but the transaction did not touch this entry. */ else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id)) { if (s_entry) { SVN_ERR(svn_fs_fs__dag_set_entry(target, key, s_entry->id, s_entry->kind, txn_id, iterpool)); } else { SVN_ERR(svn_fs_fs__dag_delete(target, key, txn_id, iterpool)); } } /* Changes were made to this entry both within the transaction and to the repository while the transaction was in progress. They must be merged or declared to be in conflict. */ else { dag_node_t *s_ent_node, *t_ent_node, *a_ent_node; const char *new_tpath; /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a double delete; flag a conflict. */ if (s_entry == NULL || t_entry == NULL) return conflict_err(conflict_p, svn_path_join(target_path, a_entry->name, iterpool)); /* If any of the three entries is of type file, flag a conflict. */ if (s_entry->kind == svn_node_file || t_entry->kind == svn_node_file || a_entry->kind == svn_node_file) return conflict_err(conflict_p, svn_path_join(target_path, a_entry->name, iterpool)); /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct modification of ANCESTOR-ENTRY, declare a conflict. */ if (strcmp(svn_fs_fs__id_node_id(s_entry->id), svn_fs_fs__id_node_id(a_entry->id)) != 0 || strcmp(svn_fs_fs__id_copy_id(s_entry->id), svn_fs_fs__id_copy_id(a_entry->id)) != 0 || strcmp(svn_fs_fs__id_node_id(t_entry->id), svn_fs_fs__id_node_id(a_entry->id)) != 0 || strcmp(svn_fs_fs__id_copy_id(t_entry->id), svn_fs_fs__id_copy_id(a_entry->id)) != 0) return conflict_err(conflict_p, svn_path_join(target_path, a_entry->name, iterpool)); /* Direct modifications were made to the directory ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively merge these modifications. */ SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs, s_entry->id, iterpool)); SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs, t_entry->id, iterpool)); SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs, a_entry->id, iterpool)); new_tpath = svn_path_join(target_path, t_entry->name, iterpool); SVN_ERR(merge(conflict_p, new_tpath, t_ent_node, s_ent_node, a_ent_node, txn_id, iterpool)); } /* We've taken care of any possible implications E could have. Remove it from source_entries, so it's easy later to loop over all the source entries that didn't exist in ancestor_entries. */ end: apr_hash_set(s_entries, key, klen, NULL); } /* For each entry E in source but not in ancestor */ for (hi = apr_hash_first(pool, s_entries); hi; hi = apr_hash_next(hi)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -