# # add_file "selectors.cc" # # add_file "selectors.hh" # # add_file "tests/t_automate_select.at" # # patch "ChangeLog" # from [5dcfd55a684cf7b13eeb0af6c73ad78f4e058903] # to [f3205a41fa35adee3a51524c7cbbe3e6875b7d17] # # patch "Makefile.am" # from [1f3917db0f85d6d44fde8522bd6b3baefe369cc5] # to [173c6adb631418d176e6a98563ffb3c431d8adeb] # # patch "automate.cc" # from [9a12b01deba6551f2bb0cce3d99c44d6d4dd472b] # to [9eb7fae312c0ac8f8886cfc038e8df41df71d29b] # # patch "commands.cc" # from [4dc702d5085d7a7652585d4d549f659a16c4e6a1] # to [29e00cb074065130e97b16828db04e616d838660] # # patch "commands.hh" # from [47e15e3250b284fb4308d4acfb3cc8e5cd4db749] # to [fcf355949c96243d66f874e1e57a9c3f5bc98777] # # patch "database.cc" # from [4f80a2f66a26787de8cae7d7052fe37a299ce9e3] # to [8bd9d2d49322d496a8cd4ed3e3a8f802eb0d9ed2] # # patch "database.hh" # from [7c532dedbdcb9c8dd9a19b7a80c3ce63a855f3c9] # to [794783807386029f0a6d8067e2a7bb0eba88b0a2] # # patch "monotone.texi" # from [39658b68662d6e52e7554098c647d20f08f140dd] # to [fc3e57e3608d16ca6aad878c95961e8975b8bcda] # # patch "selectors.cc" # from [] # to [e0fe016aca3d1fcc1c64d3f7543b4e2bc7419e64] # # patch "selectors.hh" # from [] # to [0a31a5fa5e4adaf3e65f8c0935fb58e88cee0aac] # # patch "tests/t_automate_select.at" # from [] # to [6ca40daab6a3893a842ab2042d9419589a36826e] # # patch "testsuite.at" # from [c95827be5d2e1b6c8f78a6670c09b041c3717b64] # to [01070f5e50431da13698d08b8682d6b64a2e14cb] # --- ChangeLog +++ ChangeLog @@ -1,3 +1,19 @@ +2005-04-28 Richard Levitte + + * commands.cc, commands.hh: Selector functions and type are moved + to... + * selectors.cc, selectors.hh: ... these files. + * database.cc, database.hh: Adapt to this change. + * automate.cc (automate_select): New function, implements + 'automate select'. + (automate_command): Use it. + * monotone.texi (Automation): Document it. + + * tests/t_automate_select.at: New test. + * testsuite.at: Use it. + + * Makefile.am (MOST_SOURCES): reorganise. Add selectors.{cc,hh}. + 2005-04-27 Richard Levitte * quick_alloc.hh: Define QA_SUPPORTED when quick allocation is --- Makefile.am +++ Makefile.am @@ -1,29 +1,49 @@ AUTOMAKE_OPTIONS=subdir-objects 1.7.1 ACLOCAL_AMFLAGS = -I m4 MOST_SOURCES = \ - app_state.cc commands.cc diff_patch.cc lua.cc \ - transforms.cc update.cc work.cc cert.cc database.cc file_io.cc \ - keys.cc manifest.cc packet.cc sanity.cc vocab.cc rcs_file.cc \ - xdelta.cc ui.cc schema_migration.cc \ - constants.cc netsync.cc netcmd.cc merkle_tree.cc basic_io.cc \ - mkstemp.cc lcs.cc rcs_import.hh rcs_import.cc revision.cc \ - change_set.cc mt_version.cc automate.cc database_check.cc \ - path_component.cc epoch.cc inodeprint.cc \ + app_state.cc app_state.hh \ + commands.cc commands.hh \ + diff_patch.cc diff_patch.hh \ + lua.cc lua.hh \ + transforms.cc transforms.hh \ + update.cc update.hh \ + work.cc work.hh \ + cert.cc cert.hh \ + database.cc database.hh \ + file_io.cc file_io.hh \ + keys.cc keys.hh \ + manifest.cc manifest.hh \ + packet.cc packet.hh \ + sanity.cc sanity.hh \ + vocab.cc vocab.hh vocab_terms.hh numeric_vocab.hh \ + rcs_file.cc rcs_file.hh \ + xdelta.cc xdelta.hh \ + ui.cc ui.hh \ + schema_migration.cc schema_migration.hh \ + constants.cc constants.hh \ + netsync.cc netsync.hh \ + netcmd.cc netcmd.hh \ + merkle_tree.cc merkle_tree.hh \ + basic_io.cc basic_io.hh \ + mkstemp.cc mkstemp.hh \ + lcs.cc lcs.hh \ + rcs_import.cc rcs_import.hh \ + revision.cc revision.hh \ + change_set.cc change_set.hh \ + mt_version.cc mt_version.hh \ + automate.cc automate.hh \ + database_check.cc database_check.hh \ + path_component.cc path_component.hh \ + epoch.cc epoch.hh \ + inodeprint.cc inodeprint.hh \ + selectors.cc selectors.hh \ \ - app_state.hh commands.hh file_io.hh manifest.hh packet.hh \ - sanity.hh update.hh work.hh cert.hh database.hh keys.hh \ - transforms.hh vocab.hh \ - cleanup.hh diff_patch.hh lua.hh basic_io.hh \ - unit_tests.hh vocab_terms.hh interner.hh cycle_detector.hh \ - randomfile.hh rcs_file.hh xdelta.hh adler32.hh \ - lcs.hh constants.hh ui.hh schema_migration.hh quick_alloc.hh \ - netsync.hh netcmd.hh netio.hh merkle_tree.hh \ - numeric_vocab.hh revision.hh change_set.hh \ - mkstemp.hh mt_version.hh automate.hh database_check.hh smap.hh \ - gettext.h package_revision.c package_full_revision.c \ - path_component.hh epoch.hh package_full_revision.h \ - package_revision.h inodeprint.hh + cleanup.hh unit_tests.hh interner.hh \ + cycle_detector.hh randomfile.hh adler32.hh quick_alloc.hh \ + netio.hh smap.hh gettext.h \ + package_revision.c package_revision.h \ + package_full_revision.c package_full_revision.h NETXX_SOURCES = \ netxx/accept.cxx netxx/accept.h netxx/address.cxx \ --- automate.cc +++ automate.cc @@ -1,3 +1,4 @@ +// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*- // copyright (C) 2004 nathaniel smith // all rights reserved. // licensed to the public under the terms of the GNU GPL (>= 2) @@ -6,6 +7,8 @@ #include #include #include +#include +#include #include "vocab.hh" #include "app_state.hh" @@ -417,6 +420,36 @@ } } +// Name: select +// Arguments: +// 1: selector +// Added in: 0.2 +// Purpose: Prints all the revisions that match the given selector. +// Output format: A list of revision ids, in hexadecimal, each followed by a +// newline. Revision ids are printed in alphabetically sorted order. +// Error conditions: None. +static void +automate_select(std::vector args, + std::string const & help_name, + app_state & app, + std::ostream & output) +{ + if (args.size() != 1) + throw usage(help_name); + + std::vector > + sels(selectors::parse_selector(args[0](), app)); + + // we jam through an "empty" selection on sel_ident type + std::set completions; + selectors::selector_type ty = selectors::sel_ident; + selectors::complete_selector("", sels, ty, completions, app); + + for (std::set::const_iterator i = completions.begin(); + i != completions.end(); ++i) + output << *i << std::endl; +} + void automate_command(utf8 cmd, std::vector args, std::string const & root_cmd_name, @@ -445,6 +478,8 @@ automate_children(args, root_cmd_name, app, output); else if (cmd() == "graph") automate_graph(args, root_cmd_name, app, output); + else if (cmd() == "select") + automate_select(args, root_cmd_name, app, output); else throw usage(root_cmd_name); } --- commands.cc +++ commands.cc @@ -44,6 +44,7 @@ #include "automate.hh" #include "inodeprint.hh" #include "platform.hh" +#include "selectors.hh" // // this file defines the task-oriented "top level" commands which can be @@ -739,110 +740,20 @@ return description; } -static void -decode_selector(string const & orig_sel, - selector_type & type, - string & sel, - app_state & app) -{ - sel = orig_sel; - - L(F("decoding selector '%s'\n") % sel); - - if (sel.size() < 2 || sel[1] != ':') - { - string tmp; - if (!app.lua.hook_expand_selector(sel, tmp)) - { - L(F("expansion of selector '%s' failed\n") % sel); - } - else - { - P(F("expanded selector '%s' -> '%s'\n") % sel % tmp); - sel = tmp; - } - } - - if (sel.size() >= 2 && sel[1] == ':') - { - switch (sel[0]) - { - case 'a': - type = sel_author; - break; - case 'b': - type = sel_branch; - break; - case 'd': - type = sel_date; - break; - case 'i': - type = sel_ident; - break; - case 't': - type = sel_tag; - break; - case 'c': - type = sel_cert; - break; - default: - W(F("unknown selector type: %c\n") % sel[0]); - break; - } - sel.erase(0,2); - } -} - -static void -complete_selector(string const & orig_sel, - vector > const & limit, - selector_type & type, - set & completions, - app_state & app) -{ - string sel; - decode_selector(orig_sel, type, sel, app); - app.db.complete(type, sel, limit, completions); -} - - static void complete(app_state & app, string const & str, revision_id & completion) { + vector > + sels(selectors::parse_selector(str, app)); - // this rule should always be enabled, even if the user specifies - // --norc: if you provide a revision id, you get a revision id. - if (str.find_first_not_of(constants::legal_id_bytes) == string::npos - && str.size() == constants::idlen) - { - completion = revision_id(str); - return; - } - - typedef boost::tokenizer > tokenizer; - boost::char_separator slash("/"); - tokenizer tokens(str, slash); - - vector selector_strings; - vector > selectors; - copy(tokens.begin(), tokens.end(), back_inserter(selector_strings)); - for (vector::const_iterator i = selector_strings.begin(); - i != selector_strings.end(); ++i) - { - string sel; - selector_type type = sel_unknown; - decode_selector(*i, type, sel, app); - selectors.push_back(make_pair(type, sel)); - } - P(F("expanding selection '%s'\n") % str); // we jam through an "empty" selection on sel_ident type set completions; - selector_type ty = sel_ident; - complete_selector("", selectors, ty, completions, app); + selectors::selector_type ty = selectors::sel_ident; + selectors::complete_selector("", sels, ty, completions, app); N(completions.size() != 0, F("no match for selection '%s'") % str); --- commands.hh +++ commands.hh @@ -25,17 +25,6 @@ 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); - typedef enum - { - sel_author, - sel_branch, - sel_date, - sel_tag, - sel_ident, - sel_cert, - sel_unknown - } - selector_type; }; #endif --- database.cc +++ database.cc @@ -2137,28 +2137,28 @@ completions.insert(file_id(res[i][0])); } -using commands::selector_type; +using selectors::selector_type; static void selector_to_certname(selector_type ty, string & s) { switch (ty) { - case commands::sel_author: + case selectors::sel_author: s = author_cert_name; break; - case commands::sel_branch: + case selectors::sel_branch: s = branch_cert_name; break; - case commands::sel_date: + case selectors::sel_date: s = date_cert_name; break; - case commands::sel_tag: + case selectors::sel_tag: s = tag_cert_name; break; - case commands::sel_ident: - case commands::sel_cert: - case commands::sel_unknown: + case selectors::sel_ident: + case selectors::sel_cert: + case selectors::sel_unknown: I(false); // don't do this. break; } @@ -2192,13 +2192,13 @@ else lim += " INTERSECT "; - if (i->first == commands::sel_ident) + if (i->first == selectors::sel_ident) { lim += "SELECT id FROM revision_certs "; lim += (F("WHERE id GLOB '%s*'") % i->second).str(); } - else if (i->first == commands::sel_cert) + else if (i->first == selectors::sel_cert) { if (i->second.length() > 0) { @@ -2225,7 +2225,7 @@ } } - else if (i->first == commands::sel_unknown) + else if (i->first == selectors::sel_unknown) { lim += "SELECT id FROM revision_certs "; lim += (F(" WHERE (name='%s' OR name='%s' OR name='%s')") @@ -2252,14 +2252,14 @@ // which generally means "author, tag or branch" string query; - if (ty == commands::sel_ident) + if (ty == selectors::sel_ident) { query = (F("SELECT id FROM %s") % lim).str(); } else { query = "SELECT value FROM revision_certs WHERE"; - if (ty == commands::sel_unknown) + if (ty == selectors::sel_unknown) { query += (F(" (name='%s' OR name='%s' OR name='%s')") @@ -2283,7 +2283,7 @@ fetch(res, one_col, any_rows, query.c_str()); for (size_t i = 0; i < res.size(); ++i) { - if (ty == commands::sel_ident) + if (ty == selectors::sel_ident) completions.insert(res[i][0]); else { --- database.hh +++ database.hh @@ -16,7 +16,7 @@ #include -#include "commands.hh" +#include "selectors.hh" #include "manifest.hh" #include "numeric_vocab.hh" #include "vocab.hh" @@ -415,9 +415,9 @@ void complete(std::string const & partial, std::set & completions); - void complete(commands::selector_type ty, + void complete(selectors::selector_type ty, std::string const & partial, - std::vector > const & limit, std::set & completions); --- monotone.texi +++ monotone.texi @@ -4757,6 +4757,41 @@ @end table + address@hidden monotone automate select @var{selector} + address@hidden @strong address@hidden Arguments: + +One selector (or combined selector). + address@hidden Added in: + +0.2 + address@hidden Purpose: + +Print all revisions that match the given selector. + address@hidden Sample output: + address@hidden +28ce076c69eadb9b1ca7bdf9d40ce95fe2f29b61 +75156724e0e2e3245838f356ec373c50fa469f1f address@hidden verbatim + address@hidden Output format: + +Zero or more lines, each giving the id of one revision that matches the +given selector. Each line consists of a revision id, in hexadecimal, +followed by a newline. + address@hidden Error conditions: + +None. + address@hidden table + @end ftable @page --- selectors.cc +++ selectors.cc @@ -0,0 +1,117 @@ +// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*- +// copyright (C) 2002, 2003 graydon hoare +// all rights reserved. +// licensed to the public under the terms of the GNU GPL (>= 2) +// see the file COPYING for details + +#include "selectors.hh" +#include "sanity.hh" +#include "app_state.hh" +#include "constants.hh" + +namespace selectors +{ + + static void + decode_selector(std::string const & orig_sel, + selector_type & type, + std::string & sel, + app_state & app) + { + sel = orig_sel; + + L(F("decoding selector '%s'\n") % sel); + + if (sel.size() < 2 || sel[1] != ':') + { + std::string tmp; + if (!app.lua.hook_expand_selector(sel, tmp)) + { + L(F("expansion of selector '%s' failed\n") % sel); + } + else + { + P(F("expanded selector '%s' -> '%s'\n") % sel % tmp); + sel = tmp; + } + } + + if (sel.size() >= 2 && sel[1] == ':') + { + switch (sel[0]) + { + case 'a': + type = sel_author; + break; + case 'b': + type = sel_branch; + break; + case 'd': + type = sel_date; + break; + case 'i': + type = sel_ident; + break; + case 't': + type = sel_tag; + break; + case 'c': + type = sel_cert; + break; + default: + W(F("unknown selector type: %c\n") % sel[0]); + break; + } + sel.erase(0,2); + } + } + + void + complete_selector(std::string const & orig_sel, + std::vector > const & limit, + selector_type & type, + std::set & completions, + app_state & app) + { + std::string sel; + decode_selector(orig_sel, type, sel, app); + app.db.complete(type, sel, limit, completions); + } + + std::vector > + parse_selector(std::string const & str, + app_state & app) + { + std::vector > sels; + + // this rule should always be enabled, even if the user specifies + // --norc: if you provide a revision id, you get a revision id. + if (str.find_first_not_of(constants::legal_id_bytes) == std::string::npos + && str.size() == constants::idlen) + { + sels.push_back(std::make_pair(sel_ident, str)); + } + else + { + typedef boost::tokenizer > tokenizer; + boost::char_separator slash("/"); + tokenizer tokens(str, slash); + + std::vector selector_strings; + copy(tokens.begin(), tokens.end(), back_inserter(selector_strings)); + + for (std::vector::const_iterator i = selector_strings.begin(); + i != selector_strings.end(); ++i) + { + std::string sel; + selector_type type = sel_unknown; + + decode_selector(*i, type, sel, app); + sels.push_back(std::make_pair(type, sel)); + } + } + + return sels; + } + +}; // namespace selectors --- selectors.hh +++ selectors.hh @@ -0,0 +1,44 @@ +// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*- +// copyright (C) 2002, 2003 graydon hoare +// all rights reserved. +// licensed to the public under the terms of the GNU GPL (>= 2) +// see the file COPYING for details + +#ifndef __SELECTORS_HH__ +#define __SELECTORS_HH__ + +#include +#include +#include +#include + +class app_state; + +namespace selectors +{ + + typedef enum + { + sel_author, + sel_branch, + sel_date, + sel_tag, + sel_ident, + sel_cert, + sel_unknown + } + selector_type; + + void + complete_selector(std::string const & orig_sel, + std::vector > const & limit, + selector_type & type, + std::set & completions, + app_state & app); + std::vector > + parse_selector(std::string const & str, + app_state & app); + +}; // namespace selectors + +#endif // __SELECTORS_HH__ --- tests/t_automate_select.at +++ tests/t_automate_select.at @@ -0,0 +1,37 @@ +AT_SETUP([check automate select]) +MONOTONE_SETUP + +ADD_FILE(testfile, [this is just a file +]) +AT_CHECK(cp testfile testfile1) +COMMIT(testbranch) +FIRST=`BASE_REVISION` + +AT_DATA(testfile, [Now, this is a different file +]) +AT_CHECK(cp testfile testfile2) +COMMIT(testbranch) +SECOND=`BASE_REVISION` + +AT_DATA(testfile, [And we change it a third time +]) +AT_CHECK(cp testfile testfile3) +COMMIT(testbranch) + +AT_CHECK(MONOTONE cert $FIRST testcert 'value=with=equal=signs', [], [ignore], [ignore]) +AT_CHECK(MONOTONE cert $SECOND testcert 'value', [], [ignore], [ignore]) + +# Check that inexact values fail... +CHECK_SAME_STDOUT(MONOTONE automate select 'c:testcert=value=' | wc -l, echo 0) + +# Check that wild cards succeed (this one becomes a misuse, because it will +# match two revisions)... +CHECK_SAME_STDOUT(MONOTONE automate select 'c:testcert=value*' | wc -l, echo 2) + +# Check that no value succeeds... +CHECK_SAME_STDOUT(MONOTONE automate select 'c:testcert' | wc -l, echo 2) + +# Check that exact value succeed... +CHECK_SAME_STDOUT(MONOTONE automate select 'c:testcert=value' | wc -l, echo 1) + +AT_CLEANUP --- testsuite.at +++ testsuite.at @@ -587,3 +587,4 @@ m4_include(tests/t_i18n_file_data.at) m4_include(tests/t_cvsimport_manifest_cycle.at) m4_include(tests/t_select_cert.at) +m4_include(tests/t_automate_select.at)