#
# 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)