#
# patch "ChangeLog"
# from [ac05412182119677d3c15007216968d129a477a3]
# to [d5cdabdc96bae23dfa3127be81f64cdc11cf8f6f]
#
# patch "automate.cc"
# from [104a3a0307c16c724da0fed33f087175aaaba5bf]
# to [6e3b8cec00c8f4d0d91d8d9fd14c4d06e8f0eef0]
#
# patch "manifest.cc"
# from [cfa44e867c9068323e6cfe566b69d2e84f103264]
# to [2d4e047a2587f9b379fad559d192b317d6e2b47e]
#
# patch "manifest.hh"
# from [d96380ff7a7e4e2cef53ad864608b6e97e484207]
# to [d86a3fe804cf18e9e31af16401dcda6391225288]
#
# patch "monotone.texi"
# from [1043aaa99cad1d0060f75058661a71ad85c12ac5]
# to [9f2c6606e84058bb985b759da3da959f94696ab0]
#
# patch "tests/t_automate_inventory.at"
# from [81165e26ca6a1c4c1310a8f2d1ad28feab636ea9]
# to [109af5a233e8700e3d11694aefc5f03ddc84107e]
#
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,17 @@
+2005-05-12 Derek Scherger
+
+ * automate.cc: bump version number to 1.0
+ (struct inventory_item): add pre/post states
+ (inventory_paths): remove obsolete function
+ (inventory_pre_state, inventory_post_state, inventory_file_state,
+ inventory_renames): add fancy new functions
+ (automate_inventory): rework for new output format
+ * manifest.{cc,hh} (classify_paths): rename to ...
+ (classify_manifest_paths): ... this and work solely from manifest
+ * monotone.texi: (Automation): update inventory docs
+ * tests/t_automate_inventory.at: update for new format and add
+ some more tests
+
2005-05-11 Timothy Brownawell
* revision.cc (expand_dominators): Fix bitmap size-matching.
--- automate.cc
+++ automate.cc
@@ -15,9 +15,10 @@
#include "commands.hh"
#include "restrictions.hh"
#include "revision.hh"
+#include "transforms.hh"
#include "vocab.hh"
-static std::string const interface_version = "0.2";
+static std::string const interface_version = "1.0";
// Name: interface_version
// Arguments: none
@@ -452,102 +453,158 @@
output << *i << std::endl;
}
+// consider a changeset with the following
+//
+// deletions
+// renames from to
+// additions
+//
+// pre-state corresponds to deletions and the "from" side of renames
+// post-state corresponds to the "to" side of renames and additions
+// file-state corresponds to the state of the file with the given name
+//
+// pre and post state are related to the path rearrangement specified in MT/work
+// file state is related to the details of the resulting file
+
struct inventory_item
{
- enum pstat
- { UNCHANGED_PATH, ADDED_PATH, DROPPED_PATH, RENAMED_PATH, UNKNOWN_PATH, IGNORED_PATH }
- path_status;
+ enum pstate
+ { KNOWN_PATH, ADDED_PATH, DROPPED_PATH, RENAMED_PATH }
+ pre_state, post_state;
- enum dstat
- { UNCHANGED_DATA, PATCHED_DATA, MISSING_DATA }
- data_status;
+ enum fstate
+ { KNOWN_FILE, PATCHED_FILE, MISSING_FILE, UNKNOWN_FILE, IGNORED_FILE }
+ file_state;
enum ptype
{ FILE, DIRECTORY }
path_type;
- file_path old_path;
+ size_t pre_id, post_id;
inventory_item():
- path_status(UNCHANGED_PATH), data_status(UNCHANGED_DATA), path_type(FILE), old_path() {}
+ pre_state(KNOWN_PATH), post_state(KNOWN_PATH),
+ file_state(KNOWN_FILE),
+ path_type(FILE),
+ pre_id(0), post_id(0) {}
};
typedef std::map inventory_map;
static void
-inventory_paths(inventory_map & inventory,
- path_set const & paths,
- inventory_item::pstat path_status,
- inventory_item::ptype path_type = inventory_item::FILE)
+inventory_pre_state(inventory_map & inventory,
+ path_set const & paths,
+ inventory_item::pstate pre_state,
+ size_t id = 0,
+ inventory_item::ptype path_type = inventory_item::FILE)
{
for (path_set::const_iterator i = paths.begin(); i != paths.end(); i++)
{
- L(F("%d %d %s\n") % inventory[*i].path_status % path_status % *i);
- I(inventory[*i].path_status == inventory_item::UNCHANGED_PATH);
- inventory[*i].path_status = path_status;
+ L(F("%d %d %s\n") % inventory[*i].pre_state % pre_state % *i);
+ I(inventory[*i].pre_state == inventory_item::KNOWN_PATH);
+ inventory[*i].pre_state = pre_state;
inventory[*i].path_type = path_type;
+ if (id != 0)
+ {
+ I(inventory[*i].pre_id == 0);
+ inventory[*i].pre_id = id;
+ }
}
}
static void
-inventory_paths(inventory_map & inventory,
- path_set const & paths,
- inventory_item::dstat data_status)
+inventory_post_state(inventory_map & inventory,
+ path_set const & paths,
+ inventory_item::pstate post_state,
+ size_t id = 0,
+ inventory_item::ptype path_type = inventory_item::FILE)
{
for (path_set::const_iterator i = paths.begin(); i != paths.end(); i++)
{
- L(F("%d %d %s\n") % inventory[*i].data_status % data_status % *i);
- I(inventory[*i].data_status == inventory_item::UNCHANGED_DATA);
- inventory[*i].data_status = data_status;
+ L(F("%d %d %s\n") % inventory[*i].post_state % post_state % *i);
+ I(inventory[*i].post_state == inventory_item::KNOWN_PATH);
+ inventory[*i].post_state = post_state;
+ inventory[*i].path_type = path_type;
+ if (id != 0)
+ {
+ I(inventory[*i].post_id == 0);
+ inventory[*i].post_id = id;
+ }
}
}
static void
-inventory_paths(inventory_map & inventory,
- std::map const & renames,
- inventory_item::pstat path_status,
- inventory_item::ptype path_type = inventory_item::FILE)
+inventory_file_state(inventory_map & inventory,
+ path_set const & paths,
+ inventory_item::fstate file_state)
{
+ for (path_set::const_iterator i = paths.begin(); i != paths.end(); i++)
+ {
+ L(F("%d %d %s\n") % inventory[*i].file_state % file_state % *i);
+ I(inventory[*i].file_state == inventory_item::KNOWN_FILE);
+ inventory[*i].file_state = file_state;
+ }
+}
+
+static void
+inventory_renames(inventory_map & inventory,
+ std::map const & renames,
+ inventory_item::ptype path_type = inventory_item::FILE)
+{
+ path_set old_name;
+ path_set new_name;
+
+ static size_t id = 1;
+
for (std::map::const_iterator i = renames.begin();
i != renames.end(); i++)
{
- L(F("%d %d %s %s\n") % inventory[i->second].path_status % path_status % i->first % i->second);
- I(inventory[i->second].path_status == inventory_item::UNCHANGED_PATH);
- inventory[i->second].path_status = inventory_item::RENAMED_PATH;
- inventory[i->second].path_type = path_type;
- inventory[i->second].old_path = i->first;
+ old_name.insert(i->first);
+ new_name.insert(i->second);
+
+ inventory_pre_state(inventory, old_name, inventory_item::RENAMED_PATH, id, path_type);
+ inventory_post_state(inventory, new_name, inventory_item::RENAMED_PATH, id, path_type);
+
+ id++;
+
+ old_name.clear();
+ new_name.clear();
}
}
// Name: inventory
// Arguments: none
-// Added in: 0.2
-// Purpose: Prints all the files found in a working copy or current manifest
-// prefixed by 2 status code characters. The first status code character
-// indicates the status of the path itsself and is drawn from the following
-// set:
+// Added in: 1.0
+// Purpose: Prints a summary of every file found in the working copy or its
+// associated base manifest. Each unique path is listed on a line prefixed by
+// three status characters and two numeric values used for identifying
+// renames. The three status characters are as follows.
//
-// ' ' the path is unchanged from the current manifest
-// '+' the path has been added to the current manifest
-// '-' the path has been dropped from the current manifest
-// '%' the path has been renamed in the current manifest, both the old and new name are listed
-// '?' the path is unknown, it exists in the working copy but not in the current manifest
-// '~' the path is ignored by the current ignore_file lua hook setting
-//
-// The second status code character indicates the status of the data associated
-// with the path and is drawn from the following set:
+// column 1 pre-state
+// ' ' the path was unchanged in the pre-state
+// 'D' the path was deleted from the pre-state
+// 'R' the path was renamed from the pre-state name
+// column 2 post-state
+// ' ' the path was unchanged in the post-state
+// 'R' the path was renamed to the post-state name
+// 'A' the path was added to the post-state
+// column 3 file-state
+// ' ' the file is known and unchanged from the current manifest version
+// 'P' the file is patched to a new version
+// 'U' the file is unknown and not included in the current manifest
+// 'I' the file is ignored and not included in the current manifest
+// 'M' the file is missing but is included in the current manifest
//
-// ' ' the data is unchanged, its sha1 version matches the version in the base manifest
-// '#' the data is changed, its sha1 version differs from the version in the base manifest
-// '!' the data is missing and its sha1 version cannot be computed
+// Output format: Each path is printed on its own line, prefixed by three status
+// characters as described above. The status is followed by a single space and
+// two numbers, each separated by a single space, used for identifying renames.
+// The numbers are followed by a single space and then the pathname, which
+// includes the rest of the line. Directory paths are identified as ending with
+// the "/" character, file paths do not end in this character.
//
-// Output format: Each file is printed on its own line, prefixed by a
-// two character status code and a single space character. All filenames are
-// quoted with double quotes (") to support filenames containg spaces. Intervening quotes
-// are escaped with \". Directories are identified by paths ending with '/' characters.
-// Rename lines list the old name first, followed by the new name.
// Error conditions: If no working copy book keeping MT directory is found,
// prints an error message to stderr, and exits with status 1.
+
static void
automate_inventory(std::vector args,
std::string const & help_name,
@@ -559,12 +616,12 @@
manifest_id old_manifest_id;
revision_id old_revision_id;
- manifest_map m_old;
+ manifest_map m_old, m_new;
path_set old_paths, new_paths, empty;
change_set::path_rearrangement included, excluded;
+ change_set cs;
path_set missing, changed, unchanged, unknown, ignored;
inventory_map inventory;
-
app.require_working_copy();
calculate_restricted_rearrangement(app, args,
@@ -572,74 +629,111 @@
m_old, old_paths, new_paths,
included, excluded);
- file_itemizer u(app, new_paths, unknown, ignored);
- walk_tree(u);
+ // this is a bit screwey. we need to rearrange the old manifest
+ // according to the included rearrangement and for that we need
+ // a complete changeset, which is normally obtained from both
+ // the old and the new manifest. we can't do that because there
+ // may be missing files, so instead we add our own set of deltas
+ // below.
- // remove deleted paths from the set of unknown paths
+ // we have the rearrangement of the changeset from above
+ // now we need to build up the deltas for the added files
- for (path_set::const_iterator i = included.deleted_files.begin();
- i != included.deleted_files.end(); ++i)
- unknown.erase(*i);
+ cs.rearrangement = included;
- for (path_set::const_iterator i = included.deleted_dirs.begin();
- i != included.deleted_dirs.end(); ++i)
- unknown.erase(*i);
+ hexenc null_ident;
- classify_paths(app, new_paths, m_old, missing, changed, unchanged);
+ for (path_set::const_iterator
+ i = included.added_files.begin();
+ i != included.added_files.end(); ++i)
+ {
+ if (file_exists(*i))
+ {
+ // add path from [] to [xxx]
+ hexenc ident;
+ calculate_ident(*i, ident, app.lua);
+ cs.deltas.insert(std::make_pair(*i,std::make_pair(null_ident, ident)));
+ }
+ else
+ {
+ // remove missing files from the added list since they have not deltas
+ missing.insert(*i);
+ cs.rearrangement.added_files.erase(*i);
+ }
+ }
- inventory_paths(inventory, missing, inventory_item::MISSING_DATA);
+ apply_change_set(m_old, cs, m_new);
- inventory_paths(inventory, included.deleted_files, inventory_item::DROPPED_PATH);
- inventory_paths(inventory, included.deleted_dirs, inventory_item::DROPPED_PATH, inventory_item::DIRECTORY);
+ classify_manifest_paths(app, m_new, missing, changed, unchanged);
- inventory_paths(inventory, included.renamed_files, inventory_item::RENAMED_PATH);
- inventory_paths(inventory, included.renamed_dirs, inventory_item::RENAMED_PATH, inventory_item::DIRECTORY);
+ // remove the remaining added files from the unchanged set since they have been
+ // changed in the deltas construction above. also, only consider the file as
+ // changed if its not missing
- inventory_paths(inventory, included.added_files, inventory_item::ADDED_PATH);
- inventory_paths(inventory, changed, inventory_item::PATCHED_DATA);
-
- inventory_paths(inventory, unchanged, inventory_item::UNCHANGED_DATA);
- inventory_paths(inventory, unknown, inventory_item::UNKNOWN_PATH);
- inventory_paths(inventory, ignored, inventory_item::IGNORED_PATH);
+ for (path_set::const_iterator
+ i = included.added_files.begin();
+ i != included.added_files.end(); ++i)
+ {
+ unchanged.erase(*i);
+ if (missing.find(*i) == missing.end())
+ changed.insert(*i);
+ }
+ file_itemizer u(app, new_paths, unknown, ignored);
+ walk_tree(u);
+
+ inventory_file_state(inventory, missing, inventory_item::MISSING_FILE);
+
+ inventory_pre_state(inventory, included.deleted_files, inventory_item::DROPPED_PATH);
+ inventory_pre_state(inventory, included.deleted_dirs,
+ inventory_item::DROPPED_PATH, inventory_item::DIRECTORY);
+
+ inventory_renames(inventory, included.renamed_files);
+ inventory_renames(inventory, included.renamed_dirs, inventory_item::DIRECTORY);
+
+ inventory_post_state(inventory, included.added_files, inventory_item::ADDED_PATH);
+
+ inventory_file_state(inventory, changed, inventory_item::PATCHED_FILE);
+ inventory_file_state(inventory, unchanged, inventory_item::KNOWN_FILE);
+ inventory_file_state(inventory, unknown, inventory_item::UNKNOWN_FILE);
+ inventory_file_state(inventory, ignored, inventory_item::IGNORED_FILE);
+
for (inventory_map::const_iterator i = inventory.begin(); i != inventory.end(); ++i)
{
- switch (inventory[i->first].path_status)
+ switch (inventory[i->first].pre_state)
{
- case inventory_item::UNCHANGED_PATH: output << " "; break;
- case inventory_item::ADDED_PATH: output << "+"; break;
- case inventory_item::DROPPED_PATH: output << "-"; break;
- case inventory_item::RENAMED_PATH: output << "%"; break;
- case inventory_item::UNKNOWN_PATH: output << "?"; break;
- case inventory_item::IGNORED_PATH: output << "~"; break;
+ case inventory_item::KNOWN_PATH: output << " "; break;
+ case inventory_item::DROPPED_PATH: output << "D"; break;
+ case inventory_item::RENAMED_PATH: output << "R"; break;
+ default: I(false); // invalid pre_state
}
- switch (inventory[i->first].data_status)
+ switch (inventory[i->first].post_state)
{
- case inventory_item::UNCHANGED_DATA: output << " "; break;
- case inventory_item::PATCHED_DATA: output << "#"; break;
- case inventory_item::MISSING_DATA: output << "!"; break;
+ case inventory_item::KNOWN_PATH: output << " "; break;
+ case inventory_item::RENAMED_PATH: output << "R"; break;
+ case inventory_item::ADDED_PATH: output << "A"; break;
+ default: I(false); // invalid post_state
}
- output << " ";
-
- switch (inventory[i->first].path_type)
+ switch (inventory[i->first].file_state)
{
- case inventory_item::FILE:
- if (inventory[i->first].path_status == inventory_item::RENAMED_PATH)
- output << basic_io::escape(inventory[i->first].old_path()) << " ";
-
- output << basic_io::escape(i->first());
- break;
+ case inventory_item::KNOWN_FILE: output << " "; break;
+ case inventory_item::PATCHED_FILE: output << "P"; break;
+ case inventory_item::UNKNOWN_FILE: output << "U"; break;
+ case inventory_item::IGNORED_FILE: output << "I"; break;
+ case inventory_item::MISSING_FILE: output << "M"; break;
+ }
- case inventory_item::DIRECTORY:
- if (inventory[i->first].path_status == inventory_item::RENAMED_PATH)
- output << basic_io::escape(inventory[i->first].old_path() + "/") << " ";
-
- output << basic_io::escape(i->first() + "/");
- break;
- }
-
+ // need directory indicators
+
+ output << " " << inventory[i->first].pre_id
+ << " " << inventory[i->first].post_id
+ << " " << i->first;
+
+ if (inventory[i->first].path_type == inventory_item::DIRECTORY)
+ output << "/";
+
output << std::endl;
}
--- manifest.cc
+++ manifest.cc
@@ -84,12 +84,11 @@
}
void
-classify_paths(app_state & app,
- path_set const & paths,
- manifest_map const & m_old,
- path_set & missing,
- path_set & changed,
- path_set & unchanged)
+classify_manifest_paths(app_state & app,
+ manifest_map const & man,
+ path_set & missing,
+ path_set & changed,
+ path_set & unchanged)
{
inodeprint_map ipm;
@@ -103,48 +102,41 @@
// this code is speed critical, hence the use of inode fingerprints so be
// careful when making changes in here and preferably do some timing tests
- for (path_set::const_iterator i = paths.begin(); i != paths.end(); ++i)
+ for (manifest_map::const_iterator i = man.begin(); i != man.end(); ++i)
{
- if (app.restriction_includes(*i))
+ if (app.restriction_includes(i->first))
{
// compute the current sha1 id for included files
// we might be able to avoid it, if we have an inode fingerprint...
- if (inodeprint_unchanged(ipm, *i))
+ if (inodeprint_unchanged(ipm, i->first))
{
// the inode fingerprint hasn't changed, so we assume the file
// hasn't either.
- manifest_map::const_iterator k = m_old.find(*i);
- I(k != m_old.end());
- unchanged.insert(*i);
+ manifest_map::const_iterator k = man.find(i->first);
+ I(k != man.end());
+ unchanged.insert(i->first);
continue;
}
// ...ah, well, no good fingerprint, just check directly.
- if (file_exists(*i))
+ if (file_exists(i->first))
{
hexenc ident;
- calculate_ident(*i, ident, app.lua);
- manifest_map::const_iterator k = m_old.find(*i);
+ calculate_ident(i->first, ident, app.lua);
- if (k != m_old.end())
- {
- if (ident == k->second.inner())
- unchanged.insert(*i);
- else
- changed.insert(*i);
- }
+ if (ident == i->second.inner())
+ unchanged.insert(i->first);
+ else
+ changed.insert(i->first);
- // if the path was not found in the old manifest it must have
- // been added or renamed ad it's ignored here
-
}
else
- missing.insert(*i);
+ missing.insert(i->first);
}
else
{
// changes to excluded files are ignored
- unchanged.insert(*i);
+ unchanged.insert(i->first);
}
}
}
--- manifest.hh
+++ manifest.hh
@@ -76,12 +76,11 @@
void extract_path_set(manifest_map const & man, path_set & paths);
-void classify_paths(app_state & app,
- path_set const & paths,
- manifest_map const & m_old,
- path_set & missing,
- path_set & changed,
- path_set & unchanged);
+void classify_manifest_paths(app_state & app,
+ manifest_map const & man,
+ path_set & missing,
+ path_set & changed,
+ path_set & unchanged);
void build_restricted_manifest_map(path_set const & paths,
manifest_map const & m_old,
--- monotone.texi
+++ monotone.texi
@@ -4778,63 +4778,111 @@
@item Added in:
-0.2
+1.0
@item Purpose:
-Prints the inventory of all files found in the current working copy
-prefixed by 2 status code characters.
+Prints the inventory of every file found in the working copy or its
+associated base manifest. Each unique path is listed on a line prefixed by
+three status characters and two numeric values used for identifying
+renames.
@item Sample output:
@verbatim
-+ "added-file"
-+! "added-and-missing-file"
-- "dropped-file"
- # "edited-file"
-% "file-before-rename" "file-after-rename"
-%# "file-before-rename" "file-after-rename-and-edit"
-~ "ignored-file"
- ! "missing-file"
- "unchanged-file"
-? "unknown-file"
address@hidden verbatim
+ M 0 0 missing
+ AP 0 0 added patched
+D 0 0 dropped
+R 1 0 renamed-from-this
+ R 0 1 renamed-to-this
+ P 0 0 patched
+ 0 0 unchanged
+ U 0 0 unknown
+ I 0 0 ignored
address@hidden Output format:
+# swapped but not moved
-Each file is printed on its own line, prefixed by a two character status
-code characters and a single space character. All filenames are quoted
-with double quotes (") to support filenames containg spaces. Intervening
-quotes are escaped with \". Directories are identified by paths ending
-with '/' characters. Rename lines list the old name first, followed by
-the new name. Lines are ordered by their pathname, with renames using
-the new name is used for ordering.
+RRP 1 2 unchanged
+RRP 2 1 original
-The first status character indicates the status of the path itsself and
-may be one of the following:
+# swapped and moved
address@hidden
address@hidden ' ' the path is unchanged from the current manifest
address@hidden '+' the path has been added to the current manifest
address@hidden '-' the path has been dropped from the current manifest
address@hidden '%' the path has been renamed in the current manifest, both the old and new name are listed
address@hidden '?' the path is unknown, it exists in the working copy but not in the current manifest
address@hidden '~' the path is ignored by the current ignore_file lua hook setting
address@hidden itemize
+RR 1 2 unchanged
+RR 2 1 original
-The second status character indicates the status of the data associated
-witth the path and may be one of the following:
+# rename foo bar; add foo
address@hidden
address@hidden ' ' the data is unchanged, its sha1 version matches the version in the base manifest
address@hidden '#' the data is changed, its sha1 version differs from the version in the base manifest
address@hidden '!' the data is missing and its sha1 version cannot be computed
address@hidden itemize
+RAP 1 0 foo
+ R 0 1 bar
+# rotated but not moved dropped -> missing -> original -> dropped
+
+RRP 1 3 dropped
+RRP 2 1 missing
+RRP 3 2 original
+
+# rotated and moved
+
+RR 1 3 dropped
+RR 2 1 missing
+RR 3 2 original
+
+# dropped but not removed and thus unknown
+
+D U 0 0 dropped
+
+# added but removed and thus missing
+
+ AM 0 0 added
+
+# renamed but not moved and thus unknown source and missing target
+
+R U 1 0 original
+ RM 0 1 renamed
+
+# moved but not renamed and thus missing source and unknown target
+
+ M 0 0 original
+ U 0 0 renamed
+
+# renamed and patched
+
+R 1 0 original
+ RP 0 1 renamed
+
address@hidden verbatim
+
address@hidden Output format:
+
+Each path is printed on its own line, prefixed by three status
+characters described below. The status is followed by a single space and
+two numbers, each separated by a single space, used for identifying renames.
+The numbers are followed by a single space and then the pathname, which
+includes the rest of the line. Directory paths are identified as ending with
+the "/" character, file paths do not end in this character.
+
+The three status characters are as follows.
+
address@hidden
+column 1 pre-state
+ ' ' the path was unchanged in the pre-state
+ 'D' the path was deleted from the pre-state
+ 'R' the path was renamed from the pre-state name
+column 2 post-state
+ ' ' the path was unchanged in the post-state
+ 'R' the path was renamed to the post-state name
+ 'A' the path was added to the post-state
+column 3 file-state
+ ' ' the file is known and unchanged from the current manifest version
+ 'P' the file is patched to a new version
+ 'U' the file is unknown and not included in the current manifest
+ 'I' the file is ignored and not included in the current manifest
+ 'M' the file is missing but is included in the current manifest
address@hidden verbatim
+
Full support for versioned directories is not yet complete and the
inventory will only list entries for renamed or dropped
-directories. Directory entries are designated by pathnames ending with
-"/" characters.
+directories.
@item Error conditions:
--- tests/t_automate_inventory.at
+++ tests/t_automate_inventory.at
@@ -18,11 +18,13 @@
ADD_FILE(unchanged, [unchanged
])
-ADD_FILE(changed, [changed
+ADD_FILE(patched, [patched
])
COMMIT(testbranch)
+# single status changes
+
ADD_FILE(added, [added
])
AT_DATA(unknown, [unknown
@@ -31,9 +33,9 @@
])
AT_CHECK(rm missing)
-
+AT_CHECK(rm dropped)
AT_CHECK(mv original renamed)
-AT_DATA(changed, [something has changed
+AT_DATA(patched, [something has changed
])
AT_CHECK(MONOTONE add added, [], [ignore], [ignore])
@@ -41,35 +43,131 @@
AT_CHECK(MONOTONE drop dropped, [], [ignore], [ignore])
AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^ M 0 0 missing$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ AP 0 0 added$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^D 0 0 dropped$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^R 1 0 original$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ R 0 1 renamed$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ P 0 0 patched$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ 0 0 unchanged$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ U 0 0 unknown$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ I 0 0 ignored~$' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^ ! "missing"' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^+ "added"' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^- "dropped"' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^% "original" "renamed"' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^ . "changed"' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^ "unchanged"' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^? "unknown"' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^~ "ignored~"' stdout, [], [ignore], [ignore])
+# swapped but not moved
-# renamed and changed
+AT_CHECK(MONOTONE revert, [], [ignore], [ignore])
-AT_DATA(renamed, [renamed and changed
-])
+AT_CHECK(MONOTONE rename unchanged temporary, [], [ignore], [ignore])
+AT_CHECK(MONOTONE rename original unchanged, [], [ignore], [ignore])
+AT_CHECK(MONOTONE rename temporary original, [], [ignore], [ignore])
-AT_CHECK(MONOTONE automate inventory, [], [stdout], [ignore])
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^RRP 1 2 original$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^RRP 2 1 unchanged$' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^%. "renamed"' stdout, [1], [ignore], [ignore])
-AT_CHECK(grep '^% "original" "renamed"' stdout, [], [ignore], [ignore])
+# swapped and moved
-# missing renamed and added files
+AT_CHECK(mv unchanged temporary)
+AT_CHECK(mv original unchanged)
+AT_CHECK(mv temporary original)
-AT_CHECK(rm renamed added)
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^RR 1 2 original$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^RR 2 1 unchanged$' stdout, [], [ignore], [ignore])
-AT_CHECK(MONOTONE automate inventory, [], [stdout], [ignore])
+# rename foo bar; add foo
-AT_CHECK(grep '^+! "added"' stdout, [], [ignore], [ignore])
-AT_CHECK(grep '^%! "original" "renamed"' stdout, [], [ignore], [ignore])
+AT_CHECK(MONOTONE revert, [], [ignore], [ignore])
+AT_CHECK(MONOTONE rename original renamed, [], [ignore], [ignore])
+AT_CHECK(MONOTONE add original, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^RAP 1 0 original$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ R 0 1 renamed$' stdout, [], [ignore], [ignore])
+
+# rotated but not moved
+# - note that things are listed and numbered in path collating order
+# dropped -> missing -> original -> dropped
+
+AT_CHECK(MONOTONE revert, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE rename original temporary, [], [ignore], [ignore])
+AT_CHECK(MONOTONE rename missing original, [], [ignore], [ignore])
+AT_CHECK(MONOTONE rename dropped missing, [], [ignore], [ignore])
+AT_CHECK(MONOTONE rename temporary dropped, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^RRP 1 3 dropped$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^RRP 2 1 missing$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^RRP 3 2 original$' stdout, [], [ignore], [ignore])
+
+# rotated and moved
+
+AT_CHECK(mv original temporary)
+AT_CHECK(mv missing original)
+AT_CHECK(mv dropped missing)
+AT_CHECK(mv temporary dropped)
+
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^RR 1 3 dropped$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^RR 2 1 missing$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^RR 3 2 original$' stdout, [], [ignore], [ignore])
+
+# dropped but not removed and thus unknown
+
+AT_CHECK(MONOTONE revert, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE drop dropped, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^D U 0 0 dropped$' stdout, [], [ignore], [ignore])
+
+# added but removed and thus missing
+
+AT_CHECK(MONOTONE revert, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE add added, [], [ignore], [ignore])
+AT_CHECK(rm added, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^ AM 0 0 added$' stdout, [], [ignore], [ignore])
+
+# renamed but not moved and thus unknown source and missing target
+
+AT_CHECK(MONOTONE revert, [], [ignore], [ignore])
+
+AT_CHECK(rm renamed)
+AT_CHECK(MONOTONE rename original renamed, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^R U 1 0 original$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ RM 0 1 renamed$' stdout, [], [ignore], [ignore])
+
+# moved but not renamed and thus missing source and unknown target
+
+AT_CHECK(MONOTONE revert, [], [ignore], [ignore])
+
+AT_CHECK(mv original renamed, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+AT_CHECK(grep '^ M 0 0 original$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ U 0 0 renamed$' stdout, [], [ignore], [ignore])
+
+# renamed and patched
+
+AT_CHECK(MONOTONE revert, [], [ignore], [ignore])
+
+AT_DATA(renamed, [renamed and patched
+])
+AT_CHECK(rm original, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE rename original renamed, [], [ignore], [ignore])
+AT_CHECK(MONOTONE automate inventory --rcfile=inventory_hooks.lua, [], [stdout], [ignore])
+
+AT_CHECK(grep '^R 1 0 original$' stdout, [], [ignore], [ignore])
+AT_CHECK(grep '^ RP 0 1 renamed$' stdout, [], [ignore], [ignore])
+
# need tests for deleted and renamed directories, once these actually work!
AT_CLEANUP