# # # patch "app_state.cc" # from [d3ce40848b3c514e3b1b7dbbeaf0e8987a9efa06] # to [907f829b5a05cb08d929ec85b245d5e6e975e027] # # patch "app_state.hh" # from [8967f9cf2d271717a32f37463fadf64f96158ca7] # to [002842b58181964f749775de1a5f7b3d616de85f] # # patch "automate.cc" # from [7e4511251379539cef80d5d9016cf025f42f3cb2] # to [d1b854b13c6b3fa0293fdb3c090bcbf88684e071] # # patch "cmd_db.cc" # from [f01ed60ffdc4fc22a4b7806161dc3c56b2e6fa1c] # to [3cc0588377b032c8b436a7145c9c077e91e30227] # # patch "cmd_diff_log.cc" # from [2d7990de11718287188fda97e47402b3dd0c36f5] # to [9104b66d0447196da48692151e68cc59f4735b52] # # patch "cmd_merging.cc" # from [8960af5e11ed8cc50bb05dc3dd29bd438287df5b] # to [fd4090137c7de4255f1f28befebcea19a1a2cf56] # # patch "cmd_netsync.cc" # from [9904866a3fd2d2b4811d405c57d6c2c0e39cc35a] # to [22dba42e1bbf5540eb7de9a918d314c1fbe466eb] # # patch "cmd_ws_commit.cc" # from [45743e541263b5feb6f6e605b9431a6f86688211] # to [0de256cb81117d341fec0f55cb7664d51db95988] # # patch "commands.cc" # from [e367cd7602398b96d4b5cd34882f0b43d97ffe97] # to [085f80ef9e44e89cf9a8679212f45319f377bda2] # # patch "database.cc" # from [dc4ea850465d2c7825192cd8732b70311a75cd4c] # to [12855c10843f0770e7ffe9138750ab18d9601c94] # # patch "monotone.cc" # from [5d6d40f09d80ce2fa27a125625aeeb71051dfbd6] # to [80f8d31167282f20be097b4497456af98e226381] # # patch "selectors.cc" # from [f36b8286536456f635dc8a3ec6ff9f64c6560eeb] # to [c60f8ced9db5d747054b763370746189a7e85874] # # patch "work.cc" # from [f7da6e8d4930dfe92ba826ba76033b70513fdaf0] # to [3ad67462febf24934ece86c2b7a4ee8417592618] # # patch "work.hh" # from [f86a3eb582bbf5f33572dac57d4281cb81076dbd] # to [db1bfaf458523bf0b4400bb90623823421bab433] # # patch "work_migration.cc" # from [3d58c3332cd195309eacf5cc52d4e88ad66a6c81] # to [7b1b482621ac0a94b6e7de9a6f6e4e279e497c54] # ============================================================ --- app_state.cc d3ce40848b3c514e3b1b7dbbeaf0e8987a9efa06 +++ app_state.cc 907f829b5a05cb08d929ec85b245d5e6e975e027 @@ -15,124 +15,18 @@ app_state::app_state() app_state::app_state() : lua(this), work(lua), - found_workspace(false), - mtn_automate_allowed(false), - branch_is_sticky(false) + mtn_automate_allowed(false) {} app_state::~app_state() {} void -app_state::process_options() +app_state::require_workspace() { - system_path database_option; - branch_name branch_option; - rsa_keypair_id key_option; - system_path keydir_option; - - if (!found_workspace) - return; - - work.check_ws_format(); - work.get_ws_options(database_option, branch_option, - key_option, keydir_option); - - // Workspace options are not to override the command line. - if (!opts.dbname_given) - { - I(opts.dbname.empty()); - opts.dbname = database_option; - } - - if (!opts.key_dir_given && !opts.conf_dir_given) - { - I(opts.key_dir.empty()); - opts.key_dir = keydir_option; - } - - if (opts.branchname().empty() && !branch_option().empty()) - { - opts.branchname = branch_option; - branch_is_sticky = true; - } - - L(FL("branch name is '%s'") % opts.branchname); - - if (!opts.key_given) - opts.signing_key = key_option; + workspace::require_workspace(opts); } -void -app_state::write_options(bool branch_is_sticky) -{ - system_path database_option; - branch_name branch_option; - rsa_keypair_id key_option; - system_path keydir_option; - - database_option = opts.dbname; - keydir_option = opts.key_dir; - - if (branch_is_sticky || this->branch_is_sticky) - branch_option = opts.branchname; - - if (opts.key_given) - key_option = opts.signing_key; - - work.set_ws_options(database_option, branch_option, - key_option, keydir_option); -} - -void -app_state::require_workspace(string const & explanation) -{ - N(found_workspace, - F("workspace required but not found%s%s") - % (explanation.empty() ? "" : "\n") % explanation); - write_options(false); -} - -void -app_state::create_workspace(system_path const & new_dir) -{ - N(!new_dir.empty(), F("invalid directory ''")); - - L(FL("creating workspace in %s") % new_dir); - - mkdir_p(new_dir); - go_to_workspace(new_dir); - mark_std_paths_used(); - - N(!directory_exists(bookkeeping_root), - F("monotone bookkeeping directory '%s' already exists in '%s'") - % bookkeeping_root % new_dir); - - L(FL("creating bookkeeping directory '%s' for workspace in '%s'") - % bookkeeping_root % new_dir); - - mkdir_p(bookkeeping_root); - - write_options(true); - - work.write_ws_format(); - work.blank_user_log(); - - if (lua.hook_use_inodeprints()) - work.enable_inodeprints(); - - found_workspace = true; - - bookkeeping_path dump_path; - work.get_local_dump_path(dump_path); - // The 'false' means that, e.g., if we're running checkout, - // then it's okay for dumps to go into our starting working - // dir's _MTN rather than the new workspace dir's _MTN. - global_sanity.set_dump_path(system_path(dump_path, false).as_external()); - - lua.load_rcfiles(opts); -} - // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- app_state.hh 8967f9cf2d271717a32f37463fadf64f96158ca7 +++ app_state.hh 002842b58181964f749775de1a5f7b3d616de85f @@ -31,14 +31,9 @@ public: lua_hooks lua; workspace work; - bool found_workspace; bool mtn_automate_allowed; - bool branch_is_sticky; - void process_options(); - void require_workspace(std::string const & explanation = ""); - void create_workspace(system_path const & dir); - void write_options(bool branch_is_sticky); + void require_workspace(); }; // Local Variables: ============================================================ --- automate.cc 7e4511251379539cef80d5d9016cf025f42f3cb2 +++ automate.cc d1b854b13c6b3fa0293fdb3c090bcbf88684e071 @@ -1790,26 +1790,7 @@ CMD_AUTOMATE(get_option, N_("OPTION"), F("wrong argument count")); CMD_REQUIRES_WORKSPACE(app); - - system_path database_option; - branch_name branch_option; - rsa_keypair_id key_option; - system_path keydir_option; - work.get_ws_options(database_option, branch_option, - key_option, keydir_option); - - string opt = args[0](); - - if (opt == "database") - output << database_option << '\n'; - else if (opt == "branch") - output << branch_option << '\n'; - else if (opt == "key") - output << key_option << '\n'; - else if (opt == "keydir") - output << keydir_option << '\n'; - else - N(false, F("'%s' is not a recognized workspace option") % opt); + work.print_ws_option(args[0], output); } // Name: get_content_changed ============================================================ --- cmd_db.cc f01ed60ffdc4fc22a4b7806161dc3c56b2e6fa1c +++ cmd_db.cc 3cc0588377b032c8b436a7145c9c077e91e30227 @@ -155,7 +155,7 @@ CMD(db_kill_rev_locally, "kill_rev_local // 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 - if (app.found_workspace) + if (workspace::found) { revision_t old_work_rev; app.work.get_work_rev(old_work_rev); ============================================================ --- cmd_diff_log.cc 2d7990de11718287188fda97e47402b3dd0c36f5 +++ cmd_diff_log.cc 9104b66d0447196da48692151e68cc59f4735b52 @@ -639,9 +639,6 @@ CMD(log, "log", "", CMD_REF(informative) database db(app); project_t project(db); - if (app.opts.from.size() == 0) - app.require_workspace("try passing a --from revision to start at"); - long last = app.opts.last; long next = app.opts.next; @@ -653,6 +650,9 @@ CMD(log, "log", "", CMD_REF(informative) if (app.opts.from.size() == 0) { + workspace::require_workspace(app.opts, + F("try passing a --from revision to start at").str()); + revision_t rev; app.work.get_work_rev(rev); for (edge_map::const_iterator i = rev.edges.begin(); ============================================================ --- cmd_merging.cc 8960af5e11ed8cc50bb05dc3dd29bd438287df5b +++ cmd_merging.cc fd4090137c7de4255f1f28befebcea19a1a2cf56 @@ -213,7 +213,7 @@ CMD(update, "update", "", CMD_REF(worksp P(F("already up to date at %s") % old_rid); // do still switch the workspace branch, in case they have used // update to switch branches. - app.write_options(true); + app.work.set_ws_options(app.opts, true); return; } @@ -302,7 +302,7 @@ CMD(update, "update", "", CMD_REF(worksp app.work.put_work_rev(remaining); app.work.update_any_attrs(db); app.work.maybe_update_inodeprints(db); - app.write_options(true); + app.work.set_ws_options(app.opts, true); if (switched_branch) P(F("switched branch; next commit will use branch %s") % app.opts.branchname()); ============================================================ --- cmd_netsync.cc 9904866a3fd2d2b4811d405c57d6c2c0e39cc35a +++ cmd_netsync.cc 22dba42e1bbf5540eb7de9a918d314c1fbe466eb @@ -305,7 +305,7 @@ CMD(clone, "clone", "", CMD_REF(network) // must do this after setting dbname so that _MTN/options is written // correctly - app.create_workspace(workspace_dir); + workspace::create_workspace(app.opts, app.lua, workspace_dir); database db(app); if (get_path_status(db.get_filename()) == path::nonexistent) ============================================================ --- cmd_ws_commit.cc 45743e541263b5feb6f6e605b9431a6f86688211 +++ cmd_ws_commit.cc 0de256cb81117d341fec0f55cb7664d51db95988 @@ -670,7 +670,7 @@ CMD(checkout, "checkout", "co", CMD_REF( (dir, F("checkout directory '%s' already exists") % dir); } - app.create_workspace(dir); + workspace::create_workspace(app.opts, app.lua, dir); shared_ptr empty_roster = shared_ptr(new roster_t()); roster_t current_roster; @@ -1269,7 +1269,7 @@ CMD(commit, "commit", "ci", CMD_REF(work } // the workspace should remember the branch we just committed to. - app.write_options(true); + app.work.set_ws_options(app.opts, true); // the work revision is now whatever changes remain on top of the revision // we just checked in. @@ -1334,7 +1334,7 @@ CMD_NO_WORKSPACE(setup, "setup", "", CMD else dir = "."; - app.create_workspace(dir); + workspace::create_workspace(app.opts, app.lua, dir); revision_t rev; make_revision_for_workspace(revision_id(), cset(), rev); @@ -1399,7 +1399,7 @@ CMD_NO_WORKSPACE(import, "import", "", C F("import directory '%s' doesn't exists") % dir, F("import directory '%s' is a file") % dir); - app.create_workspace(dir); + workspace::create_workspace(app.opts, app.lua, dir); try { @@ -1408,8 +1408,6 @@ CMD_NO_WORKSPACE(import, "import", "", C app.work.put_work_rev(rev); // prepare stuff for 'add' and so on. - app.found_workspace = true; // Yup, this is cheating! - args_vector empty_args; options save_opts; // add --unknown ============================================================ --- commands.cc e367cd7602398b96d4b5cd34882f0b43d97ffe97 +++ commands.cc 085f80ef9e44e89cf9a8679212f45319f377bda2 @@ -768,7 +768,10 @@ namespace commands // at this point we process the data from _MTN/options if // the command needs it. if (cmd->use_workspace_options()) - app.process_options(); + { + workspace::check_ws_format(); + workspace::get_ws_options(app.opts); + } cmd->exec(app, ident, args); } ============================================================ --- database.cc dc4ea850465d2c7825192cd8732b70311a75cd4c +++ database.cc 12855c10843f0770e7ffe9138750ab18d9601c94 @@ -3613,33 +3613,22 @@ database_impl::check_db_exists() { switch (get_path_status(filename)) { - case path::nonexistent: - N(false, F("database %s does not exist") % filename); - break; case path::file: return; + + case path::nonexistent: + N(false, F("database %s does not exist") % filename); + case path::directory: - { - system_path database_option; - branch_name branch_option; - rsa_keypair_id key_option; - system_path keydir_option; - if (workspace::get_ws_options_from_path( - filename, - database_option, - branch_option, - key_option, - keydir_option)) - { - N(database_option.empty(), - F("You gave a database option of: \n" - "%s\n" - "That is actually a workspace. Did you mean: \n" - "%s") % filename % database_option ); - } - N(false, F("%s is a directory, not a database") % filename); - } - break; + if (directory_is_workspace(filename)) + { + system_path workspace_database; + workspace::get_database_option(filename, workspace_database); + N(workspace_database.empty(), + F("%s is a workspace, not a database\n" + "(did you mean %s?)") % filename % workspace_database); + } + N(false, F("%s is a directory, not a database") % filename); } } ============================================================ --- monotone.cc 5d6d40f09d80ce2fa27a125625aeeb71051dfbd6 +++ monotone.cc 80f8d31167282f20be097b4497456af98e226381 @@ -210,7 +210,7 @@ cpp_main(int argc, char ** argv) // and if found, change directory to it // Certain commands may subsequently require a workspace or fail // if we didn't find one at this point. - app.found_workspace = find_and_go_to_workspace(app.opts.root); + workspace::found = find_and_go_to_workspace(app.opts.root); // Load all available monotonercs. If we found a workspace above, // we'll pick up _MTN/monotonerc as well as the user's monotonerc. @@ -220,10 +220,10 @@ cpp_main(int argc, char ** argv) // this needs to happen after the monotonercs have been read commands::command_id cmd = read_options(app.opts, optset, opt_args); - if (app.found_workspace) + if (workspace::found) { bookkeeping_path dump_path; - app.work.get_local_dump_path(dump_path); + workspace::get_local_dump_path(dump_path); // The 'false' means that, e.g., if we're running checkout, // then it's okay for dumps to go into our starting working ============================================================ --- selectors.cc f36b8286536456f635dc8a3ec6ff9f64c6560eeb +++ selectors.cc c60f8ced9db5d747054b763370746189a7e85874 @@ -159,7 +159,7 @@ decode_selector(app_state & app, : F("the empty head selector h: refers to " "the head of the current branch") ).str(); - app.require_workspace(msg); + workspace::require_workspace(app.opts, msg); sel = app.opts.branchname(); } break; ============================================================ --- work.cc f7da6e8d4930dfe92ba826ba76033b70513fdaf0 +++ work.cc 3ad67462febf24934ece86c2b7a4ee8417592618 @@ -10,6 +10,7 @@ #include "base.hh" #include "work.hh" +#include #include #include #include @@ -78,6 +79,82 @@ get_inodeprints_path(bookkeeping_path & L(FL("inodeprints path is %s") % ip_path); } +static void +get_user_log_path(bookkeeping_path & ul_path) +{ + ul_path = bookkeeping_root / user_log_file_name; + L(FL("user log path is %s") % ul_path); +} + +// + +bool +directory_is_workspace(system_path const & dir) +{ + // as far as the users of this function are concerned, a version 0 + // workspace (MT directory instead of _MTN) does not count. + return directory_exists(dir / bookkeeping_root_component); +} + +bool workspace::found; +bool workspace::branch_is_sticky; + +void +workspace::require_workspace(options const & opts, + string const & explanation) +{ + N(workspace::found, + F("workspace required but not found%s%s") + % (explanation.empty() ? "" : "\n") % explanation); + set_ws_options(opts, false); +} + +void +workspace::create_workspace(options const & opts, + lua_hooks & lua, + system_path const & new_dir) +{ + N(!new_dir.empty(), F("invalid directory ''")); + + L(FL("creating workspace in %s") % new_dir); + + mkdir_p(new_dir); + go_to_workspace(new_dir); + mark_std_paths_used(); + + N(!directory_exists(bookkeeping_root), + F("monotone bookkeeping directory '%s' already exists in '%s'") + % bookkeeping_root % new_dir); + + L(FL("creating bookkeeping directory '%s' for workspace in '%s'") + % bookkeeping_root % new_dir); + + mkdir_p(bookkeeping_root); + + workspace::found = true; + workspace::set_ws_options(opts, true); + workspace::write_ws_format(); + + data empty; + bookkeeping_path ul_path; + get_user_log_path(ul_path); + write_data(ul_path, empty); + + if (lua.hook_use_inodeprints()) + { + bookkeeping_path ip_path; + get_inodeprints_path(ip_path); + write_data(ip_path, empty); + } + + bookkeeping_path dump_path; + workspace::get_local_dump_path(dump_path); + // The 'false' means that, e.g., if we're running checkout, + // then it's okay for dumps to go into our starting working + // dir's _MTN rather than the new workspace dir's _MTN. + global_sanity.set_dump_path(system_path(dump_path, false).as_external()); +} + // routines for manipulating the bookkeeping directory // revision file contains a partial revision describing the workspace @@ -206,13 +283,6 @@ void // user log file void -workspace::get_user_log_path(bookkeeping_path & ul_path) -{ - ul_path = bookkeeping_root / user_log_file_name; - L(FL("user log path is %s") % ul_path); -} - -void workspace::read_user_log(utf8 & dat) { bookkeeping_path ul_path; @@ -256,106 +326,55 @@ workspace::has_contents_user_log() // _MTN/options handling. -void -workspace::get_ws_options(system_path & database_option, - branch_name & branch_option, - rsa_keypair_id & key_option, - system_path & keydir_option) +static void +read_options_file(any_path const & optspath, + system_path & database_option, + branch_name & branch_option, + rsa_keypair_id & key_option, + system_path & keydir_option) { - system_path empty_path; - get_ws_options_from_path(empty_path, database_option, - branch_option, key_option, keydir_option); -} - -bool -workspace::get_ws_options_from_path(system_path const & workspace, - system_path & database_option, - branch_name & branch_option, - rsa_keypair_id & key_option, - system_path & keydir_option) -{ - any_path * o_path; - bookkeeping_path ws_o_path; - system_path sys_o_path; - - if (workspace.empty()) + data dat; + try { - get_options_path(ws_o_path); - o_path = & ws_o_path; + read_data(optspath, dat); } - else + catch (exception & e) { - get_options_path(workspace, sys_o_path); - o_path = & sys_o_path; + W(F("Failed to read options file %s: %s") % optspath % e.what()); + return; } - try + basic_io::input_source src(dat(), optspath.as_external()); + basic_io::tokenizer tok(src); + basic_io::parser parser(tok); + + while (parser.symp()) { - if (path_exists(*o_path)) - { - data dat; - read_data(*o_path, dat); - - basic_io::input_source src(dat(), o_path->as_external()); - basic_io::tokenizer tok(src); - basic_io::parser parser(tok); - - while (parser.symp()) - { - string opt, val; - parser.sym(opt); - parser.str(val); - - if (opt == "database") - database_option = system_path(val); - else if (opt == "branch") - branch_option = branch_name(val); - else if (opt == "key") - internalize_rsa_keypair_id(utf8(val), key_option); - else if (opt == "keydir") - keydir_option = system_path(val); - else - W(F("unrecognized key '%s' in options file %s - ignored") - % opt % o_path); - } - return true; - } + string opt, val; + parser.sym(opt); + parser.str(val); + + if (opt == "database") + database_option = system_path(val); + else if (opt == "branch") + branch_option = branch_name(val); + else if (opt == "key") + internalize_rsa_keypair_id(utf8(val), key_option); + else if (opt == "keydir") + keydir_option = system_path(val); else - return false; + W(F("unrecognized key '%s' in options file %s - ignored") + % opt % optspath); } - catch(exception & e) - { - W(F("Failed to read options file %s: %s") % *o_path % e.what()); - } - - return false; } -void -workspace::set_ws_options(system_path & database_option, - branch_name & branch_option, - rsa_keypair_id & key_option, - system_path & keydir_option) +static void +write_options_file(bookkeeping_path const & optspath, + system_path const & database_option, + branch_name const & branch_option, + rsa_keypair_id const & key_option, + system_path const & keydir_option) { - // If caller passes an empty string for any of the incoming options, - // we want to leave that option as is in _MTN/options, not write out - // an empty option. - system_path old_database_option; - branch_name old_branch_option; - rsa_keypair_id old_key_option; - system_path old_keydir_option; - get_ws_options(old_database_option, old_branch_option, - old_key_option, old_keydir_option); - - if (database_option.as_internal().empty()) - database_option = old_database_option; - if (branch_option().empty()) - branch_option = old_branch_option; - if (key_option().empty()) - key_option = old_key_option; - if (keydir_option.as_internal().empty()) - keydir_option = old_keydir_option; - basic_io::stanza st; if (!database_option.as_internal().empty()) st.push_str_pair(symbol("database"), database_option.as_internal()); @@ -372,24 +391,139 @@ workspace::set_ws_options(system_path & basic_io::printer pr; pr.print_stanza(st); + try + { + write_data(optspath, data(pr.buf)); + } + catch(exception & e) + { + W(F("Failed to write options file %s: %s") % optspath % e.what()); + } +} +void +workspace::get_ws_options(options & opts) +{ + if (!workspace::found) + return; + + system_path database_option; + branch_name branch_option; + rsa_keypair_id key_option; + system_path keydir_option; + bookkeeping_path o_path; get_options_path(o_path); - try + read_options_file(o_path, + database_option, branch_option, key_option, keydir_option); + + // Workspace options are not to override the command line. + if (!opts.dbname_given) { - write_data(o_path, data(pr.buf)); + I(opts.dbname.empty()); + opts.dbname = database_option; } - catch(exception & e) + + if (!opts.key_dir_given && !opts.conf_dir_given) { - W(F("Failed to write options file %s: %s") % o_path % e.what()); + I(opts.key_dir.empty()); + opts.key_dir = keydir_option; } + + if (opts.branchname().empty() && !branch_option().empty()) + { + opts.branchname = branch_option; + branch_is_sticky = true; + } + + L(FL("branch name is '%s'") % opts.branchname); + + if (!opts.key_given) + opts.signing_key = key_option; } +void +workspace::get_database_option(system_path const & workspace, + system_path & database_option) +{ + branch_name branch_option; + rsa_keypair_id key_option; + system_path keydir_option; + + system_path o_path = (workspace + / bookkeeping_root_component + / options_file_name); + read_options_file(o_path, + database_option, branch_option, key_option, keydir_option); +} + +void +workspace::set_ws_options(options const & opts, bool branch_is_sticky) +{ + N(workspace::found, F("workspace required but not found")); + + bookkeeping_path o_path; + get_options_path(o_path); + + // If any of the incoming options was empty, we want to leave that option + // as is in _MTN/options, not write out an empty option. + system_path database_option; + branch_name branch_option; + rsa_keypair_id key_option; + system_path keydir_option; + + if (file_exists(o_path)) + read_options_file(o_path, + database_option, branch_option, key_option, keydir_option); + + if (!opts.dbname.as_internal().empty()) + database_option = opts.dbname; + if (!opts.key_dir.as_internal().empty()) + keydir_option = opts.key_dir; + if ((branch_is_sticky || workspace::branch_is_sticky) + && !opts.branchname().empty()) + branch_option = opts.branchname; + if (opts.key_given) + key_option = opts.signing_key; + + write_options_file(o_path, + database_option, branch_option, key_option, keydir_option); +} + +void +workspace::print_ws_option(utf8 const & opt, std::ostream & output) +{ + N(workspace::found, F("workspace required but not found")); + + bookkeeping_path o_path; + get_options_path(o_path); + + system_path database_option; + branch_name branch_option; + rsa_keypair_id key_option; + system_path keydir_option; + read_options_file(o_path, + database_option, branch_option, key_option, keydir_option); + + if (opt() == "database") + output << database_option << '\n'; + else if (opt() == "branch") + output << branch_option << '\n'; + else if (opt() == "key") + output << key_option << '\n'; + else if (opt() == "keydir") + output << keydir_option << '\n'; + else + N(false, F("'%s' is not a recognized workspace option") % opt); +} + // local dump file void workspace::get_local_dump_path(bookkeeping_path & d_path) { + N(workspace::found, F("workspace required but not found")); + d_path = bookkeeping_root / local_dump_file_name; L(FL("local dump path is %s") % d_path); } ============================================================ --- work.hh f86a3eb582bbf5f33572dac57d4281cb81076dbd +++ work.hh db1bfaf458523bf0b4400bb90623823421bab433 @@ -24,6 +24,20 @@ class lua_hooks; // // this file defines structures to deal with the "workspace" of a tree // +// at present the presence or absence of a workspace is intrinsically global +// state, because it affects things like file_path construction (over in +// paths.cc) and the current working directory. also, there are a bunch of +// operations, mostly during program initialization, that are conditional on +// whether or not we are inside a workspace. this has two visible +// consequences to this api: first, you cannot create more than one +// workspace object, and second, the workspace class has many class methods +// as well as many instance methods. class methods can be used when you're +// not sure yet whether or not there is a workspace. instance methods can +// only be used if there definitely is a workspace; the workspace object +// constructor will throw an N() if there isn't one. (this can also be +// triggered by the class method require_workspace, for the sake of a few +// places that need to do that but not create the workspace object yet.) +// // // workspace book-keeping files are stored in a directory called _MTN, off @@ -64,8 +78,28 @@ class lua_hooks; // conditions, but works in practice (it is, for instance, the same // expectation used by "make"). nonetheless, this mode is off by default. +bool directory_is_workspace(system_path const & dir); + struct workspace { + // This is a public flag because it's set from monotone.cc using a + // function (find_and_go_to_workspace) which cannot presently be moved + // from paths.cc. + static bool found; + +private: + // This is used by get_ws_options and set_ws_options. + static bool branch_is_sticky; + +public: + static void require_workspace(options const & opts, + std::string const & explanation = ""); + + static void create_workspace(options const & opts, + lua_hooks & lua, + system_path const & new_dir); + + // Methods for manipulating the workspace's content. void find_missing(roster_t const & new_roster_shape, node_restriction const & mask, std::set & missing); @@ -146,7 +180,6 @@ struct workspace // the user log is then blanked. If the commit does not succeed, no // change is made to the user log file. - void get_user_log_path(bookkeeping_path & ul_path); void read_user_log(utf8 & dat); void write_user_log(utf8 const & dat); void blank_user_log(); @@ -155,43 +188,33 @@ struct workspace // the "options map" is another administrative file, stored in // _MTN/options. it keeps a list of name/value pairs which are considered // "persistent options", associated with a particular workspace and - // implied unless overridden on the command line. the set of valid keys - // corresponds exactly to the argument list of these functions. + // implied unless overridden on the command line. + static void get_ws_options(options & opts); + static void get_database_option(system_path const & workspace_root, + system_path & database_option); + static void set_ws_options(options const & opts, bool branch_is_sticky); + static void print_ws_option(utf8 const & opt, std::ostream & output); - static bool get_ws_options_from_path(system_path const & workspace, - system_path & database_option, - branch_name & branch_option, - rsa_keypair_id & key_option, - system_path & keydir_option); - void get_ws_options(system_path & database_option, - branch_name & branch_option, - rsa_keypair_id & key_option, - system_path & keydir_option); - void set_ws_options(system_path & database_option, - branch_name & branch_option, - rsa_keypair_id & key_option, - system_path & keydir_option); - // the "workspace format version" is a nonnegative integer value, stored // in _MTN/format as an unadorned decimal number. at any given time // monotone supports actual use of only one workspace format. - // check_ws_format throws an error if the workspace's format number is not - // equal to the currently supported format number. it is automatically - // called for all commands defined with CMD() (not CMD_NO_WORKSPACE()). - // migrate_ws_format is called only on explicit user request (mtn ws - // migrate) and will convert a workspace from any older format to the new - // one. unlike most routines in this class, it is defined in its own - // file, work_migration.cc. finally, write_ws_format is called only when - // a workspace is created, and simply writes the current workspace format - // number to _MTN/format. - void check_ws_format(); + // check_ws_format throws an error if the workspace exists but its format + // number is not equal to the currently supported format number. it is + // automatically called for all commands defined with CMD() (not + // CMD_NO_WORKSPACE()). migrate_ws_format is called only on explicit user + // request (mtn ws migrate) and will convert a workspace from any older + // format to the new one. finally, write_ws_format is called only when a + // workspace is created, and simply writes the current workspace format + // number to _MTN/format. unlike most routines in this class, these + // functions are defined in their own file, work_migration.cc. + static void check_ws_format(); + static void write_ws_format(); void migrate_ws_format(); - void write_ws_format(); // the "local dump file' is a debugging file, stored in _MTN/debug. if we // crash, we save some debugging information here. - void get_local_dump_path(bookkeeping_path & d_path); + static void get_local_dump_path(bookkeeping_path & d_path); // the 'inodeprints file' contains inode fingerprints ============================================================ --- work_migration.cc 3d58c3332cd195309eacf5cc52d4e88ad66a6c81 +++ work_migration.cc 7b1b482621ac0a94b6e7de9a6f6e4e279e497c54 @@ -112,6 +112,9 @@ workspace::check_ws_format() void workspace::check_ws_format() { + if (!workspace::found) + return; + unsigned int format = get_ws_format(); // Don't give user false expectations about format 0.