summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/blame.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/blame.c')
-rw-r--r--subversion/libsvn_client/blame.c174
1 files changed, 100 insertions, 74 deletions
diff --git a/subversion/libsvn_client/blame.c b/subversion/libsvn_client/blame.c
index b5c56ae..188fdd2 100644
--- a/subversion/libsvn_client/blame.c
+++ b/subversion/libsvn_client/blame.c
@@ -54,9 +54,9 @@ struct rev
/* One chunk of blame */
struct blame
{
- struct rev *rev; /* the responsible revision */
- apr_off_t start; /* the starting diff-token (line) */
- struct blame *next; /* the next chunk */
+ const struct rev *rev; /* the responsible revision */
+ apr_off_t start; /* the starting diff-token (line) */
+ struct blame *next; /* the next chunk */
};
/* A chain of blame chunks */
@@ -70,7 +70,7 @@ struct blame_chain
/* The baton use for the diff output routine. */
struct diff_baton {
struct blame_chain *chain;
- struct rev *rev;
+ const struct rev *rev;
};
/* The baton used for a file revision. */
@@ -79,13 +79,12 @@ struct file_rev_baton {
const char *target;
svn_client_ctx_t *ctx;
const svn_diff_file_options_t *diff_options;
- svn_boolean_t ignore_mime_type;
/* name of file containing the previous revision of the file */
const char *last_filename;
struct rev *rev; /* the rev for which blame is being assigned
during a diff */
struct blame_chain *chain; /* the original blame chain. */
- const char *tmp_path; /* temp file name to feed svn_io_open_unique_file */
+ const char *repos_root_url; /* To construct a url */
apr_pool_t *mainpool; /* lives during the whole sequence of calls */
apr_pool_t *lastpool; /* pool used during previous call */
apr_pool_t *currpool; /* pool used during this call */
@@ -107,7 +106,6 @@ struct delta_baton {
svn_txdelta_window_handler_t wrapped_handler;
void *wrapped_baton;
struct file_rev_baton *file_rev_baton;
- svn_stream_t *source_stream; /* the delta source */
const char *filename;
};
@@ -118,7 +116,7 @@ struct delta_baton {
at token START, and allocated in CHAIN->mainpool. */
static struct blame *
blame_create(struct blame_chain *chain,
- struct rev *rev,
+ const struct rev *rev,
apr_off_t start)
{
struct blame *blame;
@@ -217,7 +215,7 @@ blame_delete_range(struct blame_chain *chain,
at token START and continuing for LENGTH tokens */
static svn_error_t *
blame_insert_range(struct blame_chain *chain,
- struct rev *rev,
+ const struct rev *rev,
apr_off_t start,
apr_off_t length)
{
@@ -273,8 +271,8 @@ static const svn_diff_output_fns_t output_fns = {
output_diff_modified
};
-/* Add the blame for the diffs between LAST_FILE and CUR_FILE with the rev
- specified in FRB. LAST_FILE may be NULL in which
+/* Add the blame for the diffs between LAST_FILE and CUR_FILE to CHAIN,
+ for revision REV. LAST_FILE may be NULL in which
case blame is added for every line of CUR_FILE. */
static svn_error_t *
add_file_blame(const char *last_file,
@@ -306,6 +304,13 @@ add_file_blame(const char *last_file,
return SVN_NO_ERROR;
}
+/* The delta window handler for the text delta between the previously seen
+ * revision and the revision currently being handled.
+ *
+ * Record the blame information for this revision in BATON->file_rev_baton.
+ *
+ * Implements svn_txdelta_window_handler_t.
+ */
static svn_error_t *
window_handler(svn_txdelta_window_t *window, void *baton)
{
@@ -320,13 +325,6 @@ window_handler(svn_txdelta_window_t *window, void *baton)
if (window)
return SVN_NO_ERROR;
- /* Close the source file used for the delta.
- It is important to do this early, since otherwise, they will be deleted
- before all handles are closed, which leads to failures on some platforms
- when new tempfiles are to be created. */
- if (dbaton->source_stream)
- SVN_ERR(svn_stream_close(dbaton->source_stream));
-
/* If we are including merged revisions, we need to add each rev to the
merged chain. */
if (frb->include_merged_revisions)
@@ -376,29 +374,16 @@ window_handler(svn_txdelta_window_t *window, void *baton)
return SVN_NO_ERROR;
}
-/* Throw an SVN_ERR_CLIENT_IS_BINARY_FILE error if PROP_DIFFS indicates a
- binary MIME type. Else, return SVN_NO_ERROR. */
-static svn_error_t *
-check_mimetype(const apr_array_header_t *prop_diffs, const char *target,
- apr_pool_t *pool)
-{
- int i;
-
- for (i = 0; i < prop_diffs->nelts; ++i)
- {
- const svn_prop_t *prop = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t);
- if (strcmp(prop->name, SVN_PROP_MIME_TYPE) == 0
- && prop->value
- && svn_mime_type_is_binary(prop->value->data))
- return svn_error_createf
- (SVN_ERR_CLIENT_IS_BINARY_FILE, 0,
- _("Cannot calculate blame information for binary file '%s'"),
- svn_dirent_local_style(target, pool));
- }
- return SVN_NO_ERROR;
-}
-
+/* Calculate and record blame information for one revision of the file,
+ * by comparing the file content against the previously seen revision.
+ *
+ * This handler is called once for each interesting revision of the file.
+ *
+ * Record the blame information for this revision in (file_rev_baton) BATON.
+ *
+ * Implements svn_file_rev_handler_t.
+ */
static svn_error_t *
file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
apr_hash_t *rev_props,
@@ -417,14 +402,14 @@ file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
/* Clear the current pool. */
svn_pool_clear(frb->currpool);
- /* If this file has a non-textual mime-type, bail out. */
- if (! frb->ignore_mime_type)
- SVN_ERR(check_mimetype(prop_diffs, frb->target, frb->currpool));
-
if (frb->ctx->notify_func2)
{
svn_wc_notify_t *notify
- = svn_wc_create_notify(path, svn_wc_notify_blame_revision, pool);
+ = svn_wc_create_notify_url(
+ svn_path_url_add_component2(frb->repos_root_url,
+ path+1, pool),
+ svn_wc_notify_blame_revision, pool);
+ notify->path = path;
notify->kind = svn_node_none;
notify->content_state = notify->prop_state
= svn_wc_notify_state_inapplicable;
@@ -453,12 +438,10 @@ file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
/* Prepare the text delta window handler. */
if (frb->last_filename)
- SVN_ERR(svn_stream_open_readonly(&delta_baton->source_stream, frb->last_filename,
+ SVN_ERR(svn_stream_open_readonly(&last_stream, frb->last_filename,
frb->currpool, pool));
else
- /* Means empty stream below. */
- delta_baton->source_stream = NULL;
- last_stream = svn_stream_disown(delta_baton->source_stream, pool);
+ last_stream = svn_stream_empty(frb->currpool);
if (frb->include_merged_revisions && !frb->merged_revision)
filepool = frb->filepool;
@@ -467,7 +450,7 @@ file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
SVN_ERR(svn_stream_open_unique(&cur_stream, &delta_baton->filename, NULL,
svn_io_file_del_on_pool_cleanup,
- filepool, filepool));
+ filepool, pool));
/* Get window handler for applying delta. */
svn_txdelta_apply(last_stream, cur_stream, NULL, NULL,
@@ -588,8 +571,8 @@ svn_client_blame5(const char *target,
{
struct file_rev_baton frb;
svn_ra_session_t *ra_session;
- const char *url;
svn_revnum_t start_revnum, end_revnum;
+ svn_client__pathrev_t *end_loc;
struct blame *walk, *walk_merged = NULL;
apr_pool_t *iterpool;
svn_stream_t *last_stream;
@@ -607,10 +590,10 @@ svn_client_blame5(const char *target,
SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool));
/* Get an RA plugin for this filesystem object. */
- SVN_ERR(svn_client__ra_session_from_path(&ra_session, &end_revnum,
- &url, target, NULL,
- peg_revision, end,
- ctx, pool));
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &end_loc,
+ target, NULL, peg_revision, end,
+ ctx, pool));
+ end_revnum = end_loc->rev;
SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx,
target_abspath_or_url, ra_session,
@@ -621,12 +604,43 @@ svn_client_blame5(const char *target,
(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Start revision must precede end revision"));
+ /* We check the mime-type of the yougest revision before getting all
+ the older revisions. */
+ if (!ignore_mime_type)
+ {
+ apr_hash_t *props;
+ apr_hash_index_t *hi;
+
+ SVN_ERR(svn_client_propget5(&props, NULL, SVN_PROP_MIME_TYPE,
+ target_abspath_or_url, peg_revision,
+ end, NULL, svn_depth_empty, NULL, ctx,
+ pool, pool));
+
+ /* props could be keyed on URLs or paths depending on the
+ peg_revision and end values so avoid using the key. */
+ hi = apr_hash_first(pool, props);
+ if (hi)
+ {
+ svn_string_t *value;
+
+ /* Should only be one value */
+ SVN_ERR_ASSERT(apr_hash_count(props) == 1);
+
+ value = svn__apr_hash_index_val(hi);
+ if (value && svn_mime_type_is_binary(value->data))
+ return svn_error_createf
+ (SVN_ERR_CLIENT_IS_BINARY_FILE, 0,
+ _("Cannot calculate blame information for binary file '%s'"),
+ (svn_path_is_url(target)
+ ? target : svn_dirent_local_style(target, pool)));
+ }
+ }
+
frb.start_rev = start_revnum;
frb.end_rev = end_revnum;
frb.target = target;
frb.ctx = ctx;
frb.diff_options = diff_options;
- frb.ignore_mime_type = ignore_mime_type;
frb.include_merged_revisions = include_merged_revisions;
frb.last_filename = NULL;
frb.last_original_filename = NULL;
@@ -642,8 +656,7 @@ svn_client_blame5(const char *target,
frb.merged_chain->pool = pool;
}
- SVN_ERR(svn_io_temp_dir(&frb.tmp_path, pool));
- frb.tmp_path = svn_dirent_join(frb.tmp_path, "tmp", pool),
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &frb.repos_root_url, pool));
frb.mainpool = pool;
/* The callback will flip the following two pools, because it needs
@@ -675,28 +688,41 @@ svn_client_blame5(const char *target,
SVN_ERR(svn_wc_status3(&status, ctx->wc_ctx, target_abspath_or_url, pool,
pool));
- if (status->text_status != svn_wc_status_normal)
+ if (status->text_status != svn_wc_status_normal
+ || (status->prop_status != svn_wc_status_normal
+ && status->prop_status != svn_wc_status_none))
{
- apr_hash_t *props;
svn_stream_t *wcfile;
- svn_string_t *keywords;
svn_stream_t *tempfile;
+ svn_opt_revision_t rev;
+ svn_boolean_t normalize_eols = FALSE;
const char *temppath;
- apr_hash_t *kw = NULL;
-
- SVN_ERR(svn_wc_prop_list2(&props, ctx->wc_ctx, target_abspath_or_url,
- pool, pool));
- SVN_ERR(svn_stream_open_readonly(&wcfile, target, pool, pool));
- keywords = apr_hash_get(props, SVN_PROP_KEYWORDS,
- APR_HASH_KEY_STRING);
-
- if (keywords)
- SVN_ERR(svn_subst_build_keywords2(&kw, keywords->data, NULL, NULL,
- 0, NULL, pool));
+ if (status->prop_status != svn_wc_status_none)
+ {
+ const svn_string_t *eol_style;
+ SVN_ERR(svn_wc_prop_get2(&eol_style, ctx->wc_ctx,
+ target_abspath_or_url,
+ SVN_PROP_EOL_STYLE,
+ pool, pool));
+
+ if (eol_style)
+ {
+ svn_subst_eol_style_t style;
+ const char *eol;
+ svn_subst_eol_style_from_value(&style, &eol, eol_style->data);
+
+ normalize_eols = (style == svn_subst_eol_style_native);
+ }
+ }
- wcfile = svn_subst_stream_translated(wcfile, "\n", TRUE, kw, FALSE,
- pool);
+ rev.kind = svn_opt_revision_working;
+ SVN_ERR(svn_client__get_normalized_stream(&wcfile, ctx->wc_ctx,
+ target_abspath_or_url, &rev,
+ FALSE, normalize_eols,
+ ctx->cancel_func,
+ ctx->cancel_baton,
+ pool, pool));
SVN_ERR(svn_stream_open_unique(&tempfile, &temppath, NULL,
svn_io_file_del_on_pool_cleanup,