diff options
Diffstat (limited to 'subversion/libsvn_client/blame.c')
-rw-r--r-- | subversion/libsvn_client/blame.c | 174 |
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, |