# # delete_file "tests/t_merge_binary.at" # # add_file "tests/t_merge_manual.at" # # patch "ChangeLog" # from [0c7ce88d4c09b80dab45c122aad3aa8bd0ec670c] # to [eff9a1b084062e5c3330260c8b501e8f5952381f] # # patch "diff_patch.cc" # from [209e60efcca30474071383769840ae18d3d1aeef] # to [cb8bea5dd5b9e5b9c4eb838dd89bb85200c434aa] # # 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 [107b2deb3efdabe34542d967503b2250055b4d23] # to [c5a01c71aef633d6ad5ad78650e4e0601295b349] # # patch "monotone.texi" # from [e4a42059771b12538e575453621b24cc20ddf2ad] # to [b1147c864ad611f55aec986899580628fc4ceb16] # # patch "std_hooks.lua" # from [572befdb727cd626e7a8efd27d5a8109bdfebd27] # to [ed37f9501c86a0a007be67f0dc381e92172af818] # # patch "tests/t_merge_manual.at" # from [] # to [30eeeb2ea878e180248e8969dda05c07cd9df743] # # patch "testsuite.at" # from [4a1a612444d0916a4fcbe71053f66ba1d7aaa18e] # to [870e8ba2df0bf039b75504971f0b4ba11c1cc859] # # patch "work.cc" # from [b165e29831c433d9866bb5d3e03ceccdefd6be39] # to [a62b401c81e47925f2911689aa985d652eb624ae] # # patch "work.hh" # from [70d8503b81c8ab4d49481cd740f49fa7e4375498] # to [b632c7b9bda8e2df8b392b3606d554398e6ddb97] # --- ChangeLog +++ ChangeLog @@ -1,3 +1,15 @@ +2005-06-09 Riccardo Ghetta + + * diff_patch.cc/hh: honor the new manual_merge attribute + * file_io.cc/hh: move here the guess_binary function + * lua.cc: let guess_binary available to lua + * std_hooks.lua: handle manual_merge as an add-time attribute and + initialize by default make it true if the file appears to be binary. + Make read_contents_of_file able to read "binary" files. + * tests/t_merge_manual.at: tests new behaviour, superceding the + old XFAIL t_merge_binary.at test. + * monotone.texi: document changes, adding a small section on merging. + 2005-06-07 Nathaniel Smith * ChangeLog: Fixup. --- 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,53 @@ 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 (!attribute_manual_merge(left_path, left_man) && + !attribute_manual_merge(right_path, right_man)) + { + // both files mergeable by monotone internal algorithm, try to merge + // note: the ancestor is not considered for manual merging. Forcing the + // user to merge manually just because of an ancestor mistakenly marked + // manual seems too harsh + 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); - 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; + 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 +723,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 +863,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() --- monotone.texi +++ monotone.texi @@ -2229,6 +2229,7 @@ * Reserved Certs:: Certificate names with special meanings. * Naming Conventions:: Choosing appropriate names for keys and branches. * File Attributes:: Marking files as executable, or other attributes. +* Merging:: Merging with external tools, handling binary files. * Migrating and Dumping:: Changing the underlying storage system. * Importing from CVS:: Building a monotone database from a CVS repository. @end menu @@ -2898,7 +2899,48 @@ other people make, you will have to resolve those conflicts, as plain text, just as with any other text file in your working copy. address@hidden address@hidden Merging address@hidden Merging +Monotone has two merging modes, controlled by the @code{manual_merge} +attribute. +By default all files are merged in automatic mode, unless the address@hidden attribute for that file is present and address@hidden +In automatic mode files are merged without user intervention, using +monotone internal three-way merging algorithm. +Only if there are conflicts or an ancestor is not available monotone +switches to manual mode, essentially escalating the merging to the user. +When working in manual mode, monotone invokes the merge2 (for two-way +merging) or merge3 (three-way) hooks to start an user defined external +merge tool. +If the tool terminates without writing the merged file, monotone aborts the +merging, reverting any changes made. +By redefining the aforementioned hooks the user can not only choose a +preferred merge tool, but even select different programs for different +file types. For example, gimp for .png files, OpenOffice.org for +.doc, and so on. +Starting with monotone 0.20, the @code{manual_merge} attribute is +automatically set at add time for all ``binary'' files, i.e. all files +for wich the @code{binary_file} hook returns true. +Currently, this means all files with extension gif, jpeg, png, bz2, gz +and zip, plus files containing at least one of the following +bytes: + address@hidden address@hidden +0x00 thru 0x06 +0x0E thru 0x1a +0x1c thru 0x1f address@hidden group address@hidden smallexample + +The attribute could also be manually forced or removed using the +apposite monotone commands. +Remember that monotone switches to manual merging even if only one of +the files to be merged has the @code{manual_merge} attribute set. + @page @node Migrating and Dumping @section Migrating and Dumping @@ -6008,8 +6050,8 @@ stored in @file{.mt-attrs} for the given @var{filename}. This table of hook functions is called once for each file during an @dfn{add}. -By default, there is only one entry in this table, for the @code{execute} -attribute. Its definition is: +By default, there are only two entries in this table, for the address@hidden and @code{manual_merge} attributes. Their definition is: @smallexample @group @@ -6021,9 +6063,50 @@ return nil 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 @end group @end smallexample +The @code{binary_file} function is also defined as a lua hook as +follows: + address@hidden address@hidden +function binary_file(name) + local 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 built-in + -- monotone function + filedata=read_contents_of_file(name) + if (filedata ~= nil) then return guess_binary(filedata) end + -- if still unknown, treat as binary + return true +end address@hidden group address@hidden smallexample + + @end ftable @node Special Topics --- 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,32 @@ return false; end +-- return true means "binary", false means "text", +-- nil means "unknown, try to guess" +function binary_file(name) + local 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 + -- monotone built-in function + filedata=read_contents_of_file(name, "rb") + 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" @@ -281,8 +315,8 @@ return filename end -function read_contents_of_file(filename) - tmp = io.open(filename, "r") +function read_contents_of_file(filename, mode) + tmp = io.open(filename, mode) if (tmp == nil) then return nil end @@ -362,9 +396,9 @@ cmd () if tbl.meld_exists then - ret = read_contents_of_file (tbl.lfile) + ret = read_contents_of_file (tbl.lfile, "r") else - ret = read_contents_of_file (tbl.outfile) + ret = read_contents_of_file (tbl.outfile, "r") end if string.len (ret) == 0 then @@ -453,9 +487,9 @@ cmd () if tbl.meld_exists then - ret = read_contents_of_file (tbl.afile) + ret = read_contents_of_file (tbl.afile, "r") else - ret = read_contents_of_file (tbl.outfile) + ret = read_contents_of_file (tbl.outfile, "r") end if string.len (ret) == 0 then --- 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,10 +650,10 @@ 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_cvsimport_drepper.at) m4_include(tests/t_update_with_pending_drop.at) m4_include(tests/t_update_with_pending_add.at) m4_include(tests/t_update_with_pending_rename.at) m4_include(tests/t_restricted_commit_with_inodeprints.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,