diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_repos/rev_hunt.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_repos/rev_hunt.c')
-rw-r--r-- | subversion/libsvn_repos/rev_hunt.c | 289 |
1 files changed, 144 insertions, 145 deletions
diff --git a/subversion/libsvn_repos/rev_hunt.c b/subversion/libsvn_repos/rev_hunt.c index 77b1f2a..d6cc495 100644 --- a/subversion/libsvn_repos/rev_hunt.c +++ b/subversion/libsvn_repos/rev_hunt.c @@ -38,6 +38,8 @@ #include "svn_mergeinfo.h" #include "repos.h" #include "private/svn_fspath.h" +#include "private/svn_fs_private.h" +#include "private/svn_sorts_private.h" /* Note: this binary search assumes that the datestamp properties on @@ -171,12 +173,8 @@ svn_repos_get_committed_info(svn_revnum_t *committed_rev, SVN_ERR(svn_fs_revision_proplist(&revprops, fs, *committed_rev, pool)); /* Extract date and author from these revprops. */ - committed_date_s = apr_hash_get(revprops, - SVN_PROP_REVISION_DATE, - sizeof(SVN_PROP_REVISION_DATE)-1); - last_author_s = apr_hash_get(revprops, - SVN_PROP_REVISION_AUTHOR, - sizeof(SVN_PROP_REVISION_AUTHOR)-1); + committed_date_s = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE); + last_author_s = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR); *committed_date = committed_date_s ? committed_date_s->data : NULL; *last_author = last_author_s ? last_author_s->data : NULL; @@ -233,7 +231,7 @@ svn_repos_history2(svn_fs_t *fs, return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL); } - SVN_ERR(svn_fs_node_history(&history, root, path, oldpool)); + SVN_ERR(svn_fs_node_history2(&history, root, path, oldpool, oldpool)); /* Now, we loop over the history items, calling svn_fs_history_prev(). */ do @@ -244,7 +242,8 @@ svn_repos_history2(svn_fs_t *fs, apr_pool_t *tmppool; svn_error_t *err; - SVN_ERR(svn_fs_history_prev(&history, history, cross_copies, newpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, cross_copies, newpool, + oldpool)); /* Only continue if there is further history to deal with. */ if (! history) @@ -311,12 +310,11 @@ svn_repos_deleted_rev(svn_fs_t *fs, svn_revnum_t *deleted, apr_pool_t *pool) { - apr_pool_t *subpool; - svn_fs_root_t *root, *copy_root; - const char *copy_path; + apr_pool_t *iterpool; + svn_fs_root_t *start_root, *root; svn_revnum_t mid_rev; - const svn_fs_id_t *start_node_id, *curr_node_id; - svn_error_t *err; + svn_node_kind_t kind; + svn_fs_node_relation_t node_relation; /* Validate the revision range. */ if (! SVN_IS_VALID_REVNUM(start)) @@ -337,32 +335,19 @@ svn_repos_deleted_rev(svn_fs_t *fs, } /* Ensure path exists in fs at start revision. */ - SVN_ERR(svn_fs_revision_root(&root, fs, start, pool)); - err = svn_fs_node_id(&start_node_id, root, path, pool); - if (err) + SVN_ERR(svn_fs_revision_root(&start_root, fs, start, pool)); + SVN_ERR(svn_fs_check_path(&kind, start_root, path, pool)); + if (kind == svn_node_none) { - if (err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - /* Path must exist in fs at start rev. */ - *deleted = SVN_INVALID_REVNUM; - svn_error_clear(err); - return SVN_NO_ERROR; - } - return svn_error_trace(err); + /* Path must exist in fs at start rev. */ + *deleted = SVN_INVALID_REVNUM; + return SVN_NO_ERROR; } /* Ensure path was deleted at or before end revision. */ SVN_ERR(svn_fs_revision_root(&root, fs, end, pool)); - err = svn_fs_node_id(&curr_node_id, root, path, pool); - if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - svn_error_clear(err); - } - else if (err) - { - return svn_error_trace(err); - } - else + SVN_ERR(svn_fs_check_path(&kind, root, path, pool)); + if (kind != svn_node_none) { /* path exists in the end node and the end node is equivalent or otherwise equivalent to the start node. This can mean @@ -389,9 +374,12 @@ svn_repos_deleted_rev(svn_fs_t *fs, 5) The start node was deleted and replaced by a node which it does not share any history with. */ - SVN_ERR(svn_fs_node_id(&curr_node_id, root, path, pool)); - if (svn_fs_compare_ids(start_node_id, curr_node_id) != -1) + SVN_ERR(svn_fs_node_relation(&node_relation, start_root, path, + root, path, pool)); + if (node_relation != svn_fs_node_unrelated) { + svn_fs_root_t *copy_root; + const char *copy_path; SVN_ERR(svn_fs_closest_copy(©_root, ©_path, root, path, pool)); if (!copy_root || @@ -445,36 +433,33 @@ svn_repos_deleted_rev(svn_fs_t *fs, */ mid_rev = (start + end) / 2; - subpool = svn_pool_create(pool); + iterpool = svn_pool_create(pool); while (1) { - svn_pool_clear(subpool); + svn_pool_clear(iterpool); /* Get revision root and node id for mid_rev at that revision. */ - SVN_ERR(svn_fs_revision_root(&root, fs, mid_rev, subpool)); - err = svn_fs_node_id(&curr_node_id, root, path, subpool); - - if (err) + SVN_ERR(svn_fs_revision_root(&root, fs, mid_rev, iterpool)); + SVN_ERR(svn_fs_check_path(&kind, root, path, iterpool)); + if (kind == svn_node_none) { - if (err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - /* Case D: Look lower in the range. */ - svn_error_clear(err); - end = mid_rev; - mid_rev = (start + mid_rev) / 2; - } - else - return svn_error_trace(err); + /* Case D: Look lower in the range. */ + end = mid_rev; + mid_rev = (start + mid_rev) / 2; } else { + svn_fs_root_t *copy_root; + const char *copy_path; /* Determine the relationship between the start node and the current node. */ - int cmp = svn_fs_compare_ids(start_node_id, curr_node_id); - SVN_ERR(svn_fs_closest_copy(©_root, ©_path, root, - path, subpool)); - if (cmp == -1 || + SVN_ERR(svn_fs_node_relation(&node_relation, start_root, path, + root, path, iterpool)); + if (node_relation != svn_fs_node_unrelated) + SVN_ERR(svn_fs_closest_copy(©_root, ©_path, root, + path, iterpool)); + if (node_relation == svn_fs_node_unrelated || (copy_root && (svn_fs_revision_root_revision(copy_root) > start))) { @@ -497,7 +482,7 @@ svn_repos_deleted_rev(svn_fs_t *fs, } } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -542,7 +527,7 @@ check_ancestry_of_peg_path(svn_boolean_t *is_ancestor, SVN_ERR(svn_fs_revision_root(&root, fs, future_revision, pool)); - SVN_ERR(svn_fs_node_history(&history, root, fs_path, lastpool)); + SVN_ERR(svn_fs_node_history2(&history, root, fs_path, lastpool, lastpool)); /* Since paths that are different according to strcmp may still be equivalent (due to number of consecutive slashes and the fact that @@ -555,7 +540,8 @@ check_ancestry_of_peg_path(svn_boolean_t *is_ancestor, { apr_pool_t *tmppool; - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, currpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, currpool, + lastpool)); if (!history) break; @@ -657,14 +643,13 @@ svn_repos_trace_node_locations(svn_fs_t *fs, svn_revnum_t revision; svn_boolean_t is_ancestor; apr_pool_t *lastpool, *currpool; - const svn_fs_id_t *id; SVN_ERR_ASSERT(location_revisions_orig->elt_size == sizeof(svn_revnum_t)); /* Ensure that FS_PATH is absolute, because our path-math below will depend on that being the case. */ if (*fs_path != '/') - fs_path = apr_pstrcat(pool, "/", fs_path, (char *)NULL); + fs_path = apr_pstrcat(pool, "/", fs_path, SVN_VA_NULL); /* Another sanity check. */ if (authz_read_func) @@ -726,23 +711,6 @@ svn_repos_trace_node_locations(svn_fs_t *fs, if (! prev_path) break; - if (authz_read_func) - { - svn_boolean_t readable; - svn_fs_root_t *tmp_root; - - SVN_ERR(svn_fs_revision_root(&tmp_root, fs, revision, currpool)); - SVN_ERR(authz_read_func(&readable, tmp_root, path, - authz_read_baton, currpool)); - if (! readable) - { - svn_pool_destroy(lastpool); - svn_pool_destroy(currpool); - - return SVN_NO_ERROR; - } - } - /* Assign the current path to all younger revisions until we reach the copy target rev. */ while ((revision_ptr < revision_ptr_end) @@ -765,6 +733,20 @@ svn_repos_trace_node_locations(svn_fs_t *fs, path = prev_path; revision = prev_rev; + if (authz_read_func) + { + svn_boolean_t readable; + SVN_ERR(svn_fs_revision_root(&root, fs, revision, currpool)); + SVN_ERR(authz_read_func(&readable, root, path, + authz_read_baton, currpool)); + if (!readable) + { + svn_pool_destroy(lastpool); + svn_pool_destroy(currpool); + return SVN_NO_ERROR; + } + } + /* Clear last pool and switch. */ svn_pool_clear(lastpool); tmppool = lastpool; @@ -777,20 +759,22 @@ svn_repos_trace_node_locations(svn_fs_t *fs, the node existing at the same path. We will look up path@lrev for each remaining location-revision and make sure it is related to path@revision. */ - SVN_ERR(svn_fs_revision_root(&root, fs, revision, currpool)); - SVN_ERR(svn_fs_node_id(&id, root, path, pool)); + SVN_ERR(svn_fs_revision_root(&root, fs, revision, lastpool)); while (revision_ptr < revision_ptr_end) { svn_node_kind_t kind; - const svn_fs_id_t *lrev_id; + svn_fs_node_relation_t node_relation; + svn_fs_root_t *cur_rev_root; svn_pool_clear(currpool); - SVN_ERR(svn_fs_revision_root(&root, fs, *revision_ptr, currpool)); - SVN_ERR(svn_fs_check_path(&kind, root, path, currpool)); + SVN_ERR(svn_fs_revision_root(&cur_rev_root, fs, *revision_ptr, + currpool)); + SVN_ERR(svn_fs_check_path(&kind, cur_rev_root, path, currpool)); if (kind == svn_node_none) break; - SVN_ERR(svn_fs_node_id(&lrev_id, root, path, currpool)); - if (! svn_fs_check_related(id, lrev_id)) + SVN_ERR(svn_fs_node_relation(&node_relation, root, path, + cur_rev_root, path, currpool)); + if (node_relation == svn_fs_node_unrelated) break; /* The node exists at the same path; record that and advance. */ @@ -881,7 +865,7 @@ svn_repos_node_location_segments(svn_repos_t *repos, /* Ensure that PATH is absolute, because our path-math will depend on that being the case. */ if (*path != '/') - path = apr_pstrcat(pool, "/", path, (char *)NULL); + path = apr_pstrcat(pool, "/", path, SVN_VA_NULL); /* Auth check. */ if (authz_read_func) @@ -945,7 +929,7 @@ svn_repos_node_location_segments(svn_repos_t *repos, /* authz_read_func requires path to have a leading slash. */ const char *abs_path = apr_pstrcat(subpool, "/", segment->path, - (char *)NULL); + SVN_VA_NULL); SVN_ERR(svn_fs_revision_root(&cur_rev_root, fs, segment->range_end, subpool)); @@ -982,37 +966,6 @@ svn_repos_node_location_segments(svn_repos_t *repos, return SVN_NO_ERROR; } -/* Get the mergeinfo for PATH in REPOS at REVNUM and store it in MERGEINFO. */ -static svn_error_t * -get_path_mergeinfo(apr_hash_t **mergeinfo, - svn_fs_t *fs, - const char *path, - svn_revnum_t revnum, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_mergeinfo_catalog_t tmp_catalog; - svn_fs_root_t *root; - apr_array_header_t *paths = apr_array_make(scratch_pool, 1, - sizeof(const char *)); - - APR_ARRAY_PUSH(paths, const char *) = path; - - SVN_ERR(svn_fs_revision_root(&root, fs, revnum, scratch_pool)); - /* We do not need to call svn_repos_fs_get_mergeinfo() (which performs authz) - because we will filter out unreadable revisions in - find_interesting_revision(), above */ - SVN_ERR(svn_fs_get_mergeinfo2(&tmp_catalog, root, paths, - svn_mergeinfo_inherited, FALSE, TRUE, - result_pool, scratch_pool)); - - *mergeinfo = svn_hash_gets(tmp_catalog, path); - if (!*mergeinfo) - *mergeinfo = apr_hash_make(result_pool); - - return SVN_NO_ERROR; -} - static APR_INLINE svn_boolean_t is_path_in_hash(apr_hash_t *duplicate_path_revs, const char *path, @@ -1031,7 +984,8 @@ struct path_revision svn_revnum_t revnum; const char *path; - /* Does this path_rev have merges to also be included? */ + /* Does this path_rev have merges to also be included? If so, this is + the union of both additions and (negated) deletions of mergeinfo. */ apr_hash_t *merged_mergeinfo; /* Is this a merged revision? */ @@ -1040,6 +994,7 @@ struct path_revision /* Check for merges in OLD_PATH_REV->PATH at OLD_PATH_REV->REVNUM. Store the mergeinfo difference in *MERGED_MERGEINFO, allocated in POOL. The + difference is the union of both additions and (negated) deletions. The returned *MERGED_MERGEINFO will be NULL if there are no changes. */ static svn_error_t * get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, @@ -1050,7 +1005,7 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, { apr_hash_t *curr_mergeinfo, *prev_mergeinfo, *deleted, *changed; svn_error_t *err; - svn_fs_root_t *root; + svn_fs_root_t *root, *prev_root; apr_hash_t *changed_paths; const char *path = old_path_rev->path; @@ -1062,7 +1017,8 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, while (1) { svn_fs_path_change2_t *changed_path = svn_hash_gets(changed_paths, path); - if (changed_path && changed_path->prop_mod) + if (changed_path && changed_path->prop_mod + && changed_path->mergeinfo_mod != svn_tristate_false) break; if (svn_fspath__is_root(path, strlen(path))) { @@ -1074,9 +1030,13 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, /* First, find the mergeinfo difference for old_path_rev->revnum, and old_path_rev->revnum - 1. */ - err = get_path_mergeinfo(&curr_mergeinfo, repos->fs, old_path_rev->path, - old_path_rev->revnum, scratch_pool, - scratch_pool); + /* We do not need to call svn_repos_fs_get_mergeinfo() (which performs authz) + because we will filter out unreadable revisions in + find_interesting_revision() */ + err = svn_fs__get_mergeinfo_for_path(&curr_mergeinfo, + root, old_path_rev->path, + svn_mergeinfo_inherited, TRUE, + scratch_pool, scratch_pool); if (err) { if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) @@ -1094,9 +1054,12 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, } } - err = get_path_mergeinfo(&prev_mergeinfo, repos->fs, old_path_rev->path, - old_path_rev->revnum - 1, scratch_pool, - scratch_pool); + SVN_ERR(svn_fs_revision_root(&prev_root, repos->fs, old_path_rev->revnum - 1, + scratch_pool)); + err = svn_fs__get_mergeinfo_for_path(&prev_mergeinfo, + prev_root, old_path_rev->path, + svn_mergeinfo_inherited, TRUE, + scratch_pool, scratch_pool); if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND || err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)) { @@ -1109,7 +1072,8 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, else SVN_ERR(err); - /* Then calculate and merge the differences. */ + /* Then calculate and merge the differences, combining additions and + (negated) deletions as all positive changes in CHANGES. */ SVN_ERR(svn_mergeinfo_diff2(&deleted, &changed, prev_mergeinfo, curr_mergeinfo, FALSE, result_pool, scratch_pool)); @@ -1157,7 +1121,8 @@ find_interesting_revisions(apr_array_header_t *path_revisions, path, end); /* Open a history object. */ - SVN_ERR(svn_fs_node_history(&history, root, path, scratch_pool)); + SVN_ERR(svn_fs_node_history2(&history, root, path, scratch_pool, + scratch_pool)); while (1) { struct path_revision *path_rev; @@ -1168,7 +1133,8 @@ find_interesting_revisions(apr_array_header_t *path_revisions, svn_pool_clear(iterpool); /* Fetch the history object to walk through. */ - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iterpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool, + iterpool)); if (!history) break; SVN_ERR(svn_fs_history_location(&tmp_path, &tmp_revnum, @@ -1290,16 +1256,14 @@ find_merged_revisions(apr_array_header_t **merged_path_revisions_out, for (hi = apr_hash_first(iterpool, old_pr->merged_mergeinfo); hi; hi = apr_hash_next(hi)) { + const char *path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); apr_pool_t *iterpool3; - svn_rangelist_t *rangelist; - const char *path; int j; svn_pool_clear(iterpool2); iterpool3 = svn_pool_create(iterpool2); - apr_hash_this(hi, (void *) &path, NULL, (void *) &rangelist); - for (j = 0; j < rangelist->nelts; j++) { svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, j, @@ -1346,8 +1310,7 @@ find_merged_revisions(apr_array_header_t **merged_path_revisions_out, while (new_merged_path_revs->nelts > 0); /* Sort MERGED_PATH_REVISIONS in increasing order by REVNUM. */ - qsort(merged_path_revisions->elts, merged_path_revisions->nelts, - sizeof(struct path_revision *), compare_path_revisions); + svn_sort__array(merged_path_revisions, compare_path_revisions); /* Copy to the output array. */ *merged_path_revisions_out = apr_array_copy(result_pool, @@ -1366,6 +1329,7 @@ struct send_baton apr_hash_t *last_props; const char *last_path; svn_fs_root_t *last_root; + svn_boolean_t include_merged_revisions; }; /* Send PATH_REV to HANDLER and HANDLER_BATON, using information provided by @@ -1403,14 +1367,32 @@ send_path_revision(struct path_revision *path_rev, SVN_ERR(svn_prop_diffs(&prop_diffs, props, sb->last_props, sb->iterpool)); - /* Check if the contents changed. */ - /* Special case: In the first revision, we always provide a delta. */ - if (sb->last_root) - SVN_ERR(svn_fs_contents_changed(&contents_changed, sb->last_root, - sb->last_path, root, path_rev->path, - sb->iterpool)); + /* Check if the contents *may* have changed. */ + if (! sb->last_root) + { + /* Special case: In the first revision, we always provide a delta. */ + contents_changed = TRUE; + } + else if (sb->include_merged_revisions + && strcmp(sb->last_path, path_rev->path)) + { + /* ### This is a HACK!!! + * Blame -g, in older clients anyways, relies on getting a notification + * whenever the path changes - even if there was no content change. + * + * TODO: A future release should take an extra parameter and depending + * on that either always send a text delta or only send it if there + * is a difference. */ + contents_changed = TRUE; + } else - contents_changed = TRUE; + { + /* Did the file contents actually change? + * It could e.g. be a property-only change. */ + SVN_ERR(svn_fs_contents_different(&contents_changed, sb->last_root, + sb->last_path, root, path_rev->path, + sb->iterpool)); + } /* We have all we need, give to the handler. */ SVN_ERR(handler(handler_baton, path_rev->path, path_rev->revnum, @@ -1422,7 +1404,7 @@ send_path_revision(struct path_revision *path_rev, /* Compute and send delta if client asked for it. Note that this was initialized to NULL, so if !contents_changed, no deltas will be computed. */ - if (delta_handler) + if (delta_handler && delta_handler != svn_delta_noop_window_handler) { /* Get the content delta. */ SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream, @@ -1479,6 +1461,7 @@ get_file_revs_backwards(svn_repos_t *repos, last_pool = svn_pool_create(scratch_pool); sb.iterpool = svn_pool_create(scratch_pool); sb.last_pool = svn_pool_create(scratch_pool); + sb.include_merged_revisions = FALSE; /* We want the first txdelta to be against the empty file. */ sb.last_root = NULL; @@ -1496,7 +1479,7 @@ get_file_revs_backwards(svn_repos_t *repos, path, end); /* Open a history object. */ - SVN_ERR(svn_fs_node_history(&history, root, path, scratch_pool)); + SVN_ERR(svn_fs_node_history2(&history, root, path, scratch_pool, iterpool)); while (1) { struct path_revision *path_rev; @@ -1506,7 +1489,8 @@ get_file_revs_backwards(svn_repos_t *repos, svn_pool_clear(iterpool); /* Fetch the history object to walk through. */ - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iterpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool, + iterpool)); if (!history) break; SVN_ERR(svn_fs_history_location(&tmp_path, &tmp_revnum, @@ -1594,6 +1578,18 @@ svn_repos_get_file_revs2(svn_repos_t *repos, struct send_baton sb; int mainline_pos, merged_pos; + if (!SVN_IS_VALID_REVNUM(start) + || !SVN_IS_VALID_REVNUM(end)) + { + svn_revnum_t youngest_rev; + SVN_ERR(svn_fs_youngest_rev(&youngest_rev, repos->fs, scratch_pool)); + + if (!SVN_IS_VALID_REVNUM(start)) + start = youngest_rev; + if (!SVN_IS_VALID_REVNUM(end)) + end = youngest_rev; + } + if (end < start) { if (include_merged_revisions) @@ -1621,6 +1617,9 @@ svn_repos_get_file_revs2(svn_repos_t *repos, /* Create an empty hash table for the first property diff. */ sb.last_props = apr_hash_make(sb.last_pool); + /* Inform send_path_revision() whether workarounds / special behavior + * may be needed. */ + sb.include_merged_revisions = include_merged_revisions; /* Get the revisions we are interested in. */ duplicate_path_revs = apr_hash_make(scratch_pool); |