You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pb...@apache.org on 2010/11/05 21:35:23 UTC
svn commit: r1031780 - in /subversion/branches/issue-3668-3669/subversion:
libsvn_client/copy.c libsvn_client/merge.c libsvn_client/mergeinfo.c
libsvn_client/mergeinfo.h tests/cmdline/merge_reintegrate_tests.py
tests/cmdline/merge_tests.py
Author: pburba
Date: Fri Nov 5 20:35:22 2010
New Revision: 1031780
URL: http://svn.apache.org/viewvc?rev=1031780&view=rev
Log:
On the issue-3668-3669 branch: Fix issue #3669 'inheritance can result in
mergeinfo describing nonexistent sources'.
* subversion/libsvn_client/copy.c
(svn_client__get_repos_mergeinfo): Update call to
svn_client__get_repos_mergeinfo().
* subversion/libsvn_client/merge.c
(svn_client__get_repos_mergeinfo): New.
(get_full_mergeinfo): Use new function to validate inherited mergeinfo.
* subversion/libsvn_client/mergeinfo.h
(svn_client__get_repos_mergeinfo,
svn_client__get_repos_mergeinfo_catalog): Add new arg to optionally
validate inherited mergeinfo
* subversion/libsvn_client/mergeinfo.c
(svn_client__get_repos_mergeinfo,
svn_client__get_repos_mergeinfo_catalog): Add new arg to optionally
validate inherited mergeinfo
(svn_client__get_wc_or_repos_mergeinfo_catalog,
get_mergeinfo): Update calls to svn_client__get_repos_mergeinfo_catalog.
* subversion/tests/cmdline/merge_reintegrate_tests.py
(added_subtrees_with_mergeinfo_break_reintegrate): Don't expect mergeinfo
update notification to be ' G' anymore, rather ' U' because the diff
applied by the reintegrate is now inoperative.
* subversion/tests/cmdline/merge_tests.py
(new_subtrees_should_not_break_merge): Don't expect inherited mergeinfo to
be self-referential.
(no_self_referential_or_nonexistent_inherited_mergeinfo): Remove comment
re XFail status.
(test_list): Remove XFail from
no_self_referential_or_nonexistent_inherited_mergeinfo.
Modified:
subversion/branches/issue-3668-3669/subversion/libsvn_client/copy.c
subversion/branches/issue-3668-3669/subversion/libsvn_client/merge.c
subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.c
subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.h
subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_reintegrate_tests.py
subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_tests.py
Modified: subversion/branches/issue-3668-3669/subversion/libsvn_client/copy.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-3668-3669/subversion/libsvn_client/copy.c?rev=1031780&r1=1031779&r2=1031780&view=diff
==============================================================================
--- subversion/branches/issue-3668-3669/subversion/libsvn_client/copy.c (original)
+++ subversion/branches/issue-3668-3669/subversion/libsvn_client/copy.c Fri Nov 5 20:35:22 2010
@@ -129,7 +129,7 @@ calculate_target_mergeinfo(svn_ra_sessio
SVN_ERR(svn_client__get_repos_mergeinfo(ra_session, &src_mergeinfo,
"", src_revnum,
svn_mergeinfo_inherited,
- TRUE, pool));
+ TRUE, FALSE, pool));
if (old_session_url)
SVN_ERR(svn_ra_reparent(ra_session, old_session_url, pool));
}
Modified: subversion/branches/issue-3668-3669/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-3668-3669/subversion/libsvn_client/merge.c?rev=1031780&r1=1031779&r2=1031780&view=diff
==============================================================================
--- subversion/branches/issue-3668-3669/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/issue-3668-3669/subversion/libsvn_client/merge.c Fri Nov 5 20:35:22 2010
@@ -3290,6 +3290,86 @@ fix_deleted_subtree_ranges(const char *u
/*** Determining What Remains To Be Merged ***/
+
+/* Set *INVALID_INHERITED_MERGEINFO to the mergeinfo that working copy path
+ TARGET_ABSPATH inherits, less any merge source path-revisions that don't
+ actually exist in the repository. If all inherited mergeinfo describes
+ non-existent paths, then *INVALID_INHERITED_MERGEINFO is set to an empty
+ hash. If TARGET_ABSPATH inherits no mergeinfo whatsoever (pre-validation),
+ then *INVALID_INHERITED_MERGEINFO is set to NULL.
+
+ RA_SESSION is an open session that points to TARGET_ABSPATH's repository
+ location or to the location of one of TARGET_ABSPATH's parents. It may
+ be temporarily reparented.
+
+ RESULT_POOL is used to allocate *INVALID_INHERITED_MERGEINFO, SCRATCH_POOL
+ is used for any temporary allocations. */
+static svn_error_t *
+get_invalid_inherited_mergeinfo(svn_mergeinfo_t *invalid_inherited_mergeinfo,
+ svn_ra_session_t *ra_session,
+ const char *target_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_mergeinfo_t repos_raw_inherited;
+ svn_mergeinfo_t repos_validated_inherited;
+ svn_revnum_t base_revision;
+
+ /* Our starting assumption. */
+ *invalid_inherited_mergeinfo = NULL;
+
+ SVN_ERR(svn_wc__node_get_base_rev(&base_revision, ctx->wc_ctx,
+ target_abspath, scratch_pool));
+
+ /* If there is no base revision then TARGET_ABSPATH doesn't exist
+ in the repository yet, so we're done. */
+ if (SVN_IS_VALID_REVNUM(base_revision))
+ {
+ const char *target_url;
+ const char *session_url;
+
+ /* Reparent RA_SESSION if necessary. */
+ SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, target_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client__ensure_ra_session_url(&session_url, ra_session,
+ target_url, scratch_pool));
+
+ /* Contact the repository to derive the portion of
+ TARGET_ABSPATH's inherited mergeinfo which is non-existent
+ and remove it from */
+ SVN_ERR(svn_client__get_repos_mergeinfo(
+ ra_session, &repos_raw_inherited, "", base_revision,
+ svn_mergeinfo_inherited, TRUE,
+ FALSE, /* validate_inherited_mergeinfo */
+ scratch_pool));
+
+ if (repos_raw_inherited == NULL)
+ {
+ *invalid_inherited_mergeinfo = NULL;
+ }
+ else if (apr_hash_count(repos_raw_inherited) == 0)
+ {
+ *invalid_inherited_mergeinfo = apr_hash_make(result_pool);
+ }
+ else
+ {
+ SVN_ERR(svn_client__get_repos_mergeinfo(
+ ra_session, &repos_validated_inherited, "", base_revision,
+ svn_mergeinfo_inherited, TRUE,
+ TRUE, /* validate_inherited_mergeinfo */
+ scratch_pool));
+ SVN_ERR(svn_mergeinfo_remove2(invalid_inherited_mergeinfo,
+ repos_validated_inherited,
+ repos_raw_inherited, FALSE,
+ result_pool, scratch_pool));
+ }
+ SVN_ERR(svn_client__ensure_ra_session_url(&session_url, ra_session,
+ session_url, scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
/* Get explicit and/or implicit mergeinfo for the working copy path
TARGET_ABSPATH.
@@ -3314,6 +3394,9 @@ fix_deleted_subtree_ranges(const char *u
is older than START, then the base revision is used as the younger
bound in place of START.
+ RA_SESSION is an open session that may be temporarily reparented as
+ needed by this function.
+
Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
Use SCRATCH_POOL for any temporary allocations. */
static svn_error_t *
@@ -3331,8 +3414,10 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
{
svn_boolean_t inherited = FALSE;
- /* First, we get the real mergeinfo. */
- if (recorded_mergeinfo)
+ /* First, we get the real mergeinfo. We use SCRATCH_POOL throughout this
+ block because we'll make a final copy of *RECORDED_MERGEINFO only after
+ removing any self-referential mergeinfo. */
+if (recorded_mergeinfo)
{
/* ### FIXME: There's probably an RA session we could/should be
### using here instead of having this function possibly spawn
@@ -3344,6 +3429,25 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
ctx, scratch_pool));
if (indirect)
*indirect = inherited;
+
+ /* Issue #3669: Remove any non-existent mergeinfo sources
+ from TARGET_ABSPATH's inherited mergeinfo. */
+ if (inherited)
+ {
+ svn_mergeinfo_t invalid_inherited_mergeinfo;
+
+ SVN_ERR(get_invalid_inherited_mergeinfo(&invalid_inherited_mergeinfo,
+ ra_session, target_abspath,
+ ctx, scratch_pool,
+ scratch_pool));
+
+ if (invalid_inherited_mergeinfo
+ && apr_hash_count(invalid_inherited_mergeinfo))
+ SVN_ERR(svn_mergeinfo_remove2(recorded_mergeinfo,
+ invalid_inherited_mergeinfo,
+ *recorded_mergeinfo, FALSE,
+ scratch_pool, scratch_pool));
+ }
}
if (implicit_mergeinfo)
@@ -3351,7 +3455,6 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
const char *session_url = NULL, *url;
svn_revnum_t target_rev;
svn_opt_revision_t peg_revision;
- apr_pool_t *sesspool = NULL;
svn_error_t *err;
/* Assert that we have sane input. */
@@ -3359,10 +3462,12 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
&& SVN_IS_VALID_REVNUM(end)
&& (start > end));
+ *implicit_mergeinfo = NULL;
+
peg_revision.kind = svn_opt_revision_working;
err = svn_client__derive_location(&url, &target_rev, target_abspath,
&peg_revision, ra_session,
- ctx, result_pool, scratch_pool);
+ ctx, scratch_pool, scratch_pool);
if (err)
{
@@ -3375,7 +3480,6 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
* mergeinfo is empty. */
svn_error_clear(err);
*implicit_mergeinfo = apr_hash_make(result_pool);
- return SVN_NO_ERROR;
}
else
return svn_error_return(err);
@@ -3386,49 +3490,37 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
/* We're asking about a range outside our natural history
altogether. That means our implicit mergeinfo is empty. */
*implicit_mergeinfo = apr_hash_make(result_pool);
- return SVN_NO_ERROR;
}
- /* Temporarily point our RA_SESSION at our target URL so we can
- fetch so-called "implicit mergeinfo" (that is, natural history). */
- if (ra_session)
+ if (*implicit_mergeinfo == NULL)
{
- SVN_ERR(svn_client__ensure_ra_session_url(&session_url, ra_session,
- url, scratch_pool));
- }
- else
- {
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url,
- NULL, NULL,
- FALSE, TRUE,
- ctx, scratch_pool));
- }
-
- /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
- TARGET_ABSPATH might not even exist, and even if it does the
- working copy is *at* TARGET_REV so its implicit history ends
- at TARGET_REV! */
- if (target_rev < start)
- start = target_rev;
-
- /* Fetch the implicit mergeinfo. */
- peg_revision.kind = svn_opt_revision_number;
- peg_revision.value.number = target_rev;
- SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo, url,
- &peg_revision, start, end,
- ra_session, ctx,
- result_pool));
+ /* Temporarily point our RA_SESSION at our target URL so we can
+ fetch so-called "implicit mergeinfo" (that is, natural
+ history). */
+ SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
+ ra_session, url,
+ scratch_pool));
- /* If we created an RA_SESSION above, destroy it. Otherwise, if
- reparented an existing session, point it back where it was when
- we were called. */
- if (sesspool)
- {
- svn_pool_destroy(sesspool);
- }
- else if (session_url)
- {
- SVN_ERR(svn_ra_reparent(ra_session, session_url, scratch_pool));
+ /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
+ TARGET_ABSPATH might not even exist, and even if it does the
+ working copy is *at* TARGET_REV so its implicit history ends
+ at TARGET_REV! */
+ if (target_rev < start)
+ start = target_rev;
+
+ /* Fetch the implicit mergeinfo. */
+ peg_revision.kind = svn_opt_revision_number;
+ peg_revision.value.number = target_rev;
+ SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
+ url, &peg_revision,
+ start, end,
+ ra_session, ctx,
+ result_pool));
+
+ /* Return RA_SESSION back to where it was when we were called. */
+ SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
+ ra_session, session_url,
+ scratch_pool));
}
} /*if (implicit_mergeinfo) */
Modified: subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.c?rev=1031780&r1=1031779&r2=1031780&view=diff
==============================================================================
--- subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.c Fri Nov 5 20:35:22 2010
@@ -442,6 +442,7 @@ svn_client__get_repos_mergeinfo(svn_ra_s
svn_revnum_t rev,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t squelch_incapable,
+ svn_boolean_t validate_inherited_mergeinfo,
apr_pool_t *pool)
{
svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
@@ -452,6 +453,7 @@ svn_client__get_repos_mergeinfo(svn_ra_s
ra_session,
rel_path, rev, inherit,
squelch_incapable, FALSE,
+ validate_inherited_mergeinfo,
pool, pool));
if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
@@ -468,15 +470,17 @@ svn_client__get_repos_mergeinfo(svn_ra_s
}
svn_error_t *
-svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
- svn_ra_session_t *ra_session,
- const char *rel_path,
- svn_revnum_t rev,
- svn_mergeinfo_inheritance_t inherit,
- svn_boolean_t squelch_incapable,
- svn_boolean_t include_descendants,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+svn_client__get_repos_mergeinfo_catalog(
+ svn_mergeinfo_catalog_t *mergeinfo_cat,
+ svn_ra_session_t *ra_session,
+ const char *rel_path,
+ svn_revnum_t rev,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t squelch_incapable,
+ svn_boolean_t include_descendants,
+ svn_boolean_t validate_inherited_mergeinfo,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_error_t *err;
svn_mergeinfo_t repos_mergeinfo;
@@ -486,8 +490,9 @@ svn_client__get_repos_mergeinfo_catalog(
APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
/* Fetch the mergeinfo. */
- err = svn_ra_get_mergeinfo(ra_session, &repos_mergeinfo, rel_paths, rev,
- inherit, include_descendants, result_pool);
+ err = svn_ra_get_mergeinfo2(ra_session, &repos_mergeinfo, rel_paths, rev,
+ inherit, validate_inherited_mergeinfo,
+ include_descendants, result_pool);
if (err)
{
if (squelch_incapable && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
@@ -624,7 +629,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
target_mergeinfo_catalog, ra_session,
"", target_rev, inherit,
- TRUE, FALSE, result_pool, scratch_pool));
+ TRUE, FALSE, FALSE, result_pool, scratch_pool));
if (*target_mergeinfo_catalog
&& apr_hash_get(*target_mergeinfo_catalog, "",
@@ -1043,7 +1048,7 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
svn_mergeinfo_inherited,
FALSE,
include_descendants,
- result_pool,
+ FALSE, result_pool,
scratch_pool));
/* If we're not querying the root of the repository, the catalog
Modified: subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.h?rev=1031780&r1=1031779&r2=1031780&view=diff
==============================================================================
--- subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/issue-3668-3669/subversion/libsvn_client/mergeinfo.h Fri Nov 5 20:35:22 2010
@@ -155,7 +155,12 @@ svn_client__get_wc_mergeinfo_catalog(svn
If there is no mergeinfo available for REL_PATH, or if the server
doesn't support a mergeinfo capability and SQUELCH_INCAPABLE is
- TRUE, set *TARGET_MERGEINFO to NULL. */
+ TRUE, set *TARGET_MERGEINFO to NULL.
+
+ If the *TARGET_MERGEINFO for REL_PATH path is inherited and
+ VALIDATE_INHERITED_MERGEINFO is TRUE, then *TARGET_MERGEINFO
+ will only contain merge source path-revisions that actually
+ exist in repository. */
svn_error_t *
svn_client__get_repos_mergeinfo(svn_ra_session_t *ra_session,
svn_mergeinfo_t *target_mergeinfo,
@@ -163,6 +168,7 @@ svn_client__get_repos_mergeinfo(svn_ra_s
svn_revnum_t rev,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t squelch_incapable,
+ svn_boolean_t validate_inherited_mergeinfo,
apr_pool_t *pool);
/* If INCLUDE_DESCENDANTS is FALSE, behave exactly like
@@ -176,15 +182,17 @@ svn_client__get_repos_mergeinfo(svn_ra_s
paths of the subtrees. If no mergeinfo is found, then
*TARGET_MERGEINFO_CAT is set to NULL. */
svn_error_t *
-svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
- svn_ra_session_t *ra_session,
- const char *rel_path,
- svn_revnum_t rev,
- svn_mergeinfo_inheritance_t inherit,
- svn_boolean_t squelch_incapable,
- svn_boolean_t include_descendants,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
+svn_client__get_repos_mergeinfo_catalog(
+ svn_mergeinfo_catalog_t *mergeinfo_cat,
+ svn_ra_session_t *ra_session,
+ const char *rel_path,
+ svn_revnum_t rev,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t squelch_incapable,
+ svn_boolean_t include_descendants,
+ svn_boolean_t validate_inherited_mergeinfo,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* Retrieve the direct mergeinfo for the TARGET_WCPATH from the WC's
mergeinfo prop, or that inherited from its nearest ancestor if the
Modified: subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_reintegrate_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_reintegrate_tests.py?rev=1031780&r1=1031779&r2=1031780&view=diff
==============================================================================
--- subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_reintegrate_tests.py (original)
+++ subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_reintegrate_tests.py Fri Nov 5 20:35:22 2010
@@ -1997,7 +1997,7 @@ def added_subtrees_with_mergeinfo_break_
})
expected_mergeinfo_output = wc.State(A_path, {
'' : Item(status=' U'),
- 'C/nu' : Item(status=' G'),
+ 'C/nu' : Item(status=' U'),
})
expected_elision_output = wc.State(A_path, {
})
Modified: subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_tests.py?rev=1031780&r1=1031779&r2=1031780&view=diff
==============================================================================
--- subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/issue-3668-3669/subversion/tests/cmdline/merge_tests.py Fri Nov 5 20:35:22 2010
@@ -9586,7 +9586,7 @@ def new_subtrees_should_not_break_merge(
'D/H/psi' : Item("This is the file 'psi'.\n"),
'D/H/omega' : Item("New content"),
'D/H/nu' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
+ props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
})
expected_skip = wc.State(A_COPY_path, { })
svntest.actions.run_and_verify_merge(A_COPY_path, '4', '6',
@@ -9632,7 +9632,7 @@ def new_subtrees_should_not_break_merge(
'H/psi' : Item("This is the file 'psi'.\n"),
'H/omega' : Item("This is the file 'omega'.\n"),
'H/nu' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
+ props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
})
expected_skip = wc.State(D_COPY_path, { })
svntest.actions.run_and_verify_merge(D_COPY_path, '6', '5',
@@ -9704,7 +9704,7 @@ def new_subtrees_should_not_break_merge(
'D/H/psi' : Item("This is the file 'psi'.\n"),
'D/H/omega' : Item("New content"),
'D/H/nu' : Item("New content",
- props={SVN_PROP_MERGEINFO : '/A/D/H/nu:7-8'}),
+ props={SVN_PROP_MERGEINFO : '/A/D/H/nu:8'}),
})
expected_skip = wc.State(A_COPY_path, { })
svntest.actions.run_and_verify_merge(A_COPY_path, '5', '6',
@@ -16157,10 +16157,6 @@ def no_self_referential_or_nonexistent_i
# Update the WC in preparation for merges.
svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
- # This test is marked as XFail because the following two merges
- # create mergeinfo with both non-existent path-revs and self-referential
- # mergeinfo.c
- #
# Merge all available revisions from A/C/nu to A_COPY/C/nu.
# The target has no explicit mergeinfo of its own but inherits mergeinfo
# from A_COPY. A_COPY has the mergeinfo '/A:2-9' so the naive mergeinfo
@@ -16414,7 +16410,7 @@ test_list = [ None,
merge_into_locally_added_file,
merge_into_locally_added_directory,
merge_with_os_deleted_subtrees,
- XFail(no_self_referential_or_nonexistent_inherited_mergeinfo),
+ no_self_referential_or_nonexistent_inherited_mergeinfo,
]
if __name__ == '__main__':