# # add_file "tests/t_selector_later_earlier.at" # # patch "database.cc" # from [591ac0be0c3855db8b3817c9f1b049a070c3ea55] # to [a59a347c6c190c09f42107d114045da78f942d4a] # # patch "lua.cc" # from [51659bca46465ce818dc3ed0adf1a81568c64cf3] # to [76cfc26a5a3d0c64a9f244d78ad51c8975aa8d52] # # patch "lua.hh" # from [5840b51a224c1bf2e25d448b00c2a3c48e2fd1dc] # to [a1b3877535049b77b678d864354d030f8433854c] # # patch "monotone.texi" # from [e531baf707ae182b4cda0442bb875a1c48bc9970] # to [03846afb7f0083c87c97df8f446db98995c36706] # # patch "selectors.cc" # from [6e04d827ad6cdcd45bf4a9b2b703a32d36cdca1c] # to [af2e5813aba3cdf936fbcbb31dbb05ddf3690ccc] # # patch "selectors.hh" # from [4c1242970da2473e28d859f407a05e0792ff5a20] # to [54ed172da5c3a3c4721152012d831f51e693cab9] # # patch "std_hooks.lua" # from [22397c44293cb716cf7a7b2f564eb3d9d4e892f1] # to [05e5e9b2b7f37107a7f38161d258e582eb13d697] # # patch "tests/t_selector_later_earlier.at" # from [] # to [126ad78666f5b4f1d281caf4726da7d5d021fee4] # # patch "testsuite.at" # from [cc9eaf6d0d1061195b4e304d107dba977bbb3a6d] # to [c182f042ad061ca7872e4e3bf676aed475ac1d40] # --- database.cc +++ database.cc @@ -1749,7 +1749,6 @@ &sqlite3_unbase64_fn, NULL, NULL) == 0); - I(sqlite3_create_function(sql(), "unpack", -1, SQLITE_UTF8, NULL, &sqlite3_unpack_fn, @@ -2151,6 +2150,8 @@ s = branch_cert_name; break; case selectors::sel_date: + case selectors::sel_later: + case selectors::sel_earlier: s = date_cert_name; break; case selectors::sel_tag: @@ -2239,9 +2240,19 @@ { string certname; selector_to_certname(i->first, certname); - lim += "SELECT id FROM revision_certs "; - lim += (F("WHERE name='%s' AND unbase64(value) glob '*%s*'") - % certname % i->second).str(); + lim += (F("SELECT id FROM revision_certs WHERE name='%s' AND ") % certname).str(); + switch (i->first) + { + case selectors::sel_earlier: + lim += (F("unbase64(value) <= X'%s'") % encode_hexenc(i->second)).str(); + break; + case selectors::sel_later: + lim += (F("unbase64(value) > X'%s'") % encode_hexenc(i->second)).str(); + break; + default: + lim += (F("unbase64(value) glob '*%s*'") % i->second).str(); + break; + } } } } @@ -2279,6 +2290,8 @@ query += (F(" AND (id IN %s)") % lim).str(); } + // std::cerr << query << std::endl; // debug expr + results res; fetch(res, one_col, any_rows, query.c_str()); for (size_t i = 0; i < res.size(); ++i) @@ -2444,4 +2457,3 @@ { committed = true; } - --- lua.cc +++ lua.cc @@ -648,6 +648,20 @@ } bool +lua_hooks::hook_expand_date(std::string const & sel, + std::string & exp) +{ + exp.clear(); + bool res= Lua(st) + .func("expand_date") + .push_str(sel) + .call(1,1) + .extract_str(exp) + .ok(); + return res && exp.size(); +} + +bool lua_hooks::hook_get_branch_key(cert_value const & branchname, rsa_keypair_id & k) { --- lua.hh +++ lua.hh @@ -39,6 +39,7 @@ // cert hooks bool hook_expand_selector(std::string const & sel, std::string & exp); + bool hook_expand_date(std::string const & sel, std::string & exp); bool hook_get_branch_key(cert_value const & branchname, rsa_keypair_id & k); bool lua_hooks::hook_get_priv_key(rsa_keypair_id const & k, base64< arc4 > & priv_key ); --- monotone.texi +++ monotone.texi @@ -2292,7 +2292,20 @@ @code{branch} certs where the cert value begins with @code{net.venge}. @item Date selection Uses selector type @code{d}. For example, @code{d:2004-04} matches address@hidden certs where the cert value begins with @code{2004-04}. address@hidden certs where the cert value begins with address@hidden This selector also accepts expanded date syntax (see below). address@hidden "Earlier or equal than" selection +Uses selector type @code{e}. For example, @code{e:2004-04-25} matches address@hidden certs where the cert value is less or equal than address@hidden:00:00}. If the time component is unspecified, +monotone will assume 00:00:00. This selector also accepts expanded date +syntax (see below) address@hidden "Later than" selection +Uses selector type @code{l}. For example, @code{l:2004-04-25} matches address@hidden certs where the cert value is strictly less than address@hidden:00:00}. If the time component is unspecified, +monotone will assume 00:00:00. This selector also accepts expanded date +syntax (see below) @item Identifier selection Uses selector type @code{i}. For example, @code{i:0f3a} matches revision IDs which begin with @code{0f3a}. @@ -2325,6 +2338,37 @@ addresses, branch names, or date specifications. For the complete source code of the hook, see @ref{Hook Reference}. address@hidden Expanding dates + +All date-related selectors (@code{d}, @code{e}, @code{l}) support an +english-like syntax similar to CVS. This syntax is expanded to the +numeric format by a lua hook: @code{expand_date}. +The allowed date formats are: address@hidden @asis + address@hidden now +Expands to the current date and time. address@hidden today +Expands to today's date. @code{e} and @code{l} selectors assume time 00:00:00 address@hidden yesterday +Expands to yesterday's date. @code{e} and @code{l} selectors assume +time 00:00:00 address@hidden @{minute|address@hidden +Expands to today date and time, minus the specified @code{number} of +minutes|hours. address@hidden @{day|week|month|address@hidden +Expands to today date, minus the specified @code{number} of +days|weeks|months|years. @code{e} and @code{l} selectors assume time +00:00:00 address@hidden -[-day[Thour:minute:second]] +Expands to the supplied year/month. The day and time component are +optional. If missing, @code{e} and @code{l} selectors assume the first +day of month and time 00:00:00. +The time component, if supplied, must be complete to the second. address@hidden table + +For the complete source code of the hook, see @ref{Hook Reference}. + @heading Typeless selection If, after expansion, a selector still has no type, it is matched as a @@ -5580,11 +5624,10 @@ @group function expand_selector(str) - -- simple date patterns - if string.find(str, "^19%d%d%-%d%d") - or string.find(str, "^20%d%d%-%d%d") + -- something which looks like a generic cert pattern + if string.find(str, "^[^=]*=.*$") then - return ("d:" .. str) + return ("c:" .. str) end -- something which looks like an email address @@ -5605,11 +5648,55 @@ return ("i:" .. str) end + -- tries to expand as a date + local dtstr = expand_date(str) + if dtstr ~= nil + then + return ("d:" .. dtstr) + end + + return nil +end address@hidden group address@hidden smallexample + address@hidden expand_date (@var{str}) + +Attempts to expand @var{str} as a date expression. Expansion means recognizing +and interpreting special words such as @code{yesterday} or @code{6 +months ago} and converting them into well formed date expressions. For more +detail on the use of selectors, see @ref{Selectors}. The default +definition of this hook is: + address@hidden address@hidden +function expand_date(str) + -- simple date patterns + if string.find(str, "^19%d%d%-%d%d") + or string.find(str, "^20%d%d%-%d%d") + then + return (str) + end + + -- "now" + if str == "now" + then + local t = os.time(os.date('!*t')) + return os.date("%FT%T", t) + end + + -- today don't uses the time + if str == "today" + then + local t = os.time(os.date('!*t')) + return os.date("%F", t) + end + -- "yesterday", the source of all hangovers if str == "yesterday" then local t = os.time(os.date('!*t')) - return os.date("d:%F", t - 86400) + return os.date("%F", t - 86400) end -- "CVS style" relative dates such as "3 weeks ago" @@ -5621,21 +5708,24 @@ month = 2678400; year = 31536000 @} - local pos, len, n, type = string.find(str, "(%d+) - ([minutehordaywk]+)s? ago") + local pos, len, n, type = string.find(str, "(%d+) ([minutehordaywk]+)s? ago") if trans[type] ~= nil then local t = os.time(os.date('!*t')) - return os.date("d:%F", t - (n * trans[type])) + if trans[type] <= 3600 + then + return os.date("%FT%T", t - (n * trans[type])) + else + return os.date("%F", t - (n * trans[type])) + end end - + return nil end @end group @end smallexample - @item get_system_linesep () Returns a string which defines the default system line separator. --- selectors.cc +++ selectors.cc @@ -22,9 +22,9 @@ L(F("decoding selector '%s'\n") % sel); + std::string tmp; 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); @@ -58,11 +58,35 @@ case 'c': type = sel_cert; break; + case 'l': + type = sel_later; + break; + case 'e': + type = sel_earlier; + break; default: W(F("unknown selector type: %c\n") % sel[0]); break; } sel.erase(0,2); + + /* a selector date-related should be validated */ + if (sel_date==type || sel_later==type || sel_earlier==type) + { + N (app.lua.hook_expand_date(sel, tmp), + F ("selector '%s' is not a valid date\n") % sel); + + if (tmp.size()<8 && (sel_later==type || sel_earlier==type)) + tmp += "-01T00:00:00"; + else if (tmp.size()<11 && (sel_later==type || sel_earlier==type)) + tmp += "T00:00:00"; + N(tmp.size()==19 || sel_date==type, F ("selector '%s' is not a valid date (%s)\n") % sel % tmp); + if (sel != tmp) + { + P (F ("expanded date '%s' -> '%s'\n") % sel % tmp); + sel = tmp; + } + } } } --- selectors.hh +++ selectors.hh @@ -25,6 +25,8 @@ sel_tag, sel_ident, sel_cert, + sel_earlier, + sel_later, sel_unknown } selector_type; --- std_hooks.lua +++ std_hooks.lua @@ -436,13 +436,6 @@ return ("c:" .. str) end - -- simple date patterns - if string.find(str, "^19%d%d%-%d%d") - or string.find(str, "^20%d%d%-%d%d") - then - return ("d:" .. str) - end - -- something which looks like an email address if string.find(str, "address@hidden") then @@ -461,11 +454,44 @@ return ("i:" .. str) end + -- tries to expand as a date + local dtstr = expand_date(str) + if dtstr ~= nil + then + return ("d:" .. dtstr) + end + + return nil +end + +-- expansion of a date expression +function expand_date(str) + -- simple date patterns + if string.find(str, "^19%d%d%-%d%d") + or string.find(str, "^20%d%d%-%d%d") + then + return (str) + end + + -- "now" + if str == "now" + then + local t = os.time(os.date('!*t')) + return os.date("%FT%T", t) + end + + -- today don't uses the time + if str == "today" + then + local t = os.time(os.date('!*t')) + return os.date("%F", t) + end + -- "yesterday", the source of all hangovers if str == "yesterday" then local t = os.time(os.date('!*t')) - return os.date("d:%F", t - 86400) + return os.date("%F", t - 86400) end -- "CVS style" relative dates such as "3 weeks ago" @@ -481,9 +507,14 @@ if trans[type] ~= nil then local t = os.time(os.date('!*t')) - return os.date("d:%F", t - (n * trans[type])) + if trans[type] <= 3600 + then + return os.date("%FT%T", t - (n * trans[type])) + else + return os.date("%F", t - (n * trans[type])) + end end - + return nil end --- tests/t_selector_later_earlier.at +++ tests/t_selector_later_earlier.at @@ -0,0 +1,103 @@ +AT_SETUP([check later and earlier selectors]) +MONOTONE_SETUP + +ADD_FILE(testfile, [this is just a file +]) +AT_CHECK(cp testfile testfile1) +AT_CHECK(MONOTONE commit --date="2005-03-11T20:33:01" --branch=foo --message=march, [], [ignore], [ignore]) +AT_CHECK(echo "`BASE_REVISION`" , [], [stdout], [ignore]) +AT_CHECK(mv stdout first) + +AT_DATA(testfile, [Now, this is a different file +]) +AT_CHECK(cp testfile testfile2) +AT_CHECK(MONOTONE commit --date="2005-04-22T12:15:00" --branch=foo --message=aprila, [], [ignore], [ignore]) +AT_CHECK(echo "`BASE_REVISION`" , [], [stdout], [ignore]) +AT_CHECK(mv stdout second) + +AT_DATA(testfile, [And we change it a third time +]) +AT_CHECK(cp testfile testfile3) +AT_CHECK(MONOTONE commit --date="2005-04-24T07:44:39" --branch=foo --message=aprilb, [], [ignore], [ignore]) +AT_CHECK(echo "`BASE_REVISION`" , [], [stdout], [ignore]) +AT_CHECK(mv stdout third) + +# ------------------- +# check 'earlier or equal' selector +# ------------------- + +# this time is just 'before' the first commit, thus no output should come +AT_CHECK(RAW_MONOTONE automate select "e:2005-03-11T20:33:00", [], [stdout], [ignore]) +AT_CHECK(mv stdout nosel) +AT_CHECK(test -f nosel -a ! -s nosel) + +# these sels should extract only the first commit +# Note: the second sel is the exact time of the first commit. +AT_CHECK(cp -f first expout) +AT_CHECK(RAW_MONOTONE automate select "e:2005-04", [], [expout], [ignore]) +AT_CHECK(RAW_MONOTONE automate select "e:2005-03-11T20:33:01", [], [expout], [ignore]) +AT_CHECK(RAW_MONOTONE automate select "e:2005-03-11T20:33:02", [], [expout], [ignore]) + +# now the first two +AT_CHECK(cat second first , [], [stdout], [ignore]) +AT_CHECK(mv stdout expout) +AT_CHECK(RAW_MONOTONE automate select "e:2005-04-23", [], [expout], [ignore]) + +# finally, all the files +AT_CHECK(RAW_MONOTONE automate select "e:2005-04-30", [], [stdout], [ignore]) +AT_CHECK(mv stdout a_s) +AT_CHECK(test 3 -eq "`wc -l