# # # patch "ChangeLog" # from [dccb6e1883e29ab316de6bbd602c762b229d3545] # to [9710d177621d9a9d07ee431fb2d4ddf3c2e0ddbe] # # patch "automate.cc" # from [18458490c7f2fd24860839b4a474e4e2197f56dd] # to [fdadb677f9ae73cd6fccfe9faa181fdf731779c6] # # patch "cmd.hh" # from [7142b69f986d070ea15a283a19fa16e4fb9edd59] # to [288b76d82d3f84e98ac280e660c03018ad9da22e] # # patch "cmd_diff_log.cc" # from [9bf839e131811070ee4518463761205937bf6e6f] # to [eda0cb4e16eccb3f65d97e59f7a478b1d3c75763] # # patch "commands.cc" # from [c9e6fdb67f72a37dc491317be70923b5c701748a] # to [0b458fd786d390b71125302a999cd2ad075f7f0d] # ============================================================ --- ChangeLog dccb6e1883e29ab316de6bbd602c762b229d3545 +++ ChangeLog 9710d177621d9a9d07ee431fb2d4ddf3c2e0ddbe @@ -1,3 +1,15 @@ +2006-10-19 Thomas Keller + + * automate.cc: added content_diff command which makes the output of mtn diff + (minus the revision part) available via mtn automate + * automate.cc: fixed "introduced in" of get_content_changed and + get_corresponding_path (this was in 3.1, not 3.2) + * cmd_automate.cc: bumped interface version to 3.2 + * cmd_diff_log.cc: moved dump_diff and do_external_diff ... + * commands.*: ...here and added an additional ostream parameter to + dump_diff to be able to use this with automate stdio + * TODO: docs and tests + 2006-10-15 Timothy Brownawell Propagate the rerwite of 'automate stdio' (in branch ============================================================ --- automate.cc 18458490c7f2fd24860839b4a474e4e2197f56dd +++ automate.cc fdadb677f9ae73cd6fccfe9faa181fdf731779c6 @@ -1499,7 +1499,7 @@ AUTOMATE(get_option, N_("OPTION")) // Arguments: // 1: a revision ID // 2: a file name -// Added in: 3.2 +// Added in: 3.1 // Purpose: Returns a list of revision IDs in which the content // was most recently changed, relative to the revision ID specified // in argument 1. This equates to a content mark following @@ -1556,7 +1556,7 @@ AUTOMATE(get_content_changed, N_("REV FI // 1: a source revision ID // 2: a file name (in the source revision) // 3: a target revision ID -// Added in: 3.2 +// Added in: 3.1 // Purpose: Given a the file name in the source revision, a filename // will if possible be returned naming the file in the target revision. // This allows the same file to be matched between revisions, accounting @@ -1612,6 +1612,154 @@ AUTOMATE(get_corresponding_path, N_("REV output.write(prt.buf.data(), prt.buf.size()); } +// Name: content_diff +// Arguments: +// 1: (optional) one or more files to include +// 2: (optional) if given, left rev is workspace and right is this rev +// 3: (optional) if given, 2) is left rev and this is the right rev +// Added in: 3.2 +// Purpose: Availability of mtn diff as automate command. +// +// Output format: Like mtn diff, but with the header part omitted (as this is +// doubles the output of automate get_revision). If no content changes happened, +// the output is empty. All file operations beside mtn add are omitted, +// as they don't change the content of the file. +AUTOMATE(content_diff, N_("[FILE [...] [REV1 [REV2]]]")) +{ + vector rev_ids; + vector other_args; + revision_id ident; + + // check for revision and file arguments, go from right to left + for (int i=args.size()-1; i>=0; i--) + { + ident = revision_id(); + if (app.db.revision_exists(ident)) + { + rev_ids.push_back(ident); + continue; + } + + // then maybe this is a path...? + other_args.push_back(utf8(idx(args, i)())); + } + + int rev_count = rev_ids.size(); + + // check if we got more than the expected two revids + N(rev_count <= 2, + F("expected zero, one or two REVISION arguments")); + + // if we didn't receive two revision ids, we require a workspace against + // we can diff + if (rev_count < 2) + app.require_workspace(); + + temp_node_id_source nis; + cset included, excluded; + bool new_is_archived; + + // + // What follows is pretty much copied from cmd_diff_log.cc line 202ff + // with some slight adaptions noted with separate FIXMEs + // TODO: someone with a better overview should probably factor this + // out somewhere + // + if (rev_count == 0) + { + roster_t new_roster, old_roster; + revision_id old_rid; + + app.work.get_base_and_current_roster_shape(old_roster, new_roster, nis); + app.work.get_revision_id(old_rid); + + //node_restriction mask(args_to_paths(args), + node_restriction mask(args_to_paths(other_args), + args_to_paths(app.exclude_patterns), + app.depth, + old_roster, new_roster, app); + + app.work.update_current_roster_from_filesystem(new_roster, mask); + make_restricted_csets(old_roster, new_roster, + included, excluded, mask); + check_restricted_cset(old_roster, included); + + new_is_archived = false; + // FIXME: not needed here, automate get_revision does the same + //header << "# old_revision [" << old_rid << "]" << "\n"; + } + else if (rev_count == 1) + { + roster_t new_roster, old_roster; + revision_id r_old_id; + + // FIXME: not needed here, since we require complete revids and + // already have checked their existence + //complete(app, idx(app.revision_selectors, 0)(), r_old_id); + //N(app.db.revision_exists(r_old_id), + // F("no such revision '%s'") % r_old_id); + + app.work.get_base_and_current_roster_shape(old_roster, new_roster, nis); + // Clobber old_roster with the one specified + app.db.get_roster(r_old_id, old_roster); + + // FIXME: handle no ancestor case + // N(r_new.edges.size() == 1, F("current revision has no ancestor")); + + //node_restriction mask(args_to_paths(args), + node_restriction mask(args_to_paths(other_args), + args_to_paths(app.exclude_patterns), + app.depth, + old_roster, new_roster, app); + + app.work.update_current_roster_from_filesystem(new_roster, mask); + make_restricted_csets(old_roster, new_roster, + included, excluded, mask); + check_restricted_cset(old_roster, included); + + new_is_archived = false; + // FIXME: not needed here, automate get_revision does the same + //header << "# old_revision [" << r_old_id << "]" << "\n"; + } + else + { + roster_t new_roster, old_roster; + revision_id r_old_id, r_new_id; + + // FIXME: not needed here, since we require complete revids and + // already have checked their existence + //complete(app, idx(app.revision_selectors, 0)(), r_old_id); + //complete(app, idx(app.revision_selectors, 1)(), r_new_id); + //N(app.db.revision_exists(r_old_id), + // F("no such revision '%s'") % r_old_id); + //N(app.db.revision_exists(r_new_id), + // F("no such revision '%s'") % r_new_id); + + app.db.get_roster(r_old_id, old_roster); + app.db.get_roster(r_new_id, new_roster); + + node_restriction mask(args_to_paths(args), + args_to_paths(app.exclude_patterns), + app.depth, + old_roster, new_roster, app); + + // FIXME: this is *possibly* a UI bug, insofar as we [...] + // (see cmd_diff_log.cc for the full comment) + + make_restricted_csets(old_roster, new_roster, + included, excluded, mask); + check_restricted_cset(old_roster, included); + + new_is_archived = true; + } + + // + // copy from cmd_diff_log.cc end + // + + dump_diffs(included, app, new_is_archived, output); +} + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- cmd.hh 7142b69f986d070ea15a283a19fa16e4fb9edd59 +++ cmd.hh 288b76d82d3f84e98ac280e660c03018ad9da22e @@ -87,6 +87,25 @@ void revision_id const & id); void +dump_diffs(cset const & cs, + app_state & app, + bool new_is_archived, + std::ostream & output); + +void +dump_diffs(cset const & cs, + app_state & app, + bool new_is_archived, + std::ostream & output, + std::set const & paths, + bool limit_paths=false); + +void +do_external_diff(cset const & cs, + app_state & app, + bool new_is_archived); + +void complete(app_state & app, std::string const & str, revision_id & completion, ============================================================ --- cmd_diff_log.cc 9bf839e131811070ee4518463761205937bf6e6f +++ cmd_diff_log.cc eda0cb4e16eccb3f65d97e59f7a478b1d3c75763 @@ -14,7 +14,6 @@ #include "cmd.hh" #include "diff_patch.hh" -#include "localized_file_io.hh" #include "restrictions.hh" #include "revision.hh" #include "simplestring_xform.hh" @@ -172,162 +171,6 @@ changes_summary::print(ostream & os, siz } } -static void -do_external_diff(cset const & cs, - app_state & app, - bool new_is_archived) -{ - for (map >::const_iterator - i = cs.deltas_applied.begin(); - i != cs.deltas_applied.end(); ++i) - { - data data_old; - data data_new; - - file_data f_old; - app.db.get_file_version(delta_entry_src(i), f_old); - data_old = f_old.inner(); - - if (new_is_archived) - { - file_data f_new; - app.db.get_file_version(delta_entry_dst(i), f_new); - data_new = f_new.inner(); - } - else - { - read_localized_data(file_path(delta_entry_path(i)), - data_new, app.lua); - } - - bool is_binary = false; - if (guess_binary(data_old()) || - guess_binary(data_new())) - is_binary = true; - - app.lua.hook_external_diff(file_path(delta_entry_path(i)), - data_old, - data_new, - is_binary, - app.diff_args_provided, - app.diff_args(), - delta_entry_src(i).inner()(), - delta_entry_dst(i).inner()()); - } -} - -static void -dump_diffs(cset const & cs, - app_state & app, - bool new_is_archived, - set const & paths, - bool limit_paths = false) -{ - // 60 is somewhat arbitrary, but less than 80 - string patch_sep = string(60, '='); - - for (map::const_iterator - i = cs.files_added.begin(); - i != cs.files_added.end(); ++i) - { - if (limit_paths && paths.find(i->first) == paths.end()) - continue; - - cout << patch_sep << "\n"; - data unpacked; - vector lines; - - if (new_is_archived) - { - file_data dat; - app.db.get_file_version(i->second, dat); - unpacked = dat.inner(); - } - else - { - read_localized_data(file_path(i->first), - unpacked, app.lua); - } - - std::string pattern(""); - if (app.diff_show_encloser) - app.lua.hook_get_encloser_pattern(file_path(i->first), - pattern); - - make_diff(file_path(i->first).as_internal(), - file_path(i->first).as_internal(), - i->second, - i->second, - data(), unpacked, - cout, app.diff_format, pattern); - } - - map reverse_rename_map; - - for (map::const_iterator - i = cs.nodes_renamed.begin(); - i != cs.nodes_renamed.end(); ++i) - { - reverse_rename_map.insert(make_pair(i->second, i->first)); - } - - for (map >::const_iterator - i = cs.deltas_applied.begin(); - i != cs.deltas_applied.end(); ++i) - { - if (limit_paths && paths.find(i->first) == paths.end()) - continue; - - file_data f_old; - data data_old, data_new; - - cout << patch_sep << "\n"; - - app.db.get_file_version(delta_entry_src(i), f_old); - data_old = f_old.inner(); - - if (new_is_archived) - { - file_data f_new; - app.db.get_file_version(delta_entry_dst(i), f_new); - data_new = f_new.inner(); - } - else - { - read_localized_data(file_path(delta_entry_path(i)), - data_new, app.lua); - } - - split_path dst_path = delta_entry_path(i); - split_path src_path = dst_path; - map::const_iterator re; - re = reverse_rename_map.find(dst_path); - if (re != reverse_rename_map.end()) - src_path = re->second; - - std::string pattern(""); - if (app.diff_show_encloser) - app.lua.hook_get_encloser_pattern(file_path(src_path), - pattern); - - make_diff(file_path(src_path).as_internal(), - file_path(dst_path).as_internal(), - delta_entry_src(i), - delta_entry_dst(i), - data_old, data_new, - cout, app.diff_format, pattern); - } -} - -static void -dump_diffs(cset const & cs, - app_state & app, - bool new_is_archived) -{ - set dummy; - dump_diffs(cs, app, new_is_archived, dummy); -} - CMD(diff, N_("informative"), N_("[PATH]..."), N_("show current diffs on stdout.\n" "If one revision is given, the diff between the workspace and\n" @@ -481,7 +324,7 @@ CMD(diff, N_("informative"), N_("[PATH]. if (app.diff_format == external_diff) { do_external_diff(included, app, new_is_archived); } else - dump_diffs(included, app, new_is_archived); + dump_diffs(included, app, new_is_archived, cout); } static void @@ -728,8 +571,8 @@ CMD(log, N_("informative"), N_("[FILE] . for (edge_map::const_iterator e = rev.edges.begin(); e != rev.edges.end(); ++e) { - dump_diffs(edge_changes(e), app, true, diff_paths, - !mask.empty()); + dump_diffs(edge_changes(e), app, true, cout, + diff_paths, !mask.empty()); } } ============================================================ --- commands.cc c9e6fdb67f72a37dc491317be70923b5c701748a +++ commands.cc 0b458fd786d390b71125302a999cd2ad075f7f0d @@ -12,7 +12,9 @@ #include "transforms.hh" #include "simplestring_xform.hh" +#include "localized_file_io.hh" #include "charset.hh" +#include "diff_patch.hh" #include "inodeprint.hh" #include "cert.hh" #include "ui.hh" @@ -24,6 +26,8 @@ using std::cin; #endif using std::cin; +using std::map; +using std::ostream; using std::pair; using std::set; using std::string; @@ -474,6 +478,165 @@ process_commit_message_args(bool & given given = false; } +void +do_external_diff(cset const & cs, + app_state & app, + bool new_is_archived) +{ + for (map >::const_iterator + i = cs.deltas_applied.begin(); + i != cs.deltas_applied.end(); ++i) + { + data data_old; + data data_new; + + file_data f_old; + app.db.get_file_version(delta_entry_src(i), f_old); + data_old = f_old.inner(); + + if (new_is_archived) + { + file_data f_new; + app.db.get_file_version(delta_entry_dst(i), f_new); + data_new = f_new.inner(); + } + else + { + read_localized_data(file_path(delta_entry_path(i)), + data_new, app.lua); + } + + bool is_binary = false; + if (guess_binary(data_old()) || + guess_binary(data_new())) + is_binary = true; + + app.lua.hook_external_diff(file_path(delta_entry_path(i)), + data_old, + data_new, + is_binary, + app.diff_args_provided, + app.diff_args(), + delta_entry_src(i).inner()(), + delta_entry_dst(i).inner()()); + } +} + +void +dump_diffs(cset const & cs, + app_state & app, + bool new_is_archived, + ostream & output, + set const & paths, + bool limit_paths) +{ + // 60 is somewhat arbitrary, but less than 80 + string patch_sep = string(60, '='); + + for (map::const_iterator + i = cs.files_added.begin(); + i != cs.files_added.end(); ++i) + { + if (limit_paths && paths.find(i->first) == paths.end()) + continue; + + output << patch_sep << "\n"; + data unpacked; + vector lines; + + if (new_is_archived) + { + file_data dat; + app.db.get_file_version(i->second, dat); + unpacked = dat.inner(); + } + else + { + read_localized_data(file_path(i->first), + unpacked, app.lua); + } + + std::string pattern(""); + if (app.diff_show_encloser) + app.lua.hook_get_encloser_pattern(file_path(i->first), + pattern); + + make_diff(file_path(i->first).as_internal(), + file_path(i->first).as_internal(), + i->second, + i->second, + data(), unpacked, + output, app.diff_format, pattern); + } + + map reverse_rename_map; + + for (map::const_iterator + i = cs.nodes_renamed.begin(); + i != cs.nodes_renamed.end(); ++i) + { + reverse_rename_map.insert(make_pair(i->second, i->first)); + } + + for (map >::const_iterator + i = cs.deltas_applied.begin(); + i != cs.deltas_applied.end(); ++i) + { + if (limit_paths && paths.find(i->first) == paths.end()) + continue; + + file_data f_old; + data data_old, data_new; + + output << patch_sep << "\n"; + + app.db.get_file_version(delta_entry_src(i), f_old); + data_old = f_old.inner(); + + if (new_is_archived) + { + file_data f_new; + app.db.get_file_version(delta_entry_dst(i), f_new); + data_new = f_new.inner(); + } + else + { + read_localized_data(file_path(delta_entry_path(i)), + data_new, app.lua); + } + + split_path dst_path = delta_entry_path(i); + split_path src_path = dst_path; + map::const_iterator re; + re = reverse_rename_map.find(dst_path); + if (re != reverse_rename_map.end()) + src_path = re->second; + + std::string pattern(""); + if (app.diff_show_encloser) + app.lua.hook_get_encloser_pattern(file_path(src_path), + pattern); + + make_diff(file_path(src_path).as_internal(), + file_path(dst_path).as_internal(), + delta_entry_src(i), + delta_entry_dst(i), + data_old, data_new, + output, app.diff_format, pattern); + } +} + +void +dump_diffs(cset const & cs, + app_state & app, + bool new_is_archived, + ostream & output) +{ + set dummy; + dump_diffs(cs, app, new_is_archived, output, dummy); +} + + // Local Variables: // mode: C++ // fill-column: 76