# # # patch "NEWS" # from [5e34c68cee702db44d6f5302adeeca951920449d] # to [0bad929ea63c745532616f7f7218f56eadf2aa93] # # patch "cmd_merging.cc" # from [24996c053cd6ce5bfbe9466e9d4bbc36fe071c3f] # to [43295b660dbeef73009f01d85ceb7f0c3f5261b2] # # patch "luaext_platform.cc" # from [16f1628b90d20a981fcf134de97bea86f89660f7] # to [b22b5f7fa127055e909280379694c1635f4923ff] # # patch "monotone.texi" # from [b43f96531707281d1cb82b9aeb3793c14db296b8] # to [0989dbcc231553830b0c89b47e88eb990534fc56] # # patch "platform.hh" # from [88ead5a98aeec122e00284198ad381d67cd2b75c] # to [579df5c8d9106dcec4fce731f445877ed5754cba] # # patch "std_hooks.lua" # from [64007e804a2f1f9e4cf7a2c1c3faeb9771d83c05] # to [e649c4ba5118116f0f6db708bbcd02b08f67d8f2] # # patch "tests/attr_mtn_execute/__driver__.lua" # from [4a2d3a7f0c2fa9e711b6ba46238fe11a6e6c3087] # to [51c34985351d3f80ed3f3b42ca266f641be56625] # # patch "tests/defecting_attributes/__driver__.lua" # from [23a95f7b5c56347e26787c3b2e77835467e87f0d] # to [9872139f7947e77709d408ac3667a288e4aaeba7] # # patch "unix/process.cc" # from [99af2840bfac311181ff2d0ee5360288494a1cb4] # to [9a3e6385e29a2ad049c1fc870d1303ae89d927ce] # # patch "win32/process.cc" # from [b04ad9cc13ce045696222da5b9796bc39397cc5d] # to [11c10a5d20b95df5c0c4e6243650f828936be8c4] # # patch "work.cc" # from [269f9057c546abfb57d5adb516731b40ef0884e8] # to [518958b5c1abd181d67132ef354e11f1c73a7027] # ============================================================ --- NEWS 5e34c68cee702db44d6f5302adeeca951920449d +++ NEWS 0bad929ea63c745532616f7f7218f56eadf2aa93 @@ -70,6 +70,9 @@ restrict revisions printed to those touching specific paths. Log now allows these options and uses them properly. + - The update command previously did not clear execute permissions from + files that had their associated 'mtn:execute' attribute cleared. + New features - The `mtn_automate' lua function now correctly parses and sets ============================================================ --- cmd_merging.cc 24996c053cd6ce5bfbe9466e9d4bbc36fe071c3f +++ cmd_merging.cc 43295b660dbeef73009f01d85ceb7f0c3f5261b2 @@ -43,6 +43,19 @@ static void using boost::shared_ptr; static void +add_dormant_attrs(node_t const parent, node_t child) +{ + for (attr_map_t::const_iterator i = parent->attrs.begin(); + i != parent->attrs.end(); ++i) + { + // if the child does not have the associated attr add a dormant one + if (child->attrs.find(i->first) == child->attrs.end()) + safe_insert(child->attrs, + make_pair(i->first, make_pair(false, attr_value()))); + } +} + +static void three_way_merge(revision_id const & ancestor_rid, roster_t const & ancestor_roster, revision_id const & left_rid, roster_t const & left_roster, revision_id const & right_rid, roster_t const & right_roster, @@ -58,6 +71,29 @@ three_way_merge(revision_id const & ance MM(left_rid); MM(right_rid); + // for this to work correctly attrs that exist in the ancestor *must* + // exist in both children, since attrs are never deleted they are only + // marked as dormant. however, since this may be any arbitrary arrangement + // of three revisions it is possible that attrs do exist in the parent and + // not in the children. in this case the attrs must be added to the + // children as dormant so that roster_merge works correctly. + + roster_t left_with_attrs(left_roster); + roster_t right_with_attrs(right_roster); + + MM(left_with_attrs); + MM(right_with_attrs); + + node_map const & nodes = ancestor_roster.all_nodes(); + + for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i) + { + if (left_with_attrs.has_node(i->first)) + add_dormant_attrs(i->second, left_with_attrs.get_node(i->first)); + if (right_with_attrs.has_node(i->first)) + add_dormant_attrs(i->second, right_with_attrs.get_node(i->first)); + } + // Mark up the ANCESTOR marking_map ancestor_markings; MM(ancestor_markings); mark_roster_with_no_parents(ancestor_rid, ancestor_roster, ancestor_markings); @@ -66,13 +102,13 @@ three_way_merge(revision_id const & ance left_markings.clear(); MM(left_markings); mark_roster_with_one_parent(ancestor_roster, ancestor_markings, - left_rid, left_roster, left_markings); + left_rid, left_with_attrs, left_markings); // Mark up the RIGHT roster right_markings.clear(); MM(right_markings); mark_roster_with_one_parent(ancestor_roster, ancestor_markings, - right_rid, right_roster, right_markings); + right_rid, right_with_attrs, right_markings); // Make the synthetic graph, by creating uncommon ancestor sets std::set left_uncommon_ancestors, right_uncommon_ancestors; @@ -83,8 +119,8 @@ three_way_merge(revision_id const & ance P(F("[right] %s") % right_rid); // And do the merge - roster_merge(left_roster, left_markings, left_uncommon_ancestors, - right_roster, right_markings, right_uncommon_ancestors, + roster_merge(left_with_attrs, left_markings, left_uncommon_ancestors, + right_with_attrs, right_markings, right_uncommon_ancestors, result); } ============================================================ --- luaext_platform.cc 16f1628b90d20a981fcf134de97bea86f89660f7 +++ luaext_platform.cc b22b5f7fa127055e909280379694c1635f4923ff @@ -40,13 +40,20 @@ LUAEXT(is_executable, ) return 1; } -LUAEXT(make_executable, ) +LUAEXT(set_executable, ) { const char *path = luaL_checkstring(LS, -1); - lua_pushnumber(LS, make_executable(path)); + lua_pushnumber(LS, set_executable(path)); return 1; } +LUAEXT(clear_executable, ) +{ + const char *path = luaL_checkstring(LS, -1); + lua_pushnumber(LS, clear_executable(path)); + return 1; +} + LUAEXT(spawn, ) { int n = lua_gettop(LS); ============================================================ --- monotone.texi b43f96531707281d1cb82b9aeb3793c14db296b8 +++ monotone.texi 0989dbcc231553830b0c89b47e88eb990534fc56 @@ -10232,7 +10232,7 @@ @subsection Attribute Handling or programs. Monotone allows each file (or directory) in a repository to carry -arbitrary @ref{File Attributes}. Persistent attributes are stored +arbitrary @ref{File Attributes}. Persistent attributes are stored in each revision's manifest. The hooks in this section allow files to be automatically recognised as having certain attributes at the time they're added, and for custom triggers to be invoked on each file @@ -10260,8 +10260,10 @@ @subsection Attribute Handling @group attr_functions["mtn:execute"] = function(filename, value) - if (value == "true") then - make_executable(filename) + if (value == "true") then + set_executable(filename) + else + clear_executable(filename) end end @end group ============================================================ --- platform.hh 88ead5a98aeec122e00284198ad381d67cd2b75c +++ platform.hh 579df5c8d9106dcec4fce731f445877ed5754cba @@ -22,7 +22,8 @@ int existsonpath(const char *exe); // For LUA int existsonpath(const char *exe); -int make_executable(const char *path); +int set_executable(const char *path); +int clear_executable(const char *path); pid_t process_spawn(const char * const argv[]); pid_t process_spawn_redirected(char const * in, char const * out, ============================================================ --- std_hooks.lua 64007e804a2f1f9e4cf7a2c1c3faeb9771d83c05 +++ std_hooks.lua e649c4ba5118116f0f6db708bbcd02b08f67d8f2 @@ -96,7 +96,9 @@ attr_functions["mtn:execute"] = attr_functions["mtn:execute"] = function(filename, value) if (value == "true") then - make_executable(filename) + set_executable(filename) + else + clear_executable(filename) end end ============================================================ --- tests/attr_mtn_execute/__driver__.lua 4a2d3a7f0c2fa9e711b6ba46238fe11a6e6c3087 +++ tests/attr_mtn_execute/__driver__.lua 51c34985351d3f80ed3f3b42ca266f641be56625 @@ -15,15 +15,8 @@ with_x = base_revision() commit() with_x = base_revision() -check(mtn("update", "-r"..without_x), 0, true, true) --- expected to fail: --- * we don't call hooks when an attribute is cleared --- * we don't have a way to clear the x-bit from lua --- (that's easy to implement though) -xfail({"test", "!", "-x","foo"}, 0, false, false) +check(mtn("update", "-r", without_x), 0, true, true) +check({"test", "!", "-x","foo"}, 0, false, false) --- note: tests following an xfail are not executed - -check(mtn("update", "-r "..with_x), 0, false, false) +check(mtn("update", "-r", with_x), 0, false, false) check({"test", "-x","foo"}, 0, false, false) - ============================================================ --- tests/defecting_attributes/__driver__.lua 23a95f7b5c56347e26787c3b2e77835467e87f0d +++ tests/defecting_attributes/__driver__.lua 9872139f7947e77709d408ac3667a288e4aaeba7 @@ -22,5 +22,5 @@ check(mtn("status"), 0, true, false) -- update to child check(mtn("update", "-r", child), 0, false, false) check(mtn("status"), 0, true, false) -xfail(qgrep("no changes", "stdout")) +check(qgrep("no changes", "stdout")) ============================================================ --- unix/process.cc 99af2840bfac311181ff2d0ee5360288494a1cb4 +++ unix/process.cc 9a3e6385e29a2ad049c1fc870d1303ae89d927ce @@ -75,7 +75,7 @@ read_umask() return mask; } -int make_executable(const char *path) +int set_executable(const char *path) { mode_t mode; struct stat s; @@ -90,6 +90,7 @@ int make_executable(const char *path) return -1; mode = s.st_mode; mode |= ((S_IXUSR|S_IXGRP|S_IXOTH) & ~read_umask()); + L(FL("setting execute permission on path %s with mode %s") % path % mode); int ret = fchmod(fd, mode); if (close(fd) != 0) { @@ -100,6 +101,32 @@ int make_executable(const char *path) return ret; } +int clear_executable(const char *path) +{ + mode_t mode; + struct stat s; + int fd = open(path, O_RDONLY); + if (fd == -1) + { + const int err = errno; + E(false, origin::user, + F("error opening file %s: %s") % path % os_strerror(err)); + } + if (fstat(fd, &s)) + return -1; + mode = s.st_mode; + mode &= (~(S_IXUSR|S_IXGRP|S_IXOTH) & ~read_umask()); + L(FL("clearing execute permission on path %s with mode %s") % path % mode); + int ret = fchmod(fd, mode); + if (close(fd) != 0) + { + const int err = errno; + E(false, origin::system, + F("error closing file %s: %s") % path % os_strerror(err)); + } + return ret; +} + pid_t process_spawn(const char * const argv[]) { { ============================================================ --- win32/process.cc b04ad9cc13ce045696222da5b9796bc39397cc5d +++ win32/process.cc 11c10a5d20b95df5c0c4e6243650f828936be8c4 @@ -108,11 +108,16 @@ bool is_executable(const char *path) return false; /* Basically meaningless on win32 */ } -int make_executable(const char *path) +int set_executable(const char *path) { return 0; /* Basically meaningless on win32 */ } +int clear_executable(const char *path) +{ + return 0; /* Basically meaningless on win32 */ +} + pid_t process_spawn(const char * const argv[]) { char *realexe,*filepart; ============================================================ --- work.cc 269f9057c546abfb57d5adb516731b40ef0884e8 +++ work.cc 518958b5c1abd181d67132ef354e11f1c73a7027 @@ -939,10 +939,11 @@ struct editable_working_tree : public ed struct editable_working_tree : public editable_tree { - editable_working_tree(workspace & work, content_merge_adaptor const & source, + editable_working_tree(workspace & work, lua_hooks & lua, + content_merge_adaptor const & source, bool const messages) - : work(work), source(source), next_nid(1), root_dir_attached(true), - messages(messages) + : work(work), lua(lua), source(source), next_nid(1), + root_dir_attached(true), messages(messages) {}; virtual node_id detach_node(file_path const & src); @@ -966,6 +967,7 @@ private: virtual ~editable_working_tree(); private: workspace & work; + lua_hooks & lua; content_merge_adaptor const & source; node_id next_nid; std::map rename_add_drop_map; @@ -1192,7 +1194,8 @@ editable_working_tree::clear_attr(file_p editable_working_tree::clear_attr(file_path const & path, attr_key const & key) { - // FIXME_ROSTERS: call a lua hook + L(FL("calling hook to clear attribute %s on %s") % key % path); + lua.hook_apply_attribute(key(), path, ""); } void @@ -1200,7 +1203,8 @@ editable_working_tree::set_attr(file_pat attr_key const & key, attr_value const & value) { - // FIXME_ROSTERS: call a lua hook + L(FL("calling hook to set attribute %s on %s to %s") % key % path % value); + lua.hook_apply_attribute(key(), path, value()); } void @@ -1853,7 +1857,7 @@ workspace::perform_content_update(databa mkdir_p(detached); - editable_working_tree ewt(*this, ca, messages); + editable_working_tree ewt(*this, this->lua, ca, messages); update.apply_to(ewt); delete_dir_shallow(detached);