# # # patch "cmd_ws_commit.cc" # from [3fcaf3975fa321f3c19c87432fc83b3d2fd7d652] # to [522d9a9e138d06c46a4a462411d1dadd53874d21] # # patch "tests/bisect/__driver__.lua" # from [b4a1510356c9aec93b7fe1b7cd4bdc1e82bcc623] # to [a73ff819c30fa23311b146f206405ae47d199330] # ============================================================ --- cmd_ws_commit.cc 3fcaf3975fa321f3c19c87432fc83b3d2fd7d652 +++ cmd_ws_commit.cc 522d9a9e138d06c46a4a462411d1dadd53874d21 @@ -1473,15 +1473,20 @@ CMD_GROUP(bisect, "bisect", "", CMD_REF( CMD_GROUP(bisect, "bisect", "", CMD_REF(informative), N_("Search revisions to find where a change first appeared"), - N_("This command subdivides a set of revisions into good and bad sets " - "and successively narrows the set to find the revision that introduced " - "some change")); + N_("These commands subdivide a set of revisions into good, bad " + "and untested subsets and successively narrow the untested set " + "to find the first revision that introduced some change.")); CMD(reset, "reset", "", CMD_REF(bisect), "", - N_("Resets the current search information allowing a new search to be started"), - N_("All current search information is discarded"), + N_("Reset the current bisection search"), + N_("Update the workspace back to the revision from which the bisection " + "was started and remove all current search information, allowing a new " + "search to be started."), options::opts::none) { + if (args.size() != 0) + throw usage(execid); + database db(app); workspace work(app); project_t project(db); @@ -1535,42 +1540,6 @@ CMD(reset, "reset", "", CMD_REF(bisect), work.remove_bisect_info(); } -CMD(bisect_status, "status", "", CMD_REF(bisect), "", - N_("Reports on the current status of the search"), - N_("Lists the number of revisions remaining to be searched"), - options::opts::none) -{ - workspace work(app); - vector bisect; - work.get_bisect_info(bisect); - - for (vector::const_iterator i = bisect.begin(); - i != bisect.end(); ++i) - { - switch (i->first) - { - case bisect::start: - std::cerr << " start " << i->second << std::endl; - break; - case bisect::good: - std::cerr << " good " << i->second << std::endl; - break; - case bisect::bad: - std::cerr << " bad " << i->second << std::endl; - break; - case bisect::skipped: - std::cerr << "skipped " << i->second << std::endl; - break; - } - } - - // report the number of revs marked as good - // report the number of revs marked as bad - // report the number of revs marked as skipped - // report the number of remaining revs - // if --verbose report the number of remaining revs after each good/bad/skip operation -} - // FIXME: this is copied from cmd_diff_log.cc class revision_loader { @@ -1629,67 +1598,20 @@ class revision_loader }; - -static void -bisect_update(app_state & app, commands::command_id const & execid, bisect::type type) +static bool +bisect_setup(database & db, + vector const & info, + set & remaining) { - database db(app); - workspace work(app); - project_t project(db); revision_loader loader(db); + set good, bad, skipped; - parent_map parents; - work.get_parent_rosters(db, parents); - E(parents.size() == 1, origin::user, - F("this command can only be used in a single-parent workspace")); + E(!info.empty(), origin::user, + F("no bisection in progress")); - revision_id current_id = parent_id(*parents.begin()); - - temp_node_id_source nis; - roster_t current_roster; - work.get_current_roster_shape(db, nis, current_roster); - work.update_current_roster_from_filesystem(current_roster); - - E(parent_roster(parents.begin()) == current_roster, origin::user, - F("'%s' can only be used in a workspace with no pending changes") % - join_words(execid)()); - - set marked_ids; - - // mark the current or specified revisions as good, bad or skipped - if (app.opts.revision_selectors.empty()) - marked_ids.insert(current_id); - else - 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); - marked_ids.insert(rids.begin(), rids.end()); - } - - vector bisect; - work.get_bisect_info(bisect); - - if (bisect.empty()) + for (vector::const_iterator i = info.begin(); + i != info.end(); ++i) { - bisect.push_back(make_pair(bisect::start, current_id)); - P(F("bisection started at revision %s") % describe_revision(project, current_id)); - } - - // push back all marked revs with the appropriate type - for (set::const_iterator i = marked_ids.begin(); - i != marked_ids.end(); ++i) - bisect.push_back(make_pair(type, *i)); - - work.put_bisect_info(bisect); - - set good, bad, skipped; - for (vector::const_iterator i = bisect.begin(); - i != bisect.end(); ++i) - { switch (i->first) { case bisect::start: @@ -1712,13 +1634,13 @@ bisect_update(app_state & app, commands: { P(F("bisecting revisions; %d good; %d bad; %d skipped; specify good revisions to start search") % good.size() % bad.size() % skipped.size()); - return; + return false; } else if (!good.empty() && bad.empty()) { P(F("bisecting revisions; %d good; %d bad; %d skipped; specify bad revisions to start search") % good.size() % bad.size() % skipped.size()); - return; + return false; } I(!good.empty()); @@ -1774,7 +1696,6 @@ bisect_update(app_state & app, commands: known_bad.begin(), known_bad.end(), inserter(removed, removed.begin())); - set remaining; set_difference(searchable.begin(), searchable.end(), removed.begin(), removed.end(), inserter(remaining, remaining.end())); @@ -1795,6 +1716,70 @@ bisect_update(app_state & app, commands: revision_id bad0 = bad_sorted[0]; remaining.insert(bad0); + return true; +} + +static void +bisect_update(app_state & app, + commands::command_id const & execid, + bisect::type type) +{ + database db(app); + workspace work(app); + project_t project(db); + + parent_map parents; + work.get_parent_rosters(db, parents); + E(parents.size() == 1, origin::user, + F("this command can only be used in a single-parent workspace")); + + revision_id current_id = parent_id(*parents.begin()); + + temp_node_id_source nis; + roster_t current_roster; + work.get_current_roster_shape(db, nis, current_roster); + work.update_current_roster_from_filesystem(current_roster); + + E(parent_roster(parents.begin()) == current_roster, origin::user, + F("'%s' can only be used in a workspace with no pending changes") % + join_words(execid)()); + + set marked_ids; + + // mark the current or specified revisions as good, bad or skipped + if (app.opts.revision_selectors.empty()) + marked_ids.insert(current_id); + else + 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); + marked_ids.insert(rids.begin(), rids.end()); + } + + vector info; + work.get_bisect_info(info); + + if (info.empty()) + { + info.push_back(make_pair(bisect::start, current_id)); + P(F("bisection started at revision %s") % describe_revision(project, current_id)); + } + + // push back all marked revs with the appropriate type + for (set::const_iterator i = marked_ids.begin(); + i != marked_ids.end(); ++i) + info.push_back(make_pair(type, *i)); + + work.put_bisect_info(info); + + set remaining; + if (!bisect_setup(db, info, remaining)) + return; + // remove the current revision from the remaining set so it cannot be // chosen as the next update target. this may remove the top bad revision // and end the search. @@ -1831,19 +1816,38 @@ bisect_update(app_state & app, commands: cset empty; make_revision_for_workspace(selected_id, empty, selected_rev); - // FIXME: work.put_update_id(current_id); // probably not wanted - work.put_work_rev(selected_rev); work.maybe_update_inodeprints(db); // FIXME: what about the workspace branch option? - // what if the chosen rev is in more than one branch?!? + // what if the selected rev is in more than one branch?!? } +CMD(bisect_status, "status", "", CMD_REF(bisect), "", + N_("Reports on the current status of the bisection search"), + N_("Lists the total number of revisions in the search set; " + "the number of revisions that have been determined to be good or bad; " + "the number of revisions that have been skipped " + "and the number of revisions remaining to be tested."), + options::opts::none) +{ + if (args.size() != 0) + throw usage(execid); + + database db(app); + workspace work(app); + + vector info; + work.get_bisect_info(info); + + set remaining; + bisect_setup(db, info, remaining); +} + CMD(skip, "skip", "", CMD_REF(bisect), "", - N_("Excludes the current revision or specified revisions from the search set"), - N_("Excluded revisions are removed from the set being searched. Revisions " - "that cannot be tested for some reason should be excluded"), + N_("Excludes the current revision or specified revisions from the search"), + N_("Skipped revisions are removed from the set being searched. Revisions " + "that cannot be tested for some reason should be skipped."), options::opts::revision) { if (args.size() != 0) @@ -1853,7 +1857,7 @@ CMD(bad, "bad", "", CMD_REF(bisect), "", CMD(bad, "bad", "", CMD_REF(bisect), "", N_("Marks the current revision or specified revisions as bad"), - N_("Bad revisions are included in the set being searched"), + N_("Known bad revisions are removed from the set being searched."), options::opts::revision) { if (args.size() != 0) @@ -1863,7 +1867,7 @@ CMD(good, "good", "", CMD_REF(bisect), " CMD(good, "good", "", CMD_REF(bisect), "", N_("Marks the current revision or specified revisions as good"), - N_("Good revisions are excluded from the set being searched"), + N_("Known good revisions are removed from the set being searched."), options::opts::revision) { if (args.size() != 0) ============================================================ --- tests/bisect/__driver__.lua b4a1510356c9aec93b7fe1b7cd4bdc1e82bcc623 +++ tests/bisect/__driver__.lua a73ff819c30fa23311b146f206405ae47d199330 @@ -81,9 +81,9 @@ check(exists("head3")) -- test 1: addition of head3 is considered to be the error check(exists("head3")) -check(not exists("_MTN/bisect")) +check(not exists("_MTN/bisect")) check(mtn("bisect", "bad"), 0, false, false) -check(exists("_MTN/bisect")) +check(exists("_MTN/bisect")) check(mtn("bisect", "good", "--revision", head1), 0, false, false) while rev ~= base_revision() do @@ -119,14 +119,14 @@ check(mtn("bisect", "reset"), 0, false, -- test 2: addition of left2 is considered to be the error check(mtn("bisect", "reset"), 0, false, false) -check(not exists("_MTN/bisect")) +check(not exists("_MTN/bisect")) check(base_revision() == tail4) rev = base_revision() check(exists("left2")) check(mtn("bisect", "good", "--revision", head1), 0, false, false) -check(exists("_MTN/bisect")) +check(exists("_MTN/bisect")) check(mtn("bisect", "bad"), 0, false, false) while rev ~= base_revision() do @@ -144,14 +144,14 @@ check(mtn("bisect", "reset"), 0, false, -- test 3: addition of right3 is considered to be the error check(mtn("bisect", "reset"), 0, false, false) -check(not exists("_MTN/bisect")) +check(not exists("_MTN/bisect")) check(base_revision() == tail4) rev = base_revision() check(exists("right3")) check(mtn("bisect", "good", "--revision", head1), 0, false, false) -check(exists("_MTN/bisect")) +check(exists("_MTN/bisect")) check(mtn("bisect", "bad"), 0, false, false) while rev ~= base_revision() do @@ -169,14 +169,14 @@ check(mtn("bisect", "reset"), 0, false, -- test 4: addition of tail1 is considered to be the error check(mtn("bisect", "reset"), 0, false, false) -check(not exists("_MTN/bisect")) +check(not exists("_MTN/bisect")) check(base_revision() == tail4) rev = base_revision() check(exists("tail1")) check(mtn("bisect", "good", "--revision", head1), 0, false, false) -check(exists("_MTN/bisect")) +check(exists("_MTN/bisect")) check(mtn("bisect", "bad"), 0, false, false) while rev ~= base_revision() do @@ -196,16 +196,19 @@ check(base_revision() == tail4) check(mtn("bisect", "reset"), 0, false, false) check(base_revision() == tail4) -check(not exists("_MTN/bisect")) - +check(not exists("_MTN/bisect")) +check(mtn("bisect", "status"), 1, false, false) + rev = base_revision() check(exists("tail1")) check(mtn("bisect", "good", "--revision", head4, "--revision", right4), 0, false, false) -check(exists("_MTN/bisect")) +check(exists("_MTN/bisect")) check(mtn("bisect", "skip", "--revision", tail1, "--revision", head4), 0, false, false) check(mtn("bisect", "bad", "--revision", tail4, "--revision", left4), 0, false, false) +check(mtn("bisect", "status"), 0, false, false) + while rev ~= base_revision() do rev = base_revision() if exists("left3") then