📄 diff.c
字号:
path2 = apr_psprintf (subpool, "%s\t(...%s)", path, path2);
else
path2 = apr_psprintf (subpool, "%s\t(.../%s)", path, path2);
label1 = diff_label (path1, rev1, subpool);
label2 = diff_label (path2, rev2, subpool);
/* Possible easy-out: if either mime-type is binary, don't attempt
to generate a viewable diff at all. Print a warning and exit. */
if (mimetype1)
mt1_binary = svn_mime_type_is_binary (mimetype1);
if (mimetype2)
mt2_binary = svn_mime_type_is_binary (mimetype2);
if (mt1_binary || mt2_binary)
{
/* Print out the diff header. */
SVN_ERR (svn_stream_printf (os, subpool,
"Index: %s" APR_EOL_STR
"%s" APR_EOL_STR, path, equal_string));
SVN_ERR (svn_stream_printf
(os, subpool,
_("Cannot display: file marked as a binary type.%s"),
APR_EOL_STR));
if (mt1_binary && !mt2_binary)
SVN_ERR (svn_stream_printf (os, subpool,
"svn:mime-type = %s" APR_EOL_STR,
mimetype1));
else if (mt2_binary && !mt1_binary)
SVN_ERR (svn_stream_printf (os, subpool,
"svn:mime-type = %s" APR_EOL_STR,
mimetype2));
else if (mt1_binary && mt2_binary)
{
if (strcmp (mimetype1, mimetype2) == 0)
SVN_ERR (svn_stream_printf
(os, subpool,
"svn:mime-type = %s" APR_EOL_STR,
mimetype1));
else
SVN_ERR (svn_stream_printf
(os, subpool,
"svn:mime-type = (%s, %s)" APR_EOL_STR,
mimetype1, mimetype2));
}
/* Exit early. */
if (state)
*state = svn_wc_notify_state_unknown;
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
/* Find out if we need to run an external diff */
if (diff_cmd_baton->config)
{
svn_config_t *cfg = apr_hash_get (diff_cmd_baton->config,
SVN_CONFIG_CATEGORY_CONFIG,
APR_HASH_KEY_STRING);
svn_config_get (cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF_CMD, NULL);
}
if (diff_cmd)
{
/* Print out the diff header. */
SVN_ERR (svn_stream_printf (os, subpool,
"Index: %s" APR_EOL_STR
"%s" APR_EOL_STR, path, equal_string));
/* Close the stream (flush) */
SVN_ERR (svn_stream_close (os));
SVN_ERR (svn_io_run_diff (".", args, nargs, label1, label2,
tmpfile1, tmpfile2,
&exitcode, diff_cmd_baton->outfile, errfile,
diff_cmd, subpool));
}
else /* use libsvn_diff to generate the diff */
{
svn_diff_t *diff;
/* We don't currently support any options (well, other than -u, since we
default to unified diff output anyway), so if we received anything
other than that it's an error. */
if (diff_cmd_baton->options)
{
for (i = 0; i < diff_cmd_baton->options->nelts; ++i)
{
const char *arg
= ((const char **)(diff_cmd_baton->options->elts))[i];
if (strcmp(arg, "-u") == 0)
continue;
else
return svn_error_createf(SVN_ERR_INVALID_DIFF_OPTION, NULL,
_("'%s' is not supported"), arg);
}
}
SVN_ERR (svn_diff_file_diff (&diff, tmpfile1, tmpfile2, subpool));
if (svn_diff_contains_diffs (diff) || diff_cmd_baton->force_diff_output)
{
/* Print out the diff header. */
SVN_ERR (svn_stream_printf (os, subpool,
"Index: %s" APR_EOL_STR
"%s" APR_EOL_STR, path, equal_string));
/* Output the actual diff */
SVN_ERR (svn_diff_file_output_unified (os, diff,
tmpfile1, tmpfile2,
label1, label2,
subpool));
}
}
/* ### todo: someday we'll need to worry about whether we're going
to need to write a diff plug-in mechanism that makes use of the
two paths, instead of just blindly running SVN_CLIENT_DIFF. */
if (state)
*state = svn_wc_notify_state_unknown;
/* Destroy the subpool. */
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
/* The because the repos-diff editor passes at least one empty file to
each of these next two functions, they can be dumb wrappers around
the main workhorse routine. */
static svn_error_t *
diff_file_added (svn_wc_adm_access_t *adm_access,
svn_wc_notify_state_t *state,
const char *path,
const char *tmpfile1,
const char *tmpfile2,
svn_revnum_t rev1,
svn_revnum_t rev2,
const char *mimetype1,
const char *mimetype2,
void *diff_baton)
{
struct diff_cmd_baton *diff_cmd_baton = diff_baton;
/* We want diff_file_changed to unconditionally show diffs, even if
the diff is empty (as would be the case if an empty file were
added.) It's important, because 'patch' would still see an empty
diff and create an empty file. It's also important to let the
user see that *something* happened. */
diff_cmd_baton->force_diff_output = TRUE;
SVN_ERR (diff_file_changed (adm_access, state, path, tmpfile1, tmpfile2,
rev1, rev2,
mimetype1, mimetype2, diff_baton));
diff_cmd_baton->force_diff_output = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *
diff_file_deleted_with_diff (svn_wc_adm_access_t *adm_access,
svn_wc_notify_state_t *state,
const char *path,
const char *tmpfile1,
const char *tmpfile2,
const char *mimetype1,
const char *mimetype2,
void *diff_baton)
{
struct diff_cmd_baton *diff_cmd_baton = diff_baton;
return diff_file_changed (adm_access, state, path, tmpfile1, tmpfile2,
diff_cmd_baton->revnum1, diff_cmd_baton->revnum2,
mimetype1, mimetype2, diff_baton);
}
static svn_error_t *
diff_file_deleted_no_diff (svn_wc_adm_access_t *adm_access,
svn_wc_notify_state_t *state,
const char *path,
const char *tmpfile1,
const char *tmpfile2,
const char *mimetype1,
const char *mimetype2,
void *diff_baton)
{
struct diff_cmd_baton *diff_cmd_baton = diff_baton;
if (state)
*state = svn_wc_notify_state_unknown;
SVN_ERR (file_printf_from_utf8
(diff_cmd_baton->outfile,
"Index: %s (deleted)" APR_EOL_STR "%s" APR_EOL_STR,
path, equal_string));
return SVN_NO_ERROR;
}
/* For now, let's have 'svn diff' send feedback to the top-level
application, so that something reasonable about directories and
propsets gets printed to stdout. */
static svn_error_t *
diff_dir_added (svn_wc_adm_access_t *adm_access,
svn_wc_notify_state_t *state,
const char *path,
svn_revnum_t rev,
void *diff_baton)
{
if (state)
*state = svn_wc_notify_state_unknown;
/* ### todo: send feedback to app */
return SVN_NO_ERROR;
}
static svn_error_t *
diff_dir_deleted (svn_wc_adm_access_t *adm_access,
svn_wc_notify_state_t *state,
const char *path,
void *diff_baton)
{
if (state)
*state = svn_wc_notify_state_unknown;
return SVN_NO_ERROR;
}
static svn_error_t *
diff_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 *diff_baton)
{
struct diff_cmd_baton *diff_cmd_baton = diff_baton;
apr_array_header_t *props;
apr_pool_t *subpool = svn_pool_create (diff_cmd_baton->pool);
SVN_ERR (svn_categorize_props (propchanges, NULL, NULL, &props, subpool));
if (props->nelts > 0)
SVN_ERR (display_prop_diffs (props, original_props, path,
diff_cmd_baton->outfile, subpool));
if (state)
*state = svn_wc_notify_state_unknown;
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
/*-----------------------------------------------------------------*/
/*** Callbacks for 'svn merge', invoked by the repos-diff editor. ***/
struct merge_cmd_baton {
svn_boolean_t force;
svn_boolean_t dry_run;
const char *target; /* Working copy target of merge */
const char *url; /* The second URL in the merge */
const char *path; /* The wc path of the second target, this
can be NULL if we don't have one. */
const svn_opt_revision_t *revision; /* Revision of second URL in the merge */
svn_client_ctx_t *ctx;
/* The diff3_cmd in ctx->config, if any, else null. We could just
extract this as needed, but since more than one caller uses it,
we just set it up when this baton is created. */
const char *diff3_cmd;
apr_pool_t *pool;
};
static svn_error_t *
merge_file_changed (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 older_rev,
svn_revnum_t yours_rev,
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);
/* 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 (state)
*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_merge() 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 (state)
*state = svn_wc_notify_state_missing;
return SVN_NO_ERROR;
}
}
/* This callback is essentially no more than a wrapper around
svn_wc_merge(). Thank goodness that all the
diff-editor-mechanisms are doing the hard work of getting the
fulltexts! */
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. */
if ((! has_local_mods)
&& ((mimetype1 && svn_mime_type_is_binary (mimetype1))
|| (mimetype2 && svn_mime_type_is_binary (mimetype2))))
{
svn_boolean_t same_contents;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -