📄 diff.c
字号:
/* ### someday, we should just be able to compare
identity-strings here. */
SVN_ERR (svn_io_files_contents_same_p (&same_contents,
older, mine, subpool));
if (same_contents)
{
if (! 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_merge (older, yours, mine, adm_access,
left_label, right_label, target_label,
merge_b->dry_run, &merge_outcome,
merge_b->diff3_cmd, 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 (state)
{
if (merge_outcome == svn_wc_merge_conflict)
*state = svn_wc_notify_state_conflicted;
else if (has_local_mods)
*state = svn_wc_notify_state_merged;
else if (merge_outcome == svn_wc_merge_merged)
*state = svn_wc_notify_state_changed;
else if (merge_outcome == svn_wc_merge_no_merge)
*state = svn_wc_notify_state_missing;
else /* merge_outcome == svn_wc_merge_unchanged */
*state = svn_wc_notify_state_unchanged;
}
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
merge_file_added (svn_wc_adm_access_t *adm_access,
svn_wc_notify_state_t *state,
const char *mine,
const char *older,
const char *yours,
svn_revnum_t rev1,
svn_revnum_t rev2,
const char *mimetype1,
const char *mimetype2,
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;
/* 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 (state)
*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 (state)
*state = svn_wc_notify_state_obstructed;
return SVN_NO_ERROR;
}
if (! merge_b->dry_run)
{
child = svn_path_is_child(merge_b->target, mine, merge_b->pool);
assert (child != NULL);
copyfrom_url = svn_path_url_add_component (merge_b->url, child,
merge_b->pool);
SVN_ERR (check_schema_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_file (mine, adm_access,
yours,
apr_hash_make (merge_b->pool),
copyfrom_url,
rev2,
merge_b->pool));
}
if (state)
*state = svn_wc_notify_state_changed;
}
break;
case svn_node_dir:
{
/* this will make the repos_editor send a 'skipped' message */
if (state)
*state = svn_wc_notify_state_obstructed;
}
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 (state)
*state = svn_wc_notify_state_obstructed;
}
else
{
SVN_ERR (merge_file_changed (adm_access, state,
mine, older, yours,
rev1, rev2,
mimetype1, mimetype2,
baton));
}
break;
}
default:
if (state)
*state = svn_wc_notify_state_unknown;
break;
}
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
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,
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;
/* 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 (state)
*state = svn_wc_notify_state_missing;
return SVN_NO_ERROR;
}
SVN_ERR (svn_io_check_path (mine, &kind, subpool));
switch (kind)
{
case svn_node_file:
svn_path_split (mine, &parent_path, NULL, merge_b->pool);
SVN_ERR (svn_wc_adm_retrieve (&parent_access, adm_access, parent_path,
merge_b->pool));
{
/* Passing NULL for the notify_func and notify_baton because
* repos_diff.c:delete_item will do it for us. */
err = svn_client__wc_delete (mine, parent_access, merge_b->force,
merge_b->dry_run,
NULL, NULL,
merge_b->ctx, subpool);
}
if (err && state)
{
*state = svn_wc_notify_state_obstructed;
svn_error_clear (err);
}
else if (state)
{
*state = svn_wc_notify_state_changed;
}
break;
case svn_node_dir:
if (state)
*state = svn_wc_notify_state_obstructed;
break;
case svn_node_none:
/* file is already non-existent, this is a no-op. */
if (state)
*state = svn_wc_notify_state_missing;
break;
default:
if (state)
*state = svn_wc_notify_state_unknown;
break;
}
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
merge_dir_added (svn_wc_adm_access_t *adm_access,
svn_wc_notify_state_t *state,
const char *path,
svn_revnum_t rev,
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 svn_wc_entry_t *entry;
const char *copyfrom_url, *child;
/* 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 (state)
*state = svn_wc_notify_state_missing;
return SVN_NO_ERROR;
}
child = svn_path_is_child (merge_b->target, path, subpool);
assert (child != NULL);
copyfrom_url = svn_path_url_add_component (merge_b->url, child, subpool);
SVN_ERR (check_schema_match (adm_access, copyfrom_url));
SVN_ERR (svn_io_check_path (path, &kind, subpool));
switch (kind)
{
case svn_node_none:
SVN_ERR (svn_wc_entry (&entry, path, adm_access, FALSE, subpool));
if (entry && entry->schedule != svn_wc_schedule_delete)
{
/* Versioned but missing */
if (state)
*state = svn_wc_notify_state_obstructed;
return SVN_NO_ERROR;
}
if (! merge_b->dry_run)
{
SVN_ERR (svn_io_make_dir_recursively (path, subpool));
SVN_ERR (svn_wc_add (path, adm_access,
copyfrom_url, rev,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL, /* don't pass notification func! */
merge_b->pool));
}
if (state)
*state = svn_wc_notify_state_changed;
break;
case svn_node_dir:
/* Adding an unversioned directory doesn't destroy data */
SVN_ERR (svn_wc_entry (&entry, path, adm_access, TRUE, subpool));
if (! entry || (entry && entry->schedule == svn_wc_schedule_delete))
{
if (!merge_b->dry_run)
SVN_ERR (svn_wc_add (path, adm_access,
copyfrom_url, rev,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL, /* no notification func! */
merge_b->pool));
if (state)
*state = svn_wc_notify_state_changed;
}
else
{
if (state)
*state = svn_wc_notify_state_obstructed;
}
break;
case svn_node_file:
if (state)
*state = svn_wc_notify_state_obstructed;
break;
default:
if (state)
*state = svn_wc_notify_state_unknown;
break;
}
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
/* Struct used for as the baton for calling merge_delete_notify_func(). */
typedef struct merge_delete_notify_baton_t
{
svn_client_ctx_t *ctx;
/** path to skip */
const char *path_skip;
} merge_delete_notify_baton_t;
/* Notify callback function that wraps the normal callback
* function to remove a notification that will be sent twice
* and set the proper action. */
static void
merge_delete_notify_func (void *baton,
const char *path,
svn_wc_notify_action_t action,
svn_node_kind_t kind,
const char *mime_type,
svn_wc_notify_state_t content_state,
svn_wc_notify_state_t prop_state,
svn_revnum_t revision)
{
merge_delete_notify_baton_t *mdb = (merge_delete_notify_baton_t *) baton;
/* Skip the notification for the path we called svn_client__wc_delete() with,
* because it will be outputed by repos_diff.c:delete_item */
if (strcmp (path, mdb->path_skip) == 0)
return;
/* svn_client__wc_delete() is written primarily for scheduling operations not
* update operations. Since merges are update operations we need to alter
* the delete notification to show as an update not a schedule so alter
* the action. */
if (action == svn_wc_notify_delete)
action = svn_wc_notify_update_delete;
if (mdb->ctx->notify_func)
(*mdb->ctx->notify_func) (mdb->ctx->notify_baton, path, action, kind,
mime_type, content_state, prop_state, revision);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -