# # # add_dir "tests/log_from_to_revision" # # add_file "tests/log_from_to_revision/__driver__.lua" # content [b25ceae98f8d3ecb22d79165c18d02a0d9d2992c] # # patch "NEWS" # from [6746ecaa1c40d162e80b9342d2de5eb7783440dd] # to [730ace914a4621c491e14e302834289f77ed52de] # # patch "cmd_diff_log.cc" # from [82df8ea559ed4f3a912de21f0ef3b1a576ab6b14] # to [5e5c21fdce653b9ed94bf91aa5c3a5dc3c9478aa] # # patch "monotone.texi" # from [3446d31f637ac3426b7ff20ffaadde1b2b2ba0de] # to [48bab75212b595ebbd07ce72fb3442531ed3530a] # ============================================================ --- tests/log_from_to_revision/__driver__.lua b25ceae98f8d3ecb22d79165c18d02a0d9d2992c +++ tests/log_from_to_revision/__driver__.lua b25ceae98f8d3ecb22d79165c18d02a0d9d2992c @@ -0,0 +1,52 @@ +mtn_setup() + +r = {} + +addfile("foo", "0") +commit() +table.insert(r, 1, base_revision()) + +for i=2,10 do + writefile("foo", i) + commit() + table.insert(r, i, base_revision()) +end + +check(mtn("log", "--brief", "--no-graph", "--from", r[7], "--to", r[4]), 0, true, false) + +check(not qgrep(r[1], "stdout")) +check(not qgrep(r[2], "stdout")) +check(not qgrep(r[3], "stdout")) +check(not qgrep(r[4], "stdout")) +check(qgrep(r[5], "stdout")) +check(qgrep(r[6], "stdout")) +check(qgrep(r[7], "stdout")) +check(not qgrep(r[8], "stdout")) +check(not qgrep(r[9], "stdout")) +check(not qgrep(r[10], "stdout")) + +check(mtn("log", "--brief", "--no-graph", "-r", r[3], "-r", r[4], "-r", r[5]), 0, true, false) + +check(not qgrep(r[1], "stdout")) +check(not qgrep(r[2], "stdout")) +check(qgrep(r[3], "stdout")) +check(qgrep(r[4], "stdout")) +check(qgrep(r[5], "stdout")) +check(not qgrep(r[6], "stdout")) +check(not qgrep(r[7], "stdout")) +check(not qgrep(r[8], "stdout")) +check(not qgrep(r[9], "stdout")) +check(not qgrep(r[10], "stdout")) + +check(mtn("log", "--brief", "--no-graph", "-r", r[3], "-r", r[4], "-r", r[5], "--from", r[4]), 0, true, false) + +check(not qgrep(r[1], "stdout")) +check(not qgrep(r[2], "stdout")) +check(qgrep(r[3], "stdout")) +check(qgrep(r[4], "stdout")) +check(not qgrep(r[5], "stdout")) +check(not qgrep(r[6], "stdout")) +check(not qgrep(r[7], "stdout")) +check(not qgrep(r[8], "stdout")) +check(not qgrep(r[9], "stdout")) +check(not qgrep(r[10], "stdout")) ============================================================ --- NEWS 6746ecaa1c40d162e80b9342d2de5eb7783440dd +++ NEWS 730ace914a4621c491e14e302834289f77ed52de @@ -58,7 +58,7 @@ own for directory creation and permissions if you take the key on standard output and redirect it to a file.) - - The log command was missing --depth and --exclude options used to + - The log command was missing '--depth' and '--exclude' options used to restrict revisions printed to those touching specific paths. Log now allows these options and uses them properly. @@ -74,6 +74,9 @@ variables are honored; for instance, EDITOR="emacs -nw" works now. (Debian bug #320565.) + - Additional '--revision' option for 'mtn log' allows logging of + selected sets of revisions. + Internal - Using 64 bit integer values to represent dates internally. This ============================================================ --- cmd_diff_log.cc 82df8ea559ed4f3a912de21f0ef3b1a576ab6b14 +++ cmd_diff_log.cc 5e5c21fdce653b9ed94bf91aa5c3a5dc3c9478aa @@ -700,13 +700,12 @@ typedef priority_queue >, rev_cmp> frontier_t; -CMD(log, "log", "", CMD_REF(informative), N_("[FILE] ..."), - N_("Prints history in reverse order"), - N_("This command prints history in reverse order, filtering it by " - "FILE if given. If one or more revisions are given, uses them as " - "a starting point."), +CMD(log, "log", "", CMD_REF(informative), N_("[PATH] ..."), + N_("Prints selected history in forward or reverse order"), + N_("This command prints selected history in forward or reverse order, " + "filtering it by PATH if given."), options::opts::last | options::opts::next | - options::opts::from | options::opts::to | + options::opts::from | options::opts::to | options::opts::revision | options::opts::brief | options::opts::diffs | options::opts::depth | options::opts::exclude | options::opts::no_merges | options::opts::no_files | @@ -732,66 +731,84 @@ CMD(log, "log", "", CMD_REF(informative) frontier_t frontier(cmp); revision_id first_rid; // for mapping paths to node ids when restricted - if (app.opts.from.empty()) + // start at revisions specified and implied by --from selectors + + set starting_revs; + if (app.opts.from.empty() && app.opts.revision_selectors.empty()) { - workspace work(app, - F("try passing a --from revision to start at")); + // only set default --from revs if no --revision selectors were specified + workspace work(app, F("try passing a --from revision to start at")); revision_t rev; work.get_work_rev(rev); for (edge_map::const_iterator i = rev.edges.begin(); i != rev.edges.end(); i++) { - rev_height height; - db.get_rev_height(edge_old_revision(i), height); - frontier.push(make_pair(height, edge_old_revision(i))); + starting_revs.insert(edge_old_revision(i)); + if (i == rev.edges.begin()) + first_rid = edge_old_revision(i); } } - else + else if (!app.opts.from.empty()) { for (args_vector::const_iterator i = app.opts.from.begin(); i != app.opts.from.end(); i++) { set rids; + MM(rids); + MM(*i); complete(app.opts, app.lua, project, (*i)(), rids); - for (set::const_iterator j = rids.begin(); - j != rids.end(); ++j) - { - rev_height height; - db.get_rev_height(*j, height); - frontier.push(make_pair(height, *j)); - } + starting_revs.insert(rids.begin(), rids.end()); if (i == app.opts.from.begin()) first_rid = *rids.begin(); } } - // If --to was given, don't log past those revisions. - set disallowed; - bool use_disallowed(!app.opts.to.empty()); - if (use_disallowed) + L(FL("%d starting revisions") % starting_revs.size()); + + // stop at revisions specified and implied by --to selectors + + set ending_revs; + if (!app.opts.to.empty()) { - std::deque to; for (args_vector::const_iterator i = app.opts.to.begin(); i != app.opts.to.end(); i++) { + set rids; + MM(rids); MM(*i); + complete(app.opts, app.lua, project, (*i)(), rids); + ending_revs.insert(rids.begin(), rids.end()); + } + + loader.load_implied_revs(ending_revs); + } + + L(FL("%d ending revisions") % ending_revs.size()); + + // select revisions specified by --revision selectors + + set selected_revs; + if (!app.opts.revision_selectors.empty()) + { + for (args_vector::const_iterator i = app.opts.revision_selectors.begin(); + i != app.opts.revision_selectors.end(); i++) + { set rids; + MM(rids); + MM(*i); complete(app.opts, app.lua, project, (*i)(), rids); - for (set::const_iterator j = rids.begin(); - j != rids.end(); ++j) - { - I(!null_id(*j)); - pair::iterator, bool> res = disallowed.insert(*j); - if (res.second) - { - to.push_back(*j); - } - } + + // only select revs outside of the ending set + set_difference(rids.begin(), rids.end(), + ending_revs.begin(), ending_revs.end(), + inserter(selected_revs, selected_revs.end())); + if (null_id(first_rid) && i == app.opts.revision_selectors.begin()) + first_rid = *rids.begin(); } + } - loader.load_implied_revs(disallowed); - } + L(FL("%d selected revisions") % selected_revs.size()); node_restriction mask; @@ -817,6 +834,7 @@ CMD(log, "log", "", CMD_REF(informative) { // FIXME_RESTRICTIONS: should this add paths from the rosters of // all selected revs? + I(!null_id(first_rid)); roster_t roster; db.get_roster(first_rid, roster); @@ -826,6 +844,33 @@ CMD(log, "log", "", CMD_REF(informative) } } + // if --revision was specified without --from log only the selected revs + bool log_selected(!app.opts.revision_selectors.empty() && + app.opts.from.empty()); + + if (log_selected) + { + for (set::const_iterator i = selected_revs.begin(); + i != selected_revs.end(); ++i) + { + rev_height height; + db.get_rev_height(*i, height); + frontier.push(make_pair(height, *i)); + } + L(FL("log %d selected revisions") % selected_revs.size()); + } + else + { + for (set::const_iterator i = starting_revs.begin(); + i != starting_revs.end(); ++i) + { + rev_height height; + db.get_rev_height(*i, height); + frontier.push(make_pair(height, *i)); + } + L(FL("log %d starting revisions") % starting_revs.size()); + } + cert_name author_name(author_cert_name); cert_name date_name(date_cert_name); cert_name branch_name(branch_cert_name); @@ -840,7 +885,7 @@ CMD(log, "log", "", CMD_REF(informative) revision_t rev; // this is instantiated even when not used, but it's lightweight asciik graph(cout); - while(! frontier.empty() && last != 0 && next != 0) + while(!frontier.empty() && last != 0 && next != 0) { revision_id const & rid = frontier.top().second; @@ -913,6 +958,9 @@ CMD(log, "log", "", CMD_REF(informative) if (app.opts.no_merges && rev.is_merge_node()) print_this = false; + else if (!app.opts.revision_selectors.empty() && + selected_revs.find(rid) == selected_revs.end()) + print_this = false; set interesting; // if rid is not marked we can jump directly to the marked ancestors, @@ -1009,16 +1057,18 @@ CMD(log, "log", "", CMD_REF(informative) frontier.pop(); // beware: rid is invalid from now on - for (set::const_iterator i = interesting.begin(); - i != interesting.end(); ++i) + if (!log_selected) { - if (use_disallowed && (disallowed.find(*i) != disallowed.end())) + // only add revs to the frontier when not logging specific selected revs + for (set::const_iterator i = interesting.begin(); + i != interesting.end(); ++i) { - continue; + if (!app.opts.to.empty() && (ending_revs.find(*i) != ending_revs.end())) + continue; + rev_height height; + db.get_rev_height(*i, height); + frontier.push(make_pair(height, *i)); } - rev_height height; - db.get_rev_height(*i, height); - frontier.push(make_pair(height, *i)); } } } ============================================================ --- monotone.texi 3446d31f637ac3426b7ff20ffaadde1b2b2ba0de +++ monotone.texi 48bab75212b595ebbd07ce72fb3442531ed3530a @@ -29,7 +29,7 @@ Copyright @copyright{} 2003, 2004 Graydon Hoare @* Copyright @copyright{} 2004, 2005, 2006 Nathaniel Smith @* -Copyright @copyright{} 2005, 2008 Derek Scherger @* +Copyright @copyright{} 2005, 2008, 2009 Derek Scherger @* Copyright @copyright{} 2005, 2006 Daniel Carosone @* Copyright @copyright{} 2006 Jeronimo Pellegrini @* Copyright @copyright{} 2006 Alex Queiroz @* @@ -2881,6 +2881,7 @@ @section Restrictions @item @command{list ignored} @item @command{list missing} @item @command{list changed} address@hidden @command{log} @end itemize Including either the old or new name of a renamed file or directory will @@ -5243,33 +5244,61 @@ @section Informative changed within the current subdirectory of the workspace. @item mtn log address@hidden mtn log address@hidden address@hidden address@hidden [...]] address@hidden [...]] [--brief] [--no-merges] [--no-files] [--diffs] address@hidden [...]] address@hidden mtn log address@hidden address@hidden address@hidden [...]] address@hidden [...]] address@hidden [...]] [--brief] [--no-merges] [--no-files] [--no-graph] [--diffs] address@hidden -This command prints out a log, in reverse-ancestry order, of small -history summaries. Each summary contains author, date, changelog and -comment information associated with a revision. +This command prints out a log, in forward ancestry order by default +but optionally in reverse ancestry order, of small history summaries. +Each summary contains author, date, branch, changelog and comment +information associated with a revision. -If @option{--brief} is given, the output consists of one line per revision -with the revision ID, the author, the date and the branches (separated -with commas). +If @option{--brief} is given, the output consists of two lines per +revision with the revision ID, the author, the date and the branches +(separated with commas). If the @option{--no-graph} option is also +given the output contains only one line per revision. -If @address@hidden is given, at most @var{n} log entries will be -given. +If @address@hidden is given, at most @var{n} log entries will +be given and log will trace through history in reverse-ancestry order, +from newer revisions to older revisions. -If @address@hidden is given, at most @var{n} log entries towards -the current head revision will be given from the workspace's base -revision in forward-ancestry order. This is useful to review changes -that will be applied to the workspace when @command{update} is run. +If @address@hidden is given, at most @var{n} log entries will +be given and log will trace through history in forward-ancestry order, +from older revisions to newer revisions. This is useful to review +changes that will be applied to the workspace when @command{update} is +run. -If @address@hidden is given, the command starts tracing back through -history from these revisions, otherwise it starts from the base revision of -your workspace. +If @address@hidden is given, log starts tracing through +history from the specified revisions, otherwise it starts from the +base revision of your workspace. Log will stop when it reaches the end +of the revision history or revisions specified by the @option{--to} +option. -If @address@hidden is given, log will only print entries for revisions -that would not also be printed when logging from the revisions specified in address@hidden This is useful for reviewing changes between two points -in history. +When tracing through history in reverse-ancestry order and address@hidden@var{id} is given, log will stop when it reaches the +specified revisions or any of their ancestors or the end of the +revision history. When tracing through history in forward-ancestry +order log will stop when it reaches the specified revisions or any of +their descendants or the end of the revision history. +If @address@hidden is given, log will print only the +specified revisions. + +If both @option{--from} and @option{--revision} are given only +revisions included by both options will be logged. Revisions specified +by @option{--revision} that are beyond the starting points specified +by @option{--from} will be excluded. + +Additionally, each of the @option{--from}, @option{--to} and address@hidden options accept selectors, see address@hidden These can be used in various ways to log interesting +revisions. For example: address@hidden +$ mtn log --revision b: +$ mtn log --revision today +$ mtn log --revision bob address@hidden verbatim +will log all revisions from the current branch, all revisions dated +today and all revisions with bob as the author, respectively. + By default, the log entries for merge nodes are shown. If @option{--no-merges} is given, the log entries for these nodes will be excluded. @@ -5277,6 +5306,9 @@ @section Informative If @option{--no-files} is given, the log output excludes the list of files changed in each revision. +If @option{--no-graph} is given, the log output excludes the ASCII revision graph +prefix on log output lines. + Specifying @option{--diffs} causes the log output to include a unified diff of the changes in each revision.