📄 diff.c
字号:
svn_wc_notify_state_t *state, const char *path, const apr_array_header_t *propchanges, apr_hash_t *original_props, void *baton){ apr_array_header_t *props; struct merge_cmd_baton *merge_b = baton; apr_pool_t *subpool = svn_pool_create(merge_b->pool); svn_error_t *err; SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props, subpool)); /* We only want to merge "regular" version properties: by definition, 'svn merge' shouldn't touch any data within .svn/ */ if (props->nelts) { err = svn_wc_merge_props(state, path, adm_access, original_props, props, FALSE, merge_b->dry_run, subpool); if (err && (err->apr_err == SVN_ERR_ENTRY_NOT_FOUND || err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE)) { /* if the entry doesn't exist in the wc, just 'skip' over this part of the tree-delta. */ if (state) *state = svn_wc_notify_state_missing; svn_error_clear(err); return SVN_NO_ERROR; } else if (err) return err; } svn_pool_destroy(subpool); return SVN_NO_ERROR;}/* A svn_wc_diff_callbacks2_t function. */static svn_error_t *merge_file_changed(svn_wc_adm_access_t *adm_access, svn_wc_notify_state_t *content_state, svn_wc_notify_state_t *prop_state, const char *mine, const char *older, const char *yours, svn_revnum_t older_rev, svn_revnum_t yours_rev, const char *mimetype1, const char *mimetype2, const apr_array_header_t *prop_changes, apr_hash_t *original_props, void *baton){ struct merge_cmd_baton *merge_b = baton; apr_pool_t *subpool = svn_pool_create(merge_b->pool); /* xgettext: the '.working', '.merge-left.r%ld' and '.merge-right.r%ld' strings are used to tag onto a filename in case of a merge conflict */ const char *target_label = _(".working"); const char *left_label = apr_psprintf(subpool, _(".merge-left.r%ld"), older_rev); const char *right_label = apr_psprintf(subpool, _(".merge-right.r%ld"), yours_rev); svn_boolean_t has_local_mods; svn_boolean_t merge_required = TRUE; enum svn_wc_merge_outcome_t merge_outcome; /* Easy out: no access baton means there ain't no merge target */ if (adm_access == NULL) { if (content_state) *content_state = svn_wc_notify_state_missing; if (prop_state) *prop_state = svn_wc_notify_state_missing; return SVN_NO_ERROR; } /* Other easy outs: if the merge target isn't under version control, or is just missing from disk, fogettaboutit. There's no way svn_wc_merge2() can do the merge. */ { const svn_wc_entry_t *entry; svn_node_kind_t kind; SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool)); SVN_ERR(svn_io_check_path(mine, &kind, subpool)); /* ### a future thought: if the file is under version control, but the working file is missing, maybe we can 'restore' the working file from the text-base, and then allow the merge to run? */ if ((! entry) || (kind != svn_node_file)) { if (content_state) *content_state = svn_wc_notify_state_missing; if (prop_state) *prop_state = svn_wc_notify_state_missing; return SVN_NO_ERROR; } } /* This callback is essentially no more than a wrapper around svn_wc_merge2(). Thank goodness that all the diff-editor-mechanisms are doing the hard work of getting the fulltexts! */ /* Do property merge before text merge so that keyword expansion takes into account the new property values. */ if (prop_changes->nelts > 0) SVN_ERR(merge_props_changed(adm_access, prop_state, mine, prop_changes, original_props, baton)); else if (prop_state) *prop_state = svn_wc_notify_state_unchanged; if (older) { SVN_ERR(svn_wc_text_modified_p(&has_local_mods, mine, FALSE, adm_access, subpool)); /* Special case: if a binary file isn't locally modified, and is exactly identical to the 'left' side of the merge, then don't allow svn_wc_merge to produce a conflict. Instead, just overwrite the working file with the 'right' side of the merge. Alternately, if the 'left' side of the merge doesn't exist in the repository, and the 'right' side of the merge is identical to the WC, pretend we did the merge (a no-op). */ if ((! has_local_mods) && ((mimetype1 && svn_mime_type_is_binary(mimetype1)) || (mimetype2 && svn_mime_type_is_binary(mimetype2)))) { /* For adds, the 'left' side of the merge doesn't exist. */ svn_boolean_t older_revision_exists = !merge_b->add_necessitated_merge; svn_boolean_t same_contents; SVN_ERR(svn_io_files_contents_same_p(&same_contents, (older_revision_exists ? older : yours), mine, subpool)); if (same_contents) { if (older_revision_exists && !merge_b->dry_run) SVN_ERR(svn_io_file_rename(yours, mine, subpool)); merge_outcome = svn_wc_merge_merged; merge_required = FALSE; } } if (merge_required) { SVN_ERR(svn_wc_merge2(&merge_outcome, older, yours, mine, adm_access, left_label, right_label, target_label, merge_b->dry_run, merge_b->diff3_cmd, merge_b->merge_options, subpool)); } /* Philip asks "Why?" Why does the notification depend on whether the file had modifications before the merge? If the merge didn't change the file because the local mods already included the change why does that result it "merged" notification? That's information available through the status command, while the fact that the merge didn't change the file is lost :-( */ if (content_state) { if (merge_outcome == svn_wc_merge_conflict) *content_state = svn_wc_notify_state_conflicted; else if (has_local_mods) *content_state = svn_wc_notify_state_merged; else if (merge_outcome == svn_wc_merge_merged) *content_state = svn_wc_notify_state_changed; else if (merge_outcome == svn_wc_merge_no_merge) *content_state = svn_wc_notify_state_missing; else /* merge_outcome == svn_wc_merge_unchanged */ *content_state = svn_wc_notify_state_unchanged; } } svn_pool_destroy(subpool); return SVN_NO_ERROR;}/* A svn_wc_diff_callbacks2_t function. */static svn_error_t *merge_file_added(svn_wc_adm_access_t *adm_access, svn_wc_notify_state_t *content_state, svn_wc_notify_state_t *prop_state, const char *mine, const char *older, const char *yours, svn_revnum_t rev1, svn_revnum_t rev2, const char *mimetype1, const char *mimetype2, const apr_array_header_t *prop_changes, apr_hash_t *original_props, void *baton){ struct merge_cmd_baton *merge_b = baton; apr_pool_t *subpool = svn_pool_create(merge_b->pool); svn_node_kind_t kind; const char *copyfrom_url; const char *child; int i; apr_hash_t *new_props; /* In most cases, we just leave prop_state as unknown, and let the content_state what happened, so we set prop_state here to avoid that below. */ if (prop_state) *prop_state = svn_wc_notify_state_unknown; /* Apply the prop changes to a new hash table. */ new_props = apr_hash_copy(subpool, original_props); for (i = 0; i < prop_changes->nelts; ++i) { const svn_prop_t *prop = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t); apr_hash_set(new_props, prop->name, APR_HASH_KEY_STRING, prop->value); } /* Easy out: if we have no adm_access for the parent directory, then this portion of the tree-delta "patch" must be inapplicable. Send a 'missing' state back; the repos-diff editor should then send a 'skip' notification. */ if (! adm_access) { if (merge_b->dry_run && merge_b->added_path && svn_path_is_child(merge_b->added_path, mine, subpool)) { if (content_state) *content_state = svn_wc_notify_state_changed; if (prop_state && apr_hash_count(new_props)) *prop_state = svn_wc_notify_state_changed; } else *content_state = svn_wc_notify_state_missing; return SVN_NO_ERROR; } SVN_ERR(svn_io_check_path(mine, &kind, subpool)); switch (kind) { case svn_node_none: { const svn_wc_entry_t *entry; SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool)); if (entry && entry->schedule != svn_wc_schedule_delete) { /* It's versioned but missing. */ if (content_state) *content_state = svn_wc_notify_state_obstructed; return SVN_NO_ERROR; } if (! merge_b->dry_run) { child = svn_path_is_child(merge_b->target, mine, subpool); assert(child != NULL); copyfrom_url = svn_path_url_add_component(merge_b->url, child, subpool); SVN_ERR(check_scheme_match(adm_access, copyfrom_url)); /* Since 'mine' doesn't exist, and this is 'merge_file_added', I hope it's safe to assume that 'older' is empty, and 'yours' is the full file. Merely copying 'yours' to 'mine', isn't enough; we need to get the whole text-base and props installed too, just as if we had called 'svn cp wc wc'. */ SVN_ERR(svn_wc_add_repos_file2(mine, adm_access, yours, NULL, new_props, NULL, copyfrom_url, rev2, subpool)); } if (content_state) *content_state = svn_wc_notify_state_changed; if (prop_state && apr_hash_count(new_props)) *prop_state = svn_wc_notify_state_changed; } break; case svn_node_dir: if (content_state) { /* directory already exists, is it under version control? */ const svn_wc_entry_t *entry; SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool)); if (entry && dry_run_deleted_p(merge_b, mine)) *content_state = svn_wc_notify_state_changed; else /* this will make the repos_editor send a 'skipped' message */ *content_state = svn_wc_notify_state_obstructed; } break; case svn_node_file: { /* file already exists, is it under version control? */ const svn_wc_entry_t *entry; SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool)); /* If it's an unversioned file, don't touch it. If its scheduled for deletion, then rm removed it from the working copy and the user must have recreated it, don't touch it */ if (!entry || entry->schedule == svn_wc_schedule_delete) { /* this will make the repos_editor send a 'skipped' message */ if (content_state) *content_state = svn_wc_notify_state_obstructed; } else { if (dry_run_deleted_p(merge_b, mine)) { if (content_state) *content_state = svn_wc_notify_state_changed; } else { /* Indicate that we merge because of an add to handle a special case for binary files with no local mods. */ merge_b->add_necessitated_merge = TRUE; SVN_ERR(merge_file_changed(adm_access, content_state, prop_state, mine, older, yours, rev1, rev2, mimetype1, mimetype2, prop_changes, original_props, baton)); /* Reset the state so that the baton can safely be reused in subsequent ops occurring during this merge. */ merge_b->add_necessitated_merge = FALSE; } } break; } default: if (content_state) *content_state = svn_wc_notify_state_unknown; break; } svn_pool_destroy(subpool); return SVN_NO_ERROR;}/* A svn_wc_diff_callbacks2_t function. */static svn_error_t *merge_file_deleted(svn_wc_adm_access_t *adm_access, svn_wc_notify_state_t *state, const char *mine, const char *older, const char *yours, const char *mimetype1, const char *mimetype2, apr_hash_t *original_props, void *baton){ struct merge_cmd_baton *merge_b = baton; apr_pool_t *subpool = svn_pool_create(merge_b->pool); svn_node_kind_t kind; svn_wc_adm_access_t *parent_access; const char *parent_path; svn_error_t *err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -