Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 157841 Details for
Bug 228653
dev-util/subversion-1.5.0 released
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
subversion-1.5.0-merge-improvements.patch
subversion-1.5.0-merge-improvements.patch (text/plain), 52.03 KB, created by
Arfrever Frehtes Taifersar Arahesis (RETIRED)
on 2008-06-21 02:27:47 UTC
(
hide
)
Description:
subversion-1.5.0-merge-improvements.patch
Filename:
MIME Type:
Creator:
Arfrever Frehtes Taifersar Arahesis (RETIRED)
Created:
2008-06-21 02:27:47 UTC
Size:
52.03 KB
patch
obsolete
>Merge improvements from upstream > >https://svn.collab.net/viewvc/svn?view=rev&revision=30748 >https://svn.collab.net/viewvc/svn?view=rev&revision=31075 >https://svn.collab.net/viewvc/svn?view=rev&revision=31151 >https://svn.collab.net/viewvc/svn?view=rev&revision=31301 >https://svn.collab.net/viewvc/svn?view=rev&revision=31312 >https://svn.collab.net/viewvc/svn?view=rev&revision=31482 >https://svn.collab.net/viewvc/svn?view=rev&revision=31504 >https://svn.collab.net/viewvc/svn?view=rev&revision=31795 >https://svn.collab.net/viewvc/svn?view=rev&revision=31798 >https://svn.collab.net/viewvc/svn?view=rev&revision=31801 >https://svn.collab.net/viewvc/svn?view=rev&revision=31817 > >--- subversion/libsvn_client/merge.c >+++ subversion/libsvn_client/merge.c >@@ -1311,12 +1311,17 @@ > } notification_receiver_baton_t; > > >-/* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for PATH. >+/* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for PATH. If >+ PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO >+ where child->path == PATH is considered PATH's ancestor. If FALSE, >+ then child->path must be a proper ancestor of PATH. >+ > CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first > order of path. Nearest ancestor's index from > CHILDREN_WITH_MERGEINFO is returned. */ > static int > find_nearest_ancestor(apr_array_header_t *children_with_mergeinfo, >+ svn_boolean_t path_is_own_ancestor, > const char *path) > { > int i; >@@ -1332,8 +1337,10 @@ > { > svn_client__merge_path_t *child = > APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); >- if (svn_path_is_ancestor(child->path, path)) >- ancestor_index = i; >+ if (svn_path_is_ancestor(child->path, path) >+ && (svn_path_compare_paths(child->path, path) != 0 >+ || path_is_own_ancestor)) >+ ancestor_index = i; > } > return ancestor_index; > } >@@ -1371,9 +1378,27 @@ > /* See if this is an operative directory merge. */ > if (!(notify_b->is_single_file_merge) && is_operative_notification) > { >+ /* Find NOTIFY->PATH's nearest ancestor in >+ NOTIFY->CHILDREN_WITH_MERGEINFO. Normally we consider a child in >+ NOTIFY->CHILDREN_WITH_MERGEINFO representing PATH to be an >+ ancestor of PATH, but if this is a deletion of PATH then the >+ notification must be for a proper ancestor of PATH. This ensures >+ we don't get notifications like: >+ >+ --- Merging rX into 'PARENT/CHILD' >+ D PARENT/CHILD >+ >+ But rather: >+ >+ --- Merging rX into 'PARENT' >+ D PARENT/CHILD >+ */ > int new_nearest_ancestor_index = >- find_nearest_ancestor(notify_b->children_with_mergeinfo, >- notify->path); >+ find_nearest_ancestor( >+ notify_b->children_with_mergeinfo, >+ notify->action == svn_wc_notify_update_delete ? FALSE : TRUE, >+ notify->path); >+ > if (new_nearest_ancestor_index != notify_b->cur_ancestor_index) > { > svn_client__merge_path_t *child = >@@ -1853,7 +1878,6 @@ > const char *child_repos_path; > const svn_wc_entry_t *child_entry; > const char *child_url1, *child_url2; >- svn_mergeinfo_t implicit_mergeinfo; > svn_client__merge_path_t *child = > APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); > >@@ -1875,7 +1899,7 @@ > FALSE, iterpool)); > > SVN_ERR(get_full_mergeinfo(&(child->pre_merge_mergeinfo), >- &implicit_mergeinfo, child_entry, >+ &(child->implicit_mergeinfo), child_entry, > &(child->indirect_mergeinfo), > svn_mergeinfo_inherited, NULL, child->path, > MAX(revision1, revision2), >@@ -1888,7 +1912,7 @@ > child_url2, revision2, > inheritable, > child->pre_merge_mergeinfo, >- implicit_mergeinfo, >+ child->implicit_mergeinfo, > ra_session, child_entry, merge_b->ctx, > pool)); > } >@@ -1925,8 +1949,8 @@ > /*** Other Helper Functions ***/ > > >-/* Create mergeinfo describing the merge of RANGE into our target, accounting >- for paths unaffected by the merge due to skips or conflicts from >+/* Create mergeinfo describing the merge of RANGELIST into TARGET_WCPATH, >+ accounting for paths unaffected by the merge due to skips or conflicts from > NOTIFY_B. For 'immediates' merge it sets an inheritable mergeinfo > corresponding to current merge on merge target. For 'files' merge it sets > an inheritable mergeinfo corrsponding to current merge on merged files. >@@ -1934,19 +1958,17 @@ > TARGET_MISSING_CHILD should be true, otherwise it is false.*/ > static svn_error_t * > determine_merges_performed(apr_hash_t **merges, const char *target_wcpath, >- svn_merge_range_t *range, >+ apr_array_header_t *rangelist, > svn_depth_t depth, > svn_wc_adm_access_t *adm_access, > notification_receiver_baton_t *notify_b, > merge_cmd_baton_t *merge_b, > apr_pool_t *pool) > { >- apr_array_header_t *rangelist = apr_array_make(pool, 1, sizeof(range)); > apr_size_t nbr_skips = (notify_b->skipped_paths != NULL ? > apr_hash_count(notify_b->skipped_paths) : 0); > > *merges = apr_hash_make(pool); >- APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range; > apr_hash_set(*merges, target_wcpath, APR_HASH_KEY_STRING, rangelist); > if (nbr_skips > 0) > { >@@ -1978,7 +2000,7 @@ > ### see issue #2915. */ > apr_hash_set(*merges, (const char *) skipped_path, > APR_HASH_KEY_STRING, >- apr_array_make(pool, 0, sizeof(range))); >+ apr_array_make(pool, 0, sizeof(svn_merge_range_t))); > > if (nbr_skips < notify_b->nbr_notifications) > /* ### Use RANGELIST as the mergeinfo for all children of >@@ -1996,10 +2018,8 @@ > hi = apr_hash_next(hi)) > { > const svn_wc_entry_t *child_entry; >- svn_merge_range_t *child_merge_range; > apr_array_header_t *rangelist_of_child = NULL; > apr_hash_this(hi, &merged_path, NULL, NULL); >- child_merge_range = svn_merge_range_dup(range, pool); > SVN_ERR(svn_wc__entry_versioned(&child_entry, > merged_path, > adm_access, FALSE, >@@ -2014,14 +2034,17 @@ > 1. Merge target directory if depth is immediates. > 2. If merge is on a file and requested depth is 'files'. > */ >- child_merge_range->inheritable = TRUE; >- rangelist_of_child = apr_array_make(pool, 1, sizeof(range)); >+ int i; >+ rangelist_of_child = svn_rangelist_dup(rangelist, pool); >+ for (i = 0; i < rangelist_of_child->nelts; i++) >+ { >+ svn_merge_range_t *rng = >+ APR_ARRAY_IDX(rangelist_of_child, i, svn_merge_range_t *); >+ rng->inheritable = TRUE; >+ } > } > if (rangelist_of_child) > { >- APR_ARRAY_PUSH(rangelist_of_child, svn_merge_range_t *) = >- child_merge_range; >- > apr_hash_set(*merges, (const char *)merged_path, > APR_HASH_KEY_STRING, rangelist_of_child); > } >@@ -2510,16 +2533,24 @@ > } > } > >-/* For each child of CHILDREN_WITH_MERGEINFO create a new remaining_ranges >- by removing the first item from the original range list and overwrite the >- original remaining_ranges with this new list. >- All the allocations are persistent from a POOL. >- TODO, we should have remaining_ranges in reverse order to avoid recreating >- the remaining_ranges every time instead of one 'pop' operation. */ >+/* Helper for do_directory_merge(). >+ >+ Remove the first remaining revision range for each child in >+ CHILDREN_WITH_MERGEINFO *iff* that child was already merged. END_REV is the >+ ending revision of the most recently merged range, i.e. the same end_rev >+ passed to drive_merge_report_editor() by do_directory_merge(). If a >+ range is removed from a child's remaining_ranges array, allocate the new >+ remaining_ranges array in POOL. >+ >+ ### TODO: We should have remaining_ranges in reverse order to avoid >+ ### recreating and reallocationg the remaining_ranges every time we want >+ ### to remove the first range. If the ranges were reversed we could simply >+ ### pop the last element in the array. */ > static void >-remove_first_range_from_remaining_ranges( >- apr_array_header_t *children_with_mergeinfo, >- apr_pool_t *pool) >+remove_first_range_from_remaining_ranges(svn_revnum_t end_rev, >+ apr_array_header_t >+ *children_with_mergeinfo, >+ apr_pool_t *pool) > { > int i, j; > for (i = 0; i < children_with_mergeinfo->nelts; i++) >@@ -2531,17 +2562,24 @@ > continue; > if (child->remaining_ranges->nelts > 0) > { >- apr_array_header_t *orig_remaining_ranges = child->remaining_ranges; >- child->remaining_ranges = >- apr_array_make(pool, (child->remaining_ranges->nelts - 1), >- sizeof(svn_merge_range_t *)); >- for (j = 1; j < orig_remaining_ranges->nelts; j++) >+ svn_merge_range_t *first_range = >+ APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *); >+ if (first_range->end == end_rev) > { >- svn_merge_range_t *range = APR_ARRAY_IDX(orig_remaining_ranges, >- j, >- svn_merge_range_t *); >- APR_ARRAY_PUSH(child->remaining_ranges, svn_merge_range_t *) >- = range; >+ apr_array_header_t *orig_remaining_ranges = >+ child->remaining_ranges; >+ child->remaining_ranges = >+ apr_array_make(pool, (child->remaining_ranges->nelts - 1), >+ sizeof(svn_merge_range_t *)); >+ for (j = 1; j < orig_remaining_ranges->nelts; j++) >+ { >+ svn_merge_range_t *range = >+ APR_ARRAY_IDX(orig_remaining_ranges, >+ j, >+ svn_merge_range_t *); >+ APR_ARRAY_PUSH(child->remaining_ranges, >+ svn_merge_range_t *) = range; >+ } > } > } > } >@@ -3455,6 +3493,89 @@ > } > > >+/* Implements the svn_log_entry_receiver_t interface. */ >+static svn_error_t * >+log_changed_revs(void *baton, >+ svn_log_entry_t *log_entry, >+ apr_pool_t *pool) >+{ >+ apr_array_header_t *revs = baton; >+ svn_revnum_t *revision = apr_palloc(revs->pool, sizeof(*revision)); >+ *revision = log_entry->revision; >+ APR_ARRAY_PUSH(revs, svn_revnum_t *) = revision; >+ return SVN_NO_ERROR; >+} >+ >+ >+/* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge >+ range objects copied wholesale from RANGES which have the property >+ that in some revision within that range the object identified by >+ RA_SESSION was modified (if by "modified" we mean "'svn log' would >+ return that revision). *OPERATIVE_RANGES_P is allocated from the >+ same pool as RANGES, and the ranges within it are shared with >+ RANGES, too. Use POOL for temporary allocations. */ >+static svn_error_t * >+remove_noop_merge_ranges(apr_array_header_t **operative_ranges_p, >+ svn_ra_session_t *ra_session, >+ apr_array_header_t *ranges, >+ apr_pool_t *pool) >+{ >+ int i; >+ svn_revnum_t oldest_rev = SVN_INVALID_REVNUM; >+ svn_revnum_t youngest_rev = SVN_INVALID_REVNUM; >+ apr_array_header_t *changed_revs = >+ apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t *)); >+ apr_array_header_t *operative_ranges = >+ apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size); >+ apr_array_header_t *log_targets = >+ apr_array_make(pool, 1, sizeof(const char *)); >+ APR_ARRAY_PUSH(log_targets, const char *) = ""; >+ >+ /* Find the revision extremes of the RANGES we have. */ >+ for (i = 0; i < ranges->nelts; i++) >+ { >+ svn_merge_range_t *r = APR_ARRAY_IDX(ranges, i, svn_merge_range_t *); >+ svn_revnum_t max_rev = MAX(r->start, r->end); >+ svn_revnum_t min_rev = MIN(r->start, r->end) + 1; >+ >+ if ((! SVN_IS_VALID_REVNUM(youngest_rev)) || (max_rev > youngest_rev)) >+ youngest_rev = max_rev; >+ if ((! SVN_IS_VALID_REVNUM(oldest_rev)) || (min_rev < oldest_rev)) >+ oldest_rev = min_rev; >+ } >+ >+ /* Get logs across those ranges, recording which revisions hold >+ changes to our object's history. */ >+ SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev, >+ oldest_rev, 0, FALSE, FALSE, FALSE, >+ apr_array_make(pool, 0, sizeof(const char *)), >+ log_changed_revs, changed_revs, pool)); >+ >+ /* Now, copy from RANGES to *OPERATIVE_RANGES, filtering out ranges >+ that aren't operative (by virtue of not having any revisions >+ represented in the CHANGED_REVS array). */ >+ for (i = 0; i < ranges->nelts; i++) >+ { >+ svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i, svn_merge_range_t *); >+ int j; >+ >+ for (j = 0; j < changed_revs->nelts; j++) >+ { >+ svn_revnum_t *changed_rev = >+ APR_ARRAY_IDX(changed_revs, j, svn_revnum_t *); >+ if ((*changed_rev > MIN(range->start, range->end)) >+ && (*changed_rev <= MAX(range->start, range->end))) >+ { >+ APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) = range; >+ break; >+ } >+ } >+ } >+ *operative_ranges_p = operative_ranges; >+ return SVN_NO_ERROR; >+} >+ >+ > /*-----------------------------------------------------------------------*/ > > /*** Merge Source Normalization ***/ >@@ -3859,6 +3980,54 @@ > > /*** Merge Workhorse Functions ***/ > >+/* Helper for do_directory_merge() and do_file_merge() which filters out a >+ path's own natural history from the mergeinfo describing a merge. >+ >+ Given the natural history IMPLICIT_MERGEINFO of some wc merge target path, >+ the repository relative merge source path SOURCE_REL_PATH, and the >+ requested merge range REQUESTED_RANGE from SOURCE_REL_PATH, remove any >+ portion of REQUESTED_RANGE which is already described in >+ IMPLICIT_MERGEINFO. Store the result in *FILTERED_RANGELIST. >+ >+ *FILTERED_RANGELIST is allocated in POOL. */ >+static svn_error_t * >+filter_natural_history_from_mergeinfo(apr_array_header_t **filtered_rangelist, >+ const char *source_rel_path, >+ svn_mergeinfo_t implicit_mergeinfo, >+ svn_merge_range_t *requested_range, >+ apr_pool_t *pool) >+{ >+ /* Make the REQUESTED_RANGE into a rangelist. */ >+ apr_array_header_t *requested_rangelist = >+ apr_array_make(pool, 0, sizeof(svn_merge_range_t *)); >+ APR_ARRAY_PUSH(requested_rangelist, svn_merge_range_t *) = >+ svn_merge_range_dup(requested_range, pool); >+ >+ *filtered_rangelist = NULL; >+ >+ /* If the IMPLICIT_MERGEINFO already describes ranges associated >+ with SOURCE_REL_PATH then filter those ranges out. */ >+ if (implicit_mergeinfo) >+ { >+ apr_array_header_t *implied_rangelist = >+ apr_hash_get(implicit_mergeinfo, source_rel_path, >+ APR_HASH_KEY_STRING); >+ >+ if (implied_rangelist) >+ SVN_ERR(svn_rangelist_remove(filtered_rangelist, >+ implied_rangelist, >+ requested_rangelist, >+ FALSE, pool)); >+ } >+ >+ /* If no filtering was performed the filtered rangelist is >+ simply the requested rangelist.*/ >+ if (! (*filtered_rangelist)) >+ *filtered_rangelist = requested_rangelist; >+ >+ return SVN_NO_ERROR; >+} >+ > /* The single-file, simplified version of do_directory_merge(), which see for > parameter descriptions. > >@@ -3868,6 +4037,9 @@ > merge source are historically related (ancestors, uncles, second > cousins thrice removed, etc...). (This is used to simulate the > history checks that the repository logic does in the directory case.) >+ >+ Note: MERGE_B->RA_SESSION1 must be associated with URL1 and >+ MERGE_B->RA_SESSION2 with URL2. > */ > static svn_error_t * > do_file_merge(const char *url1, >@@ -3900,6 +4072,7 @@ > svn_boolean_t is_rollback = (revision1 > revision2); > const char *primary_url = is_rollback ? url1 : url2; > svn_boolean_t honor_mergeinfo, record_mergeinfo; >+ svn_mergeinfo_t implicit_mergeinfo; > > mergeinfo_behavior(&honor_mergeinfo, &record_mergeinfo, merge_b); > >@@ -3922,32 +4095,30 @@ > if (honor_mergeinfo) > { > const char *source_root_url; >- svn_mergeinfo_t implicit_mergeinfo; >- >- >+ > SVN_ERR(svn_ra_get_repos_root2(merge_b->ra_session1, > &source_root_url, pool)); > SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, primary_url, > source_root_url, TRUE, NULL, > NULL, pool)); > >+ /* Fetch mergeinfo (temporarily reparenting ra_session1 to >+ working copy target URL). */ >+ SVN_ERR(svn_ra_reparent(merge_b->ra_session1, entry->url, pool)); >+ SVN_ERR(get_full_mergeinfo(&target_mergeinfo, &implicit_mergeinfo, >+ entry, &indirect, svn_mergeinfo_inherited, >+ merge_b->ra_session1, target_wcpath, >+ MAX(revision1, revision2), >+ MIN(revision1, revision2), >+ adm_access, ctx, pool)); >+ >+ SVN_ERR(svn_ra_reparent(merge_b->ra_session1, url1, pool)); >+ > /* Calculate remaining merges unless this is a record only merge. > In that case the remaining range is the whole range described > by REVISION1:REVISION2. */ > if (!merge_b->record_only) > { >- /* Fetch mergeinfo (temporarily reparenting ra_session1 to >- working copy target URL). */ >- SVN_ERR(svn_ra_reparent(merge_b->ra_session1, entry->url, pool)); >- SVN_ERR(get_full_mergeinfo(&target_mergeinfo, &implicit_mergeinfo, >- entry, &indirect, svn_mergeinfo_inherited, >- merge_b->ra_session1, target_wcpath, >- MAX(revision1, revision2), >- MIN(revision1, revision2), >- adm_access, ctx, pool)); >- >- SVN_ERR(svn_ra_reparent(merge_b->ra_session1, url1, pool)); >- > SVN_ERR(calculate_remaining_ranges(&remaining_ranges, > source_root_url, > url1, revision1, url2, revision2, >@@ -3969,16 +4140,38 @@ > > if (!merge_b->record_only) > { >- for (i = 0; i < remaining_ranges->nelts; i++) >+ apr_array_header_t *ranges_to_merge = remaining_ranges; >+ >+ /* If we have ancestrally related sources and more than one >+ range to merge, eliminate no-op ranges before going through >+ the effort of downloading the many copies of the file >+ required to do these merges (two copies per range). */ >+ if (merge_b->sources_ancestral && (remaining_ranges->nelts > 1)) > { >+ const char *old_sess_url = NULL; >+ SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, >+ merge_b->ra_session1, >+ primary_url, subpool)); >+ SVN_ERR(remove_noop_merge_ranges(&ranges_to_merge, >+ merge_b->ra_session1, >+ remaining_ranges, subpool)); >+ if (old_sess_url) >+ SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess_url, >+ subpool)); >+ svn_pool_clear(subpool); >+ } >+ >+ for (i = 0; i < ranges_to_merge->nelts; i++) >+ { > svn_wc_notify_t *n; > svn_boolean_t header_sent = FALSE; > svn_error_t *err = SVN_NO_ERROR; >+ svn_ra_session_t *ra_session1, *ra_session2; > > /* When using this merge range, account for the exclusivity of > its low value (which is indicated by this operation being a > merge vs. revert). */ >- svn_merge_range_t *r = APR_ARRAY_IDX(remaining_ranges, i, >+ svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, i, > svn_merge_range_t *); > > svn_pool_clear(subpool); >@@ -3991,12 +4184,31 @@ > if (merge_b->sources_ancestral) > n->merge_range = r; > >+ /* Issue #3174: If we are honoring mergeinfo, then URL1, URL2, >+ REVISION1, and REVISION2 meet the conditions described in >+ 'MERGEINFO MERGE SOURCE NORMALIZATION'. This means that >+ URL1@REVISION1 may be the copy source of URL2@REVISION2. >+ If this is the case, then URL1 != URL2. Since >+ MERGE_B->RA_SESSION1 is always opened with URL1, the only time >+ we can safely call single_file_merge_get_file() with that RA >+ session is for REVISION1 (or REVISION2 if this is a reverse >+ merge). */ >+ ra_session1 = merge_b->ra_session1; >+ ra_session2 = merge_b->ra_session2; >+ if (honor_mergeinfo && strcmp(url1, url2) != 0) >+ { >+ if (!is_rollback && r->start != revision1) >+ ra_session1 = ra_session2; /* Use URL2's RA session. */ >+ else if (is_rollback && r->end != revision2) >+ ra_session2 = ra_session1; /* Use URL1's RA session. */ >+ } >+ > /* While we currently don't allow it, in theory we could be > fetching two fulltexts from two different repositories here. */ >- SVN_ERR(single_file_merge_get_file(&tmpfile1, merge_b->ra_session1, >+ SVN_ERR(single_file_merge_get_file(&tmpfile1, ra_session1, > &props1, r->start, target_wcpath, > subpool)); >- SVN_ERR(single_file_merge_get_file(&tmpfile2, merge_b->ra_session2, >+ SVN_ERR(single_file_merge_get_file(&tmpfile2, ra_session2, > &props2, r->end, target_wcpath, > subpool)); > >@@ -4078,7 +4290,7 @@ > return err; > svn_error_clear(err); > >- if ((i < (remaining_ranges->nelts - 1)) >+ if ((i < (ranges_to_merge->nelts - 1)) > && is_path_conflicted_by_merge(merge_b)) > { > conflicted_range = r; >@@ -4088,24 +4300,41 @@ > } /* !merge_b->record_only */ > > /* Record updated WC mergeinfo to account for our new merges, minus >- any unresolved conflicts and skips. */ >+ any unresolved conflicts and skips. We use the original >+ REMAINING_RANGES here instead of the possibly-pared-down >+ RANGES_TO_MERGE because we want to record all the requested >+ merge ranges, include the noop ones. */ > if (record_mergeinfo && remaining_ranges->nelts) > { > apr_hash_t *merges; >- SVN_ERR(determine_merges_performed(&merges, target_wcpath, >- &range, svn_depth_infinity, >- adm_access, notify_b, merge_b, >- subpool)); >- /* If merge target has indirect mergeinfo set it before >- recording the first merge range. */ >- if (indirect) >- SVN_ERR(svn_client__record_wc_mergeinfo(target_wcpath, >- target_mergeinfo, >- adm_access, subpool)); >+ apr_array_header_t *filtered_rangelist; >+ >+ /* Filter any ranges from TARGET_WCPATH's own history, there is no >+ need to record this explicitly in mergeinfo, it is already part >+ of TARGET_WCPATH's natural history (implicit mergeinfo). */ >+ SVN_ERR(filter_natural_history_from_mergeinfo(&filtered_rangelist, >+ mergeinfo_path, >+ implicit_mergeinfo, >+ &range, subpool)); >+ >+ if (filtered_rangelist->nelts) >+ { >+ SVN_ERR(determine_merges_performed(&merges, target_wcpath, >+ filtered_rangelist, >+ svn_depth_infinity, >+ adm_access, notify_b, >+ merge_b, subpool)); >+ /* If merge target has indirect mergeinfo set it before >+ recording the first merge range. */ >+ if (indirect) >+ SVN_ERR(svn_client__record_wc_mergeinfo(target_wcpath, >+ target_mergeinfo, >+ adm_access, subpool)); > >- SVN_ERR(update_wc_mergeinfo(target_wcpath, entry, mergeinfo_path, >- merges, is_rollback, adm_access, >- ctx, subpool)); >+ SVN_ERR(update_wc_mergeinfo(target_wcpath, entry, mergeinfo_path, >+ merges, is_rollback, adm_access, >+ ctx, subpool)); >+ } > } > > svn_pool_destroy(subpool); >@@ -4258,6 +4487,11 @@ > ra_session, mergeinfo_path, > adm_access, merge_b)); > >+ /* Always start with a range which describes our most inclusive merge. */ >+ range.start = revision1; >+ range.end = revision2; >+ range.inheritable = inheritable; >+ > if (honor_mergeinfo && !merge_b->record_only) > { > svn_revnum_t start_rev, end_rev; >@@ -4267,119 +4501,116 @@ > end revisions. */ > start_rev = get_most_inclusive_start_rev(children_with_mergeinfo, > is_rollback); >- if (start_rev == SVN_INVALID_REVNUM) >- start_rev = revision1; >+ >+ /* Is there anything to merge? */ >+ if (SVN_IS_VALID_REVNUM(start_rev)) >+ { >+ range.start = start_rev; >+ end_rev = get_youngest_end_rev(children_with_mergeinfo, is_rollback); > >- end_rev = get_youngest_end_rev(children_with_mergeinfo, is_rollback); >+ /* Build a range which describes our most inclusive merge. */ >+ range.start = start_rev; > >- /* Build a range which describes our most inclusive merge. */ >- range.start = start_rev; >- range.end = revision2; >- range.inheritable = inheritable; >+ /* While END_REV is valid, do the following: > >- /* While END_REV is valid, do the following: >+ 1. slice each remaining ranges around this 'end_rev'. >+ 2. starting with START_REV, call >+ drive_merge_report_editor() on MERGE_B->target for >+ start_rev:end_rev. >+ 3. remove the first item from each remaining range. >+ 4. set START_REV=END_REV and pick the next END_REV. >+ 5. lather, rinse, repeat. >+ */ >+ iterpool = svn_pool_create(pool); >+ while (end_rev != SVN_INVALID_REVNUM) >+ { >+ svn_revnum_t next_end_rev; >+ const char *real_url1 = url1, *real_url2 = url2; >+ const char *old_sess1_url = NULL, *old_sess2_url = NULL; > >- 1. slice each remaining ranges around this 'end_rev'. >- 2. starting with START_REV, call >- drive_merge_report_editor() on MERGE_B->target for >- start_rev:end_rev. >- 3. remove the first item from each remaining range. >- 4. set START_REV=END_REV and pick the next END_REV. >- 5. lather, rinse, repeat. >- */ >- iterpool = svn_pool_create(pool); >- while (end_rev != SVN_INVALID_REVNUM) >- { >- svn_revnum_t next_end_rev; >- const char *real_url1 = url1, *real_url2 = url2; >- const char *old_sess1_url = NULL, *old_sess2_url = NULL; >+ svn_pool_clear(iterpool); > >- svn_pool_clear(iterpool); >+ /* Use persistent pool while playing with remaining_ranges. */ >+ slice_remaining_ranges(children_with_mergeinfo, is_rollback, >+ end_rev, pool); >+ notify_b->cur_ancestor_index = -1; > >- /* Use persistent pool while playing with remaining_ranges. */ >- slice_remaining_ranges(children_with_mergeinfo, is_rollback, >- end_rev, pool); >- notify_b->cur_ancestor_index = -1; >+ /* URL1@REVISION1 is a real location; URL2@REVISION2 is a >+ real location -- that much we know (thanks to the merge >+ source normalization code). But for revisions between >+ them, the URLs might differ. Here are the rules: > >- /* URL1@REVISION1 is a real location; URL2@REVISION2 is a >- real location -- that much we know (thanks to the merge >- source normalization code). But for revisions between >- them, the URLs might differ. Here are the rules: >+ * If URL1 == URL2, then all URLs between REVISION1 and >+ REVISION2 also match URL1/URL2. > >- * If URL1 == URL2, then all URLs between REVISION1 and >- REVISION2 also match URL1/URL2. >+ * If URL1 != URL2, then: > >- * If URL1 != URL2, then: >+ * If REVISION1 < REVISION2, only REVISION1 maps to >+ URL1. The revisions between REVISION1+1 and >+ REVISION2 (inclusive) map to URL2. > >- * If REVISION1 < REVISION2, only REVISION1 maps to >- URL1. The revisions between REVISION1+1 and >- REVISION2 (inclusive) map to URL2. >- >- * If REVISION1 > REVISION2, Only REVISION2 maps to >- URL2. The revisions between REVISION1 and >- REVISION2+1 (inclusive) map to URL1. >- >- We need to adjust our URLs accordingly, here. >- */ >- if (! same_urls) >- { >- if (is_rollback && (end_rev != revision2)) >+ * If REVISION1 > REVISION2, Only REVISION2 maps to >+ URL2. The revisions between REVISION1 and >+ REVISION2+1 (inclusive) map to URL1. >+ >+ We need to adjust our URLs accordingly, here. >+ */ >+ if (! same_urls) > { >- real_url2 = url1; >- SVN_ERR(svn_client__ensure_ra_session_url >- (&old_sess2_url, merge_b->ra_session2, >- real_url2, iterpool)); >+ if (is_rollback && (end_rev != revision2)) >+ { >+ real_url2 = url1; >+ SVN_ERR(svn_client__ensure_ra_session_url >+ (&old_sess2_url, merge_b->ra_session2, >+ real_url2, iterpool)); >+ } >+ if ((! is_rollback) && (start_rev != revision1)) >+ { >+ real_url1 = url2; >+ SVN_ERR(svn_client__ensure_ra_session_url >+ (&old_sess1_url, merge_b->ra_session1, >+ real_url1, iterpool)); >+ } > } >- if ((! is_rollback) && (start_rev != revision1)) >+ SVN_ERR(drive_merge_report_editor(merge_b->target, >+ real_url1, start_rev, >+ real_url2, end_rev, >+ children_with_mergeinfo, >+ is_rollback, >+ depth, notify_b, adm_access, >+ &merge_callbacks, merge_b, >+ iterpool)); >+ if (old_sess1_url) >+ SVN_ERR(svn_ra_reparent(merge_b->ra_session1, >+ old_sess1_url, iterpool)); >+ if (old_sess2_url) >+ SVN_ERR(svn_ra_reparent(merge_b->ra_session2, >+ old_sess2_url, iterpool)); >+ >+ /* Prepare for the next iteration (if any). */ >+ remove_first_range_from_remaining_ranges( >+ end_rev, children_with_mergeinfo, pool); >+ next_end_rev = get_youngest_end_rev(children_with_mergeinfo, >+ is_rollback); >+ if ((next_end_rev != SVN_INVALID_REVNUM) >+ && is_path_conflicted_by_merge(merge_b)) > { >- real_url1 = url2; >- SVN_ERR(svn_client__ensure_ra_session_url >- (&old_sess1_url, merge_b->ra_session1, >- real_url1, iterpool)); >+ svn_merge_range_t conflicted_range; >+ conflicted_range.start = start_rev; >+ conflicted_range.end = end_rev; >+ err = make_merge_conflict_error(merge_b->target, >+ &conflicted_range, pool); >+ range.end = end_rev; >+ break; > } >+ start_rev = end_rev; >+ end_rev = next_end_rev; > } >- SVN_ERR(drive_merge_report_editor(merge_b->target, >- real_url1, start_rev, real_url2, >- end_rev, children_with_mergeinfo, >- is_rollback, >- depth, notify_b, adm_access, >- &merge_callbacks, merge_b, >- iterpool)); >- if (old_sess1_url) >- SVN_ERR(svn_ra_reparent(merge_b->ra_session1, >- old_sess1_url, iterpool)); >- if (old_sess2_url) >- SVN_ERR(svn_ra_reparent(merge_b->ra_session2, >- old_sess2_url, iterpool)); >- >- /* Prepare for the next iteration (if any). */ >- remove_first_range_from_remaining_ranges(children_with_mergeinfo, >- pool); >- next_end_rev = get_youngest_end_rev(children_with_mergeinfo, >- is_rollback); >- if ((next_end_rev != SVN_INVALID_REVNUM) >- && is_path_conflicted_by_merge(merge_b)) >- { >- svn_merge_range_t conflicted_range; >- conflicted_range.start = start_rev; >- conflicted_range.end = end_rev; >- err = make_merge_conflict_error(merge_b->target, >- &conflicted_range, pool); >- range.end = end_rev; >- break; >- } >- start_rev = end_rev; >- end_rev = next_end_rev; >+ svn_pool_destroy(iterpool); > } >- svn_pool_destroy(iterpool); > } > else > { >- /* Build a range which describes our most inclusive merge. */ >- range.start = revision1; >- range.end = revision2; >- range.inheritable = inheritable; >- > if (!merge_b->record_only) > { > /* Reset cur_ancestor_index to -1 so that subsequent cherry >@@ -4405,6 +4636,10 @@ > iterpool = svn_pool_create(pool); > if (record_mergeinfo) > { >+ apr_array_header_t *filtered_rangelist; >+ svn_client__merge_path_t *merge_target = >+ APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *); >+ > /* Update the WC mergeinfo here to account for our new > merges, minus any unresolved conflicts and skips. */ > apr_hash_t *merges; >@@ -4414,15 +4649,30 @@ > calculate the merges performed. */ > remove_absent_children(merge_b->target, > children_with_mergeinfo, notify_b); >- SVN_ERR(determine_merges_performed(&merges, merge_b->target, &range, >- depth, adm_access, notify_b, merge_b, >- iterpool)); >- SVN_ERR(record_mergeinfo_on_merged_children(depth, adm_access, notify_b, >- merge_b, iterpool)); >- SVN_ERR(update_wc_mergeinfo(merge_b->target, parent_entry, >- mergeinfo_path, merges, >- is_rollback, adm_access, merge_b->ctx, >- iterpool)); >+ >+ /* Filter any ranges from MERGE_B->TARGET's own history, there is no >+ need to record this explicitly in mergeinfo, it is already part of >+ MERGE_B->TARGET's natural history (implicit mergeinfo). */ >+ SVN_ERR(filter_natural_history_from_mergeinfo( >+ &filtered_rangelist, mergeinfo_path, merge_target->implicit_mergeinfo, >+ &range, iterpool)); >+ >+ if (filtered_rangelist->nelts) >+ { >+ SVN_ERR(determine_merges_performed(&merges, merge_b->target, >+ filtered_rangelist, depth, >+ adm_access, notify_b, >+ merge_b, iterpool)); >+ >+ SVN_ERR(record_mergeinfo_on_merged_children(depth, adm_access, >+ notify_b, merge_b, >+ iterpool)); >+ SVN_ERR(update_wc_mergeinfo(merge_b->target, parent_entry, >+ mergeinfo_path, merges, >+ is_rollback, adm_access, merge_b->ctx, >+ iterpool)); >+ } >+ > for (i = 0; i < children_with_mergeinfo->nelts; i++) > { > const char *child_repos_path; >@@ -4430,7 +4680,6 @@ > const svn_wc_entry_t *child_entry; > apr_array_header_t *child_merge_rangelist; > apr_hash_t *child_merges; >- svn_merge_range_t *child_merge_range; > svn_client__merge_path_t *child = > APR_ARRAY_IDX(children_with_mergeinfo, i, > svn_client__merge_path_t *); >@@ -4449,19 +4698,31 @@ > adm_access, FALSE, iterpool)); > > child_merges = apr_hash_make(iterpool); >- child_merge_range = svn_merge_range_dup(&range, iterpool); >- if (child_entry->kind == svn_node_file) >- child_merge_range->inheritable = TRUE; >+ >+ /* As we did above for the merge target, filter any ranges from >+ each child's natural history before setting mergeinfo. */ >+ SVN_ERR(filter_natural_history_from_mergeinfo( >+ &child_merge_rangelist, child_merge_src_canon_path, >+ child->implicit_mergeinfo, &range, iterpool)); >+ >+ if (child_merge_rangelist->nelts == 0) >+ continue; > else >- child_merge_range->inheritable = >- (!(child->missing_child) >- && (depth == svn_depth_infinity >+ { >+ int j; >+ for (j = 0; j < child_merge_rangelist->nelts; j++) >+ { >+ svn_merge_range_t *rng = >+ APR_ARRAY_IDX(child_merge_rangelist, j, >+ svn_merge_range_t *); >+ if (child_entry->kind == svn_node_file) >+ rng->inheritable = TRUE; >+ else >+ rng->inheritable = (!(child->missing_child) >+ && (depth == svn_depth_infinity > || depth == svn_depth_immediates)); >- child_merge_rangelist = >- apr_array_make(iterpool, 1, >- sizeof(child_merge_range)); >- APR_ARRAY_PUSH(child_merge_rangelist, >- svn_merge_range_t *) = child_merge_range; >+ } >+ } > apr_hash_set(child_merges, child->path, APR_HASH_KEY_STRING, > child_merge_rangelist); > /* If merge target has indirect mergeinfo set it before >@@ -4489,10 +4750,42 @@ > merge_b, > children_with_mergeinfo, > i, iterpool)); >+ >+ /* Elide explicit subtree mergeinfo. */ > if (i > 0) >- SVN_ERR(svn_client__elide_mergeinfo(child->path, merge_b->target, >- child_entry, adm_access, >- merge_b->ctx, iterpool)); >+ { >+ svn_boolean_t in_switched_subtree = FALSE; >+ >+ if (child->switched) >+ in_switched_subtree = TRUE; >+ else if (i > 1) >+ { >+ /* Check if CHILD is part of a switched subtree */ >+ svn_client__merge_path_t *parent; >+ int j = i - 1; >+ for (; j > 0; j--) >+ { >+ parent = APR_ARRAY_IDX(children_with_mergeinfo, j, >+ svn_client__merge_path_t *); >+ if (parent >+ && parent->switched >+ && svn_path_is_ancestor(parent->path, child->path)) >+ { >+ in_switched_subtree = TRUE; >+ break; >+ } >+ } >+ } >+ >+ /* Allow mergeinfo on switched subtrees to elide to the >+ repository. Otherwise limit elision to the merge target >+ for now. do_directory_merge() will eventually try to >+ elide that when the merge is complete. */ >+ SVN_ERR(svn_client__elide_mergeinfo( >+ child->path, >+ in_switched_subtree ? NULL : merge_b->target, >+ child_entry, adm_access, merge_b->ctx, iterpool)); >+ } > } /* (i = 0; i < children_with_mergeinfo->nelts; i++) */ > > /* If a path has an immediate parent with non-inheritable mergeinfo at >--- subversion/libsvn_client/mergeinfo.c >+++ subversion/libsvn_client/mergeinfo.c >@@ -164,14 +164,14 @@ > SVN_ERR(svn_client__parse_mergeinfo(&wc_mergeinfo, entry, wcpath, > pristine, adm_access, ctx, > pool)); >- >- /* If WCPATH is switched, don't look any higher for inherited >- mergeinfo. */ >- SVN_ERR(svn_wc__path_switched(wcpath, &switched, entry, pool)); >- if (switched) >- break; > } > >+ /* If WCPATH is switched, don't look any higher for inherited >+ mergeinfo. */ >+ SVN_ERR(svn_wc__path_switched(wcpath, &switched, entry, pool)); >+ if (switched) >+ break; >+ > /* Subsequent svn_wc_adm_access_t need to be opened with > an absolute path so we can walk up and out of the WC > if necessary. If we are using LIMIT_PATH it needs to >@@ -725,57 +725,52 @@ > { > svn_mergeinfo_t target_mergeinfo; > svn_mergeinfo_t mergeinfo = NULL; >- svn_boolean_t inherited, switched; >+ svn_boolean_t inherited; > const char *walk_path; > >- /* Check for second easy out: TARGET_WCPATH is switched. */ >- SVN_ERR(svn_wc__path_switched(target_wcpath, &switched, entry, pool)); >- if (!switched) >- { >- /* Get the TARGET_WCPATH's explicit mergeinfo. */ >- SVN_ERR(svn_client__get_wc_mergeinfo(&target_mergeinfo, &inherited, >- FALSE, svn_mergeinfo_inherited, >- entry, target_wcpath, >- wc_elision_limit_path >- ? wc_elision_limit_path >- : NULL, >- &walk_path, adm_access, >- ctx, pool)); >+ /* Get the TARGET_WCPATH's explicit mergeinfo. */ >+ SVN_ERR(svn_client__get_wc_mergeinfo(&target_mergeinfo, &inherited, >+ FALSE, svn_mergeinfo_inherited, >+ entry, target_wcpath, >+ wc_elision_limit_path >+ ? wc_elision_limit_path >+ : NULL, >+ &walk_path, adm_access, >+ ctx, pool)); > >- /* If TARGET_WCPATH has no explicit mergeinfo, there's nothing to >- elide, we're done. */ >- if (inherited || target_mergeinfo == NULL) >- return SVN_NO_ERROR; >+ /* If TARGET_WCPATH has no explicit mergeinfo, there's nothing to >+ elide, we're done. */ >+ if (inherited || target_mergeinfo == NULL) >+ return SVN_NO_ERROR; > >- /* Get TARGET_WCPATH's inherited mergeinfo from the WC. */ >- SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, &inherited, FALSE, >- svn_mergeinfo_nearest_ancestor, >- entry, target_wcpath, >- wc_elision_limit_path >- ? wc_elision_limit_path >- : NULL, >- &walk_path, adm_access, >- ctx, pool)); >+ /* Get TARGET_WCPATH's inherited mergeinfo from the WC. */ >+ SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, &inherited, FALSE, >+ svn_mergeinfo_nearest_ancestor, >+ entry, target_wcpath, >+ wc_elision_limit_path >+ ? wc_elision_limit_path >+ : NULL, >+ &walk_path, adm_access, >+ ctx, pool)); > >- /* If TARGET_WCPATH inherited no mergeinfo from the WC and we are >- not limiting our search to the working copy then check if it >- inherits any from the repos. */ >- if (!mergeinfo && !wc_elision_limit_path) >- { >- SVN_ERR(svn_client__get_wc_or_repos_mergeinfo >- (&mergeinfo, entry, &inherited, TRUE, >- svn_mergeinfo_nearest_ancestor, >- NULL, target_wcpath, adm_access, ctx, pool)); >- } >+ /* If TARGET_WCPATH inherited no mergeinfo from the WC and we are >+ not limiting our search to the working copy then check if it >+ inherits any from the repos. */ >+ if (!mergeinfo && !wc_elision_limit_path) >+ { >+ SVN_ERR(svn_client__get_wc_or_repos_mergeinfo >+ (&mergeinfo, entry, &inherited, TRUE, >+ svn_mergeinfo_nearest_ancestor, >+ NULL, target_wcpath, adm_access, ctx, pool)); >+ } > >- /* If there is nowhere to elide TARGET_WCPATH's mergeinfo to and >- the elision is limited, then we are done.*/ >- if (!mergeinfo && wc_elision_limit_path) >- return SVN_NO_ERROR; >+ /* If there is nowhere to elide TARGET_WCPATH's mergeinfo to and >+ the elision is limited, then we are done.*/ >+ if (!mergeinfo && wc_elision_limit_path) >+ return SVN_NO_ERROR; > >- SVN_ERR(elide_mergeinfo(mergeinfo, target_mergeinfo, target_wcpath, >- NULL, adm_access, pool)); >- } >+ SVN_ERR(elide_mergeinfo(mergeinfo, target_mergeinfo, target_wcpath, >+ NULL, adm_access, pool)); > } > return SVN_NO_ERROR; > } >--- subversion/libsvn_client/mergeinfo.h >+++ subversion/libsvn_client/mergeinfo.h >@@ -51,6 +51,8 @@ > apr_array_header_t *remaining_ranges; /* Per path remaining ranges list. */ > svn_mergeinfo_t pre_merge_mergeinfo; /* mergeinfo on a path prior to a > merge.*/ >+ svn_mergeinfo_t implicit_mergeinfo; /* Implicit mergeinfo on a path prior >+ to a merge.*/ > svn_boolean_t indirect_mergeinfo; > svn_boolean_t scheduled_for_deletion; /* PATH is scheduled for deletion. */ > } svn_client__merge_path_t; >@@ -72,7 +74,7 @@ > inherited mergeinfo for WCPATH is retrieved. > > Don't look for inherited mergeinfo any higher than LIMIT_PATH >- (ignored if NULL). >+ (ignored if NULL) or beyond any switched path. > > Set *WALKED_PATH to the path climbed from WCPATH to find inherited > mergeinfo, or "" if none was found. (ignored if NULL). */ >@@ -196,10 +198,10 @@ > copy (or possibly repository) ancestor with equivalent mergeinfo. > > If WC_ELISION_LIMIT_PATH is NULL check up to the root of the working copy >- for an elision destination, if none is found check the repository, >- otherwise check as far as WC_ELISION_LIMIT_PATH within the working copy. >- TARGET_PATH and WC_ELISION_LIMIT_PATH, if it exists, must both be absolute >- or relative to the working directory. >+ or the nearest switched parent for an elision destination, if none is found >+ check the repository, otherwise check as far as WC_ELISION_LIMIT_PATH >+ within the working copy. TARGET_PATH and WC_ELISION_LIMIT_PATH, if it >+ exists, must both be absolute or relative to the working directory. > > Elision occurs if: > >--- subversion/libsvn_wc/merge.c >+++ subversion/libsvn_wc/merge.c >@@ -624,9 +624,18 @@ > } > else > { >- svn_boolean_t same; >+ svn_boolean_t same, special; >+ /* If 'special', then use the detranslated form of the >+ target file. This is so we don't try to follow symlinks, >+ but the same treatment is probably also appropriate for >+ whatever special file types we may invent in the future. */ >+ SVN_ERR(svn_wc__get_special(&special, merge_target, >+ adm_access, pool)); > SVN_ERR(svn_io_files_contents_same_p(&same, result_target, >- merge_target, pool)); >+ (special ? >+ tmp_target : >+ merge_target), >+ pool)); > > *merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged; > }
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 228653
:
157839
| 157841