#
# add_file "options.hh"
#
# patch "ChangeLog"
# from [1ca0c140585eb24f3192496cb9194d1cde27c84b]
# to [3fc8d9c3d1147a8a769762b8a75e3ab171f87dd6]
#
# patch "commands.cc"
# from [42a9854e615fff7b96075632e07cb88a6d12ae29]
# to [b049d01f7ac06a0d91533b09248110209b37b52c]
#
# patch "commands.hh"
# from [f2f45ab20f37d33f52054ae343a0b1a099494a35]
# to [88e2abf45cac40d7427a2c657ff1be5d5d7705c6]
#
# patch "monotone.cc"
# from [7b86d611ebb4f89b6fb733b5e322bd500302d020]
# to [0b61014f912a7e1a9fea5022edbefe8f5efad585]
#
# patch "options.hh"
# from []
# to [09fb6eac3b7dd6930f3fc7f564e3e3ef3cc16a79]
#
# patch "tests/t_automate_heads.at"
# from [4c9ac3371e007c84364f5b48004cb4f4fe5604c8]
# to [7f7f54a5b2003ee87db7266b7979a98163df0891]
#
# patch "tests/t_sticky_branch.at"
# from [a54a5cc4d95a7d7b47b4d4458e26868c28dad48e]
# to [0c2988980ecf63dc282283f17ad783839f18e0ca]
#
# patch "tests/t_update_missing.at"
# from [e0dbf59a244cae8582b3a34c1f53418b1eceb869]
# to [9fe6a573758f9e4659a018284aacb77ea3b0805b]
#
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,41 @@
+2005-04-23 Richard Levitte
+
+ * monotone.cc, options.hh: Move the option numbers to options.hh,
+ so they can be easily retrieved by other modules.
+ * monotone.cc: split the options table in global options and
+ command specific options. The former are always understood, while
+ the latter are only understood by the commands that declare it
+ (see below).
+ (my_poptStuffArgFile): There's no need to keep a copy of the
+ stuffed argv. This was really never a problem.
+ (coption_string): New function to find the option string from an
+ option number.
+ (cpp_main): Keep track of which command-specific options were
+ given, and check that the given command really uses them. Make
+ sure that when the help is written, only the appropriate command-
+ specific options are shown. We do this by hacking the command-
+ specific options table.
+ Throw away sub_argvs, as it's not needed any more (and realy never
+ was).
+
+ * commands.cc: Include options.hh to get the option numbers.
+ (commands_ops): New structure to hold the option
+ numbers used by a command.
+ (commands): Use it.
+ (command_options): Function to get the set of command-specific
+ options for a specific command.
+ (CMD): Changed to take a new parameter describing which command-
+ specific options this command takes. Note that for commands that
+ do not take command-specific options, this new parameter must
+ still be given, just left empty.
+ Update all commands with this new parameter.
+ * commands.hh: Declare command_options.
+
+ * tests/t_automate_heads.at: 'automate heads' never used the value
+ of --branch.
+ * tests/t_sticky_branch.at: and neither did 'log'...
+ * tests/t_update_missing.at: nor did 'add'...
+
2005-04-21 Jeremy Cowgar
* tests/t_multiple_heads_msg.at: Now checks to ensure 'multiple head'
--- commands.cc
+++ commands.cc
@@ -43,6 +43,7 @@
#include "automate.hh"
#include "inodeprint.hh"
#include "platform.hh"
+#include "options.hh"
//
// this file defines the task-oriented "top level" commands which can be
@@ -80,16 +81,29 @@
static map cmds;
+ struct command_opts
+ {
+ set opts;
+ command_opts() {}
+ command_opts & operator%(int o)
+ { opts.insert(o); return *this; }
+ command_opts & operator%(command_opts const &o)
+ { opts.insert(o.opts.begin(), o.opts.end()); return *this; }
+ };
+
struct command
{
string name;
string cmdgroup;
string params;
string desc;
+ command_opts options;
command(string const & n,
string const & g,
string const & p,
- string const & d) : name(n), cmdgroup(g), params(p), desc(d)
+ string const & d,
+ command_opts const & o)
+ : name(n), cmdgroup(g), params(p), desc(d), options(o)
{ cmds[n] = this; }
virtual ~command() {}
virtual void exec(app_state & app, vector const & args) = 0;
@@ -222,26 +236,42 @@
}
}
-#define CMD(C, group, params, desc) \
-struct cmd_ ## C : public command \
-{ \
- cmd_ ## C() : command(#C, group, params, desc) \
- {} \
- virtual void exec(app_state & app, \
- vector const & args); \
-}; \
-static cmd_ ## C C ## _cmd; \
-void cmd_ ## C::exec(app_state & app, \
- vector const & args) \
+ set command_options(string const & cmd)
+ {
+ string completed = complete_command(cmd);
+
+ if (cmds.find(completed) != cmds.end())
+ {
+ return cmds[completed]->options.opts;
+ }
+ else
+ {
+ return set();
+ }
+ }
-#define ALIAS(C, realcommand) \
-CMD(C, realcommand##_cmd.cmdgroup, realcommand##_cmd.params, \
- realcommand##_cmd.desc + "\nAlias for " #realcommand) \
-{ \
- process(app, string(#realcommand), args); \
+#define CMD(C, group, params, desc, opts) \
+struct cmd_ ## C : public command \
+{ \
+ cmd_ ## C() : command(#C, group, params, desc, \
+ command_opts() opts) \
+ {} \
+ virtual void exec(app_state & app, \
+ vector const & args); \
+}; \
+static cmd_ ## C C ## _cmd; \
+void cmd_ ## C::exec(app_state & app, \
+ vector const & args) \
+
+#define ALIAS(C, realcommand) \
+CMD(C, realcommand##_cmd.cmdgroup, realcommand##_cmd.params, \
+ realcommand##_cmd.desc + "\nAlias for " #realcommand, \
+ % realcommand##_cmd.options) \
+{ \
+ process(app, string(#realcommand), args); \
}
-static void
+static void
get_work_path(local_path & w_path)
{
w_path = (mkpath(book_keeping_dir) / mkpath(work_file_name)).string();
@@ -1175,7 +1205,8 @@
#undef PRINT_INDENTED_SET
}
-CMD(genkey, "key and cert", "KEYID", "generate an RSA key-pair")
+CMD(genkey, "key and cert", "KEYID", "generate an RSA key-pair",
+ % OPT_ROOT % OPT_DB_NAME % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (args.size() != 1)
throw usage(name);
@@ -1197,7 +1228,8 @@
guard.commit();
}
-CMD(dropkey, "key and cert", "KEYID", "drop a public and private key")
+CMD(dropkey, "key and cert", "KEYID", "drop a public and private key",
+ % OPT_ROOT % OPT_DB_NAME)
{
bool key_deleted = false;
@@ -1229,7 +1261,8 @@
guard.commit();
}
-CMD(chkeypass, "key and cert", "KEYID", "change passphrase of a private RSA key")
+CMD(chkeypass, "key and cert", "KEYID", "change passphrase of a private RSA key",
+ % OPT_ROOT % OPT_DB_NAME % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (args.size() != 1)
throw usage(name);
@@ -1252,7 +1285,8 @@
}
CMD(cert, "key and cert", "REVISION CERTNAME [CERTVAL]",
- "create a cert for a revision")
+ "create a cert for a revision",
+ % OPT_ROOT % OPT_DB_NAME)
{
if ((args.size() != 3) && (args.size() != 2))
throw usage(name);
@@ -1293,7 +1327,8 @@
CMD(trusted, "key and cert", "REVISION NAME VALUE SIGNER1 [SIGNER2 [...]]",
"test whether a hypothetical cert would be trusted\n"
- "by current settings")
+ "by current settings",
+ % OPT_ROOT % OPT_DB_NAME % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (args.size() < 4)
throw usage(name);
@@ -1330,7 +1365,8 @@
}
CMD(tag, "review", "REVISION TAGNAME",
- "put a symbolic tag cert on a revision version")
+ "put a symbolic tag cert on a revision version",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 2)
throw usage(name);
@@ -1343,7 +1379,8 @@
CMD(testresult, "review", "ID (pass|fail|true|false|yes|no|1|0)",
- "note the results of running a test on a revision")
+ "note the results of running a test on a revision",
+ % OPT_ROOT % OPT_DB_NAME )
{
if (args.size() != 2)
throw usage(name);
@@ -1355,7 +1392,8 @@
}
CMD(approve, "review", "REVISION",
- "approve of a particular revision")
+ "approve of a particular revision",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -1371,7 +1409,8 @@
CMD(disapprove, "review", "REVISION",
- "disapprove of a particular revision")
+ "disapprove of a particular revision",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -1416,7 +1455,8 @@
}
CMD(comment, "review", "REVISION [COMMENT]",
- "comment on a particular revision")
+ "comment on a particular revision",
+ % OPT_ROOT % OPT_DB_NAME % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (args.size() != 1 && args.size() != 2)
throw usage(name);
@@ -1439,7 +1479,8 @@
-CMD(add, "working copy", "PATH...", "add files to working copy")
+CMD(add, "working copy", "PATH...", "add files to working copy",
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (args.size() < 1)
throw usage(name);
@@ -1463,7 +1504,8 @@
update_any_attrs(app);
}
-CMD(drop, "working copy", "PATH...", "drop files from working copy")
+CMD(drop, "working copy", "PATH...", "drop files from working copy",
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (args.size() < 1)
throw usage(name);
@@ -1488,7 +1530,8 @@
}
-CMD(rename, "working copy", "SRC DST", "rename entries in the working copy")
+CMD(rename, "working copy", "SRC DST", "rename entries in the working copy",
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (args.size() != 2)
throw usage(name);
@@ -1514,7 +1557,8 @@
// (such as automated processes might want to do).
CMD(fcommit, "tree", "REVISION FILENAME [LOG_MESSAGE]",
- "commit change to a single file")
+ "commit change to a single file",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 2 && args.size() != 3)
throw usage(name);
@@ -1596,7 +1640,8 @@
}
-CMD(fload, "debug", "", "load file contents into db")
+CMD(fload, "debug", "", "load file contents into db",
+ % OPT_ROOT % OPT_DB_NAME)
{
string s = get_stdin();
@@ -1609,7 +1654,8 @@
dbw.consume_file_data(f_id, f_data);
}
-CMD(fmerge, "debug", " ", "merge 3 files and output result")
+CMD(fmerge, "debug", " ", "merge 3 files and output result",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 3)
throw usage(name);
@@ -1640,7 +1686,8 @@
}
-CMD(status, "informative", "[PATH]...", "show status of working copy")
+CMD(status, "informative", "[PATH]...", "show status of working copy",
+ % OPT_ROOT % OPT_DB_NAME)
{
revision_set rs;
manifest_map m_old, m_new;
@@ -1655,7 +1702,8 @@
}
CMD(identify, "working copy", "[PATH]",
- "calculate identity of PATH or stdin")
+ "calculate identity of PATH or stdin",
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (!(args.size() == 0 || args.size() == 1))
throw usage(name);
@@ -1679,7 +1727,8 @@
CMD(cat, "informative",
"(file|manifest|revision) [ID]\n"
"file REVISION FILENAME",
- "write file, manifest, or revision from database to stdout")
+ "write file, manifest, or revision from database to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() < 1 || args.size() > 3)
throw usage(name);
@@ -1781,7 +1830,9 @@
CMD(checkout, "tree", "REVISION DIRECTORY\nDIRECTORY\n",
- "check out revision from database into directory")
+ "check out revision from database into directory",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
revision_id ident;
@@ -1875,7 +1926,8 @@
ALIAS(co, checkout)
-CMD(heads, "tree", "", "show unmerged head revisions of branch")
+CMD(heads, "tree", "", "show unmerged head revisions of branch",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME)
{
set heads;
if (args.size() != 0)
@@ -2095,7 +2147,8 @@
"ignored\n"
"missing",
"show database objects, or the current working copy manifest,\n"
- "or unknown, intentionally ignored, or missing state files")
+ "or unknown, intentionally ignored, or missing state files",
+ % OPT_ROOT % OPT_DB_NAME % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
if (args.size() == 0)
throw usage(name);
@@ -2130,7 +2183,8 @@
ALIAS(ls, list)
-CMD(mdelta, "packet i/o", "OLDID NEWID", "write manifest delta packet to stdout")
+CMD(mdelta, "packet i/o", "OLDID NEWID", "write manifest delta packet to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 2)
throw usage(name);
@@ -2154,7 +2208,8 @@
manifest_delta(del));
}
-CMD(fdelta, "packet i/o", "OLDID NEWID", "write file delta packet to stdout")
+CMD(fdelta, "packet i/o", "OLDID NEWID", "write file delta packet to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 2)
throw usage(name);
@@ -2176,7 +2231,8 @@
pw.consume_file_delta(f_old_id, f_new_id, file_delta(del));
}
-CMD(rdata, "packet i/o", "ID", "write revision data packet to stdout")
+CMD(rdata, "packet i/o", "ID", "write revision data packet to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -2193,7 +2249,8 @@
pw.consume_revision_data(r_id, r_data);
}
-CMD(mdata, "packet i/o", "ID", "write manifest data packet to stdout")
+CMD(mdata, "packet i/o", "ID", "write manifest data packet to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -2211,7 +2268,8 @@
}
-CMD(fdata, "packet i/o", "ID", "write file data packet to stdout")
+CMD(fdata, "packet i/o", "ID", "write file data packet to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -2229,7 +2287,8 @@
}
-CMD(certs, "packet i/o", "ID", "write cert packets to stdout")
+CMD(certs, "packet i/o", "ID", "write cert packets to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -2246,7 +2305,8 @@
pw.consume_revision_cert(idx(certs, i));
}
-CMD(pubkey, "packet i/o", "ID", "write public key packet to stdout")
+CMD(pubkey, "packet i/o", "ID", "write public key packet to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -2261,7 +2321,8 @@
pw.consume_public_key(ident, key);
}
-CMD(privkey, "packet i/o", "ID", "write private key packet to stdout")
+CMD(privkey, "packet i/o", "ID", "write private key packet to stdout",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -2277,7 +2338,8 @@
}
-CMD(read, "packet i/o", "", "read packets from stdin")
+CMD(read, "packet i/o", "", "read packets from stdin",
+ % OPT_ROOT % OPT_DB_NAME)
{
packet_db_writer dbw(app, true);
size_t count = read_packets(cin, dbw);
@@ -2290,7 +2352,8 @@
CMD(reindex, "network", "",
- "rebuild the indices used to sync over the network")
+ "rebuild the indices used to sync over the network",
+ % OPT_ROOT % OPT_DB_NAME % OPT_TICKER)
{
if (args.size() > 0)
throw usage(name);
@@ -2359,7 +2422,8 @@
}
CMD(push, "network", "[ADDRESS[:PORTNUMBER] [COLLECTION]]",
- "push COLLECTION to netsync server at ADDRESS")
+ "push COLLECTION to netsync server at ADDRESS",
+ % OPT_ROOT % OPT_DB_NAME % OPT_KEY_NAME % OPT_TICKER)
{
utf8 addr;
vector collections;
@@ -2373,7 +2437,8 @@
}
CMD(pull, "network", "[ADDRESS[:PORTNUMBER] [COLLECTION]]",
- "pull COLLECTION from netsync server at ADDRESS")
+ "pull COLLECTION from netsync server at ADDRESS",
+ % OPT_ROOT % OPT_DB_NAME % OPT_KEY_NAME % OPT_TICKER)
{
utf8 addr;
vector collections;
@@ -2386,7 +2451,8 @@
}
CMD(sync, "network", "[ADDRESS[:PORTNUMBER] [COLLECTION]]",
- "sync COLLECTION with netsync server at ADDRESS")
+ "sync COLLECTION with netsync server at ADDRESS",
+ % OPT_ROOT % OPT_DB_NAME % OPT_KEY_NAME % OPT_TICKER)
{
utf8 addr;
vector collections;
@@ -2400,7 +2466,8 @@
}
CMD(serve, "network", "ADDRESS[:PORTNUMBER] COLLECTION...",
- "listen on ADDRESS and serve COLLECTION to connecting clients")
+ "listen on ADDRESS and serve COLLECTION to connecting clients",
+ % OPT_ROOT % OPT_DB_NAME % OPT_KEY_NAME % OPT_NORC % OPT_NOSTD % OPT_RCFILE)
{
if (args.size() < 2)
throw usage(name);
@@ -2432,7 +2499,8 @@
"changesetify\n"
"rebuild\n"
"set_epoch BRANCH EPOCH\n",
- "manipulate database state")
+ "manipulate database state",
+ % OPT_ROOT % OPT_DB_NAME % OPT_TICKER)
{
if (args.size() == 1)
{
@@ -2481,7 +2549,7 @@
}
CMD(attr, "working copy", "set FILE ATTR VALUE\nget FILE [ATTR]\ndrop FILE",
- "set, get or drop file attributes")
+ "set, get or drop file attributes",)
{
if (args.size() < 2 || args.size() > 4)
throw usage(name);
@@ -2606,8 +2674,10 @@
I(false);
}
-CMD(commit, "working copy", "[--message=STRING] [PATH]...",
- "commit working copy to database")
+CMD(commit, "working copy", "[PATH]...",
+ "commit working copy to database",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME % OPT_MESSAGE % OPT_DATE % OPT_AUTHOR
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
string log_message("");
revision_set rs;
@@ -2652,11 +2722,11 @@
N(log_message.find_first_not_of(" \r\t\n") != string::npos,
F("empty log message"));
-
+
transaction_guard guard(app.db);
{
packet_db_writer dbw(app);
-
+
if (app.db.revision_exists(rid))
{
W(F("revision %s already in database\n") % rid);
@@ -2665,11 +2735,11 @@
{
// new revision
L(F("inserting new revision %s\n") % rid);
-
+
I(rs.edges.size() == 1);
edge_map::const_iterator edge = rs.edges.begin();
I(edge != rs.edges.end());
-
+
// process manifest delta or new manifest
if (app.db.manifest_version_exists(rs.new_manifest))
{
@@ -2677,13 +2747,13 @@
}
else if (app.db.manifest_version_exists(edge_old_manifest(edge)))
{
- L(F("inserting manifest delta %s -> %s\n")
- % edge_old_manifest(edge)
+ L(F("inserting manifest delta %s -> %s\n")
+ % edge_old_manifest(edge)
% rs.new_manifest);
delta del;
diff(m_old, m_new, del);
- dbw.consume_manifest_delta(edge_old_manifest(edge),
- rs.new_manifest,
+ dbw.consume_manifest_delta(edge_old_manifest(edge),
+ rs.new_manifest,
manifest_delta(del));
}
else
@@ -2693,7 +2763,7 @@
write_manifest_map(m_new, m_new_data);
dbw.consume_manifest_data(rs.new_manifest, m_new_data);
}
-
+
// process file deltas or new files
for (change_set::delta_map::const_iterator i = edge_changes(edge).deltas.begin();
i != edge_changes(edge).deltas.end(); ++i)
@@ -2701,13 +2771,13 @@
if (! delta_entry_src(i).inner()().empty() &&
app.db.file_version_exists(delta_entry_dst(i)))
{
- L(F("skipping file delta %s, already in database\n")
+ L(F("skipping file delta %s, already in database\n")
% delta_entry_dst(i));
}
else if (! delta_entry_src(i).inner()().empty() &&
app.db.file_version_exists(delta_entry_src(i)))
{
- L(F("inserting delta %s -> %s\n")
+ L(F("inserting delta %s -> %s\n")
% delta_entry_src(i) % delta_entry_dst(i));
file_data old_data;
data new_data;
@@ -2721,7 +2791,7 @@
% delta_entry_path(i));
delta del;
diff(old_data.inner(), new_data, del);
- dbw.consume_file_delta(delta_entry_src(i),
+ dbw.consume_file_delta(delta_entry_src(i),
delta_entry_dst(i),
file_delta(del));
}
@@ -2820,7 +2890,7 @@
}
else
{
- read_localized_data(delta_entry_path(i),
+ read_localized_data(delta_entry_path(i),
unpacked, app.lua);
}
@@ -3030,18 +3100,23 @@
}
CMD(cdiff, "informative", "[--revision=REVISION [--revision=REVISION]] [PATH]...",
- "show current context diffs on stdout")
+ "show current context diffs on stdout",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME % OPT_REVISION
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
do_diff(name, app, args, context_diff);
}
CMD(diff, "informative", "[--revision=REVISION [--revision=REVISION]] [PATH]...",
- "show current unified diffs on stdout")
+ "show current unified diffs on stdout",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME % OPT_REVISION
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
do_diff(name, app, args, unified_diff);
}
-CMD(lca, "debug", "LEFT RIGHT", "print least common ancestor")
+CMD(lca, "debug", "LEFT RIGHT", "print least common ancestor",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 2)
throw usage(name);
@@ -3058,7 +3133,8 @@
}
-CMD(lcad, "debug", "LEFT RIGHT", "print least common ancestor / dominator")
+CMD(lcad, "debug", "LEFT RIGHT", "print least common ancestor / dominator",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 2)
throw usage(name);
@@ -3075,7 +3151,8 @@
}
-CMD(agraph, "debug", "", "dump ancestry graph to stdout in VCG format")
+CMD(agraph, "debug", "", "dump ancestry graph to stdout in VCG format",
+ % OPT_ROOT % OPT_DB_NAME)
{
set nodes;
multimap branches;
@@ -3180,7 +3257,9 @@
// cout << "change set '" << name << "'\n" << dat << endl;
// }
-CMD(update, "working copy", "\nREVISION", "update working copy to be based off another revision")
+CMD(update, "working copy", "\nREVISION", "update working copy to be based off another revision",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME
+ % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
manifest_map m_old, m_ancestor, m_working, m_chosen;
manifest_id m_ancestor_id, m_chosen_id;
@@ -3422,7 +3501,8 @@
}
-CMD(merge, "tree", "", "merge unmerged heads of branch")
+CMD(merge, "tree", "", "merge unmerged heads of branch",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME)
{
set heads;
@@ -3471,7 +3551,8 @@
}
CMD(propagate, "tree", "SOURCE-BRANCH DEST-BRANCH",
- "merge from one branch to another asymmetrically")
+ "merge from one branch to another asymmetrically",
+ % OPT_ROOT % OPT_DB_NAME)
{
// this is a special merge operator, but very useful for people maintaining
// "slightly disparate but related" trees. it does a one-way merge; less
@@ -3554,7 +3635,8 @@
}
CMD(explicit_merge, "tree", "LEFT-REVISION RIGHT-REVISION DEST-BRANCH\nLEFT-REVISION RIGHT-REVISION COMMON-ANCESTOR DEST-BRANCH",
- "merge two explicitly given revisions, placing result in given branch")
+ "merge two explicitly given revisions, placing result in given branch",
+ % OPT_ROOT % OPT_DB_NAME)
{
revision_id left, right, ancestor;
string branch;
@@ -3609,7 +3691,9 @@
P(F("[merged] %s\n") % merged);
}
-CMD(complete, "informative", "(revision|manifest|file) PARTIAL-ID", "complete partial id")
+CMD(complete, "informative", "(revision|manifest|file) PARTIAL-ID",
+ "complete partial id",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 2)
throw usage(name);
@@ -3650,7 +3734,8 @@
CMD(revert, "working copy", "[PATH]...",
- "revert file(s), dir(s) or entire working copy")
+ "revert file(s), dir(s) or entire working copy",
+ % OPT_ROOT % OPT_DB_NAME % OPT_NOSTD % OPT_NORC % OPT_RCFILE)
{
manifest_map m_old;
revision_id old_revision_id;
@@ -3711,7 +3796,8 @@
CMD(rcs_import, "debug", "RCSFILE...",
"import all versions in RCS files\n"
- "this command doesn't reconstruct revisions. you probably want cvs_import")
+ "this command doesn't reconstruct revisions. you probably want cvs_import",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME)
{
if (args.size() < 1)
throw usage(name);
@@ -3726,7 +3812,8 @@
}
-CMD(cvs_import, "rcs", "CVSROOT", "import all versions in CVS repository")
+CMD(cvs_import, "rcs", "CVSROOT", "import all versions in CVS repository",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME)
{
if (args.size() != 1)
throw usage(name);
@@ -3755,7 +3842,8 @@
}
}
-CMD(log, "informative", "[ID] [file]", "print history in reverse order starting from 'ID' (filtering by 'file')")
+CMD(log, "informative", "[ID] [file]", "print history in reverse order starting from 'ID' (filtering by 'file')",
+ % OPT_ROOT % OPT_DB_NAME % OPT_DEPTH)
{
revision_set rev;
revision_id rid;
@@ -3903,7 +3991,8 @@
}
-CMD(setup, "tree", "DIRECTORY", "setup a new working copy directory")
+CMD(setup, "tree", "DIRECTORY", "setup a new working copy directory",
+ % OPT_ROOT % OPT_DB_NAME % OPT_BRANCH_NAME % OPT_KEY_NAME)
{
string dir;
@@ -3925,7 +4014,8 @@
"toposort [REV1 [REV2 [REV3 [...]]]]\n"
"ancestry_difference NEW_REV [OLD_REV1 [OLD_REV2 [...]]]\n"
"leaves",
- "automation interface")
+ "automation interface",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() == 0)
throw usage(name);
@@ -3939,7 +4029,8 @@
}
CMD(set, "vars", "DOMAIN NAME VALUE",
- "set the database variable NAME to VALUE, in domain DOMAIN")
+ "set the database variable NAME to VALUE, in domain DOMAIN",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 3)
throw usage(name);
@@ -3954,7 +4045,8 @@
}
CMD(unset, "vars", "DOMAIN NAME",
- "remove the database variable NAME in domain DOMAIN")
+ "remove the database variable NAME in domain DOMAIN",
+ % OPT_ROOT % OPT_DB_NAME)
{
if (args.size() != 2)
throw usage(name);
--- commands.hh
+++ commands.hh
@@ -25,6 +25,7 @@
namespace commands {
void explain_usage(std::string const & cmd, std::ostream & out);
int process(app_state & app, std::string const & cmd, std::vector const & args);
+ std::set command_options(std::string const & cmd);
typedef enum
{
sel_author,
--- monotone.cc
+++ monotone.cc
@@ -26,28 +26,8 @@
#include "transforms.hh"
#include "ui.hh"
#include "mt_version.hh"
+#include "options.hh"
-#define OPT_DEBUG 1
-#define OPT_HELP 2
-#define OPT_NOSTD 3
-#define OPT_NORC 4
-#define OPT_RCFILE 5
-#define OPT_DB_NAME 6
-#define OPT_KEY_NAME 7
-#define OPT_BRANCH_NAME 8
-#define OPT_QUIET 9
-#define OPT_VERSION 10
-#define OPT_DUMP 11
-#define OPT_TICKER 12
-#define OPT_FULL_VERSION 13
-#define OPT_REVISION 14
-#define OPT_MESSAGE 15
-#define OPT_ROOT 16
-#define OPT_DEPTH 17
-#define OPT_ARGFILE 18
-#define OPT_DATE 19
-#define OPT_AUTHOR 20
-
// main option processing and exception handling code
using namespace std;
@@ -55,28 +35,44 @@
char * argstr = NULL;
long arglong = 0;
+// Options are divide into two tables. The first one is command-specific
+// options (hence the `c' in `coptions'). The second is the global one
+// with options that aren't tied to specific commands.
+
+struct poptOption coptions[] =
+ {
+ {"branch", 'b', POPT_ARG_STRING, &argstr, OPT_BRANCH_NAME, "select branch cert for operation", NULL},
+ {"ticker", 0, POPT_ARG_STRING, &argstr, OPT_TICKER, "set ticker style (count|dot|none) [count]", NULL},
+ {"revision", 'r', POPT_ARG_STRING, &argstr, OPT_REVISION, "select revision id for operation", NULL},
+ {"message", 'm', POPT_ARG_STRING, &argstr, OPT_MESSAGE, "set commit changelog message", NULL},
+ {"date", 0, POPT_ARG_STRING, &argstr, OPT_DATE, "override date/time for commit", NULL},
+ {"author", 0, POPT_ARG_STRING, &argstr, OPT_AUTHOR, "override author for commit", NULL},
+ {"depth", 0, POPT_ARG_LONG, &arglong, OPT_DEPTH, "limit the log output to the given number of entries", NULL},
+ { NULL, 0, 0, NULL, 0, NULL, NULL }
+ };
+
struct poptOption options[] =
{
{"debug", 0, POPT_ARG_NONE, NULL, OPT_DEBUG, "print debug log to stderr while running", NULL},
{"dump", 0, POPT_ARG_STRING, &argstr, OPT_DUMP, "file to dump debugging log to, on failure", NULL},
{"quiet", 0, POPT_ARG_NONE, NULL, OPT_QUIET, "suppress log and progress messages", NULL},
{"help", 0, POPT_ARG_NONE, NULL, OPT_HELP, "display help message", NULL},
+ {"version", 0, POPT_ARG_NONE, NULL, OPT_VERSION, "print version number, then exit", NULL},
+ {"full-version", 0, POPT_ARG_NONE, NULL, OPT_FULL_VERSION, "print detailed version number, then exit", NULL},
+ {"xargs", '@', POPT_ARG_STRING, &argstr, OPT_ARGFILE, "insert command line arguments taken from the given file", NULL},
+
+ // Personally, I think these should be command-specific. However, the
+ // monotone test suite requires these to be global, it seems...
+ // -- Richard Levitte
{"nostd", 0, POPT_ARG_NONE, NULL, OPT_NOSTD, "do not load standard lua hooks", NULL},
{"norc", 0, POPT_ARG_NONE, NULL, OPT_NORC, "do not load ~/.monotone/monotonerc or MT/monotonerc lua files", NULL},
{"rcfile", 0, POPT_ARG_STRING, &argstr, OPT_RCFILE, "load extra rc file", NULL},
{"key", 'k', POPT_ARG_STRING, &argstr, OPT_KEY_NAME, "set key for signatures", NULL},
{"db", 'd', POPT_ARG_STRING, &argstr, OPT_DB_NAME, "set name of database", NULL},
- {"branch", 'b', POPT_ARG_STRING, &argstr, OPT_BRANCH_NAME, "select branch cert for operation", NULL},
- {"version", 0, POPT_ARG_NONE, NULL, OPT_VERSION, "print version number, then exit", NULL},
- {"full-version", 0, POPT_ARG_NONE, NULL, OPT_FULL_VERSION, "print detailed version number, then exit", NULL},
- {"ticker", 0, POPT_ARG_STRING, &argstr, OPT_TICKER, "set ticker style (count|dot|none) [count]", NULL},
- {"revision", 'r', POPT_ARG_STRING, &argstr, OPT_REVISION, "select revision id for operation", NULL},
- {"message", 'm', POPT_ARG_STRING, &argstr, OPT_MESSAGE, "set commit changelog message", NULL},
- {"date", 0, POPT_ARG_STRING, &argstr, OPT_DATE, "override date/time for commit", NULL},
- {"author", 0, POPT_ARG_STRING, &argstr, OPT_AUTHOR, "override author for commit", NULL},
{"root", 0, POPT_ARG_STRING, &argstr, OPT_ROOT, "limit search for working copy to specified root", NULL},
- {"depth", 0, POPT_ARG_LONG, &arglong, OPT_DEPTH, "limit the log output to the given number of entries", NULL},
- {"xargs", '@', POPT_ARG_STRING, &argstr, OPT_ARGFILE, "insert command line arguments taken from the given file", NULL},
+
+ // Use the coptions table as well.
+ { NULL, 0, POPT_ARG_INCLUDE_TABLE, coptions, 0, "Command-specific options", NULL },
{ NULL, 0, 0, NULL, 0, NULL, NULL }
};
@@ -162,7 +158,7 @@
// Read arguments from a file. The special file '-' means stdin.
// Returned value must be free()'d, after arg parsing has completed.
-static const char **
+static void
my_poptStuffArgFile(poptContext con, utf8 const & filename)
{
utf8 argstr;
@@ -172,7 +168,7 @@
external ext(dat());
system_to_utf8(ext, argstr);
}
-
+
const char **argv = 0;
int argc = 0;
int rc;
@@ -192,19 +188,28 @@
F("weird error when stuffing arguments read from %s: %s\n")
% filename % poptStrerror(rc));
}
- else
- {
- free(argv); // just in case there was something...
- argv = 0;
- }
- return argv;
+ free(argv);
}
+static string
+coption_string(int o)
+{
+ char buf[2] = { 0,0 };
+ for(struct poptOption *opt = coptions; opt->val; opt++)
+ if (o == opt->val)
+ {
+ buf[0] = opt->shortName;
+ return opt->longName
+ ? string("--") + string(opt->longName)
+ : string("-") + string(buf);
+ }
+ return string();
+}
+
int
cpp_main(int argc, char ** argv)
{
-
clean_shutdown = false;
atexit(&dumper);
@@ -215,7 +220,7 @@
setlocale(LC_MESSAGES, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
-
+
{
std::ostringstream cmdline_ss;
for (int i = 0; i < argc; ++i)
@@ -225,42 +230,45 @@
cmdline_ss << "'" << argv[i] << "'";
}
L(F("command line: %s\n") % cmdline_ss.str());
- }
+ }
L(F("set locale: LC_CTYPE=%s, LC_MESSAGES=%s\n")
% (setlocale(LC_CTYPE, NULL) == NULL ? "n/a" : setlocale(LC_CTYPE, NULL))
% (setlocale(LC_MESSAGES, NULL) == NULL ? "n/a" : setlocale(LC_CTYPE, NULL)));
-
+
// decode all argv values into a UTF-8 array
save_initial_path();
utf8_argv uv(argc, argv);
// prepare for arg parsing
-
+
cleanup_ptr
ctx(poptGetContext(NULL, argc, (char const **) uv.argv, options, 0),
&my_poptFreeContext);
+ set local_options;
+ for (poptOption *opt = coptions; opt->val; opt++)
+ local_options.insert(opt->val);
+
// process main program options
int ret = 0;
int opt;
bool requested_help = false;
+ set used_local_options;
- // keep a list of argv vectors created by get_args_from_file, since they
- // must be individually free()'d, but not until arg parsing is done.
- std::vector sub_argvs;
-
poptSetOtherOptionHelp(ctx(), "[OPTION...] command [ARGS...]\n");
try
{
-
app_state app;
while ((opt = poptGetNextOpt(ctx())) > 0)
{
+ if (local_options.find(opt) != local_options.end())
+ used_local_options.insert(opt);
+
switch(opt)
{
case OPT_DEBUG:
@@ -345,8 +353,7 @@
break;
case OPT_ARGFILE:
- sub_argvs.push_back(my_poptStuffArgFile(ctx(),
- utf8(string(argstr))));
+ my_poptStuffArgFile(ctx(), utf8(string(argstr)));
break;
case OPT_HELP:
@@ -391,22 +398,43 @@
else
{
string cmd(poptGetArg(ctx()));
+
+ // Make sure the local options used are really used by the
+ // given command.
+ set command_options = commands::command_options(cmd);
+ for (set::const_iterator i = used_local_options.begin();
+ i != used_local_options.end(); ++i)
+ N(command_options.find(*i) != command_options.end(),
+ F("monotone %s doesn't use the option %s")
+ % cmd % coption_string(*i));
+
vector args;
while(poptPeekArg(ctx()))
{
args.push_back(utf8(string(poptGetArg(ctx()))));
}
- // we've copied everything we want from the command line into our
- // own data structures, so we can finally delete popt's malloc'ed
- // argv stuff.
- for (std::vector::const_iterator i = sub_argvs.begin();
- i != sub_argvs.end(); ++i)
- free(*i);
ret = commands::process(app, cmd, args);
}
}
catch (usage & u)
{
+ // Make sure to hide documentation that's not part of
+ // the current command.
+ set command_options = commands::command_options(u.which);
+ for (poptOption *o = coptions; o->val != 0; o++)
+ {
+ if (command_options.find(o->val) != command_options.end())
+ {
+ o->argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
+ L(F("Removed 'hidden' from option # %d\n") % o->argInfo);
+ }
+ else
+ {
+ o->argInfo |= POPT_ARGFLAG_DOC_HIDDEN;
+ L(F("Added 'hidden' to option # %d\n") % o->argInfo);
+ }
+ }
+
poptPrintHelp(ctx(), stdout, 0);
cout << endl;
commands::explain_usage(u.which, cout);
--- options.hh
+++ options.hh
@@ -0,0 +1,30 @@
+// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
+// copyright (C) 2002, 2003 graydon hoare
+// copyright (C) 2005 Richard Levitte
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+#include "popt/popt.h"
+
+#define OPT_DEBUG 1
+#define OPT_HELP 2
+#define OPT_NOSTD 3
+#define OPT_NORC 4
+#define OPT_RCFILE 5
+#define OPT_DB_NAME 6
+#define OPT_KEY_NAME 7
+#define OPT_BRANCH_NAME 8
+#define OPT_QUIET 9
+#define OPT_VERSION 10
+#define OPT_DUMP 11
+#define OPT_TICKER 12
+#define OPT_FULL_VERSION 13
+#define OPT_REVISION 14
+#define OPT_MESSAGE 15
+#define OPT_ROOT 16
+#define OPT_DEPTH 17
+#define OPT_ARGFILE 18
+#define OPT_DATE 19
+#define OPT_AUTHOR 20
+
--- tests/t_automate_heads.at
+++ tests/t_automate_heads.at
@@ -36,7 +36,7 @@
AT_CHECK(sort wanted_heads_unsorted >wanted_heads)
-AT_CHECK(MONOTONE --branch=otherbranch automate heads testbranch, [], [stdout], [ignore])
+AT_CHECK(MONOTONE automate heads testbranch, [], [stdout], [ignore])
AT_CHECK(CANONICALISE(stdout))
AT_CHECK(cmp wanted_heads stdout)
--- tests/t_sticky_branch.at
+++ tests/t_sticky_branch.at
@@ -16,7 +16,7 @@
AT_CHECK(cd codir && MONOTONE commit --message=foo, [], [ignore], [ignore])
# log doesn't affect given branch
-AT_CHECK(cd codir && MONOTONE log --branch=otherbranch, [], [ignore], [ignore])
+AT_CHECK(cd codir && MONOTONE log, [], [ignore], [ignore])
AT_DATA(codir/foo, [more more
])
AT_CHECK(cd codir && MONOTONE commit --message=foo, [], [ignore], [ignore])
--- tests/t_update_missing.at
+++ tests/t_update_missing.at
@@ -6,7 +6,7 @@
touch a
-AT_CHECK(MONOTONE --branch=a add a, [], [ignore], [ignore])
+AT_CHECK(MONOTONE add a, [], [ignore], [ignore])
AT_CHECK(MONOTONE --branch=a commit --message "commit a", [], [ignore], [ignore])
ROOT_R_SHA=`BASE_REVISION`
@@ -15,7 +15,7 @@
mkdir b
touch b/c
-AT_CHECK(MONOTONE --branch=a add b, [], [ignore], [ignore])
+AT_CHECK(MONOTONE add b, [], [ignore], [ignore])
AT_CHECK(MONOTONE --branch=a commit --message "commit b/c", [], [ignore], [ignore])
PROBE_NODE(a, $ROOT_R_SHA, $ROOT_F_SHA)
@@ -24,7 +24,7 @@
rmdir b
touch d
-AT_CHECK(MONOTONE --branch=a add d, [], [ignore], [ignore])
+AT_CHECK(MONOTONE add d, [], [ignore], [ignore])
AT_CHECK(MONOTONE --branch=a commit --message "commit d", [], [ignore], [ignore])
AT_CHECK(MONOTONE --branch=a merge, [], [ignore], [ignore])