📄 diff.c
字号:
/* * diff.c: comparing and merging * * ==================================================================== * Copyright (c) 2000-2006 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_io.h"#include "svn_utf.h"#include "svn_pools.h"#include "svn_config.h"#include "svn_props.h"#include "svn_time.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 apr_file_printf(), which see. FORMAT is a utf8-encoded string after it is formatted, so this function can convert it to ENCODING before printing. */static svn_error_t *file_printf_from_utf8(apr_file_t *fptr, const char *encoding, const char *format, ...) __attribute__ ((format(printf, 3, 4)));static svn_error_t *file_printf_from_utf8(apr_file_t *fptr, const char *encoding, 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_utf_cstring_from_utf8_ex2(&buf_apr, buf, encoding, 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_diff3, 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, const char *encoding, apr_file_t *file, apr_pool_t *pool){ int i; SVN_ERR(file_printf_from_utf8(file, encoding, _("%sProperty changes on: %s%s"), APR_EOL_STR, svn_path_local_style(path, pool), APR_EOL_STR)); SVN_ERR(file_printf_from_utf8(file, encoding, "%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; /* If the property doesn't exist on either side, or if it exists with the same value, skip it. */ if ((! (original_value || propchange->value)) || (original_value && propchange->value && svn_string_compare(original_value, propchange->value))) continue; SVN_ERR(file_printf_from_utf8(file, encoding, _("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, encoding, " - %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, encoding, " + %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 URL's scheme does not match the scheme of the url for ADM_ACCESS's path; return SVN_ERR_BAD_URL if no scheme can be found for one or both urls; otherwise return SVN_NO_ERROR. Use ADM_ACCESS's pool for temporary allocation. */static svn_error_t *check_scheme_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 scheme ('%s' and '%s')"), url, ent->url); } else if (idx1 == NULL) { return svn_error_createf (SVN_ERR_BAD_URL, NULL, _("URL has no scheme: '%s'"), url); } else if (idx2 == NULL) { return svn_error_createf (SVN_ERR_BAD_URL, NULL, _("URL has no scheme: '%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 scheme 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; const char *header_encoding; /* 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_diff3, either may be SVN_INVALID_REVNUM. We need these because some of the svn_wc_diff_callbacks2_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 if you want diff output even for binary files. */ svn_boolean_t force_binary; /* Set this flag if you want diff_file_changed to output diffs unconditionally, even if the diffs are empty. */ svn_boolean_t force_empty;};/* 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. Assumes the paths are already in the desired style (local vs internal). Allocate the label in POOL. */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;}/* A svn_wc_diff_callbacks2_t function. Used for both file and directory property diffs. */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->header_encoding, diff_cmd_baton->outfile, subpool)); if (state) *state = svn_wc_notify_state_unknown; svn_pool_destroy(subpool); return SVN_NO_ERROR;}/* Show differences between TMPFILE1 and TMPFILE2. PATH, REV1, and REV2 are used in the headers to indicate the file and revisions. If either MIMETYPE1 or MIMETYPE2 indicate binary content, don't show a diff, but instread print a warning message. */static svn_error_t *diff_content_changed(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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -