2 * svnrdump.c: Produce a dumpfile of a local or remote repository
3 * without touching the filesystem, but for temporary files.
5 * ====================================================================
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance
12 * with the License. You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
22 * ====================================================================
25 #include "svn_pools.h"
26 #include "svn_cmdline.h"
27 #include "svn_client.h"
30 #include "svn_repos.h"
33 #include "svn_string.h"
34 #include "svn_props.h"
35 #include "svn_dirent_uri.h"
37 #include "svn17_compat.h"
38 #include "dump_editor.h"
39 #include "load_editor.h"
41 static svn_opt_subcommand_t dump_cmd
, load_cmd
;
43 enum svn_svnrdump__longopt_t
45 opt_config_dir
= SVN_OPT_FIRST_LONGOPT_ID
,
54 static const svn_opt_subcommand_desc2_t svnrdump__cmd_table
[] =
56 { "dump", dump_cmd
, { 0 },
57 N_("usage: svnrdump dump URL [-r LOWER[:UPPER]]\n\n"
58 "Dump revisions LOWER to UPPER of repository at remote URL "
59 "to stdout in a 'dumpfile' portable format.\n"
60 "If only LOWER is given, dump that one revision.\n"),
62 { "load", load_cmd
, { 0 },
63 N_("usage: svnrdump load URL\n\n"
64 "Load a 'dumpfile' given on stdin to a repository "
67 { "help", 0, { "?", "h" },
68 N_("usage: svnrdump help [SUBCOMMAND...]\n\n"
69 "Describe the usage of this program or its subcommands.\n"),
71 { NULL
, NULL
, { 0 }, NULL
, { 0 } }
74 static const apr_getopt_option_t svnrdump__options
[] =
76 {"revision", 'r', 1, N_("specify revision number ARG (or X:Y range)")},
77 {"quiet", 'q', 0, N_("no progress (only errors) to stderr")},
78 {"config-dir", opt_config_dir
, 1, N_("read user configuration files from"
80 {"username", opt_auth_username
, 1, N_("specify a username ARG")},
81 {"password", opt_auth_password
, 1, N_("specify a password ARG")},
82 {"non-interactive", opt_non_interactive
, 0, N_("do no interactive"
84 {"no-auth-cache", opt_auth_nocache
, 0, N_("do not cache authentication"
87 {"help", 'h', 0, N_("display this help")},
88 {"version", opt_version
, 0, N_("show program version information")},
89 {"config-option", opt_config_option
, 1,
90 N_("set user configuration option in the format:\n"
92 " FILE:SECTION:OPTION=[VALUE]\n"
96 " servers:global:http-library=serf")},
100 /* Baton for the RA replay session. */
101 struct replay_baton
{
102 /* The editor producing diffs. */
103 const svn_delta_editor_t
*editor
;
105 /* Baton for the editor. */
108 /* Whether to be quiet. */
113 svn_ra_session_t
*session
;
115 svn_revnum_t start_revision
;
116 svn_revnum_t end_revision
;
121 replay_revstart(svn_revnum_t revision
,
123 const svn_delta_editor_t
**editor
,
125 apr_hash_t
*rev_props
,
128 struct replay_baton
*rb
= replay_baton
;
129 svn_stringbuf_t
*propstring
;
130 svn_stream_t
*stdout_stream
;
131 svn_stream_t
*revprop_stream
;
133 svn_stream_for_stdout(&stdout_stream
, pool
);
135 /* Revision-number: 19 */
136 SVN_ERR(svn_stream_printf(stdout_stream
, pool
,
137 SVN_REPOS_DUMPFILE_REVISION_NUMBER
138 ": %ld\n", revision
));
139 SVN_ERR(normalize_props(rev_props
, pool
));
140 propstring
= svn_stringbuf_create_ensure(0, pool
);
141 revprop_stream
= svn_stream_from_stringbuf(propstring
, pool
);
142 SVN_ERR(svn_hash_write2(rev_props
, revprop_stream
, "PROPS-END", pool
));
143 SVN_ERR(svn_stream_close(revprop_stream
));
145 /* Prop-content-length: 13 */
146 SVN_ERR(svn_stream_printf(stdout_stream
, pool
,
147 SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
148 ": %" APR_SIZE_T_FMT
"\n", propstring
->len
));
150 /* Content-length: 29 */
151 SVN_ERR(svn_stream_printf(stdout_stream
, pool
,
152 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
153 ": %" APR_SIZE_T_FMT
"\n\n", propstring
->len
));
156 SVN_ERR(svn_stream_write(stdout_stream
, propstring
->data
,
157 &(propstring
->len
)));
159 SVN_ERR(svn_stream_printf(stdout_stream
, pool
, "\n"));
160 SVN_ERR(svn_stream_close(stdout_stream
));
162 /* Extract editor and editor_baton from the replay_baton and
163 set them so that the editor callbacks can use them. */
164 *editor
= rb
->editor
;
165 *edit_baton
= rb
->edit_baton
;
171 replay_revend(svn_revnum_t revision
,
173 const svn_delta_editor_t
*editor
,
175 apr_hash_t
*rev_props
,
178 /* No resources left to free. */
179 struct replay_baton
*rb
= replay_baton
;
181 svn_cmdline_fprintf(stderr
, pool
, "* Dumped revision %lu.\n", revision
);
185 /* Return in *SESSION a new RA session to URL.
186 * Allocate *SESSION and related data structures in POOL.
187 * Use CONFIG_DIR and pass USERNAME, PASSWORD, CONFIG_DIR and
188 * NO_AUTH_CACHE to initialize the authorization baton.
189 * CONFIG_OPTIONS (if not NULL) is a list of configuration overrides. */
191 open_connection(svn_ra_session_t
**session
,
193 svn_boolean_t non_interactive
,
194 const char *username
,
195 const char *password
,
196 const char *config_dir
,
197 svn_boolean_t no_auth_cache
,
198 apr_array_header_t
*config_options
,
201 svn_client_ctx_t
*ctx
= NULL
;
202 svn_config_t
*cfg_config
;
204 SVN_ERR(svn_ra_initialize(pool
));
206 SVN_ERR(svn_config_ensure(config_dir
, pool
));
207 SVN_ERR(svn_client_create_context(&ctx
, pool
));
209 SVN_ERR(svn_config_get_config(&(ctx
->config
), config_dir
, pool
));
212 SVN_ERR(svn_cmdline__apply_config_options(ctx
->config
, config_options
,
213 "svnrdump: ", "--config-option"));
215 cfg_config
= apr_hash_get(ctx
->config
, SVN_CONFIG_CATEGORY_CONFIG
,
216 APR_HASH_KEY_STRING
);
218 /* Default authentication providers for non-interactive use */
219 SVN_ERR(svn_cmdline_create_auth_baton(&(ctx
->auth_baton
), non_interactive
,
220 username
, password
, config_dir
,
221 no_auth_cache
, FALSE
, cfg_config
,
222 ctx
->cancel_func
, ctx
->cancel_baton
,
224 SVN_ERR(svn_client_open_ra_session(session
, url
, ctx
, pool
));
229 replay_revisions(svn_ra_session_t
*session
, const char *url
,
230 svn_revnum_t start_revision
, svn_revnum_t end_revision
,
231 svn_boolean_t quiet
, apr_pool_t
*pool
)
233 const svn_delta_editor_t
*dump_editor
;
234 struct replay_baton
*replay_baton
;
237 svn_stream_t
*stdout_stream
;
239 SVN_ERR(svn_stream_for_stdout(&stdout_stream
, pool
));
241 SVN_ERR(get_dump_editor(&dump_editor
, &dump_baton
, stdout_stream
, pool
));
243 replay_baton
= apr_pcalloc(pool
, sizeof(*replay_baton
));
244 replay_baton
->editor
= dump_editor
;
245 replay_baton
->edit_baton
= dump_baton
;
246 replay_baton
->quiet
= quiet
;
248 /* Write the magic header and UUID */
249 SVN_ERR(svn_stream_printf(stdout_stream
, pool
,
250 SVN_REPOS_DUMPFILE_MAGIC_HEADER
": %d\n\n",
251 SVN_REPOS_DUMPFILE_FORMAT_VERSION
));
252 SVN_ERR(svn_ra_get_uuid2(session
, &uuid
, pool
));
253 SVN_ERR(svn_stream_printf(stdout_stream
, pool
,
254 SVN_REPOS_DUMPFILE_UUID
": %s\n\n", uuid
));
256 /* Fake revision 0 if necessary */
257 if (start_revision
== 0)
259 apr_hash_t
*prophash
;
260 svn_stringbuf_t
*propstring
;
261 svn_stream_t
*propstream
;
262 SVN_ERR(svn_stream_printf(stdout_stream
, pool
,
263 SVN_REPOS_DUMPFILE_REVISION_NUMBER
264 ": %ld\n", start_revision
));
266 prophash
= apr_hash_make(pool
);
267 propstring
= svn_stringbuf_create("", pool
);
269 SVN_ERR(svn_ra_rev_proplist(session
, start_revision
,
272 propstream
= svn_stream_from_stringbuf(propstring
, pool
);
273 SVN_ERR(svn_hash_write2(prophash
, propstream
, "PROPS-END", pool
));
274 SVN_ERR(svn_stream_close(propstream
));
276 /* Property-content-length: 14; Content-length: 14 */
277 SVN_ERR(svn_stream_printf(stdout_stream
, pool
,
278 SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
279 ": %" APR_SIZE_T_FMT
"\n",
281 SVN_ERR(svn_stream_printf(stdout_stream
, pool
,
282 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
283 ": %" APR_SIZE_T_FMT
"\n\n",
286 SVN_ERR(svn_stream_write(stdout_stream
, propstring
->data
,
287 &(propstring
->len
)));
288 SVN_ERR(svn_stream_printf(stdout_stream
, pool
, "\n"));
290 svn_cmdline_fprintf(stderr
, pool
, "* Dumped revision %lu.\n", start_revision
);
295 SVN_ERR(svn_ra_replay_range(session
, start_revision
, end_revision
,
296 0, TRUE
, replay_revstart
, replay_revend
,
297 replay_baton
, pool
));
298 SVN_ERR(svn_stream_close(stdout_stream
));
303 load_revisions(svn_ra_session_t
*session
, const char *url
,
304 svn_boolean_t quiet
, apr_pool_t
*pool
)
306 apr_file_t
*stdin_file
;
307 svn_stream_t
*stdin_stream
;
308 const svn_repos_parse_fns2_t
*parser
;
311 apr_file_open_stdin(&stdin_file
, pool
);
312 stdin_stream
= svn_stream_from_aprfile2(stdin_file
, FALSE
, pool
);
314 SVN_ERR(get_dumpstream_loader(&parser
, &parse_baton
, session
, pool
));
315 SVN_ERR(drive_dumpstream_loader(stdin_stream
, parser
, parse_baton
, session
, pool
));
317 svn_stream_close(stdin_stream
);
324 ensure_appname(const char *progname
, apr_pool_t
*pool
)
329 progname
= svn_dirent_internal_style(progname
, pool
);
330 return svn_dirent_basename(progname
, NULL
);
334 usage(const char *progname
, apr_pool_t
*pool
)
336 progname
= ensure_appname(progname
, pool
);
338 SVN_ERR(svn_cmdline_fprintf(stderr
, pool
,
339 _("Type '%s help' for usage.\n"),
345 version(const char *progname
, apr_pool_t
*pool
)
347 const char *ra_desc_start
348 = _("The following repository access (RA) modules are available:\n\n");
350 svn_stringbuf_t
*version_footer
= svn_stringbuf_create(ra_desc_start
,
353 SVN_ERR(svn_ra_print_modules(version_footer
, pool
));
355 progname
= ensure_appname(progname
, pool
);
357 return svn_opt_print_help3(NULL
, progname
, TRUE
, FALSE
,
358 version_footer
->data
, NULL
, NULL
,
359 NULL
, NULL
, NULL
, pool
);
363 /** A statement macro, similar to @c SVN_ERR, but returns an integer.
365 * Evaluate @a expr. If it yields an error, handle that error and
366 * return @c EXIT_FAILURE.
368 #define SVNRDUMP_ERR(expr) \
371 svn_error_t *svn_err__temp = (expr); \
374 svn_handle_error2(svn_err__temp, stderr, FALSE, "svnrdump: "); \
375 svn_error_clear(svn_err__temp); \
376 return EXIT_FAILURE; \
382 dump_cmd(apr_getopt_t
*os
, void *baton
, apr_pool_t
*pool
)
384 opt_baton_t
*opt_baton
= baton
;
385 SVN_ERR(replay_revisions(opt_baton
->session
, opt_baton
->url
,
386 opt_baton
->start_revision
, opt_baton
->end_revision
,
387 opt_baton
->quiet
, pool
));
392 load_cmd(apr_getopt_t
*os
, void *baton
, apr_pool_t
*pool
)
394 opt_baton_t
*opt_baton
= baton
;
395 SVN_ERR(load_revisions(opt_baton
->session
, opt_baton
->url
,
396 opt_baton
->quiet
, pool
));
401 help_cmd(apr_getopt_t
*os
, void *baton
, apr_pool_t
*pool
)
404 _("general usage: svnrdump SUBCOMMAND URL [-r LOWER[:UPPER]]\n"
405 "Type 'svnrdump help <subcommand>' for help on a specific subcommand.\n\n"
406 "Available subcommands:\n");
408 SVN_ERR(svn_opt_print_help3(os
, "svnrdump", FALSE
, FALSE
, NULL
, header
,
409 svnrdump__cmd_table
, svnrdump__options
, NULL
,
416 main(int argc
, const char **argv
)
418 const svn_opt_subcommand_desc2_t
*subcommand
= NULL
;
419 opt_baton_t
*opt_baton
;
420 char *revision_cut
= NULL
;
421 svn_revnum_t latest_revision
= svn_opt_revision_unspecified
;
422 apr_pool_t
*pool
= NULL
;
423 const char *config_dir
= NULL
;
424 const char *username
= NULL
;
425 const char *password
= NULL
;
426 svn_boolean_t no_auth_cache
= FALSE
;
427 svn_boolean_t non_interactive
= FALSE
;
428 apr_array_header_t
*config_options
= NULL
;
430 const char *first_arg
;
432 if (svn_cmdline_init ("svnrdump", stderr
) != EXIT_SUCCESS
)
435 pool
= svn_pool_create(NULL
);
436 opt_baton
= apr_pcalloc(pool
, sizeof(*opt_baton
));
437 opt_baton
->start_revision
= svn_opt_revision_unspecified
;
438 opt_baton
->end_revision
= svn_opt_revision_unspecified
;
439 opt_baton
->url
= NULL
;
441 SVNRDUMP_ERR(svn_cmdline__getopt_init(&os
, argc
, argv
, pool
));
443 os
->interleave
= TRUE
; /* Options and arguments can be interleaved */
449 apr_status_t status
= apr_getopt_long(os
, svnrdump__options
, &opt
,
452 if (APR_STATUS_IS_EOF(status
))
454 if (status
!= APR_SUCCESS
)
456 SVNRDUMP_ERR(usage(argv
[0], pool
));
464 revision_cut
= strchr(opt_arg
, ':');
467 opt_baton
->start_revision
= (svn_revnum_t
)strtoul(opt_arg
,
469 opt_baton
->end_revision
= (svn_revnum_t
)strtoul(revision_cut
+ 1,
474 opt_baton
->start_revision
= (svn_revnum_t
)strtoul(opt_arg
, NULL
, 10);
475 opt_baton
->end_revision
= opt_baton
->start_revision
;
480 opt_baton
->quiet
= TRUE
;
483 config_dir
= opt_arg
;
486 SVNRDUMP_ERR(version(argv
[0], pool
));
490 SVNRDUMP_ERR(help_cmd(os
, opt_baton
, pool
));
493 case opt_auth_username
:
494 SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&username
, opt_arg
, pool
));
496 case opt_auth_password
:
497 SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&password
, opt_arg
, pool
));
499 case opt_auth_nocache
:
500 no_auth_cache
= TRUE
;
502 case opt_non_interactive
:
503 non_interactive
= TRUE
;
505 case opt_config_option
:
508 apr_array_make(pool
, 1,
509 sizeof(svn_cmdline__config_argument_t
*));
511 SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&opt_arg
, opt_arg
, pool
));
512 SVNRDUMP_ERR(svn_cmdline__parse_config_option(config_options
,
517 if (os
->ind
>= os
->argc
)
520 (svn_cmdline_fprintf(stderr
, pool
,
521 _("Subcommand argument required\n")));
522 SVNRDUMP_ERR(help_cmd(NULL
, NULL
, pool
));
523 svn_pool_destroy(pool
);
527 first_arg
= os
->argv
[os
->ind
++];
529 subcommand
= svn_opt_get_canonical_subcommand2(svnrdump__cmd_table
,
532 if (subcommand
== NULL
)
534 const char *first_arg_utf8
;
535 svn_error_t
*err
= svn_utf_cstring_to_utf8(&first_arg_utf8
,
538 return svn_cmdline_handle_exit_error(err
, pool
, "svnrdump: ");
539 svn_error_clear(svn_cmdline_fprintf(stderr
, pool
,
540 _("Unknown command: '%s'\n"),
542 SVNRDUMP_ERR(help_cmd(NULL
, NULL
, pool
));
543 svn_pool_destroy(pool
);
547 if (subcommand
&& strcmp(subcommand
->name
, "help") == 0)
549 SVNRDUMP_ERR(help_cmd(os
, opt_baton
, pool
));
553 /* Only continue if the only not option argument is a url */
554 if ((os
->ind
!= os
->argc
-1)
555 || !svn_path_is_url(os
->argv
[os
->ind
]))
557 SVNRDUMP_ERR(usage(argv
[0], pool
));
561 SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&(opt_baton
->url
), os
->argv
[os
->ind
], pool
));
563 opt_baton
->url
= svn_uri_canonicalize(os
->argv
[os
->ind
], pool
);
565 SVNRDUMP_ERR(open_connection(&(opt_baton
->session
),
575 /* Have sane opt_baton->start_revision and end_revision defaults if unspecified */
576 SVNRDUMP_ERR(svn_ra_get_latest_revnum(opt_baton
->session
, &latest_revision
, pool
));
577 if (opt_baton
->start_revision
== svn_opt_revision_unspecified
)
578 opt_baton
->start_revision
= 0;
579 if (opt_baton
->end_revision
== svn_opt_revision_unspecified
)
580 opt_baton
->end_revision
= latest_revision
;
581 if (opt_baton
->end_revision
> latest_revision
)
583 SVN_INT_ERR(svn_cmdline_fprintf(stderr
, pool
,
584 _("Revision %ld does not exist.\n"),
585 opt_baton
->end_revision
));
588 if (opt_baton
->end_revision
< opt_baton
->start_revision
)
590 SVN_INT_ERR(svn_cmdline_fprintf(stderr
, pool
,
591 _("LOWER cannot be greater "
596 /* Dispatch the subcommand */
597 SVNRDUMP_ERR((*subcommand
->cmd_func
)(os
, opt_baton
, pool
));
599 svn_pool_destroy(pool
);