📄 diff.c
字号:
/*
* diff.c: comparing and merging
*
* ====================================================================
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include <apr_strings.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include "svn_wc.h"
#include "svn_delta.h"
#include "svn_diff.h"
#include "svn_client.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_test.h"
#include "svn_io.h"
#include "svn_utf.h"
#include "svn_pools.h"
#include "svn_config.h"
#include "client.h"
#include <assert.h>
#include "svn_private_config.h"
/*
* Constant separator strings
*/
static const char equal_string[] =
"===================================================================";
static const char under_string[] =
"___________________________________________________________________";
/*-----------------------------------------------------------------*/
/* Utilities */
/* Wrapper for @c apr_file_printf(), which see. @a format is a utf8-encoded
string after it is formatted, so this function can convert it to
native encoding before printing. */
static svn_error_t *
file_printf_from_utf8 (apr_file_t *fptr, const char *format, ...)
{
va_list ap;
const char *buf, *buf_apr;
va_start (ap, format);
buf = apr_pvsprintf (apr_file_pool_get (fptr), format, ap);
va_end(ap);
SVN_ERR (svn_path_cstring_from_utf8 (&buf_apr, buf,
apr_file_pool_get (fptr)));
return svn_io_file_write_full (fptr, buf_apr, strlen (buf_apr),
NULL, apr_file_pool_get (fptr));
}
/* A helper func that writes out verbal descriptions of property diffs
to FILE. Of course, the apr_file_t will probably be the 'outfile'
passed to svn_client_diff, which is probably stdout. */
static svn_error_t *
display_prop_diffs (const apr_array_header_t *propchanges,
apr_hash_t *original_props,
const char *path,
apr_file_t *file,
apr_pool_t *pool)
{
int i;
SVN_ERR (file_printf_from_utf8 (file,
_("%sProperty changes on: %s%s"),
APR_EOL_STR, path, APR_EOL_STR));
/* ### todo [issue #1533]: Use file_printf_from_utf8() to convert this
line of dashes to native encoding, at least conditionally? Or is
it better to have under_string always output the same, so
programs can find it? Also, what about checking for error? */
apr_file_printf (file, "%s" APR_EOL_STR, under_string);
for (i = 0; i < propchanges->nelts; i++)
{
const svn_prop_t *propchange
= &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
const svn_string_t *original_value;
if (original_props)
original_value = apr_hash_get (original_props,
propchange->name, APR_HASH_KEY_STRING);
else
original_value = NULL;
SVN_ERR (file_printf_from_utf8 (file, _("Name: %s%s"),
propchange->name, APR_EOL_STR));
/* For now, we have a rather simple heuristic: if this is an
"svn:" property, then assume the value is UTF-8 and must
therefore be converted before printing. Otherwise, just
print whatever's there and hope for the best. */
{
svn_boolean_t val_is_utf8 = svn_prop_is_svn_prop (propchange->name);
if (original_value != NULL)
{
if (val_is_utf8)
{
SVN_ERR (file_printf_from_utf8
(file, " - %s" APR_EOL_STR, original_value->data));
}
else
{
/* ### todo: check for error? */
apr_file_printf
(file, " - %s" APR_EOL_STR, original_value->data);
}
}
if (propchange->value != NULL)
{
if (val_is_utf8)
{
SVN_ERR (file_printf_from_utf8
(file, " + %s" APR_EOL_STR,
propchange->value->data));
}
else
{
/* ### todo: check for error? */
apr_file_printf (file, " + %s" APR_EOL_STR,
propchange->value->data);
}
}
}
}
/* ### todo [issue #1533]: Use file_printf_from_utf8() to convert this
to native encoding, at least conditionally? Or is it better to
have under_string always output the same eol, so programs can
find it consistently? Also, what about checking for error? */
apr_file_printf (file, APR_EOL_STR);
return SVN_NO_ERROR;
}
/* Return SVN_ERR_UNSUPPORTED_FEATURE if @a url's schema does not
match the schema of the url for @a adm_access's path; return
SVN_ERR_BAD_URL if no schema can be found for one or both urls;
otherwise return SVN_NO_ERROR. Use @a adm_access's pool for
temporary allocation. */
static svn_error_t *
check_schema_match (svn_wc_adm_access_t *adm_access, const char *url)
{
const char *path = svn_wc_adm_access_path (adm_access);
apr_pool_t *pool = svn_wc_adm_access_pool (adm_access);
const svn_wc_entry_t *ent;
const char *idx1, *idx2;
SVN_ERR (svn_wc_entry (&ent, path, adm_access, TRUE, pool));
idx1 = strchr (url, ':');
idx2 = strchr (ent->url, ':');
if ((idx1 == NULL) && (idx2 == NULL))
{
return svn_error_createf
(SVN_ERR_BAD_URL, NULL,
_("URLs have no schema ('%s' and '%s')"), url, ent->url);
}
else if (idx1 == NULL)
{
return svn_error_createf
(SVN_ERR_BAD_URL, NULL,
_("URL has no schema: '%s'"), url);
}
else if (idx2 == NULL)
{
return svn_error_createf
(SVN_ERR_BAD_URL, NULL,
_("URL has no schema: '%s'"), ent->url);
}
else if (((idx1 - url) != (idx2 - ent->url))
|| (strncmp (url, ent->url, idx1 - url) != 0))
{
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Access schema mixtures not yet supported ('%s' and '%s')"),
url, ent->url);
}
/* else */
return SVN_NO_ERROR;
}
/*-----------------------------------------------------------------*/
/*** Callbacks for 'svn diff', invoked by the repos-diff editor. ***/
struct diff_cmd_baton {
const apr_array_header_t *options;
apr_pool_t *pool;
apr_file_t *outfile;
apr_file_t *errfile;
/* The original targets passed to the diff command. We may need
these to construct distinctive diff labels when comparing the
same relative path in the same revision, under different anchors
(for example, when comparing a trunk against a branch). */
const char *orig_path_1;
const char *orig_path_2;
/* These are the numeric representations of the revisions passed to
svn_client_diff, either may be SVN_INVALID_REVNUM. We need these
because some of the svn_wc_diff_callbacks_t don't get revision
arguments.
### Perhaps we should change the callback signatures and eliminate
### these?
*/
svn_revnum_t revnum1;
svn_revnum_t revnum2;
/* Client config hash (may be NULL). */
apr_hash_t *config;
/* Set this flag if you want diff_file_changed to output diffs
unconditionally, even if the diffs are empty. */
svn_boolean_t force_diff_output;
};
/* Generate a label for the diff output for file PATH at revision REVNUM.
If REVNUM is invalid then it is assumed to be the current working
copy. */
static const char *
diff_label (const char *path,
svn_revnum_t revnum,
apr_pool_t *pool)
{
const char *label;
if (revnum != SVN_INVALID_REVNUM)
label = apr_psprintf (pool, _("%s\t(revision %ld)"),
path, revnum);
else
label = apr_psprintf (pool, _("%s\t(working copy)"), path);
return label;
}
/* The main workhorse, which invokes an external 'diff' program on the
two temporary files. The path is the "true" label to use in the
diff output. */
static svn_error_t *
diff_file_changed (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;
const char *diff_cmd = NULL;
const char **args = NULL;
int nargs, exitcode;
apr_pool_t *subpool = svn_pool_create (diff_cmd_baton->pool);
svn_stream_t *os;
apr_file_t *errfile = diff_cmd_baton->errfile;
const char *label1, *label2;
svn_boolean_t mt1_binary = FALSE, mt2_binary = FALSE;
const char *path1, *path2;
int i;
/* Get a stream from our output file. */
os = svn_stream_from_aprfile(diff_cmd_baton->outfile, subpool);
/* Assemble any option args. */
nargs = diff_cmd_baton->options->nelts;
if (nargs)
{
args = apr_palloc (subpool, nargs * sizeof (char *));
for (i = 0; i < diff_cmd_baton->options->nelts; i++)
{
args[i] =
((const char **)(diff_cmd_baton->options->elts))[i];
}
assert (i == nargs);
}
/* Generate the diff headers. */
/* ### Holy cow. Due to anchor/target weirdness, we can't
simply join diff_cmd_baton->orig_path_1 with path, ditto for
orig_path_2. That will work when they're directory URLs, but
not for file URLs. Nor can we just use anchor1 and anchor2
from do_diff(), at least not without some more logic here.
What a nightmare.
For now, to distinguish the two paths, we'll just put the
unique portions of the original targets in parentheses before
the received path, with ellipses for handwaving. This makes
the labels a bit clumsy, but at least distinctive. Better
solutions are possible, they'll just take more thought. */
path1 = diff_cmd_baton->orig_path_1;
path2 = diff_cmd_baton->orig_path_2;
for (i = 0; path1[i] && path2[i] && (path1[i] == path2[i]); i++)
;
/* Make sure the prefix is made of whole components. (Issue #1771) */
if (path1[i] || path2[i])
{
for ( ; (i > 0) && (path1[i] != '/'); i--)
;
}
path1 = path1 + i;
path2 = path2 + i;
if (path1[0] == '\0')
path1 = apr_psprintf (subpool, "%s", path);
else if (path1[0] == '/')
path1 = apr_psprintf (subpool, "%s\t(...%s)", path, path1);
else
path1 = apr_psprintf (subpool, "%s\t(.../%s)", path, path1);
if (path2[0] == '\0')
path2 = apr_psprintf (subpool, "%s", path);
else if (path2[0] == '/')
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -