# # delete_file "tests/t_merge_binary.at" # # add_file "tests/t_merge_manual.at" # # patch "diff_patch.cc" # from [209e60efcca30474071383769840ae18d3d1aeef] # to [9d53bf580dd2a304115ff7d74497dda7d4809bc6] # # patch "diff_patch.hh" # from [8045a7d688a9494585eab247815ef9573687039f] # to [18be28ae5d744c23da230988bd4a48f6556cb4b4] # # patch "file_io.cc" # from [35b0d27bb9a0a1c72b651fd2081a0f9a6d6c58f1] # to [6125dbcc50e69e63ba8a22664e3e8126ee1115a0] # # patch "file_io.hh" # from [d2865f7695f667b872b858b276edcec5d7cf1605] # to [80ef83f6cd2479dd5dbcfd13bca81172d90fde4d] # # patch "lua.cc" # from [e2e91c14e07acc669a5ffb10a9983c5403997cfc] # to [eb0c2968f62deddb67d810579c702d77a627e249] # # patch "lua.hh" # from [0d70b46599b54eab35528c97bb129b552d6d0f9c] # to [7ba75d6e3987a0d0ab95d23a65199a2dd2ac68bf] # # patch "std_hooks.lua" # from [05e5e9b2b7f37107a7f38161d258e582eb13d697] # to [99e7d9894b49be84c01863dcacb914adddb9d040] # # patch "tests/t_merge_manual.at" # from [] # to [30eeeb2ea878e180248e8969dda05c07cd9df743] # # patch "testsuite.at" # from [f9801da558a13e2d2ab4fa73d1ad6acefe1eeb86] # to [5a8ba7470bf4a3d8c1b2085f2f195c063232d7e1] # # patch "work.cc" # from [b165e29831c433d9866bb5d3e03ceccdefd6be39] # to [a62b401c81e47925f2911689aa985d652eb624ae] # # patch "work.hh" # from [70d8503b81c8ab4d49481cd740f49fa7e4375498] # to [b632c7b9bda8e2df8b392b3606d554398e6ddb97] # --- diff_patch.cc +++ diff_patch.cc @@ -21,18 +21,6 @@ using namespace std; -bool guess_binary(string const & s) -{ - // these do not occur in ASCII text files - // FIXME: this heuristic is (a) crap and (b) hardcoded. fix both these. - if (s.find_first_of('\x00') != string::npos || - s.find_first_of("\x01\x02\x03\x04\x05\x06\x0e\x0f" - "\x10\x11\x12\x13\x14\x15\x16\x17\x18" - "\x19\x1a\x1c\x1d\x1e\x1f") != string::npos) - return true; - return false; -} - // // a 3-way merge works like this: // @@ -510,6 +498,18 @@ return default_encoding; } +bool merge_provider::attribute_manual_merge(file_path const & path, + manifest_map const & man) +{ + std::string mmf; + if (get_attribute_from_db(path, manual_merge_attribute, man, mmf, app)) + { + return mmf == std::string("true"); + } + else + return false; // default: enable auto merge +} + bool merge_provider::try_to_merge_files(file_path const & anc_path, file_path const & left_path, file_path const & right_path, @@ -537,45 +537,51 @@ file_data left_data, right_data, ancestor_data; data left_unpacked, ancestor_unpacked, right_unpacked, merged_unpacked; - string left_encoding, anc_encoding, right_encoding; - vector left_lines, ancestor_lines, right_lines, merged_lines; this->get_version(left_path, left_id, left_data); this->get_version(anc_path, ancestor_id, ancestor_data); this->get_version(right_path, right_id, right_data); - left_encoding = this->get_file_encoding(left_path, left_man); - anc_encoding = this->get_file_encoding(anc_path, anc_man); - right_encoding = this->get_file_encoding(right_path, right_man); - left_unpacked = left_data.inner(); ancestor_unpacked = ancestor_data.inner(); right_unpacked = right_data.inner(); - split_into_lines(left_unpacked(), left_encoding, left_lines); - split_into_lines(ancestor_unpacked(), anc_encoding, ancestor_lines); - split_into_lines(right_unpacked(), right_encoding, right_lines); - - if (merge3(ancestor_lines, - left_lines, - right_lines, - merged_lines)) + if (!attribute_manual_merge(left_path, left_man) && + !attribute_manual_merge(right_path, right_man) && + !attribute_manual_merge(anc_path, anc_man)) { - hexenc tmp_id; - file_data merge_data; - string tmp; - - L(F("internal 3-way merged ok\n")); - join_lines(merged_lines, tmp); - calculate_ident(data(tmp), tmp_id); - file_id merged_fid(tmp_id); - merge_data = file_data(tmp); - - merged_id = merged_fid; - record_merge(left_id, right_id, merged_fid, - left_data, merge_data); - - return true; + // all files mergeable by monotone internal algorithm, try to merge + string left_encoding, anc_encoding, right_encoding; + left_encoding = this->get_file_encoding(left_path, left_man); + anc_encoding = this->get_file_encoding(anc_path, anc_man); + right_encoding = this->get_file_encoding(right_path, right_man); + + vector left_lines, ancestor_lines, right_lines, merged_lines; + split_into_lines(left_unpacked(), left_encoding, left_lines); + split_into_lines(ancestor_unpacked(), anc_encoding, ancestor_lines); + split_into_lines(right_unpacked(), right_encoding, right_lines); + + if (merge3(ancestor_lines, + left_lines, + right_lines, + merged_lines)) + { + hexenc tmp_id; + file_data merge_data; + string tmp; + + L(F("internal 3-way merged ok\n")); + join_lines(merged_lines, tmp); + calculate_ident(data(tmp), tmp_id); + file_id merged_fid(tmp_id); + merge_data = file_data(tmp); + + merged_id = merged_fid; + record_merge(left_id, right_id, merged_fid, + left_data, merge_data); + + return true; + } } P(F("help required for 3-way merge\n")); @@ -715,8 +721,18 @@ return default_encoding; } +bool update_merge_provider::attribute_manual_merge(file_path const & path, + manifest_map const & man) +{ + std::string mmf; + if (get_attribute_from_working_copy(path, manual_merge_attribute, mmf)) + return mmf == std::string("true"); + else if (get_attribute_from_db(path, manual_merge_attribute, man, mmf, app)) + return mmf == std::string("true"); + else + return false; // default: enable auto merge +} - // the remaining part of this file just handles printing out various // diff formats for the case where someone wants to *read* a diff // rather than apply it. @@ -845,7 +861,7 @@ if (b_len == 0) ost << " +0,0"; else - { + { ost << " +" << b_begin+1; if (b_len > 1) ost << "," << b_len; --- diff_patch.hh +++ diff_patch.hh @@ -19,7 +19,6 @@ // this file is to contain some stripped down, in-process implementations // of GNU-diffutils-like things (diff, diff3, maybe patch..) -bool guess_binary(std::string const & s); enum diff_type { @@ -81,6 +80,9 @@ virtual std::string get_file_encoding(file_path const & path, manifest_map const & man); + virtual bool attribute_manual_merge(file_path const & path, + manifest_map const & man); + virtual ~merge_provider() {} }; @@ -105,6 +107,9 @@ virtual std::string get_file_encoding(file_path const & path, manifest_map const & man); + virtual bool attribute_manual_merge(file_path const & path, + manifest_map const & man); + virtual ~update_merge_provider() {} }; --- file_io.cc +++ file_io.cc @@ -254,6 +254,18 @@ return fs::exists(localized(p)); } +bool guess_binary(string const & s) +{ + // these do not occur in ASCII text files + // FIXME: this heuristic is (a) crap and (b) hardcoded. fix both these. + if (s.find_first_of('\x00') != string::npos || + s.find_first_of("\x01\x02\x03\x04\x05\x06\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18" + "\x19\x1a\x1c\x1d\x1e\x1f") != string::npos) + return true; + return false; +} + void delete_file(local_path const & p) { --- file_io.hh +++ file_io.hh @@ -59,6 +59,9 @@ bool file_exists(local_path const & path); bool file_exists(file_path const & path); +// returns true if the string content is binary according to monotone euristic +bool guess_binary(std::string const & s); + void mkdir_p(local_path const & path); void mkdir_p(file_path const & path); void make_dir_for(file_path const & p); --- lua.cc +++ lua.cc @@ -157,6 +157,15 @@ lua_pushnumber(L, process_sleep(seconds)); return 1; } + + static int + monotone_guess_binary_for_lua(lua_State *L) + { + const char *path = lua_tostring(L, -1); + N(path, F("guess_binary called with an invalid parameter")); + lua_pushboolean(L, guess_binary(std::string(path, lua_strlen(L, -1)))); + return 1; + } } @@ -184,6 +193,7 @@ lua_register(st, "wait", monotone_wait_for_lua); lua_register(st, "kill", monotone_kill_for_lua); lua_register(st, "sleep", monotone_sleep_for_lua); + lua_register(st, "guess_binary", monotone_guess_binary_for_lua); } lua_hooks::~lua_hooks() @@ -745,7 +755,22 @@ return exec_ok && ignore_it; } +// returns false if the hook returned nil or something not boolean +// otherwise returns true and is_binary contains the return value from the hook bool +lua_hooks::hook_binary_file(file_path const & p, bool &is_binary) +{ + is_binary = false; + bool exec_ok = Lua(st) + .func("binary_file") + .push_str(p()) + .call(1,1) + .extract_bool(is_binary) + .ok(); + return exec_ok; +} + +bool lua_hooks::hook_non_blocking_rng_ok() { bool ok = false; --- lua.hh +++ lua.hh @@ -71,6 +71,13 @@ // local repo hooks bool hook_ignore_file(file_path const & p); bool hook_ignore_branch(std::string const & branch); + + // if the file type is known, the return value is true and the is_binary + // parameter will contain true if the file is binary, false if is text. + // if the hook value is false, then the file type is not known and must + // be resolved otherwise + bool hook_binary_file(file_path const & p, bool &is_binary); + bool hook_merge2(file_path const & left_path, file_path const & right_path, file_path const & merged_path, --- std_hooks.lua +++ std_hooks.lua @@ -25,7 +25,7 @@ -- bit, ACLs, various special flags) which we want to have set and -- re-set any time the files are modified. the attributes themselves -- are stored in a file .mt-attrs, in the working copy (and --- manifest). each (f,k,v) triple in an atribute file turns into a +-- manifest). each (f,k,v) triple in an attribute file turns into a -- call to attr_functions[k](f,v) in lua. if (attr_init_functions == nil) then @@ -41,11 +41,19 @@ end end +attr_init_functions["manual_merge"] = + function(filename) + if (binary_file(filename)) then + return "true" -- binary files must merged manually + else + return nil + end + end + if (attr_functions == nil) then attr_functions = {} end - attr_functions["execute"] = function(filename, value) if (value == "true") then @@ -91,6 +99,31 @@ return false; end +-- return true means "binary", false means "text", +-- nil means "unknown, try to guess" +function binary_file(name) + lowname=string.lower(name) + -- some known binaries, return true + if (string.find(lowname, "%.gif$")) then return true end + if (string.find(lowname, "%.jpe?g$")) then return true end + if (string.find(lowname, "%.png$")) then return true end + if (string.find(lowname, "%.bz2$")) then return true end + if (string.find(lowname, "%.gz$")) then return true end + if (string.find(lowname, "%.zip$")) then return true end + -- some known text, return false + if (string.find(lowname, "%.cc?$")) then return false end + if (string.find(lowname, "%.cxx$")) then return false end + if (string.find(lowname, "%.hh?$")) then return false end + if (string.find(lowname, "%.hxx$")) then return false end + if (string.find(lowname, "%.lua$")) then return false end + if (string.find(lowname, "%.texi$")) then return false end + if (string.find(lowname, "%.sql$")) then return false end + -- unknown - read file and use the guess-binary hook + filedata=read_contents_of_file(name) + if (filedata ~= nil) then return guess_binary(filedata) end + -- if still unknown, treat as binary + return true +end function edit_comment(basetext, user_log_message) local exe = "vi" @@ -282,7 +315,7 @@ end function read_contents_of_file(filename) - tmp = io.open(filename, "r") + tmp = io.open(filename, "rb") -- read the file as-is if (tmp == nil) then return nil end --- tests/t_merge_manual.at +++ tests/t_merge_manual.at @@ -0,0 +1,308 @@ +AT_SETUP([merge manual file]) +MONOTONE_SETUP + +NEED_UNB64 + +# This was a real merge error. A binary file happily merged by monotone +# just because contains some strategically placed line feeds +# now is a test for the new attribute merge_manual and its effect on merging + +AT_DATA(parent.bmp.b64, [Qk1mdQAAAAAAADYAAAAoAAAAZAAAAGQAAAABABgAAAAAADB1AADrCgAA6woAAAAAAAAAAAAAQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQApC +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQApC2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsyChsy2xsy2xsy2xsy2xsy2xsy2xsy2xsyGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxsKCgoK +]) + +AT_DATA(left.bmp.b64, [Qk1mdQAAAAAAADYAAAAoAAAAZAAAAGQAAAABABgAAAAAADB1AADrCgAA6woAAAAAAAAAAAAAQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9C +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQApC +QJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQJ9CQApC2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsyChsy2xsy2xsy2xsy2xsy2xsy2xsy2xsypzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3pzq3 +pzq3pzq3pzq3GxsKCgoK +]) + +AT_DATA(right.bmp.b64, [Qk1mdQAAAAAAADYAAAAoAAAAZAAAAGQAAAABABgAAAAAADB1AADrCgAA6woAAAAAAAAAAAAAOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrt +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtQApC +OtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtOtrtQApC2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy2xsy +2xsy2xsy2xsy2xsy2xsy2xsyChsy2xsy2xsy2xsy2xsy2xsy2xsy2xsyGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvbGxvb +GxvbGxvbGxvbGxsKCgoK +]) + +UNB64(parent.bmp.b64, parent.bmp) +UNB64(left.bmp.b64, left.bmp) +UNB64(right.bmp.b64, right.bmp) + +# hook forces all files binary +AT_DATA(binary.lua, [if (attr_init_functions == nil) then attr_init_functions = {} end +attr_init_functions[["manual_merge"]] = function(filename) return "true" end +]) + +# hook forces all files text +AT_DATA(text.lua, [if (attr_init_functions == nil) then attr_init_functions = {} end +attr_init_functions[["manual_merge"]] = function(filename) return "false" end +]) + +# --- first: auto add as binary +AT_CHECK(cp -f parent.bmp binary.bmp) +AT_CHECK(MONOTONE --rcfile=binary.lua add binary.bmp, [], [ignore], [ignore]) +COMMIT(binbranch) +PARENT_SHA=`BASE_REVISION` + +AT_CHECK(MONOTONE attr get binary.bmp manual_merge, [], [ignore], [ignore]) + +AT_CHECK(cp -f left.bmp binary.bmp) +COMMIT(binbranch) + +REVERT_TO($PARENT_SHA) + +AT_CHECK(cp -f right.bmp binary.bmp) +COMMIT(binbranch) + +# file marked binary: merge should fail +AT_CHECK(MONOTONE --branch=binbranch merge, [1], [ignore], [ignore]) + +# --- second: auto add as text +AT_CHECK(cp -f parent.bmp text.bmp) +AT_CHECK(MONOTONE --rcfile=text.lua add text.bmp, [], [ignore], [ignore]) +COMMIT(textbranch) +PARENT_SHA=`BASE_REVISION` + +AT_CHECK(cp -f left.bmp text.bmp) +COMMIT(textbranch) + +REVERT_TO($PARENT_SHA) + +AT_CHECK(cp -f right.bmp text.bmp) +COMMIT(textbranch) + +# file marked text: merge should work! +AT_CHECK(MONOTONE --branch=textbranch merge, [0], [ignore], [ignore]) + +# --- third: manually make filename as binary +AT_CHECK(cp -f parent.bmp forcebin.bmp) +AT_CHECK(MONOTONE --rcfile=text.lua add forcebin.bmp, [], [ignore], [ignore]) +COMMIT(forcebinbranch) +PARENT_SHA=`BASE_REVISION` + +AT_CHECK(cp -f left.bmp forcebin.bmp) +COMMIT(forcebinbranch) + +REVERT_TO($PARENT_SHA) + +AT_CHECK(cp -f right.bmp forcebin.bmp) + +# set bin +AT_CHECK(MONOTONE attr set forcebin.bmp manual_merge true, [], [ignore], [ignore]) +COMMIT(forcebinbranch) + +# file marked binary: merge should fail +AT_CHECK(MONOTONE --branch=forcebinbranch merge, [1], [ignore], [ignore]) + +# --- fourth: automatically make filename as binary, then force text +AT_CHECK(cp -f parent.bmp forcetext.bmp) +AT_CHECK(MONOTONE --rcfile=binary.lua add forcetext.bmp, [], [ignore], [ignore]) +AT_CHECK(MONOTONE attr set forcetext.bmp manual_merge false, [], [ignore], [ignore]) +COMMIT(forcetextbranch) +PARENT_SHA=`BASE_REVISION` + +AT_CHECK(cp -f left.bmp forcetext.bmp) +COMMIT(forcetextbranch) + +REVERT_TO($PARENT_SHA) + +AT_CHECK(cp -f right.bmp forcetext.bmp) +COMMIT(forcetextbranch) + +# file marked text: merge should work +AT_CHECK(MONOTONE --branch=forcetextbranch merge, [], [ignore], [ignore]) + +AT_CLEANUP --- testsuite.at +++ testsuite.at @@ -650,5 +650,5 @@ m4_include(tests/t_annotate_split_line.at) m4_include(tests/t_automate_certs.at) m4_include(tests/t_selector_later_earlier.at) -m4_include(tests/t_merge_binary.at) m4_include(tests/t_automate_stdio.at) +m4_include(tests/t_merge_manual.at) --- work.cc +++ work.cc @@ -674,6 +674,8 @@ string const binary_encoding("binary"); string const default_encoding("default"); +string const manual_merge_attribute("manual_merge"); + static bool find_in_attr_map(attr_map const & attr, file_path const & file, std::string const & attr_key, --- work.hh +++ work.hh @@ -165,6 +165,7 @@ attr_map const & options); extern std::string const encoding_attribute; +extern std::string const manual_merge_attribute; bool get_attribute_from_db(file_path const & file, std::string const & attr_key,