# # # add_dir "tests/clone_-b_no_dir" # # add_dir "tests/clone_creates__MTN_log" # # add_dir "tests/clone_creates_right__MTN_options" # # add_dir "tests/clone_validates_target_directory" # # add_dir "tests/clone_warning_with_multiple_heads" # # add_file "tests/clone_-b_no_dir/__driver__.lua" # content [24877f2ff4e2e8df6051bb60fa8a373dbeac2b29] # # add_file "tests/clone_creates__MTN_log/__driver__.lua" # content [da936653810379282e5456c38552448f100186eb] # # add_file "tests/clone_creates__MTN_log/commit_log.lua" # content [a9af6de4500c2fddab1853eebc185a055156f8cd] # # add_file "tests/clone_creates_right__MTN_options/__driver__.lua" # content [ff16d67dfa4f7dadc2be5fa8e1c15e397aaca498] # # add_file "tests/clone_validates_target_directory/__driver__.lua" # content [45a73099cb011e786fe7072ea9d3a1dd7f498737] # # add_file "tests/clone_warning_with_multiple_heads/__driver__.lua" # content [9d313a067d5944fdebbaf9595c791e5a4a1a16c4] # # patch "app_state.cc" # from [535224378d20deda9c44610599206018412c2929] # to [ca6df0854fa40f81085ba85d4acbce59f18c4321] # # patch "cmd_netsync.cc" # from [f0a73af0909b03372b8c96bad5647e3bddff17ac] # to [9baf26d8c41dac3a4debb44935aebfc88cf2c359] # # patch "database.cc" # from [3e65a0b3f6e71edd464d6dc18e8ac46652a44cb1] # to [6eb6abccd16cea2f3ec7517d4d0c1cb00e01fb69] # # patch "database.hh" # from [5d5f3613e3c091f1dc75eb734b86fa4559b03969] # to [6c591fe758a0dcb3706813f9fa100fae9a37c6f6] # # patch "paths.cc" # from [0dc1996f499253e7f5663c528da8ce8e7e8c7d70] # to [e074516f10d1446a9cc8a1f64b6a2289d4c379ac] # # patch "paths.hh" # from [88bab6e0937c669fd1f37c3b3963a0079514cf34] # to [367a31062ccf14c5be271ecbe270010ddf784f28] # # patch "testsuite.lua" # from [ace65535c8e4ff3106a8c3ba96fdb29086848dc8] # to [7b0a70809b212a42874dc76046955ac06de7e6ad] # ============================================================ --- tests/clone_-b_no_dir/__driver__.lua 24877f2ff4e2e8df6051bb60fa8a373dbeac2b29 +++ tests/clone_-b_no_dir/__driver__.lua 24877f2ff4e2e8df6051bb60fa8a373dbeac2b29 @@ -0,0 +1,20 @@ +mtn_setup() + +addfile("foo", "blah blah") +commit("mybranch") + +testURI="file:" .. test.root .. "/test.db" + +check(nodb_mtn("clone", "-b", "mybranch", testURI), 0, false, false) +check(exists("mybranch")) +check(readfile("foo") == readfile("mybranch/foo")) + +-- but now that that directory exists, this clone should fail +check(nodb_mtn("clone", "-b", "mybranch", testURI), 1, false, false) + +-- but succeed if given a specific dir +check(nodb_mtn("clone", "-b", "mybranch", testURI, "otherdir"), 0, false, false) + +-- clone into . should also fail, unlike checkout +mkdir("test4") +check(indir("test4", nodb_mtn("clone", "-b", "mybranch", testURI, ".")), 1, false, false) ============================================================ --- tests/clone_creates__MTN_log/__driver__.lua da936653810379282e5456c38552448f100186eb +++ tests/clone_creates__MTN_log/__driver__.lua da936653810379282e5456c38552448f100186eb @@ -0,0 +1,20 @@ + +mtn_setup() + +check(get("commit_log.lua")) + +writefile("_MTN/log", "Log entry") + +writefile("input.txt", "version 0 of the file") + +check(mtn("add", "input.txt"), 0, false, false) + +check(mtn("--branch=testbranch", "--rcfile=commit_log.lua", "commit"), 0, false, false) + +testURI="file:" .. test.root .. "/test.db" + +check(nodb_mtn("--branch=testbranch", "clone", testURI, "testbranch"), 0, false, true) + +check(exists("testbranch/_MTN/log")) +check(fsize("_MTN/log") == 0) +check(fsize("testbranch/_MTN/log") == 0) ============================================================ --- tests/clone_creates__MTN_log/commit_log.lua a9af6de4500c2fddab1853eebc185a055156f8cd +++ tests/clone_creates__MTN_log/commit_log.lua a9af6de4500c2fddab1853eebc185a055156f8cd @@ -0,0 +1,6 @@ +function edit_comment(summary, user_log_file) + -- this used to just return the variable user_log_file, + -- but now must return the original entry without the 'magic' line. + -- this avoids a failing test after the 'magic line' patch + return "Log Entry" +end ============================================================ --- tests/clone_creates_right__MTN_options/__driver__.lua ff16d67dfa4f7dadc2be5fa8e1c15e397aaca498 +++ tests/clone_creates_right__MTN_options/__driver__.lua ff16d67dfa4f7dadc2be5fa8e1c15e397aaca498 @@ -0,0 +1,47 @@ + +mtn_setup() + +addfile("testfile", "foo") +commit() +rev = base_revision() + +copy("test.db", "test-old.db") +writefile("testfile", "blah") +commit() + +testURI="file:" .. test.root .. "/test.db" + +-- We use RAW_MTN because it used to be that passing --db= (as +-- MTN does) would hide a bug in this functionality... + +-- all of these inherit options settings from the current _MTN dir +-- unless they override them on the command line + +check(nodb_mtn("--branch=testbranch", "clone", testURI, "test_dir1"), 0, false, false) +check(nodb_mtn("--branch=testbranch", "clone", testURI, "--revision", rev, "test_dir2"), 0, false, false) +check(nodb_mtn("--db=" .. test.root .. "/test-old.db", "--branch=testbranch", "clone", testURI, "test_dir3"), 0, false, false) +check(nodb_mtn("--db=" .. test.root .. "/test-old.db", "--branch=testbranch", "clone", testURI, "--revision", rev, "test_dir4"), 0, false, false) + +-- checkout fails if the specified revision is not a member of the specified branch +check(nodb_mtn("--branch=foobar", "clone", testURI, "--revision", rev, "test_dir5"), 1, false, false) +check(mtn("cert", rev, "branch", "foobar"), 0, false, false) +check(nodb_mtn("--branch=foobar", "clone", testURI, "--revision", rev, "test_dir6"), 0, false, false) + + +for i = 1,2 do + local dir = "test_dir"..i + L("dir = ", dir, "\n") + check(exists(dir.."/_MTN/options")) + check(qgrep(dir.."/_MTN/mtn.db", dir.."/_MTN/options")) + check(qgrep("testbranch", dir.."/_MTN/options")) +end + +for i = 3,4 do + local dir = "test_dir"..i + L("dir = ", dir, "\n") + check(exists(dir.."/_MTN/options")) + check(qgrep("test-old.db", dir.."/_MTN/options")) + check(qgrep("testbranch", dir.."/_MTN/options")) +end + +check(qgrep("foobar", "test_dir6/_MTN/options")) ============================================================ --- tests/clone_validates_target_directory/__driver__.lua 45a73099cb011e786fe7072ea9d3a1dd7f498737 +++ tests/clone_validates_target_directory/__driver__.lua 45a73099cb011e786fe7072ea9d3a1dd7f498737 @@ -0,0 +1,34 @@ + +mtn_setup() + +addfile("testfile", "foo") +commit() + +testURI="file:" .. test.root .. "/test.db" + +check(nodb_mtn("--branch=testbranch", "clone", testURI, "test_dir1"), 0, false, false) + +writefile("test_dir2") +check(nodb_mtn("--branch=testbranch", "clone", testURI, "test_dir2"), 1, false, false) + +mkdir("test_dir3") +check(nodb_mtn("--branch=testbranch", "clone", testURI, "test_dir3"), 1, false, false) + +if existsonpath("chmod") and existsonpath("test") then + -- skip this part if run as root (hi Gentoo!) + if check({"test", "-O", "/"}, false, false, false) == 0 then + partial_skip = true + else + mkdir("test_dir4") + check({"chmod", "444", "test_dir4"}, 0, false) + check(nodb_mtn("--branch=testbranch", "clone", testURI, "test_dir4"), + 1, false, false) + check(nodb_mtn("--branch=testbranch", "clone", testURI, "test_dir4/subdir"), + 1, false, false) + -- Reset the permissions so Autotest can correctly clean up our + -- temporary directory. + check({"chmod", "700", "test_dir4"}, 0, false) + end +else + partial_skip = true +end ============================================================ --- tests/clone_warning_with_multiple_heads/__driver__.lua 9d313a067d5944fdebbaf9595c791e5a4a1a16c4 +++ tests/clone_warning_with_multiple_heads/__driver__.lua 9d313a067d5944fdebbaf9595c791e5a4a1a16c4 @@ -0,0 +1,22 @@ + +mtn_setup() + +addfile("testfile", "foo") +commit() +REV1=base_revision() + +addfile("file2", "bar") +commit() +REV2=base_revision() + +check(mtn("update", "-r", REV1), 0, false, false) +addfile("otherfile", "splork") +commit() +REV3=base_revision() + +testURI="file:" .. test.root .. "/test.db" + +check(nodb_mtn("--branch=testbranch", "clone", testURI, "test_dir1"), + 1, false, true) +check(qgrep(REV2, "stderr")) +check(qgrep(REV3, "stderr")) ============================================================ --- app_state.cc 535224378d20deda9c44610599206018412c2929 +++ app_state.cc ca6df0854fa40f81085ba85d4acbce59f18c4321 @@ -147,6 +147,7 @@ app_state::create_workspace(system_path mkdir_p(new_dir); go_to_workspace(new_dir); + fix_paths(); N(!directory_exists(bookkeeping_root), F("monotone bookkeeping directory '%s' already exists in '%s'") @@ -167,6 +168,18 @@ app_state::create_workspace(system_path if (lua.hook_use_inodeprints()) work.enable_inodeprints(); + found_workspace = true; + if (global_sanity.filename.empty()) + { + bookkeeping_path dump_path; + work.get_local_dump_path(dump_path); + L(FL("setting dump path to %s") % dump_path); + // The 'true' 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.filename = system_path(dump_path, false).as_external(); + } + load_rcfiles(); } @@ -174,8 +187,18 @@ app_state::set_database(system_path cons app_state::set_database(system_path const & filename) { if (!filename.empty()) - db.set_filename(filename); -} + { + system_path database_option(filename); + branch_name branch_option; + rsa_keypair_id key_option; + system_path keydir_option; + + db.set_filename(filename); + + work.set_ws_options(database_option, branch_option, + key_option, keydir_option); + } + } void app_state::set_key_dir(system_path const & filename) ============================================================ --- cmd_netsync.cc f0a73af0909b03372b8c96bad5647e3bddff17ac +++ cmd_netsync.cc 9baf26d8c41dac3a4debb44935aebfc88cf2c359 @@ -1,16 +1,23 @@ #include "cmd.hh" +#include "diff_patch.hh" #include "netsync.hh" #include "globish.hh" #include "keys.hh" #include "cert.hh" +#include "revision.hh" +#include "ui.hh" #include "uri.hh" #include "vocab_cast.hh" +#include "platform-wrapped.hh" + #include using std::ifstream; using std::ofstream; +using std::map; +using std::set; using std::string; using std::vector; @@ -21,6 +28,8 @@ static const var_key default_exclude_pat static const var_key default_exclude_pattern_key(var_domain("database"), var_name("default-exclude-pattern")); +static string const ws_internal_db_file_name("mtn.db"); + static void extract_address(vector const & args, utf8 & addr, @@ -158,7 +167,178 @@ CMD(sync, N_("network"), N_("[ADDRESS[:P include_pattern, exclude_pattern, app); } +class dir_cleanup_helper +{ +public: + dir_cleanup_helper(system_path const & new_dir) : commited(false), dir(new_dir) {} + ~dir_cleanup_helper() + { + if (!commited && directory_exists(dir)) + delete_dir_recursive(dir); + } + void commit(void) + { + commited = true; + } +private: + bool commited; + system_path dir; +}; +CMD(clone, N_("network"), N_("ADDRESS[:PORTNUMBER] [DIRECTORY]"), + N_("check out a revision from remote database into directory.\n" + "If a revision is given, that's the one that will be checked out.\n" + "Otherwise, it will be the head of the branch supplied.\n" + "If no directory is given, the branch name will be used as directory"), + options::opts::exclude | options::opts::branch | options::opts::revision) +{ + revision_id ident; + system_path workspace_dir; + utf8 addr = idx(args, 0); + + if (args.size() < 1 || args.size() > 2 || app.opts.revision_selectors.size() > 1) + throw usage(name); + + N(app.opts.branch_given && !app.opts.branchname().empty(), + F("you must specify a branch to clone")); + + if (args.size() == 1) + { + // No checkout dir specified, use branch name for dir. + workspace_dir = system_path(app.opts.branchname()); + } + else + { + // Checkout to specified dir. + workspace_dir = system_path(idx(args, 1)); + } + + require_path_is_nonexistent + (workspace_dir, F("clone destination directory '%s' already exists") % workspace_dir); + + // remember the initial working dir so that relative file:// db URIs will work + system_path start_dir(get_current_working_dir()); + + dir_cleanup_helper remove_on_fail(workspace_dir); + app.create_workspace(workspace_dir); + + if (!app.opts.dbname_given || app.opts.dbname.empty()) + app.set_database(system_path(bookkeeping_root / ws_internal_db_file_name)); + else + app.set_database(app.opts.dbname); + + if (get_path_status(app.db.get_filename()) == path::nonexistent) + app.db.initialize(); + + app.db.ensure_open(); + + if (!app.db.var_exists(default_server_key) || app.opts.set_default) + { + P(F("setting default server to %s") % addr); + app.db.set_var(default_server_key, var_value(addr())); + } + + if (app.opts.signing_key() == "") + P(F("doing anonymous pull; use -kKEYNAME if you need authentication")); + + globish include_pattern(app.opts.branchname()); + if (!app.db.var_exists(default_include_pattern_key) + || app.opts.set_default) + { + P(F("setting default branch include pattern to '%s'") % include_pattern); + app.db.set_var(default_include_pattern_key, var_value(include_pattern())); + } + + globish exclude_pattern; + if (app.opts.exclude_given) + { + vector excludes; + typecast_vocab_container(app.opts.exclude_patterns, excludes); + combine_and_check_globish(excludes, exclude_pattern); + if (!app.db.var_exists(default_exclude_pattern_key) + || app.opts.set_default) + { + P(F("setting default branch exclude pattern to '%s'") % exclude_pattern); + app.db.set_var(default_exclude_pattern_key, var_value(exclude_pattern())); + } + } + else + { + exclude_pattern = globish(); + } + + // make sure we're back in the original dir so that file: URIs work + change_current_working_dir(start_dir); + + run_netsync_protocol(client_voice, sink_role, addr, + include_pattern, exclude_pattern, app); + + change_current_working_dir(workspace_dir); + + transaction_guard guard(app.db, false); + + if (app.opts.revision_selectors.size() == 0) + { + // use branch head revision + N(!app.opts.branchname().empty(), + F("use --revision or --branch to specify what to checkout")); + + set heads; + app.get_project().get_branch_heads(app.opts.branchname, heads); + N(heads.size() > 0, + F("branch '%s' is empty") % app.opts.branchname); + if (heads.size() > 1) + { + P(F("branch %s has multiple heads:") % app.opts.branchname); + for (set::const_iterator i = heads.begin(); i != heads.end(); ++i) + P(i18n_format(" %s") % describe_revision(app, *i)); + P(F("choose one with '%s checkout -r'") % ui.prog_name); + E(false, F("branch %s has multiple heads") % app.opts.branchname); + } + ident = *(heads.begin()); + } + else if (app.opts.revision_selectors.size() == 1) + { + // use specified revision + complete(app, idx(app.opts.revision_selectors, 0)(), ident); + N(app.db.revision_exists(ident), + F("no such revision '%s'") % ident); + + guess_branch(ident, app); + + I(!app.opts.branchname().empty()); + + N(app.get_project().revision_is_in_branch(ident, app.opts.branchname), + F("revision %s is not a member of branch %s") + % ident % app.opts.branchname); + } + + shared_ptr empty_roster = shared_ptr(new roster_t()); + roster_t current_roster; + + L(FL("checking out revision %s to directory %s") % ident % workspace_dir); + app.db.get_roster(ident, current_roster); + + revision_t workrev; + make_revision_for_workspace(ident, cset(), workrev); + app.work.put_work_rev(workrev); + + cset checkout; + make_cset(*empty_roster, current_roster, checkout); + + map paths; + get_content_paths(*empty_roster, paths); + + content_merge_workspace_adaptor wca(app, empty_roster, paths); + + app.work.perform_content_update(checkout, wca, false); + + app.work.update_any_attrs(); + app.work.maybe_update_inodeprints(); + guard.commit(); + remove_on_fail.commit(); +} + struct pid_file { explicit pid_file(system_path const & p) ============================================================ --- database.cc 3e65a0b3f6e71edd464d6dc18e8ac46652a44cb1 +++ database.cc 6eb6abccd16cea2f3ec7517d4d0c1cb00e01fb69 @@ -265,6 +265,8 @@ database::initialize() // make sure what we wanted is what we got check_sql_schema(__sql, filename); + + close(); } struct @@ -711,10 +713,7 @@ database::~database() statement_cache.clear(); if (__sql) - { - sqlite3_close(__sql); - __sql = 0; - } + close(); } void @@ -3305,6 +3304,17 @@ database::open() assert_sqlite3_ok(__sql); } +void +database::close() +{ + I(__sql); + + sqlite3_close(__sql); + __sql = 0; + + I(!__sql); +} + // transaction guards transaction_guard::transaction_guard(database & d, bool exclusive, ============================================================ --- database.hh 5d5f3613e3c091f1dc75eb734b86fa4559b03969 +++ database.hh 6c591fe758a0dcb3706813f9fa100fae9a37c6f6 @@ -97,6 +97,7 @@ private: void check_db_exists(); void check_db_nonexistent(); void open(); + void close(); void check_format(); public: ============================================================ --- paths.cc 0dc1996f499253e7f5663c528da8ce8e7e8c7d70 +++ paths.cc e074516f10d1446a9cc8a1f64b6a2289d4c379ac @@ -722,6 +722,13 @@ go_to_workspace(system_path const & new_ change_current_working_dir(new_workspace); } +void +fix_paths(void) +{ + working_root.get(); + initial_rel_path.get(); +} + /////////////////////////////////////////////////////////////////////////// // tests /////////////////////////////////////////////////////////////////////////// ============================================================ --- paths.hh 88bab6e0937c669fd1f37c3b3963a0079514cf34 +++ paths.hh 367a31062ccf14c5be271ecbe270010ddf784f28 @@ -270,6 +270,8 @@ go_to_workspace(system_path const & new_ void go_to_workspace(system_path const & new_workspace); +void fix_paths(void); + typedef std::set path_set; void ============================================================ --- testsuite.lua ace65535c8e4ff3106a8c3ba96fdb29086848dc8 +++ testsuite.lua 7b0a70809b212a42874dc76046955ac06de7e6ad @@ -98,6 +98,12 @@ end "address@hidden", unpack(arg)) end +function nodb_mtn(...) + return raw_mtn("--rcfile", test.root .. "/test_hooks.lua", -- "--nostd", + "--keydir", test.root .. "/keys", + "address@hidden", unpack(arg)) +end + function minhooks_mtn(...) return raw_mtn("--db=" .. test.root .. "/test.db", "--keydir", test.root .. "/keys",