# # # delete "tests/db_kill_rev_locally_command_2" # # delete "tests/db_kill_rev_locally_command_2/__driver__.lua" # # rename "tests/db_kill_rev_and_recommit" # to "tests/db_kill_rev_locally_and_recommit" # # rename "tests/db_kill_rev_locally_command" # to "tests/db_kill_rev_locally" # # patch "cmd_db.cc" # from [f07d0fd04917a29d7bcbc9bfa4a676872107c339] # to [249a3148b5f97e77a942f350028fb5a1e1e1db6e] # # patch "tests/db_kill_rev_locally/__driver__.lua" # from [7e7b3aa9e42792245a60745ca257ac29f4d57b0c] # to [058334146603b04f82a488e49f8f715d9ba31939] # # patch "work.cc" # from [8ccb08cdc7afd8db1c33770ae69ab827c5a96ce4] # to [eea53258aed3aad15c4ec0adf7f3509ec5a0bbd1] # # patch "work.hh" # from [4bfc4c2a48bf132b3701704c046ba77bdcb6302c] # to [bfd8134df9c1f339012c929233e6b12fe5a2cc48] # ============================================================ --- cmd_db.cc f07d0fd04917a29d7bcbc9bfa4a676872107c339 +++ cmd_db.cc 249a3148b5f97e77a942f350028fb5a1e1e1db6e @@ -29,27 +29,6 @@ CMD_GROUP(db, "db", "", CMD_REF(database N_("Deals with the database"), ""); -// Deletes a revision from the local database. This can be used to -// 'undo' a changed revision from a local database without leaving -// (much of) a trace. - -static void -kill_rev_locally(app_state& app, string const& id) -{ - revision_id ident; - complete(app, id, ident); - N(app.db.revision_exists(ident), - F("no such revision '%s'") % ident); - - //check that the revision does not have any children - set children; - app.db.get_revision_children(ident, children); - N(!children.size(), - F("revision %s already has children. We cannot kill it.") % ident); - - app.db.delete_existing_rev_and_certs(ident); -} - CMD(db_init, "init", "", CMD_REF(db), "", N_("Initializes a database"), N_("Creates a new database file and initializes it."), @@ -141,7 +120,65 @@ CMD(db_kill_rev_locally, "kill_rev_local if (args.size() != 1) throw usage(execid); - kill_rev_locally(app,idx(args, 0)()); + revision_id revid; + + complete(app, idx(args, 0)(), revid); + N(app.db.revision_exists(revid), + F("no such revision '%s'") % revid); + + // Check that the revision does not have any children + std::set children; + app.db.get_revision_children(revid, children); + N(!children.size(), + F("revision %s already has children. We cannot kill it.") % revid); + + // If we're executing this in a workspace, check if the workspace parent + // revision is the one to kill. If so, write out the changes made in this + // particular revision to _MTN/revision to allow the user redo his (fixed) + // commit afterwards. Of course we can't do this at all if + // + // a) the user is currently not inside a workspace + // b) the user has updated the current workspace to another revision already + // thus the working revision is no longer based on the revision we're + // trying to kill + // c) there are uncomitted changes in the working revision of this workspace. + // this *eventually* could be handled with a workspace merge scenario, but + // is left out for now + app.allow_workspace(); + if (app.found_workspace) + { + revision_t old_work_rev; + app.work.get_work_rev(old_work_rev); + + for (edge_map::const_iterator i = old_work_rev.edges.begin(); + i != old_work_rev.edges.end(); i++) + { + if (edge_old_revision(i) != revid) + continue; + + N(!app.work.has_changes(), + F("Cannot kill revision %s,\n" + "because it would leave the current workspace in an invalid\n" + "state, from which monotone cannot recover automatically since\n" + "the workspace contains uncommitted changes.\n" + "Consider updating your workspace to another revision first,\n" + "before you try to kill this revision again.") % revid); + + P(F("applying changes from %s on the current workspace") + % revid); + + revision_t new_work_rev; + app.db.get_revision(revid, new_work_rev); + new_work_rev.made_for = made_for_workspace; + app.work.put_work_rev(new_work_rev); + + // extra paranoia... we _should_ never run this section twice + // since a merged workspace would fail early with work.has_changes() + break; + } + } + + app.db.delete_existing_rev_and_certs(revid); } CMD(db_kill_branch_certs_locally, "kill_branch_certs_locally", "", CMD_REF(db), ============================================================ --- tests/db_kill_rev_locally/__driver__.lua 7e7b3aa9e42792245a60745ca257ac29f4d57b0c +++ tests/db_kill_rev_locally/__driver__.lua 058334146603b04f82a488e49f8f715d9ba31939 @@ -22,3 +22,10 @@ check(mtn("db", "check"), 0, false, fals check(mtn("db", "kill_rev_locally", child), 0, false, false) check(mtn("automate", "get_revision", child), 1, false, false) check(mtn("db", "check"), 0, false, false) + +-- now that we've killed the child, the head is ancestor and should be +-- possible to kill as well +check(mtn("automate", "get_revision", ancestor), 0, false, false) +check(mtn("db", "kill_rev_locally", ancestor), 0, false, false) +check(mtn("automate", "get_revision", ancestor), 1, false, false) +check(mtn("db", "check"), 0, false, false) ============================================================ --- work.cc 8ccb08cdc7afd8db1c33770ae69ab827c5a96ce4 +++ work.cc eea53258aed3aad15c4ec0adf7f3509ec5a0bbd1 @@ -180,6 +180,26 @@ workspace::get_current_roster_shape(rost } } +bool +workspace::has_changes() +{ + parent_map parents; + get_parent_rosters(parents); + + // if we have more than one parent roster then this workspace contains + // a merge which means this is always a committable change + if (parents.size() > 1) + return true; + + temp_node_id_source nis; + roster_t new_roster, old_roster = parent_roster(parents.begin()); + + get_current_roster_shape(new_roster, nis); + update_current_roster_from_filesystem(new_roster); + + return !(old_roster == new_roster); +} + // user log file void ============================================================ --- work.hh 4bfc4c2a48bf132b3701704c046ba77bdcb6302c +++ work.hh bfd8134df9c1f339012c929233e6b12fe5a2cc48 @@ -98,6 +98,7 @@ struct workspace void update_any_attrs(); + bool has_changes(); // write out a new (partial) revision describing the current workspace; // the important pieces of this are the base revision id and the "shape"