// Copyright (C) 2002 Graydon Hoare // // This program is made available under the GNU GPL version 2.0 or // greater. See the accompanying file COPYING for details. // // This program is distributed WITHOUT ANY WARRANTY; without even the // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. #include "base.hh" #include #include #include "charset.hh" #include "cmd.hh" #include "database_check.hh" #include "revision.hh" #include "constants.hh" #include "app_state.hh" using std::cin; using std::cout; using std::make_pair; using std::pair; using std::set; using std::string; CMD_GROUP(db, "db", "", CMD_REF(database), N_("Deals with the database"), ""); CMD(db_init, "init", "", CMD_REF(db), "", N_("Initializes a database"), N_("Creates a new database file and initializes it."), options::opts::none) { N(args.size() == 0, F("no arguments needed")); app.db.initialize(); } CMD(db_info, "info", "", CMD_REF(db), "", N_("Shows information about the database"), "", options::opts::none) { N(args.size() == 0, F("no arguments needed")); app.db.info(cout); } CMD(db_version, "version", "", CMD_REF(db), "", N_("Shows the database's version"), "", options::opts::none) { N(args.size() == 0, F("no arguments needed")); app.db.version(cout); } CMD(db_dump, "dump", "", CMD_REF(db), "", N_("Dumps the contents of the database"), N_("Generates a list of SQL instructions that represent the whole " "contents of the database. The resulting output is useful to later " "restore the database from a text file that serves as a backup."), options::opts::none) { N(args.size() == 0, F("no arguments needed")); app.db.dump(cout); } CMD(db_load, "load", "", CMD_REF(db), "", N_("Loads the contents of the database"), N_("Reads a list of SQL instructions that regenerate the contents of " "the database. This is supposed to be used in conjunction with the " "output generated by the 'dump' command."), options::opts::none) { N(args.size() == 0, F("no arguments needed")); app.db.load(cin); } CMD(db_migrate, "migrate", "", CMD_REF(db), "", N_("Migrates the database to a newer schema"), N_("Updates the database's internal schema to the most recent one. " "Needed to automatically resolve incompatibilities that may be " "introduced in newer versions of monotone."), options::opts::none) { N(args.size() == 0, F("no arguments needed")); app.db.migrate(); } CMD(db_execute, "execute", "", CMD_REF(db), "", N_("Executes an SQL command on the database"), N_("Directly executes the given SQL command on the database"), options::opts::none) { if (args.size() != 1) throw usage(execid); app.db.debug(idx(args, 0)(), cout); } CMD(db_kill_rev_locally, "kill_rev_locally", "", CMD_REF(db), "ID", N_("Kills a revision from the local database"), "", options::opts::none) { if (args.size() != 1) throw usage(execid); 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), "BRANCH", N_("Kills branch certificates from the local database"), "", options::opts::none) { if (args.size() != 1) throw usage(execid); app.db.delete_branch_named(cert_value(idx(args, 0)())); } CMD(db_kill_tag_locally, "kill_tag_locally", "", CMD_REF(db), "TAG", N_("Kills a tag from the local database"), "", options::opts::none) { if (args.size() != 1) throw usage(execid); app.db.delete_tag_named(cert_value(idx(args, 0)())); } CMD(db_check, "check", "", CMD_REF(db), "", N_("Does some sanity checks on the database"), N_("Ensures that the database is consistent by issuing multiple " "checks."), options::opts::none) { N(args.size() == 0, F("no arguments needed")); check_db(app); } CMD(db_changesetify, "changesetify", "", CMD_REF(db), "", N_("Converts the database to the changeset format"), "", options::opts::none) { N(args.size() == 0, F("no arguments needed")); build_changesets_from_manifest_ancestry(app); } CMD(db_rosterify, "rosterify", "", CMD_REF(db), "", N_("Converts the database to the rosters format"), "", options::opts::drop_attr) { N(args.size() == 0, F("no arguments needed")); build_roster_style_revs_from_manifest_style_revs(app); } CMD(db_regenerate_caches, "regenerate_caches", "", CMD_REF(db), "", N_("Regenerates the caches stored in the database"), "", options::opts::none) { N(args.size() == 0, F("no arguments needed")); regenerate_caches(app); } CMD_HIDDEN(clear_epoch, "clear_epoch", "", CMD_REF(db), "BRANCH", N_("Clears the branch's epoch"), "", options::opts::none) { if (args.size() != 1) throw usage(execid); app.db.clear_epoch(branch_name(idx(args, 0)())); } CMD(db_set_epoch, "set_epoch", "", CMD_REF(db), "BRANCH EPOCH", N_("Sets the branch's epoch"), "", options::opts::none) { if (args.size() != 2) throw usage(execid); epoch_data ed(idx(args, 1)()); N(ed.inner()().size() == constants::epochlen, F("The epoch must be %s characters") % constants::epochlen); app.db.set_epoch(branch_name(idx(args, 0)()), ed); } CMD(set, "set", "", CMD_REF(variables), N_("DOMAIN NAME VALUE"), N_("Sets a database variable"), N_("This command modifies (or adds if it did not exist before) the " "variable named NAME, stored in the database, and sets it to the " "given value in VALUE. The variable is placed in the domain DOMAIN."), options::opts::none) { if (args.size() != 3) throw usage(execid); var_domain d; var_name n; var_value v; internalize_var_domain(idx(args, 0), d); n = var_name(idx(args, 1)()); v = var_value(idx(args, 2)()); app.db.set_var(make_pair(d, n), v); } CMD(unset, "unset", "", CMD_REF(variables), N_("DOMAIN NAME"), N_("Unsets a database variable"), N_("This command removes the variable NAME from domain DOMAIN, which " "was previously stored in the database."), options::opts::none) { if (args.size() != 2) throw usage(execid); var_domain d; var_name n; internalize_var_domain(idx(args, 0), d); n = var_name(idx(args, 1)()); var_key k(d, n); N(app.db.var_exists(k), F("no var with name %s in domain %s") % n % d); app.db.clear_var(k); } CMD(complete, "complete", "", CMD_REF(informative), N_("(revision|file|key) PARTIAL-ID"), N_("Completes a partial identifier"), "", options::opts::verbose) { if (args.size() != 2) throw usage(execid); bool verbose = app.opts.verbose; N(idx(args, 1)().find_first_not_of("abcdef0123456789") == string::npos, F("non-hex digits in partial id")); if (idx(args, 0)() == "revision") { set completions; app.db.complete(idx(args, 1)(), completions); for (set::const_iterator i = completions.begin(); i != completions.end(); ++i) { if (!verbose) cout << i->inner()() << '\n'; else cout << describe_revision(app, *i) << '\n'; } } else if (idx(args, 0)() == "file") { set completions; app.db.complete(idx(args, 1)(), completions); for (set::const_iterator i = completions.begin(); i != completions.end(); ++i) cout << i->inner()() << '\n'; } else if (idx(args, 0)() == "key") { typedef set< pair > completions_t; completions_t completions; app.db.complete(idx(args, 1)(), completions); for (completions_t::const_iterator i = completions.begin(); i != completions.end(); ++i) { cout << i->first.inner()(); if (verbose) cout << ' ' << i->second(); cout << '\n'; } } else throw usage(execid); } CMD_HIDDEN(test_migration_step, "test_migration_step", "", CMD_REF(db), "SCHEMA", N_("Runs one step of migration on the specified database"), N_("This command migrates the given database from the specified " "schema in SCHEMA to its successor."), options::opts::none) { if (args.size() != 1) throw usage(execid); app.db.test_migration_step(idx(args,0)()); } // Local Variables: // mode: C++ // fill-column: 76 // c-file-style: "gnu" // indent-tabs-mode: nil // End: // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: