#
#
# add_file "tests/t_rename_destdir.at"
# content [8e16b441f52d44260f34fa9edd99ca92c3b5863a]
#
# patch "ChangeLog"
# from [4ce17cf890b330654a50dc12c047f67328ca01ed]
# to [f5febf6b21c9aeb3fc2bd4cd81fc6b7854ef03be]
#
# patch "commands.cc"
# from [8743a12d037788901f07148fad730dfd899f125c]
# to [4d20869a08800d21d6ad8df345d34d5ffbeb6ff9]
#
# patch "monotone.texi"
# from [cbc87b47e45384cb320b2b59c4ebdf0ae82f2867]
# to [d3d60b1a250d8b10dfba56c5c28d9198400c7e3b]
#
# patch "tests/t_no_rename_overwrite.at"
# from [1fe86d8eeda1180521602e9b66628aadad9b75ac]
# to [1f1e1481d40d08fbd33eae39850f79fd4804b4a7]
#
# patch "testsuite.at"
# from [47092721ad423cb1e208539f382826ec25574d5b]
# to [a9c0964810370a40023bba920ab6b811b547fb0b]
#
# patch "vocab.cc"
# from [d8da5a3ac67bb8c2b29a5cb5f3e3704e1375d954]
# to [084a7a4538c3e0b062b901d0ba891fd6b66cfdfe]
#
# patch "work.cc"
# from [5f27cea289f3b61f7f645643bf7d6b158ca031ce]
# to [f0b8375e1b0bfbaf32770b41ece3015e2a34cb66]
#
# patch "work.hh"
# from [abc46f82b48bd3a9c31beb58446c3ef484880088]
# to [8c9dc3dba09d6c20d9afb4c9f7c1e11e28e489bb]
#
============================================================
--- tests/t_rename_destdir.at 8e16b441f52d44260f34fa9edd99ca92c3b5863a
+++ tests/t_rename_destdir.at 8e16b441f52d44260f34fa9edd99ca92c3b5863a
@@ -0,0 +1,94 @@
+# -*- Autoconf -*-
+
+AT_SETUP([rename files into a directory])
+
+MONOTONE_SETUP
+
+ADD_FILE(gnat, [gnat
+])
+ADD_FILE(mosquito, [mosquito
+])
+ADD_FILE(termite, [termite
+])
+ADD_FILE(ant, [ant
+])
+
+# will force foo/, bar/ and foo/gnat/ to be created
+AT_CHECK(mkdir foo bar foo/gnat)
+ADD_FILE(foo/dummy, [... ... ...
+])
+ADD_FILE(bar/dummy, [a b c
+])
+ADD_FILE(foo/gnat/dummmy, [la la la
+])
+
+COMMIT(testbranch)
+
+# checkout in a clean dir and cd there
+m4_define([REN_CHECKOUT], [
+AT_CHECK(rm -fr $_ROOT_DIR/checkout, [], [ignore], [ignore])
+AT_CHECK(MONOTONE checkout -b testbranch $_ROOT_DIR/checkout, [0], [ignore], [ignore])
+])
+
+REN_CHECKOUT
+
+# basics
+AT_CHECK(cd checkout && MONOTONE rename ant foo, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename mosquito termite foo, [0], [ignore], [ignore])
+
+REN_CHECKOUT
+
+# with --execute
+AT_CHECK(cd checkout && MONOTONE --execute rename ant foo, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE --execute rename mosquito termite foo, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && test -e foo/ant -a -e foo/mosquito -a -e foo/termite)
+AT_CHECK(cd checkout && test ! -e ant -a ! -e mosquito -a ! -e termite)
+
+# to root
+AT_CHECK(cd checkout && MONOTONE --execute rename foo/ant ., [0], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename foo/termite ., [0], [ignore], [ignore])
+AT_CHECK(cd checkout && test -e ant -a -e foo/termite -a ! -e foo/ant -a ! -e termite)
+
+REN_CHECKOUT
+
+# conflicts
+AT_CHECK(cd checkout && MONOTONE rename gnat foo, [1], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename gnat termite foo, [1], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename termite foo, [0], [ignore], [ignore])
+
+AT_CHECK(cd checkout && MONOTONE rename mosquito foo/ant, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename ant foo, [1], [ignore], [ignore])
+
+REN_CHECKOUT
+
+AT_CHECK(cd checkout && MONOTONE --execute rename gnat foo, [1], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE --execute rename gnat termite foo, [1], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE --execute rename termite foo, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && test -e foo/termite -a ! -e termite -a -e gnat -a -d foo/gnat -a ! -e foo/gnat/gnat)
+
+REN_CHECKOUT
+
+## todo:
+# issues with missing files, should usually be allowed?
+# rename to self
+# rename root node
+
+# rename to non-existing dir path: "foo->blweorih/o4thoihs" (this isn't a destdir case, but needs testing somewhere).
+
+# file0->bar, file0 doesn't exist
+
+# file0->bar, file0 exists, -e
+
+# file0->bar, file0 doesn't exist, -e
+
+# check that nothing happens if any would fail
+# file0->bar file1->bar, file0 exists, file1 doesn't
+
+# file0->bar file1->bar, file0 exists, file1 doesn't, -e
+
+# file0->bar, bar/file0 exists in working, file0 doesn't -e
+
+# file0->bar
+
+
+AT_CLEANUP
============================================================
--- ChangeLog 4ce17cf890b330654a50dc12c047f67328ca01ed
+++ ChangeLog f5febf6b21c9aeb3fc2bd4cd81fc6b7854ef03be
@@ -1,5 +1,14 @@
2006-01-19 Matt Johnston
+ * work.{cc,hh}, commands.cc: add "rename src1 [src2 ...] dst/"
+ syntax.
+ * monotone.texi: update
+ * testsuite.at, tests/t_rename_destdir: new test (is incomplete).
+ * tests/t_no_rename_overwrite.at: syntax should now succeed.
+ * vocab.cc: add hexenc dump() instantiation.
+
+2006-01-19 Matt Johnston
+
* HACKING: escape the colon in the cino vim modeline option.
2006-01-19 Nathaniel Smith
============================================================
--- commands.cc 8743a12d037788901f07148fad730dfd899f125c
+++ commands.cc 4d20869a08800d21d6ad8df345d34d5ffbeb6ff9
@@ -1217,19 +1217,26 @@
ALIAS(rm, drop);
-CMD(rename, N_("working copy"), N_("SRC DST"),
+CMD(rename, N_("working copy"),
+ N_("SRC DEST\n"
+ "SRC1 [SRC2 [...]] DEST_DIR"),
N_("rename entries in the working copy"),
OPT_EXECUTE)
{
- if (args.size() != 2)
+ if (args.size() < 2)
throw usage(name);
app.require_working_copy();
- file_path src_path = file_path_external(idx(args, 0));
- file_path dst_path = file_path_external(idx(args, 1));
+ file_path dst_path = file_path_external(args.back());
- perform_rename(src_path, dst_path, app);
+ set src_paths;
+ for (size_t i = 0; i < args.size()-1; i++)
+ {
+ file_path s = file_path_external(idx(args, i));
+ src_paths.insert(s);
+ }
+ perform_rename(src_paths, dst_path, app);
}
ALIAS(mv, rename)
============================================================
--- monotone.texi cbc87b47e45384cb320b2b59c4ebdf0ae82f2867
+++ monotone.texi d3d60b1a250d8b10dfba56c5c28d9198400c7e3b
@@ -3848,9 +3848,11 @@
you should run @command{drop}, and then perform the actual delete using
whatever mechanism you normally use to delete files.
address@hidden monotone rename @var{src} @var{dst}
address@hidden monotone [--execute] rename @var{src} @var{dst}
address@hidden monotone [--execute] rename @var{src1} @var{...} @var{dst/}
This command places ``rename'' entries for the paths specified in
address@hidden and @var{dst} in the working copy's ``work list''. The work
address@hidden and @var{dst} in the working copy's ``work list''. The second form
+renames a number of source paths to the given destination. The work
list of your working copy is located at @file{MT/work}, and is a list of
explicit pathname changes you wish to commit at some future time, such
as addition, removal, or renaming of files. This command also moves any
@@ -3864,10 +3866,9 @@
will have any renamed entries in its manifest adjusted to their new
names.
-Currently this command does @emph{not} actually rename the file
address@hidden in your filesystem; after you run this command, you should do
-the actual rename, using whatever mechanism you normally use to rename
-files.
+The option @option{--execute} will make ``rename'' perform the actual
+rename operations in the filesystem. It will ignore missing source files
+and will not overwrite existing destination files.
@item monotone commit
@itemx monotone commit address@hidden
============================================================
--- tests/t_no_rename_overwrite.at 1fe86d8eeda1180521602e9b66628aadad9b75ac
+++ tests/t_no_rename_overwrite.at 1f1e1481d40d08fbd33eae39850f79fd4804b4a7
@@ -18,16 +18,12 @@
AT_CHECK(MONOTONE rename unknown_file other_file, [1], [ignore], [ignore])
AT_CHECK(MONOTONE rename rename_file target_file, [1], [ignore], [ignore])
-AT_CHECK(MONOTONE rename rename_file target_dir, [1], [ignore], [ignore])
AT_CHECK(MONOTONE rename rename_dir target_file, [1], [ignore], [ignore])
-AT_CHECK(MONOTONE rename rename_dir target_dir, [1], [ignore], [ignore])
COMMIT(testbranch)
AT_CHECK(MONOTONE rename unknown_file other_file, [1], [ignore], [ignore])
AT_CHECK(MONOTONE rename rename_file target_file, [1], [ignore], [ignore])
-AT_CHECK(MONOTONE rename rename_file target_dir, [1], [ignore], [ignore])
AT_CHECK(MONOTONE rename rename_dir target_file, [1], [ignore], [ignore])
-AT_CHECK(MONOTONE rename rename_dir target_dir, [1], [ignore], [ignore])
AT_CLEANUP
============================================================
--- testsuite.at 47092721ad423cb1e208539f382826ec25574d5b
+++ testsuite.at a9c0964810370a40023bba920ab6b811b547fb0b
@@ -757,3 +757,4 @@
m4_include(tests/t_revert_ignored.at)
m4_include(tests/t_no_persist_phrase.at)
m4_include(tests/t_check_db_format.at)
+m4_include(tests/t_rename_destdir.at)
============================================================
--- vocab.cc d8da5a3ac67bb8c2b29a5cb5f3e3704e1375d954
+++ vocab.cc 084a7a4538c3e0b062b901d0ba891fd6b66cfdfe
@@ -298,6 +298,9 @@
template
void dump(file_id const & r, std::string &);
+template
+void dump(hexenc const & r, std::string &);
+
// the rest is unit tests
#ifdef BUILD_UNIT_TESTS
============================================================
--- work.cc 5f27cea289f3b61f7f645643bf7d6b158ca031ce
+++ work.cc f0b8375e1b0bfbaf32770b41ece3015e2a34cb66
@@ -237,40 +237,112 @@
build.visit_dir(dirname);
}
-void
-perform_rename(file_path const & src_path,
+void
+perform_rename(set const & src_paths,
file_path const & dst_path,
app_state & app)
{
temp_node_id_source nis;
roster_t base_roster, new_roster;
- split_path src, dst;
+ split_path dst;
+ set srcs;
+ set< pair > renames;
+ I(!src_paths.empty());
+
get_base_and_current_roster_shape(base_roster, new_roster, nis, app);
- src_path.split(src);
dst_path.split(dst);
- N(new_roster.has_node(src),
- F("%s does not exist in current revision\n") % src_path);
+ if (src_paths.size() == 1 && !new_roster.has_node(dst))
+ {
+ // "rename SRC DST" case
+ split_path s;
+ src_paths.begin()->split(s);
+ renames.insert( make_pair(s, dst) );
+ add_parent_dirs(dst, new_roster, nis, app);
+ }
+ else
+ {
+ // "rename SRC1 SRC2 DST" case
+ N(new_roster.has_node(dst),
+ F("destination dir %s/ does not exist in current revision") % dst_path);
- N(!new_roster.has_node(dst),
- F("%s already exists in current revision\n") % dst_path);
+ N(is_dir_t(new_roster.get_node(dst)),
+ F("destination %s is an existing file in current revision") % dst_path);
- add_parent_dirs(dst, new_roster, nis, app);
+ for (set::const_iterator i = src_paths.begin();
+ i != src_paths.end(); i++)
+ {
+ split_path s;
+ i->split(s);
+ // TODO "rename . foo/" might be valid? Or should it already have been
+ // normalised..., in which case it might be an I().
+ N(!s.empty(),
+ F("empty path %s is not allowed") % *i);
- P(F("adding %s -> %s to working copy rename set\n") % src_path % dst_path);
+ path_component src_basename = s.back();
+ split_path d(dst);
+ d.push_back(src_basename);
+ renames.insert( make_pair(s, d) );
+ }
+ }
- node_id nid = new_roster.detach_node(src);
- new_roster.attach_node(nid, dst);
+ // one iteration to check for existing/missing files
+ for (set< pair >::const_iterator i = renames.begin();
+ i != renames.end(); i++)
+ {
+ N(new_roster.has_node(i->first),
+ F("%s does not exist in current revision") % file_path(i->first));
- // this should fail if src doesn't exist or dst does
- if (app.execute && (path_exists(src_path) || !path_exists(dst_path)))
- move_path(src_path, dst_path);
+ N(!new_roster.has_node(i->second),
+ F("destination %s already exists in current revision") % file_path(i->second));
+ }
+ // do the attach/detaching
+ for (set< pair >::const_iterator i = renames.begin();
+ i != renames.end(); i++)
+ {
+ node_id nid = new_roster.detach_node(i->first);
+ new_roster.attach_node(nid, i->second);
+ P(F("adding %s -> %s to working copy rename set")
+ % file_path(i->first)
+ % file_path(i->second));
+ }
+
cset new_work;
make_cset(base_roster, new_roster, new_work);
put_work_cset(new_work);
+
+ if (app.execute)
+ {
+ for (set< pair >::const_iterator i = renames.begin();
+ i != renames.end(); i++)
+ {
+ file_path s(i->first);
+ file_path d(i->second);
+ // silently skip files where src doesn't exist or dst does
+ bool have_src = path_exists(s);
+ bool have_dst = path_exists(d);
+ if (have_src && !have_dst)
+ {
+ move_path(s, d);
+ }
+ else if (!have_src && !have_dst)
+ {
+ W(F("%s doesn't exist in working copy, skipping") % s);
+ }
+ else if (have_src && have_dst)
+ {
+ W(F("destination %s already exists in working copy, skipping") % d);
+ }
+ else
+ {
+ L(FL("skipping move_path %s->%s silently, src doesn't exist, dst does")
+ % s % d);
+ }
+ }
+ }
update_any_attrs(app);
}
============================================================
--- work.hh abc46f82b48bd3a9c31beb58446c3ef484880088
+++ work.hh 8c9dc3dba09d6c20d9afb4c9f7c1e11e28e489bb
@@ -66,8 +66,8 @@
perform_deletions(path_set const & targets, app_state & app);
void
-perform_rename(file_path const & src_path,
- file_path const & dst_path,
+perform_rename(std::set const & src_paths,
+ file_path const & dst_dir,
app_state & app);
// the "work" file contains the current cset representing uncommitted