# # patch "automate.cc" # from [8ce97684b884ff4bce8839e3353621f9ee89aa4d] # to [7b9265dbdf4cb7458a01457f45a12fdae102dc57] # # patch "roster.cc" # from [12bf8a61f931883e89a9e016aff358e4af219607] # to [86fabe39ac8fcc3240c1c479d6e689d72453c4ea] # # patch "roster.hh" # from [5e27376e3d341c426930fb6eb71860145a59b18c] # to [6fe9181b0195f97220b667ae96399037c5648156] # # patch "tests/t_automate_inventory.at" # from [109af5a233e8700e3d11694aefc5f03ddc84107e] # to [6cf9eb1dcaaf03298b0ed13251790db8f1a6d85b] # # patch "work.cc" # from [45c039be61f0b62c573b4e4b4c18a200ba9a7b15] # to [7782aba9f7122a0d161af8d125401ec264a24efb] # ======================================================================== --- automate.cc 8ce97684b884ff4bce8839e3353621f9ee89aa4d +++ automate.cc 7b9265dbdf4cb7458a01457f45a12fdae102dc57 @@ -517,54 +517,49 @@ // // 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 +// node-state corresponds to the state of the node 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 +// pre/post state are related to the path rearrangement in MT/work +// node state is related to the details of the resulting path struct inventory_item { + // pre/post rearrangement state enum pstate - { KNOWN_PATH, ADDED_PATH, DROPPED_PATH, RENAMED_PATH } + { UNCHANGED_PATH, ADDED_PATH, DROPPED_PATH, RENAMED_PATH } pre_state, post_state; - enum fstate - { KNOWN_FILE, PATCHED_FILE, MISSING_FILE, UNKNOWN_FILE, IGNORED_FILE } - file_state; + enum nstate + { UNCHANGED_NODE, PATCHED_NODE, MISSING_NODE, UNKNOWN_NODE, IGNORED_NODE } + node_state; - enum ptype - { FILE, DIRECTORY } - path_type; - size_t pre_id, post_id; inventory_item(): - pre_state(KNOWN_PATH), post_state(KNOWN_PATH), - file_state(KNOWN_FILE), - path_type(FILE), + pre_state(UNCHANGED_PATH), post_state(UNCHANGED_PATH), + node_state(UNCHANGED_NODE), pre_id(0), post_id(0) {} }; -typedef std::map inventory_map; +typedef std::map inventory_map; +typedef std::map rename_map; // this might be good in cset.hh +typedef std::map addition_map; // ditto static void 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) + size_t rename_id) { for (path_set::const_iterator i = paths.begin(); i != paths.end(); i++) { - file_path fp(*i); - L(F("%d %d %s\n") % inventory[*i].pre_state % pre_state % fp); - I(inventory[*i].pre_state == inventory_item::KNOWN_PATH); + L(F("%d %d %s\n") % inventory[*i].pre_state % pre_state % file_path(*i)); + I(inventory[*i].pre_state == inventory_item::UNCHANGED_PATH); inventory[*i].pre_state = pre_state; - inventory[*i].path_type = path_type; - if (id != 0) + if (rename_id != 0) { I(inventory[*i].pre_id == 0); - inventory[*i].pre_id = id; + inventory[*i].pre_id = rename_id; } } } @@ -573,67 +568,69 @@ 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) + size_t rename_id) { for (path_set::const_iterator i = paths.begin(); i != paths.end(); i++) { - file_path fp(*i); - L(F("%d %d %s\n") % inventory[*i].post_state % post_state % fp); - I(inventory[*i].post_state == inventory_item::KNOWN_PATH); + L(F("%d %d %s\n") % inventory[*i].post_state % post_state % file_path(*i)); + I(inventory[*i].post_state == inventory_item::UNCHANGED_PATH); inventory[*i].post_state = post_state; - inventory[*i].path_type = path_type; - if (id != 0) + if (rename_id != 0) { I(inventory[*i].post_id == 0); - inventory[*i].post_id = id; + inventory[*i].post_id = rename_id; } } } static void -inventory_file_state(inventory_map & inventory, +inventory_node_state(inventory_map & inventory, path_set const & paths, - inventory_item::fstate file_state) + inventory_item::nstate node_state) { for (path_set::const_iterator i = paths.begin(); i != paths.end(); i++) { - file_path fp(*i); - L(F("%d %d %s\n") % inventory[*i].file_state % file_state % fp); - I(inventory[*i].file_state == inventory_item::KNOWN_FILE); - inventory[*i].file_state = file_state; + L(F("%d %d %s\n") % inventory[*i].node_state % node_state % file_path(*i)); + I(inventory[*i].node_state == inventory_item::UNCHANGED_NODE); + inventory[*i].node_state = node_state; } } static void inventory_renames(inventory_map & inventory, - std::map const & renames, - inventory_item::ptype path_type = inventory_item::FILE) + rename_map const & renames) { path_set old_name; path_set new_name; - static size_t id = 1; + static size_t rename_id = 1; - for (std::map::const_iterator i = renames.begin(); + for (rename_map::const_iterator i = renames.begin(); i != renames.end(); i++) { - split_path sp1, sp2; - i->first.split(sp1); - i->second.split(sp2); - old_name.insert(sp1); - new_name.insert(sp2); + old_name.clear(); + new_name.clear(); - 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); + old_name.insert(i->first); + new_name.insert(i->second); - id++; + inventory_pre_state(inventory, old_name, inventory_item::RENAMED_PATH, rename_id); + inventory_post_state(inventory, new_name, inventory_item::RENAMED_PATH, rename_id); - old_name.clear(); - new_name.clear(); + rename_id++; } } - + +static void +extract_added_file_paths(addition_map const & additions, path_set & paths) +{ + for (addition_map::const_iterator i = additions.begin(); i != additions.end(); ++i) + { + paths.insert(i->first); + } +} + + // Name: inventory // Arguments: none // Added in: 1.0 @@ -650,12 +647,12 @@ // ' ' 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 +// column 3 node-state +// ' ' the node is unchanged from the current roster +// 'P' the node is patched to a new version +// 'U' the node is unknown and not included in the roster +// 'I' the node is ignored and not included in the roster +// 'M' the node is missing but is included in the roster // // 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 @@ -673,134 +670,108 @@ app_state & app, std::ostream & output) { -/* -// FIXME_ROSTERS: disabled until rewritten to use rosters if (args.size() != 0) throw usage(help_name); - manifest_id old_manifest_id; - revision_id old_revision_id; - 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, - old_manifest_id, old_revision_id, - m_old, old_paths, new_paths, - included, excluded); + temp_node_id_source nis; + roster_t base, curr; + inventory_map inventory; + cset cs; + path_set unchanged, changed, missing, known, unknown, ignored; - // 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. + get_base_and_current_roster_shape(base, curr, nis, app); + make_cset(base, curr, cs); - // we have the rearrangement of the changeset from above - // now we need to build up the deltas for the added files + I(cs.deltas_applied.empty()); - cs.rearrangement = included; + // the current roster (curr) has the complete set of registered nodes + // conveniently with unchanged sha1 hash values - hexenc null_ident; + // the cset (cs) has the list of drops/renames/adds that have occurred between + // the two rosters along with an empty list of deltas. this list is empty + // only because the current roster used to generate the cset does not have + // current hash values as recorded on the filesystem (because get_..._shape + // was used to build it) - for (path_set::const_iterator - i = included.added_files.begin(); - i != included.added_files.end(); ++i) - { - if (path_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); - } - } + path_set nodes_added(cs.dirs_added); + extract_added_file_paths(cs.files_added, nodes_added); - apply_change_set(m_old, cs, m_new); + inventory_pre_state(inventory, cs.nodes_deleted, inventory_item::DROPPED_PATH, 0); + inventory_renames(inventory, cs.nodes_renamed); + inventory_post_state(inventory, nodes_added, inventory_item::ADDED_PATH, 0); - classify_manifest_paths(app, m_new, missing, changed, unchanged); + classify_roster_paths(curr, unchanged, changed, missing, app); + curr.extract_path_set(known); - // 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 - - 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); + file_itemizer u(app, known, unknown, ignored); walk_tree(file_path(), u); - inventory_file_state(inventory, missing, inventory_item::MISSING_FILE); + inventory_node_state(inventory, unchanged, inventory_item::UNCHANGED_NODE); + inventory_node_state(inventory, changed, inventory_item::PATCHED_NODE); + inventory_node_state(inventory, missing, inventory_item::MISSING_NODE); + inventory_node_state(inventory, unknown, inventory_item::UNKNOWN_NODE); + inventory_node_state(inventory, ignored, inventory_item::IGNORED_NODE); - 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); + // FIXME: do we want to report on attribute changes here?!? - inventory_renames(inventory, included.renamed_files); - inventory_renames(inventory, included.renamed_dirs, inventory_item::DIRECTORY); + for (inventory_map::const_iterator i = inventory.begin(); i != inventory.end(); ++i) + { - inventory_post_state(inventory, included.added_files, inventory_item::ADDED_PATH); + std::string path_suffix; - 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); + if (curr.has_node(i->first)) + { + // explicitly skip the root dir for now... + // the trailing / dir format isn't going to work here + node_t n = curr.get_node(i->first); + if (is_root_dir_t(n)) continue; + if (is_dir_t(n)) path_suffix = "/"; + } + else if (directory_exists(file_path(i->first))) + { + path_suffix = "/"; + } - for (inventory_map::const_iterator i = inventory.begin(); i != inventory.end(); ++i) - { - switch (inventory[i->first].pre_state) + switch (i->second.pre_state) { - case inventory_item::KNOWN_PATH: output << " "; break; + case inventory_item::UNCHANGED_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].post_state) + switch (i->second.post_state) { - case inventory_item::KNOWN_PATH: output << " "; break; + case inventory_item::UNCHANGED_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 } - switch (inventory[i->first].file_state) + switch (i->second.node_state) { - 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::UNCHANGED_NODE: output << " "; break; + case inventory_item::PATCHED_NODE: output << "P"; break; + case inventory_item::UNKNOWN_NODE: output << "U"; break; + case inventory_item::IGNORED_NODE: output << "I"; break; + case inventory_item::MISSING_NODE: output << "M"; break; + default: I(false); // invalid node_state } - // need directory indicators - - output << " " << inventory[i->first].pre_id - << " " << inventory[i->first].post_id + output << " " << i->second.pre_id + << " " << i->second.post_id << " " << i->first; - if (inventory[i->first].path_type == inventory_item::DIRECTORY) - output << "/"; + // FIXME: it's possible that a directory was deleted and a file was added + // in it's place (or vice-versa) so we need something like pre/post node + // type indicators rather than a simple path suffix! ugh. + + output << path_suffix; output << std::endl; } -*/ } // Name: certs ======================================================================== --- roster.cc 12bf8a61f931883e89a9e016aff358e4af219607 +++ roster.cc 86fabe39ac8fcc3240c1c479d6e689d72453c4ea @@ -1768,6 +1768,72 @@ } void +classify_roster_paths(roster_t const & ros, + path_set & unchanged, + path_set & changed, + path_set & missing, + app_state & app) +{ + temp_node_id_source nis; + inodeprint_map ipm; + + if (in_inodeprints_mode()) + { + data dat; + read_inodeprints(dat); + read_inodeprint_map(dat, ipm); + } + + // 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 + + if (!ros.has_root()) + return; + + node_map const & nodes = ros.all_nodes(); + for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i) + { + node_id nid = i->first; + node_t node = i->second; + + split_path sp; + ros.get_name(nid, sp); + file_path fp(sp); + + // Only analyze restriction-included files. + if (app.restriction_includes(fp)) + { + if (is_dir_t(node) || inodeprint_unchanged(ipm, fp)) + { + // dirs don't have content changes + unchanged.insert(sp); + } + else + { + file_t file = downcast_to_file_t(node); + file_id fid; + if (ident_existing_file(fp, fid, app.lua)) + { + if (file->content == fid) + unchanged.insert(sp); + else + changed.insert(sp); + } + else + { + missing.insert(sp); + } + } + } + else + { + // changes to excluded files are ignored + unchanged.insert(sp); + } + } +} + +void update_restricted_roster_from_filesystem(roster_t & ros, app_state & app) { @@ -1790,8 +1856,7 @@ return; node_map const & nodes = ros.all_nodes(); - for (node_map::const_iterator i = nodes.begin(); - i != nodes.end(); ++i) + for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i) { node_id nid = i->first; node_t node = i->second; ======================================================================== --- roster.hh 5e27376e3d341c426930fb6eb71860145a59b18c +++ roster.hh 6fe9181b0195f97220b667ae96399037c5648156 @@ -113,7 +113,18 @@ return static_cast(f); } +inline bool +is_root_dir_t(node_t n) +{ + if (is_dir_t(n) && null_name(n->name)) + { + I(null_node(n->parent)); + return true; + } + return false; +} + inline dir_t downcast_to_dir_t(node_t const n) { @@ -285,6 +296,13 @@ std::set & nodes_born); void +classify_roster_paths(roster_t const & ros, + path_set & unchanged, + path_set & changed, + path_set & missing, + app_state & app); + +void update_restricted_roster_from_filesystem(roster_t & ros, app_state & app); ======================================================================== --- tests/t_automate_inventory.at 109af5a233e8700e3d11694aefc5f03ddc84107e +++ tests/t_automate_inventory.at 6cf9eb1dcaaf03298b0ed13251790db8f1a6d85b @@ -44,7 +44,7 @@ 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 '^ A 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]) @@ -62,8 +62,8 @@ AT_CHECK(MONOTONE rename temporary original, [], [ignore], [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 '^RRP 2 1 original$' stdout, [], [ignore], [ignore]) +AT_CHECK(grep '^RRP 1 2 unchanged$' stdout, [], [ignore], [ignore]) # swapped and moved @@ -72,8 +72,8 @@ AT_CHECK(mv temporary original) 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(grep '^RR 2 1 original$' stdout, [], [ignore], [ignore]) +AT_CHECK(grep '^RR 1 2 unchanged$' stdout, [], [ignore], [ignore]) # rename foo bar; add foo @@ -83,7 +83,7 @@ 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 '^RA 1 0 original$' stdout, [], [ignore], [ignore]) AT_CHECK(grep '^ R 0 1 renamed$' stdout, [], [ignore], [ignore]) # rotated but not moved @@ -98,9 +98,9 @@ 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]) +AT_CHECK(grep '^RRP 1 3 original$' stdout, [], [ignore], [ignore]) +AT_CHECK(grep '^RRP 2 1 dropped$' stdout, [], [ignore], [ignore]) +AT_CHECK(grep '^RRP 3 2 missing$' stdout, [], [ignore], [ignore]) # rotated and moved @@ -110,9 +110,9 @@ 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]) +AT_CHECK(grep '^RR 1 3 original$' stdout, [], [ignore], [ignore]) +AT_CHECK(grep '^RR 2 1 dropped$' stdout, [], [ignore], [ignore]) +AT_CHECK(grep '^RR 3 2 missing$' stdout, [], [ignore], [ignore]) # dropped but not removed and thus unknown ======================================================================== --- work.cc 45c039be61f0b62c573b4e4b4c18a200ba9a7b15 +++ work.cc 7782aba9f7122a0d161af8d125401ec264a24efb @@ -247,7 +247,8 @@ node_id nid = new_roster.detach_node(src); new_roster.attach_node(nid, dst); - if (app.execute && path_exists(src_path) && !path_exists(dst_path)) + // 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); cset new_work;