📄 diff.c
字号:
}
static svn_error_t *
merge_dir_deleted (svn_wc_adm_access_t *adm_access,
svn_wc_notify_state_t *state,
const char *path,
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 (path, &kind, subpool));
switch (kind)
{
case svn_node_dir:
{
merge_delete_notify_baton_t mdb;
mdb.ctx = merge_b->ctx;
mdb.path_skip = path;
svn_path_split (path, &parent_path, NULL, merge_b->pool);
SVN_ERR (svn_wc_adm_retrieve (&parent_access, adm_access, parent_path,
merge_b->pool));
err = svn_client__wc_delete (path, parent_access, merge_b->force,
merge_b->dry_run,
merge_delete_notify_func, &mdb,
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_file:
if (state)
*state = svn_wc_notify_state_obstructed;
break;
case svn_node_none:
/* dir 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_props_changed (svn_wc_adm_access_t *adm_access,
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_prop_diffs (state, path, adm_access, props,
FALSE, merge_b->dry_run, subpool);
if (err && (err->apr_err == SVN_ERR_ENTRY_NOT_FOUND))
{
/* 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;
}
/* The main callback table for 'svn merge'. */
static const svn_wc_diff_callbacks_t
merge_callbacks =
{
merge_file_changed,
merge_file_added,
merge_file_deleted,
merge_dir_added,
merge_dir_deleted,
merge_props_changed
};
/*-----------------------------------------------------------------------*/
/** The logic behind 'svn diff' and 'svn merge'. */
/* Hi! This is a comment left behind by Karl, and Ben is too afraid
to erase it at this time, because he's not fully confident that all
this knowledge has been grokked yet.
There are five cases:
1. path is not an URL and start_revision != end_revision
2. path is not an URL and start_revision == end_revision
3. path is an URL and start_revision != end_revision
4. path is an URL and start_revision == end_revision
5. path is not an URL and no revisions given
With only one distinct revision the working copy provides the
other. When path is an URL there is no working copy. Thus
1: compare repository versions for URL coresponding to working copy
2: compare working copy against repository version
3: compare repository versions for URL
4: nothing to do.
5: compare working copy against text-base
Case 4 is not as stupid as it looks, for example it may occur if
the user specifies two dates that resolve to the same revision. */
/* Helper function: given a working-copy PATH, return its associated
url in *URL, allocated in POOL. If PATH is *already* a URL, that's
fine, just set *URL = PATH. */
static svn_error_t *
convert_to_url (const char **url,
const char *path,
apr_pool_t *pool)
{
svn_wc_adm_access_t *adm_access; /* ### FIXME local */
const svn_wc_entry_t *entry;
if (svn_path_is_url (path))
{
*url = path;
return SVN_NO_ERROR;
}
/* ### This may not be a good idea, see issue 880 */
SVN_ERR (svn_wc_adm_probe_open2(&adm_access, NULL, path, FALSE,
0, pool));
SVN_ERR (svn_wc_entry (&entry, path, adm_access, FALSE, pool));
SVN_ERR (svn_wc_adm_close (adm_access));
if (! entry)
return svn_error_createf (SVN_ERR_UNVERSIONED_RESOURCE, NULL,
_("'%s' is not under version control"), path);
if (entry->url)
*url = apr_pstrdup (pool, entry->url);
else
*url = apr_pstrdup (pool, entry->copyfrom_url);
return SVN_NO_ERROR;
}
/* URL1/PATH1, URL2/PATH2, and TARGET_WCPATH all better be
directories. For the single file case, the caller does the merging
manually. PATH1 and PATH2 can be NULL.
If PEG_REVISION is specified, then INITIAL_PATH2 is the path to peg
off of, unless it is NULL, in which case INITIAL_URL2 is the peg
path. The actual two paths to compare are then found by tracing
copy history from this peg path to INITIAL_REVISION2 and
INITIAL_REVISION1.
*/
static svn_error_t *
do_merge (const char *initial_URL1,
const char *initial_path1,
const svn_opt_revision_t *initial_revision1,
const char *initial_URL2,
const char *initial_path2,
const svn_opt_revision_t *initial_revision2,
const svn_opt_revision_t *peg_revision,
const char *target_wcpath,
svn_wc_adm_access_t *adm_access,
svn_boolean_t recurse,
svn_boolean_t ignore_ancestry,
svn_boolean_t dry_run,
const svn_wc_diff_callbacks_t *callbacks,
void *callback_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_revnum_t start_revnum, end_revnum;
void *ra_baton, *session, *session2;
svn_ra_plugin_t *ra_lib;
const svn_ra_reporter_t *reporter;
void *report_baton;
const svn_delta_editor_t *diff_editor;
void *diff_edit_baton;
struct merge_cmd_baton *merge_b = callback_baton;
const char *URL1, *URL2, *path1, *path2;
svn_opt_revision_t *revision1, *revision2;
/* Sanity check -- ensure that we have valid revisions to look at. */
if ((initial_revision1->kind == svn_opt_revision_unspecified)
|| (initial_revision2->kind == svn_opt_revision_unspecified))
{
return svn_error_create
(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Not all required revisions are specified"));
}
/* Establish first RA session to URL1. */
SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, initial_URL1, pool));
/* If we are performing a pegged merge, we need to find out what our
actual URLs will be. */
if (peg_revision->kind != svn_opt_revision_unspecified)
{
SVN_ERR (svn_client__repos_locations (&URL1, &revision1,
&URL2, &revision2,
initial_path2 ? initial_path2
: initial_URL2,
peg_revision,
initial_revision1,
initial_revision2,
ra_lib, ctx, pool));
merge_b->url = URL2;
path1 = NULL;
path2 = NULL;
merge_b->path = NULL;
}
else
{
URL1 = initial_URL1;
URL2 = initial_URL2;
path1 = initial_path1;
path2 = initial_path2;
revision1 = apr_pcalloc (pool, sizeof (*revision1));
*revision1 = *initial_revision1;
revision2 = apr_pcalloc (pool, sizeof (*revision2));
*revision2 = *initial_revision2;
}
SVN_ERR (svn_client__open_ra_session (&session, ra_lib, URL1, NULL,
NULL, NULL, FALSE, TRUE,
ctx, pool));
/* Resolve the revision numbers. */
SVN_ERR (svn_client__get_revision_number
(&start_revnum, ra_lib, session, revision1, path1, pool));
SVN_ERR (svn_client__get_revision_number
(&end_revnum, ra_lib, session, revision2, path2, pool));
/* Open a second session used to request individual file
contents. Although a session can be used for multiple requests, it
appears that they must be sequential. Since the first request, for
the diff, is still being processed the first session cannot be
reused. This applies to ra_dav, ra_local does not appears to have
this limitation. */
SVN_ERR (svn_client__open_ra_session (&session2, ra_lib, URL1, NULL,
NULL, NULL, FALSE, TRUE,
ctx, pool));
SVN_ERR (svn_client__get_diff_editor (target_wcpath,
adm_access,
callbacks,
callback_baton,
recurse,
dry_run,
ra_lib, session2,
start_revnum,
ctx->notify_func,
ctx->notify_baton,
ctx->cancel_func,
ctx->cancel_baton,
&diff_editor,
&diff_edit_baton,
pool));
SVN_ERR (ra_lib->do_diff (session,
&reporter, &report_baton,
end_revnum,
"",
recurse,
ignore_ancestry,
URL2,
diff_editor, diff_edit_baton, pool));
SVN_ERR (reporter->set_path (report_baton, "", start_revnum, FALSE, pool));
SVN_ERR (reporter->finish_report (report_baton, pool));
return SVN_NO_ERROR;
}
/* Get REVISION of the file at URL. SOURCE is a path that refers to that
file's entry in the working copy, or NULL if we don't have one. Return in
*FILENAME the name of a file containing the file contents, in *PROPS a hash
containing the properties and in *REV the revision. All allocation occurs
in POOL. */
static svn_error_t *
single_file_merge_get_file (const char **filename,
apr_hash_t **props,
svn_revnum_t *rev,
const char *url,
const char *path,
const svn_opt_revision_t *revision,
void *ra_baton,
struct merge_cmd_baton *merge_b,
apr_pool_t *pool)
{
svn_ra_plugin_t *ra_lib;
void *session;
apr_file_t *fp;
SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, url, pool));
SVN_ERR (svn_client__open_ra_session (&session, ra_lib, url, NULL,
NULL, NULL, FALSE, TRUE,
merge_b->ctx, pool));
SVN_ERR (svn_client__get_revision_number (rev, ra_lib, session, revision,
path, pool));
SVN_ERR (svn_io_open_unique_file (&fp, filename,
merge_b->target, ".tmp",
FALSE, pool));
SVN_ERR (ra_lib->get_file (session, "", *rev,
svn_stream_from_aprfile (fp, pool),
NULL, props, pool));
SVN_ERR (svn_io_file_close (fp, pool));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -