# # # patch "cmd_diff_log.cc" # from [37b1cfffa001ccc5b813fe6f0e84dc6a5dc3996e] # to [76c360bdf6afbbed99b284eed52e121cbf4a81d1] # ============================================================ --- cmd_diff_log.cc 37b1cfffa001ccc5b813fe6f0e84dc6a5dc3996e +++ cmd_diff_log.cc 76c360bdf6afbbed99b284eed52e121cbf4a81d1 @@ -19,6 +19,7 @@ #include "cmd.hh" #include "diff_output.hh" #include "file_io.hh" +#include "parallel_iter.hh" #include "restrictions.hh" #include "revision.hh" #include "rev_height.hh" @@ -185,194 +186,209 @@ static void } static void -do_external_diff(options & opts, lua_hooks & lua, database & db, - cset const & cs, bool new_is_archived) +get_data(database & db, + file_path const & path, file_id const & id, + bool const from_db, data & unpacked) { - for (map >::const_iterator - i = cs.deltas_applied.begin(); - i != cs.deltas_applied.end(); ++i) + if (from_db) { - data data_old; - data data_new; + file_data dat; + db.get_file_version(id, dat); + unpacked = dat.inner(); + } + else + { + read_data(path, unpacked); + } +} - file_data f_old; - db.get_file_version(delta_entry_src(i), f_old); - data_old = f_old.inner(); - - if (new_is_archived) - { - file_data f_new; - db.get_file_version(delta_entry_dst(i), f_new); - data_new = f_new.inner(); - } - else - { - read_data(delta_entry_path(i), data_new); - } - +static void +dump_diff(lua_hooks & lua, + file_path const & left_path, file_path const & right_path, + file_id const left_id, file_id const right_id, + data const & left_data, data const & right_data, + diff_type const diff_format, + bool external_diff_args_given, + string external_diff_args, + string const & encloser, + ostream & output) +{ + if (diff_format == external_diff) + { bool is_binary = false; - if (guess_binary(data_old()) || - guess_binary(data_new())) + if (guess_binary(left_data()) || guess_binary(right_data())) is_binary = true; - lua.hook_external_diff(delta_entry_path(i), - data_old, - data_new, + file_path path = right_path; + if (path.empty()) // use the left path for deletes + path = left_path; + + lua.hook_external_diff(path, + left_data, + right_data, is_binary, - opts.external_diff_args_given, - opts.external_diff_args, - encode_hexenc(delta_entry_src(i).inner()(), - delta_entry_src(i).inner().made_from), - encode_hexenc(delta_entry_dst(i).inner()(), - delta_entry_dst(i).inner().made_from)); + external_diff_args_given, + external_diff_args, + encode_hexenc(left_id.inner()(), + left_id.inner().made_from), + encode_hexenc(right_id.inner()(), + right_id.inner().made_from)); } + else + { + // 60 is somewhat arbitrary, but less than 80 + string patch_sep = string(60, '='); + output << patch_sep << '\n'; + + // see the big comment in diff_output.cc about what paths should be + string left = left_path.as_internal(); + if (left.empty()) + left = "/dev/null"; + + string right = right_path.as_internal(); + if (right.empty()) + right = "/dev/null"; + + make_diff(left, right, + left_id, right_id, + left_data, right_data, + output, diff_format, encloser); + } + } static void dump_diffs(lua_hooks & lua, database & db, - cset const & cs, - set const & paths, + roster_t const & left_roster, + roster_t const & right_roster, std::ostream & output, diff_type diff_format, - bool new_is_archived, - bool old_is_archived, - bool show_encloser, - bool limit_paths) + bool external_diff_args_given, + string external_diff_args, + bool left_from_db, + bool right_from_db, + bool show_encloser) { - // 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) + parallel::iter i(left_roster.all_nodes(), right_roster.all_nodes()); + while (i.next()) { - if (limit_paths && paths.find(i->first) == paths.end()) - continue; - - output << patch_sep << '\n'; - data unpacked; - vector lines; - - if (new_is_archived) + MM(i); + switch (i.state()) { - file_data dat; - db.get_file_version(i->second, dat); - unpacked = dat.inner(); - } - else - { - read_data(i->first, unpacked); - } + case parallel::invalid: + I(false); - std::string pattern(""); - if (show_encloser) - lua.hook_get_encloser_pattern(i->first, pattern); + case parallel::in_left: + // deleted + if (is_file_t(i.left_data())) + { + file_path left_path, right_path; + left_roster.get_name(i.left_key(), left_path); + // right_path is null + + file_id left_id, right_id; + left_id = downcast_to_file_t(i.left_data())->content; + // right_id is null - make_diff(i->first.as_internal(), - i->first.as_internal(), - i->second, - i->second, - data(), unpacked, - output, diff_format, pattern); - } + data left_data, right_data; + get_data(db, left_path, left_id, left_from_db, left_data); + // right_data is null - map reverse_rename_map; + string encloser(""); + if (show_encloser) + lua.hook_get_encloser_pattern(left_path, encloser); - 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)); - } + dump_diff(lua, + left_path, right_path, + left_id, right_id, + left_data, right_data, + diff_format, external_diff_args_given, external_diff_args, + encloser, output); + } + break; - 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; + case parallel::in_right: + // added + if (is_file_t(i.right_data())) + { + file_path left_path, right_path; + // left_path is null + right_roster.get_name(i.right_key(), right_path); + + file_id left_id, right_id; + // left_id is null + right_id = downcast_to_file_t(i.right_data())->content; - file_data f_old; - data data_old, data_new; + data left_data, right_data; + // left_data is null + get_data(db, right_path, right_id, right_from_db, right_data); - output << patch_sep << '\n'; + string encloser(""); + if (show_encloser) + lua.hook_get_encloser_pattern(right_path, encloser); - if (old_is_archived) - { - db.get_file_version(delta_entry_src(i), f_old); - data_old = f_old.inner(); - } - else - { - I(new_is_archived); - read_data(delta_entry_path(i), data_old); - } + dump_diff(lua, + left_path, right_path, + left_id, right_id, + left_data, right_data, + diff_format, external_diff_args_given, external_diff_args, + encloser, output); + } + break; - if (new_is_archived) - { - file_data f_new; - db.get_file_version(delta_entry_dst(i), f_new); - data_new = f_new.inner(); - } - else - { - I(old_is_archived); - read_data(delta_entry_path(i), data_new); - } + case parallel::in_both: + // moved/renamed/patched/attribute changes + if (is_file_t(i.left_data())) + { + + file_id left_id, right_id; + left_id = downcast_to_file_t(i.left_data())->content; + right_id = downcast_to_file_t(i.right_data())->content; - file_path dst_path = delta_entry_path(i); - file_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; + if (left_id == right_id) + continue; - std::string pattern(""); - if (show_encloser) - lua.hook_get_encloser_pattern(src_path, pattern); + file_path left_path, right_path; + left_roster.get_name(i.left_key(), left_path); + right_roster.get_name(i.right_key(), right_path); + + data left_data, right_data; + get_data(db, left_path, left_id, left_from_db, left_data); + get_data(db, right_path, right_id, right_from_db, right_data); - make_diff(src_path.as_internal(), - dst_path.as_internal(), - delta_entry_src(i), - delta_entry_dst(i), - data_old, data_new, - output, diff_format, pattern); + string encloser(""); + if (show_encloser) + lua.hook_get_encloser_pattern(right_path, encloser); + + dump_diff(lua, + left_path, right_path, + left_id, right_id, + left_data, right_data, + diff_format, external_diff_args_given, external_diff_args, + encloser, output); + } + break; + } } } -static void -dump_diffs(lua_hooks & lua, - database & db, - cset const & cs, - std::ostream & output, - diff_type diff_format, - bool new_is_archived, - bool old_is_archived, - bool show_encloser) -{ - set dummy; - dump_diffs(lua, db, cs, dummy, output, - diff_format, new_is_archived, old_is_archived, show_encloser, false); -} - // common functionality for diff and automate content_diff to determine // revisions and rosters which should be diffed // FIXME needs app_state in order to create workspace objects (sometimes) static void prepare_diff(app_state & app, database & db, - cset & included, + roster_t & old_roster, + roster_t & new_roster, args_vector args, - bool & new_is_archived, - bool & old_is_archived, + bool & old_from_db, + bool & new_from_db, std::string & revheader) { temp_node_id_source nis; ostringstream header; - // The resulting diff is output in 'included'. - // initialize before transaction so we have a database to work with. project_t project(db); @@ -384,7 +400,7 @@ prepare_diff(app_state & app, if (app.opts.revision_selectors.empty()) { - roster_t old_roster, restricted_roster, new_roster; + roster_t left_roster, restricted_roster, right_roster; revision_id old_rid; parent_map parents; workspace work(app); @@ -397,71 +413,75 @@ prepare_diff(app_state & app, "(specify a revision to diff against with --revision)")); old_rid = parent_id(parents.begin()); - old_roster = parent_roster(parents.begin()); - work.get_current_roster_shape(db, nis, new_roster); + left_roster = parent_roster(parents.begin()); + work.get_current_roster_shape(db, nis, right_roster); node_restriction mask(args_to_paths(args), args_to_paths(app.opts.exclude_patterns), app.opts.depth, - old_roster, new_roster, ignored_file(work)); + left_roster, right_roster, ignored_file(work)); - work.update_current_roster_from_filesystem(new_roster, mask); + work.update_current_roster_from_filesystem(right_roster, mask); - make_restricted_roster(old_roster, new_roster, restricted_roster, + make_restricted_roster(left_roster, right_roster, restricted_roster, mask); - make_cset(old_roster, restricted_roster, included); + old_roster = left_roster; + new_roster = restricted_roster; - new_is_archived = false; - old_is_archived = true; + old_from_db = true; + new_from_db = false; + header << "# old_revision [" << old_rid << "]\n"; } else if (app.opts.revision_selectors.size() == 1) { - roster_t old_roster, restricted_roster, new_roster; + roster_t left_roster, restricted_roster, right_roster; revision_id r_old_id; workspace work(app); complete(app.opts, app.lua, project, idx(app.opts.revision_selectors, 0)(), r_old_id); - db.get_roster(r_old_id, old_roster); - work.get_current_roster_shape(db, nis, new_roster); + db.get_roster(r_old_id, left_roster); + work.get_current_roster_shape(db, nis, right_roster); node_restriction mask(args_to_paths(args), args_to_paths(app.opts.exclude_patterns), app.opts.depth, - old_roster, new_roster, ignored_file(work)); + left_roster, right_roster, ignored_file(work)); - work.update_current_roster_from_filesystem(new_roster, mask); + work.update_current_roster_from_filesystem(right_roster, mask); - make_restricted_roster(old_roster, new_roster, restricted_roster, + make_restricted_roster(left_roster, right_roster, restricted_roster, mask); if (app.opts.reverse) { - make_cset(restricted_roster, old_roster, included); - new_is_archived = true; - old_is_archived = false; + old_roster = restricted_roster; + new_roster = left_roster; + old_from_db = false; + new_from_db = true; } else { - make_cset(old_roster, restricted_roster, included); - new_is_archived = false; - old_is_archived = true; + old_roster = left_roster; + new_roster = restricted_roster; + old_from_db = true; + new_from_db = false; } header << "# old_revision [" << r_old_id << "]\n"; } else if (app.opts.revision_selectors.size() == 2) { - roster_t old_roster, restricted_roster, new_roster; + roster_t left_roster, restricted_roster, right_roster; revision_id r_old_id, r_new_id; complete(app.opts, app.lua, project, idx(app.opts.revision_selectors, 0)(), r_old_id); complete(app.opts, app.lua, project, idx(app.opts.revision_selectors, 1)(), r_new_id); - db.get_roster(r_old_id, old_roster); - db.get_roster(r_new_id, new_roster); + db.get_roster(r_old_id, left_roster); + db.get_roster(r_new_id, right_roster); // FIXME: this is *possibly* a UI bug, insofar as we // look at the restriction name(s) you provided on the command @@ -488,30 +508,34 @@ prepare_diff(app_state & app, node_restriction mask(args_to_paths(args), args_to_paths(app.opts.exclude_patterns), app.opts.depth, - old_roster, new_roster); + left_roster, right_roster); - make_restricted_roster(old_roster, new_roster, restricted_roster, + make_restricted_roster(left_roster, right_roster, restricted_roster, mask); - make_cset(old_roster, restricted_roster, included); + old_roster = left_roster; + new_roster = restricted_roster; - new_is_archived = true; - old_is_archived = true; - + old_from_db = true; + new_from_db = true; } else { I(false); } - revheader = header.str(); + revheader = header.str(); } void dump_header(std::string const & revs, - cset const & changes, + roster_t const & old_roster, + roster_t const & new_roster, std::ostream & out, bool show_if_empty) { + cset changes; + make_cset(old_roster, new_roster, changes); + data summary; write_cset(changes, summary); if (summary().empty() && !show_if_empty) @@ -542,37 +566,33 @@ CMD(diff, "diff", "di", CMD_REF(informat "that revision is shown. If two revisions are given, the diff " "between them is given. If no format is specified, unified is " "used by default."), - options::opts::revision | options::opts::depth | options::opts::exclude - | options::opts::diff_options) + options::opts::revision | options::opts::depth | options::opts::exclude | + options::opts::diff_options) { if (app.opts.external_diff_args_given) E(app.opts.diff_format == external_diff, origin::user, F("--diff-args requires --external\n" "try adding --external or removing --diff-args?")); - cset included; + roster_t old_roster, new_roster; std::string revs; - bool new_is_archived; - bool old_is_archived; + bool old_from_db; + bool new_from_db; database db(app); - prepare_diff(app, db, included, args, new_is_archived, old_is_archived, revs); + prepare_diff(app, db, old_roster, new_roster, args, old_from_db, new_from_db, revs); if (!app.opts.without_header) { - dump_header(revs, included, cout, true); + dump_header(revs, old_roster, new_roster, cout, true); } - if (app.opts.diff_format == external_diff) - { - do_external_diff(app.opts, app.lua, db, included, new_is_archived); - } - else - { - dump_diffs(app.lua, db, included, cout, - app.opts.diff_format, new_is_archived, old_is_archived, - !app.opts.no_show_encloser); - } + dump_diffs(app.lua, db, old_roster, new_roster, cout, + app.opts.diff_format, + app.opts.external_diff_args_given, + app.opts.external_diff_args, + old_from_db, new_from_db, + !app.opts.no_show_encloser); } @@ -592,22 +612,25 @@ CMD_AUTOMATE(content_diff, N_("[FILE [.. options::opts::revision | options::opts::depth | options::opts::exclude | options::opts::reverse) { - cset included; - std::string dummy_header; - bool new_is_archived; - bool old_is_archived; + roster_t old_roster, new_roster; + string dummy_header; + bool old_from_db; + bool new_from_db; database db(app); - prepare_diff(app, db, included, args, new_is_archived, old_is_archived, dummy_header); + prepare_diff(app, db, old_roster, new_roster, args, old_from_db, new_from_db, + dummy_header); if (app.opts.with_header) { - dump_header(dummy_header, included, output, false); + dump_header(dummy_header, old_roster, new_roster, output, false); } - dump_diffs(app.lua, db, included, output, - app.opts.diff_format, new_is_archived, old_is_archived, !app.opts.no_show_encloser); + dump_diffs(app.lua, db, old_roster, new_roster, output, + app.opts.diff_format, + app.opts.external_diff_args_given, app.opts.external_diff_args, + old_from_db, new_from_db, !app.opts.no_show_encloser); } @@ -935,7 +958,6 @@ CMD(log, "log", "", CMD_REF(informative) revision_id const & rid = frontier.top().second; bool print_this = mask.empty(); - set diff_paths; if (null_id(rid) || seen.find(rid) != seen.end()) { @@ -990,12 +1012,6 @@ CMD(log, "log", "", CMD_REF(informative) if (roster.has_node(*n) && mask.includes(roster, *n)) { print_this = true; - if (app.opts.diffs) - { - file_path fp; - roster.get_name(*n, fp); - diff_paths.insert(fp); - } } } } @@ -1083,11 +1099,31 @@ CMD(log, "log", "", CMD_REF(informative) if (app.opts.diffs) { + // if the current roster was loaded above this should hit the + // cache and not cost much... logging diffs isn't superfast + // regardless. + roster_t current_roster; + db.get_roster(rid, current_roster); + for (edge_map::const_iterator e = rev.edges.begin(); e != rev.edges.end(); ++e) - dump_diffs(app.lua, db, edge_changes(e), diff_paths, out, - app.opts.diff_format, true, true, - !app.opts.no_show_encloser, !mask.empty()); + { + roster_t parent_roster, restricted_roster; + + db.get_roster(edge_old_revision(e), parent_roster); + + // always show forward diffs from the parent roster to + // the current roster regardless of the log direction + make_restricted_roster(parent_roster, current_roster, + restricted_roster, mask); + + dump_diffs(app.lua, db, parent_roster, restricted_roster, + out, app.opts.diff_format, + app.opts.external_diff_args_given, + app.opts.external_diff_args, + true, true, + !app.opts.no_show_encloser); + } } if (next > 0)