# # patch "ChangeLog" # from [c07c28aa7857f525902c741ae80f37b9b03aade3] # to [793b3227e1615234c0c3fd3199052188933b9e84] # # patch "cset.cc" # from [9cad7c1bca5a2bfa8f43851240d784a5223b26ed] # to [55ef8c7b5fad784204c6598267a3d140b310e5ea] # =============================================== --- ChangeLog c07c28aa7857f525902c741ae80f37b9b03aade3 +++ ChangeLog 793b3227e1615234c0c3fd3199052188933b9e84 @@ -1,3 +1,9 @@ +2005-07-24 graydon hoare + + * cset.cc (change_automaton): New random-testing struct. + (automaton_cset_test): New test using it. + Lots of bugs picked out by random tester. + 2005-07-23 graydon hoare * basic_io.{cc,hh} (basic_io::stanza::push_str_triple): New method. @@ -15,911 +21,6 @@ * cset.cc: Rewrite-in-progress on change_set.cc. -2005-06-14 Richard Levitte - - * std_hooks.lua (get_preferred_merge2_command, - get_preferred_merge3_command): EDITOR may be undefined. In that - case, os.getenv() returns nil, on which string.lower() chokes. - It's much better to check for that and default to an empty - string. - -2005-06-11 Derek Scherger - - * commands.cc (complete_command): log command expansion messages - with L instead of P to reduce chatter - (status): add --brief option and corresponding output - (identify): add trailing space to comment gcc complains about - * monotone.cc: fix comment typo and add additional details for - command specific options - * monotone.texi (Automation): list inventory status code - combinations and descriptions - * tests/t_status.at: new test of status command and --brief option - * testsuite.at: add it - -2005-06-11 Matt Johnston - - * commands.cc: revert should ignore the ignore hooks, otherwise bad - things happen (revert a single ignored file, resultant empty ignore list - reverts the whole working copy). - * app_state.cc, app_state.hh: give set_restriction a flag to disregard - file-ignore hooks. - * tests/t_revert_restrict.at, testsuite.at: a test - -2005-06-09 Riccardo Ghetta - - * std_hooks.lua: make binary_file return nil on unreadable/empty files - -2005-06-10 Joel Reed - - * commands.cc (CMD(cdiff)): Add OPT_DEPTH to command options. - * t_restrictions.at: Add to testcase. - -2005-06-09 Joel Reed - - * commands.cc (CMD(diff)): Add OPT_DEPTH back in, as it is used. - * t_restrictions.at: Add to testcase to increase likelihood of - keeping it around :) - -2005-06-10 Richard Levitte - - * commands.cc (CMD(diff)): Remove OPT_DEPTH, as it was never - used. - -2005-06-09 Richard Levitte - - * monotone.texi (Merging): I assume that "apposite" was supposed - to be "appropriate". - -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. - -2005-06-07 Nathaniel Smith - - * monotone.texi (Storage and workflow): Attempt to thwart some - common misconceptions. - -2005-06-07 Nathaniel Smith - - * netsync.cc (rebuild_merkle_trees): Add a comment describing how - this code should work (and why it currently doesn't quite). - -2005-06-05 Nathaniel Smith - - * tests/t_bad_packets.at: Expect certs on a non-existent rev to - fail. Run db check instead. - * commands.cc (complete): Let callers specify they're okay with - non-existent revisions. - (CMD(trusted)): So specify. - -2005-06-05 Nathaniel Smith - - * tests/t_tags.at: 'tag' on a non-existent revid should fail. - * commands.cc (complete): Fail on non-existent revids. - -2005-05-29 Nathaniel Smith - - * tests/t_epoch.at: Typo. - * tests/t_automate_certs.at, tests/t_selector_later_earlier.at: - Throw in some calls to CANONICALISE, maybe this will help on - Win32... - -2005-06-04 Timothy Brownawell - - * netsync.cc, netcmd.cc: Style cleanups (mostly whitespace). - -2005-06-04 Timothy Brownawell - - * netsync.cc (process_hello_cmd): Warn about collection/regex - usage when talking to an old server. - -2005-06-04 Derek Scherger - - * commands.cc (update): update MT/work based on the changes - between the chosen revision and the new merge revision - * tests/t_update_with_pending_drop.at: - * tests/t_update_with_pending_add.at: - * tests/t_update_with_pending_rename.at: un-XFAIL and clean up now - that things work - -2005-06-04 Timothy Brownawell - - * netcmd.{cc,hh}, netsync.cc: Move {read,write}_*_cmd_payload - to netcmd::{read,write}_*_cmd . - * netcmd.cc, netsync.cc: Compatibility infrastructure. - * netsync.cc: Interoperate with v4 servers. - -2005-06-03 Timothy Brownawell - - * automate.cc (print_some_output): Fix compiler warning. - -2005-06-04 Derek Scherger - - * app_state.cc (app_state): initialize diffs to false; it seemed - to be defaulting to true for me - -2005-06-04 Derek Scherger - - * tests/t_update_with_pending_drop.at: - * tests/t_update_with_pending_add.at: - * tests/t_update_with_pending_rename.at: - * tests/t_restricted_commit_with_inodeprints.at: new bug reports - * testsuite.at: call them - -2005-06-04 graydon hoare - - * rcs_import.cc - (note_state_at_branch_beginning): Move time back when - there are known commits on a branch. - -2005-06-03 Joel Reed - - * commands.cc, monotone.texi: provide --verbose option for - monotone complete revision which adds date and author - completion output - * contrib/monotone.zsh_completion: use verbose output when - completing revisions - -2005-06-02 graydon hoare - - * rcs_import.cc - (cvs_key::is_synthetic_branch_founding_commit): New field. - (cvs_key::operator==): Handle synthetic case specially. - (cvs_key::operator<): Likewise. - (note_state_at_branch_beginning): Likewise. - * tests/t_cvsimport_drepper.at: Converted bug testcase. - * testsuite.at: Call it. - - * monotone.cc, commands.cc, options.hh - (OPT_NO_MERGES, OPT_DIFFS): New options. - * app_state.cc (app_state::no_merges, app_state::diffs): Likewise. - * commands.cc (log): Honor no_merges, diffs. - * contrib/color_logs.{sh,conf}: Helpers for reviewing work in a - nice colorized, easy-to-read fashion. - * contrib/colorize: A colorization script found on the net. - - * HACKING, ROADMAP: Expand a bit. - * commands.cc (changes_summary::print): Change macro to helper fn. - * contrib/monotone.el (monotone-cmd): Handle nil exit code. - -2005-06-02 Joel Reed - - * commands.cc, database.cc, database.hh, vocab.hh, vocab_terms.hh: - add complete key subcommand and provide --brief option of zsh/bash - completion. See http://lists.gnu.org/archive/html/monotone-devel/2005-05/msg00461.html - * tests/t_rebuild.at: add tests for complete key subcommand - * monotone.texi: document new subcommand - * contrib/monotone.zsh_completion: update for new complete key - command, improve _monotone_existing_entries using new --depth=0 - option, add revision completion for cert command, and a bugfix - for cat command - -2005-06-01 Matt Johnston - - * tests/t_i18n_changelog.at: capitalise UTF-8 CHARSET to keep - solaris happy. - -2005-06-01 Timothy Brownawell - - * netsync.cc (analyze_ancestry_graph): Try to fix segfault. - Always accept tags. - -2005-06-01 Timothy Brownawell - - * netsync.cc (process_auth_cmd, analyze_ancestry_graph): Move - write-permission checking to where it belongs, *after* we know - exactly what we're checking permissions about. Drop things we - don't want. - -2005-06-01 Matt Johnston - - * tests/t_cvsimport_deleted_invar.at: don't use -C with tar - * tests/t_i18n_file.at: capitalise CHARSET=UTF-8, seems more standard. - * tests/t_merge_normalization_edge_case.at: use known-good output - rather than using diff3 --merge - -2005-05-31 Timothy Brownawell - - * tests/t_epoch_server.at: fix typo - * netsync.cc (session::process_auth_cmd): If no branches are allowed - for writing, also check for write permissions to branch "" (needed - for serving empty dbs). For sync, don't refuse connection if there - are no readable branches (only do this for pull). - -2005-05-31 Timothy Brownawell - - * monotone.texi: Update documentation for get_netsync_*_permitted - hooks to reflect that they now get individual branch names. - -2005-05-31 Timothy Brownawell - - * netsync.cc: session::rebuild_merkle_trees now takes a set of - branches to include as an argument. On the server, calculate - this set at the same time the get_netsync_*_permitted hooks are - called; call said hooks on each branch individually. - -2005-05-31 Timothy Brownawell - - Remove old collection support in favor of using regexes exclusively. - * netsync.cc (convert_pattern): Remove function. - * (14 files): collections are unexist; do not mention (potential - for confusion) - * constants.cc: Increase netsync protocol version. - * monotone.texi: Update documentation. - * tests/t_epoch_unidirectional.at: Fix to sync subbranches. - * commands.cc (CMD update): Fix usage check. - * tests/t_select_cert.at: Fix to use --revision. - -2005-05-30 Timothy Brownawell - - * netsync.cc: Call note_netsync_*_received hooks in the order they're - written to the db (for revisions, gives topological order). - -2005-05-30 Timothy Brownawell - - * lua.{cc,hh}: Replace note_netsync_commit with - note_netsync_{revision,cert,pubkey}_received - * packet.{cc,hh}: Callbacks for cert or key written to the database. - * netsync.cc: Use said callbacks, call note_netsync_*_received hooks. - * monotone.texi: Update documentation. - -2005-05-30 Timothy Brownawell - - * packet.{cc,hh}, netsync.cc: on_revision_written callback now takes - the revision_id as an argument. - * lua.{cc,hh}: New Lua hook, note_netsync_commit. - * netsync.cc: At end of netsync session, call new hook for each - revision received. - monotone.texi: Document new hook. - -2005-05-30 Richard Levitte - - * commands.cc (CMD(checkout), CMD(cdiff), CMD(diff), CMD(log)): - Remove '[--revision=REVISION]' from command argument synopsis, - and add more text to the help to explain what happens when - --revision options are used. - (CMD(update)): Instead of the optional revision argument, use - the --revision option. Add information on what happens when the - --revision option is used, and when it's not. - - * tests/t_add_stomp_file.at, tests/t_add_vs_commit.at, - tests/t_annotate.at, tests/t_lf_crlf.at, - tests/t_update_nonexistent.at, tests/t_update_off_branch.at, - tests/t_update_to_revision.at: Update to use --revision with - 'monotone update'. - -2005-05-30 Matt Johnston - - * netsync.cc: cosmetic linebreak tidying for "double-check the - fingerprint" message. - * main.cc: make it clearer that "unknown type" refers to an exception - * monotone.cc: catch early informative_failures (due to charset - problems etc) - -2005-05-30 Matt Johnston - - * tests/t_fmerge.at: scrap all the diff3/ed, just compare it with - known-good output. - -2005-05-30 Timothy Brownawell - - * revision.cc (toposort): Better algorithm. - -2005-05-30 Matt Johnston - - * tests/t_fmerge.at: make sure we write the file with the ed script. - -2005-05-30 Matt Johnston - - * testsuite.at: use "command -v" rather than "which", since - Solaris doesn't give useful exit codes for "which". - * tests/t_fmerge.at: don't use --merge with diff3, pipe to ed instead - so we don't rely on gnu diff3. - -2005-05-29 Timothy Brownawell - - * contrib/monoprof.sh: Add support for using valgrind for - heap profiling. - -2005-05-28 Joel Reed - - * app_state.cc, app_state.hh, commands.cc, monotone.cc, options.h: - add new --depth command, and rename log's --depth to --last - * monotone.texi: update documentation - * tests/t_log_depth.at, tests/t_log_depth_single.at: update - log tests to use --last instead of --depth - * tests/t_options.at, tests/t_restrictions.at: test usage of - --depth for commands using restrictions - * contrib/ciabot_monotone.py, contrib/monotone-notify.pl, - contrib/monotone.el, contrib/monotone.zsh_completion, - contrib/mtbrowse.sh: change all occurences of "depth" to "last" - -2005-05-28 Timothy Brownawell - - * netcmd.cc (read_netcmd): Reserve space in the buffer if needed, - swap buffers instead of copying (memory savings for sync - large files) - * netsync.cc (session::arm): Don't clear the buffer (now done - by read_netcmd). - -2005-05-27 Timothy Brownawell - - * netsync.cc: Allow REGEXes as well as collections. - Fix out-of-branch ancestor handling. - * tests/t_netsync_diffbranch.at: Remove bug report and XFAIL (fixed). - * commands.cc: Update description fields for netsync commands. - * monotone.texi: Update documentation. - -2005-05-25 Timothy Brownawell - - * tests/t_automate_stdio.at: Make it self-contained. - -2005-05-25 Timothy Brownawell - - * contrib/get_stdio.pl (new file): Perl script to parse the output from - "mtn automate stdio". Used by... - * tests/t_automate_stdio.at (new file): Test for "mtn automate stdio". - * testsuite.at: Add it. - -2005-05-25 Timothy Brownawell - - * automate.cc ("automate stdio"): Fix block size limiting. - Honor "output.flush()" in commands. - -2005-05-24 Timothy Brownawell - - * automate.cc: Fix buffering for "automate stdio" - -2005-05-24 Timothy Brownawell - - * automate.cc: Put back lost "automate certs". - -2005-05-24 Matt Johnston - - * commands.cc (try_one_merge, CMD(merge), CMD(explicit_merge), - CMD(propagate): allow --author flag. - -2005-05-24 Timothy Brownawell - - * automate.cc: Fix comment for automate stdio to match the code. - * monotone.texi: Document ignored locations in automate stdio - input as reserved. - -2005-05-24 Riccardo Ghetta - - * tests/t_merge_binary.at: new XFAIL test to cover monotone - inclination to algorithmically merge binary files. - -2005-05-24 Richard Levitte - - * commands.cc (try_one_merge): Change 'rid' to 'merged_id'. - -2005-05-23 Timothy Brownawell - - Fix "automate stdio" input/output format according to ML discussion - * automate.cc: changed: automate_stdio - added: print_some_output, class my_stringbuf - * constants.{cc,hh}: add constant for automate stdio block size - * monotone.texi: update documentation - -2005-05-23 Nathaniel Smith - - * win32/terminal.cc (have_smart_terminal): Call _isatty on stderr, - not stdout. - -2005-05-23 Richard Levitte - - * commands.cc (try_one_merge): Use the value of --date and - --author if there are any. - (CMD(merge), CMD(propagate), CMD(explicit_merge)): Change to - accept --date and --author. - -2005-05-23 Riccardo Ghetta - - * selectors.cc/.hh, database.cc: add two new selectors: - "earlier or equal than" and "later than". - * lua.cc/.hh, std-hooks.lua: create a new "expand_date" hook - * monotone.texi: document the changes - * testsuite.at, tests/t_selector_later_earlier.at: add specific tests - for the new selectors - -2005-05-21 Richard Levitte - - * Makefile.am: Make monotone.pdf and monotone.dvi depend on - version.texi. - -2005-05-21 Richard Levitte - - * monotone.texi: Add a note about the --brief option with - 'monotone log', and restructure the synopsis since it was getting - a bit silly with all possible variants. - -2005-05-21 Richard Levitte - - * commands.cc (log_certs): Add two arguments; a separator string - to be used in front of the second to last cert for multi-valued - cert types, a bool to say if each cert should be ended with a - newline. Overload with shortcuts. - (CMD(log)): Use the --brief option and implement it using the - shortcut variants of log_certs. - * monotone.cc, options.hh: Add the --brief option (OPT_BRIEF - internally). - * sanity.cc, sanity.hh (struct sanity): Add the member variable - and function to hold and set the brief flag. - -2005-05-21 Matt Johnston - - * tests/t_short_opts.at: remove the saved MT/log message - from the failed commit. - * Makefile.am: MAKEINFOFALGS to MAKEINFOFLAGS - -2005-05-21 Matt Johnston - - * commands.cc (commit): write the log message to MT/log - during the commit, so it will be available later if the commit - fails. - * work.{cc,hh} (write_user_log): new function - -2005-05-20 Nathaniel Smith - - * contrib/mtbrowse.sh: New file. - * contrib/README: Document it. Also, document some missed files, - and re-order listing. - * Makefile.am (EXTRA_DIST): Add several missing contrib/ files. - -2005-05-21 Grahame Bowland - - * automate.cc: (automate_certs) change "status" field - to "signature". Check whether each cert is trusted, and - output in the "trusted" field. - * testsuite.at: add t_automate_certs.at - * tests/t_automate_certs.at: Test that the output of - "automate certs" is consistent, and that we exit with - error when rev is incomplete or missing. - * monotone.texi: update output documentation for - "automate certs" - -2005-05-20 Emile Snyder - - * annotate.{hh,cc}: Rework to handle lineage dependent line - mappings and lines which split from a single line in a parent - revision into multiple lines in some descendent. Fixes bug where - some lines remained unannotated. Fixes wrong assignment of lines - bug. - * tests/t_annotate.at: Check no-changes since addition of file - case. - * tests/t_annotate_lineage_dependent.at - * tests/t_annotate_split_lines.at: New tests. - * testsuite.at: Add them. - -2005-05-20 Nathaniel Smith - - * monotone.texi (Network): Clarify that ports can be specified on - the command line to serve/pull/push/sync. - -2005-05-21 Matt Johnston - - * packet.cc (db_packet_writer::~impl, prerequisite.cleanup): - add code to remove up circular dependencies between prerequisite - and delayed_packet shared_ptrs upon destruction, so that unsatisified - dependency warnings are printed. - -2005-05-19 Matt Johnston - - * change_set.cc (merge_disjoint_analyses): handle the case where - a file is dropped on both sides but re-added on one. - * tests/t_drop_vs_dropadd.at: a test for it - * testsuite.at - -2005-05-19 Derek Scherger - - * commands.cc (checkout): rearrange to use --revision option - * monotone.1: - * monotone.texi: document checkout --revision option - * tests/t_attr.at: - * tests/t_attributes.at: - * tests/t_checkout_id_sets_branch.at: - * tests/t_checkout_noop_on_fail.at: - * tests/t_checkout_options.at: - * tests/t_cwork.at: - * tests/t_delete_dir.at: - * tests/t_delete_dir_patch.at: - * tests/t_empty_path.at: - * tests/t_i18n_file_data.at: - * tests/t_inodeprints_hook.at: - * tests/t_inodeprints_update.at: - * tests/t_largish_file.at: - * tests/t_lf_crlf.at: - * tests/t_monotone_up.at: - * tests/t_netsync_defaults.at: - * tests/t_netsync_set_defaults.at: - * tests/t_persistent_server_revision.at: - * tests/t_rename_added_in_rename.at: - * tests/t_rename_dir_cross_level.at: - * tests/t_rename_dir_patch.at: - * tests/t_single_char_filenames.at: - * tests/t_subdir_add.at: - * tests/t_subdir_attr.at: - * tests/t_subdir_drop.at: - * tests/t_subdir_rename.at: - * tests/t_subdir_revert.at: - * tests/t_tags.at: - * tests/t_update_off_branch.at: - * tests/t_versions.at: - * testsuite.at: add --revision option to checkout - -2005-05-18 Richard Levitte - - * ui.cc: Move the copyright and license section to the top of the - file, and add an emacs mode specifier. - * ui.cc (write_ticks): Change the counter ticker so the trailer - comes at the end of the counter line instead of the title line. - This is especially important for code that changes the trailer - a little now and then. - -2005-05-17 Grahame Bowland - - * commands.cc: add "automate certs ID" to the help string - for the automate command - * automate.cc: implement "automate certs". Add to the list - of commands available through "automate stdio". - * monotone.texi: document "automate certs" - -2005-05-17 Nathaniel Smith - - * monotone.texi (Network): Document 'serve' as taking more than - one collection argument. - -2005-05-15 graydon hoare - - * rcs_import.cc (note_state_at_branch_beginning): collect - branch beginning states into a single synthetic commit. - -2005-05-15 graydon hoare - - * rcs_import.cc: rewrite most of the branch logic to - address issues raised in bugs 13032 and 13063. - * tests/t_cvsimport_deleted_invar.at: un-XFAIL. - -2005-05-16 Matt Johnston - - * commands.cc (commit): change scope of the transaction guard so that - the transaction will fail before MT/revision is written (which could - leave a non-committed revision/bad working dir). - -2005-05-16 Grahame Bowland - - * monotone.texi: update "monotone log" documentation - * commands.cc: fix "monotone log" when run with no --revision args - -2005-05-15 Derek Scherger - - * tests/t_update_with_blocked_rename.at: new test - * testsuite.at: call it - -2005-05-15 Derek Scherger - - * netsync.cc (process_anonymous_cmd, process_auth_cmd): log - details of permissions allowed/denied - * tests/t_netsync_permissions.at: new test - * testsuite.at: call it - -2005-05-15 Richard Levitte - - * contrib/monotone-notify.pl (revision_is_in_branch): Another - place where --revision was missing. - -2005-05-14 Timothy Brownawell - - * contrib/monoprof.sh: Clean up variable definitions some. - - Add option --datadir, should now be usable without editing - variables to match system paths - - Add option --setup, generates most of the needed files - -2005-05-13 Timothy Brownawell - - Add "monotone automate stdio", to let the automation interface - take commands on standard input. - * automate.cc: (automate_stdio) New function. - (automate_command) Add it. - * commands.cc: Add to description for "automate". - * monotone.texi: Add to documentation. - -2005-05-13 Joel Reed - - * tests/t_unidiff3.at: opps. forgot to add this file which - should have been included as fix for bug 13072. - -2005-05-13 Joel Reed - - * diff_patch.cc, transforms.cc, testsuite.at: Patch from - address@hidden, who writes: "The attached patch should fix bug - 13072. I have no idea why the code in transform.cc insists on - adding an empty line in case the file is empty. Removing the code - didn't cause any regressions in the test suite and the - diff_patch.cc change corrects the output format. A new test case - is included as well." - -2005-05-13 Joel Reed - - * automate.cc: add automate attributes command - * commands.cc: add attributes subcommand helptext - * contrib/monotone.zsh_completion: use automate attributes - for completion of monotone attr and cleanup ignore files code - * tests/t_automate_attributes.at: add testcase - * testsuite.at: include new testcaes - -2005-05-13 Jon Bright - * testsuite.at (UNGZ): Change the way the ungzipping works on - Win32, in the hope that test 206 will no longer be given invalid - files. - -2005-05-12 Derek Scherger - - * automate.cc: bump version number to 1.0 - (struct inventory_item): add pre/post states - (inventory_paths): remove obsolete function - (inventory_pre_state, inventory_post_state, inventory_file_state, - inventory_renames): add fancy new functions - (automate_inventory): rework for new output format - * manifest.{cc,hh} (classify_paths): rename to ... - (classify_manifest_paths): ... this and work solely from manifest - * monotone.texi: (Automation): update inventory docs - * tests/t_automate_inventory.at: update for new format and add - some more tests - -2005-05-13 Matthew Gregan - - * HACKING: New file. First pass at a brief document to help - newcomers hack on monotone. - -2005-05-12 Riccardo Ghetta - - * options.hh (OPT_MSGFILE): New option. - * monotone.cc (message-file): New option. - (cpp_main): Handle it. - * app_state.{cc,hh} (set_message_file): New function. - * commands.cc (commit): Accept and handle new option. - * monotone.1, monotone.texi: Document it. - * tests/t_commit_message_file.at: New test. - * testsuite.at: Add it. - -2005-05-12 Timothy Brownawell - - * (20 files): Do not indent with both tabs and spaces in the same file. - -2005-05-13 Ulrich Drepper - - * rcs_import.cc (process_one_hunk): Improve handling of corrupt - RCS files. - -2005-05-13 Matthew Gregan - - * testsuite.at: Fix typo error in Win32 kill logic that was - causing the testsuites to hang on Win32 machines that don't have - pskill installed. - -2005-05-12 Matthew Gregan - - * file_io.cc (write_data_impl): Use portable boost::filesystem - calls in place of unlink(2)/remove(2). - -2005-05-12 Grahame Bowland - - * commands.cc: Modify the "log" command to accept multiple - revisions on command line, and display the log for all - of those revisions. - -2005-05-11 Nathaniel Smith - - * std_hooks.lua (ignore_file): Organize a bit more, add - patterns for autotools cache files, and darcs, codeville, git - metadata directories. - -2005-05-11 Timothy Brownawell - - * revision.cc (expand_dominators): Fix bitmap size-matching. - (find_common_ancestor_for_merge): Do not wait for ancestors - to be expanded to the beginning of time before expanding - dominators. Requires above fix for correct behavior. - * ChangeLog: Fix date on previous entry. - -2005-05-11 Timothy Brownawell - - * contrib/monoprof.sh: Add profiling test for "netsync large file". - Add options to only run specific profile tests. - -2005-05-11 Stanislav Karchebny - - * contrib/monotone-notify.pl: 'monotone log' takes a revision - through the --revision= option. - -2005-05-11 Richard Levitte - - * contrib/monotone-notify.pl: Change all occurences of $symbol' to - ${symbol}' to avoid a confusing Perl warning. - -2005-05-11 Joel Reed - - * contrib/monotone.zsh_completion: add zsh completion contrib. - -2005-05-11 Matt Johnston - - * tests/t_add_intermediate_MT_path.at: remove the drop dir part - * tests/t_delete_dir.at: add a note about re-enabling the above test - * tests/t_cvsimport3.at: ignore stderr - -2005-05-11 Matt Johnston - - * rcs_import.cc (find_branchpoint): if a branch is derived from two - differing parent branches, take the one closest to the trunk. - * tests/t_cvsimport3.at: add a test for cvs_importing where branches - come off a vendor import. - * testsuite.at: add it - -2005-05-11 Nathaniel Smith - - * work.cc (build_deletions): Disable delete_dir. - -2005-05-11 Matthew Gregan - - * constants.cc (constants::bufsz): Increase buffer size. Reduces - the runtime to tests/t_netsync_largish_file.at by four to seven - times on my test machines. - -2005-05-10 Timothy Brownawell - - * revision.cc: Make expand_{ancestors,dominators} twice as fast. - Loop over revisions in the other direction so that changes at the - frontier propogate fully in 1 pass, instead of one level at a time. - -2005-05-10 Timothy Brownawell - - * packet.{cc,hh}: Give packet_consumer and children a callback to call - after writing out a revision. - * netsync.cc: Use this callback to add a "revisions written" ticker, - to provide user feedback while sanity checking. - -2005-05-10 Timothy Brownawell - - * ui.cc: Make tick_write_count take less horizontal space - -2005-05-09 Nathaniel Smith - - * AUTHORS: Give Riccardo his real name. - * ChangeLog: Likewise. - -2005-05-09 Riccardo Ghetta - - * std_hooks.lua: Support kdiff3. - -2005-05-09 Matthew Gregan - - * lua.cc (loadstring, run_string): New parameter to identify the - source of the Lua string being loaded. - (add_{std,test}_hooks, load_rcfile): Pass an identity through. - -2005-05-09 Matthew Gregan - - * monotone.cc: Absolutify and tilde expand pid file. - -2005-05-09 Matthew Gregan - - * testsuite.at: Revert bogus changes committed in revision 9d478. - -2005-05-09 Matt Johnston - - * commands.cc (pid_file): use fs::path .empty() rather than ==, since - boost 1.31 doesn't seem to have the latter. - -2005-05-08 Matthew Gregan - - * lua.cc (report_error, load{file,string}): New member functions. - Error handling in call moved into report_error. - (call): Call report_error. - (run_{file,string}): Call load{file,string} member functions to - load Lua code into the VM. Allows us to report syntax errors when - loading rc files. - * testsuite.at: test_hooks.lua was calling nonexistent (obsolete) - strfind function and failing silently. The improved error - reporting from Lua caught this and cause testsuite failures. - -2005-05-08 Matthew Gregan - - * monotone.1: Document --pid-file option. Also make some minor - spelling and punctuation fixes. - -2005-05-08 Timothy Brownawell - * app_state.cc: {read,write}_options now print a warning instead of - failing on unreadable/unwritable MT/options . - * tests/t_unreadable_MT.at: add matching test - * testsuite.at: add test - * tests/README: Mention that new tests must be added to testsuite.at - * work.cc: (get_revision_id) Friendlier error message for - unreadable MT/revision . - -2005-05-08 Matthew Gregan - - * monotone.texi: Right words, wrong order. - * testsuite.at: Drop pid mapping trickery, it doesn't work - consistently. We now try and use SysInternal's pskill to kill the - process. If pskill is not available, we fall back to the old - 'kill all monotone processes' method. These changes affect - Win32/MingW only. - -2005-05-07 Matthew Gregan - - * commands.cc (pid_file): Remove leftover debugging output. - * configure.ac: Correct typos in TYPE_PID_T test. - * testsuite.at: Use some trickery on MingW/Cygwin to map the - Windows pid to the Cygwin pid. - * win32/process.cc (process_wait): Correct return type. - (process_spawn): Replace dropped cast on return. - -2005-05-07 Matt Johnston - - * change_set.cc: fix the code which skips deltas on deleted files, - it was looking at the merged filename not the ancestor - filename. - * tests/t_drop_vs_patch_rename.at: a test for the above fix - * testsuite.at: add it - -2005-05-06 Timothy Brownawell - - * contrib/monoprof.sh: Add lcad test. - Add options to pull/rebuild before profiling. - -2005-05-06 Nathaniel Smith - - * INSTALL: s/g++ 3.2 or 3.3/g++ 3.2 or later/. - -2005-05-06 Nathaniel Smith - - * monotone.1: - * monotone.texi (Commands, Importing from CVS, RCS): Clarify - cvs_import documentation on cvsroot vs. module issues. - -2005-05-05 Richard Levitte - - * AUTHORS: Add rghetta. - -2005-05-05 Matthew Gregan - - * monotone.texi: Document --pid-file option for serve command. - * app_state.{cc,hh} (set_pidfile, pidfile): New function, new - member. - * commands.cc (pid_file): New class. - (CMD(serve)): Use pid_file. - * monotone.cc (coptions, cppmain): Add command-specific option - --pid-file. - * options.hh (OPT_PIDFILE): New option. - * {unix,win32}/process.cc (get_process_id): New function. - (process_{spawn,wait,kill}): Use pid_t. - * platform.hh (process_{spawn,wait,kill}): Use pid_t. - (get_process_id): New function - * configure.ac: Test for pid_t. - * lua.cc (monotone_{spawn,wait,kill}_for_lua): Use pid_t. - * testsuite.at: Update netsync kill functions to use pid file. - * tests/t_netsync_sigpipe.at: Update to use pid file. - * tests/t_netsync_single.at: Update to use pid file. - -2005-05-04 Nathaniel Smith - - * tests/t_monotone_up.at: New test. - * testsuite.at: Add it. - 2005-07-23 Matthew Gregan * commands.cc (CMD(annotate)): Check for a valid revision before =============================================== --- cset.cc 9cad7c1bca5a2bfa8f43851240d784a5223b26ed +++ cset.cc 55ef8c7b5fad784204c6598267a3d140b310e5ea @@ -1,21 +1,20 @@ /* nodes: ~~~~~~ - a node is either a file or a directory. nodes have attributes, an - ident, a parent-ident, and a set of heirs and sires. directory nodes - have a map of children and a map of clobbered-children. file nodes - have a content hash. see below for the definitions of these members. + a node is either a file or a directory. nodes have attributes, an ident, a + parent-ident, possibly an heir, and a set of sires. directory nodes have a + map of children and a map of clobbered-children. file nodes have a content + hash. see below for the definitions of these members. mfests: ~~~~~~~ - an mfest is an index-set of nodes X and a tree T of nodes starting - from a root. the index X maps ident numbers to shared pointers into - T. there may be entries in X which are not in T. T must always be a - well-formed tree. + an mfest is an index-set of nodes X and a tree T of nodes starting from a + root. the index X maps ident numbers to shared pointers into T. there may + be entries in X which are not in T. T must always be a well-formed tree. an mfest has a normal form, in which: @@ -31,10 +30,10 @@ ~~~~~~ a cset is a pair of mfests A, B. the mfests in a cset are *not* - normalized. it is an invariant that idents(A.X) = idents(B.X), but it - is only sometimes true that idents(A.T) = idents(B.T); some nodes - might be present in one mfest's tree but absent from the other's (if - they were added or deleted). + normalized. it is an invariant that idents(A.X) = idents(B.X), but it is + only sometimes true that idents(A.T) = idents(B.T); some nodes might be + present in one mfest's tree but absent from the other's (if they were + added or deleted). change_consumers: @@ -69,22 +68,24 @@ heirs and sires: ~~~~~~~~~~~~~~~~ - nodes may have heirs or sires. only nodes being deleted in a cset may - have an heir; only nodes being added in a cset may have a sire. the - heir of a node is a target to send future content deltas and - attributes to; it is a "forwarding address" used in cases where - two files with separate histories are considered identical in a merge - and "sutured": one node is deleted, and it marks the other node as an + nodes may have heirs or sires. only nodes being deleted in a cset may have + an heir; only nodes being added in a cset may have a sire. a node may have + at most one heir. the heir of a node is a target to send future content + deltas and attributes to; it is a "forwarding address" used in cases where + two files with separate histories are considered identical in a merge and + "sutured": one node is deleted, and it marks the other node as an heir. the name of the heir is looked up in the new manifest. only attribute and content-merging passes care about heirs and sires. they do not affect lifecycle decisions during merging. - an added node A has a node S as sire in cset C iff there is a cset C' - in which A was being deleted with heir S, and C' = inverse(C). in - other words, sires exist *only* to preserve information about heirs - during cset inversion. there are no user-accessible primitives for - creating sire relationships. + an added node A has a node S as sire in cset C iff there is a cset C' in + which A was being deleted with heir S, and C' = inverse(C). in other + words, sires exist *only* to preserve information about heirs during cset + inversion. there are no user-accessible primitives for creating sire + relationships. a node S may be the sire of many other nodes N1,...,Nk, if + multiple Nk consider S their heir. the sire relationship is therefore a + set. generation numbers: @@ -166,6 +167,7 @@ #define __STDC_CONSTANT_MACROS #define __STDC_LIMIT_MACROS + #include #include #include @@ -175,18 +177,24 @@ #include #include + #include #include + +#include #include #include #include + #include "basic_io.hh" +#include "constants.hh" #include "numeric_vocab.hh" #include "sanity.hh" #include "vocab.hh" + using std::deque; using std::map; using std::ostream; @@ -198,12 +206,14 @@ using std::inserter; using std::copy; using std::make_pair; +using boost::lexical_cast; using boost::scoped_ptr; using boost::shared_ptr; using boost::dynamic_pointer_cast; using __gnu_cxx::hash_map; using __gnu_cxx::hash_set; + struct node; struct dir_node; struct file_node; @@ -212,6 +222,7 @@ struct dirent_hash; struct dirent_eq; + namespace __gnu_cxx { template<> @@ -223,6 +234,7 @@ }; } + typedef uint64_t ident_t; typedef uint64_t gen_t; typedef shared_ptr node_t; @@ -235,15 +247,19 @@ typedef string attr_name; typedef string attr_val; + static ident_t null_ident = 0; + static ident_t nursery_ident = 1; + static ident_t graveyard_ident = 2; + struct ident_source { @@ -252,6 +268,7 @@ ident_t next() { I(ctr != UINT64_MAX); return ctr++; } }; + //================================================================== // dirents and paths //================================================================== @@ -279,9 +296,11 @@ } }; + dirent_t null_dirent(new dir_entry("")); + bool operator<(dirent_t const & a, dirent_t const & b) @@ -289,12 +308,14 @@ return *a < *b; } + ostream & operator<<(ostream & o, dirent_t const & d) { return o << d->val; } + struct dirent_hash { @@ -304,6 +325,7 @@ } }; + struct dirent_eq { @@ -314,6 +336,7 @@ } }; + // this helper class represents an "unpacked" view of the path // through a tree of directory nodes to some specific leaf (either // dir or file); it is just a faster form of a non-empty file_path @@ -329,29 +352,43 @@ { } - path_vec_t(file_path const & f) + static dirent_t intern_component(string const & s) { + dirent_t tmp(new dir_entry(s)); + dirset_t::const_iterator i = dset.find(tmp); + if (i == dset.end()) + { + dset.insert(tmp); + } + else + { + tmp = *i; + } + return tmp; + } + + static path_vec_t from_file_path(file_path const & f) + { fs::path fp(f()); + path_vec_t pv; for (fs::path::iterator i = fp.begin(); i != fp.end(); ++i) { - dirent_t tmp(new dir_entry(*i)); - dirset_t::const_iterator j = dset.find(tmp); - if (j == dset.end()) - { - dset.insert(tmp); - } - else - { - tmp = *j; - } - dir.push_back(tmp); + dirent_t tmp = intern_component(*i); + pv.dir.push_back(tmp); } - I(!dir.empty()); - leaf = dir.back(); - dir.pop_back(); + I(!pv.dir.empty()); + pv.leaf = pv.dir.back(); + pv.dir.pop_back(); + return pv; } + void operator/=(dirent_t d) + { + dir.push_back(leaf); + leaf = d; + } + file_path to_file_path() const { fs::path fp; @@ -366,6 +403,7 @@ }; + //================================================================== // nodes //================================================================== @@ -374,6 +412,7 @@ dirset_t path_vec_t::dset; + struct node { @@ -497,6 +536,7 @@ virtual ~node() {} }; + struct file_node : public node @@ -507,6 +547,7 @@ virtual ~file_node() {} }; + node_t file_node::shallow_copy() const { @@ -545,6 +586,7 @@ virtual ~dir_node() {} }; + void dir_node::add_child(dirent_t name, node_t n) { @@ -555,6 +597,7 @@ entries.insert(make_pair(name,n)); } + void dir_node::drop_child(dirent_t c) { @@ -562,6 +605,7 @@ entries.erase(c); } + static inline bool is_dir_t(node_t n) { @@ -569,6 +613,7 @@ return static_cast(d); } + static inline bool is_file_t(node_t n) { @@ -576,6 +621,7 @@ return static_cast(f); } + static inline dir_t downcast_to_dir_t(node_t const n) { @@ -584,6 +630,7 @@ return d; } + static inline file_t downcast_to_file_t(node_t const n) { @@ -592,9 +639,13 @@ return f; } + struct dfs_iter { + // NB: the dfs_iter struct *does not return* the node it's + // constructed on, as part of its iteration + stack< pair > stk; dfs_iter(dir_t root) @@ -694,83 +745,6 @@ }; -static bool -shallow_equal(node_t const & a, - node_t const & b) -{ - // L(F("shallow equal: idents (%d,%d), parents (%d,%d), names (%s,%s)\n") - // % a->ident % b->ident - // % a->parent % b->parent - // % a->name % b->name); - - if ((a->ident != b->ident) - || (a->parent != b->parent) - || (a->name != b->name)) - return false; - - if (a->fancy || b->fancy) - { - if (!(b->fancy && b->fancy)) - return false; - if ((a->fancy->sire != b->fancy->sire) - || (a->fancy->heir != b->fancy->heir) - || (a->fancy->attrs != b->fancy->attrs)) - return false; - } - - if (is_file_t(a) && is_file_t(b)) - { - file_t fa = downcast_to_file_t(a); - file_t fb = downcast_to_file_t(b); - // L(F("files: content %s vs %s\n") % fa->content % fb->content); - if (! (fa->content == fb->content)) - return false; - return true; - } - - else if (is_dir_t(a) && is_dir_t(b)) - { - dir_t da = downcast_to_dir_t(a); - dir_t db = downcast_to_dir_t(b); - dirmap_t::const_iterator di = da->entries.begin(); - dirmap_t::const_iterator dj = db->entries.begin(); - while (di != da->entries.end() && dj != db->entries.end()) - { - if (di->first != dj->first) - return false; - if (di->second->ident != dj->second->ident) - return false; - ++di; - ++dj; - } - if (di != da->entries.end() || - dj != db->entries.end()) - return false; - return true; - } - else - return false; -} - -static bool -deep_equal(node_t const & a, - node_t const & b) -{ - bfs_iter pa(a), pb(b); - while (!(pa.finished() || pb.finished())) - { - if (!shallow_equal(*pa, *pb)) - return false; - ++pa; - ++pb; - } - - if (! (pa.finished() && pb.finished())) - return false; - - return true; -} - bool dir_node::contains_entry(dirent_t p) const { @@ -780,6 +754,7 @@ return true; } + node_t dir_node::get_entry(dirent_t p) const { @@ -789,6 +764,7 @@ return i->second; } + node_t dir_node::shallow_copy() const { @@ -842,13 +818,13 @@ } - //================================================================== // mfests //================================================================== typedef hash_map node_map_t; + static void index_nodes(dir_t d, node_map_t & nodes) { @@ -856,6 +832,7 @@ nodes.insert(make_pair((*i)->ident, *i)); } + struct mfest { @@ -907,12 +884,12 @@ }; - mfest::mfest(mfest const & other) { this->reset(other); } + void mfest::reset(mfest const & other) { @@ -922,6 +899,7 @@ index_nodes(root, nodes); } + dir_t mfest::make_dir() { @@ -933,6 +911,7 @@ return n; } + file_t mfest::make_file() { @@ -944,6 +923,7 @@ return n; } + void mfest::check_sane() const { @@ -954,23 +934,23 @@ // directory for absence of cycle-forming edges and agreement // between names. - // L(F("mfest sanity check beginning...\n")); + L(F("mfest sanity check beginning...\n")); for(bfs_iter i(root); !i.finished(); ++i) { - // if ((*i)->live()) - // { - // path_vec_t v; - // get_path(*i, v); - // L(F("tree iter visiting live node %d = '%s'\n") - // % (*i)->ident - // % v.to_file_path()); - // } - // else - // { - // L(F("tree iter visiting %s node %d\n") - // % ((*i)->unborn() ? "unborn" : "killed") - // % (*i)->ident); - // } + if ((*i)->live()) + { + path_vec_t v; + get_path(*i, v); + L(F("tree iter visiting live node %d = '%s'\n") + % (*i)->ident + % v.to_file_path()); + } + else + { + L(F("tree iter visiting %s node %d\n") + % ((*i)->unborn() ? "unborn" : "killed") + % (*i)->ident); + } I(seen.find((*i)->ident) == seen.end()); seen.insert((*i)->ident); } @@ -1002,13 +982,14 @@ I(seen.find(i->first) != seen.end()); } } - // L(F("mfest sanity check done")); + L(F("mfest sanity check done")); } + bool mfest::file_exists(file_path fp) const { - path_vec_t v(fp); + path_vec_t v = path_vec_t::from_file_path(fp); dir_t d = root; vector::const_iterator i = v.dir.begin(), j = v.dir.end(); while(i != j) @@ -1027,10 +1008,11 @@ && is_file_t(d->get_entry(v.leaf)); } + bool mfest::dir_exists(file_path dp) const { - path_vec_t v(dp); + path_vec_t v = path_vec_t::from_file_path(dp); dir_t d = root; vector::const_iterator i = v.dir.begin(), j = v.dir.end(); while(i != j) @@ -1049,6 +1031,7 @@ && is_dir_t(d->get_entry(v.leaf)); } + node_t mfest::get_node(ident_t i) const { @@ -1058,30 +1041,35 @@ return j->second; } + dir_t mfest::get_dir_node(ident_t i) const { return downcast_to_dir_t(get_node(i)); } + file_t mfest::get_file_node(ident_t i) const { return downcast_to_file_t(get_node(i)); } + dir_t mfest::get_dir_node(path_vec_t const & d) const { return downcast_to_dir_t(get_node(d)); } + file_t mfest::get_file_node(path_vec_t const & f) const { return downcast_to_file_t(get_node(f)); } + dir_t mfest::get_containing_dir_node(path_vec_t const & v) const { @@ -1095,12 +1083,14 @@ return d; } + node_t mfest::get_node(path_vec_t const & n) const { return get_containing_dir_node(n)->get_entry(n.leaf); } + void mfest::get_path(node_t const & n, path_vec_t & path) const @@ -1141,13 +1131,14 @@ } } + dir_t mfest::get_containing_dir_node(path_vec_t const & v, gen_t write_generation) { if (root->generation < write_generation) { - // L(F("upgrading root to write generation %d\n") % write_generation); + L(F("upgrading root to write generation %d\n") % write_generation); root = downcast_to_dir_t(root->shallow_copy()); root->generation = write_generation; nodes.erase(root->ident); @@ -1165,6 +1156,7 @@ return d; } + node_t mfest::get_node(path_vec_t const & pth, gen_t write_generation) @@ -1175,6 +1167,7 @@ return n; } + dir_t mfest::get_dir_node(path_vec_t const & pth, gen_t write_generation) @@ -1182,6 +1175,7 @@ return downcast_to_dir_t(get_node(pth, write_generation)); } + file_t mfest::get_file_node(path_vec_t const & pth, gen_t write_generation) @@ -1189,51 +1183,86 @@ return downcast_to_file_t(get_node(pth, write_generation)); } + node_t mfest::get_node(ident_t i, gen_t write_generation) { path_vec_t pth; + if (i == root->ident) + return root; node_t n = get_node(i); get_path(n, pth); return get_node(pth, write_generation); } + dir_t mfest::get_dir_node(ident_t i, gen_t write_generation) { return downcast_to_dir_t(get_node(i, write_generation)); } + file_t mfest::get_file_node(ident_t i, gen_t write_generation) { return downcast_to_file_t(get_node(i, write_generation)); } + bool -mfest::operator==(mfest const & other) const +equal_up_to_renumbering(mfest const & ma, + mfest const & mb) { - // L(F("comparing manifests: max ident %d vs. %d\n") - // % max_ident % other.max_ident); - if (max_ident != other.max_ident) - return false; + // NB: this function compares mfests for structural equality over the + // abstract filesystem; it ignores differences which may exist between + // the mfests' node sets, ident numbers, and any sire/heir relationships + // (which are only relevant in the context of csets) - // L(F("comparing manifests: deep_equal\n")); - if (!deep_equal(root, other.root)) - return false; + bfs_iter pa(ma.root), pb(mb.root); - for (node_map_t::const_iterator i = nodes.begin(); - i != nodes.end(); ++i) + while (!(pa.finished() || pb.finished())) { - // L(F("comparing manifests: node %d\n") % i->first); - node_map_t::const_iterator j = other.nodes.find(i->first); - if (j == other.nodes.end()) - return false; + node_t a = *pa; + node_t b = *pb; + + if ((a->name != b->name)) + return false; + + if (a->fancy || b->fancy) + { + if (!(a->fancy && b->fancy)) + return false; + if (a->fancy->attrs != b->fancy->attrs) + return false; + } - if (!shallow_equal(i->second, j->second)) - return false; + if (is_file_t(a) && is_file_t(b)) + { + file_t fa = downcast_to_file_t(a); + file_t fb = downcast_to_file_t(b); + if (! (fa->content == fb->content)) + return false; + } + + else if (is_dir_t(a) && is_dir_t(b)) + { + dir_t da = downcast_to_dir_t(a); + dir_t db = downcast_to_dir_t(b); + if (da->entries.size() != da->entries.size()) + return false; + } + else + return false; + + ++pa; + ++pb; } + + if (! (pa.finished() && pb.finished())) + return false; + return true; } @@ -1303,78 +1332,87 @@ attr_name const & name) = 0; }; + void change_consumer::set_heir(file_path const & dying, file_path const & heir) { L(F("set_heir('%s', '%s')\n") % dying % heir); - this->set_heir(path_vec_t(dying), - path_vec_t(heir)); + this->set_heir(path_vec_t::from_file_path(dying), + path_vec_t::from_file_path(heir)); } + void change_consumer::delete_node(file_path const & dp) { L(F("delete_node('%s')\n") % dp); - this->delete_node(path_vec_t(dp)); + this->delete_node(path_vec_t::from_file_path(dp)); } + void change_consumer::rename_node(file_path const & a, file_path const & b) { L(F("rename_node('%s', '%s')\n") % a % b); - this->rename_node(path_vec_t(a), - path_vec_t(b)); + this->rename_node(path_vec_t::from_file_path(a), + path_vec_t::from_file_path(b)); } + void change_consumer::add_dir(file_path const & dp) { L(F("add_dir('%s')\n") % dp); - this->add_dir(path_vec_t(dp)); + this->add_dir(path_vec_t::from_file_path(dp)); } + void change_consumer::add_file(file_path const & fp) { L(F("add_file('%s')\n") % fp); - this->add_file(path_vec_t(fp)); + this->add_file(path_vec_t::from_file_path(fp)); } + void change_consumer::set_sire(file_path const & newborn, file_path const & sire) { L(F("set_sire('%s', '%s')\n") % newborn % sire); - this->set_sire(path_vec_t(newborn), - path_vec_t(sire)); + this->set_sire(path_vec_t::from_file_path(newborn), + path_vec_t::from_file_path(sire)); } + void change_consumer::apply_delta(file_path const & path, file_id const & src, file_id const & dst) { L(F("apply_delta('%s', [%s], [%s])\n") % path % src % dst); - this->apply_delta(path_vec_t(path), src, dst); + this->apply_delta(path_vec_t::from_file_path(path), src, dst); } + void change_consumer::set_attr(file_path const & path, attr_name const & name, attr_val const & val) { L(F("set_attr('%s', '%s', '%s')\n") % path % name % val); - this->set_attr(path_vec_t(path), name, val); + this->set_attr(path_vec_t::from_file_path(path), name, val); } + void change_consumer::clear_attr(file_path const & path, attr_name const & name) { L(F("clear_attr('%s', '%s')\n") % path % name); - this->clear_attr(path_vec_t(path), name); + this->clear_attr(path_vec_t::from_file_path(path), name); } @@ -1404,12 +1442,14 @@ }; + void attach_detach_change_consumer::delete_node(path_vec_t const & path) { this->detach(path); } + void attach_detach_change_consumer::rename_node(path_vec_t const & src, path_vec_t const & dst) @@ -1417,6 +1457,7 @@ pending_renames.push_back(make_pair(src, dst)); } + void attach_detach_change_consumer::finalize_renames() { @@ -1491,28 +1532,20 @@ virtual void clear_attr(path_vec_t const & path, attr_name const & name); +}; - bool operator==(cset const & other) const - { - if (! (old_mfest == other.old_mfest)) - return false; - if (! (new_mfest == other.new_mfest)) - return false; - return true; - } -}; - static gen_t find_max_write_generation(dir_t d) { - gen_t m = 0; + gen_t m = d->generation; for (dfs_iter i(d); !i.finished(); ++i) if ((*i)->generation > m) - m = (*i)->generation; + m = (*i)->generation; return m; } + void cset::reset(mfest const & m) { @@ -1521,6 +1554,7 @@ write_generation = find_max_write_generation(m.root); I(write_generation != UINT64_MAX); ++write_generation; + L(F("cset write generation is %d\n") % write_generation); } @@ -1536,9 +1570,10 @@ } } + static void check_mfests_agree(mfest const & a, - mfest const & b) + mfest const & b) { check_hash_inclusion(a,b); check_hash_inclusion(b,a); @@ -1573,6 +1608,7 @@ } } + void cset::set_sire(path_vec_t const & newborn, path_vec_t const & sire) @@ -1588,6 +1624,7 @@ } } + ident_t cset::detach(path_vec_t const & path) { @@ -1606,6 +1643,7 @@ return dst->ident; } + void cset::attach(path_vec_t const & path, ident_t id) { @@ -1619,11 +1657,14 @@ } } + void cset::add_dir(path_vec_t const & dp) { dir_t new_dst_parent = new_mfest.get_containing_dir_node(dp, write_generation); dir_t new_dir = old_mfest.make_dir(); + if (new_dir->ident > new_mfest.max_ident) + new_mfest.max_ident = new_dir->ident; node_t new_dir_in_new_mfest = new_dir->shallow_copy(); new_dst_parent->add_child(dp.leaf, new_dir_in_new_mfest); @@ -1635,11 +1676,14 @@ } } + void cset::add_file(path_vec_t const & fp) { dir_t new_dst_parent = new_mfest.get_containing_dir_node(fp, write_generation); file_t new_file = old_mfest.make_file(); + if (new_file->ident > new_mfest.max_ident) + new_mfest.max_ident = new_file->ident; node_t new_file_in_new_mfest = new_file->shallow_copy(); new_dst_parent->add_child(fp.leaf, new_file_in_new_mfest); @@ -1651,6 +1695,7 @@ } } + void cset::apply_delta(path_vec_t const & path, file_id const & s, @@ -1668,6 +1713,7 @@ } } + void cset::set_attr(path_vec_t const & path, attr_name const & name, @@ -1677,6 +1723,7 @@ n->set_attr(name, val); } + void cset::clear_attr(path_vec_t const & path, string const & name) @@ -1692,6 +1739,7 @@ shared_ptr > > > node_attr_name_vec; + struct replay_record { node_pair_vec heirs_set; @@ -1705,6 +1753,7 @@ node_attr_name_vec attrs_changed; }; + static void play_back_replay_record(replay_record const & rr, mfest const & src, @@ -1994,6 +2043,7 @@ } } + static void build_replay_record(mfest const & src, mfest const & dst, @@ -2115,6 +2165,7 @@ } } + void cset::replay_changes(change_consumer & cc) const { @@ -2124,6 +2175,7 @@ play_back_replay_record(rr, old_mfest, new_mfest, cc); } + void cset::replay_inverse_changes(change_consumer & cc) const { @@ -2193,8 +2245,6 @@ // TODO: implement this algorithm! - - namespace { namespace syms @@ -2309,6 +2359,7 @@ } } + struct cset_printer : public change_consumer @@ -2334,6 +2385,7 @@ attr_val const & val); }; + void cset_printer::set_heir(path_vec_t const & dying, path_vec_t const & heir) @@ -2344,6 +2396,7 @@ printer.print_stanza(st); } + void cset_printer::delete_node(path_vec_t const & dp) { @@ -2352,6 +2405,7 @@ printer.print_stanza(st); } + void cset_printer::rename_node(path_vec_t const & src, path_vec_t const & dst) @@ -2362,6 +2416,7 @@ printer.print_stanza(st); } + void cset_printer::add_dir(path_vec_t const & dp) { @@ -2370,6 +2425,7 @@ printer.print_stanza(st); } + void cset_printer::add_file(path_vec_t const & fp) { @@ -2378,6 +2434,7 @@ printer.print_stanza(st); } + void cset_printer::set_sire(path_vec_t const & newborn, path_vec_t const & sire) @@ -2388,6 +2445,7 @@ printer.print_stanza(st); } + void cset_printer::apply_delta(path_vec_t const & path, file_id const & src, @@ -2400,6 +2458,7 @@ printer.print_stanza(st); } + void cset_printer::clear_attr(path_vec_t const & path, attr_name const & attr) @@ -2410,6 +2469,7 @@ printer.print_stanza(st); } + void cset_printer::set_attr(path_vec_t const & path, attr_name const & attr, @@ -2436,6 +2496,7 @@ cs.check_sane(); } + void write_cset(cset const & cs, data & dat) @@ -2453,20 +2514,26 @@ mfest & m) { cset c; - std::string pth, n, v; + change_consumer & cs = c; + std::string pth, ident, n, v; while (p.symp()) { if (p.symp(syms::dir)) { p.sym(); p.str(pth); - c.add_dir(file_path(pth)); + // L(F("read dir %s\n") % pth); + cs.add_dir(file_path(pth)); } else if (p.symp(syms::file)) { p.sym(); p.str(pth); - c.add_file(file_path(pth)); + p.esym(syms::content); + p.hex(ident); + // L(F("read file %s\n") % pth); + cs.add_file(file_path(pth)); + cs.apply_delta(file_path(pth), file_id(), file_id(ident)); } else break; @@ -2476,7 +2543,8 @@ p.sym(); p.str(n); p.str(v); - c.set_attr(file_path(pth), n, v); + // L(F("read attr %s : %s = %s\n") % pth % n % v); + cs.set_attr(file_path(pth), n, v); } } @@ -2484,6 +2552,7 @@ m.reset(c.new_mfest); } + void print_mfest(basic_io::printer & p, mfest const & m) @@ -2491,25 +2560,38 @@ for (dfs_iter i(m.root); !i.finished(); ++i) { node_t curr = *i; + path_vec_t pv; + m.get_path(curr, pv); + + file_path fp = pv.to_file_path(); + basic_io::stanza st; if (is_dir_t(curr)) - st.push_str_pair(syms::dir, curr->name->val); + { + // L(F("printing dir %s\n") % fp); + st.push_str_pair(syms::dir, fp()); + } else { file_t ftmp = downcast_to_file_t(curr); - st.push_str_pair(syms::file, curr->name->val); + st.push_str_pair(syms::file, fp()); st.push_hex_pair(syms::content, ftmp->content.inner()()); + // L(F("printing file %s\n") % fp); } if (curr->has_attrs()) { for (map::const_iterator j = curr->fancy->attrs.begin(); j != curr->fancy->attrs.end(); ++j) - st.push_str_triple(syms::attr, j->first, j->second); + { + // L(F("printing attr %s : %s = %s\n") % fp % j->first % j->second); + st.push_str_triple(syms::attr, j->first, j->second); + } } - } - + p.print_stanza(st); + } } + void read_mfest(data const & dat, mfest & mf) @@ -2523,6 +2605,7 @@ mf.check_sane(); } + void write_mfest(mfest const & mf, data & dat) @@ -2546,6 +2629,288 @@ file_id null_file_id; + +struct +change_automaton +{ + + change_automaton() + { + srand(0x12345678); + } + + string new_word() + { + static string wordchars = "abcdefghijlkmnopqrstuvwxyz_-ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static unsigned tick = 0; + string tmp; + do + { + tmp += wordchars[rand() % wordchars.size()]; + } + while (tmp.size() < 10 && !flip(10)); + return tmp + lexical_cast(tick++); + } + + file_id new_ident() + { + static string tab = "0123456789abcdef"; + string tmp; + tmp.reserve(constants::idlen); + for (unsigned i = 0; i < constants::idlen; ++i) + tmp += tab[rand() % tab.size()]; + return file_id(tmp); + } + + dirent_t new_entry() + { + return path_vec_t::intern_component(new_word()); + } + + bool flip(unsigned n = 2) + { + return (rand() % n) == 0; + } + + attr_name pick_attr(node_t n) + { + I(n->has_attrs()); + vector tmp; + for (map::const_iterator i = n->fancy->attrs.begin(); + i != n->fancy->attrs.end(); ++i) + { + tmp.push_back(i->first); + } + return tmp[rand() % tmp.size()]; + } + + enum action + { + add_a_node = 0, + delete_a_node = 1, + rename_a_node = 2, + apply_a_delta = 3, + set_an_attribute = 4, + clear_an_attribute = 5, + + number_of_actions = 6 + }; + + void inspect(mfest const & m, + bool & has_nonroot_nodes, + bool & has_attrs) + { + + has_nonroot_nodes = false; + has_attrs = false; + + for (bfs_iter i(m.root); !i.finished(); ++i) + { + if ((*i)->ident != m.root->ident) + has_nonroot_nodes = true; + + if ((*i)->has_attrs()) + has_attrs = true; + + if (has_nonroot_nodes + && has_attrs) + return; + } + } + + void perform_random_action(cset & c) + { + set parents; + path_vec_t pv_a, pv_b; + file_path fp_a, fp_b; + + bool has_nonroot_nodes; + bool has_attrs; + + change_consumer & cs = c; + + inspect(c.new_mfest, + has_nonroot_nodes, + has_attrs); + + vector nodes; + get_nodes(c.new_mfest, nodes); + + bool did_something = false; + while (! did_something) + { + switch (static_cast(rand() % static_cast(number_of_actions))) + { + + case add_a_node: + { + node_t n = random_node(c.new_mfest, nodes, pv_a, parents); + if (is_dir_t(n) && flip()) + { + // add a child of an existing entry + pv_a /= new_entry(); + } + else + { + // add a sibling of an existing entry + pv_a.leaf = new_entry(); + } + + fp_a = pv_a.to_file_path(); + + if (flip()) + cs.add_dir(fp_a); + else + { + cs.add_file(fp_a); + cs.apply_delta(fp_a, null_file_id, new_ident()); + } + did_something = true; + } + break; + + case delete_a_node: + { + if (has_nonroot_nodes) + { + node_t n = random_node(c.new_mfest, nodes, pv_a, parents); + + if (n->live() + && (n->ident != c.new_mfest.root->ident) + && (is_file_t(n) || (is_dir_t(n) + && downcast_to_dir_t(n)->entries.empty()))) + { + fp_a = pv_a.to_file_path(); + cs.delete_node(fp_a); + did_something = true; + } + } + break; + + } + + case rename_a_node: + { + // FIXME : cc.finalize_renames(); + + if (false && has_nonroot_nodes) + { + node_t src = random_node(c.old_mfest, nodes, pv_a, parents); + node_t dst = random_node(c.new_mfest, nodes, pv_b, parents); + + // we want to be sure we're not moving src into one of + // its own children; this is the same as saying that + // src isn't in dst's parents + + if (src->live() + && dst->live() + && (parents.find(src->ident) == parents.end())) + { + if (is_dir_t(dst)) + { + + pv_b /= new_entry(); + } + else + { + pv_b.leaf = new_entry(); + } + fp_a = pv_a.to_file_path(); + fp_b = pv_b.to_file_path(); + cs.rename_node(fp_a, fp_b); + did_something = true; + } + } + } + break; + + case apply_a_delta: + { + if (has_nonroot_nodes) + { + node_t f = random_node(c.new_mfest, nodes, pv_a, parents); + if (f->live() && is_file_t(f)) + { + cs.apply_delta(pv_a.to_file_path(), + downcast_to_file_t(f)->content, + new_ident()); + did_something = true; + } + } + } + break; + + case set_an_attribute: + { + if (has_nonroot_nodes) + { + node_t n = random_node(c.new_mfest, nodes, pv_a, parents); + if (n->ident != c.new_mfest.root->ident) + { + fp_a = pv_a.to_file_path(); + cs.set_attr(fp_a, new_word(), new_word()); + did_something = true; + } + } + } + break; + + case clear_an_attribute: + { + if (has_nonroot_nodes && has_attrs) + { + node_t n = random_node(c.new_mfest, nodes, pv_a, parents); + if (n->ident != c.new_mfest.root->ident) + { + fp_a = pv_a.to_file_path(); + if (n->has_attrs()) + { + cs.clear_attr(fp_a, pick_attr(n)); + did_something = true; + } + } + } + } + break; + + case number_of_actions: + break; + + } + } + } + + void get_nodes(mfest const & m, vector & nodes) + { + nodes.clear(); + for (bfs_iter i(m.root); !i.finished(); ++i) + nodes.push_back(*i); + } + + node_t random_node(mfest const & m, + vector & nodes, + path_vec_t & pv, + set & parents) + { + parents.clear(); + + node_t result = nodes[rand() % nodes.size()]; + + for (node_t tmp = result; tmp->ident != m.root->ident; tmp = m.get_node(tmp->parent)) + { + parents.insert(result->parent); + } + + m.get_path(result, pv); + + return result; + } + + +}; + + + static void spin_mfest(mfest const & m) { @@ -2554,13 +2919,16 @@ m1.reset(m); for (unsigned i = 0; i < 5; ++i) { + L(F("spinning %d-entry mfest (pass %d)\n") % m1.nodes.size() % i); write_mfest(m1, tmp); + L(F("wrote mfest: [[%s]]\n") % tmp); read_mfest(tmp, m2); - I(m1 == m2); + BOOST_CHECK(equal_up_to_renumbering(m1, m2)); m1.reset(m2); } } + static void spin_cset(cset const & cs) { @@ -2574,23 +2942,25 @@ { data tmp2; cset cs2; + L(F("spinning cset (pass %d)\n") % i); write_cset(cs1, tmp2); BOOST_CHECK(tmp1 == tmp2); read_cset(tmp2, cs2); - BOOST_CHECK(cs1 == cs2); - cs1 = cs2; spin_mfest(cs2.old_mfest); spin_mfest(cs2.new_mfest); + cs1 = cs2; } } + static void basic_cset_test() { try { - cset cs; + cset c; + change_consumer & cs = c; cs.add_dir(file_path("usr")); cs.add_dir(file_path("usr/bin")); cs.add_file(file_path("usr/bin/cat")); @@ -2605,7 +2975,7 @@ null_file_id, file_id(hexenc("adc83b19e793491b1c6ea0fd8b46cd9f32e592fc"))); - spin_cset(cs); + spin_cset(c); } catch (informative_failure & exn) { @@ -2617,11 +2987,27 @@ } } +static void +automaton_cset_test() +{ + mfest m1; + + change_automaton aut; + for (int i = 0; i < 1000; ++i) + { + cset cs(m1); + aut.perform_random_action(cs); + m1.reset(cs.new_mfest); + } +} + + void add_cset_tests(test_suite * suite) { I(suite); suite->add(BOOST_TEST_CASE(&basic_cset_test)); + suite->add(BOOST_TEST_CASE(&automaton_cset_test)); }