Followup to r29625: fix getopt tests.
[svn.git] / subversion / svn / main.c
blob026cb905cfee29833d3aafdef35bf5d7d01c04fe
1 /*
2 * main.c: Subversion command line client.
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes. ***/
25 #include <string.h>
26 #include <assert.h>
28 #include <apr_strings.h>
29 #include <apr_tables.h>
30 #include <apr_general.h>
31 #include <apr_signal.h>
33 #include "svn_cmdline.h"
34 #include "svn_pools.h"
35 #include "svn_wc.h"
36 #include "svn_client.h"
37 #include "svn_config.h"
38 #include "svn_string.h"
39 #include "svn_path.h"
40 #include "svn_delta.h"
41 #include "svn_diff.h"
42 #include "svn_error.h"
43 #include "svn_io.h"
44 #include "svn_opt.h"
45 #include "svn_utf.h"
46 #include "svn_auth.h"
47 #include "svn_hash.h"
48 #include "cl.h"
50 #include "svn_private_config.h"
53 /*** Option Processing ***/
55 /* Add an identifier here for long options that don't have a short
56 option. Options that have both long and short options should just
57 use the short option letter as identifier. */
58 typedef enum {
59 opt_ancestor_path = SVN_OPT_FIRST_LONGOPT_ID,
60 opt_auth_password,
61 opt_auth_username,
62 opt_autoprops,
63 opt_changelist,
64 opt_config_dir,
65 opt_diff_cmd,
66 opt_dry_run,
67 opt_editor_cmd,
68 opt_encoding,
69 opt_force_log,
70 opt_force,
71 opt_keep_changelists,
72 opt_ignore_ancestry,
73 opt_ignore_externals,
74 opt_incremental,
75 opt_merge_cmd,
76 opt_native_eol,
77 opt_new_cmd,
78 opt_no_auth_cache,
79 opt_no_autoprops,
80 opt_no_diff_deleted,
81 opt_no_ignore,
82 opt_no_unlock,
83 opt_non_interactive,
84 opt_notice_ancestry,
85 opt_old_cmd,
86 opt_record_only,
87 opt_relocate,
88 opt_remove,
89 opt_revprop,
90 opt_stop_on_copy,
91 opt_strict,
92 opt_summarize,
93 opt_targets,
94 opt_depth,
95 opt_set_depth,
96 opt_version,
97 opt_xml,
98 opt_keep_local,
99 opt_with_revprop,
100 opt_with_all_revprops,
101 opt_parents,
102 opt_accept,
103 opt_from_source,
104 opt_reintegrate
105 } svn_cl__longopt_t;
107 /* Option codes and descriptions for the command line client.
109 * The entire list must be terminated with an entry of nulls.
111 const apr_getopt_option_t svn_cl__options[] =
113 {"force", opt_force, 0, N_("force operation to run")},
114 {"force-log", opt_force_log, 0,
115 N_("force validity of log message source")},
116 {"help", 'h', 0, N_("show help on a subcommand")},
117 {NULL, '?', 0, N_("show help on a subcommand")},
118 {"message", 'm', 1, N_("specify log message ARG")},
119 {"quiet", 'q', 0, N_("print nothing, or only summary information")},
120 {"recursive", 'R', 0, N_("descend recursively, same as --depth=infinity")},
121 {"non-recursive", 'N', 0, N_("obsolete; try --depth=files or --depth=immediates")},
122 {"change", 'c', 1, N_
123 ("the change made by revision ARG (like -r ARG-1:ARG)\n"
124 " If ARG is negative this is like -r ARG:ARG-1")
126 {"revision", 'r', 1, N_
127 ("ARG (some commands also take ARG1:ARG2 range)\n"
128 " A revision argument can be one of:\n"
129 " NUMBER revision number\n"
130 " '{' DATE '}' revision at start of the date\n"
131 " 'HEAD' latest in repository\n"
132 " 'BASE' base rev of item's working copy\n"
133 " 'COMMITTED' last commit at or before BASE\n"
134 " 'PREV' revision just before COMMITTED")
135 /* spacing corresponds to svn_opt_format_option */
137 {"file", 'F', 1, N_("read log message from file ARG")},
138 {"incremental", opt_incremental, 0,
139 N_("give output suitable for concatenation")},
140 #ifndef AS400
141 {"encoding", opt_encoding, 1,
142 N_("treat value as being in charset encoding ARG")},
143 #endif
144 {"version", opt_version, 0, N_("show program version information")},
145 {"verbose", 'v', 0, N_("print extra information")},
146 {"show-updates", 'u', 0, N_("display update information")},
147 {"username", opt_auth_username, 1, N_("specify a username ARG")},
148 {"password", opt_auth_password, 1, N_("specify a password ARG")},
149 #ifndef AS400
150 {"extensions", 'x', 1,
151 N_("Default: '-u'. When Subversion is invoking an\n"
153 " external diff program, ARG is simply passed along\n"
155 " to the program. But when Subversion is using its\n"
157 " default internal diff implementation, or when\n"
159 " Subversion is displaying blame annotations, ARG\n"
161 " could be any of the following:\n"
163 " -u (--unified):\n"
165 " Output 3 lines of unified context.\n"
167 " -b (--ignore-space-change):\n"
169 " Ignore changes in the amount of white space.\n"
171 " -w (--ignore-all-space):\n"
173 " Ignore all white space.\n"
175 " --ignore-eol-style:\n"
177 " Ignore changes in EOL style\n"
179 " -p (--show-c-function):\n"
181 " Show C function name in diff output.")},
182 #endif
183 {"targets", opt_targets, 1,
184 N_("pass contents of file ARG as additional args")},
185 {"depth", opt_depth, 1,
186 N_("limit operation by depth ARG ('empty', 'files',\n"
188 "'immediates', or 'infinity')")},
189 {"set-depth", opt_set_depth, 1,
190 N_("set new working copy depth to ARG ('empty',\n"
192 "'files', 'immediates', or 'infinity')")},
193 {"xml", opt_xml, 0, N_("output in XML")},
194 {"strict", opt_strict, 0, N_("use strict semantics")},
195 {"stop-on-copy", opt_stop_on_copy, 0,
196 N_("do not cross copies while traversing history")},
197 {"no-ignore", opt_no_ignore, 0,
198 N_("disregard default and svn:ignore property ignores")},
199 {"no-auth-cache", opt_no_auth_cache, 0,
200 N_("do not cache authentication tokens")},
201 {"non-interactive", opt_non_interactive, 0,
202 N_("do no interactive prompting")},
203 {"dry-run", opt_dry_run, 0,
204 N_("try operation but make no changes")},
205 {"no-diff-deleted", opt_no_diff_deleted, 0,
206 N_("do not print differences for deleted files")},
207 {"notice-ancestry", opt_notice_ancestry, 0,
208 N_("notice ancestry when calculating differences")},
209 {"ignore-ancestry", opt_ignore_ancestry, 0,
210 N_("ignore ancestry when calculating merges")},
211 {"ignore-externals", opt_ignore_externals, 0,
212 N_("ignore externals definitions")},
213 #ifndef AS400
214 {"diff-cmd", opt_diff_cmd, 1, N_("use ARG as diff command")},
215 {"diff3-cmd", opt_merge_cmd, 1, N_("use ARG as merge command")},
216 {"editor-cmd", opt_editor_cmd, 1, N_("use ARG as external editor")},
217 #endif
218 {"record-only", opt_record_only, 0,
219 N_("mark revisions as merged (use with -r)")},
220 {"old", opt_old_cmd, 1, N_("use ARG as the older target")},
221 {"new", opt_new_cmd, 1, N_("use ARG as the newer target")},
222 {"revprop", opt_revprop, 0,
223 N_("operate on a revision property (use with -r)")},
224 {"relocate", opt_relocate, 0, N_("relocate via URL-rewriting")},
225 {"config-dir", opt_config_dir, 1,
226 N_("read user configuration files from directory ARG")},
227 {"auto-props", opt_autoprops, 0, N_("enable automatic properties")},
228 {"no-auto-props", opt_no_autoprops, 0, N_("disable automatic properties")},
229 {"native-eol", opt_native_eol, 1,
230 N_("use a different EOL marker than the standard\n"
232 "system marker for files with the svn:eol-style\n"
234 "property set to 'native'.\n"
236 "ARG may be one of 'LF', 'CR', 'CRLF'")},
237 {"limit", 'l', 1, N_("maximum number of log entries")},
238 {"no-unlock", opt_no_unlock, 0, N_("don't unlock the targets")},
239 {"summarize", opt_summarize, 0, N_("show a summary of the results")},
240 {"remove", opt_remove, 0, N_("remove changelist association")},
241 {"changelist", opt_changelist, 1,
242 N_("operate only on members of changelist ARG\n"
244 "[aliases: --cl]")},
245 {"keep-changelists", opt_keep_changelists, 0,
246 N_("don't delete changelists after commit")},
247 {"keep-local", opt_keep_local, 0, N_("keep path in working copy")},
248 {"with-all-revprops", opt_with_all_revprops, 0,
249 N_("retrieve all revision properties")},
250 {"with-revprop", opt_with_revprop, 1,
251 N_("set revision property ARG in new revision\n"
253 "using the name[=value] format")},
254 {"parents", opt_parents, 0, N_("make intermediate directories")},
255 {"use-merge-history", 'g', 0,
256 N_("use/display additional information from merge\n"
258 "history")},
259 {"accept", opt_accept, 1,
260 N_("specify automatic conflict resolution action\n"
262 "('" SVN_CL__ACCEPT_POSTPONE "',"
263 " '" SVN_CL__ACCEPT_BASE "',"
264 /* These two are not implemented yet, so don't
265 waste the user's time with them. */
266 /* " '" SVN_CL__ACCEPT_MINE_CONFLICT "'," */
267 /* " '" SVN_CL__ACCEPT_THEIRS_CONFLICT "'," */
268 " '" SVN_CL__ACCEPT_MINE_FULL "',"
269 " '" SVN_CL__ACCEPT_THEIRS_FULL "',"
270 "\n "
271 " '" SVN_CL__ACCEPT_EDIT "',"
272 " '" SVN_CL__ACCEPT_LAUNCH "')")},
273 {"from-source", opt_from_source, 1,
274 N_("query a particular merge source URL")},
275 {"reintegrate", opt_reintegrate, 0,
276 N_("lump-merge all of source URL's unmerged changes")},
278 /* Long-opt Aliases
280 * These have NULL desriptions, but an option code that matches some
281 * other option (whose description should probably mention its aliases).
284 {"cl", opt_changelist, 1, NULL},
286 {0, 0, 0, 0},
291 /*** Command dispatch. ***/
293 /* Our array of available subcommands.
295 * The entire list must be terminated with an entry of nulls.
297 * In most of the help text "PATH" is used where a working copy path is
298 * required, "URL" where a repository URL is required and "TARGET" when
299 * either a path or an url can be used. Hmm, should this be part of the
300 * help text?
303 /* Options that apply to all commands. (While not every command may
304 currently require authentication or be interactive, allowing every
305 command to take these arguments allows scripts to just pass them
306 willy-nilly to every invocation of 'svn') . */
307 const int svn_cl__global_options[] =
308 { opt_auth_username, opt_auth_password, opt_no_auth_cache, opt_non_interactive,
309 opt_config_dir, 0
312 /* Options for giving a log message. (Some of these also have other uses.)
314 #define SVN_CL__LOG_MSG_OPTIONS 'm', 'F', \
315 opt_force_log, \
316 opt_editor_cmd, \
317 opt_encoding, \
318 opt_with_revprop
320 const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
322 { "add", svn_cl__add, {0}, N_
323 ("Put files and directories under version control, scheduling\n"
324 "them for addition to repository. They will be added in next commit.\n"
325 "usage: add PATH...\n"),
326 {opt_targets, 'N', opt_depth, 'q', opt_force, opt_no_ignore, opt_autoprops,
327 opt_no_autoprops, opt_parents },
328 {{opt_parents, N_("add intermediate parents")}} },
330 { "blame", svn_cl__blame, {"praise", "annotate", "ann"}, N_
331 ("Output the content of specified files or\n"
332 "URLs with revision and author information in-line.\n"
333 "usage: blame TARGET[@REV]...\n"
334 "\n"
335 " If specified, REV determines in which revision the target is first\n"
336 " looked up.\n"),
337 {'r', 'v', 'g', opt_incremental, opt_xml, 'x', opt_force} },
339 { "cat", svn_cl__cat, {0}, N_
340 ("Output the content of specified files or URLs.\n"
341 "usage: cat TARGET[@REV]...\n"
342 "\n"
343 " If specified, REV determines in which revision the target is first\n"
344 " looked up.\n"),
345 {'r'} },
347 { "changelist", svn_cl__changelist, {"cl"}, N_
348 ("Associate (or dissociate) changelist CLNAME with the named files.\n"
349 "usage: 1. changelist CLNAME TARGET...\n"
350 " 2. changelist --remove TARGET...\n"),
351 { 'q', 'R', opt_depth, opt_remove, opt_targets, opt_changelist} },
353 { "checkout", svn_cl__checkout, {"co"}, N_
354 ("Check out a working copy from a repository.\n"
355 "usage: checkout URL[@REV]... [PATH]\n"
356 "\n"
357 " If specified, REV determines in which revision the URL is first\n"
358 " looked up.\n"
359 "\n"
360 " If PATH is omitted, the basename of the URL will be used as\n"
361 " the destination. If multiple URLs are given each will be checked\n"
362 " out into a sub-directory of PATH, with the name of the sub-directory\n"
363 " being the basename of the URL.\n"
364 "\n"
365 " If --force is used, unversioned obstructing paths in the working\n"
366 " copy destination do not automatically cause the check out to fail.\n"
367 " If the obstructing path is the same type (file or directory) as the\n"
368 " corresponding path in the repository it becomes versioned but its\n"
369 " contents are left 'as-is' in the working copy. This means that an\n"
370 " obstructing directory's unversioned children may also obstruct and\n"
371 " become versioned. For files, any content differences between the\n"
372 " obstruction and the repository are treated like a local modification\n"
373 " to the working copy. All properties from the repository are applied\n"
374 " to the obstructing path.\n"),
375 {'r', 'q', 'N', opt_depth, opt_force, opt_ignore_externals, opt_accept} },
377 { "cleanup", svn_cl__cleanup, {0}, N_
378 ("Recursively clean up the working copy, removing locks, resuming\n"
379 "unfinished operations, etc.\n"
380 "usage: cleanup [PATH...]\n"),
381 {opt_merge_cmd} },
383 { "commit", svn_cl__commit, {"ci"},
384 #ifndef AS400
385 N_("Send changes from your working copy to the repository.\n"
386 "usage: commit [PATH...]\n"
387 "\n"
388 " A log message must be provided, but it can be empty. If it is not\n"
389 " given by a --message or --file option, an editor will be started.\n"
390 " If any targets are (or contain) locked items, those will be\n"
391 " unlocked after a successful commit.\n"),
392 #else
393 N_("Send changes from your working copy to the repository.\n"
394 "usage: commit [PATH...]\n"
395 "\n"
396 " A log message must be provided, but it can be empty.\n"
397 " OS400 does not support the starting of an editor,\n"
398 " so --message or --file must be used. If any targets are\n"
399 " (or contain) locked items, those will be unlocked after a\n"
400 " successful commit.\n"),
401 #endif
402 {'q', 'N', opt_depth, opt_targets, opt_no_unlock, SVN_CL__LOG_MSG_OPTIONS,
403 opt_changelist, opt_keep_changelists} },
405 { "copy", svn_cl__copy, {"cp"}, N_
406 ("Duplicate something in working copy or repository, remembering\n"
407 "history.\n"
408 "usage: copy SRC[@REV]... DST\n"
409 "\n"
410 "When copying multiple sources, they will be added as children of DST,\n"
411 "which must be a directory.\n"
412 "\n"
413 " SRC and DST can each be either a working copy (WC) path or URL:\n"
414 " WC -> WC: copy and schedule for addition (with history)\n"
415 " WC -> URL: immediately commit a copy of WC to URL\n"
416 " URL -> WC: check out URL into WC, schedule for addition\n"
417 " URL -> URL: complete server-side copy; used to branch and tag\n"
418 " All the SRCs must be of the same type.\n"
419 "\n"
420 "WARNING: For compatibility with previous versions of Subversion,\n"
421 "copies performed using two working copy paths (WC -> WC) will not\n"
422 "contact the repository. As such, they may not, by default, be able\n"
423 "to propagate merge tracking information from the source of the copy\n"
424 "to the destination.\n"),
425 {'r', 'q', opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
427 { "delete", svn_cl__delete, {"del", "remove", "rm"}, N_
428 ("Remove files and directories from version control.\n"
429 "usage: 1. delete PATH...\n"
430 " 2. delete URL...\n"
431 "\n"
432 " 1. Each item specified by a PATH is scheduled for deletion upon\n"
433 " the next commit. Files, and directories that have not been\n"
434 " committed, are immediately removed from the working copy\n"
435 " unless the --keep-local option is given.\n"
436 " PATHs that are, or contain, unversioned or modified items will\n"
437 " not be removed unless the --force option is given.\n"
438 "\n"
439 " 2. Each item specified by a URL is deleted from the repository\n"
440 " via an immediate commit.\n"),
441 {opt_force, 'q', opt_targets, SVN_CL__LOG_MSG_OPTIONS, opt_keep_local} },
443 { "diff", svn_cl__diff, {"di"}, N_
444 ("Display the differences between two revisions or paths.\n"
445 "usage: 1. diff [-c M | -r N[:M]] [TARGET[@REV]...]\n"
446 " 2. diff [-r N[:M]] --old=OLD-TGT[@OLDREV] [--new=NEW-TGT[@NEWREV]] \\\n"
447 " [PATH...]\n"
448 " 3. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]\n"
449 "\n"
450 " 1. Display the changes made to TARGETs as they are seen in REV between\n"
451 " two revisions. TARGETs may be all working copy paths or all URLs.\n"
452 " If TARGETs are working copy paths, N defaults to BASE and M to the\n"
453 " working copy; if URLs, N must be specified and M defaults to HEAD.\n"
454 " The '-c M' option is equivalent to '-r N:M' where N = M-1.\n"
455 " Using '-c -M' does the reverse: '-r M:N' where N = M-1.\n"
456 "\n"
457 " 2. Display the differences between OLD-TGT as it was seen in OLDREV and\n"
458 " NEW-TGT as it was seen in NEWREV. PATHs, if given, are relative to\n"
459 " OLD-TGT and NEW-TGT and restrict the output to differences for those\n"
460 " paths. OLD-TGT and NEW-TGT may be working copy paths or URL[@REV].\n"
461 " NEW-TGT defaults to OLD-TGT if not specified. -r N makes OLDREV default\n"
462 " to N, -r N:M makes OLDREV default to N and NEWREV default to M.\n"
463 "\n"
464 " 3. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] --new=NEW-URL[@NEWREV]'\n"
465 "\n"
466 " Use just 'svn diff' to display local modifications in a working copy.\n"),
467 {'r', 'c', opt_old_cmd, opt_new_cmd, 'N', opt_depth, opt_diff_cmd, 'x',
468 opt_no_diff_deleted, opt_notice_ancestry, opt_summarize, opt_changelist,
469 opt_force, opt_xml} },
471 { "export", svn_cl__export, {0}, N_
472 ("Create an unversioned copy of a tree.\n"
473 "usage: 1. export [-r REV] URL[@PEGREV] [PATH]\n"
474 " 2. export [-r REV] PATH1[@PEGREV] [PATH2]\n"
475 "\n"
476 " 1. Exports a clean directory tree from the repository specified by\n"
477 " URL, at revision REV if it is given, otherwise at HEAD, into\n"
478 " PATH. If PATH is omitted, the last component of the URL is used\n"
479 " for the local directory name.\n"
480 "\n"
481 " 2. Exports a clean directory tree from the working copy specified by\n"
482 " PATH1, at revision REV if it is given, otherwise at WORKING, into\n"
483 " PATH2. If PATH2 is omitted, the last component of the PATH1 is used\n"
484 " for the local directory name. If REV is not specified, all local\n"
485 " changes will be preserved. Files not under version control will\n"
486 " not be copied.\n"
487 "\n"
488 " If specified, PEGREV determines in which revision the target is first\n"
489 " looked up.\n"),
490 {'r', 'q', 'N', opt_depth, opt_force, opt_native_eol, opt_ignore_externals} },
492 { "help", svn_cl__help, {"?", "h"}, N_
493 ("Describe the usage of this program or its subcommands.\n"
494 "usage: help [SUBCOMMAND...]\n"),
495 {0} },
496 /* This command is also invoked if we see option "--help", "-h" or "-?". */
498 { "import", svn_cl__import, {0}, N_
499 ("Commit an unversioned file or tree into the repository.\n"
500 "usage: import [PATH] URL\n"
501 "\n"
502 " Recursively commit a copy of PATH to URL.\n"
503 " If PATH is omitted '.' is assumed.\n"
504 " Parent directories are created as necessary in the repository.\n"
505 " If PATH is a directory, the contents of the directory are added\n"
506 " directly under URL.\n"
507 " Unversionable items such as device files and pipes are ignored\n"
508 " if --force is specified.\n"),
509 {'q', 'N', opt_depth, opt_autoprops, opt_force, opt_no_autoprops,
510 SVN_CL__LOG_MSG_OPTIONS, opt_no_ignore} },
512 { "info", svn_cl__info, {0}, N_
513 ("Display information about a local or remote item.\n"
514 "usage: info [TARGET[@REV]...]\n"
515 "\n"
516 " Print information about each TARGET (default: '.')\n"
517 " TARGET may be either a working-copy path or URL. If specified, REV\n"
518 " determines in which revision the target is first looked up.\n"),
519 {'r', 'R', opt_depth, opt_targets, opt_incremental, opt_xml, opt_changelist}
522 { "list", svn_cl__list, {"ls"}, N_
523 ("List directory entries in the repository.\n"
524 "usage: list [TARGET[@REV]...]\n"
525 "\n"
526 " List each TARGET file and the contents of each TARGET directory as\n"
527 " they exist in the repository. If TARGET is a working copy path, the\n"
528 " corresponding repository URL will be used. If specified, REV determines\n"
529 " in which revision the target is first looked up.\n"
530 "\n"
531 " The default TARGET is '.', meaning the repository URL of the current\n"
532 " working directory.\n"
533 "\n"
534 " With --verbose, the following fields will be shown for each item:\n"
535 "\n"
536 " Revision number of the last commit\n"
537 " Author of the last commit\n"
538 " If locked, the letter 'O'. (Use 'svn info URL' to see details)\n"
539 " Size (in bytes)\n"
540 " Date and time of the last commit\n"),
541 {'r', 'v', 'R', opt_depth, opt_incremental, opt_xml} },
543 { "lock", svn_cl__lock, {0}, N_
544 ("Lock working copy paths or URLs in the repository, so that\n"
545 "no other user can commit changes to them.\n"
546 "usage: lock TARGET...\n"
547 "\n"
548 " Use --force to steal the lock from another user or working copy.\n"),
549 { opt_targets, 'm', 'F', opt_force_log, opt_encoding, opt_force },
550 {{'F', N_("read lock comment from file ARG")},
551 {'m', N_("specify lock comment ARG")},
552 {opt_force_log, N_("force validity of lock comment source")}} },
554 { "log", svn_cl__log, {0}, N_
555 ("Show the log messages for a set of revision(s) and/or file(s).\n"
556 "usage: 1. log [PATH]\n"
557 " 2. log URL[@REV] [PATH...]\n"
558 "\n"
559 " 1. Print the log messages for a local PATH (default: '.').\n"
560 " The default revision range is BASE:1.\n"
561 "\n"
562 " 2. Print the log messages for the PATHs (default: '.') under URL.\n"
563 " If specified, REV determines in which revision the URL is first\n"
564 " looked up. The default revision range is HEAD:1.\n"
565 "\n"
566 " With -v, also print all affected paths with each log message.\n"
567 " With -q, don't print the log message body itself (note that this is\n"
568 " compatible with -v).\n"
569 "\n"
570 " Each log message is printed just once, even if more than one of the\n"
571 " affected paths for that revision were explicitly requested. Logs\n"
572 " follow copy history by default. Use --stop-on-copy to disable this\n"
573 " behavior, which can be useful for determining branchpoints.\n"
574 "\n"
575 " Examples:\n"
576 " svn log\n"
577 " svn log foo.c\n"
578 " svn log http://www.example.com/repo/project/foo.c\n"
579 " svn log http://www.example.com/repo/project foo.c bar.c\n"),
580 {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, opt_incremental,
581 opt_xml, 'l', opt_with_all_revprops, opt_with_revprop},
582 {{opt_with_revprop, N_("retrieve revision property ARG")},
583 {'c', N_("the change made by ARG")}} },
585 { "merge", svn_cl__merge, {0}, N_
586 ("Apply the differences between two sources to a working copy path.\n"
587 "usage: 1. merge sourceURL1[@N] sourceURL2[@M] [WCPATH]\n"
588 " 2. merge sourceWCPATH1@N sourceWCPATH2@M [WCPATH]\n"
589 " 3. merge [[-c M]... | [-r N:M]...] [SOURCE[@REV] [WCPATH]]\n"
590 "\n"
591 " 1. In the first form, the source URLs are specified at revisions\n"
592 " N and M. These are the two sources to be compared. The revisions\n"
593 " default to HEAD if omitted.\n"
594 "\n"
595 " 2. In the second form, the URLs corresponding to the source working\n"
596 " copy paths define the sources to be compared. The revisions must\n"
597 " be specified.\n"
598 "\n"
599 " 3. In the third form, SOURCE can be either a URL or a working copy\n"
600 " path (in which case its corresponding URL is used). If not\n"
601 " specified, SOURCE will be the same as WCPATH. SOURCE in revision\n"
602 " REV is compared as it existed between revisions N and M for each\n"
603 " revision range provided. If REV is not specified, HEAD is\n"
604 " assumed. '-c M' is equivalent to '-r <M-1>:M', and '-c -M' does\n"
605 " the reverse: '-r M:<M-1>'. If no revision ranges are specified,\n"
606 " the default range of 1:HEAD is used. Multiple '-c' and/or '-r'\n"
607 " instances may be specified, and mixing of forward and reverse\n"
608 " ranges is allowed.\n"
609 "\n"
610 " WCPATH is the working copy path that will receive the changes.\n"
611 " If WCPATH is omitted, a default value of '.' is assumed, unless\n"
612 " the sources have identical basenames that match a file within '.':\n"
613 " in which case, the differences will be applied to that file.\n"
614 "\n"
615 " NOTE: Subversion will only internally track metadata about the\n"
616 " merge operation if the two sources are ancestrally related -- if the\n"
617 " first source is an ancestor of the second, or vice-versa. This is\n"
618 " guaranteed to be the case when using the third form listed above.\n"),
619 {'r', 'c', 'N', opt_depth, 'q', opt_force, opt_dry_run, opt_merge_cmd,
620 opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate} },
622 { "mergeinfo", svn_cl__mergeinfo, {0}, N_
623 ("Query merge-related information.\n"
624 "usage: mergeinfo [TARGET[@REV]...]\n"),
625 {'r', opt_from_source} },
627 { "mkdir", svn_cl__mkdir, {0}, N_
628 ("Create a new directory under version control.\n"
629 "usage: 1. mkdir PATH...\n"
630 " 2. mkdir URL...\n"
631 "\n"
632 " Create version controlled directories.\n"
633 "\n"
634 " 1. Each directory specified by a working copy PATH is created locally\n"
635 " and scheduled for addition upon the next commit.\n"
636 "\n"
637 " 2. Each directory specified by a URL is created in the repository via\n"
638 " an immediate commit.\n"
639 "\n"
640 " In both cases, all the intermediate directories must already exist,\n"
641 " unless the --parents option is given.\n"),
642 {'q', opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
644 { "move", svn_cl__move, {"mv", "rename", "ren"}, N_
645 ("Move and/or rename something in working copy or repository.\n"
646 "usage: move SRC... DST\n"
647 "\n"
648 "When moving multiple sources, they will be added as children of DST,\n"
649 "which must be a directory.\n"
650 "\n"
651 " Note: this subcommand is equivalent to a 'copy' and 'delete'.\n"
652 " Note: the --revision option has no use and is deprecated.\n"
653 "\n"
654 " SRC and DST can both be working copy (WC) paths or URLs:\n"
655 " WC -> WC: move and schedule for addition (with history)\n"
656 " URL -> URL: complete server-side rename.\n"
657 " All the SRCs must be of the same type.\n"),
658 {'r', 'q', opt_force, opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
660 { "propdel", svn_cl__propdel, {"pdel", "pd"}, N_
661 ("Remove a property from files, dirs, or revisions.\n"
662 "usage: 1. propdel PROPNAME [PATH...]\n"
663 " 2. propdel PROPNAME --revprop -r REV [TARGET]\n"
664 "\n"
665 " 1. Removes versioned props in working copy.\n"
666 " 2. Removes unversioned remote prop on repos revision.\n"
667 " TARGET only determines which repository to access.\n"),
668 {'q', 'R', opt_depth, 'r', opt_revprop, opt_changelist} },
670 #ifndef AS400
671 { "propedit", svn_cl__propedit, {"pedit", "pe"}, N_
672 ("Edit a property with an external editor.\n"
673 "usage: 1. propedit PROPNAME TARGET...\n"
674 " 2. propedit PROPNAME --revprop -r REV [TARGET]\n"
675 "\n"
676 " 1. Edits versioned prop in working copy or repository.\n"
677 " 2. Edits unversioned remote prop on repos revision.\n"
678 " TARGET only determines which repository to access.\n"
679 "\n"
680 "See 'svn help propset' for more on property setting.\n"),
681 {'r', opt_revprop, SVN_CL__LOG_MSG_OPTIONS, opt_force} },
682 #endif
684 { "propget", svn_cl__propget, {"pget", "pg"}, N_
685 ("Print the value of a property on files, dirs, or revisions.\n"
686 "usage: 1. propget PROPNAME [TARGET[@REV]...]\n"
687 " 2. propget PROPNAME --revprop -r REV [TARGET]\n"
688 "\n"
689 " 1. Prints versioned props. If specified, REV determines in which\n"
690 " revision the target is first looked up.\n"
691 " 2. Prints unversioned remote prop on repos revision.\n"
692 " TARGET only determines which repository to access.\n"
693 "\n"
694 " By default, this subcommand will add an extra newline to the end\n"
695 " of the property values so that the output looks pretty. Also,\n"
696 " whenever there are multiple paths involved, each property value\n"
697 " is prefixed with the path with which it is associated. Use\n"
698 " the --strict option to disable these beautifications (useful,\n"
699 " for example, when redirecting binary property values to a file).\n"),
700 {'R', opt_depth, 'r', opt_revprop, opt_strict, opt_xml, opt_changelist } },
702 { "proplist", svn_cl__proplist, {"plist", "pl"}, N_
703 ("List all properties on files, dirs, or revisions.\n"
704 "usage: 1. proplist [TARGET[@REV]...]\n"
705 " 2. proplist --revprop -r REV [TARGET]\n"
706 "\n"
707 " 1. Lists versioned props. If specified, REV determines in which\n"
708 " revision the target is first looked up.\n"
709 " 2. Lists unversioned remote props on repos revision.\n"
710 " TARGET only determines which repository to access.\n"),
711 {'v', 'R', opt_depth, 'r', 'q', opt_revprop, opt_xml, opt_changelist } },
713 { "propset", svn_cl__propset, {"pset", "ps"}, N_
714 ("Set the value of a property on files, dirs, or revisions.\n"
715 "usage: 1. propset PROPNAME PROPVAL PATH...\n"
716 " 2. propset PROPNAME --revprop -r REV PROPVAL [TARGET]\n"
717 "\n"
718 " 1. Creates a versioned, local propchange in working copy.\n"
719 " 2. Creates an unversioned, remote propchange on repos revision.\n"
720 " TARGET only determines which repository to access.\n"
721 "\n"
722 " The value may be provided with the --file option instead of PROPVAL.\n"
723 "\n"
724 " Note: svn recognizes the following special versioned properties\n"
725 " but will store any arbitrary properties set:\n"
726 " svn:ignore - A newline separated list of file patterns to ignore.\n"
727 " svn:keywords - Keywords to be expanded. Valid keywords are:\n"
728 " URL, HeadURL - The URL for the head version of the object.\n"
729 " Author, LastChangedBy - The last person to modify the file.\n"
730 " Date, LastChangedDate - The date/time the object was last modified.\n"
731 " Rev, Revision, - The last revision the object changed.\n"
732 " LastChangedRevision\n"
733 " Id - A compressed summary of the previous\n"
734 " 4 keywords.\n"
735 " svn:executable - If present, make the file executable. Use\n"
736 " 'svn propdel svn:executable PATH...' to clear.\n"
737 " svn:eol-style - One of 'native', 'LF', 'CR', 'CRLF'.\n"
738 " svn:mime-type - The mimetype of the file. Used to determine\n"
739 " whether to merge the file, and how to serve it from Apache.\n"
740 " A mimetype beginning with 'text/' (or an absent mimetype) is\n"
741 " treated as text. Anything else is treated as binary.\n"
742 " svn:externals - A newline separated list of module specifiers,\n"
743 " each of which consists of a relative directory path, optional\n"
744 " revision flags and an URL. The ordering of the three elements\n"
745 " implements different behavior. Subversion 1.4 and earlier only\n"
746 " support the following formats and the URLs cannot have peg\n"
747 " revisions:\n"
748 " foo http://example.com/repos/zig\n"
749 " foo/bar -r 1234 http://example.com/repos/zag\n"
750 " Subversion 1.5 and greater support the above formats and the\n"
751 " following formats where the URLs may have peg revisions:\n"
752 " http://example.com/repos/zig foo\n"
753 " -r 1234 http://example.com/repos/zig foo/bar\n"
754 " Relative URLs are supported in Subversion 1.5 and greater for\n"
755 " all above formats and are indicated by starting the URL with one\n"
756 " of the following strings\n"
757 " ../ to the parent directory of the extracted external\n"
758 " ^/ to the repository root\n"
759 " // to the scheme\n"
760 " / to the server root\n"
761 " The ambiguous format 'relative_path relative_path' is taken as\n"
762 " 'relative_url relative_path' with peg revision support.\n"
763 " svn:needs-lock - If present, indicates that the file should be locked\n"
764 " before it is modified. Makes the working copy file read-only\n"
765 " when it is not locked. Use 'svn propdel svn:needs-lock PATH...'\n"
766 " to clear.\n"
767 "\n"
768 " The svn:keywords, svn:executable, svn:eol-style, svn:mime-type and\n"
769 " svn:needs-lock properties cannot be set on a directory. A non-recursive\n"
770 " attempt will fail, and a recursive attempt will set the property\n"
771 " only on the file children of the directory.\n"),
772 {'F', opt_encoding, 'q', 'r', opt_targets, 'R', opt_depth, opt_revprop,
773 opt_force, opt_changelist },
774 {{'F', N_("read property value from file ARG")}} },
776 { "resolved", svn_cl__resolved, {0}, N_
777 ("Remove 'conflicted' state on working copy files or directories.\n"
778 "usage: resolved PATH...\n"
779 "\n"
780 " Note: this subcommand does not semantically resolve conflicts or\n"
781 " remove conflict markers; it merely removes the conflict-related\n"
782 " artifact files and allows PATH to be committed again.\n"),
783 {opt_targets, 'R', opt_depth, 'q', opt_accept},
784 {{opt_accept, N_("specify automatic conflict resolution source\n"
786 " '" SVN_CL__ACCEPT_BASE "',"
787 /* These two are not implemented yet, so
788 don't waste the user's time with them. */
789 /* " '" SVN_CL__ACCEPT_MINE_CONFLICT "'," */
790 /* " '" SVN_CL__ACCEPT_THEIRS_CONFLICT "'," */
791 " '" SVN_CL__ACCEPT_MINE_FULL "',"
792 " '" SVN_CL__ACCEPT_THEIRS_FULL "')")}} },
794 { "revert", svn_cl__revert, {0}, N_
795 ("Restore pristine working copy file (undo most local edits).\n"
796 "usage: revert PATH...\n"
797 "\n"
798 " Note: this subcommand does not require network access, and resolves\n"
799 " any conflicted states. However, it does not restore removed directories.\n"),
800 {opt_targets, 'R', opt_depth, 'q', opt_changelist} },
802 { "status", svn_cl__status, {"stat", "st"}, N_
803 ("Print the status of working copy files and directories.\n"
804 "usage: status [PATH...]\n"
805 "\n"
806 " With no args, print only locally modified items (no network access).\n"
807 " With -q, print only summary information about locally modified items.\n"
808 " With -u, add working revision and server out-of-date information.\n"
809 " With -v, print full revision information on every item.\n"
810 "\n"
811 " The first six columns in the output are each one character wide:\n"
812 " First column: Says if item was added, deleted, or otherwise changed\n"
813 " ' ' no modifications\n"
814 " 'A' Added\n"
815 " 'C' Conflicted\n"
816 " 'D' Deleted\n"
817 " 'I' Ignored\n"
818 " 'M' Modified\n"
819 " 'R' Replaced\n"
820 " 'X' item is unversioned, but is used by an externals definition\n"
821 " '?' item is not under version control\n"
822 " '!' item is missing (removed by non-svn command) or incomplete\n"
823 " '~' versioned item obstructed by some item of a different kind\n"
824 " Second column: Modifications of a file's or directory's properties\n"
825 " ' ' no modifications\n"
826 " 'C' Conflicted\n"
827 " 'M' Modified\n"
828 " Third column: Whether the working copy directory is locked\n"
829 " ' ' not locked\n"
830 " 'L' locked\n"
831 " Fourth column: Scheduled commit will contain addition-with-history\n"
832 " ' ' no history scheduled with commit\n"
833 " '+' history scheduled with commit\n"
834 " Fifth column: Whether the item is switched relative to its parent\n"
835 " ' ' normal\n"
836 " 'S' switched\n"
837 " Sixth column: Repository lock token\n"
838 " (without -u)\n"
839 " ' ' no lock token\n"
840 " 'K' lock token present\n"
841 " (with -u)\n"
842 " ' ' not locked in repository, no lock token\n"
843 " 'K' locked in repository, lock toKen present\n"
844 " 'O' locked in repository, lock token in some Other working copy\n"
845 " 'T' locked in repository, lock token present but sTolen\n"
846 " 'B' not locked in repository, lock token present but Broken\n"
847 "\n"
848 " The out-of-date information appears in the eighth column (with -u):\n"
849 " '*' a newer revision exists on the server\n"
850 " ' ' the working copy is up to date\n"
851 "\n"
852 " Remaining fields are variable width and delimited by spaces:\n"
853 " The working revision (with -u or -v)\n"
854 " The last committed revision and last committed author (with -v)\n"
855 " The working copy path is always the final field, so it can\n"
856 " include spaces.\n"
857 "\n"
858 " Example output:\n"
859 " svn status wc\n"
860 " M wc/bar.c\n"
861 " A + wc/qax.c\n"
862 "\n"
863 " svn status -u wc\n"
864 " M 965 wc/bar.c\n"
865 " * 965 wc/foo.c\n"
866 " A + 965 wc/qax.c\n"
867 " Status against revision: 981\n"
868 "\n"
869 " svn status --show-updates --verbose wc\n"
870 " M 965 938 kfogel wc/bar.c\n"
871 " * 965 922 sussman wc/foo.c\n"
872 " A + 965 687 joe wc/qax.c\n"
873 " 965 687 joe wc/zig.c\n"
874 " Status against revision: 981\n"),
875 { 'u', 'v', 'N', opt_depth, 'q', opt_no_ignore, opt_incremental, opt_xml,
876 opt_ignore_externals, opt_changelist} },
878 { "switch", svn_cl__switch, {"sw"}, N_
879 ("Update the working copy to a different URL.\n"
880 "usage: 1. switch URL[@PEGREV] [PATH]\n"
881 " 2. switch --relocate FROM TO [PATH...]\n"
882 "\n"
883 " 1. Update the working copy to mirror a new URL within the repository.\n"
884 " This behaviour is similar to 'svn update', and is the way to\n"
885 " move a working copy to a branch or tag within the same repository.\n"
886 " If specified, PEGREV determines in which revision the target is first\n"
887 " looked up.\n"
888 "\n"
889 " If --force is used, unversioned obstructing paths in the working\n"
890 " copy do not automatically cause a failure if the switch attempts to\n"
891 " add the same path. If the obstructing path is the same type (file\n"
892 " or directory) as the corresponding path in the repository it becomes\n"
893 " versioned but its contents are left 'as-is' in the working copy.\n"
894 " This means that an obstructing directory's unversioned children may\n"
895 " also obstruct and become versioned. For files, any content differences\n"
896 " between the obstruction and the repository are treated like a local\n"
897 " modification to the working copy. All properties from the repository\n"
898 " are applied to the obstructing path.\n"
899 "\n"
900 " Use the --set-depth option to set a new working copy depth on the\n"
901 " targets of this operation. Currently, the depth of a working copy\n"
902 " directory can only be increased (telescoped more deeply); you cannot\n"
903 " make a directory more shallow.\n"
904 "\n"
905 " 2. Rewrite working copy URL metadata to reflect a syntactic change only.\n"
906 " This is used when repository's root URL changes (such as a scheme\n"
907 " or hostname change) but your working copy still reflects the same\n"
908 " directory within the same repository.\n"),
909 { 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_relocate,
910 opt_ignore_externals, opt_force, opt_accept} },
912 { "unlock", svn_cl__unlock, {0}, N_
913 ("Unlock working copy paths or URLs.\n"
914 "usage: unlock TARGET...\n"
915 "\n"
916 " Use --force to break the lock.\n"),
917 { opt_targets, opt_force } },
919 { "update", svn_cl__update, {"up"}, N_
920 ("Bring changes from the repository into the working copy.\n"
921 "usage: update [PATH...]\n"
922 "\n"
923 " If no revision given, bring working copy up-to-date with HEAD rev.\n"
924 " Else synchronize working copy to revision given by -r.\n"
925 "\n"
926 " For each updated item a line will start with a character reporting the\n"
927 " action taken. These characters have the following meaning:\n"
928 "\n"
929 " A Added\n"
930 " D Deleted\n"
931 " U Updated\n"
932 " C Conflict\n"
933 " G Merged\n"
934 " E Existed\n"
935 "\n"
936 " A character in the first column signifies an update to the actual file,\n"
937 " while updates to the file's properties are shown in the second column.\n"
938 " A 'B' in the third column signifies that the lock for the file has\n"
939 " been broken or stolen.\n"
940 "\n"
941 " If --force is used, unversioned obstructing paths in the working\n"
942 " copy do not automatically cause a failure if the update attempts to\n"
943 " add the same path. If the obstructing path is the same type (file\n"
944 " or directory) as the corresponding path in the repository it becomes\n"
945 " versioned but its contents are left 'as-is' in the working copy.\n"
946 " This means that an obstructing directory's unversioned children may\n"
947 " also obstruct and become versioned. For files, any content differences\n"
948 " between the obstruction and the repository are treated like a local\n"
949 " modification to the working copy. All properties from the repository\n"
950 " are applied to the obstructing path. Obstructing paths are reported\n"
951 " in the first column with code 'E'.\n"
952 "\n"
953 " Use the --set-depth option to set a new working copy depth on the\n"
954 " targets of this operation. Currently, the depth of a working copy\n"
955 " directory can only be increased (telescoped more deeply); you cannot\n"
956 " make a directory more shallow.\n"),
957 {'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_force,
958 opt_ignore_externals, opt_changelist, opt_editor_cmd, opt_accept} },
960 { NULL, NULL, {0}, NULL, {0} }
964 /* Version compatibility check */
965 static svn_error_t *
966 check_lib_versions(void)
968 static const svn_version_checklist_t checklist[] =
970 { "svn_subr", svn_subr_version },
971 { "svn_client", svn_client_version },
972 { "svn_wc", svn_wc_version },
973 { "svn_ra", svn_ra_version },
974 { "svn_delta", svn_delta_version },
975 { "svn_diff", svn_diff_version },
976 { NULL, NULL }
979 SVN_VERSION_DEFINE(my_version);
980 return svn_ver_check_list(&my_version, checklist);
984 /* A flag to see if we've been cancelled by the client or not. */
985 static volatile sig_atomic_t cancelled = FALSE;
987 /* A signal handler to support cancellation. */
988 static void
989 signal_handler(int signum)
991 apr_signal(signum, SIG_IGN);
992 cancelled = TRUE;
995 /* Our cancellation callback. */
996 svn_error_t *
997 svn_cl__check_cancel(void *baton)
999 if (cancelled)
1000 return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
1001 else
1002 return SVN_NO_ERROR;
1007 /*** Main. ***/
1010 main(int argc, const char *argv[])
1012 svn_error_t *err;
1013 apr_allocator_t *allocator;
1014 apr_pool_t *pool;
1015 int opt_id;
1016 apr_getopt_t *os;
1017 svn_cl__opt_state_t opt_state = { 0, { 0 } };
1018 svn_client_ctx_t *ctx;
1019 apr_array_header_t *received_opts;
1020 int i;
1021 const svn_opt_subcommand_desc2_t *subcommand = NULL;
1022 const char *dash_m_arg = NULL, *dash_F_arg = NULL;
1023 const char *path_utf8;
1024 apr_status_t apr_err;
1025 svn_cl__cmd_baton_t command_baton;
1026 svn_auth_baton_t *ab;
1027 svn_config_t *cfg;
1028 svn_boolean_t descend = TRUE;
1029 svn_boolean_t interactive_conflicts = FALSE;
1030 apr_hash_t *changelists;
1032 /* Initialize the app. */
1033 if (svn_cmdline_init("svn", stderr) != EXIT_SUCCESS)
1034 return EXIT_FAILURE;
1036 /* Create our top-level pool. Use a seperate mutexless allocator,
1037 * given this application is single threaded.
1039 if (apr_allocator_create(&allocator))
1040 return EXIT_FAILURE;
1042 apr_allocator_max_free_set(allocator, SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
1044 pool = svn_pool_create_ex(NULL, allocator);
1045 apr_allocator_owner_set(allocator, pool);
1047 received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
1049 /* Check library versions */
1050 err = check_lib_versions();
1051 if (err)
1052 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1054 #if defined(WIN32) || defined(__CYGWIN__)
1055 /* Set the working copy administrative directory name. */
1056 if (getenv("SVN_ASP_DOT_NET_HACK"))
1058 err = svn_wc_set_adm_dir("_svn", pool);
1059 if (err)
1060 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1062 #endif
1064 /* Initialize the RA library. */
1065 err = svn_ra_initialize(pool);
1066 if (err)
1067 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1069 /* Init our changelists hash. */
1070 changelists = apr_hash_make(pool);
1072 /* Begin processing arguments. */
1073 opt_state.start_revision.kind = svn_opt_revision_unspecified;
1074 opt_state.end_revision.kind = svn_opt_revision_unspecified;
1075 opt_state.revision_ranges =
1076 apr_array_make(pool, 0, sizeof(svn_opt_revision_range_t *));
1077 opt_state.depth = svn_depth_unknown;
1078 opt_state.set_depth = svn_depth_unknown;
1079 opt_state.accept_which = svn_cl__accept_invalid;
1081 /* No args? Show usage. */
1082 if (argc <= 1)
1084 svn_cl__help(NULL, NULL, pool);
1085 svn_pool_destroy(pool);
1086 return EXIT_FAILURE;
1089 /* Else, parse options. */
1090 err = svn_cmdline__getopt_init(&os, argc, argv, pool);
1091 if (err)
1092 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1094 os->interleave = 1;
1095 while (1)
1097 const char *opt_arg;
1098 const char *utf8_opt_arg;
1100 /* Parse the next option. */
1101 apr_err = apr_getopt_long(os, svn_cl__options, &opt_id, &opt_arg);
1102 if (APR_STATUS_IS_EOF(apr_err))
1103 break;
1104 else if (apr_err)
1106 svn_cl__help(NULL, NULL, pool);
1107 svn_pool_destroy(pool);
1108 return EXIT_FAILURE;
1111 /* Stash the option code in an array before parsing it. */
1112 APR_ARRAY_PUSH(received_opts, int) = opt_id;
1114 switch (opt_id) {
1115 case 'l':
1117 char *end;
1118 opt_state.limit = strtol(opt_arg, &end, 10);
1119 if (end == opt_arg || *end != '\0')
1121 err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1122 _("Non-numeric limit argument given"));
1123 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1125 if (opt_state.limit <= 0)
1127 err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
1128 _("Argument to --limit must be positive"));
1129 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1132 break;
1133 case 'm':
1134 /* Note that there's no way here to detect if the log message
1135 contains a zero byte -- if it does, then opt_arg will just
1136 be shorter than the user intended. Oh well. */
1137 opt_state.message = apr_pstrdup(pool, opt_arg);
1138 dash_m_arg = opt_arg;
1139 break;
1140 case 'c':
1142 char *end;
1143 svn_revnum_t changeno;
1144 svn_opt_revision_range_t *range;
1146 if (opt_state.old_target)
1148 err = svn_error_create
1149 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1150 _("Can't specify -c with --old"));
1151 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1153 changeno = strtol(opt_arg, &end, 10);
1154 if (end == opt_arg || *end != '\0')
1156 err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1157 _("Non-numeric change argument "
1158 "given to -c"));
1159 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1161 if (changeno == 0)
1163 err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1164 _("There is no change 0"));
1165 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1168 /* Figure out the range:
1169 -c N -> -r N-1:N
1170 -c -N -> -r N:N-1 */
1171 range = apr_palloc(pool, sizeof(*range));
1172 if (changeno > 0)
1174 range->start.value.number = changeno - 1;
1175 range->end.value.number = changeno;
1177 else
1179 changeno = -changeno;
1180 range->start.value.number = changeno;
1181 range->end.value.number = changeno - 1;
1183 opt_state.used_change_arg = TRUE;
1184 range->start.kind = svn_opt_revision_number;
1185 range->end.kind = svn_opt_revision_number;
1186 APR_ARRAY_PUSH(opt_state.revision_ranges,
1187 svn_opt_revision_range_t *) = range;
1189 break;
1190 case 'r':
1191 if (svn_opt_parse_revision_to_range(opt_state.revision_ranges,
1192 opt_arg, pool) != 0)
1194 err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
1195 if (! err)
1196 err = svn_error_createf
1197 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1198 _("Syntax error in revision argument '%s'"),
1199 utf8_opt_arg);
1200 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1202 break;
1203 case 'v':
1204 opt_state.verbose = TRUE;
1205 break;
1206 case 'u':
1207 opt_state.update = TRUE;
1208 break;
1209 case 'h':
1210 case '?':
1211 opt_state.help = TRUE;
1212 break;
1213 case 'q':
1214 opt_state.quiet = TRUE;
1215 break;
1216 case opt_incremental:
1217 opt_state.incremental = TRUE;
1218 break;
1219 case 'F':
1220 err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
1221 if (! err)
1222 err = svn_stringbuf_from_file(&(opt_state.filedata),
1223 utf8_opt_arg, pool);
1224 if (err)
1225 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1226 dash_F_arg = opt_arg;
1227 break;
1228 case opt_targets:
1230 svn_stringbuf_t *buffer, *buffer_utf8;
1232 /* We need to convert to UTF-8 now, even before we divide
1233 the targets into an array, because otherwise we wouldn't
1234 know what delimiter to use for svn_cstring_split(). */
1236 err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
1238 if (! err)
1239 err = svn_stringbuf_from_file(&buffer, utf8_opt_arg, pool);
1240 if (! err)
1241 err = svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool);
1242 if (err)
1243 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1244 opt_state.targets = svn_cstring_split(buffer_utf8->data, "\n\r",
1245 TRUE, pool);
1247 break;
1248 case opt_force:
1249 opt_state.force = TRUE;
1250 break;
1251 case opt_force_log:
1252 opt_state.force_log = TRUE;
1253 break;
1254 case opt_dry_run:
1255 opt_state.dry_run = TRUE;
1256 break;
1257 case opt_revprop:
1258 opt_state.revprop = TRUE;
1259 break;
1260 case 'R':
1261 opt_state.depth = SVN_DEPTH_INFINITY_OR_FILES(TRUE);
1262 break;
1263 case 'N':
1264 descend = FALSE;
1265 break;
1266 case opt_depth:
1267 err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
1268 if (err)
1269 return svn_cmdline_handle_exit_error
1270 (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1271 _("Error converting depth "
1272 "from locale to UTF8")), pool, "svn: ");
1273 opt_state.depth = svn_depth_from_word(utf8_opt_arg);
1274 if (opt_state.depth == svn_depth_unknown
1275 || opt_state.depth == svn_depth_exclude)
1277 return svn_cmdline_handle_exit_error
1278 (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1279 _("'%s' is not a valid depth; try "
1280 "'empty', 'files', 'immediates', "
1281 "or 'infinity'"),
1282 utf8_opt_arg), pool, "svn: ");
1284 break;
1285 case opt_set_depth:
1286 err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
1287 if (err)
1288 return svn_cmdline_handle_exit_error
1289 (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1290 _("Error converting depth "
1291 "from locale to UTF8")), pool, "svn: ");
1292 opt_state.set_depth = svn_depth_from_word(utf8_opt_arg);
1293 if (opt_state.set_depth == svn_depth_unknown
1294 || opt_state.set_depth == svn_depth_exclude)
1296 return svn_cmdline_handle_exit_error
1297 (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1298 _("'%s' is not a valid depth; try "
1299 "'empty', 'files', 'immediates', "
1300 "or 'infinity'"),
1301 utf8_opt_arg), pool, "svn: ");
1303 break;
1304 case opt_version:
1305 opt_state.version = TRUE;
1306 break;
1307 case opt_auth_username:
1308 err = svn_utf_cstring_to_utf8(&opt_state.auth_username,
1309 opt_arg, pool);
1310 if (err)
1311 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1312 break;
1313 case opt_auth_password:
1314 err = svn_utf_cstring_to_utf8(&opt_state.auth_password,
1315 opt_arg, pool);
1316 if (err)
1317 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1318 break;
1319 case opt_encoding:
1320 opt_state.encoding = apr_pstrdup(pool, opt_arg);
1321 break;
1322 case opt_xml:
1323 opt_state.xml = TRUE;
1324 break;
1325 case opt_stop_on_copy:
1326 opt_state.stop_on_copy = TRUE;
1327 break;
1328 case opt_strict:
1329 opt_state.strict = TRUE;
1330 break;
1331 case opt_no_ignore:
1332 opt_state.no_ignore = TRUE;
1333 break;
1334 case opt_no_auth_cache:
1335 opt_state.no_auth_cache = TRUE;
1336 break;
1337 case opt_non_interactive:
1338 opt_state.non_interactive = TRUE;
1339 break;
1340 case opt_no_diff_deleted:
1341 opt_state.no_diff_deleted = TRUE;
1342 break;
1343 case opt_notice_ancestry:
1344 opt_state.notice_ancestry = TRUE;
1345 break;
1346 case opt_ignore_ancestry:
1347 opt_state.ignore_ancestry = TRUE;
1348 break;
1349 case opt_ignore_externals:
1350 opt_state.ignore_externals = TRUE;
1351 break;
1352 case opt_relocate:
1353 opt_state.relocate = TRUE;
1354 break;
1355 case 'x':
1356 err = svn_utf_cstring_to_utf8(&opt_state.extensions, opt_arg, pool);
1357 if (err)
1358 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1359 break;
1360 case opt_diff_cmd:
1361 opt_state.diff_cmd = apr_pstrdup(pool, opt_arg);
1362 break;
1363 case opt_merge_cmd:
1364 opt_state.merge_cmd = apr_pstrdup(pool, opt_arg);
1365 break;
1366 case opt_record_only:
1367 opt_state.record_only = TRUE;
1368 break;
1369 case opt_editor_cmd:
1370 opt_state.editor_cmd = apr_pstrdup(pool, opt_arg);
1371 break;
1372 case opt_old_cmd:
1373 if (opt_state.used_change_arg)
1375 err = svn_error_create
1376 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1377 _("Can't specify -c with --old"));
1378 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1380 opt_state.old_target = apr_pstrdup(pool, opt_arg);
1381 break;
1382 case opt_new_cmd:
1383 opt_state.new_target = apr_pstrdup(pool, opt_arg);
1384 break;
1385 case opt_config_dir:
1386 err = svn_utf_cstring_to_utf8(&path_utf8, opt_arg, pool);
1387 opt_state.config_dir = svn_path_canonicalize(path_utf8, pool);
1388 break;
1389 case opt_autoprops:
1390 opt_state.autoprops = TRUE;
1391 break;
1392 case opt_no_autoprops:
1393 opt_state.no_autoprops = TRUE;
1394 break;
1395 case opt_native_eol:
1396 if ( !strcmp("LF", opt_arg) || !strcmp("CR", opt_arg) ||
1397 !strcmp("CRLF", opt_arg))
1398 opt_state.native_eol = apr_pstrdup(pool, opt_arg);
1399 else
1401 err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
1402 if (! err)
1403 err = svn_error_createf
1404 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1405 _("Syntax error in native-eol argument '%s'"),
1406 utf8_opt_arg);
1407 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1409 break;
1410 case opt_no_unlock:
1411 opt_state.no_unlock = TRUE;
1412 break;
1413 case opt_summarize:
1414 opt_state.summarize = TRUE;
1415 break;
1416 case opt_remove:
1417 opt_state.remove = TRUE;
1418 break;
1419 case opt_changelist:
1420 opt_state.changelist = apr_pstrdup(pool, opt_arg);
1421 apr_hash_set(changelists, opt_state.changelist,
1422 APR_HASH_KEY_STRING, (void *)1);
1423 break;
1424 case opt_keep_changelists:
1425 opt_state.keep_changelists = TRUE;
1426 break;
1427 case opt_keep_local:
1428 opt_state.keep_local = TRUE;
1429 break;
1430 case opt_with_all_revprops:
1431 /* If --with-all-revprops is specified along with one or more
1432 * --with-revprops options, --with-all-revprops takes precedence. */
1433 opt_state.all_revprops = TRUE;
1434 break;
1435 case opt_with_revprop:
1436 err = svn_opt_parse_revprop(&opt_state.revprop_table, opt_arg, pool);
1437 if (err != SVN_NO_ERROR)
1438 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1439 break;
1440 case opt_parents:
1441 opt_state.parents = TRUE;
1442 break;
1443 case 'g':
1444 opt_state.use_merge_history = TRUE;
1445 break;
1446 case opt_accept:
1447 opt_state.accept_which = svn_cl__accept_from_word(opt_arg);
1448 if (opt_state.accept_which == svn_cl__accept_invalid)
1449 return svn_cmdline_handle_exit_error
1450 (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1451 _("'%s' is not a valid accept value"), opt_arg),
1452 pool, "svn: ");
1453 break;
1454 case opt_from_source:
1455 err = svn_utf_cstring_to_utf8(&path_utf8, opt_arg, pool);
1456 if (! svn_path_is_url(path_utf8))
1457 return svn_cmdline_handle_exit_error
1458 (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1459 _("'%s' is not a URL"), opt_arg),
1460 pool, "svn: ");
1461 opt_state.from_source = svn_path_canonicalize(path_utf8, pool);
1462 break;
1463 case opt_reintegrate:
1464 opt_state.reintegrate = TRUE;
1465 break;
1466 default:
1467 /* Hmmm. Perhaps this would be a good place to squirrel away
1468 opts that commands like svn diff might need. Hmmm indeed. */
1469 break;
1473 /* Turn our hash of changelists into an array of unique ones. */
1474 err = svn_hash_keys(&(opt_state.changelists), changelists, pool);
1475 if (err)
1476 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1478 /* ### This really belongs in libsvn_client. The trouble is,
1479 there's no one place there to run it from, no
1480 svn_client_init(). We'd have to add it to all the public
1481 functions that a client might call. It's unmaintainable to do
1482 initialization from within libsvn_client itself, but it seems
1483 burdensome to demand that all clients call svn_client_init()
1484 before calling any other libsvn_client function... On the other
1485 hand, the alternative is effectively to demand that they call
1486 svn_config_ensure() instead, so maybe we should have a generic
1487 init function anyway. Thoughts? */
1488 err = svn_config_ensure(opt_state.config_dir, pool);
1489 if (err)
1490 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1492 /* If the user asked for help, then the rest of the arguments are
1493 the names of subcommands to get help on (if any), or else they're
1494 just typos/mistakes. Whatever the case, the subcommand to
1495 actually run is svn_cl__help(). */
1496 if (opt_state.help)
1497 subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table, "help");
1499 /* If we're not running the `help' subcommand, then look for a
1500 subcommand in the first argument. */
1501 if (subcommand == NULL)
1503 if (os->ind >= os->argc)
1505 if (opt_state.version)
1507 /* Use the "help" subcommand to handle the "--version" option. */
1508 static const svn_opt_subcommand_desc2_t pseudo_cmd =
1509 { "--version", svn_cl__help, {0}, "",
1510 {opt_version, /* must accept its own option */
1511 'q', /* brief output */
1512 opt_config_dir /* all commands accept this */
1513 } };
1515 subcommand = &pseudo_cmd;
1517 else
1519 svn_error_clear
1520 (svn_cmdline_fprintf(stderr, pool,
1521 _("Subcommand argument required\n")));
1522 svn_cl__help(NULL, NULL, pool);
1523 svn_pool_destroy(pool);
1524 return EXIT_FAILURE;
1527 else
1529 const char *first_arg = os->argv[os->ind++];
1530 subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table,
1531 first_arg);
1532 if (subcommand == NULL)
1534 const char *first_arg_utf8;
1535 err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, pool);
1536 if (err)
1537 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1538 svn_error_clear
1539 (svn_cmdline_fprintf(stderr, pool,
1540 _("Unknown command: '%s'\n"),
1541 first_arg_utf8));
1542 svn_cl__help(NULL, NULL, pool);
1543 svn_pool_destroy(pool);
1544 return EXIT_FAILURE;
1549 /* Check that the subcommand wasn't passed any inappropriate options. */
1550 for (i = 0; i < received_opts->nelts; i++)
1552 opt_id = APR_ARRAY_IDX(received_opts, i, int);
1554 /* All commands implicitly accept --help, so just skip over this
1555 when we see it. Note that we don't want to include this option
1556 in their "accepted options" list because it would be awfully
1557 redundant to display it in every commands' help text. */
1558 if (opt_id == 'h' || opt_id == '?')
1559 continue;
1561 if (! svn_opt_subcommand_takes_option3(subcommand, opt_id,
1562 svn_cl__global_options))
1564 const char *optstr;
1565 const apr_getopt_option_t *badopt =
1566 svn_opt_get_option_from_code2(opt_id, svn_cl__options,
1567 subcommand, pool);
1568 svn_opt_format_option(&optstr, badopt, FALSE, pool);
1569 if (subcommand->name[0] == '-')
1570 svn_cl__help(NULL, NULL, pool);
1571 else
1572 svn_error_clear
1573 (svn_cmdline_fprintf
1574 (stderr, pool, _("Subcommand '%s' doesn't accept option '%s'\n"
1575 "Type 'svn help %s' for usage.\n"),
1576 subcommand->name, optstr, subcommand->name));
1577 svn_pool_destroy(pool);
1578 return EXIT_FAILURE;
1582 /* Only merge supports multiple revisions/revision ranges. */
1583 if (subcommand->cmd_func != svn_cl__merge)
1585 if (opt_state.revision_ranges->nelts > 1)
1587 err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1588 _("Multiple revision arguments "
1589 "encountered; can't specify -c twice, "
1590 "or both -c and -r"));
1591 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1595 /* Merge doesn't support specifying a revision range
1596 when using --reintegrate. */
1597 if (subcommand->cmd_func == svn_cl__merge
1598 && opt_state.revision_ranges->nelts
1599 && opt_state.reintegrate)
1601 err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1602 _("-r and -c can't be used with --reintegrate"));
1603 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1606 /* Disallow simultaneous use of both --depth and --set-depth. */
1607 if ((opt_state.depth != svn_depth_unknown)
1608 && (opt_state.set_depth != svn_depth_unknown))
1610 err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1611 _("--depth and --set-depth are mutually "
1612 "exclusive"));
1613 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1616 /* Ensure that 'revision_ranges' has at least one item, and that
1617 'start_revision' and 'end_revision' match that item. */
1618 if (opt_state.revision_ranges->nelts == 0)
1620 svn_opt_revision_range_t *range = apr_palloc(pool, sizeof(*range));
1621 range->start.kind = svn_opt_revision_unspecified;
1622 range->end.kind = svn_opt_revision_unspecified;
1623 APR_ARRAY_PUSH(opt_state.revision_ranges,
1624 svn_opt_revision_range_t *) = range;
1626 opt_state.start_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0,
1627 svn_opt_revision_range_t *)->start;
1628 opt_state.end_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0,
1629 svn_opt_revision_range_t *)->end;
1631 /* If we're running a command that could result in a commit, verify
1632 that any log message we were given on the command line makes
1633 sense (unless we've also been instructed not to care). */
1634 if ((! opt_state.force_log)
1635 && (subcommand->cmd_func == svn_cl__commit
1636 || subcommand->cmd_func == svn_cl__copy
1637 || subcommand->cmd_func == svn_cl__delete
1638 || subcommand->cmd_func == svn_cl__import
1639 || subcommand->cmd_func == svn_cl__mkdir
1640 || subcommand->cmd_func == svn_cl__move
1641 || subcommand->cmd_func == svn_cl__lock
1642 || subcommand->cmd_func == svn_cl__propedit))
1644 /* If the -F argument is a file that's under revision control,
1645 that's probably not what the user intended. */
1646 if (dash_F_arg)
1648 svn_wc_adm_access_t *adm_access;
1649 const svn_wc_entry_t *e;
1650 const char *fname_utf8 = svn_path_internal_style(dash_F_arg, pool);
1651 err = svn_wc_adm_probe_open3(&adm_access, NULL, fname_utf8,
1652 FALSE, 0, NULL, NULL, pool);
1653 if (! err)
1654 err = svn_wc_entry(&e, fname_utf8, adm_access, FALSE, pool);
1655 if ((err == SVN_NO_ERROR) && e)
1657 if (subcommand->cmd_func != svn_cl__lock)
1659 err = svn_error_create
1660 (SVN_ERR_CL_LOG_MESSAGE_IS_VERSIONED_FILE, NULL,
1661 _("Log message file is a versioned file; "
1662 "use '--force-log' to override"));
1664 else
1666 err = svn_error_create
1667 (SVN_ERR_CL_LOG_MESSAGE_IS_VERSIONED_FILE, NULL,
1668 _("Lock comment file is a versioned file; "
1669 "use '--force-log' to override"));
1671 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1673 svn_error_clear(err);
1676 /* If the -m argument is a file at all, that's probably not what
1677 the user intended. */
1678 if (dash_m_arg)
1680 apr_finfo_t finfo;
1681 if (apr_stat(&finfo, dash_m_arg,
1682 APR_FINFO_MIN, pool) == APR_SUCCESS)
1684 if (subcommand->cmd_func != svn_cl__lock)
1686 err = svn_error_create
1687 (SVN_ERR_CL_LOG_MESSAGE_IS_PATHNAME, NULL,
1688 _("The log message is a pathname "
1689 "(was -F intended?); use '--force-log' to override"));
1691 else
1693 err = svn_error_create
1694 (SVN_ERR_CL_LOG_MESSAGE_IS_PATHNAME, NULL,
1695 _("The lock comment is a pathname "
1696 "(was -F intended?); use '--force-log' to override"));
1698 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1703 if (opt_state.relocate && (opt_state.depth != svn_depth_unknown))
1705 err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
1706 _("--relocate and --depth are mutually "
1707 "exclusive"));
1708 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1711 /* Only a few commands can accept a revision range; the rest can take at
1712 most one revision number. */
1713 if (subcommand->cmd_func != svn_cl__blame
1714 && subcommand->cmd_func != svn_cl__diff
1715 && subcommand->cmd_func != svn_cl__log
1716 && subcommand->cmd_func != svn_cl__merge)
1718 if (opt_state.end_revision.kind != svn_opt_revision_unspecified)
1720 err = svn_error_create(SVN_ERR_CLIENT_REVISION_RANGE, NULL, NULL);
1721 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1725 /* -N has a different meaning depending on the command */
1726 if (descend == FALSE)
1728 if (subcommand->cmd_func == svn_cl__status)
1730 opt_state.depth = SVN_DEPTH_INFINITY_OR_IMMEDIATES(FALSE);
1732 else if (subcommand->cmd_func == svn_cl__revert
1733 || subcommand->cmd_func == svn_cl__add)
1735 /* In pre-1.5 Subversion, some commands treated -N like
1736 --depth=empty, so . Also, with revert it makes sense to be
1737 especially conservative, since revert can lose data. */
1738 opt_state.depth = svn_depth_empty;
1740 else
1742 opt_state.depth = SVN_DEPTH_INFINITY_OR_FILES(FALSE);
1745 /* Create a client context object. */
1746 command_baton.opt_state = &opt_state;
1747 if ((err = svn_client_create_context(&ctx, pool)))
1748 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1749 command_baton.ctx = ctx;
1751 err = svn_config_get_config(&(ctx->config),
1752 opt_state.config_dir, pool);
1753 if (err)
1755 /* Fallback to default config if the config directory isn't readable. */
1756 if (err->apr_err == APR_EACCES)
1758 svn_handle_warning(stderr, err);
1759 svn_error_clear(err);
1761 else
1762 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1765 cfg = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
1766 APR_HASH_KEY_STRING);
1768 /* Update the options in the config */
1769 /* XXX: Only diff_cmd for now, overlay rest later and stop passing
1770 opt_state altogether? */
1771 if (opt_state.diff_cmd)
1772 svn_config_set(cfg, SVN_CONFIG_SECTION_HELPERS,
1773 SVN_CONFIG_OPTION_DIFF_CMD, opt_state.diff_cmd);
1774 if (opt_state.merge_cmd)
1775 svn_config_set(cfg, SVN_CONFIG_SECTION_HELPERS,
1776 SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd);
1778 /* Check for mutually exclusive args --auto-props and --no-auto-props */
1779 if (opt_state.autoprops && opt_state.no_autoprops)
1781 err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
1782 _("--auto-props and --no-auto-props are "
1783 "mutually exclusive"));
1784 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1787 /* The --reintegrate option is mutually exclusive with both
1788 --ignore-ancestry and --record-only. */
1789 if (opt_state.reintegrate)
1791 if (opt_state.ignore_ancestry)
1793 if (opt_state.record_only)
1795 err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
1796 _("--reintegrate cannot be used with "
1797 "--ignore-ancestry or "
1798 "--record-only"));
1799 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1801 else
1803 err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
1804 _("--reintegrate cannot be used with "
1805 "--ignore-ancestry"));
1806 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1809 else if (opt_state.record_only)
1811 err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
1812 _("--reintegrate cannot be used with "
1813 "--record-only"));
1814 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1818 /* Update auto-props-enable option, and populate the MIME types map,
1819 for add/import commands */
1820 if (subcommand->cmd_func == svn_cl__add
1821 || subcommand->cmd_func == svn_cl__import)
1823 const char *mimetypes_file;
1824 svn_config_get(cfg, &mimetypes_file,
1825 SVN_CONFIG_SECTION_MISCELLANY,
1826 SVN_CONFIG_OPTION_MIMETYPES_FILE, FALSE);
1827 if (mimetypes_file && *mimetypes_file)
1829 if ((err = svn_io_parse_mimetypes_file(&(ctx->mimetypes_map),
1830 mimetypes_file, pool)))
1831 svn_handle_error2(err, stderr, TRUE, "svn: ");
1834 if (opt_state.autoprops)
1836 svn_config_set_bool(cfg, SVN_CONFIG_SECTION_MISCELLANY,
1837 SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, TRUE);
1839 if (opt_state.no_autoprops)
1841 svn_config_set_bool(cfg, SVN_CONFIG_SECTION_MISCELLANY,
1842 SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE);
1846 /* Update the 'keep-locks' runtime option */
1847 if (opt_state.no_unlock)
1848 svn_config_set_bool(cfg, SVN_CONFIG_SECTION_MISCELLANY,
1849 SVN_CONFIG_OPTION_NO_UNLOCK, TRUE);
1851 /* Set the log message callback function. Note that individual
1852 subcommands will populate the ctx->log_msg_baton3. */
1853 ctx->log_msg_func3 = svn_cl__get_log_message;
1855 /* Set up our cancellation support. */
1856 ctx->cancel_func = svn_cl__check_cancel;
1857 apr_signal(SIGINT, signal_handler);
1858 #ifdef SIGBREAK
1859 /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
1860 apr_signal(SIGBREAK, signal_handler);
1861 #endif
1862 #ifdef SIGHUP
1863 apr_signal(SIGHUP, signal_handler);
1864 #endif
1865 #ifdef SIGTERM
1866 apr_signal(SIGTERM, signal_handler);
1867 #endif
1869 #ifdef SIGPIPE
1870 /* Disable SIGPIPE generation for the platforms that have it. */
1871 apr_signal(SIGPIPE, SIG_IGN);
1872 #endif
1874 #ifdef SIGXFSZ
1875 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
1876 * working with large files when compiled against an APR that doesn't have
1877 * large file support will crash the program, which is uncool. */
1878 apr_signal(SIGXFSZ, SIG_IGN);
1879 #endif
1881 /* Set up Authentication stuff. */
1882 if ((err = svn_cmdline_setup_auth_baton(&ab,
1883 opt_state.non_interactive,
1884 opt_state.auth_username,
1885 opt_state.auth_password,
1886 opt_state.config_dir,
1887 opt_state.no_auth_cache,
1888 cfg,
1889 ctx->cancel_func,
1890 ctx->cancel_baton,
1891 pool)))
1892 svn_handle_error2(err, stderr, TRUE, "svn: ");
1894 ctx->auth_baton = ab;
1896 /* Set up conflict resolution callback. */
1897 if ((err = svn_config_get_bool(cfg, &interactive_conflicts,
1898 SVN_CONFIG_SECTION_MISCELLANY,
1899 SVN_CONFIG_OPTION_INTERACTIVE_CONFLICTS,
1900 TRUE))) /* ### interactivity on by default.
1901 we can change this. */
1902 svn_handle_error2(err, stderr, TRUE, "svn: ");
1904 if ((opt_state.accept_which == svn_cl__accept_invalid
1905 && (!interactive_conflicts || opt_state.non_interactive))
1906 || opt_state.accept_which == svn_cl__accept_postpone)
1908 /* If no --accept option at all and we're non-interactive, we're
1909 leaving the conflicts behind, so don't need the callback. Same if
1910 the user said to postpone. */
1911 ctx->conflict_func = NULL;
1912 ctx->conflict_baton = NULL;
1914 else
1916 svn_cmdline_prompt_baton_t *pb = apr_palloc(pool, sizeof(*pb));
1917 pb->cancel_func = ctx->cancel_func;
1918 pb->cancel_baton = ctx->cancel_baton;
1920 if (opt_state.non_interactive)
1922 if (opt_state.accept_which == svn_cl__accept_edit)
1923 return svn_cmdline_handle_exit_error
1924 (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1925 _("--accept=%s incompatible with"
1926 " --non-interactive"), SVN_CL__ACCEPT_EDIT),
1927 pool, "svn: ");
1928 if (opt_state.accept_which == svn_cl__accept_launch)
1929 return svn_cmdline_handle_exit_error
1930 (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1931 _("--accept=%s incompatible with"
1932 " --non-interactive"),
1933 SVN_CL__ACCEPT_LAUNCH),
1934 pool, "svn: ");
1937 ctx->conflict_func = svn_cl__conflict_handler;
1938 ctx->conflict_baton = svn_cl__conflict_baton_make(
1939 opt_state.accept_which,
1940 ctx->config,
1941 opt_state.editor_cmd,
1943 pool);
1946 /* And now we finally run the subcommand. */
1947 err = (*subcommand->cmd_func)(os, &command_baton, pool);
1948 if (err)
1950 svn_error_t *tmp_err;
1952 /* For argument-related problems, suggest using the 'help'
1953 subcommand. */
1954 if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
1955 || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
1957 err = svn_error_quick_wrap(err,
1958 _("Try 'svn help' for more info"));
1960 svn_handle_error2(err, stderr, FALSE, "svn: ");
1962 /* Tell the user about 'svn cleanup' if any error on the stack
1963 was about locked working copies. */
1964 for (tmp_err = err; tmp_err; tmp_err = tmp_err->child)
1965 if (tmp_err->apr_err == SVN_ERR_WC_LOCKED)
1967 svn_error_clear
1968 (svn_cmdline_fputs(_("svn: run 'svn cleanup' to remove locks "
1969 "(type 'svn help cleanup' for details)\n"),
1970 stderr, pool));
1971 break;
1974 svn_error_clear(err);
1975 svn_pool_destroy(pool);
1976 return EXIT_FAILURE;
1978 else
1980 /* Ensure that stdout is flushed, so the user will see any write errors.
1981 This makes sure that output is not silently lost. */
1982 err = svn_cmdline_fflush(stdout);
1983 if (err)
1984 return svn_cmdline_handle_exit_error(err, pool, "svn: ");
1986 svn_pool_destroy(pool);
1987 return EXIT_SUCCESS;