Hello!
I've attached a patch which adds a new option (--include-parents) to 'svnadmin
dump'. This option works with existing --include option and includes parent
nodes for nodes specified in --include option.
This should fix the mentioned case.
The option doesn't work with globbing for now, since I don't see a simple
way to implement this functionality for globbing. Thus the option is
explicitly forbidden if --pattern option is specified. The option is also
forbidden to use with --exclude option because it doesn't make sense.
Log message:
[[[
Add new option (--include-parents) to 'svnadmin dump': include parent
nodes for nodes specified in --include option.
* subversion/svnadmin/svnadmin.c
(svnadmin__cmdline_options_t): Add new member SVNADMIN__INCLUDE_PARENTS.
(options_table): Add an entry for new option (--include-parents).
(cmd_table): Add new options (--include-parents) for 'dump' subcommand.
(struct svnadmin_opt_state): Add member for new option (--include-parents).
(ary_prefix_match): Add new parameter INCLUDE_PARENTS. Return TRUE if
PATH is parent of any of specified prefixes and INCLUDE_PARENTS set
to true. Update comment.
(struct dump_filter_baton_t): Add new member INCLUDE_PARENTS.
(dump_filter_func): Pass B->INCLUDE_PARENTS to ary_prefix_match().
(subcommand_dump): Initialize INCLUDE_PARENTS member of FILTER_BATON.
Rework of filtering options validation code.
(sub_main): Handle new option (--include-parents).
* subversion/tests/cmdline/svnadmin_tests.py
(dump_invalid_filtering_option): Extend test with new cases. Update docstring.
(dump_include_parents): New test.
(test_list): Add new test to table.
Patch by: sergey.raevskiy{_AT_}visualsvn.com
]]]
On Mon, Mar 23, 2020 at 8:11 AM 钱海远(Nathan) <[email protected]> wrote:
>
> Yes, I think so. It's just for include. It's not a very good patch, I think ,
> sorry for this .
>
> And it has a known issue , it will include the parent node path , this is a
> problem. I made another program to delete parent node.
>
>
> Best Regards!
> Haiyuan Qian
> R & D Management Group
> Hangzhou Hikvision Digital Technology Co.,Ltd
> No.555 Qianmo Road, Binjiang District, Hangzhou 310052, China
> M (86)18969199712
>
> 本邮件及其附件含有海康威视公司的保密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件!
> This e-mail and its attachments contain confidential information from
> HIKVISION, which is intended only for the person or entity whose address is
> listed above. Any use of the information contained herein in any way
> (including, but not limited to, total or partial disclosure, reproduction, or
> dissemination) by persons other than the intended recipient(s) is
> prohibited. If you receive this e-mail in error, please notify the sender by
> phone or email immediately and delete it!
>
>
> -----邮件原件-----
> 发件人: Daniel Shahaf <[email protected]>
> 发送时间: 2020年3月22日 1:35
> 收件人: 钱海远(Nathan) <[email protected]>
> 抄送: [email protected]; [email protected]
> 主题: Re: Svnadmin dump with include can not dump the subdir into add when it's
> parent path was a branch
>
> [moving to dev@; please remove users@ from replies]
>
> 钱海远(Nathan) wrote on Sat, 21 Mar 2020 06:12 +0000:
> > I found the there is a BUG in subversion 1.10.6.
> >
> > Svnadmin dump with include can not dump the subdir into add when it's
> > parent path was a branch:
> > 1. 1、/A was copy from/XX , revision was 50, it the first revision of /A; 2.
> > 2、There is a subdir named /A/subdir, and several files and dir under
> > /A/subdir; 3. 3、Try to run “svnadmin dump /data/repos_root --include
> > /A/subdir >a” . The expected dump file will include /A/subdir(revision 50)
> > into add. But in fact there was nothing.
> > 4. 4、I was try to fix this bug in the svnadmin.c , a function named
> > ary_prefix_match. If the change list is the parent dir for include path, it
> > return true. Then the dump file will include /A/subdir and it's subdir or
> > subfile into add , but regrettably, the dump file will also include /A into
> > add.
>
> 钱海远(Nathan) wrote on Sat, 21 Mar 2020 06:12 +0000:
> > ---
> > C:/Users/QIANHA~1/AppData/Local/Temp/svnadmin.c-rev30397.svn001.tmp.c____
> > ____ 25 11:51:32 2020
> > +++
> > C:/Users/QIANHA~1/AppData/Local/Temp/svnadmin.c-rev30398.svn000.tmp.c____
> > ____ 21 12:58:58 2020
> > @@ -1297,3 +1297,3 @@ ary_prefix_match(const apr_array_header_t *pfxlist
> > - if (path_len < pfx_len)
> > - continue;
> > - if (strncmp(path, pfx, pfx_len) == 0
> > + /*if (path_len < pfx_len)
> > + continue;*/
> > + if ((strncmp(path, pfx, pfx_len) == 0
> > @@ -1300,0 +1301,3 @@ ary_prefix_match(const apr_array_header_t
> > *pfxlist
> > + || (strncmp(pfx,path, path_len) == 0
> > + && (path_len == 1 || path[path_len] == '\0' || path[path_len] ==
> > '/'))
> > + )
>
> Thanks for the patch. I don't see any obvious problems with this approach.
> However, the patch as it stands causes a regression:
>
> [[[
> W: /home/daniel/src/svn/t1/./subversion/libsvn_repos/load.c:667,
> W: /home/daniel/src/svn/t1/./subversion/libsvn_repos/load-fs-vtable.c:718,
> W: /home/daniel/src/svn/t1/./subversion/libsvn_repos/load-fs-vtable.c:591,
> W: /home/daniel/src/svn/t1/./subversion/libsvn_fs/fs-loader.c:1482,
> W: /home/daniel/src/svn/t1/./subversion/libsvn_fs_fs/tree.c:2524,
> W: /home/daniel/src/svn/t1/./subversion/libsvn_fs_fs/tree.c:1145:
> (apr_err=SVN_ERR_FS_NOT_FOUND)
> W: svnadmin: E160013: File not found: transaction '0-0', path '/A/B/F'
> W: CWD: /tmp/svn/subversion/tests/cmdline
> W: EXCEPTION: Failure: Command failed: "/tmp/svn/subversion/svnadmin/svnadmin
> load --quiet svn-test-work/repositories/svnadmin_tests-60-1"; exit code 1
> Traceback (most recent call last):
> File "/home/daniel/src/svn/t1/subversion/tests/cmdline/svntest/main.py",
> line 1931, in run
> rc = self.pred.run(sandbox)
> File
> "/home/daniel/src/svn/t1/subversion/tests/cmdline/svntest/testcase.py", line
> 178, in run
> result = self.func(sandbox)
> File "/home/daniel/src/svn/t1/subversion/tests/cmdline/svnadmin_tests.py",
> line 3510, in dump_exclude
> load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
> File "/home/daniel/src/svn/t1/subversion/tests/cmdline/svnadmin_tests.py",
> line 304, in load_and_verify_dumpstream
> 'load', '--quiet', sbox.repo_dir, *varargs)
> File "/home/daniel/src/svn/t1/subversion/tests/cmdline/svntest/main.py",
> line 666, in run_command_stdin
> '"; exit code ' + str(exit_code))
> svntest.Failure: Command failed: "/tmp/svn/subversion/svnadmin/svnadmin load
> --quiet svn-test-work/repositories/svnadmin_tests-60-1"; exit code 1
> FAIL: svnadmin_tests.py 60: svnadmin dump with excluded paths ]]]
>
> So, I guess the new condition should be applied to --include's but not to
> --exclude's?
>
> Cheers,
>
> Daniel
>
> ________________________________
> CONFIDENTIALITY NOTICE: This electronic message is intended to be viewed only
> by the individual or entity to whom it is addressed. It may contain
> information that is privileged, confidential and exempt from disclosure under
> applicable law. Any dissemination, distribution or copying of this
> communication is strictly prohibited without our prior permission. If the
> reader of this message is not the intended recipient, or the employee or
> agent responsible for delivering the message to the intended recipient, or if
> you have received this communication in error, please notify us immediately
> by return e-mail and delete the original message and any copies of it from
> your computer system. For further information about Hikvision company. please
> see our website at www.hikvision.com<http://www.hikvision.com>
>
Index: subversion/svnadmin/svnadmin.c
===================================================================
--- subversion/svnadmin/svnadmin.c (revision 1875560)
+++ subversion/svnadmin/svnadmin.c (working copy)
@@ -158,7 +158,8 @@ enum svnadmin__cmdline_options_t
svnadmin__normalize_props,
svnadmin__exclude,
svnadmin__include,
- svnadmin__glob
+ svnadmin__glob,
+ svnadmin__include_parents
};
/* Option codes and descriptions.
@@ -297,6 +298,9 @@ static const apr_getopt_option_t options_table[] =
" Character '/' is not treated specially,
so\n"
" pattern /*/foo matches paths /a/foo and
/a/b/foo.") },
+ {"include-parents", svnadmin__include_parents, 0,
+ N_("include parent nodes for nodes specified with --include option")},
+
{NULL}
};
@@ -372,7 +376,7 @@ static const svn_opt_subcommand_desc3_t cmd_table[
"excluded, the copy is transformed into an add (unlike in
'svndumpfilter').\n"
)},
{'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M', 'F',
- svnadmin__exclude, svnadmin__include, svnadmin__glob },
+ svnadmin__exclude, svnadmin__include, svnadmin__glob,
svnadmin__include_parents },
{{'F', N_("write to file ARG instead of stdout")}} },
{"dump-revprops", subcommand_dump_revprops, {0}, {N_(
@@ -662,6 +666,7 @@ struct svnadmin_opt_state
apr_array_header_t *exclude; /* --exclude */
apr_array_header_t *include; /* --include */
svn_boolean_t glob; /* --pattern */
+ svn_boolean_t include_parents; /* --include-parents */
const char *config_dir; /* Overriding Configuration Directory */
};
@@ -1361,10 +1366,13 @@ get_dump_range(svn_revnum_t *lower,
/* Compare the node-path PATH with the (const char *) prefixes in PFXLIST.
* Return TRUE if any prefix is a prefix of PATH (matching whole path
* components); FALSE otherwise.
+ * If INCLUDE_PARENTS is set to TRUE, return TRUE if PATH is parent of
+ * any of specified prefixes.
* PATH starts with a '/', as do the (const char *) paths in PREFIXES. */
-/* This function is a duplicate of svndumpfilter.c:ary_prefix_match(). */
+/* This function is an extended version of svndumpfilter.c:ary_prefix_match().
*/
static svn_boolean_t
-ary_prefix_match(const apr_array_header_t *pfxlist, const char *path)
+ary_prefix_match(const apr_array_header_t *pfxlist, const char *path,
+ svn_boolean_t include_parents)
{
int i;
size_t path_len = strlen(path);
@@ -1374,11 +1382,18 @@ static svn_boolean_t
const char *pfx = APR_ARRAY_IDX(pfxlist, i, const char *);
size_t pfx_len = strlen(pfx);
- if (path_len < pfx_len)
- continue;
- if (strncmp(path, pfx, pfx_len) == 0
- && (pfx_len == 1 || path[pfx_len] == '\0' || path[pfx_len] == '/'))
- return TRUE;
+ if (path_len < pfx_len && include_parents)
+ {
+ if (strncmp(pfx, path, path_len) == 0
+ && (path_len == 1 || pfx[path_len] == '/'))
+ return TRUE;
+ }
+ else if (path_len >= pfx_len)
+ {
+ if (strncmp(path, pfx, pfx_len) == 0
+ && (pfx_len == 1 || path[pfx_len] == '\0' || path[pfx_len] ==
'/'))
+ return TRUE;
+ }
}
return FALSE;
@@ -1390,6 +1405,7 @@ struct dump_filter_baton_t
apr_array_header_t *prefixes;
svn_boolean_t glob;
svn_boolean_t do_exclude;
+ svn_boolean_t include_parents;
};
/* Implements svn_repos_dump_filter_func_t. */
@@ -1404,7 +1420,7 @@ dump_filter_func(svn_boolean_t *include,
const svn_boolean_t matches =
(b->glob
? svn_cstring_match_glob_list(path, b->prefixes)
- : ary_prefix_match(b->prefixes, path));
+ : ary_prefix_match(b->prefixes, path, b->include_parents));
*include = b->do_exclude ? !matches : matches;
return SVN_NO_ERROR;
@@ -1445,25 +1461,42 @@ subcommand_dump(apr_getopt_t *os, void *baton, apr
if (! opt_state->quiet)
feedback_stream = recode_stream_create(stderr, pool);
+ /* Validate filtering options. */
+ if (opt_state->exclude && opt_state->include)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'--exclude' and '--include' options "
+ "cannot be used simultaneously"));
+ }
+
+ if (opt_state->glob && opt_state->include_parents)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'--pattern' and '--include-parents' options "
+ "cannot be used simultaneously"));
+ }
+
+ if (opt_state->exclude && opt_state->include_parents)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'--exclude' and '--include-parents' options "
+ "cannot be used simultaneously"));
+ }
+
/* Initialize the filter baton. */
filter_baton.glob = opt_state->glob;
+ filter_baton.include_parents = opt_state->include_parents;
- if (opt_state->exclude && !opt_state->include)
+ if (opt_state->exclude)
{
filter_baton.prefixes = opt_state->exclude;
filter_baton.do_exclude = TRUE;
}
- else if (opt_state->include && !opt_state->exclude)
+ else if (opt_state->include)
{
filter_baton.prefixes = opt_state->include;
filter_baton.do_exclude = FALSE;
}
- else if (opt_state->include && opt_state->exclude)
- {
- return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("'--exclude' and '--include' options "
- "cannot be used simultaneously"));
- }
SVN_ERR(svn_repos_dump_fs4(repos, out_stream, lower, upper,
opt_state->incremental, opt_state->use_deltas,
@@ -3178,6 +3211,9 @@ sub_main(int *exit_code, int argc, const char *arg
case svnadmin__glob:
opt_state.glob = TRUE;
break;
+ case svnadmin__include_parents:
+ opt_state.include_parents = TRUE;
+ break;
default:
{
SVN_ERR(subcommand_help(NULL, NULL, pool));
Index: subversion/tests/cmdline/svnadmin_tests.py
===================================================================
--- subversion/tests/cmdline/svnadmin_tests.py (revision 1875560)
+++ subversion/tests/cmdline/svnadmin_tests.py (working copy)
@@ -3808,7 +3808,7 @@ def dump_exclude_all_rev_changes(sbox):
'log', '-v', sbox2.repo_url)
def dump_invalid_filtering_option(sbox):
- "dump with --include and --exclude simultaneously"
+ "dump with invalid filtering options"
sbox.build(create_wc=False, empty=False)
@@ -3822,6 +3822,27 @@ def dump_invalid_filtering_option(sbox):
'--include', '/A/B/E',
sbox.repo_dir)
+ # Attempt to dump repository with '--include-parents' and '--exclude' options
+ # specified simultaneously.
+ expected_error = ".*: '--exclude' and '--include-parents' options cannot be
used " \
+ "simultaneously"
+ svntest.actions.run_and_verify_svnadmin(None, expected_error,
+ 'dump', '-q',
+ '--exclude', '/A/D/H',
+ '--include-parents',
+ sbox.repo_dir)
+
+ # Attempt to dump repository with '--include-parents' and '--pattern' options
+ # specified simultaneously.
+ expected_error = ".*: '--pattern' and '--include-parents' options cannot be
used " \
+ "simultaneously"
+ svntest.actions.run_and_verify_svnadmin(None, expected_error,
+ 'dump', '-q',
+ '--include', '/A/D/H',
+ '--pattern',
+ '--include-parents',
+ sbox.repo_dir)
+
@Issue(4725)
def load_issue4725(sbox):
"""load that triggers issue 4725"""
@@ -4036,7 +4057,62 @@ PROPS-END
if output != ['\n', '\n']:
raise svntest.Failure("Unexpected property value %s" % output)
+def dump_include_parents(sbox):
+ "svnadmin dump --include with parents"
+ sbox.build(create_wc=False, empty=True)
+
+ # Create a couple of directories.
+ svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
+ sbox.repo_url + '/project/trunk',
+ sbox.repo_url + '/project/branches',
+ sbox.repo_url + '/project/tags',
+ "-m", "Create repository structure.",
+ "--parents")
+
+ # Make some changes in trunk.
+ svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
+ sbox.repo_url + '/project/trunk/dir',
+ "-m", "Add directory.")
+
+ # Create branch 'b1'.
+ svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "copy",
+ sbox.repo_url + '/project/trunk',
+ sbox.repo_url + '/project/branches/b1',
+ "-m", "Create branch.")
+
+ # Dump with only 'b1' included.
+ _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
+ 'dump', '-q',
+ '--include',
'/project/branches/b1',
+ '--include-parents',
+ sbox.repo_dir)
+
+ # Load repository from dump.
+ sbox2 = sbox.clone_dependent()
+ sbox2.build(create_wc=False, empty=True)
+ load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
+
+ # Check log.
+ expected_output = svntest.verify.RegexListOutput([
+ '-+\\n',
+ 'r3\ .*\n',
+ re.escape('Changed paths:'),
+ re.escape(' A /project/branches/b1'),
+ re.escape(' A /project/branches/b1/dir'),
+ '-+\\n',
+ 'r2\ .*\n',
+ '-+\\n',
+ 'r1\ .*\n',
+ re.escape('Changed paths:'),
+ re.escape(' A /project'),
+ re.escape(' A /project/branches'),
+ '-+\\n'
+ ])
+ svntest.actions.run_and_verify_svn(expected_output, [],
+ 'log', '-v', '-q', sbox2.repo_url)
+
+
########################################################################
# Run the tests
@@ -4116,6 +4192,7 @@ test_list = [ None,
recover_prunes_rep_cache_when_disabled,
dump_include_copied_directory,
load_normalize_node_props,
+ dump_include_parents
]
if __name__ == '__main__':