Merge lp:~abentley/bzr/unbreak-merge into lp:bzr/2.0

Proposed by Aaron Bentley
Status: Rejected
Rejected by: Aaron Bentley
Proposed branch: lp:~abentley/bzr/unbreak-merge
Merge into: lp:bzr/2.0
Diff against target: 54390 lines (+23989/-8597)
483 files modified
.bzrignore (+3/-0)
Makefile (+10/-19)
NEWS (+993/-13)
bzr (+1/-1)
bzrlib/__init__.py (+3/-8)
bzrlib/_annotator_pyx.pyx (+1/-1)
bzrlib/_bencode_pyx.pyx (+10/-2)
bzrlib/_btree_serializer_py.py (+14/-8)
bzrlib/_btree_serializer_pyx.pyx (+88/-44)
bzrlib/_chk_map_py.py (+5/-4)
bzrlib/_chk_map_pyx.pyx (+42/-19)
bzrlib/_dirstate_helpers_pyx.pyx (+1/-1)
bzrlib/_export_c_api.h (+104/-0)
bzrlib/_groupcompress_pyx.pyx (+68/-51)
bzrlib/_import_c_api.h (+189/-0)
bzrlib/_knit_load_data_pyx.pyx (+1/-1)
bzrlib/_known_graph_py.py (+91/-2)
bzrlib/_known_graph_pyx.pyx (+132/-20)
bzrlib/_patiencediff_c.c (+2/-2)
bzrlib/_readdir_pyx.pyx (+1/-1)
bzrlib/_rio_pyx.pyx (+1/-1)
bzrlib/_simple_set_pyx.pxd (+91/-0)
bzrlib/_simple_set_pyx.pyx (+592/-0)
bzrlib/_static_tuple_c.c (+945/-0)
bzrlib/_static_tuple_c.h (+116/-0)
bzrlib/_static_tuple_c.pxd (+44/-0)
bzrlib/_static_tuple_py.py (+80/-0)
bzrlib/_walkdirs_win32.pyx (+1/-1)
bzrlib/annotate.py (+2/-1)
bzrlib/benchmarks/bench_dirstate.py (+7/-7)
bzrlib/bencode.py (+4/-1)
bzrlib/branch.py (+30/-17)
bzrlib/btree_index.py (+50/-17)
bzrlib/bugtracker.py (+20/-17)
bzrlib/builtins.py (+974/-864)
bzrlib/bundle/__init__.py (+26/-29)
bzrlib/bundle/apply_bundle.py (+2/-1)
bzrlib/bzrdir.py (+69/-22)
bzrlib/check.py (+7/-7)
bzrlib/chk_map.py (+147/-36)
bzrlib/chk_serializer.py (+4/-3)
bzrlib/cleanup.py (+186/-0)
bzrlib/cmd_version_info.py (+6/-3)
bzrlib/commands.py (+58/-37)
bzrlib/commit.py (+138/-149)
bzrlib/config.py (+43/-3)
bzrlib/conflicts.py (+8/-10)
bzrlib/decorators.py (+105/-1)
bzrlib/delta.py (+2/-2)
bzrlib/diff.py (+70/-20)
bzrlib/dirstate.py (+6/-5)
bzrlib/doc_generate/autodoc_man.py (+2/-2)
bzrlib/doc_generate/autodoc_rstx.py (+43/-59)
bzrlib/errors.py (+56/-7)
bzrlib/export/dir_exporter.py (+1/-1)
bzrlib/export/zip_exporter.py (+3/-2)
bzrlib/fetch.py (+19/-6)
bzrlib/foreign.py (+51/-13)
bzrlib/globbing.py (+41/-4)
bzrlib/graph.py (+17/-2)
bzrlib/groupcompress.py (+63/-48)
bzrlib/help.py (+6/-2)
bzrlib/help_topics/__init__.py (+66/-27)
bzrlib/help_topics/en/authentication.txt (+2/-4)
bzrlib/help_topics/en/configuration.txt (+74/-1)
bzrlib/help_topics/en/conflict-types.txt (+26/-3)
bzrlib/help_topics/en/content-filters.txt (+2/-2)
bzrlib/help_topics/en/debug-flags.txt (+8/-3)
bzrlib/help_topics/en/log-formats.txt (+1/-1)
bzrlib/help_topics/en/patterns.txt (+3/-0)
bzrlib/help_topics/en/rules.txt (+9/-6)
bzrlib/help_topics/es/conflicts.txt (+43/-43)
bzrlib/hooks.py (+4/-2)
bzrlib/index.py (+57/-23)
bzrlib/info.py (+2/-1)
bzrlib/inventory.py (+66/-51)
bzrlib/knit.py (+12/-5)
bzrlib/lock.py (+21/-0)
bzrlib/lockable_files.py (+2/-2)
bzrlib/lockdir.py (+23/-16)
bzrlib/log.py (+112/-52)
bzrlib/lsprof.py (+55/-26)
bzrlib/mail_client.py (+66/-0)
bzrlib/merge.py (+462/-276)
bzrlib/merge_directive.py (+6/-8)
bzrlib/msgeditor.py (+9/-11)
bzrlib/mutabletree.py (+41/-34)
bzrlib/option.py (+15/-19)
bzrlib/osutils.py (+235/-27)
bzrlib/patches.py (+1/-1)
bzrlib/plugin.py (+90/-26)
bzrlib/plugins/launchpad/__init__.py (+45/-33)
bzrlib/plugins/launchpad/lp_api.py (+135/-0)
bzrlib/plugins/launchpad/lp_directory.py (+3/-9)
bzrlib/plugins/launchpad/lp_registration.py (+54/-18)
bzrlib/plugins/launchpad/test_lp_api.py (+101/-0)
bzrlib/plugins/launchpad/test_lp_directory.py (+145/-2)
bzrlib/plugins/launchpad/test_lp_open.py (+16/-4)
bzrlib/plugins/news_merge/README (+7/-0)
bzrlib/plugins/news_merge/__init__.py (+66/-0)
bzrlib/plugins/news_merge/news_merge.py (+94/-0)
bzrlib/plugins/news_merge/parser.py (+62/-0)
bzrlib/plugins/news_merge/tests/__init__.py (+23/-0)
bzrlib/plugins/news_merge/tests/test_news_merge.py (+29/-0)
bzrlib/progress.py (+15/-3)
bzrlib/push.py (+5/-1)
bzrlib/python-compat.h (+11/-0)
bzrlib/reconcile.py (+50/-51)
bzrlib/reconfigure.py (+1/-3)
bzrlib/registry.py (+4/-1)
bzrlib/remote.py (+98/-26)
bzrlib/repofmt/groupcompress_repo.py (+24/-9)
bzrlib/repofmt/pack_repo.py (+104/-56)
bzrlib/repofmt/weaverepo.py (+1/-1)
bzrlib/repository.py (+65/-30)
bzrlib/revision.py (+6/-3)
bzrlib/revisionspec.py (+81/-22)
bzrlib/revisiontree.py (+5/-2)
bzrlib/rio.py (+3/-1)
bzrlib/send.py (+2/-3)
bzrlib/serializer.py (+6/-2)
bzrlib/shelf_ui.py (+120/-27)
bzrlib/shellcomplete.py (+1/-1)
bzrlib/smart/branch.py (+2/-2)
bzrlib/smart/bzrdir.py (+67/-8)
bzrlib/smart/client.py (+4/-1)
bzrlib/smart/medium.py (+19/-7)
bzrlib/smart/message.py (+1/-1)
bzrlib/smart/protocol.py (+81/-17)
bzrlib/smart/repository.py (+1/-1)
bzrlib/smart/request.py (+65/-16)
bzrlib/smart/server.py (+139/-33)
bzrlib/smart/vfs.py (+10/-1)
bzrlib/static_tuple.py (+56/-0)
bzrlib/status.py (+17/-11)
bzrlib/switch.py (+9/-6)
bzrlib/tests/TestUtil.py (+13/-1)
bzrlib/tests/__init__.py (+932/-662)
bzrlib/tests/blackbox/test_add.py (+1/-16)
bzrlib/tests/blackbox/test_annotate.py (+1/-1)
bzrlib/tests/blackbox/test_bound_branches.py (+6/-12)
bzrlib/tests/blackbox/test_branch.py (+9/-10)
bzrlib/tests/blackbox/test_breakin.py (+20/-23)
bzrlib/tests/blackbox/test_cat.py (+22/-11)
bzrlib/tests/blackbox/test_checkout.py (+2/-10)
bzrlib/tests/blackbox/test_commit.py (+51/-5)
bzrlib/tests/blackbox/test_debug.py (+2/-3)
bzrlib/tests/blackbox/test_diff.py (+7/-7)
bzrlib/tests/blackbox/test_dpush.py (+105/-44)
bzrlib/tests/blackbox/test_exceptions.py (+67/-13)
bzrlib/tests/blackbox/test_export.py (+2/-2)
bzrlib/tests/blackbox/test_filesystem_cicp.py (+9/-2)
bzrlib/tests/blackbox/test_filtered_view_ops.py (+22/-21)
bzrlib/tests/blackbox/test_help.py (+18/-3)
bzrlib/tests/blackbox/test_ignore.py (+1/-2)
bzrlib/tests/blackbox/test_info.py (+9/-7)
bzrlib/tests/blackbox/test_init.py (+4/-3)
bzrlib/tests/blackbox/test_locale.py (+9/-6)
bzrlib/tests/blackbox/test_log.py (+16/-1)
bzrlib/tests/blackbox/test_ls.py (+2/-1)
bzrlib/tests/blackbox/test_merge.py (+98/-42)
bzrlib/tests/blackbox/test_mv.py (+10/-2)
bzrlib/tests/blackbox/test_non_ascii.py (+7/-7)
bzrlib/tests/blackbox/test_outside_wt.py (+33/-31)
bzrlib/tests/blackbox/test_pull.py (+1/-1)
bzrlib/tests/blackbox/test_push.py (+50/-7)
bzrlib/tests/blackbox/test_remerge.py (+2/-2)
bzrlib/tests/blackbox/test_remove.py (+9/-7)
bzrlib/tests/blackbox/test_remove_tree.py (+27/-0)
bzrlib/tests/blackbox/test_selftest.py (+13/-16)
bzrlib/tests/blackbox/test_send.py (+6/-9)
bzrlib/tests/blackbox/test_serve.py (+154/-137)
bzrlib/tests/blackbox/test_shared_repository.py (+21/-3)
bzrlib/tests/blackbox/test_shelve.py (+24/-1)
bzrlib/tests/blackbox/test_split.py (+1/-1)
bzrlib/tests/blackbox/test_switch.py (+43/-1)
bzrlib/tests/blackbox/test_too_much.py (+8/-25)
bzrlib/tests/blackbox/test_uncommit.py (+2/-2)
bzrlib/tests/blackbox/test_update.py (+141/-35)
bzrlib/tests/blackbox/test_version.py (+20/-0)
bzrlib/tests/commands/test_branch.py (+4/-4)
bzrlib/tests/commands/test_cat.py (+2/-2)
bzrlib/tests/commands/test_checkout.py (+3/-3)
bzrlib/tests/commands/test_commit.py (+2/-2)
bzrlib/tests/commands/test_init.py (+2/-2)
bzrlib/tests/commands/test_init_repository.py (+2/-2)
bzrlib/tests/commands/test_merge.py (+2/-2)
bzrlib/tests/commands/test_missing.py (+2/-2)
bzrlib/tests/commands/test_pull.py (+3/-3)
bzrlib/tests/commands/test_push.py (+3/-3)
bzrlib/tests/commands/test_update.py (+5/-2)
bzrlib/tests/features.py (+11/-19)
bzrlib/tests/ftp_server/__init__.py (+3/-3)
bzrlib/tests/ftp_server/medusa_based.py (+5/-4)
bzrlib/tests/ftp_server/pyftpdlib_based.py (+16/-12)
bzrlib/tests/http_server.py (+18/-16)
bzrlib/tests/http_utils.py (+4/-5)
bzrlib/tests/lock_helpers.py (+2/-0)
bzrlib/tests/per_branch/test_hooks.py (+3/-7)
bzrlib/tests/per_branch/test_iter_merge_sorted_revisions.py (+23/-1)
bzrlib/tests/per_branch/test_locking.py (+2/-2)
bzrlib/tests/per_branch/test_parent.py (+3/-3)
bzrlib/tests/per_branch/test_permissions.py (+4/-4)
bzrlib/tests/per_branch/test_push.py (+4/-5)
bzrlib/tests/per_bzrdir/test_bzrdir.py (+42/-14)
bzrlib/tests/per_foreign_vcs/__init__.py (+50/-0)
bzrlib/tests/per_foreign_vcs/test_branch.py (+151/-0)
bzrlib/tests/per_foreign_vcs/test_repository.py (+88/-0)
bzrlib/tests/per_interbranch/test_push.py (+1/-2)
bzrlib/tests/per_intertree/__init__.py (+3/-2)
bzrlib/tests/per_merger.py (+403/-0)
bzrlib/tests/per_pack_repository.py (+6/-8)
bzrlib/tests/per_repository/test_check.py (+6/-9)
bzrlib/tests/per_repository/test_check_reconcile.py (+3/-4)
bzrlib/tests/per_repository/test_commit_builder.py (+7/-1)
bzrlib/tests/per_repository/test_repository.py (+10/-17)
bzrlib/tests/per_repository/test_write_group.py (+1/-1)
bzrlib/tests/per_repository_reference/test_check.py (+2/-3)
bzrlib/tests/per_transport.py (+6/-2)
bzrlib/tests/per_tree/test_get_file_with_stat.py (+4/-2)
bzrlib/tests/per_tree/test_inv.py (+1/-1)
bzrlib/tests/per_tree/test_iter_search_rules.py (+1/-2)
bzrlib/tests/per_tree/test_path_content_summary.py (+28/-5)
bzrlib/tests/per_uifactory/__init__.py (+251/-0)
bzrlib/tests/per_versionedfile.py (+5/-1)
bzrlib/tests/per_workingtree/test_content_filters.py (+1/-1)
bzrlib/tests/per_workingtree/test_flush.py (+9/-1)
bzrlib/tests/per_workingtree/test_is_ignored.py (+16/-1)
bzrlib/tests/per_workingtree/test_locking.py (+11/-2)
bzrlib/tests/per_workingtree/test_set_root_id.py (+5/-0)
bzrlib/tests/per_workingtree/test_smart_add.py (+8/-2)
bzrlib/tests/per_workingtree/test_workingtree.py (+37/-16)
bzrlib/tests/script.py (+477/-0)
bzrlib/tests/ssl_certs/create_ssls.py (+8/-1)
bzrlib/tests/ssl_certs/server.crt (+26/-26)
bzrlib/tests/ssl_certs/server.csr (+23/-23)
bzrlib/tests/ssl_certs/server_with_pass.key (+50/-50)
bzrlib/tests/ssl_certs/server_without_pass.key (+49/-49)
bzrlib/tests/stub_sftp.py (+379/-46)
bzrlib/tests/test__annotator.py (+4/-31)
bzrlib/tests/test__bencode.py (+16/-43)
bzrlib/tests/test__chk_map.py (+25/-46)
bzrlib/tests/test__chunks_to_lines.py (+11/-29)
bzrlib/tests/test__dirstate_helpers.py (+28/-30)
bzrlib/tests/test__groupcompress.py (+12/-26)
bzrlib/tests/test__known_graph.py (+105/-25)
bzrlib/tests/test__rio.py (+3/-30)
bzrlib/tests/test__simple_set.py (+381/-0)
bzrlib/tests/test__static_tuple.py (+648/-0)
bzrlib/tests/test__walkdirs_win32.py (+3/-16)
bzrlib/tests/test_branch.py (+17/-0)
bzrlib/tests/test_btree_index.py (+39/-39)
bzrlib/tests/test_bundle.py (+5/-8)
bzrlib/tests/test_bzrdir.py (+14/-6)
bzrlib/tests/test_chk_map.py (+50/-77)
bzrlib/tests/test_cleanup.py (+278/-0)
bzrlib/tests/test_commit.py (+2/-2)
bzrlib/tests/test_config.py (+80/-13)
bzrlib/tests/test_conflicts.py (+92/-92)
bzrlib/tests/test_crash.py (+17/-26)
bzrlib/tests/test_decorators.py (+33/-10)
bzrlib/tests/test_diff.py (+72/-31)
bzrlib/tests/test_directory_service.py (+12/-6)
bzrlib/tests/test_errors.py (+39/-2)
bzrlib/tests/test_foreign.py (+45/-41)
bzrlib/tests/test_globbing.py (+54/-1)
bzrlib/tests/test_graph.py (+19/-1)
bzrlib/tests/test_groupcompress.py (+132/-28)
bzrlib/tests/test_hooks.py (+13/-13)
bzrlib/tests/test_http.py (+247/-117)
bzrlib/tests/test_http_response.py (+2/-3)
bzrlib/tests/test_ignores.py (+5/-1)
bzrlib/tests/test_index.py (+37/-2)
bzrlib/tests/test_info.py (+13/-17)
bzrlib/tests/test_knit.py (+5/-15)
bzrlib/tests/test_lockable_files.py (+3/-3)
bzrlib/tests/test_lockdir.py (+7/-10)
bzrlib/tests/test_log.py (+321/-414)
bzrlib/tests/test_lsprof.py (+19/-0)
bzrlib/tests/test_mail_client.py (+4/-0)
bzrlib/tests/test_merge.py (+209/-162)
bzrlib/tests/test_merge_core.py (+75/-32)
bzrlib/tests/test_msgeditor.py (+28/-3)
bzrlib/tests/test_mutabletree.py (+30/-9)
bzrlib/tests/test_osutils.py (+196/-33)
bzrlib/tests/test_patches.py (+1/-1)
bzrlib/tests/test_permissions.py (+4/-4)
bzrlib/tests/test_plugins.py (+133/-65)
bzrlib/tests/test_progress.py (+14/-1)
bzrlib/tests/test_reconfigure.py (+14/-1)
bzrlib/tests/test_registry.py (+63/-6)
bzrlib/tests/test_remote.py (+109/-24)
bzrlib/tests/test_repository.py (+170/-7)
bzrlib/tests/test_revisionspec.py (+44/-19)
bzrlib/tests/test_revisiontree.py (+9/-1)
bzrlib/tests/test_script.py (+449/-0)
bzrlib/tests/test_selftest.py (+380/-299)
bzrlib/tests/test_sftp_transport.py (+29/-47)
bzrlib/tests/test_shelf.py (+6/-0)
bzrlib/tests/test_shelf_ui.py (+116/-10)
bzrlib/tests/test_smart.py (+206/-33)
bzrlib/tests/test_smart_transport.py (+38/-29)
bzrlib/tests/test_source.py (+13/-5)
bzrlib/tests/test_ssh_transport.py (+20/-1)
bzrlib/tests/test_status.py (+1/-1)
bzrlib/tests/test_switch.py (+17/-0)
bzrlib/tests/test_trace.py (+27/-17)
bzrlib/tests/test_transform.py (+103/-11)
bzrlib/tests/test_transport.py (+275/-81)
bzrlib/tests/test_transport_log.py (+4/-5)
bzrlib/tests/test_ui.py (+165/-132)
bzrlib/tests/test_urlutils.py (+6/-2)
bzrlib/tests/test_version.py (+16/-2)
bzrlib/tests/test_win32utils.py (+170/-37)
bzrlib/tests/test_wsgi.py (+55/-7)
bzrlib/tests/test_xml.py (+34/-1)
bzrlib/tests/transport_util.py (+15/-3)
bzrlib/textui.py (+0/-40)
bzrlib/trace.py (+43/-30)
bzrlib/transform.py (+32/-6)
bzrlib/transport/__init__.py (+9/-7)
bzrlib/transport/chroot.py (+22/-112)
bzrlib/transport/decorator.py (+6/-7)
bzrlib/transport/ftp/__init__.py (+22/-34)
bzrlib/transport/ftp/_gssapi.py (+11/-41)
bzrlib/transport/http/__init__.py (+2/-2)
bzrlib/transport/http/_urllib.py (+3/-3)
bzrlib/transport/http/_urllib2_wrappers.py (+37/-12)
bzrlib/transport/http/wsgi.py (+5/-4)
bzrlib/transport/local.py (+3/-6)
bzrlib/transport/memory.py (+10/-7)
bzrlib/transport/pathfilter.py (+195/-0)
bzrlib/transport/sftp.py (+5/-328)
bzrlib/transport/ssh.py (+50/-25)
bzrlib/tree.py (+7/-4)
bzrlib/ui/__init__.py (+118/-101)
bzrlib/ui/text.py (+163/-26)
bzrlib/upgrade.py (+5/-4)
bzrlib/urlutils.py (+10/-4)
bzrlib/util/_bencode_py.py (+9/-0)
bzrlib/version.py (+6/-3)
bzrlib/version_info_formats/format_rio.py (+2/-1)
bzrlib/versionedfile.py (+63/-2)
bzrlib/win32utils.py (+151/-30)
bzrlib/workingtree.py (+56/-26)
bzrlib/workingtree_4.py (+13/-23)
bzrlib/xml4.py (+6/-4)
bzrlib/xml5.py (+5/-3)
bzrlib/xml7.py (+4/-3)
bzrlib/xml8.py (+10/-7)
bzrlib/xml_serializer.py (+8/-3)
contrib/debian/default (+20/-0)
contrib/debian/init.d (+135/-0)
doc/default.css (+4/-4)
doc/developers/HACKING.txt (+74/-60)
doc/developers/_static/bzr-doc.css (+4/-0)
doc/developers/_templates/layout.html (+5/-0)
doc/developers/add.txt (+1/-1)
doc/developers/api-versioning.txt (+2/-2)
doc/developers/apport.txt (+2/-2)
doc/developers/authentication-ring.txt (+6/-6)
doc/developers/bug-handling.txt (+62/-44)
doc/developers/bundles.txt (+1/-1)
doc/developers/case-insensitive-file-systems.txt (+1/-1)
doc/developers/colocated-branches.txt (+9/-9)
doc/developers/commit.txt (+19/-19)
doc/developers/container-format.txt (+5/-5)
doc/developers/content-filtering.txt (+1/-1)
doc/developers/contribution-quickstart.txt (+59/-0)
doc/developers/cycle.txt (+56/-58)
doc/developers/development-repo.txt (+2/-2)
doc/developers/diff.txt (+1/-1)
doc/developers/directory-fingerprints.txt (+33/-33)
doc/developers/ec2.txt (+16/-16)
doc/developers/improved_chk_index.txt (+6/-6)
doc/developers/incremental-push-pull.txt (+5/-5)
doc/developers/index-plain.txt (+9/-9)
doc/developers/index.txt (+13/-2)
doc/developers/integration.txt (+2/-2)
doc/developers/inventory.txt (+10/-10)
doc/developers/last-modified.txt (+6/-6)
doc/developers/network-protocol.txt (+18/-16)
doc/developers/overview.txt (+5/-5)
doc/developers/performance-use-case-analysis.txt (+5/-5)
doc/developers/planned-change-integration.txt (+5/-5)
doc/developers/planned-performance-changes.txt (+1/-1)
doc/developers/plans.txt (+1/-1)
doc/developers/plugin-api.txt (+6/-6)
doc/developers/ppa.txt (+59/-33)
doc/developers/principles.txt (+62/-0)
doc/developers/process.txt (+5/-5)
doc/developers/profiling.txt (+1/-1)
doc/developers/releasing.txt (+103/-56)
doc/developers/repository-stream.txt (+1/-1)
doc/developers/repository.txt (+8/-8)
doc/developers/revert.txt (+1/-1)
doc/developers/specifications.txt (+1/-1)
doc/developers/status.txt (+2/-2)
doc/developers/testing.txt (+147/-16)
doc/developers/tortoise-strategy.txt (+9/-9)
doc/developers/update.txt (+5/-5)
doc/developers/win32_build_setup.txt (+213/-0)
doc/en/_templates/index.html (+9/-5)
doc/en/admin-guide/advanced.txt (+164/-0)
doc/en/admin-guide/backup.txt (+204/-0)
doc/en/admin-guide/code-browsing.txt (+128/-0)
doc/en/admin-guide/hooks-plugins.txt (+310/-0)
doc/en/admin-guide/index-plain.txt (+23/-0)
doc/en/admin-guide/index.txt (+25/-114)
doc/en/admin-guide/integration.txt (+14/-0)
doc/en/admin-guide/introduction.txt (+55/-0)
doc/en/admin-guide/migration.txt (+73/-0)
doc/en/admin-guide/other-setups.txt (+60/-0)
doc/en/admin-guide/security.txt (+116/-0)
doc/en/admin-guide/simple-setups.txt (+93/-0)
doc/en/admin-guide/upgrade.txt (+77/-0)
doc/en/conf.py (+1/-2)
doc/en/index.txt (+1/-0)
doc/en/mini-tutorial/index.txt (+35/-33)
doc/en/tutorials/centralized_workflow.txt (+6/-6)
doc/en/tutorials/tutorial.txt (+78/-34)
doc/en/tutorials/using_bazaar_with_launchpad.txt (+1/-1)
doc/en/user-guide/adv_merging.txt (+23/-0)
doc/en/user-guide/branching_a_project.txt (+6/-6)
doc/en/user-guide/bug_trackers.txt (+1/-1)
doc/en/user-guide/configuring_bazaar.txt (+1/-1)
doc/en/user-guide/controlling_registration.txt (+1/-1)
doc/en/user-guide/distributed_intro.txt (+1/-1)
doc/en/user-guide/getting_help.txt (+9/-7)
doc/en/user-guide/hooks.txt (+6/-4)
doc/en/user-guide/http_smart_server.txt (+8/-8)
doc/en/user-guide/index-plain.txt (+1/-1)
doc/en/user-guide/index.txt (+1/-1)
doc/en/user-guide/introducing_bazaar.txt (+3/-3)
doc/en/user-guide/plugins.txt (+9/-21)
doc/en/user-guide/publishing_a_branch.txt (+0/-1)
doc/en/user-guide/recording_changes.txt (+1/-1)
doc/en/user-guide/resolving_conflicts.txt (+1/-1)
doc/en/user-guide/reviewing_changes.txt (+10/-2)
doc/en/user-guide/sending_changes.txt (+2/-2)
doc/en/user-guide/server.txt (+17/-7)
doc/en/user-guide/setting_up_email.txt (+2/-2)
doc/en/user-guide/shared_repository_layouts.txt (+22/-22)
doc/en/user-guide/shelving_changes.txt (+6/-6)
doc/en/user-guide/specifying_revisions.txt (+12/-6)
doc/en/user-guide/stacked.txt (+1/-1)
doc/en/user-guide/svn_plugin.txt (+3/-5)
doc/en/user-guide/undoing_mistakes.txt (+1/-1)
doc/en/user-guide/version_info.txt (+1/-1)
doc/en/user-guide/web_browsing.txt (+2/-2)
doc/en/user-guide/writing_a_plugin.txt (+8/-5)
doc/en/user-guide/zen.txt (+1/-1)
doc/es/index.txt (+1/-1)
doc/es/mini-tutorial/index.txt (+22/-22)
doc/es/user-guide/index-plain.txt (+2/-2)
doc/es/user-guide/index.txt (+2/-2)
doc/es/user-guide/version_info.txt (+17/-17)
doc/index.es.txt (+4/-4)
doc/index.ru.txt (+7/-7)
doc/ja/tutorials/using_bazaar_with_launchpad.txt (+1/-1)
doc/ja/upgrade-guide/data_migration.txt (+1/-1)
doc/ja/user-guide/entering_commands.txt (+1/-1)
doc/ja/user-guide/http_smart_server.txt (+9/-9)
doc/ja/user-guide/introducing_bazaar.txt (+2/-2)
doc/ja/user-guide/setting_up_email.txt (+2/-2)
doc/ja/user-guide/version_info.txt (+3/-3)
doc/ja/user-reference/index.txt (+148/-148)
doc/ru/index.txt (+4/-4)
doc/ru/mini-tutorial/index.txt (+10/-10)
doc/ru/tutorials/centralized_workflow.txt (+4/-4)
doc/ru/tutorials/tutorial.txt (+2/-2)
doc/ru/tutorials/using_bazaar_with_launchpad.txt (+1/-1)
doc/ru/user-guide/branching_a_project.txt (+1/-1)
doc/ru/user-guide/index-plain.txt (+1/-1)
doc/ru/user-guide/index.txt (+1/-1)
doc/ru/user-guide/introducing_bazaar.txt (+2/-2)
doc/ru/user-guide/specifying_revisions.txt (+1/-1)
doc/ru/user-guide/zen.txt (+6/-6)
setup.py (+12/-3)
tools/packaging/build-packages.sh (+1/-1)
tools/packaging/update-changelogs.sh (+12/-7)
tools/packaging/update-control.sh (+31/-0)
tools/packaging/update-packaging-branches.sh (+14/-6)
To merge this branch: bzr merge lp:~abentley/bzr/unbreak-merge
Reviewer Review Type Date Requested Status
John A Meinel Needs Fixing
Review via email: mp+19556@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

Hi all,

This branch fixes merge so that it works when the this_tree is not a
working tree. The ConfigurableFileMerger was using merger.this_tree.branch, to
retrieve the configuration, but it should actually use merger.this_branch.

The test simply shows that generating a preview transform with a RevisionTree
as the this_tree does not raise an exception.

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Aaron Bentley wrote:
> Aaron Bentley has proposed merging lp:~abentley/bzr/unbreak-merge into lp:bzr/2.0.
>
> Requested reviews:
> bzr-core (bzr-core)
>
>
> Hi all,
>
> This branch fixes merge so that it works when the this_tree is not a
> working tree. The ConfigurableFileMerger was using merger.this_tree.branch, to
> retrieve the configuration, but it should actually use merger.this_branch.
>
> The test simply shows that generating a preview transform with a RevisionTree
> as the this_tree does not raise an exception.The attached diff has been truncated due to its size.
>

 review: needs_fixing

I don't know why you proposed it into 2.0, given that this only exists
in 2.1. I'm guessing you just need to resubmit vs the 2.1 branch.
The discussion seems fine.

John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkt8hfMACgkQJdeBCYSNAAM+6gCgz6ys8C4wk4VP/1IUcKjU0a1D
MvwAoJHN0QnqkQT6ojX/SYmOXGFbzoG2
=6RG9
-----END PGP SIGNATURE-----

review: Needs Fixing
Revision history for this message
Aaron Bentley (abentley) wrote :

Robert told me to JFDI, so it's playing in PQM (against 2.1) now.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2009-09-09 11:43:10 +0000
3+++ .bzrignore 2010-02-18 00:00:52 +0000
4@@ -58,6 +58,9 @@
5 bzrlib/_known_graph_pyx.c
6 bzrlib/_readdir_pyx.c
7 bzrlib/_rio_pyx.c
8+bzrlib/_simple_set_pyx.c
9+bzrlib/_simple_set_pyx.h
10+bzrlib/_simple_set_pyx_api.h
11 bzrlib/_walkdirs_win32.c
12 # built extension modules
13 bzrlib/_*.dll
14
15=== modified file 'Makefile'
16--- Makefile 2009-11-18 04:29:56 +0000
17+++ Makefile 2010-02-18 00:00:52 +0000
18@@ -1,4 +1,4 @@
19-# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
20+# Copyright (C) 2005-2010 Canonical Ltd
21 #
22 # This program is free software; you can redistribute it and/or modify
23 # it under the terms of the GNU General Public License as published by
24@@ -40,8 +40,6 @@
25
26 check-nodocs: extensions
27 $(PYTHON) -Werror -O ./bzr selftest -1v $(tests)
28- @echo "Running all tests with no locale."
29- LC_CTYPE= LANG=C LC_ALL= ./bzr selftest -1v $(tests) 2>&1 | sed -e 's/^/[ascii] /'
30
31 # Run Python style checker (apt-get install pyflakes)
32 #
33@@ -201,7 +199,6 @@
34
35 # translate txt docs to html
36 derived_txt_files = \
37- doc/en/user-reference/bzr_man.txt \
38 doc/en/release-notes/NEWS.txt
39 txt_all = \
40 doc/en/tutorials/tutorial.txt \
41@@ -214,6 +211,7 @@
42 doc/ja/tutorials/centralized_workflow.txt \
43 $(wildcard doc/*/mini-tutorial/index.txt) \
44 $(wildcard doc/*/user-guide/index-plain.txt) \
45+ doc/en/admin-guide/index-plain.txt \
46 $(wildcard doc/es/guia-usario/*.txt) \
47 $(derived_txt_files) \
48 doc/en/upgrade-guide/index.txt \
49@@ -223,7 +221,8 @@
50 doc/en/user-guide/index.txt \
51 doc/es/user-guide/index.txt \
52 doc/ja/user-guide/index.txt \
53- doc/ru/user-guide/index.txt
54+ doc/ru/user-guide/index.txt \
55+ doc/en/admin-guide/index.txt
56 txt_files = $(filter-out $(txt_nohtml), $(txt_all))
57 htm_files = $(patsubst %.txt, %.html, $(txt_files))
58
59@@ -281,6 +280,9 @@
60 #doc/ru/user-guide/index.html: $(wildcard $(addsuffix /*.txt, doc/ru/user-guide))
61 # $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@
62 #
63+doc/en/admin-guide/index-plain.html: $(wildcard $(addsuffix /*.txt, doc/en/admin-guide))
64+ $(rst2html) --stylesheet=../../default.css $(dir $@)index-plain.txt $@
65+
66 doc/developers/%.html: doc/developers/%.txt
67 $(rst2html) --stylesheet=../default.css $< $@
68
69@@ -293,9 +295,6 @@
70 %.html: %.txt
71 $(rst2html) --stylesheet=../../default.css $< $@
72
73-doc/en/user-reference/bzr_man.txt: $(MAN_DEPENDENCIES)
74- $(PYTHON) tools/generate_docs.py -o $@ rstx
75-
76 doc/en/release-notes/NEWS.txt: NEWS
77 $(PYTHON) -c "import shutil; shutil.copyfile('$<', '$@')"
78
79@@ -409,7 +408,7 @@
80
81 .PHONY: dist dist-upload-escudero check-dist-tarball
82
83-# build a distribution tarball and zip file.
84+# build a distribution source tarball
85 #
86 # this method of copying the pyrex generated files is a bit ugly; it would be
87 # nicer to generate it from distutils.
88@@ -419,15 +418,12 @@
89 expbasedir=`mktemp -t -d tmp_bzr_dist.XXXXXXXXXX` && \
90 expdir=$$expbasedir/bzr-$$version && \
91 tarball=$$PWD/../bzr-$$version.tar.gz && \
92- zipball=$$PWD/../bzr-$$version.zip && \
93 $(MAKE) clean && \
94 $(MAKE) && \
95 bzr export $$expdir && \
96- cp bzrlib/*.c $$expdir/bzrlib/. && \
97+ cp bzrlib/*.c bzrlib/*.h $$expdir/bzrlib/. && \
98 tar cfz $$tarball -C $$expbasedir bzr-$$version && \
99- (cd $$expbasedir && zip -r $$zipball bzr-$$version) && \
100 gpg --detach-sign $$tarball && \
101- gpg --detach-sign $$zipball && \
102 rm -rf $$expbasedir
103
104 # run all tests in a previously built tarball
105@@ -445,15 +441,10 @@
106 dist-upload-escudero:
107 version=`./bzr version --short` && \
108 tarball=../bzr-$$version.tar.gz && \
109- zipball=../bzr-$$version.zip && \
110- scp $$zipball $$zipball.sig $$tarball $$tarball.sig \
111+ scp $$tarball $$tarball.sig \
112 escudero.ubuntu.com:/srv/bazaar.canonical.com/www/releases/src \
113 && \
114 echo verifying over http... && \
115- curl http://bazaar-vcs.org/releases/src/bzr-$$version.zip \
116- | diff -s - $$zipball && \
117- curl http://bazaar-vcs.org/releases/src/bzr-$$version.zip.sig \
118- | diff -s - $$zipball.sig
119 curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz \
120 | diff -s - $$tarball && \
121 curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz.sig \
122
123=== modified file 'NEWS'
124--- NEWS 2010-02-12 06:00:33 +0000
125+++ NEWS 2010-02-18 00:00:52 +0000
126@@ -5,6 +5,348 @@
127 .. contents:: List of Releases
128 :depth: 1
129
130+
131+bzr 2.1.1
132+#########
133+
134+:2.1.1: not released yet
135+
136+Bug Fixes
137+*********
138+
139+* Register SIGWINCH handler only when creating a ``TextUIFactory``; avoids
140+ problems importing bzrlib from a non-main thread.
141+ (Elliot Murphy, #521989)
142+
143+* Standardize the error handling when creating a new ``StaticTuple``
144+ (problems will raise TypeError). (Matt Nordhoff, #457979)
145+
146+* Merge correctly when this_tree is not a WorkingTree. (Aaron Bentley)
147+
148+
149+bzr 2.1.0
150+#########
151+
152+:Codename: Strasbourg
153+:2.1.0: 2010-02-11
154+
155+This release marks our second long-term-stable series. The Bazaar team
156+has decided that we will continue to make bugfix-only 2.0.x and 2.1.x
157+releases, along with 2.2 development releases.
158+
159+This is a fairly incremental update, focusing on polish and bugfixing.
160+There are no changes for supported disk formats. Key updates include
161+reduced memory consumption for many operations, a new per-file merge
162+hook, ignore patterns can now include '!' to exclude files, globbing
163+support for all commands on Windows, and support for addressing home
164+directories via ``bzr+ssh://host/~/`` syntax.
165+
166+Users are encouraged to upgrade from the 2.0 stable series.
167+
168+Bug Fixes
169+*********
170+
171+* Don't require testtools to use sftp.
172+ (Vincent Ladeuil, #516183)
173+
174+* Fix "AttributeError in Inter1and2Helper" during fetch.
175+ (Martin Pool, #513432)
176+
177+* Ignore ``KeyError`` from ``remove_index`` during ``_abort_write_group``
178+ in a pack repository, which can happen harmlessly if the abort occurs during
179+ finishing the write group. Also use ``bzrlib.cleanup`` so that any
180+ other errors that occur while aborting the individual packs won't be
181+ hidden by secondary failures when removing the corresponding indices.
182+ (Andrew Bennetts, #423015)
183+
184+* Using the ``bzrlib.chk_map`` module from within multiple threads at the
185+ same time was broken due to race conditions with a module level page
186+ cache. This shows up as a KeyError in the ``bzrlib.lru_cache`` code with
187+ ``bzrlib.chk_map`` in the backtrace, and can be triggered without using
188+ the same high level objects such as ``bzrlib.repository.Repository``
189+ from different threads. chk_map now uses a thread local cache which may
190+ increase memory pressure on processes using threads.
191+ (Robert Collins, John Arbash Meinel, #514090)
192+
193+* The new ``merge_file_content`` should now be ok with tests to avoid
194+ regressions.
195+ (Vincent Ladeuil, #515597)
196+
197+Internals
198+*********
199+
200+* Use ``bzrlib.cleanup`` rather than less robust ``try``/``finally``
201+ blocks in several places in ``bzrlib.merge``. This avoids masking prior
202+ errors when errors like ``ImmortalPendingDeletion`` occur during cleanup
203+ in ``do_merge``.
204+ (Andrew Bennetts, #517275)
205+
206+API Changes
207+***********
208+
209+* The ``remove_index`` method of
210+ ``bzrlib.repofmt.pack_repo.AggregateIndex`` no longer takes a ``pack``
211+ argument. This argument was always ignored.
212+ (Andrew Bennetts, #423015)
213+
214+bzr 2.1.0rc2
215+############
216+
217+:Codename: after the bubbles
218+:2.1.0rc2: 2010-01-29
219+
220+This is a quick-turn-around to update a small issue with our new per-file
221+merge hook. We expect no major changes from this to the final 2.1.0.
222+
223+API Changes
224+***********
225+
226+* The new ``merge_file_content`` hook point has been altered to provide a
227+ better API where state for extensions can be stored rather than the
228+ too-simple function based approach. This fixes a performance regression
229+ where branch configuration would be parsed per-file during merge. As
230+ part of this the included news_merger has been refactored into a base
231+ helper class ``bzrlib.merge.ConfigurableFileMerger``.
232+ (Robert Collins, John Arbash Meinel, #513822)
233+
234+
235+bzr 2.1.0rc1
236+############
237+
238+:Codename: the 'new' stable
239+:2.1.0rc1: 2009-01-21
240+
241+This is the first stable release candidate for Bazaar's 2.1 series. From
242+this point onwards, the 2.1 series will be considered stable (as the 2.0
243+series) and only bugfixes are expected to be incorporated. The dozen or so
244+bugfixes in the 2.0.4 release are also included in this release (along
245+with more than 15 more bugfixes). Some of the interesting features are
246+support for per-file merge hooks, ``bzr unshelve --preview``, support
247+for using ! in ignore files to exclude files from being ignored, a small
248+memory leak was squashed, and many ``ObjectNotLocked`` errors were fixed.
249+This looks to be a very good start for a new stable series.
250+
251+
252+New Features
253+************
254+
255+* Add bug information to log output when available.
256+ (Neil Martinsen-Burrell, Guillermo Gonzalez, #251729)
257+
258+* Added ``merge_file_content`` hook point to ``Merger``, allowing plugins
259+ to register custom merge logic, e.g. to provide smarter merging for
260+ particular files.
261+
262+* Bazaar now includes the ``news_merge`` plugin. It is disabled by
263+ default, to enable it add a ``news_merge_files`` option to your
264+ configuration. Consult ``bzr help news_merge`` for more information.
265+ (Andrew Bennetts)
266+
267+* ``bzr branch`` now takes a ``--bind`` option. This lets you
268+ branch and bind all in one command. (Ian Clatworthy)
269+
270+* ``bzr switch`` now takes a ``--revision`` option, to allow switching to
271+ a specific revision of a branch. (Daniel Watkins, #183559)
272+
273+* ``bzr unshelve --preview`` can now be used to show how a patch on the
274+ shelf would be applied to the working tree.
275+ (Guilherme Salgado, #308122)
276+
277+* ``bzr update`` now takes a ``--revision`` argument. This lets you
278+ change the revision of the working tree to any revision in the
279+ ancestry of the current or master branch. (Matthieu Moy, Mark Hammond,
280+ Martin Pool, #45719)
281+
282+* ``-Dbytes`` can now be used to display the total number of bytes
283+ transferred for the current command. This information is always logged
284+ to ``.bzr.log`` for later inspection. (John Arbash Meinel)
285+
286+* New ignore patterns. Patterns prefixed with '!' are exceptions to
287+ ignore patterns and take precedence over regular ignores. Such
288+ exceptions are used to specify files that should be versioned which
289+ would otherwise be ignored. Patterns prefixed with '!!' act as regular
290+ ignore patterns, but have highest precedence, even over the '!'
291+ exception patterns. (John Whitley, #428031)
292+
293+* The ``supress_warnings`` configuration option has been introduced to disable
294+ various warnings (it currently only supports the ``format_deprecation``
295+ warning). The new option can be set in any of the following locations:
296+ ``bazaar.conf``, ``locations.conf`` and/or ``branch.conf``.
297+ (Ted Gould, Matthew Fuller, Vincent Ladeuil)
298+
299+Bug Fixes
300+*********
301+
302+* Always show a message if an OS error occurs while trying to run a
303+ user-specified commit message editor.
304+ (Martin Pool, #504842)
305+
306+* ``bzr diff`` will now use the epoch when it is unable to determine
307+ the timestamp of a file, if the revision it was introduced in is a
308+ ghost. (Jelmer Vernooij, #295611)
309+
310+* ``bzr switch -b`` can now create branches that are located using directory
311+ services such as ``lp:``, even when the branch name doesn't contain a
312+ '/'. (Neil Martinsen-Burrell, #495263)
313+
314+* ``bzr unshelve`` has improved messages about what it is doing.
315+ (Neil Martinsen-Burrell, #496917)
316+
317+* Concurrent autopacking is more resilient to already-renamed pack files.
318+ If we find that a file we are about to obsolete is already obsoleted, we
319+ do not try to rename it, and we leave the file in ``obsolete_packs``.
320+ The code is also fault tolerant if a file goes missing, assuming that
321+ another process already removed the file.
322+ (John Arbash Meinel, Gareth White, #507557)
323+
324+* Fix "Too many concurrent requests" in reconcile when network connection
325+ fails. (Andrew Bennetts, #503878)
326+
327+* Fixed a side effect mutation of ``RemoteBzrDirFormat._network_name``
328+ that caused some tests to fail when run in a non-default order.
329+ Probably no user impact. (Martin Pool, #504102)
330+
331+* Fixed ``ObjectNotLocked`` error in ``bzr cat -rbranch:../foo FILE``.
332+ (Andrew Bennetts, #506274)
333+
334+* FTP transports support Unicode paths by encoding/decoding them as utf8.
335+ (Vincent Ladeuil, #472161)
336+
337+* Listen to the SIGWINCH signal to update the terminal width.
338+ (Vincent Ladeuil, #316357)
339+
340+* Progress bars are now hidden when ``--quiet`` is given.
341+ (Martin Pool, #320035)
342+
343+* ``SilentUIFactory`` now supports ``make_output_stream`` and discards
344+ whatever is written to it. This un-breaks some plugin tests that
345+ depended on this behaviour.
346+ (Martin Pool, #499757)
347+
348+* When operations update the working tree, all affected files should end
349+ up with the same mtime. (eg. when versioning a generated file, if you
350+ update the source and the generated file together, the generated file
351+ should appear up-to-date.)
352+ (John Arbash Meinel, Martin <gzlist>, #488724)
353+
354+Improvements
355+************
356+
357+* Added ``add_cleanup`` and ``cleanup_now`` to ``bzrlib.command.Command``.
358+ All the builtin commands now use ``add_cleanup`` rather than
359+ ``try``/``finally`` blocks where applicable as it is simpler and more
360+ robust. (Andrew Bennetts)
361+
362+* All except a small number of storage formats are now hidden, making
363+ the help for numerous commands far more digestible. (Ian Clatworthy)
364+
365+* Attempts to open a shared repository as a branch (e.g. ``bzr branch
366+ path/to/repo``) will now include "location is a repository" as a hint in
367+ the error message. (Brian de Alwis, Andrew Bennetts, #440952)
368+
369+* Push will now inform the user when they are trying to push to a foreign
370+ VCS for which roundtripping is not supported, and will suggest them to
371+ use dpush. (Jelmer Vernooij)
372+
373+* The version of bzr being run is now written to the log file.
374+ (__monty__, #257170)
375+
376+* Transport network activity indicator is shown more of the time when
377+ Bazaar is doing network IO.
378+ (Martin Pool)
379+
380+Documentation
381+*************
382+
383+* Add documentation on creating merges with more than one parent.
384+ (Neil Martinsen-Burrell, #481526)
385+
386+* Better explain the --uncommitted option of merge.
387+ (Neil Martinsen-Burrell, #505088)
388+
389+* Improve discussion of pending merges in the documentation for
390+ ``revert``. (Neil Martinsen-Burrell, #505093)
391+
392+* Improved help for ``bzr send``.
393+ (Martin Pool, Bojan Nikolic)
394+
395+* There is a System Administrator's Guide in ``doc/en/admin-guide``,
396+ including discussions of installation, relevant plugins, security and
397+ backup. (Neil Martinsen-Burrell)
398+
399+* The ``conflicts`` help topic has been renamed to ``conflict-types``.
400+ (Ian Clatworthy)
401+
402+* The User Reference is now presented as a series of topics.
403+ Many of the included topics have link and format tweaks applied.
404+ (Ian Clatworthy)
405+
406+API Changes
407+***********
408+
409+* Added ``cachedproperty`` decorator to ``bzrlib.decorators``.
410+ (Andrew Bennetts)
411+
412+* Many test features were renamed from ``FooFeature`` to ``foo_feature``
413+ to be consistent with instances being lower case and classes being
414+ CamelCase. For the features that were more likely to be used, we added a
415+ deprecation thunk, but not all. (John Arbash Meinel)
416+
417+* Merger classes (such as ``Merge3Merger``) now expect a ``this_branch``
418+ parameter in their constructors, and provide ``this_branch`` as an
419+ attribute. (Andrew Bennetts)
420+
421+* The Branch hooks pre_change_branch_tip no longer masks exceptions raised
422+ by plugins - the original exceptions are now preserved. (Robert Collins)
423+
424+* The Transport ``Server.tearDown`` method is now renamed to
425+ ``stop_server`` and ``setUp`` to ``start_server`` for consistency with
426+ our normal naming pattern, and to avoid confusion with Python's
427+ ``TestCase.tearDown``. (Martin Pool)
428+
429+* ``WorkingTree.update`` implementations must now accept a ``revision``
430+ parameter.
431+
432+Internals
433+*********
434+
435+* Added ``BzrDir.open_branchV3`` smart server request, which can receive
436+ a string of details (such as "location is a repository") as part of a
437+ ``nobranch`` response. (Andrew Bennetts, #440952)
438+
439+* New helper osutils.UnicodeOrBytesToBytesWriter which encodes unicode
440+ objects but passes str objects straight through. This is used for
441+ selftest but may be useful for diff and other operations that generate
442+ mixed output. (Robert Collins)
443+
444+* New exception ``NoRoundtrippingSupport``, for use by foreign branch
445+ plugins. (Jelmer Vernooij)
446+
447+Testing
448+*******
449+
450+* ``bzrlib.tests.permute_for_extension`` is a helper that simplifies
451+ running all tests in the current module, once against a pure python
452+ implementation, and once against an extension (pyrex/C) implementation.
453+ It can be used to dramatically simplify the implementation of
454+ ``load_tests``. (John Arbash Meinel)
455+
456+* ``bzrlib.tests.TestCase`` now subclasses ``testtools.testcase.TestCase``.
457+ This permits features in testtools such as getUniqueInteger and
458+ getUniqueString to be used. Because of this, testtools version 0.9.2 or
459+ newer is now a dependency to run bzr selftest. Running with versions of
460+ testtools less than 0.9.2 will cause bzr to error while loading the test
461+ suite. (Robert Collins)
462+
463+* Shell-like tests now support the command "mv" for moving files. The
464+ syntax for ``mv file1 file2``, ``mv dir1 dir2`` and ``mv file dir`` is
465+ supported. (Neil Martinsen-Burrell)
466+
467+* The test progress bar no longer distinguishes tests that 'errored' from
468+ tests that 'failed' - they're all just failures.
469+ (Martin Pool)
470+
471+
472 bzr 2.0.5 (not released yet)
473 ############################
474
475@@ -111,6 +453,190 @@
476 bug #495023. (John Arbash Meinel)
477
478
479+bzr 2.1.0b4
480+###########
481+
482+:Codename: san francisco airport
483+:2.1.0b4: 2009-12-14
484+
485+The fourth beta release in the 2.1 series brings with it a significant
486+number of bugfixes (~20). The test suite is once again (finally) "green"
487+on Windows, and should remain that way for future releases. There are a
488+few performance related updates (faster upgrade and log), and several UI
489+tweaks. There has also been a significant number of tweaks to the runtime
490+documentation. 2.1.0b4 include everything from the 2.0.3 release.
491+
492+
493+Compatibility Breaks
494+********************
495+
496+* The BZR_SSH environmental variable may now be set to the path of a secure
497+ shell client. If currently set to the value ``ssh`` it will now guess the
498+ vendor of the program with that name, to restore the old behaviour that
499+ indicated the SSH Corporation client use ``sshcorp`` instead as the magic
500+ string. (Martin <gzlist@googlemail.com>, #176292)
501+
502+New Features
503+************
504+
505+* ``bzr commit`` now has a ``--commit-time`` option.
506+ (Alexander Sack, #459276)
507+
508+* ``-Dhpss`` now increases logging done when run on the bzr server,
509+ similarly to how it works on the client. (John Arbash Meinel)
510+
511+* New option ``bzr unshelve --keep`` applies the changes and leaves them
512+ on the shelf. (Martin Pool, Oscar Fuentes, #492091)
513+
514+* The ``BZR_COLUMNS`` envrionment variable can be set to force bzr to
515+ respect a given terminal width. This can be useful when output is
516+ redirected or in obscure cases where the default value is not
517+ appropriate. Pagers can use it to get a better control of the line
518+ lengths.
519+ (Vincent Ladeuil)
520+
521+* The new command ``bzr lp-mirror`` will request that Launchpad update its
522+ mirror of a local branch. This command will only function if launchpadlib
523+ is installed.
524+ (Jonathan Lange)
525+
526+
527+Bug Fixes
528+*********
529+
530+* After renaming a file, the dirstate could accidentally reference
531+ ``source\\path`` rather than ``source/path`` on Windows. This might be a
532+ source of some dirstate-related failures. (John Arbash Meinel)
533+
534+* ``bzr commit`` now detects commit messages that looks like file names
535+ and issues a warning.
536+ (Gioele Barabucci, #73073)
537+
538+* ``bzr ignore /`` no longer causes an IndexError. (Gorder Tyler, #456036)
539+
540+* ``bzr log -n0 -rN`` should not return revisions beyond its merged revisions.
541+ (#325618, #484109, Marius Kruger)
542+
543+* ``bzr merge --weave`` and ``--lca`` will now create ``.BASE`` files for
544+ files with conflicts (similar to ``--merge3``). The contents of the file
545+ is a synthesis of all bases used for the merge.
546+ (John Arbash Meinel, #40412)
547+
548+* ``bzr mv --quiet`` really is quiet now. (Gordon Tyler, #271790)
549+
550+* ``bzr serve`` is more clear about the risk of supplying --allow-writes.
551+ (Robert Collins, #84659)
552+
553+* ``bzr serve --quiet`` really is quiet now. (Gordon Tyler, #252834)
554+
555+* Fix bug with redirected URLs over authenticated HTTP.
556+ (Glen Mailer, Neil Martinsen-Burrell, Vincent Ladeuil, #395714)
557+
558+* Interactive merge doesn't leave branch locks behind. (Aaron Bentley)
559+
560+* Lots of bugfixes for the test suite on Windows. We should once again
561+ have a test suite with no failures on Windows. (John Arbash Meinel)
562+
563+* ``osutils.terminal_width()`` obeys the BZR_COLUMNS environment
564+ variable but returns None if the terminal is not a tty (when output is
565+ redirected for example). Also fixes its usage under OSes that doesn't
566+ provide termios.TIOCGWINSZ. Make sure the corresponding tests runs on
567+ windows too.
568+ (Joke de Buhr, Vincent Ladeuil, #353370, #62539)
569+ (John Arbash Meinel, Vincent Ladeuil, #492561)
570+
571+* Terminate ssh subprocesses when no references to them remain, fixing
572+ subprocess and file descriptor leaks. (Andrew Bennetts, #426662)
573+
574+* The ``--hardlink`` option of ``bzr branch`` and ``bzr checkout`` now
575+ works for 2a format trees. Only files unaffected by content filters
576+ will be hardlinked. (Andrew Bennetts, #408193)
577+
578+* The new glob expansion on Windows would replace all ``\`` characters
579+ with ``/`` even if it there wasn't a glob to expand, the arg was quoted,
580+ etc. Now only change slashes if there is something being glob expanded.
581+ (John Arbash Meinel, #485771)
582+
583+* Use our faster ``KnownGraph.heads()`` functionality when computing the
584+ new rich-root heads. This can cut a conversion time in half (mysql from
585+ 13.5h => 6.2h) (John Arbash Meinel, #487632)
586+
587+* When launching a external diff tool via bzr diff --using, temporary files
588+ are no longer created, rather, the path to the file in the working tree is
589+ passed to the external diff tool. This allows the file to be edited if the
590+ diff tool provides for this. (Gary van der Merwe, #490738)
591+
592+* The launchpad-open command can now be used from a subdirectory of a
593+ branch, not just from the root of the branch.
594+ (Neil Martinsen-Burrell, #489102)
595+
596+
597+Improvements
598+************
599+
600+* ``bzr log`` is now faster. (Ian Clatworthy)
601+
602+* ``bzr update`` provides feedback on which branch it is up to date with.
603+ (Neil Martinsen-Burrell)
604+
605+* ``bzr upgrade`` from pre-2a to 2a can be significantly faster (4x).
606+ For details see the xml8 patch and heads() improvements.
607+ (John Arbash Meinel)
608+
609+* ``bzrlib.urlutils.local_path_from_url`` now accepts
610+ 'file://localhost/' as well as 'file:///' URLs on POSIX. (Michael
611+ Hudson)
612+
613+* The progress bar now shows only a spinner and per-operation counts,
614+ not an overall progress bar. The previous bar was often not correlated
615+ with real overall operation progress, either because the operations take
616+ nonlinear time, or because at the start of the operation Bazaar couldn't
617+ estimate how much work there was to do. (Martin Pool)
618+
619+Documentation
620+*************
621+
622+* Lots of documentation tweaks for inline help topics and command help
623+ information.
624+
625+API Changes
626+***********
627+
628+* ``bzrlib.textui`` (vestigial module) removed. (Martin Pool)
629+
630+* The Launchpad plugin now has a function ``login`` which will log in to
631+ Launchpad with launchpadlib, and ``load_branch`` which will return the
632+ Launchpad Branch object corresponding to a given Bazaar Branch object.
633+ (Jonathan Lange)
634+
635+Internals
636+*********
637+
638+* New test Feature: ``ModuleAvailableFeature``. It is designed to make it
639+ easier to handle what tests you want to run based on what modules can be
640+ imported. (Rather than lots of custom-implemented features that were
641+ basically copy-and-pasted.) (John Arbash Meinel)
642+
643+* ``osutils.timer_func()`` can be used to get either ``time.time()`` or
644+ ``time.clock()`` when you want to do performance timing.
645+ ``time.time()`` is limited to 15ms resolution on Windows, but
646+ ``time.clock()`` gives CPU and not wall-clock time on other platforms.
647+ (John Arbash Meinel)
648+
649+* Several code paths that were calling ``Transport.get().read()`` have
650+ been changed to the equalivent ``Transport.get_bytes()``. The main
651+ difference is that the latter will explicitly call ``file.close()``,
652+ rather than expecting the garbage collector to handle it. This helps
653+ with some race conditions on Windows during the test suite and sftp
654+ tests. (John Arbash Meinel)
655+
656+Testing
657+*******
658+
659+* TestCaseWithMemoryTransport no longer sets $HOME and $BZR_HOME to
660+ unicode strings. (Michael Hudson, #464174)
661+
662+
663 bzr 2.0.3
664 #########
665
666@@ -140,6 +666,233 @@
667 * Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325)
668
669
670+bzr 2.1.0b3
671+###########
672+
673+:Codename: after sprint recovery
674+:2.1.0b3: 2009-11-16
675+
676+This release was pushed up from its normal release cycle due to a
677+regression in python 2.4 compatibility in 2.1.0b2. Since this regression
678+was caught before 2.1.0b2 was officially announced, the full changelog
679+includes both 2.1.0b3 and 2.1.0b2 changes.
680+
681+Highlights of 2.1.0b3 are: new globbing code for all commands on Windows,
682+the test suite now conforms to python's trunk enhanced semantics (skip,
683+etc.), and ``bzr info -v`` will now report the correct branch and repo
684+formats for Remote objects.
685+
686+
687+New Features
688+************
689+
690+* Users can define a shelve editor to provide shelf functionality at a
691+ granularity finer than per-patch-hunk. (Aaron Bentley)
692+
693+Bug Fixes
694+*********
695+
696+* Fix for shell completion and short options. (Benoît PIERRE)
697+
698+* Hooks daughter classes should always call the base constructor.
699+ (Alexander Belchenko, Vincent Ladeuil, #389648)
700+
701+* Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325)
702+
703+* On Windows, do glob expansion at the command-line level (as is usually
704+ done in bash, etc.) This means that *all* commands get glob expansion
705+ (bzr status, bzr add, bzr mv, etc). It uses a custom command line
706+ parser, which allows us to know if a given section was quoted. It means
707+ you can now do ``bzr ignore "*.py"``.
708+ (John Arbash Meinel, #425510, #426410, #194450)
709+
710+* Sanitize commit messages that come in from the '-m' flag. We translate
711+ '\r\n' => '\n' and a plain '\r' => '\n'. The storage layer doesn't
712+ allow those because XML store silently translate it anyway. (The parser
713+ auto-translates \r\n => \n in ways that are hard for us to catch.)
714+
715+* Show correct branch and repository format descriptions in
716+ ``bzr info -v`` on a smart server location. (Andrew Bennetts, #196080)
717+
718+* The fix for bug #186920 accidentally broke compatibility with python
719+ 2.4. (Vincent Ladeuil, #475585)
720+
721+* Using ``Repository.get_commit_builder().record_iter_changes()`` now
722+ correctly sets ``self.inv_sha1`` to a sha1 string and
723+ ``self.new_inventory`` to an Inventory instance after calling
724+ ``self.finish_inventory()``. (Previously it accidently set both values
725+ as a tuple on ``self.inv_sha1``. This was missed because
726+ ``repo.add_revision`` ignores the supplied inventory sha1 and recomputes
727+ the sha1 from the repo directly. (John Arbash Meinel)
728+
729+* Shelve command refuse to run if there is no real terminal.
730+ (Alexander Belchenko)
731+
732+* Avoid unnecessarily flushing of trace file; it's now unbuffered at the
733+ Python level. (Martin Pool)
734+
735+Documentation
736+*************
737+
738+* Include Japanese translations for documentation (Inada Naoki)
739+
740+* New API ``ui_factory.make_output_stream`` to be used for sending bulk
741+ (rather than user-interaction) data to stdout. This automatically
742+ coordinates with progress bars or other terminal activity, and can be
743+ overridden by GUIs.
744+ (Martin Pool, 493944)
745+
746+Internals
747+*********
748+
749+* Some of the core groupcompress functionality now releases the GIL before
750+ operation. Similar to how zlib and bz2 operate without the GIL in the
751+ core compression and decompression routines. (John Arbash Meinel)
752+
753+Testing
754+*******
755+
756+* -Dhpssvfs will now trigger on ``RemoteBzrDir._ensure_real``, providing
757+ more debugging of VFS access triggers. (Robert Collins)
758+
759+* KnownFailure is now signalled to ``ExtendedTestResult`` using the same
760+ method that Python 2.7 uses - ``addExpectedFailure``. (Robert Collins)
761+
762+* ``--parallel=fork`` is now compatible with --subunit.
763+ (Robert Collins, Vincent Ladeuil, #419776)
764+
765+* Reporting of failures shows test ids not descriptions and thus shows
766+ parameterised tests correctly. (Robert Collins)
767+
768+* TestNotApplicable is now handled within the TestCase.run method rather
769+ than being looked for within ``ExtendedTestResult.addError``. This
770+ provides better handling with other ``TestResult`` objects, degrading to
771+ sucess rather than error. (Robert Collins)
772+
773+* The private method ``_testConcluded`` on ``ExtendedTestResult`` has been
774+ removed - it was empty and unused. (Robert Collins)
775+
776+* UnavailableFeature is now handled within the TestCase.run method rather
777+ than being looked for within addError. If the Result object does not
778+ have an addNotSupported method, addSkip is attempted instead, and
779+ failing that addSuccess. (Robert Collins)
780+
781+* When a TestResult does not have an addSkip method, skipped tests are now
782+ reported as successful tests, rather than as errors. This change is
783+ to make it possible to get a clean test run with a less capable
784+ TestResult. (Robert Collins)
785+
786+
787+
788+bzr 2.1.0b2
789+###########
790+
791+:Codename: a load off my mind
792+:2.1.0b2: 2009-11-02
793+
794+This is our second feature-filled release since 2.0, pushing us down the
795+path to a 2.1.0. Once again, all bugfixes in 2.0.2 are present in 2.1.0b2.
796+
797+Key highlights in this release are: improved handling of
798+failures-during-cleanup for commit, fixing a long-standing bug with
799+``bzr+http`` and shared repositories, all ``lp:`` urls to be resolved
800+behind proxies, and a new StaticTuple datatype, allowing us to reduce
801+memory consumption (50%) and garbage collector overhead (40% faster) for
802+many operations.
803+
804+* A new ``--concurrency`` option has been added as well as an associated
805+ BZR_CONCURRENCY environment variable to specify the number of
806+ processes that can be run concurrently when running ``bzr selftest``. The
807+ command-line option overrides the environment variable if both are
808+ specified. If none is specified. the number of processes is obtained
809+ from the OS as before. (Matt Nordhoff, Vincent Ladeuil)
810+
811+Bug Fixes
812+*********
813+
814+* ``bzr+http`` servers no longer give spurious jail break errors when
815+ serving branches inside a shared repository. (Andrew Bennetts, #348308)
816+
817+* Errors during commit are handled more robustly so that knock-on errors
818+ are less likely to occur, and will not obscure the original error if
819+ they do occur. This fixes some causes of ``TooManyConcurrentRequests``
820+ and similar errors. (Andrew Bennetts, #429747, #243391)
821+
822+* Launchpad urls can now be resolved from behind proxies.
823+ (Gordon Tyler, Vincent Ladeuil, #186920)
824+
825+* Reduce the strictness for StaticTuple, instead add a debug flag
826+ ``-Dstatic_tuple`` which will change apis to be strict and raise errors.
827+ This way, most users won't see failures, but developers can improve
828+ internals. (John Arbash Meinel, #471193)
829+
830+* TreeTransform.adjust_path updates the limbo paths of descendants of adjusted
831+ files. (Aaron Bentley)
832+
833+* Unicode paths are now handled correctly and consistently by the smart
834+ server. (Andrew Bennetts, Michael Hudson, #458762)
835+
836+Improvements
837+************
838+
839+* When reading index files, we now use a ``StaticTuple`` rather than a
840+ plain ``tuple`` object. This generally gives a 20% decrease in peak
841+ memory, and can give a performance boost up to 40% on large projects.
842+ (John Arbash Meinel)
843+
844+* Peak memory under certain operations has been reduced significantly.
845+ (eg, 'bzr branch launchpad standalone' is cut in half)
846+ (John Arbash Meinel)
847+
848+Documentation
849+*************
850+
851+* Filtered views user documentation upgraded to refer to format 2a
852+ instead of pre-2.0 formats. (Ian Clatworthy)
853+
854+API Changes
855+***********
856+
857+* Remove deprecated ``CLIUIFactory``. (Martin Pool)
858+
859+* ``UIFactory`` now has new ``show_error``, ``show_message`` and
860+ ``show_warning`` methods, which can be hooked by non-text UIs.
861+ (Martin Pool)
862+
863+Internals
864+*********
865+
866+* Added ``bzrlib._simple_set_pyx``. This is a hybrid between a Set and a
867+ Dict (it only holds keys, but you can lookup the object located at a
868+ given key). It has significantly reduced memory consumption versus the
869+ builtin objects (1/2 the size of Set, 1/3rd the size of Dict). This is
870+ used as the interning structure for StaticTuple objects.
871+ (John Arbash Meinel)
872+
873+* ``bzrlib._static_tuple_c.StaticTuple`` is now available and used by
874+ the btree index parser and the chk map parser. This class functions
875+ similarly to ``tuple`` objects. However, it can only point to a limited
876+ collection of types. (Currently StaticTuple, str, unicode, None, bool,
877+ int, long, float, but not subclasses). This allows us to remove it from
878+ the garbage collector (it cannot be in a cycle), it also allows us to
879+ intern the objects. In testing, this can reduce peak memory by 20-40%,
880+ and significantly improve performance by removing objects from being
881+ inspected by the garbage collector. (John Arbash Meinel)
882+
883+* ``GroupCompressBlock._ensure_content()`` will now release the
884+ ``zlib.decompressobj()`` when the first request is for all of the
885+ content. (Previously it would only be released if you made a request for
886+ part of the content, and then all of it later.) This turns out to be a
887+ significant memory savings, as a ``zstream`` carries around approx 260kB
888+ of internal state and buffers. (For branching bzr.dev this drops peak
889+ memory from 382MB => 345MB.) (John Arbash Meinel)
890+
891+* When streaming content between ``2a`` format repositories, we now clear
892+ caches from earlier versioned files. (So 'revisions' is cleared when we
893+ start reading 'inventories', etc.) This can have a significant impact on
894+ peak memory for initial copies (~200MB). (John Arbash Meinel)
895+
896+
897 bzr 2.0.2
898 #########
899
900@@ -187,6 +940,219 @@
901 instead of pre-2.0 formats. (Ian Clatworthy)
902
903
904+bzr 2.1.0b1
905+###########
906+
907+:Codename: While the cat is away
908+:2.1.0b1: 2009-10-14
909+
910+This is the first development release in the new split "stable" and
911+"development" series. As such, the release is a snapshot of bzr.dev
912+without creating a release candidate first. This release includes a
913+fair amount of internal changes, with deprecated code being removed,
914+and several new feature developments. People looking for a stable code
915+base with only bugfixes should focus on the 2.0.1 release. All bugfixes
916+present in 2.0.1 are present in 2.1.0b1.
917+
918+Highlights include support for ``bzr+ssh://host/~/homedir`` style urls,
919+finer control over the plugin search path via extended BZR_PLUGIN_PATH
920+syntax, visible warnings when extension modules fail to load, and improved
921+error handling during unlocking.
922+
923+
924+New Features
925+************
926+
927+* Bazaar can now send mail through Apple OS X Mail.app.
928+ (Brian de Alwis)
929+
930+* ``bzr+ssh`` and ``bzr`` paths can now be relative to home directories
931+ specified in the URL. Paths starting with a path segment of ``~`` are
932+ relative to the home directory of the user running the server, and paths
933+ starting with ``~user`` are relative to the home directory of the named
934+ user. For example, for a user "bob" with a home directory of
935+ ``/home/bob``, these URLs are all equivalent:
936+
937+ * ``bzr+ssh://bob@host/~/repo``
938+ * ``bzr+ssh://bob@host/~bob/repo``
939+ * ``bzr+ssh://bob@host/home/bob/repo``
940+
941+ If ``bzr serve`` was invoked with a ``--directory`` argument, then no
942+ home directories outside that directory will be accessible via this
943+ method.
944+
945+ This is a feature of ``bzr serve``, so pre-2.1 clients will
946+ automatically benefit from this feature when ``bzr`` on the server is
947+ upgraded. (Andrew Bennetts, #109143)
948+
949+* Extensions can now be compiled if either Cython or Pyrex is available.
950+ Currently Pyrex is preferred, but that may change in the future.
951+ (Arkanes)
952+
953+* Give more control on BZR_PLUGIN_PATH by providing a way to refer to or
954+ disable the user, site and core plugin directories.
955+ (Vincent Ladeuil, #412930, #316192, #145612)
956+
957+Bug Fixes
958+*********
959+
960+* Bazaar's native protocol code now correctly handles EINTR, which most
961+ noticeably occurs if you break in to the debugger while connected to a
962+ bzr+ssh server. You can now can continue from the debugger (by typing
963+ 'c') and the process continues. However, note that pressing C-\ in the
964+ shell may still kill the SSH process, which is bug 162509, so you must
965+ sent a signal to the bzr process specifically, for example by typing
966+ ``kill -QUIT PID`` in another shell. (Martin Pool, #341535)
967+
968+* ``bzr add`` in a tree that has files with ``\r`` or ``\n`` in the
969+ filename will issue a warning and skip over those files.
970+ (Robert Collins, #3918)
971+
972+* ``bzr dpush`` now aborts if uncommitted changes (including pending merges)
973+ are present in the working tree. The configuration option ``dpush_strict``
974+ can be used to set the default for this behavior.
975+ (Vincent Ladeuil, #438158)
976+
977+* ``bzr merge`` and ``bzr remove-tree`` now requires --force if pending
978+ merges are present in the working tree.
979+ (Vincent Ladeuil, #426344)
980+
981+* Clearer message when Bazaar runs out of memory, instead of a ``MemoryError``
982+ traceback. (Martin Pool, #109115)
983+
984+* Don't give a warning on Windows when failing to import ``_readdir_pyx``
985+ as it is never built. (John Arbash Meinel, #430645)
986+
987+* Don't restrict the command name used to run the test suite.
988+ (Vincent Ladeuil, #419950)
989+
990+* ftp transports were built differently when the kerberos python module was
991+ present leading to obscure failures related to ASCII/BINARY modes.
992+ (Vincent Ladeuil, #443041)
993+
994+* Network streams now decode adjacent records of the same type into a
995+ single stream, reducing layering churn. (Robert Collins)
996+
997+* PreviewTree behaves correctly when get_file_mtime is invoked on an unmodified
998+ file. (Aaron Bentley, #251532)
999+
1000+* Registry objects should not use iteritems() when asked to use items().
1001+ (Vincent Ladeuil, #430510)
1002+
1003+* Weave based repositories couldn't be cloned when committers were using
1004+ domains or user ids embedding '.sig'. Now they can.
1005+ (Matthew Fuller, Vincent Ladeuil, #430868)
1006+
1007+Improvements
1008+************
1009+
1010+* Revision specifiers can now be given in a more DWIM form, without
1011+ needing explicit prefixes for specifiers like tags or revision id's.
1012+ See ``bzr help revisionspec`` for full details. (Matthew Fuller)
1013+
1014+* Bazaar gives a warning before exiting, and writes into ``.bzr.log``, if
1015+ compiled extensions can't be loaded. This typically indicates a
1016+ packaging or installation problem. In this case Bazaar will keep
1017+ running using pure-Python versions, but this may be substantially
1018+ slower. The warning can be disabled by setting
1019+ ``ignore_missing_extensions = True`` in ``bazaar.conf``.
1020+ See also <https://answers.launchpad.net/bzr/+faq/703>.
1021+ (Martin Pool, #406113, #430529)
1022+
1023+* Secondary errors that occur during Branch.unlock and Repository.unlock
1024+ no longer obscure the original error. These methods now use a new
1025+ decorator, ``only_raises``. This fixes many causes of
1026+ ``TooManyConcurrentRequests`` and similar errors.
1027+ (Andrew Bennetts, #429747)
1028+
1029+Documentation
1030+*************
1031+
1032+* Describe the new shell-like test feature. (Vincent Ladeuil)
1033+
1034+* Help on hooks no longer says 'Not deprecated' for hooks that are
1035+ currently supported. (Ian Clatworthy, #422415)
1036+
1037+API Changes
1038+***********
1039+
1040+* ``bzrlib.user_encoding`` has been removed; use
1041+ ``bzrlib.osutils.get_user_encoding`` instead. (Martin Pool)
1042+
1043+* ``bzrlib.tests`` now uses ``stopTestRun`` for its ``TestResult``
1044+ subclasses - the same as python's unittest module. (Robert Collins)
1045+
1046+* ``diff._get_trees_to_diff`` has been renamed to
1047+ ``diff.get_trees_and_branches_to_diff``. It is now a public API, and it
1048+ returns the old and new branches. (Gary van der Merwe)
1049+
1050+* ``bzrlib.trace.log_error``, ``error`` and ``info`` have been deprecated.
1051+ (Martin Pool)
1052+
1053+* ``MutableTree.has_changes()`` does not require a tree parameter anymore. It
1054+ now defaults to comparing to the basis tree. It now checks for pending
1055+ merges too. ``Merger.check_basis`` has been deprecated and replaced by the
1056+ corresponding has_changes() calls. ``Merge.compare_basis``,
1057+ ``Merger.file_revisions`` and ``Merger.ensure_revision_trees`` have also
1058+ been deprecated.
1059+ (Vincent Ladeuil, #440631)
1060+
1061+* ``ProgressTask.note`` is deprecated.
1062+ (Martin Pool)
1063+
1064+Internals
1065+*********
1066+
1067+* Added ``-Drelock`` debug flag. It will ``note`` a message every time a
1068+ repository or branch object is unlocked then relocked the same way.
1069+ (Andrew Bennetts)
1070+
1071+* ``BTreeLeafParser.extract_key`` has been tweaked slightly to reduce
1072+ mallocs while parsing the index (approx 3=>1 mallocs per key read).
1073+ This results in a 10% speedup while reading an index.
1074+ (John Arbash Meinel)
1075+
1076+* The ``bzrlib.lsprof`` module has a new class ``BzrProfiler`` which makes
1077+ profiling in some situations like callbacks and generators easier.
1078+ (Robert Collins)
1079+
1080+Testing
1081+*******
1082+
1083+* Passing ``--lsprof-tests -v`` to bzr selftest will cause lsprof output to
1084+ be output for every test. Note that this is very verbose! (Robert Collins)
1085+
1086+* Setting ``BZR_TEST_PDB=1`` when running selftest will cause a pdb
1087+ post_mortem to be triggered when a test failure occurs. (Robert Collins)
1088+
1089+* Shell-like tests can now be written. Code in ``bzrlib/tests/script.py`` ,
1090+ documentation in ``developers/testing.txt`` for details.
1091+ (Vincent Ladeuil)
1092+
1093+* Some tests could end up with the same id, that was dormant for
1094+ a long time.
1095+ (Vincent Ladeuil, #442980)
1096+
1097+* Stop showing the number of tests due to missing features in the test
1098+ progress bar. (Martin Pool)
1099+
1100+* Test parameterisation now does a shallow copy, not a deep copy of the test
1101+ to be parameterised. This is not expected to break external use of test
1102+ parameterisation, and is substantially faster. (Robert Collins)
1103+
1104+* Tests that try to open a bzr dir on an arbitrary transport will now
1105+ fail unless they have explicitly permitted the transport via
1106+ ``self.permit_url``. The standard test factories such as ``self.get_url``
1107+ will permit the urls they provide automatically, so only exceptional
1108+ tests should need to do this. (Robert Collins)
1109+
1110+* The break-in test no longer cares about clean shutdown of the child,
1111+ instead it is happy if the debugger starts up. (Robert Collins)
1112+
1113+* The full test suite is expected to pass when the C extensions are not
1114+ present. (Vincent Ladeuil, #430749)
1115+
1116+
1117 bzr 2.0.1
1118 #########
1119
1120@@ -290,6 +1256,7 @@
1121 to "land in 2.0.0". (Changes how bzrlib._format_version_tuple() handles
1122 micro = 0.) (John Arbash Meinel)
1123
1124+
1125 bzr 2.0.0rc2
1126 ############
1127
1128@@ -346,10 +1313,6 @@
1129 ghosts in its mainline. (Evaluating None as a tuple is bad.)
1130 (John Arbash Meinel, #419241)
1131
1132-* Fix a segmentation fault when computing the ``merge_sort`` of a graph
1133- that has a ghost in the mainline ancestry.
1134- (John Arbash Meinel, #419241)
1135-
1136 * ``groupcompress`` sort order is now more stable, rather than relying on
1137 ``topo_sort`` ordering. The implementation is now
1138 ``KnownGraph.gc_sort``. (John Arbash Meinel)
1139@@ -417,6 +1380,9 @@
1140 Bug Fixes
1141 *********
1142
1143+* Further tweaks to handling of ``bzr add`` messages about ignored files.
1144+ (Jason Spashett, #76616)
1145+
1146 * Fetches were being requested in 'groupcompress' order, but weren't
1147 recombining the groups. Thus they would 'fragment' to get the correct
1148 order, but not 'recombine' to actually benefit from it. Until we get
1149@@ -462,7 +1428,7 @@
1150 * ``bzr shelve`` and ``bzr unshelve`` now work on windows.
1151 (Robert Collins, #305006)
1152
1153-* Commit of specific files no longer prevents using the the iter_changes
1154+* Commit of specific files no longer prevents using the iter_changes
1155 codepath. On 2a repositories, commit of specific files should now be as
1156 fast, or slightly faster, than a full commit. (Robert Collins)
1157
1158@@ -486,9 +1452,6 @@
1159 classes changed to manage lock lifetime of the trees they open in a way
1160 consistent with reader-exclusive locks. (Robert Collins, #305006)
1161
1162-Internals
1163-*********
1164-
1165 Testing
1166 *******
1167
1168@@ -535,6 +1498,9 @@
1169 a tree with content filtering where the size of the canonical form
1170 cannot be cheaply determined. (Martin Pool)
1171
1172+* When manually creating transport servers in test cases, a new helper
1173+ ``TestCase.start_server`` that registers a cleanup and starts the server
1174+ should be used. (Robert Collins)
1175
1176 bzr 1.18
1177 ########
1178@@ -872,6 +1838,17 @@
1179 ``countTestsCases``. (Robert Collins)
1180
1181
1182+bzr 1.17.1 (unreleased)
1183+#######################
1184+
1185+Bug Fixes
1186+*********
1187+
1188+* The optional ``_knit_load_data_pyx`` C extension was never being
1189+ imported. This caused significant slowdowns when reading data from
1190+ knit format repositories. (Andrew Bennetts, #405653)
1191+
1192+
1193 bzr 1.17
1194 ########
1195 :Codename: so-late-its-brunch
1196@@ -1220,7 +2197,7 @@
1197 ************
1198
1199 * A new repository format ``2a`` has been added. This is a beta release
1200- of the the brisbane-core (aka group-compress) project. This format now
1201+ of the brisbane-core (aka group-compress) project. This format now
1202 suitable for wider testing by advanced users willing to deal with some
1203 bugs. We would appreciate test reports, either positive or negative.
1204 Format 2a is substantially smaller and faster for many operations on
1205@@ -1372,6 +2349,9 @@
1206 Testing
1207 *******
1208
1209+* ``make check`` no longer repeats the test run in ``LANG=C``.
1210+ (Martin Pool, #386180)
1211+
1212 * The number of cores is now correctly detected on OSX. (John Szakmeister)
1213
1214 * The number of cores is also detected on Solaris and win32. (Vincent Ladeuil)
1215@@ -1946,7 +2926,7 @@
1216
1217 * Added ``bzrlib.inventory_delta`` module. This will be used for
1218 serializing and deserializing inventory deltas for more efficient
1219- streaming on the the network. (Robert Collins, Andrew Bennetts)
1220+ streaming on the network. (Robert Collins, Andrew Bennetts)
1221
1222 * ``Branch._get_config`` has been added, which splits out access to the
1223 specific config file from the branch. This is used to let RemoteBranch
1224@@ -2141,7 +3121,7 @@
1225 * Multiple authors for a commit can now be recorded by using the "--author"
1226 option multiple times. (James Westby, #185772)
1227
1228-* New clean-tree command, from bzrtools. (Aaron Bentley, Jelmer Vernoij)
1229+* New clean-tree command, from bzrtools. (Aaron Bentley, Jelmer Vernooij)
1230
1231 * New command ``bzr launchpad-open`` opens a Launchpad web page for that
1232 branch in your web browser, as long as the branch is on Launchpad at all.
1233@@ -4670,7 +5650,7 @@
1234 exception. (Andrew Bennetts)
1235
1236 * New ``--debugflag``/``-E`` option to ``bzr selftest`` for setting
1237- options for debugging tests, these are complementary to the the -D
1238+ options for debugging tests, these are complementary to the -D
1239 options. The ``-Dselftest_debug`` global option has been replaced by the
1240 ``-E=allow_debug`` option for selftest. (Andrew Bennetts)
1241
1242@@ -5425,7 +6405,7 @@
1243 checkouts. (Aaron Bentley, #182040)
1244
1245 * Stop polluting /tmp when running selftest.
1246- (Vincent Ladeuil, #123623)
1247+ (Vincent Ladeuil, #123363)
1248
1249 * Switch from NFKC => NFC for normalization checks. NFC allows a few
1250 more characters which should be considered valid.
1251
1252=== modified file 'bzr'
1253--- bzr 2009-12-15 16:56:18 +0000
1254+++ bzr 2010-02-18 00:00:51 +0000
1255@@ -23,7 +23,7 @@
1256 import warnings
1257
1258 # update this on each release
1259-_script_version = (2, 0, 4)
1260+_script_version = (2, 1, 1)
1261
1262 if __doc__ is None:
1263 print "bzr does not support python -OO."
1264
1265=== modified file 'bzrlib/__init__.py'
1266--- bzrlib/__init__.py 2010-01-21 19:35:57 +0000
1267+++ bzrlib/__init__.py 2010-02-18 00:00:52 +0000
1268@@ -31,16 +31,10 @@
1269 import bzrlib.lazy_regex
1270 bzrlib.lazy_regex.install_lazy_compile()
1271
1272-from bzrlib.osutils import get_user_encoding
1273-
1274
1275 IGNORE_FILENAME = ".bzrignore"
1276
1277
1278-# XXX: Deprecated as of bzr-1.17 use osutils.get_user_encoding() directly
1279-user_encoding = get_user_encoding()
1280-
1281-
1282 __copyright__ = "Copyright 2005, 2006, 2007, 2008, 2009 Canonical Ltd."
1283
1284 # same format as sys.version_info: "A tuple containing the five components of
1285@@ -50,10 +44,11 @@
1286 # Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a
1287 # releaselevel of 'dev' for unreleased under-development code.
1288
1289-version_info = (2, 0, 4, 'final', 0)
1290+version_info = (2, 1, 1, 'dev', 0)
1291
1292 # API compatibility version: bzrlib is currently API compatible with 1.15.
1293-api_minimum_version = (1, 17, 0)
1294+api_minimum_version = (2, 1, 0)
1295+
1296
1297 def _format_version_tuple(version_info):
1298 """Turn a version number 2, 3 or 5-tuple into a short string.
1299
1300=== modified file 'bzrlib/_annotator_pyx.pyx'
1301--- bzrlib/_annotator_pyx.pyx 2010-01-05 04:59:57 +0000
1302+++ bzrlib/_annotator_pyx.pyx 2010-02-18 00:00:52 +0000
1303@@ -1,4 +1,4 @@
1304-# Copyright (C) 2009 Canonical Ltd
1305+# Copyright (C) 2009, 2010 Canonical Ltd
1306 #
1307 # This program is free software; you can redistribute it and/or modify
1308 # it under the terms of the GNU General Public License as published by
1309
1310=== modified file 'bzrlib/_bencode_pyx.pyx'
1311--- bzrlib/_bencode_pyx.pyx 2009-12-18 21:58:32 +0000
1312+++ bzrlib/_bencode_pyx.pyx 2010-02-18 00:00:52 +0000
1313@@ -1,4 +1,4 @@
1314-# Copyright (C) 2007,2009 Canonical Ltd
1315+# Copyright (C) 2007, 2009, 2010 Canonical Ltd
1316 #
1317 # This program is free software; you can redistribute it and/or modify
1318 # it under the terms of the GNU General Public License as published by
1319@@ -58,6 +58,13 @@
1320 void D_UPDATE_TAIL(Decoder, int n)
1321 void E_UPDATE_TAIL(Encoder, int n)
1322
1323+# To maintain compatibility with older versions of pyrex, we have to use the
1324+# relative import here, rather than 'bzrlib._static_tuple_c'
1325+from _static_tuple_c cimport StaticTuple, StaticTuple_CheckExact, \
1326+ import_static_tuple_c
1327+
1328+import_static_tuple_c()
1329+
1330
1331 cdef class Decoder:
1332 """Bencode decoder"""
1333@@ -371,7 +378,8 @@
1334 self._encode_int(x)
1335 elif PyLong_CheckExact(x):
1336 self._encode_long(x)
1337- elif PyList_CheckExact(x) or PyTuple_CheckExact(x):
1338+ elif (PyList_CheckExact(x) or PyTuple_CheckExact(x)
1339+ or StaticTuple_CheckExact(x)):
1340 self._encode_list(x)
1341 elif PyDict_CheckExact(x):
1342 self._encode_dict(x)
1343
1344=== modified file 'bzrlib/_btree_serializer_py.py'
1345--- bzrlib/_btree_serializer_py.py 2009-03-23 14:59:43 +0000
1346+++ bzrlib/_btree_serializer_py.py 2010-02-18 00:00:50 +0000
1347@@ -1,4 +1,4 @@
1348-# Copyright (C) 2008 Canonical Ltd
1349+# Copyright (C) 2008, 2009, 2010 Canonical Ltd
1350 #
1351 # This program is free software; you can redistribute it and/or modify
1352 # it under the terms of the GNU General Public License as published by
1353@@ -17,27 +17,33 @@
1354
1355 """B+Tree index parsing."""
1356
1357+from bzrlib import static_tuple
1358+
1359+
1360 def _parse_leaf_lines(bytes, key_length, ref_list_length):
1361 lines = bytes.split('\n')
1362 nodes = []
1363+ as_st = static_tuple.StaticTuple.from_sequence
1364+ stuple = static_tuple.StaticTuple
1365 for line in lines[1:]:
1366 if line == '':
1367 return nodes
1368 elements = line.split('\0', key_length)
1369 # keys are tuples
1370- key = tuple(elements[:key_length])
1371+ key = as_st(elements[:key_length]).intern()
1372 line = elements[-1]
1373 references, value = line.rsplit('\0', 1)
1374 if ref_list_length:
1375 ref_lists = []
1376 for ref_string in references.split('\t'):
1377- ref_lists.append(tuple([
1378- tuple(ref.split('\0')) for ref in ref_string.split('\r') if ref
1379- ]))
1380- ref_lists = tuple(ref_lists)
1381- node_value = (value, ref_lists)
1382+ ref_list = as_st([as_st(ref.split('\0')).intern()
1383+ for ref in ref_string.split('\r') if ref])
1384+ ref_lists.append(ref_list)
1385+ ref_lists = as_st(ref_lists)
1386+ node_value = stuple(value, ref_lists)
1387 else:
1388- node_value = (value, ())
1389+ node_value = stuple(value, stuple())
1390+ # No need for StaticTuple here as it is put into a dict
1391 nodes.append((key, node_value))
1392 return nodes
1393
1394
1395=== modified file 'bzrlib/_btree_serializer_pyx.pyx'
1396--- bzrlib/_btree_serializer_pyx.pyx 2010-01-05 04:59:57 +0000
1397+++ bzrlib/_btree_serializer_pyx.pyx 2010-02-18 00:00:52 +0000
1398@@ -1,4 +1,4 @@
1399-# Copyright (C) 2008, 2009 Canonical Ltd
1400+# Copyright (C) 2008, 2009, 2010 Canonical Ltd
1401 #
1402 # This program is free software; you can redistribute it and/or modify
1403 # it under the terms of the GNU General Public License as published by
1404@@ -38,11 +38,16 @@
1405 Py_ssize_t PyString_Size(object p)
1406 Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *)
1407 char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *)
1408+ char * PyString_AS_STRING(object)
1409+ Py_ssize_t PyString_GET_SIZE(object)
1410 int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len)
1411 void PyString_InternInPlace(PyObject **)
1412 int PyTuple_CheckExact(object t)
1413+ object PyTuple_New(Py_ssize_t n_entries)
1414+ void PyTuple_SET_ITEM(object, Py_ssize_t offset, object) # steals the ref
1415 Py_ssize_t PyTuple_GET_SIZE(object t)
1416 PyObject *PyTuple_GET_ITEM_ptr_object "PyTuple_GET_ITEM" (object tpl, int index)
1417+ void Py_INCREF(object)
1418 void Py_DECREF_ptr "Py_DECREF" (PyObject *)
1419
1420 cdef extern from "string.h":
1421@@ -52,6 +57,12 @@
1422 # void *memrchr(void *s, int c, size_t n)
1423 int strncmp(char *s1, char *s2, size_t n)
1424
1425+# It seems we need to import the definitions so that the pyrex compiler has
1426+# local names to access them.
1427+from _static_tuple_c cimport StaticTuple, \
1428+ import_static_tuple_c, StaticTuple_New, \
1429+ StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact
1430+
1431
1432 # TODO: Find some way to import this from _dirstate_helpers
1433 cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
1434@@ -68,6 +79,7 @@
1435 pos = pos - 1
1436 return NULL
1437
1438+
1439 # TODO: Import this from _dirstate_helpers when it is merged
1440 cdef object safe_string_from_size(char *s, Py_ssize_t size):
1441 if size < 0:
1442@@ -91,6 +103,10 @@
1443 Py_DECREF_ptr(py_str)
1444 return result
1445
1446+from bzrlib import _static_tuple_c
1447+# This sets up the StaticTuple C_API functionality
1448+import_static_tuple_c()
1449+
1450
1451 cdef class BTreeLeafParser:
1452 """Parse the leaf nodes of a BTree index.
1453@@ -130,6 +146,7 @@
1454 self._cur_str = NULL
1455 self._end_str = NULL
1456 self._header_found = 0
1457+ # keys are tuples
1458
1459 cdef extract_key(self, char * last):
1460 """Extract a key.
1461@@ -139,15 +156,14 @@
1462 """
1463 cdef char *temp_ptr
1464 cdef int loop_counter
1465- # keys are tuples
1466- loop_counter = 0
1467- key_segments = []
1468- while loop_counter < self.key_length:
1469- loop_counter = loop_counter + 1
1470+ cdef StaticTuple key
1471+
1472+ key = StaticTuple_New(self.key_length)
1473+ for loop_counter from 0 <= loop_counter < self.key_length:
1474 # grab a key segment
1475 temp_ptr = <char*>memchr(self._start, c'\0', last - self._start)
1476 if temp_ptr == NULL:
1477- if loop_counter == self.key_length:
1478+ if loop_counter + 1 == self.key_length:
1479 # capture to last
1480 temp_ptr = last
1481 else:
1482@@ -157,15 +173,20 @@
1483 last - self._start)))
1484 raise AssertionError(failure_string)
1485 # capture the key string
1486- # TODO: Consider using PyIntern_FromString, the only caveat is that
1487- # it assumes a NULL-terminated string, so we have to check if
1488- # temp_ptr[0] == c'\0' or some other char.
1489- key_element = safe_interned_string_from_size(self._start,
1490+ if (self.key_length == 1
1491+ and (temp_ptr - self._start) == 45
1492+ and strncmp(self._start, 'sha1:', 5) == 0):
1493+ key_element = safe_string_from_size(self._start,
1494+ temp_ptr - self._start)
1495+ else:
1496+ key_element = safe_interned_string_from_size(self._start,
1497 temp_ptr - self._start)
1498 # advance our pointer
1499 self._start = temp_ptr + 1
1500- PyList_Append(key_segments, key_element)
1501- return tuple(key_segments)
1502+ Py_INCREF(key_element)
1503+ StaticTuple_SET_ITEM(key, loop_counter, key_element)
1504+ key = StaticTuple_Intern(key)
1505+ return key
1506
1507 cdef int process_line(self) except -1:
1508 """Process a line in the bytes."""
1509@@ -174,6 +195,7 @@
1510 cdef char *ref_ptr
1511 cdef char *next_start
1512 cdef int loop_counter
1513+ cdef Py_ssize_t str_len
1514
1515 self._start = self._cur_str
1516 # Find the next newline
1517@@ -209,12 +231,25 @@
1518 # Invalid line
1519 raise AssertionError("Failed to find the value area")
1520 else:
1521- # capture the value string
1522- value = safe_string_from_size(temp_ptr + 1, last - temp_ptr - 1)
1523+ # Because of how conversions were done, we ended up with *lots* of
1524+ # values that are identical. These are all of the 0-length nodes
1525+ # that are referred to by the TREE_ROOT (and likely some other
1526+ # directory nodes.) For example, bzr has 25k references to
1527+ # something like '12607215 328306 0 0', which ends up consuming 1MB
1528+ # of memory, just for those strings.
1529+ str_len = last - temp_ptr - 1
1530+ if (str_len > 4
1531+ and strncmp(" 0 0", last - 4, 4) == 0):
1532+ # This drops peak mem for bzr.dev from 87.4MB => 86.2MB
1533+ # For Launchpad 236MB => 232MB
1534+ value = safe_interned_string_from_size(temp_ptr + 1, str_len)
1535+ else:
1536+ value = safe_string_from_size(temp_ptr + 1, str_len)
1537 # shrink the references end point
1538 last = temp_ptr
1539+
1540 if self.ref_list_length:
1541- ref_lists = []
1542+ ref_lists = StaticTuple_New(self.ref_list_length)
1543 loop_counter = 0
1544 while loop_counter < self.ref_list_length:
1545 ref_list = []
1546@@ -246,18 +281,20 @@
1547 if temp_ptr == NULL:
1548 # key runs to the end
1549 temp_ptr = ref_ptr
1550+
1551 PyList_Append(ref_list, self.extract_key(temp_ptr))
1552- PyList_Append(ref_lists, tuple(ref_list))
1553+ ref_list = StaticTuple_Intern(StaticTuple(*ref_list))
1554+ Py_INCREF(ref_list)
1555+ StaticTuple_SET_ITEM(ref_lists, loop_counter - 1, ref_list)
1556 # prepare for the next reference list
1557 self._start = next_start
1558- ref_lists = tuple(ref_lists)
1559- node_value = (value, ref_lists)
1560+ node_value = StaticTuple(value, ref_lists)
1561 else:
1562 if last != self._start:
1563 # unexpected reference data present
1564 raise AssertionError("unexpected reference data present")
1565- node_value = (value, ())
1566- PyList_Append(self.keys, (key, node_value))
1567+ node_value = StaticTuple(value, StaticTuple())
1568+ PyList_Append(self.keys, StaticTuple(key, node_value))
1569 return 0
1570
1571 def parse(self):
1572@@ -292,7 +329,6 @@
1573 cdef Py_ssize_t flat_len
1574 cdef Py_ssize_t key_len
1575 cdef Py_ssize_t node_len
1576- cdef PyObject * val
1577 cdef char * value
1578 cdef Py_ssize_t value_len
1579 cdef char * out
1580@@ -301,13 +337,12 @@
1581 cdef int first_ref_list
1582 cdef int first_reference
1583 cdef int i
1584- cdef PyObject *ref_bit
1585 cdef Py_ssize_t ref_bit_len
1586
1587- if not PyTuple_CheckExact(node):
1588- raise TypeError('We expected a tuple() for node not: %s'
1589+ if not PyTuple_CheckExact(node) and not StaticTuple_CheckExact(node):
1590+ raise TypeError('We expected a tuple() or StaticTuple() for node not: %s'
1591 % type(node))
1592- node_len = PyTuple_GET_SIZE(node)
1593+ node_len = len(node)
1594 have_reference_lists = reference_lists
1595 if have_reference_lists:
1596 if node_len != 4:
1597@@ -316,8 +351,17 @@
1598 elif node_len < 3:
1599 raise ValueError('Without ref_lists, we need at least 3 entries not: %s'
1600 % len(node))
1601- # I don't expect that we can do faster than string.join()
1602- string_key = '\0'.join(<object>PyTuple_GET_ITEM_ptr_object(node, 1))
1603+ # TODO: We can probably do better than string.join(), namely
1604+ # when key has only 1 item, we can just grab that string
1605+ # And when there are 2 items, we could do a single malloc + len() + 1
1606+ # also, doing .join() requires a PyObject_GetAttrString call, which
1607+ # we could also avoid.
1608+ # TODO: Note that pyrex 0.9.6 generates fairly crummy code here, using the
1609+ # python object interface, versus 0.9.8+ which uses a helper that
1610+ # checks if this supports the sequence interface.
1611+ # We *could* do more work on our own, and grab the actual items
1612+ # lists. For now, just ask people to use a better compiler. :)
1613+ string_key = '\0'.join(node[1])
1614
1615 # TODO: instead of using string joins, precompute the final string length,
1616 # and then malloc a single string and copy everything in.
1617@@ -334,7 +378,7 @@
1618 refs_len = 0
1619 if have_reference_lists:
1620 # Figure out how many bytes it will take to store the references
1621- ref_lists = <object>PyTuple_GET_ITEM_ptr_object(node, 3)
1622+ ref_lists = node[3]
1623 next_len = len(ref_lists) # TODO: use a Py function
1624 if next_len > 0:
1625 # If there are no nodes, we don't need to do any work
1626@@ -348,31 +392,31 @@
1627 # references
1628 refs_len = refs_len + (next_len - 1)
1629 for reference in ref_list:
1630- if not PyTuple_CheckExact(reference):
1631+ if (not PyTuple_CheckExact(reference)
1632+ and not StaticTuple_CheckExact(reference)):
1633 raise TypeError(
1634 'We expect references to be tuples not: %s'
1635 % type(reference))
1636- next_len = PyTuple_GET_SIZE(reference)
1637+ next_len = len(reference)
1638 if next_len > 0:
1639 # We will need (len - 1) '\x00' characters to
1640 # separate the reference key
1641 refs_len = refs_len + (next_len - 1)
1642- for i from 0 <= i < next_len:
1643- ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i)
1644- if not PyString_CheckExact_ptr(ref_bit):
1645+ for ref_bit in reference:
1646+ if not PyString_CheckExact(ref_bit):
1647 raise TypeError('We expect reference bits'
1648 ' to be strings not: %s'
1649 % type(<object>ref_bit))
1650- refs_len = refs_len + PyString_GET_SIZE_ptr(ref_bit)
1651+ refs_len = refs_len + PyString_GET_SIZE(ref_bit)
1652
1653 # So we have the (key NULL refs NULL value LF)
1654 key_len = PyString_Size(string_key)
1655- val = PyTuple_GET_ITEM_ptr_object(node, 2)
1656- if not PyString_CheckExact_ptr(val):
1657+ val = node[2]
1658+ if not PyString_CheckExact(val):
1659 raise TypeError('Expected a plain str for value not: %s'
1660- % type(<object>val))
1661- value = PyString_AS_STRING_ptr(val)
1662- value_len = PyString_GET_SIZE_ptr(val)
1663+ % type(val))
1664+ value = PyString_AS_STRING(val)
1665+ value_len = PyString_GET_SIZE(val)
1666 flat_len = (key_len + 1 + refs_len + 1 + value_len + 1)
1667 line = PyString_FromStringAndSize(NULL, flat_len)
1668 # Get a pointer to the new buffer
1669@@ -394,14 +438,14 @@
1670 out[0] = c'\r'
1671 out = out + 1
1672 first_reference = 0
1673- next_len = PyTuple_GET_SIZE(reference)
1674+ next_len = len(reference)
1675 for i from 0 <= i < next_len:
1676 if i != 0:
1677 out[0] = c'\x00'
1678 out = out + 1
1679- ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i)
1680- ref_bit_len = PyString_GET_SIZE_ptr(ref_bit)
1681- memcpy(out, PyString_AS_STRING_ptr(ref_bit), ref_bit_len)
1682+ ref_bit = reference[i]
1683+ ref_bit_len = PyString_GET_SIZE(ref_bit)
1684+ memcpy(out, PyString_AS_STRING(ref_bit), ref_bit_len)
1685 out = out + ref_bit_len
1686 out[0] = c'\0'
1687 out = out + 1
1688
1689=== modified file 'bzrlib/_chk_map_py.py'
1690--- bzrlib/_chk_map_py.py 2009-04-09 20:23:07 +0000
1691+++ bzrlib/_chk_map_py.py 2010-02-18 00:00:52 +0000
1692@@ -1,4 +1,4 @@
1693-# Copyright (C) 2009 Canonical Ltd
1694+# Copyright (C) 2009, 2010 Canonical Ltd
1695 #
1696 # This program is free software; you can redistribute it and/or modify
1697 # it under the terms of the GNU General Public License as published by
1698@@ -19,6 +19,8 @@
1699 import zlib
1700 import struct
1701
1702+from bzrlib.static_tuple import StaticTuple
1703+
1704 _LeafNode = None
1705 _InternalNode = None
1706 _unknown = None
1707@@ -93,7 +95,7 @@
1708 value_lines = lines[pos:pos+num_value_lines]
1709 pos += num_value_lines
1710 value = '\n'.join(value_lines)
1711- items[tuple(elements[:-1])] = value
1712+ items[StaticTuple.from_sequence(elements[:-1])] = value
1713 if len(items) != length:
1714 raise AssertionError("item count (%d) mismatch for key %s,"
1715 " bytes %r" % (length, key, bytes))
1716@@ -141,7 +143,7 @@
1717 for line in lines[5:]:
1718 line = common_prefix + line
1719 prefix, flat_key = line.rsplit('\x00', 1)
1720- items[prefix] = (flat_key,)
1721+ items[prefix] = StaticTuple(flat_key,)
1722 if len(items) == 0:
1723 raise AssertionError("We didn't find any item for %s" % key)
1724 result._items = items
1725@@ -155,4 +157,3 @@
1726 result._node_width = len(prefix)
1727 result._search_prefix = common_prefix
1728 return result
1729-
1730
1731=== modified file 'bzrlib/_chk_map_pyx.pyx'
1732--- bzrlib/_chk_map_pyx.pyx 2010-01-05 04:59:57 +0000
1733+++ bzrlib/_chk_map_pyx.pyx 2010-02-18 00:00:51 +0000
1734@@ -1,4 +1,4 @@
1735-# Copyright (C) 2009 Canonical Ltd
1736+# Copyright (C) 2009, 2010 Canonical Ltd
1737 #
1738 # This program is free software; you can redistribute it and/or modify
1739 # it under the terms of the GNU General Public License as published by
1740@@ -29,9 +29,8 @@
1741
1742 cdef extern from "Python.h":
1743 ctypedef int Py_ssize_t # Required for older pyrex versions
1744- struct _PyObject:
1745+ ctypedef struct PyObject:
1746 pass
1747- ctypedef _PyObject PyObject
1748 int PyTuple_CheckExact(object p)
1749 Py_ssize_t PyTuple_GET_SIZE(object t)
1750 int PyString_CheckExact(object)
1751@@ -52,6 +51,18 @@
1752 char *PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *s)
1753 object PyString_FromStringAndSize(char*, Py_ssize_t)
1754
1755+# cimport all of the definitions we will need to access
1756+from _static_tuple_c cimport StaticTuple,\
1757+ import_static_tuple_c, StaticTuple_New, \
1758+ StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact
1759+
1760+cdef extern from "_static_tuple_c.h":
1761+ # Defined explicitly rather than cimport-ing. Trying to use cimport, the
1762+ # type for PyObject is a different class that happens to have the same
1763+ # name...
1764+ PyObject * StaticTuple_GET_ITEM_ptr "StaticTuple_GET_ITEM" (StaticTuple,
1765+ Py_ssize_t)
1766+
1767 cdef extern from "zlib.h":
1768 ctypedef unsigned long uLong
1769 ctypedef unsigned int uInt
1770@@ -60,8 +71,14 @@
1771 uLong crc32(uLong crc, Bytef *buf, uInt len)
1772
1773
1774+# Set up the StaticTuple C_API functionality
1775+import_static_tuple_c()
1776+
1777+cdef object _LeafNode
1778 _LeafNode = None
1779+cdef object _InternalNode
1780 _InternalNode = None
1781+cdef object _unknown
1782 _unknown = None
1783
1784 # We shouldn't just copy this from _dirstate_helpers_pyx
1785@@ -91,9 +108,9 @@
1786 cdef char *c_out
1787 cdef PyObject *bit
1788
1789- if not PyTuple_CheckExact(key):
1790- raise TypeError('key %r is not a tuple' % (key,))
1791- num_bits = PyTuple_GET_SIZE(key)
1792+ if not StaticTuple_CheckExact(key):
1793+ raise TypeError('key %r is not a StaticTuple' % (key,))
1794+ num_bits = len(key)
1795 # 4 bytes per crc32, and another 1 byte between bits
1796 num_out_bytes = (9 * num_bits) - 1
1797 out = PyString_FromStringAndSize(NULL, num_out_bytes)
1798@@ -105,7 +122,7 @@
1799 # We use the _ptr variant, because GET_ITEM returns a borrowed
1800 # reference, and Pyrex assumes that returned 'object' are a new
1801 # reference
1802- bit = PyTuple_GET_ITEM_ptr(key, i)
1803+ bit = StaticTuple_GET_ITEM_ptr(key, i)
1804 if not PyString_CheckExact_ptr(bit):
1805 raise TypeError('Bit %d of %r is not a string' % (i, key))
1806 c_bit = <Bytef *>PyString_AS_STRING_ptr(bit)
1807@@ -129,9 +146,9 @@
1808 cdef char *c_out
1809 cdef PyObject *bit
1810
1811- if not PyTuple_CheckExact(key):
1812- raise TypeError('key %r is not a tuple' % (key,))
1813- num_bits = PyTuple_GET_SIZE(key)
1814+ if not StaticTuple_CheckExact(key):
1815+ raise TypeError('key %r is not a StaticTuple' % (key,))
1816+ num_bits = len(key)
1817 # 4 bytes per crc32, and another 1 byte between bits
1818 num_out_bytes = (5 * num_bits) - 1
1819 out = PyString_FromStringAndSize(NULL, num_out_bytes)
1820@@ -140,10 +157,10 @@
1821 if i > 0:
1822 c_out[0] = c'\x00'
1823 c_out = c_out + 1
1824- bit = PyTuple_GET_ITEM_ptr(key, i)
1825+ bit = StaticTuple_GET_ITEM_ptr(key, i)
1826 if not PyString_CheckExact_ptr(bit):
1827- raise TypeError('Bit %d of %r is not a string: %r' % (i, key,
1828- <object>bit))
1829+ raise TypeError('Bit %d of %r is not a string: %r'
1830+ % (i, key, <object>bit))
1831 c_bit = <Bytef *>PyString_AS_STRING_ptr(bit)
1832 c_len = PyString_GET_SIZE_ptr(bit)
1833 crc_val = crc32(0, c_bit, c_len)
1834@@ -195,6 +212,7 @@
1835 cdef char *prefix, *value_start, *prefix_tail
1836 cdef char *next_null, *last_null, *line_start
1837 cdef char *c_entry, *entry_start
1838+ cdef StaticTuple entry_bits
1839
1840 if _LeafNode is None:
1841 from bzrlib import chk_map
1842@@ -265,12 +283,14 @@
1843 if next_line == NULL:
1844 raise ValueError('missing trailing newline')
1845 cur = next_line + 1
1846- entry_bits = PyTuple_New(width)
1847+ entry_bits = StaticTuple_New(width)
1848 for i from 0 <= i < num_prefix_bits:
1849+ # TODO: Use PyList_GetItem, or turn prefix_bits into a
1850+ # tuple/StaticTuple
1851 entry = prefix_bits[i]
1852 # SET_ITEM 'steals' a reference
1853 Py_INCREF(entry)
1854- PyTuple_SET_ITEM(entry_bits, i, entry)
1855+ StaticTuple_SET_ITEM(entry_bits, i, entry)
1856 value = PyString_FromStringAndSize(value_start, next_line - value_start)
1857 # The next entry bit needs the 'tail' from the prefix, and first part
1858 # of the line
1859@@ -288,7 +308,7 @@
1860 memcpy(c_entry + prefix_tail_len, line_start, next_null - line_start)
1861 Py_INCREF(entry)
1862 i = num_prefix_bits
1863- PyTuple_SET_ITEM(entry_bits, i, entry)
1864+ StaticTuple_SET_ITEM(entry_bits, i, entry)
1865 while next_null != last_null: # We have remaining bits
1866 i = i + 1
1867 if i > width:
1868@@ -301,11 +321,12 @@
1869 entry = PyString_FromStringAndSize(entry_start,
1870 next_null - entry_start)
1871 Py_INCREF(entry)
1872- PyTuple_SET_ITEM(entry_bits, i, entry)
1873+ StaticTuple_SET_ITEM(entry_bits, i, entry)
1874 if len(entry_bits) != width:
1875 raise AssertionError(
1876 'Incorrect number of elements (%d vs %d)'
1877 % (len(entry_bits)+1, width + 1))
1878+ entry_bits = StaticTuple_Intern(entry_bits)
1879 PyDict_SetItem(items, entry_bits, value)
1880 if len(items) != length:
1881 raise ValueError("item count (%d) mismatch for key %s,"
1882@@ -343,6 +364,8 @@
1883 _unknown = chk_map._unknown
1884 result = _InternalNode(search_key_func=search_key_func)
1885
1886+ if not StaticTuple_CheckExact(key):
1887+ raise TypeError('key %r is not a StaticTuple' % (key,))
1888 if not PyString_CheckExact(bytes):
1889 raise TypeError('bytes must be a plain string not %s' % (type(bytes),))
1890
1891@@ -384,7 +407,8 @@
1892 memcpy(c_item_prefix + prefix_length, cur, next_null - cur)
1893 flat_key = PyString_FromStringAndSize(next_null + 1,
1894 next_line - next_null - 1)
1895- PyDict_SetItem(items, item_prefix, (flat_key,))
1896+ flat_key = StaticTuple(flat_key).intern()
1897+ PyDict_SetItem(items, item_prefix, flat_key)
1898 cur = next_line + 1
1899 assert len(items) > 0
1900 result._items = items
1901@@ -398,4 +422,3 @@
1902 result._node_width = len(item_prefix)
1903 result._search_prefix = PyString_FromStringAndSize(prefix, prefix_length)
1904 return result
1905-
1906
1907=== modified file 'bzrlib/_dirstate_helpers_pyx.pyx'
1908--- bzrlib/_dirstate_helpers_pyx.pyx 2010-01-05 04:59:57 +0000
1909+++ bzrlib/_dirstate_helpers_pyx.pyx 2010-02-18 00:00:51 +0000
1910@@ -1,4 +1,4 @@
1911-# Copyright (C) 2007, 2008 Canonical Ltd
1912+# Copyright (C) 2007-2010 Canonical Ltd
1913 #
1914 # This program is free software; you can redistribute it and/or modify
1915 # it under the terms of the GNU General Public License as published by
1916
1917=== added file 'bzrlib/_export_c_api.h'
1918--- bzrlib/_export_c_api.h 1970-01-01 00:00:00 +0000
1919+++ bzrlib/_export_c_api.h 2010-02-18 00:00:51 +0000
1920@@ -0,0 +1,104 @@
1921+/* Copyright (C) 2009 Canonical Ltd
1922+ *
1923+ * This program is free software; you can redistribute it and/or modify
1924+ * it under the terms of the GNU General Public License as published by
1925+ * the Free Software Foundation; either version 2 of the License, or
1926+ * (at your option) any later version.
1927+ *
1928+ * This program is distributed in the hope that it will be useful,
1929+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1930+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1931+ * GNU General Public License for more details.
1932+ *
1933+ * You should have received a copy of the GNU General Public License
1934+ * along with this program; if not, write to the Free Software
1935+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1936+ */
1937+
1938+
1939+/* This file contains helper functions for exporting a C API for a CPython
1940+ * extension module.
1941+ */
1942+
1943+#ifndef _EXPORT_C_API_H_
1944+#define _EXPORT_C_API_H_
1945+
1946+static const char *_C_API_NAME = "_C_API";
1947+
1948+/**
1949+ * Add a C function to the modules _C_API
1950+ * This wraps the function in a PyCObject, and inserts that into a dict.
1951+ * The key of the dict is the function name, and the description is the
1952+ * signature of the function.
1953+ * This is generally called during a modules init_MODULE function.
1954+ *
1955+ * @param module A Python module (the one being initialized)
1956+ * @param funcname The name of the function being exported
1957+ * @param func A pointer to the function
1958+ * @param signature The C signature of the function
1959+ * @return 0 if everything is successful, -1 if there is a problem. An
1960+ * exception should also be set
1961+ */
1962+static int
1963+_export_function(PyObject *module, char *funcname, void *func, char *signature)
1964+{
1965+ PyObject *d = NULL;
1966+ PyObject *c_obj = NULL;
1967+
1968+ /* (char *) is because python2.4 declares this api as 'char *' rather than
1969+ * const char* which it really is.
1970+ */
1971+ d = PyObject_GetAttrString(module, (char *)_C_API_NAME);
1972+ if (!d) {
1973+ PyErr_Clear();
1974+ d = PyDict_New();
1975+ if (!d)
1976+ goto bad;
1977+ Py_INCREF(d);
1978+ if (PyModule_AddObject(module, (char *)_C_API_NAME, d) < 0)
1979+ goto bad;
1980+ }
1981+ c_obj = PyCObject_FromVoidPtrAndDesc(func, signature, 0);
1982+ if (!c_obj)
1983+ goto bad;
1984+ if (PyDict_SetItemString(d, funcname, c_obj) < 0)
1985+ goto bad;
1986+ Py_DECREF(d);
1987+ return 0;
1988+bad:
1989+ Py_XDECREF(c_obj);
1990+ Py_XDECREF(d);
1991+ return -1;
1992+}
1993+
1994+/* Note:
1995+ * It feels like more could be done here. Specifically, if you look at
1996+ * _static_tuple_c.h you can see some boilerplate where we have:
1997+ * #ifdef STATIC_TUPLE_MODULE // are we exporting or importing
1998+ * static RETVAL FUNCNAME PROTO;
1999+ * #else
2000+ * static RETVAL (*FUNCNAME) PROTO;
2001+ * #endif
2002+ *
2003+ * And then in _static_tuple_c.c we have
2004+ * int setup_c_api()
2005+ * {
2006+ * _export_function(module, #FUNCNAME, FUNCNAME, #PROTO);
2007+ * }
2008+ *
2009+ * And then in _static_tuple_c.h import_##MODULE
2010+ * struct function_definition functions[] = {
2011+ * {#FUNCNAME, (void **)&FUNCNAME, #RETVAL #PROTO},
2012+ * ...
2013+ * {NULL}};
2014+ *
2015+ * And some similar stuff for types. However, this would mean that we would
2016+ * need a way for the C preprocessor to build up a list of definitions to be
2017+ * generated, and then expand that list at the appropriate time.
2018+ * I would guess there would be a way to do this, but probably not without a
2019+ * lot of magic, and the end result probably wouldn't be very pretty to
2020+ * maintain. Perhaps python's dynamic nature has left me jaded about writing
2021+ * boilerplate....
2022+ */
2023+
2024+#endif // _EXPORT_C_API_H_
2025
2026=== modified file 'bzrlib/_groupcompress_pyx.pyx'
2027--- bzrlib/_groupcompress_pyx.pyx 2010-01-05 04:59:57 +0000
2028+++ bzrlib/_groupcompress_pyx.pyx 2010-02-18 00:00:52 +0000
2029@@ -1,4 +1,4 @@
2030-# Copyright (C) 2009 Canonical Ltd
2031+# Copyright (C) 2008, 2009, 2010 Canonical Ltd
2032 #
2033 # This program is free software; you can redistribute it and/or modify
2034 # it under the terms of the GNU General Public License as published by
2035@@ -31,10 +31,10 @@
2036
2037 cdef extern from *:
2038 ctypedef unsigned long size_t
2039- void * malloc(size_t)
2040- void * realloc(void *, size_t)
2041- void free(void *)
2042- void memcpy(void *, void *, size_t)
2043+ void * malloc(size_t) nogil
2044+ void * realloc(void *, size_t) nogil
2045+ void free(void *) nogil
2046+ void memcpy(void *, void *, size_t) nogil
2047
2048
2049 cdef extern from "delta.h":
2050@@ -44,19 +44,16 @@
2051 unsigned long agg_offset
2052 struct delta_index:
2053 pass
2054- delta_index * create_delta_index(source_info *src, delta_index *old)
2055+ delta_index * create_delta_index(source_info *src, delta_index *old) nogil
2056 delta_index * create_delta_index_from_delta(source_info *delta,
2057- delta_index *old)
2058- void free_delta_index(delta_index *index)
2059+ delta_index *old) nogil
2060+ void free_delta_index(delta_index *index) nogil
2061 void *create_delta(delta_index *indexes,
2062 void *buf, unsigned long bufsize,
2063- unsigned long *delta_size, unsigned long max_delta_size)
2064+ unsigned long *delta_size, unsigned long max_delta_size) nogil
2065 unsigned long get_delta_hdr_size(unsigned char **datap,
2066- unsigned char *top)
2067+ unsigned char *top) nogil
2068 Py_ssize_t DELTA_SIZE_MIN
2069- void *patch_delta(void *src_buf, unsigned long src_size,
2070- void *delta_buf, unsigned long delta_size,
2071- unsigned long *dst_size)
2072
2073
2074 cdef void *safe_malloc(size_t count) except NULL:
2075@@ -148,7 +145,8 @@
2076 src.buf = c_delta
2077 src.size = c_delta_size
2078 src.agg_offset = self._source_offset + unadded_bytes
2079- index = create_delta_index_from_delta(src, self._index)
2080+ with nogil:
2081+ index = create_delta_index_from_delta(src, self._index)
2082 self._source_offset = src.agg_offset + src.size
2083 if index != NULL:
2084 free_delta_index(self._index)
2085@@ -188,7 +186,8 @@
2086 self._source_offset = src.agg_offset + src.size
2087 # We delay creating the index on the first insert
2088 if source_location != 0:
2089- index = create_delta_index(src, self._index)
2090+ with nogil:
2091+ index = create_delta_index(src, self._index)
2092 if index != NULL:
2093 free_delta_index(self._index)
2094 self._index = index
2095@@ -201,7 +200,8 @@
2096
2097 # We know that self._index is already NULL, so whatever
2098 # create_delta_index returns is fine
2099- self._index = create_delta_index(&self._source_infos[0], NULL)
2100+ with nogil:
2101+ self._index = create_delta_index(&self._source_infos[0], NULL)
2102 assert self._index != NULL
2103
2104 cdef _expand_sources(self):
2105@@ -218,6 +218,7 @@
2106 cdef Py_ssize_t target_size
2107 cdef void * delta
2108 cdef unsigned long delta_size
2109+ cdef unsigned long c_max_delta_size
2110
2111 if self._index == NULL:
2112 if len(self._sources) == 0:
2113@@ -234,9 +235,11 @@
2114 # TODO: inline some of create_delta so we at least don't have to double
2115 # malloc, and can instead use PyString_FromStringAndSize, to
2116 # allocate the bytes into the final string
2117- delta = create_delta(self._index,
2118- target, target_size,
2119- &delta_size, max_delta_size)
2120+ c_max_delta_size = max_delta_size
2121+ with nogil:
2122+ delta = create_delta(self._index,
2123+ target, target_size,
2124+ &delta_size, c_max_delta_size)
2125 result = None
2126 if delta:
2127 result = PyString_FromStringAndSize(<char *>delta, delta_size)
2128@@ -276,7 +279,8 @@
2129
2130
2131 cdef unsigned char *_decode_copy_instruction(unsigned char *bytes,
2132- unsigned char cmd, unsigned int *offset, unsigned int *length): # cannot_raise
2133+ unsigned char cmd, unsigned int *offset,
2134+ unsigned int *length) nogil: # cannot_raise
2135 """Decode a copy instruction from the next few bytes.
2136
2137 A copy instruction is a variable number of bytes, so we will parse the
2138@@ -326,6 +330,7 @@
2139 cdef unsigned char *dst_buf, *out, cmd
2140 cdef Py_ssize_t size
2141 cdef unsigned int cp_off, cp_size
2142+ cdef int failed
2143
2144 data = <unsigned char *>delta
2145 top = data + delta_size
2146@@ -335,37 +340,49 @@
2147 result = PyString_FromStringAndSize(NULL, size)
2148 dst_buf = <unsigned char*>PyString_AS_STRING(result)
2149
2150- out = dst_buf
2151- while (data < top):
2152- cmd = data[0]
2153- data = data + 1
2154- if (cmd & 0x80):
2155- # Copy instruction
2156- data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)
2157- if (cp_off + cp_size < cp_size or
2158- cp_off + cp_size > source_size or
2159- cp_size > size):
2160- raise RuntimeError('Something wrong with:'
2161- ' cp_off = %s, cp_size = %s'
2162- ' source_size = %s, size = %s'
2163- % (cp_off, cp_size, source_size, size))
2164- memcpy(out, source + cp_off, cp_size)
2165- out = out + cp_size
2166- size = size - cp_size
2167- else:
2168- # Insert instruction
2169- if cmd == 0:
2170- # cmd == 0 is reserved for future encoding
2171- # extensions. In the mean time we must fail when
2172- # encountering them (might be data corruption).
2173- raise RuntimeError('Got delta opcode: 0, not supported')
2174- if (cmd > size):
2175- raise RuntimeError('Insert instruction longer than remaining'
2176- ' bytes: %d > %d' % (cmd, size))
2177- memcpy(out, data, cmd)
2178- out = out + cmd
2179- data = data + cmd
2180- size = size - cmd
2181+ failed = 0
2182+ with nogil:
2183+ out = dst_buf
2184+ while (data < top):
2185+ cmd = data[0]
2186+ data = data + 1
2187+ if (cmd & 0x80):
2188+ # Copy instruction
2189+ data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)
2190+ if (cp_off + cp_size < cp_size or
2191+ cp_off + cp_size > source_size or
2192+ cp_size > size):
2193+ failed = 1
2194+ break
2195+ memcpy(out, source + cp_off, cp_size)
2196+ out = out + cp_size
2197+ size = size - cp_size
2198+ else:
2199+ # Insert instruction
2200+ if cmd == 0:
2201+ # cmd == 0 is reserved for future encoding
2202+ # extensions. In the mean time we must fail when
2203+ # encountering them (might be data corruption).
2204+ failed = 2
2205+ break
2206+ if cmd > size:
2207+ failed = 3
2208+ break
2209+ memcpy(out, data, cmd)
2210+ out = out + cmd
2211+ data = data + cmd
2212+ size = size - cmd
2213+ if failed:
2214+ if failed == 1:
2215+ raise ValueError('Something wrong with:'
2216+ ' cp_off = %s, cp_size = %s'
2217+ ' source_size = %s, size = %s'
2218+ % (cp_off, cp_size, source_size, size))
2219+ elif failed == 2:
2220+ raise ValueError('Got delta opcode: 0, not supported')
2221+ elif failed == 3:
2222+ raise ValueError('Insert instruction longer than remaining'
2223+ ' bytes: %d > %d' % (cmd, size))
2224
2225 # sanity check
2226 if (data != top or size != 0):
2227
2228=== added file 'bzrlib/_import_c_api.h'
2229--- bzrlib/_import_c_api.h 1970-01-01 00:00:00 +0000
2230+++ bzrlib/_import_c_api.h 2010-02-18 00:00:51 +0000
2231@@ -0,0 +1,189 @@
2232+/* Copyright (C) 2009 Canonical Ltd
2233+ *
2234+ * This program is free software; you can redistribute it and/or modify
2235+ * it under the terms of the GNU General Public License as published by
2236+ * the Free Software Foundation; either version 2 of the License, or
2237+ * (at your option) any later version.
2238+ *
2239+ * This program is distributed in the hope that it will be useful,
2240+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2241+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2242+ * GNU General Public License for more details.
2243+ *
2244+ * You should have received a copy of the GNU General Public License
2245+ * along with this program; if not, write to the Free Software
2246+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2247+ */
2248+
2249+#ifndef _IMPORT_C_API_H_
2250+#define _IMPORT_C_API_H_
2251+
2252+/**
2253+ * Helper functions to eliminate some of the boilerplate when importing a C API
2254+ * from a CPython extension module.
2255+ *
2256+ * For more information see _export_c_api.h
2257+ */
2258+
2259+static const char *_C_API_NAME = "_C_API";
2260+
2261+/**
2262+ * Import a function from the _C_API_NAME dict that is part of module.
2263+ *
2264+ * @param module The Python module we are importing from
2265+ * the attribute _C_API_NAME will be used as a dictionary
2266+ * containing the function pointer we are looking for.
2267+ * @param funcname Name of the function we want to import
2268+ * @param func A pointer to the function handle where we will store the
2269+ * function.
2270+ * @param signature The C signature of the function. This is validated
2271+ * against the signature stored in the C api, to make sure
2272+ * there is no versioning skew.
2273+ */
2274+static int _import_function(PyObject *module, const char *funcname,
2275+ void **func, const char *signature)
2276+{
2277+ PyObject *d = NULL;
2278+ PyObject *c_obj = NULL;
2279+ const char *desc = NULL;
2280+
2281+ /* (char *) because Python2.4 defines this as (char *) rather than
2282+ * (const char *)
2283+ */
2284+ d = PyObject_GetAttrString(module, (char *)_C_API_NAME);
2285+ if (!d) {
2286+ // PyObject_GetAttrString sets an appropriate exception
2287+ goto bad;
2288+ }
2289+ c_obj = PyDict_GetItemString(d, funcname);
2290+ if (!c_obj) {
2291+ // PyDict_GetItemString does not set an exception
2292+ PyErr_Format(PyExc_AttributeError,
2293+ "Module %s did not export a function named %s\n",
2294+ PyModule_GetName(module), funcname);
2295+ goto bad;
2296+ }
2297+ desc = (char *)PyCObject_GetDesc(c_obj);
2298+ if (!desc || strcmp(desc, signature) != 0) {
2299+ if (desc == NULL) {
2300+ desc = "<null>";
2301+ }
2302+ PyErr_Format(PyExc_TypeError,
2303+ "C function %s.%s has wrong signature (expected %s, got %s)",
2304+ PyModule_GetName(module), funcname, signature, desc);
2305+ goto bad;
2306+ }
2307+ *func = PyCObject_AsVoidPtr(c_obj);
2308+ Py_DECREF(d);
2309+ return 0;
2310+bad:
2311+ Py_XDECREF(d);
2312+ return -1;
2313+}
2314+
2315+
2316+/**
2317+ * Get a pointer to an exported PyTypeObject.
2318+ *
2319+ * @param module The Python module we are importing from
2320+ * @param class_name Attribute of the module that should reference the
2321+ * Type object. Note that a PyTypeObject is the python
2322+ * description of the type, not the raw C structure.
2323+ * @return A Pointer to the requested type object. On error NULL will be
2324+ * returned and an exception will be set.
2325+ */
2326+static PyTypeObject *
2327+_import_type(PyObject *module, const char *class_name)
2328+{
2329+ PyObject *type = NULL;
2330+
2331+ type = PyObject_GetAttrString(module, (char *)class_name);
2332+ if (!type) {
2333+ goto bad;
2334+ }
2335+ if (!PyType_Check(type)) {
2336+ PyErr_Format(PyExc_TypeError,
2337+ "%s.%s is not a type object",
2338+ PyModule_GetName(module), class_name);
2339+ goto bad;
2340+ }
2341+ return (PyTypeObject *)type;
2342+bad:
2343+ Py_XDECREF(type);
2344+ return NULL;
2345+}
2346+
2347+
2348+struct function_description
2349+{
2350+ const char *name;
2351+ void **pointer;
2352+ const char *signature;
2353+};
2354+
2355+struct type_description
2356+{
2357+ const char *name;
2358+ PyTypeObject **pointer;
2359+};
2360+
2361+/**
2362+ * Helper for importing several functions and types in a data-driven manner.
2363+ *
2364+ * @param module The name of the module we will be importing
2365+ * @param functions A list of function_description objects, describing the
2366+ * functions being imported.
2367+ * The list should be terminated with {NULL} to indicate
2368+ * there are no more functions to import.
2369+ * @param types A list of type_description objects describing type
2370+ * objects that we want to import. The list should be
2371+ * terminated with {NULL} to indicate there are no more
2372+ * types to import.
2373+ * @return 0 on success, -1 on error and an exception should be set.
2374+ */
2375+
2376+static int
2377+_import_extension_module(const char *module_name,
2378+ struct function_description *functions,
2379+ struct type_description *types)
2380+{
2381+ PyObject *module = NULL;
2382+ struct function_description *cur_func;
2383+ struct type_description *cur_type;
2384+ int ret_code;
2385+
2386+ module = PyImport_ImportModule((char *)module_name);
2387+ if (!module)
2388+ goto bad;
2389+ if (functions != NULL) {
2390+ cur_func = functions;
2391+ while (cur_func->name != NULL) {
2392+ ret_code = _import_function(module, cur_func->name,
2393+ cur_func->pointer,
2394+ cur_func->signature);
2395+ if (ret_code < 0)
2396+ goto bad;
2397+ cur_func++;
2398+ }
2399+ }
2400+ if (types != NULL) {
2401+ PyTypeObject *type_p = NULL;
2402+ cur_type = types;
2403+ while (cur_type->name != NULL) {
2404+ type_p = _import_type(module, cur_type->name);
2405+ if (type_p == NULL)
2406+ goto bad;
2407+ *(cur_type->pointer) = type_p;
2408+ cur_type++;
2409+ }
2410+ }
2411+
2412+ Py_XDECREF(module);
2413+ return 0;
2414+bad:
2415+ Py_XDECREF(module);
2416+ return -1;
2417+}
2418+
2419+
2420+#endif // _IMPORT_C_API_H_
2421
2422=== modified file 'bzrlib/_knit_load_data_pyx.pyx'
2423--- bzrlib/_knit_load_data_pyx.pyx 2010-01-04 23:13:20 +0000
2424+++ bzrlib/_knit_load_data_pyx.pyx 2010-02-18 00:00:51 +0000
2425@@ -1,4 +1,4 @@
2426-# Copyright (C) 2007 Canonical Ltd
2427+# Copyright (C) 2007-2010 Canonical Ltd
2428 #
2429 # This program is free software; you can redistribute it and/or modify
2430 # it under the terms of the GNU General Public License as published by
2431
2432=== modified file 'bzrlib/_known_graph_py.py'
2433--- bzrlib/_known_graph_py.py 2009-08-25 18:45:40 +0000
2434+++ bzrlib/_known_graph_py.py 2010-02-18 00:00:52 +0000
2435@@ -1,4 +1,4 @@
2436-# Copyright (C) 2009 Canonical Ltd
2437+# Copyright (C) 2009, 2010 Canonical Ltd
2438 #
2439 # This program is free software; you can redistribute it and/or modify
2440 # it under the terms of the GNU General Public License as published by
2441@@ -17,6 +17,7 @@
2442 """Implementation of Graph algorithms when we have already loaded everything.
2443 """
2444
2445+from collections import deque
2446 from bzrlib import (
2447 errors,
2448 revision,
2449@@ -62,7 +63,7 @@
2450 :param parent_map: A dictionary mapping key => parent_keys
2451 """
2452 self._nodes = {}
2453- # Maps {sorted(revision_id, revision_id): heads}
2454+ # Maps {frozenset(revision_id, revision_id): heads}
2455 self._known_heads = {}
2456 self.do_cache = do_cache
2457 self._initialize_nodes(parent_map)
2458@@ -132,6 +133,71 @@
2459 # Update known_parent_gdfos for a key we couldn't process
2460 known_parent_gdfos[child_key] = known_gdfo
2461
2462+ def add_node(self, key, parent_keys):
2463+ """Add a new node to the graph.
2464+
2465+ If this fills in a ghost, then the gdfos of all children will be
2466+ updated accordingly.
2467+
2468+ :param key: The node being added. If this is a duplicate, this is a
2469+ no-op.
2470+ :param parent_keys: The parents of the given node.
2471+ :return: None (should we return if this was a ghost, etc?)
2472+ """
2473+ nodes = self._nodes
2474+ if key in nodes:
2475+ node = nodes[key]
2476+ if node.parent_keys is None:
2477+ node.parent_keys = parent_keys
2478+ # A ghost is being added, we can no-longer trust the heads
2479+ # cache, so clear it
2480+ self._known_heads.clear()
2481+ else:
2482+ # Make sure we compare a list to a list, as tuple != list.
2483+ parent_keys = list(parent_keys)
2484+ existing_parent_keys = list(node.parent_keys)
2485+ if parent_keys == existing_parent_keys:
2486+ return # Identical content
2487+ else:
2488+ raise ValueError('Parent key mismatch, existing node %s'
2489+ ' has parents of %s not %s'
2490+ % (key, existing_parent_keys, parent_keys))
2491+ else:
2492+ node = _KnownGraphNode(key, parent_keys)
2493+ nodes[key] = node
2494+ parent_gdfo = 0
2495+ for parent_key in parent_keys:
2496+ try:
2497+ parent_node = nodes[parent_key]
2498+ except KeyError:
2499+ parent_node = _KnownGraphNode(parent_key, None)
2500+ # Ghosts and roots have gdfo 1
2501+ parent_node.gdfo = 1
2502+ nodes[parent_key] = parent_node
2503+ if parent_gdfo < parent_node.gdfo:
2504+ parent_gdfo = parent_node.gdfo
2505+ parent_node.child_keys.append(key)
2506+ node.gdfo = parent_gdfo + 1
2507+ # Now fill the gdfo to all children
2508+ # Note that this loop is slightly inefficient, in that we may visit the
2509+ # same child (and its decendents) more than once, however, it is
2510+ # 'efficient' in that we only walk to nodes that would be updated,
2511+ # rather than all nodes
2512+ # We use a deque rather than a simple list stack, to go for BFD rather
2513+ # than DFD. So that if a longer path is possible, we walk it before we
2514+ # get to the final child
2515+ pending = deque([node])
2516+ while pending:
2517+ node = pending.popleft()
2518+ next_gdfo = node.gdfo + 1
2519+ for child_key in node.child_keys:
2520+ child = nodes[child_key]
2521+ if child.gdfo < next_gdfo:
2522+ # This child is being updated, we need to check its
2523+ # children
2524+ child.gdfo = next_gdfo
2525+ pending.append(child)
2526+
2527 def heads(self, keys):
2528 """Return the heads from amongst keys.
2529
2530@@ -281,3 +347,26 @@
2531 in tsort.merge_sort(as_parent_map, tip_key,
2532 mainline_revisions=None,
2533 generate_revno=True)]
2534+
2535+ def get_parent_keys(self, key):
2536+ """Get the parents for a key
2537+
2538+ Returns a list containg the parents keys. If the key is a ghost,
2539+ None is returned. A KeyError will be raised if the key is not in
2540+ the graph.
2541+
2542+ :param keys: Key to check (eg revision_id)
2543+ :return: A list of parents
2544+ """
2545+ return self._nodes[key].parent_keys
2546+
2547+ def get_child_keys(self, key):
2548+ """Get the children for a key
2549+
2550+ Returns a list containg the children keys. A KeyError will be raised
2551+ if the key is not in the graph.
2552+
2553+ :param keys: Key to check (eg revision_id)
2554+ :return: A list of children
2555+ """
2556+ return self._nodes[key].child_keys
2557
2558=== modified file 'bzrlib/_known_graph_pyx.pyx'
2559--- bzrlib/_known_graph_pyx.pyx 2010-01-05 05:03:51 +0000
2560+++ bzrlib/_known_graph_pyx.pyx 2010-02-18 00:00:51 +0000
2561@@ -1,4 +1,4 @@
2562-# Copyright (C) 2009 Canonical Ltd
2563+# Copyright (C) 2009, 2010 Canonical Ltd
2564 #
2565 # This program is free software; you can redistribute it and/or modify
2566 # it under the terms of the GNU General Public License as published by
2567@@ -51,6 +51,7 @@
2568
2569 void Py_INCREF(object)
2570
2571+from collections import deque
2572 import gc
2573
2574 from bzrlib import errors, revision
2575@@ -88,6 +89,18 @@
2576 PyList_Append(keys, child.key)
2577 return keys
2578
2579+ property parent_keys:
2580+ def __get__(self):
2581+ if self.parents is None:
2582+ return None
2583+
2584+ cdef _KnownGraphNode parent
2585+
2586+ keys = []
2587+ for parent in self.parents:
2588+ PyList_Append(keys, parent.key)
2589+ return keys
2590+
2591 cdef clear_references(self):
2592 self.parents = None
2593 self.children = None
2594@@ -180,7 +193,7 @@
2595 """This is a class which assumes we already know the full graph."""
2596
2597 cdef public object _nodes
2598- cdef object _known_heads
2599+ cdef public object _known_heads
2600 cdef public int do_cache
2601
2602 def __init__(self, parent_map, do_cache=True):
2603@@ -220,6 +233,28 @@
2604 node = <_KnownGraphNode>temp_node
2605 return node
2606
2607+ cdef _populate_parents(self, _KnownGraphNode node, parent_keys):
2608+ cdef Py_ssize_t num_parent_keys, pos
2609+ cdef _KnownGraphNode parent_node
2610+
2611+ num_parent_keys = len(parent_keys)
2612+ # We know how many parents, so we pre allocate the tuple
2613+ parent_nodes = PyTuple_New(num_parent_keys)
2614+ for pos from 0 <= pos < num_parent_keys:
2615+ # Note: it costs us 10ms out of 40ms to lookup all of these
2616+ # parents, it doesn't seem to be an allocation overhead,
2617+ # but rather a lookup overhead. There doesn't seem to be
2618+ # a way around it, and that is one reason why
2619+ # KnownGraphNode maintains a direct pointer to the parent
2620+ # node.
2621+ # We use [] because parent_keys may be a tuple or list
2622+ parent_node = self._get_or_create_node(parent_keys[pos])
2623+ # PyTuple_SET_ITEM will steal a reference, so INCREF first
2624+ Py_INCREF(parent_node)
2625+ PyTuple_SET_ITEM(parent_nodes, pos, parent_node)
2626+ PyList_Append(parent_node.children, node)
2627+ node.parents = parent_nodes
2628+
2629 def _initialize_nodes(self, parent_map):
2630 """Populate self._nodes.
2631
2632@@ -230,7 +265,7 @@
2633 child keys,
2634 """
2635 cdef PyObject *temp_key, *temp_parent_keys, *temp_node
2636- cdef Py_ssize_t pos, pos2, num_parent_keys
2637+ cdef Py_ssize_t pos
2638 cdef _KnownGraphNode node
2639 cdef _KnownGraphNode parent_node
2640
2641@@ -241,24 +276,8 @@
2642 while PyDict_Next(parent_map, &pos, &temp_key, &temp_parent_keys):
2643 key = <object>temp_key
2644 parent_keys = <object>temp_parent_keys
2645- num_parent_keys = len(parent_keys)
2646 node = self._get_or_create_node(key)
2647- # We know how many parents, so we pre allocate the tuple
2648- parent_nodes = PyTuple_New(num_parent_keys)
2649- for pos2 from 0 <= pos2 < num_parent_keys:
2650- # Note: it costs us 10ms out of 40ms to lookup all of these
2651- # parents, it doesn't seem to be an allocation overhead,
2652- # but rather a lookup overhead. There doesn't seem to be
2653- # a way around it, and that is one reason why
2654- # KnownGraphNode maintains a direct pointer to the parent
2655- # node.
2656- # We use [] because parent_keys may be a tuple or list
2657- parent_node = self._get_or_create_node(parent_keys[pos2])
2658- # PyTuple_SET_ITEM will steal a reference, so INCREF first
2659- Py_INCREF(parent_node)
2660- PyTuple_SET_ITEM(parent_nodes, pos2, parent_node)
2661- PyList_Append(parent_node.children, node)
2662- node.parents = parent_nodes
2663+ self._populate_parents(node, parent_keys)
2664
2665 def _find_tails(self):
2666 cdef PyObject *temp_node
2667@@ -322,6 +341,76 @@
2668 # anymore
2669 child.seen = 0
2670
2671+ def add_node(self, key, parent_keys):
2672+ """Add a new node to the graph.
2673+
2674+ If this fills in a ghost, then the gdfos of all children will be
2675+ updated accordingly.
2676+
2677+ :param key: The node being added. If this is a duplicate, this is a
2678+ no-op.
2679+ :param parent_keys: The parents of the given node.
2680+ :return: None (should we return if this was a ghost, etc?)
2681+ """
2682+ cdef PyObject *maybe_node
2683+ cdef _KnownGraphNode node, parent_node, child_node
2684+ cdef long parent_gdfo, next_gdfo
2685+
2686+ maybe_node = PyDict_GetItem(self._nodes, key)
2687+ if maybe_node != NULL:
2688+ node = <_KnownGraphNode>maybe_node
2689+ if node.parents is None:
2690+ # We are filling in a ghost
2691+ self._populate_parents(node, parent_keys)
2692+ # We can't trust cached heads anymore
2693+ self._known_heads.clear()
2694+ else: # Ensure that the parent_key list matches
2695+ existing_parent_keys = []
2696+ for parent_node in node.parents:
2697+ existing_parent_keys.append(parent_node.key)
2698+ # Make sure we use a list for the comparison, in case it was a
2699+ # tuple, etc
2700+ parent_keys = list(parent_keys)
2701+ if existing_parent_keys == parent_keys:
2702+ # Exact match, nothing more to do
2703+ return
2704+ else:
2705+ raise ValueError('Parent key mismatch, existing node %s'
2706+ ' has parents of %s not %s'
2707+ % (key, existing_parent_keys, parent_keys))
2708+ else:
2709+ node = _KnownGraphNode(key)
2710+ PyDict_SetItem(self._nodes, key, node)
2711+ self._populate_parents(node, parent_keys)
2712+ parent_gdfo = 0
2713+ for parent_node in node.parents:
2714+ if parent_node.gdfo == -1:
2715+ # This is a newly introduced ghost, so it gets gdfo of 1
2716+ parent_node.gdfo = 1
2717+ if parent_gdfo < parent_node.gdfo:
2718+ parent_gdfo = parent_node.gdfo
2719+ node.gdfo = parent_gdfo + 1
2720+ # Now fill the gdfo to all children
2721+ # Note that this loop is slightly inefficient, in that we may visit the
2722+ # same child (and its decendents) more than once, however, it is
2723+ # 'efficient' in that we only walk to nodes that would be updated,
2724+ # rather than all nodes
2725+ # We use a deque rather than a simple list stack, to go for BFD rather
2726+ # than DFD. So that if a longer path is possible, we walk it before we
2727+ # get to the final child
2728+ pending = deque([node])
2729+ pending_popleft = pending.popleft
2730+ pending_append = pending.append
2731+ while pending:
2732+ node = pending_popleft()
2733+ next_gdfo = node.gdfo + 1
2734+ for child_node in node.children:
2735+ if child_node.gdfo < next_gdfo:
2736+ # This child is being updated, we need to check its
2737+ # children
2738+ child_node.gdfo = next_gdfo
2739+ pending_append(child_node)
2740+
2741 def heads(self, keys):
2742 """Return the heads from amongst keys.
2743
2744@@ -549,6 +638,29 @@
2745 # shown a specific impact, yet.
2746 sorter = _MergeSorter(self, tip_key)
2747 return sorter.topo_order()
2748+
2749+ def get_parent_keys(self, key):
2750+ """Get the parents for a key
2751+
2752+ Returns a list containg the parents keys. If the key is a ghost,
2753+ None is returned. A KeyError will be raised if the key is not in
2754+ the graph.
2755+
2756+ :param keys: Key to check (eg revision_id)
2757+ :return: A list of parents
2758+ """
2759+ return self._nodes[key].parent_keys
2760+
2761+ def get_child_keys(self, key):
2762+ """Get the children for a key
2763+
2764+ Returns a list containg the children keys. A KeyError will be raised
2765+ if the key is not in the graph.
2766+
2767+ :param keys: Key to check (eg revision_id)
2768+ :return: A list of children
2769+ """
2770+ return self._nodes[key].child_keys
2771
2772
2773 cdef class _MergeSortNode:
2774
2775=== modified file 'bzrlib/_patiencediff_c.c'
2776--- bzrlib/_patiencediff_c.c 2009-03-23 14:59:43 +0000
2777+++ bzrlib/_patiencediff_c.c 2010-02-18 00:00:51 +0000
2778@@ -298,7 +298,7 @@
2779 apos = SENTINEL;
2780 /* loop through all lines in the linked list */
2781 for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) {
2782- /* the index is lower than alo, the the next line */
2783+ /* the index is lower than alo, continue to the next line */
2784 if (i < alo) {
2785 h[equiv].a_pos = i;
2786 continue;
2787@@ -319,7 +319,7 @@
2788 /* check for duplicates of this line in lines_b[blo:bhi] */
2789 /* loop through all lines in the linked list */
2790 for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) {
2791- /* the index is lower than blo, the the next line */
2792+ /* the index is lower than blo, continue to the next line */
2793 if (i < blo) {
2794 h[equiv].b_pos = i;
2795 continue;
2796
2797=== modified file 'bzrlib/_readdir_pyx.pyx'
2798--- bzrlib/_readdir_pyx.pyx 2009-12-23 02:19:04 +0000
2799+++ bzrlib/_readdir_pyx.pyx 2010-02-18 00:00:50 +0000
2800@@ -1,4 +1,4 @@
2801-# Copyright (C) 2006, 2008, 2009 Canonical Ltd
2802+# Copyright (C) 2006, 2008, 2009, 2010 Canonical Ltd
2803 #
2804 # This program is free software; you can redistribute it and/or modify
2805 # it under the terms of the GNU General Public License as published by
2806
2807=== modified file 'bzrlib/_rio_pyx.pyx'
2808--- bzrlib/_rio_pyx.pyx 2010-01-05 04:59:57 +0000
2809+++ bzrlib/_rio_pyx.pyx 2010-02-18 00:00:52 +0000
2810@@ -1,4 +1,4 @@
2811-# Copyright (C) 2009 Canonical Ltd
2812+# Copyright (C) 2009, 2010 Canonical Ltd
2813 #
2814 # This program is free software; you can redistribute it and/or modify
2815 # it under the terms of the GNU General Public License as published by
2816
2817=== added file 'bzrlib/_simple_set_pyx.pxd'
2818--- bzrlib/_simple_set_pyx.pxd 1970-01-01 00:00:00 +0000
2819+++ bzrlib/_simple_set_pyx.pxd 2010-02-18 00:00:50 +0000
2820@@ -0,0 +1,91 @@
2821+# Copyright (C) 2009, 2010 Canonical Ltd
2822+#
2823+# This program is free software; you can redistribute it and/or modify
2824+# it under the terms of the GNU General Public License as published by
2825+# the Free Software Foundation; either version 2 of the License, or
2826+# (at your option) any later version.
2827+#
2828+# This program is distributed in the hope that it will be useful,
2829+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2830+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2831+# GNU General Public License for more details.
2832+#
2833+# You should have received a copy of the GNU General Public License
2834+# along with this program; if not, write to the Free Software
2835+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2836+
2837+"""Interface definition of a class like PySet but without caching the hash.
2838+
2839+This is generally useful when you want to 'intern' objects, etc. Note that this
2840+differs from Set in that we:
2841+ 1) Don't have all of the .intersection, .difference, etc functions
2842+ 2) Do return the object from the set via queries
2843+ eg. SimpleSet.add(key) => saved_key and SimpleSet[key] => saved_key
2844+"""
2845+
2846+cdef extern from "Python.h":
2847+ ctypedef struct PyObject:
2848+ pass
2849+
2850+
2851+cdef public api class SimpleSet [object SimpleSetObject, type SimpleSet_Type]:
2852+ """A class similar to PySet, but with simpler implementation.
2853+
2854+ The main advantage is that this class uses only 2N memory to store N
2855+ objects rather than 4N memory. The main trade-off is that we do not cache
2856+ the hash value of saved objects. As such, it is assumed that computing the
2857+ hash will be cheap (such as strings or tuples of strings, etc.)
2858+
2859+ This also differs in that you can get back the objects that are stored
2860+ (like a dict), but we also don't implement the complete list of 'set'
2861+ operations (difference, intersection, etc).
2862+ """
2863+ # Data structure definition:
2864+ # This is a basic hash table using open addressing.
2865+ # http://en.wikipedia.org/wiki/Open_addressing
2866+ # Basically that means we keep an array of pointers to Python objects
2867+ # (called a table). Each location in the array is called a 'slot'.
2868+ #
2869+ # An empty slot holds a NULL pointer, a slot where there was an item
2870+ # which was then deleted will hold a pointer to _dummy, and a filled slot
2871+ # points at the actual object which fills that slot.
2872+ #
2873+ # The table is always a power of two, and the default location where an
2874+ # object is inserted is at hash(object) & (table_size - 1)
2875+ #
2876+ # If there is a collision, then we search for another location. The
2877+ # specific algorithm is in _lookup. We search until we:
2878+ # find the object
2879+ # find an equivalent object (by tp_richcompare(obj1, obj2, Py_EQ))
2880+ # find a NULL slot
2881+ #
2882+ # When an object is deleted, we set its slot to _dummy. this way we don't
2883+ # have to track whether there was a collision, and find the corresponding
2884+ # keys. (The collision resolution algorithm makes that nearly impossible
2885+ # anyway, because it depends on the upper bits of the hash.)
2886+ # The main effect of this, is that if we find _dummy, then we can insert
2887+ # an object there, but we have to keep searching until we find NULL to
2888+ # know that the object is not present elsewhere.
2889+
2890+ cdef Py_ssize_t _used # active
2891+ cdef Py_ssize_t _fill # active + dummy
2892+ cdef Py_ssize_t _mask # Table contains (mask+1) slots, a power of 2
2893+ cdef PyObject **_table # Pyrex/Cython doesn't support arrays to 'object'
2894+ # so we manage it manually
2895+
2896+ cdef PyObject *_get(self, object key) except? NULL
2897+ cdef object _add(self, key)
2898+ cdef int _discard(self, key) except -1
2899+ cdef int _insert_clean(self, PyObject *key) except -1
2900+ cdef Py_ssize_t _resize(self, Py_ssize_t min_unused) except -1
2901+
2902+
2903+# TODO: might want to export the C api here, though it is all available from
2904+# the class object...
2905+cdef api SimpleSet SimpleSet_New()
2906+cdef api object SimpleSet_Add(object self, object key)
2907+cdef api int SimpleSet_Contains(object self, object key) except -1
2908+cdef api int SimpleSet_Discard(object self, object key) except -1
2909+cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL
2910+cdef api Py_ssize_t SimpleSet_Size(object self) except -1
2911+cdef api int SimpleSet_Next(object self, Py_ssize_t *pos, PyObject **key) except -1
2912
2913=== added file 'bzrlib/_simple_set_pyx.pyx'
2914--- bzrlib/_simple_set_pyx.pyx 1970-01-01 00:00:00 +0000
2915+++ bzrlib/_simple_set_pyx.pyx 2010-02-18 00:00:50 +0000
2916@@ -0,0 +1,592 @@
2917+# Copyright (C) 2009, 2010 Canonical Ltd
2918+#
2919+# This program is free software; you can redistribute it and/or modify
2920+# it under the terms of the GNU General Public License as published by
2921+# the Free Software Foundation; either version 2 of the License, or
2922+# (at your option) any later version.
2923+#
2924+# This program is distributed in the hope that it will be useful,
2925+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2926+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2927+# GNU General Public License for more details.
2928+#
2929+# You should have received a copy of the GNU General Public License
2930+# along with this program; if not, write to the Free Software
2931+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2932+
2933+"""Definition of a class that is similar to Set with some small changes."""
2934+
2935+cdef extern from "python-compat.h":
2936+ pass
2937+
2938+cdef extern from "Python.h":
2939+ ctypedef unsigned long size_t
2940+ ctypedef long (*hashfunc)(PyObject*) except -1
2941+ ctypedef object (*richcmpfunc)(PyObject *, PyObject *, int)
2942+ ctypedef int (*visitproc)(PyObject *, void *)
2943+ ctypedef int (*traverseproc)(PyObject *, visitproc, void *)
2944+ int Py_EQ
2945+ void Py_INCREF(PyObject *)
2946+ void Py_DECREF(PyObject *)
2947+ ctypedef struct PyTypeObject:
2948+ hashfunc tp_hash
2949+ richcmpfunc tp_richcompare
2950+ traverseproc tp_traverse
2951+
2952+ PyTypeObject *Py_TYPE(PyObject *)
2953+ # Note: *Don't* use hash(), Pyrex 0.9.8.5 thinks it returns an 'int', and
2954+ # thus silently truncates to 32-bits on 64-bit machines.
2955+ long PyObject_Hash(PyObject *) except -1
2956+
2957+ void *PyMem_Malloc(size_t nbytes)
2958+ void PyMem_Free(void *)
2959+ void memset(void *, int, size_t)
2960+
2961+
2962+# Dummy is an object used to mark nodes that have been deleted. Since
2963+# collisions require us to move a node to an alternative location, if we just
2964+# set an entry to NULL on delete, we won't find any relocated nodes.
2965+# We have to use _dummy_obj because we need to keep a refcount to it, but we
2966+# also use _dummy as a pointer, because it avoids having to put <PyObject*> all
2967+# over the code base.
2968+cdef object _dummy_obj
2969+cdef PyObject *_dummy
2970+_dummy_obj = object()
2971+_dummy = <PyObject *>_dummy_obj
2972+
2973+
2974+cdef object _NotImplemented
2975+_NotImplemented = NotImplemented
2976+
2977+
2978+cdef int _is_equal(PyObject *this, long this_hash, PyObject *other) except -1:
2979+ cdef long other_hash
2980+
2981+ if this == other:
2982+ return 1
2983+ other_hash = PyObject_Hash(other)
2984+ if other_hash != this_hash:
2985+ return 0
2986+
2987+ # This implements a subset of the PyObject_RichCompareBool functionality.
2988+ # Namely it:
2989+ # 1) Doesn't try to do anything with old-style classes
2990+ # 2) Assumes that both objects have a tp_richcompare implementation, and
2991+ # that if that is not enough to compare equal, then they are not
2992+ # equal. (It doesn't try to cast them both to some intermediate form
2993+ # that would compare equal.)
2994+ res = Py_TYPE(this).tp_richcompare(this, other, Py_EQ)
2995+ if res is _NotImplemented:
2996+ res = Py_TYPE(other).tp_richcompare(other, this, Py_EQ)
2997+ if res is _NotImplemented:
2998+ return 0
2999+ if res:
3000+ return 1
3001+ return 0
3002+
3003+
3004+cdef public api class SimpleSet [object SimpleSetObject, type SimpleSet_Type]:
3005+ """This class can be used to track canonical forms for objects.
3006+
3007+ It is similar in function to the interned dictionary that is used by
3008+ strings. However:
3009+
3010+ 1) It assumes that hash(obj) is cheap, so does not need to inline a copy
3011+ of it
3012+ 2) It only stores one reference to the object, rather than 2 (key vs
3013+ key:value)
3014+
3015+ As such, it uses 1/3rd the amount of memory to store a pointer to the
3016+ interned object.
3017+ """
3018+ # Attributes are defined in the .pxd file
3019+ DEF DEFAULT_SIZE=1024
3020+
3021+ def __init__(self):
3022+ cdef Py_ssize_t size, n_bytes
3023+
3024+ size = DEFAULT_SIZE
3025+ self._mask = size - 1
3026+ self._used = 0
3027+ self._fill = 0
3028+ n_bytes = sizeof(PyObject*) * size;
3029+ self._table = <PyObject **>PyMem_Malloc(n_bytes)
3030+ if self._table == NULL:
3031+ raise MemoryError()
3032+ memset(self._table, 0, n_bytes)
3033+
3034+ def __dealloc__(self):
3035+ if self._table != NULL:
3036+ PyMem_Free(self._table)
3037+ self._table = NULL
3038+
3039+ property used:
3040+ def __get__(self):
3041+ return self._used
3042+
3043+ property fill:
3044+ def __get__(self):
3045+ return self._fill
3046+
3047+ property mask:
3048+ def __get__(self):
3049+ return self._mask
3050+
3051+ def _memory_size(self):
3052+ """Return the number of bytes of memory consumed by this class."""
3053+ return sizeof(self) + (sizeof(PyObject*)*(self._mask + 1))
3054+
3055+ def __len__(self):
3056+ return self._used
3057+
3058+ def _test_lookup(self, key):
3059+ cdef PyObject **slot
3060+
3061+ slot = _lookup(self, key)
3062+ if slot[0] == NULL:
3063+ res = '<null>'
3064+ elif slot[0] == _dummy:
3065+ res = '<dummy>'
3066+ else:
3067+ res = <object>slot[0]
3068+ return <int>(slot - self._table), res
3069+
3070+ def __contains__(self, key):
3071+ """Is key present in this SimpleSet."""
3072+ cdef PyObject **slot
3073+
3074+ slot = _lookup(self, key)
3075+ if slot[0] == NULL or slot[0] == _dummy:
3076+ return False
3077+ return True
3078+
3079+ cdef PyObject *_get(self, object key) except? NULL:
3080+ """Return the object (or nothing) define at the given location."""
3081+ cdef PyObject **slot
3082+
3083+ slot = _lookup(self, key)
3084+ if slot[0] == NULL or slot[0] == _dummy:
3085+ return NULL
3086+ return slot[0]
3087+
3088+ def __getitem__(self, key):
3089+ """Return a stored item that is equivalent to key."""
3090+ cdef PyObject *py_val
3091+
3092+ py_val = self._get(key)
3093+ if py_val == NULL:
3094+ raise KeyError("Key %s is not present" % key)
3095+ val = <object>(py_val)
3096+ return val
3097+
3098+ cdef int _insert_clean(self, PyObject *key) except -1:
3099+ """Insert a key into self.table.
3100+
3101+ This is only meant to be used during times like '_resize',
3102+ as it makes a lot of assuptions about keys not already being present,
3103+ and there being no dummy entries.
3104+ """
3105+ cdef size_t i, n_lookup
3106+ cdef long the_hash
3107+ cdef PyObject **table, **slot
3108+ cdef Py_ssize_t mask
3109+
3110+ mask = self._mask
3111+ table = self._table
3112+
3113+ the_hash = PyObject_Hash(key)
3114+ i = the_hash
3115+ for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever
3116+ slot = &table[i & mask]
3117+ if slot[0] == NULL:
3118+ slot[0] = key
3119+ self._fill = self._fill + 1
3120+ self._used = self._used + 1
3121+ return 1
3122+ i = i + 1 + n_lookup
3123+ raise RuntimeError('ran out of slots.')
3124+
3125+ def _py_resize(self, min_used):
3126+ """Do not use this directly, it is only exposed for testing."""
3127+ return self._resize(min_used)
3128+
3129+ cdef Py_ssize_t _resize(self, Py_ssize_t min_used) except -1:
3130+ """Resize the internal table.
3131+
3132+ The final table will be big enough to hold at least min_used entries.
3133+ We will copy the data from the existing table over, leaving out dummy
3134+ entries.
3135+
3136+ :return: The new size of the internal table
3137+ """
3138+ cdef Py_ssize_t new_size, n_bytes, remaining
3139+ cdef PyObject **new_table, **old_table, **slot
3140+
3141+ new_size = DEFAULT_SIZE
3142+ while new_size <= min_used and new_size > 0:
3143+ new_size = new_size << 1
3144+ # We rolled over our signed size field
3145+ if new_size <= 0:
3146+ raise MemoryError()
3147+ # Even if min_used == self._mask + 1, and we aren't changing the actual
3148+ # size, we will still run the algorithm so that dummy entries are
3149+ # removed
3150+ # TODO: Test this
3151+ # if new_size < self._used:
3152+ # raise RuntimeError('cannot shrink SimpleSet to something'
3153+ # ' smaller than the number of used slots.')
3154+ n_bytes = sizeof(PyObject*) * new_size;
3155+ new_table = <PyObject **>PyMem_Malloc(n_bytes)
3156+ if new_table == NULL:
3157+ raise MemoryError()
3158+
3159+ old_table = self._table
3160+ self._table = new_table
3161+ memset(self._table, 0, n_bytes)
3162+ self._mask = new_size - 1
3163+ self._used = 0
3164+ remaining = self._fill
3165+ self._fill = 0
3166+
3167+ # Moving everything to the other table is refcount neutral, so we don't
3168+ # worry about it.
3169+ slot = old_table
3170+ while remaining > 0:
3171+ if slot[0] == NULL: # unused slot
3172+ pass
3173+ elif slot[0] == _dummy: # dummy slot
3174+ remaining = remaining - 1
3175+ else: # active slot
3176+ remaining = remaining - 1
3177+ self._insert_clean(slot[0])
3178+ slot = slot + 1
3179+ PyMem_Free(old_table)
3180+ return new_size
3181+
3182+ def add(self, key):
3183+ """Similar to set.add(), start tracking this key.
3184+
3185+ There is one small difference, which is that we return the object that
3186+ is stored at the given location. (which is closer to the
3187+ dict.setdefault() functionality.)
3188+ """
3189+ return self._add(key)
3190+
3191+ cdef object _add(self, key):
3192+ cdef PyObject **slot, *py_key
3193+ cdef int added
3194+
3195+ py_key = <PyObject *>key
3196+ if (Py_TYPE(py_key).tp_richcompare == NULL
3197+ or Py_TYPE(py_key).tp_hash == NULL):
3198+ raise TypeError('Types added to SimpleSet must implement'
3199+ ' both tp_richcompare and tp_hash')
3200+ added = 0
3201+ # We need at least one empty slot
3202+ assert self._used < self._mask
3203+ slot = _lookup(self, key)
3204+ if (slot[0] == NULL):
3205+ Py_INCREF(py_key)
3206+ self._fill = self._fill + 1
3207+ self._used = self._used + 1
3208+ slot[0] = py_key
3209+ added = 1
3210+ elif (slot[0] == _dummy):
3211+ Py_INCREF(py_key)
3212+ self._used = self._used + 1
3213+ slot[0] = py_key
3214+ added = 1
3215+ # No else: clause. If _lookup returns a pointer to
3216+ # a live object, then we already have a value at this location.
3217+ retval = <object>(slot[0])
3218+ # PySet and PyDict use a 2-3rds full algorithm, we'll follow suit
3219+ if added and (self._fill * 3) >= ((self._mask + 1) * 2):
3220+ # However, we always work for a load factor of 2:1
3221+ self._resize(self._used * 2)
3222+ # Even if we resized and ended up moving retval into a different slot,
3223+ # it is still the value that is held at the slot equivalent to 'key',
3224+ # so we can still return it
3225+ return retval
3226+
3227+ def discard(self, key):
3228+ """Remove key from the set, whether it exists or not.
3229+
3230+ :return: False if the item did not exist, True if it did
3231+ """
3232+ if self._discard(key):
3233+ return True
3234+ return False
3235+
3236+ cdef int _discard(self, key) except -1:
3237+ cdef PyObject **slot, *py_key
3238+
3239+ slot = _lookup(self, key)
3240+ if slot[0] == NULL or slot[0] == _dummy:
3241+ return 0
3242+ self._used = self._used - 1
3243+ Py_DECREF(slot[0])
3244+ slot[0] = _dummy
3245+ # PySet uses the heuristic: If more than 1/5 are dummies, then resize
3246+ # them away
3247+ # if ((so->_fill - so->_used) * 5 < so->mask)
3248+ # However, we are planning on using this as an interning structure, in
3249+ # which we will be putting a lot of objects. And we expect that large
3250+ # groups of them are going to have the same lifetime.
3251+ # Dummy entries hurt a little bit because they cause the lookup to keep
3252+ # searching, but resizing is also rather expensive
3253+ # For now, we'll just use their algorithm, but we may want to revisit
3254+ # it
3255+ if ((self._fill - self._used) * 5 > self._mask):
3256+ self._resize(self._used * 2)
3257+ return 1
3258+
3259+ def __iter__(self):
3260+ return _SimpleSet_iterator(self)
3261+
3262+
3263+cdef class _SimpleSet_iterator:
3264+ """Iterator over the SimpleSet structure."""
3265+
3266+ cdef Py_ssize_t pos
3267+ cdef SimpleSet set
3268+ cdef Py_ssize_t _used # track if things have been mutated while iterating
3269+ cdef Py_ssize_t len # number of entries left
3270+
3271+ def __init__(self, obj):
3272+ self.set = obj
3273+ self.pos = 0
3274+ self._used = self.set._used
3275+ self.len = self.set._used
3276+
3277+ def __iter__(self):
3278+ return self
3279+
3280+ def __next__(self):
3281+ cdef Py_ssize_t mask, i
3282+ cdef PyObject *key
3283+
3284+ if self.set is None:
3285+ raise StopIteration
3286+ if self.set._used != self._used:
3287+ # Force this exception to continue to be raised
3288+ self._used = -1
3289+ raise RuntimeError("Set size changed during iteration")
3290+ if not SimpleSet_Next(self.set, &self.pos, &key):
3291+ self.set = None
3292+ raise StopIteration
3293+ # we found something
3294+ the_key = <object>key # INCREF
3295+ self.len = self.len - 1
3296+ return the_key
3297+
3298+ def __length_hint__(self):
3299+ if self.set is not None and self._used == self.set._used:
3300+ return self.len
3301+ return 0
3302+
3303+
3304+
3305+cdef api SimpleSet SimpleSet_New():
3306+ """Create a new SimpleSet object."""
3307+ return SimpleSet()
3308+
3309+
3310+cdef SimpleSet _check_self(object self):
3311+ """Check that the parameter is not None.
3312+
3313+ Pyrex/Cython will do type checking, but only to ensure that an object is
3314+ either the right type or None. You can say "object foo not None" for pure
3315+ python functions, but not for C functions.
3316+ So this is just a helper for all the apis that need to do the check.
3317+ """
3318+ cdef SimpleSet true_self
3319+ if self is None:
3320+ raise TypeError('self must not be None')
3321+ true_self = self
3322+ return true_self
3323+
3324+
3325+cdef PyObject **_lookup(SimpleSet self, object key) except NULL:
3326+ """Find the slot where 'key' would fit.
3327+
3328+ This is the same as a dicts 'lookup' function.
3329+
3330+ :param key: An object we are looking up
3331+ :param hash: The hash for key
3332+ :return: The location in self.table where key should be put.
3333+ location == NULL is an exception, but (*location) == NULL just
3334+ indicates the slot is empty and can be used.
3335+ """
3336+ # This uses Quadratic Probing:
3337+ # http://en.wikipedia.org/wiki/Quadratic_probing
3338+ # with c1 = c2 = 1/2
3339+ # This leads to probe locations at:
3340+ # h0 = hash(k1)
3341+ # h1 = h0 + 1
3342+ # h2 = h0 + 3 = h1 + 1 + 1
3343+ # h3 = h0 + 6 = h2 + 1 + 2
3344+ # h4 = h0 + 10 = h2 + 1 + 3
3345+ # Note that all of these are '& mask', but that is computed *after* the
3346+ # offset.
3347+ # This differs from the algorithm used by Set and Dict. Which, effectively,
3348+ # use double-hashing, and a step size that starts large, but dwindles to
3349+ # stepping one-by-one.
3350+ # This gives more 'locality' in that if you have a collision at offset X,
3351+ # the first fallback is X+1, which is fast to check. However, that means
3352+ # that an object w/ hash X+1 will also check there, and then X+2 next.
3353+ # However, for objects with differing hashes, their chains are different.
3354+ # The former checks X, X+1, X+3, ... the latter checks X+1, X+2, X+4, ...
3355+ # So different hashes diverge quickly.
3356+ # A bigger problem is that we *only* ever use the lowest bits of the hash
3357+ # So all integers (x + SIZE*N) will resolve into the same bucket, and all
3358+ # use the same collision resolution. We may want to try to find a way to
3359+ # incorporate the upper bits of the hash with quadratic probing. (For
3360+ # example, X, X+1, X+3+some_upper_bits, X+6+more_upper_bits, etc.)
3361+ cdef size_t i, n_lookup
3362+ cdef Py_ssize_t mask
3363+ cdef long key_hash
3364+ cdef PyObject **table, **slot, *cur, **free_slot, *py_key
3365+
3366+ py_key = <PyObject *>key
3367+ # Note: avoid using hash(obj) because of a bug w/ pyrex 0.9.8.5 and 64-bit
3368+ # (it treats hash() as returning an 'int' rather than a 'long')
3369+ key_hash = PyObject_Hash(py_key)
3370+ i = <size_t>key_hash
3371+ mask = self._mask
3372+ table = self._table
3373+ free_slot = NULL
3374+ for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever
3375+ slot = &table[i & mask]
3376+ cur = slot[0]
3377+ if cur == NULL:
3378+ # Found a blank spot
3379+ if free_slot != NULL:
3380+ # Did we find an earlier _dummy entry?
3381+ return free_slot
3382+ else:
3383+ return slot
3384+ if cur == py_key:
3385+ # Found an exact pointer to the key
3386+ return slot
3387+ if cur == _dummy:
3388+ if free_slot == NULL:
3389+ free_slot = slot
3390+ elif _is_equal(py_key, key_hash, cur):
3391+ # Both py_key and cur belong in this slot, return it
3392+ return slot
3393+ i = i + 1 + n_lookup
3394+ raise AssertionError('should never get here')
3395+
3396+
3397+cdef api PyObject **_SimpleSet_Lookup(object self, object key) except NULL:
3398+ """Find the slot where 'key' would fit.
3399+
3400+ This is the same as a dicts 'lookup' function. This is a private
3401+ api because mutating what you get without maintaing the other invariants
3402+ is a 'bad thing'.
3403+
3404+ :param key: An object we are looking up
3405+ :param hash: The hash for key
3406+ :return: The location in self._table where key should be put
3407+ should never be NULL, but may reference a NULL (PyObject*)
3408+ """
3409+ return _lookup(_check_self(self), key)
3410+
3411+
3412+cdef api object SimpleSet_Add(object self, object key):
3413+ """Add a key to the SimpleSet (set).
3414+
3415+ :param self: The SimpleSet to add the key to.
3416+ :param key: The key to be added. If the key is already present,
3417+ self will not be modified
3418+ :return: The current key stored at the location defined by 'key'.
3419+ This may be the same object, or it may be an equivalent object.
3420+ (consider dict.setdefault(key, key))
3421+ """
3422+ return _check_self(self)._add(key)
3423+
3424+
3425+cdef api int SimpleSet_Contains(object self, object key) except -1:
3426+ """Is key present in self?"""
3427+ return (key in _check_self(self))
3428+
3429+
3430+cdef api int SimpleSet_Discard(object self, object key) except -1:
3431+ """Remove the object referenced at location 'key'.
3432+
3433+ :param self: The SimpleSet being modified
3434+ :param key: The key we are checking on
3435+ :return: 1 if there was an object present, 0 if there was not, and -1 on
3436+ error.
3437+ """
3438+ return _check_self(self)._discard(key)
3439+
3440+
3441+cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL:
3442+ """Get a pointer to the object present at location 'key'.
3443+
3444+ This returns an object which is equal to key which was previously added to
3445+ self. This returns a borrowed reference, as it may also return NULL if no
3446+ value is present at that location.
3447+
3448+ :param key: The value we are looking for
3449+ :return: The object present at that location
3450+ """
3451+ return _check_self(self)._get(key)
3452+
3453+
3454+cdef api Py_ssize_t SimpleSet_Size(object self) except -1:
3455+ """Get the number of active entries in 'self'"""
3456+ return _check_self(self)._used
3457+
3458+
3459+cdef api int SimpleSet_Next(object self, Py_ssize_t *pos,
3460+ PyObject **key) except -1:
3461+ """Walk over items in a SimpleSet.
3462+
3463+ :param pos: should be initialized to 0 by the caller, and will be updated
3464+ by this function
3465+ :param key: Will return a borrowed reference to key
3466+ :return: 0 if nothing left, 1 if we are returning a new value
3467+ """
3468+ cdef Py_ssize_t i, mask
3469+ cdef SimpleSet true_self
3470+ cdef PyObject **table
3471+ true_self = _check_self(self)
3472+ i = pos[0]
3473+ if (i < 0):
3474+ return 0
3475+ mask = true_self._mask
3476+ table= true_self._table
3477+ while (i <= mask and (table[i] == NULL or table[i] == _dummy)):
3478+ i = i + 1
3479+ pos[0] = i + 1
3480+ if (i > mask):
3481+ return 0 # All done
3482+ if (key != NULL):
3483+ key[0] = table[i]
3484+ return 1
3485+
3486+
3487+cdef int SimpleSet_traverse(SimpleSet self, visitproc visit,
3488+ void *arg) except -1:
3489+ """This is an implementation of 'tp_traverse' that hits the whole table.
3490+
3491+ Cython/Pyrex don't seem to let you define a tp_traverse, and they only
3492+ define one for you if you have an 'object' attribute. Since they don't
3493+ support C arrays of objects, we access the PyObject * directly.
3494+ """
3495+ cdef Py_ssize_t pos
3496+ cdef PyObject *next_key
3497+ cdef int ret
3498+
3499+ pos = 0
3500+ while SimpleSet_Next(self, &pos, &next_key):
3501+ ret = visit(next_key, arg)
3502+ if ret:
3503+ return ret
3504+ return 0
3505+
3506+# It is a little bit ugly to do this, but it works, and means that Meliae can
3507+# dump the total memory consumed by all child objects.
3508+(<PyTypeObject *>SimpleSet).tp_traverse = <traverseproc>SimpleSet_traverse
3509
3510=== added file 'bzrlib/_static_tuple_c.c'
3511--- bzrlib/_static_tuple_c.c 1970-01-01 00:00:00 +0000
3512+++ bzrlib/_static_tuple_c.c 2010-02-18 00:00:52 +0000
3513@@ -0,0 +1,945 @@
3514+/* Copyright (C) 2009, 2010 Canonical Ltd
3515+ *
3516+ * This program is free software; you can redistribute it and/or modify
3517+ * it under the terms of the GNU General Public License as published by
3518+ * the Free Software Foundation; either version 2 of the License, or
3519+ * (at your option) any later version.
3520+ *
3521+ * This program is distributed in the hope that it will be useful,
3522+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3523+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3524+ * GNU General Public License for more details.
3525+ *
3526+ * You should have received a copy of the GNU General Public License
3527+ * along with this program; if not, write to the Free Software
3528+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3529+ */
3530+
3531+/* Must be defined before importing _static_tuple_c.h so that we get the right
3532+ * linkage.
3533+ */
3534+#define STATIC_TUPLE_MODULE
3535+
3536+#include <Python.h>
3537+#include "python-compat.h"
3538+
3539+#include "_static_tuple_c.h"
3540+#include "_export_c_api.h"
3541+
3542+/* Pyrex 0.9.6.4 exports _simple_set_pyx_api as
3543+ * import__simple_set_pyx(), while Pyrex 0.9.8.5 and Cython 0.11.3 export them
3544+ * as import_bzrlib___simple_set_pyx(). As such, we just #define one to be
3545+ * equivalent to the other in our internal code.
3546+ */
3547+#define import__simple_set_pyx import_bzrlib___simple_set_pyx
3548+#include "_simple_set_pyx_api.h"
3549+
3550+#if defined(__GNUC__)
3551+# define inline __inline__
3552+#elif defined(_MSC_VER)
3553+# define inline __inline
3554+#else
3555+# define inline
3556+#endif
3557+
3558+
3559+/* The one and only StaticTuple with no values */
3560+static StaticTuple *_empty_tuple = NULL;
3561+static PyObject *_interned_tuples = NULL;
3562+
3563+
3564+static inline int
3565+_StaticTuple_is_interned(StaticTuple *self)
3566+{
3567+ return self->flags & STATIC_TUPLE_INTERNED_FLAG;
3568+}
3569+
3570+
3571+
3572+static PyObject *
3573+StaticTuple_as_tuple(StaticTuple *self)
3574+{
3575+ PyObject *tpl = NULL, *obj = NULL;
3576+ int i, len;
3577+
3578+ len = self->size;
3579+ tpl = PyTuple_New(len);
3580+ if (!tpl) {
3581+ /* Malloc failure */
3582+ return NULL;
3583+ }
3584+ for (i = 0; i < len; ++i) {
3585+ obj = (PyObject *)self->items[i];
3586+ Py_INCREF(obj);
3587+ PyTuple_SET_ITEM(tpl, i, obj);
3588+ }
3589+ return tpl;
3590+}
3591+
3592+
3593+static char StaticTuple_as_tuple_doc[] = "as_tuple() => tuple";
3594+
3595+static StaticTuple *
3596+StaticTuple_Intern(StaticTuple *self)
3597+{
3598+ PyObject *canonical_tuple = NULL;
3599+
3600+ if (_interned_tuples == NULL || _StaticTuple_is_interned(self)) {
3601+ Py_INCREF(self);
3602+ return self;
3603+ }
3604+ /* SimpleSet_Add returns whatever object is present at self
3605+ * or the new object if it needs to add it.
3606+ */
3607+ canonical_tuple = SimpleSet_Add(_interned_tuples, (PyObject *)self);
3608+ if (!canonical_tuple) {
3609+ // Some sort of exception, propogate it.
3610+ return NULL;
3611+ }
3612+ if (canonical_tuple != (PyObject *)self) {
3613+ // There was already a tuple with that value
3614+ return (StaticTuple *)canonical_tuple;
3615+ }
3616+ self->flags |= STATIC_TUPLE_INTERNED_FLAG;
3617+ // The two references in the dict do not count, so that the StaticTuple
3618+ // object does not become immortal just because it was interned.
3619+ Py_REFCNT(self) -= 1;
3620+ return self;
3621+}
3622+
3623+static char StaticTuple_Intern_doc[] = "intern() => unique StaticTuple\n"
3624+ "Return a 'canonical' StaticTuple object.\n"
3625+ "Similar to intern() for strings, this makes sure there\n"
3626+ "is only one StaticTuple object for a given value\n."
3627+ "Common usage is:\n"
3628+ " key = StaticTuple('foo', 'bar').intern()\n";
3629+
3630+
3631+static void
3632+StaticTuple_dealloc(StaticTuple *self)
3633+{
3634+ int i, len;
3635+
3636+ if (_StaticTuple_is_interned(self)) {
3637+ /* revive dead object temporarily for Discard */
3638+ Py_REFCNT(self) = 2;
3639+ if (SimpleSet_Discard(_interned_tuples, (PyObject*)self) != 1)
3640+ Py_FatalError("deletion of interned StaticTuple failed");
3641+ self->flags &= ~STATIC_TUPLE_INTERNED_FLAG;
3642+ }
3643+ len = self->size;
3644+ for (i = 0; i < len; ++i) {
3645+ Py_XDECREF(self->items[i]);
3646+ }
3647+ Py_TYPE(self)->tp_free((PyObject *)self);
3648+}
3649+
3650+
3651+/* Similar to PyTuple_New() */
3652+static StaticTuple *
3653+StaticTuple_New(Py_ssize_t size)
3654+{
3655+ StaticTuple *stuple;
3656+
3657+ if (size < 0 || size > 255) {
3658+ /* Too big or too small */
3659+ PyErr_SetString(PyExc_ValueError, "StaticTuple(...)"
3660+ " takes from 0 to 255 items");
3661+ return NULL;
3662+ }
3663+ if (size == 0 && _empty_tuple != NULL) {
3664+ Py_INCREF(_empty_tuple);
3665+ return _empty_tuple;
3666+ }
3667+ /* Note that we use PyObject_NewVar because we want to allocate a variable
3668+ * width entry. However we *aren't* truly a PyVarObject because we don't
3669+ * use a long for ob_size. Instead we use a plain 'size' that is an int,
3670+ * and will be overloaded with flags in the future.
3671+ * As such we do the alloc, and then have to clean up anything it does
3672+ * incorrectly.
3673+ */
3674+ stuple = PyObject_NewVar(StaticTuple, &StaticTuple_Type, size);
3675+ if (stuple == NULL) {
3676+ return NULL;
3677+ }
3678+ stuple->size = size;
3679+ stuple->flags = 0;
3680+ stuple->_unused0 = 0;
3681+ stuple->_unused1 = 0;
3682+ if (size > 0) {
3683+ memset(stuple->items, 0, sizeof(PyObject *) * size);
3684+ }
3685+#if STATIC_TUPLE_HAS_HASH
3686+ stuple->hash = -1;
3687+#endif
3688+ return stuple;
3689+}
3690+
3691+
3692+static StaticTuple *
3693+StaticTuple_FromSequence(PyObject *sequence)
3694+{
3695+ StaticTuple *new = NULL;
3696+ PyObject *as_tuple = NULL;
3697+ PyObject *item;
3698+ Py_ssize_t i, size;
3699+
3700+ if (StaticTuple_CheckExact(sequence)) {
3701+ Py_INCREF(sequence);
3702+ return (StaticTuple *)sequence;
3703+ }
3704+ if (!PySequence_Check(sequence)) {
3705+ as_tuple = PySequence_Tuple(sequence);
3706+ if (as_tuple == NULL)
3707+ goto done;
3708+ sequence = as_tuple;
3709+ }
3710+ size = PySequence_Size(sequence);
3711+ if (size == -1) {
3712+ goto done;
3713+ }
3714+ new = StaticTuple_New(size);
3715+ if (new == NULL) {
3716+ goto done;
3717+ }
3718+ for (i = 0; i < size; ++i) {
3719+ // This returns a new reference, which we then 'steal' with
3720+ // StaticTuple_SET_ITEM
3721+ item = PySequence_GetItem(sequence, i);
3722+ if (item == NULL) {
3723+ Py_DECREF(new);
3724+ new = NULL;
3725+ goto done;
3726+ }
3727+ StaticTuple_SET_ITEM(new, i, item);
3728+ }
3729+done:
3730+ Py_XDECREF(as_tuple);
3731+ return (StaticTuple *)new;
3732+}
3733+
3734+static StaticTuple *
3735+StaticTuple_from_sequence(PyObject *self, PyObject *args, PyObject *kwargs)
3736+{
3737+ PyObject *sequence;
3738+ if (!PyArg_ParseTuple(args, "O", &sequence))
3739+ return NULL;
3740+ return StaticTuple_FromSequence(sequence);
3741+}
3742+
3743+
3744+/* Check that all items we point to are 'valid' */
3745+static int
3746+StaticTuple_check_items(StaticTuple *self)
3747+{
3748+ int i;
3749+ PyObject *obj;
3750+
3751+ for (i = 0; i < self->size; ++i) {
3752+ obj = self->items[i];
3753+ if (obj == NULL) {
3754+ PyErr_SetString(PyExc_RuntimeError, "StaticTuple(...)"
3755+ " should not have a NULL entry.");
3756+ return 0;
3757+ }
3758+ if (PyString_CheckExact(obj)
3759+ || StaticTuple_CheckExact(obj)
3760+ || obj == Py_None
3761+ || PyBool_Check(obj)
3762+ || PyInt_CheckExact(obj)
3763+ || PyLong_CheckExact(obj)
3764+ || PyFloat_CheckExact(obj)
3765+ || PyUnicode_CheckExact(obj)
3766+ ) continue;
3767+ PyErr_Format(PyExc_TypeError, "StaticTuple(...)"
3768+ " requires that all items are one of"
3769+ " str, StaticTuple, None, bool, int, long, float, or unicode"
3770+ " not %s.", Py_TYPE(obj)->tp_name);
3771+ return 0;
3772+ }
3773+ return 1;
3774+}
3775+
3776+static PyObject *
3777+StaticTuple_new_constructor(PyTypeObject *type, PyObject *args, PyObject *kwds)
3778+{
3779+ StaticTuple *self;
3780+ PyObject *obj = NULL;
3781+ Py_ssize_t i, len = 0;
3782+
3783+ if (type != &StaticTuple_Type) {
3784+ PyErr_SetString(PyExc_TypeError, "we only support creating StaticTuple");
3785+ return NULL;
3786+ }
3787+ if (!PyTuple_CheckExact(args)) {
3788+ PyErr_SetString(PyExc_TypeError, "args must be a tuple");
3789+ return NULL;
3790+ }
3791+ len = PyTuple_GET_SIZE(args);
3792+ if (len < 0 || len > 255) {
3793+ /* Check the length here so we can raise a TypeError instead of
3794+ * StaticTuple_New's ValueError.
3795+ */
3796+ PyErr_SetString(PyExc_TypeError, "StaticTuple(...)"
3797+ " takes from 0 to 255 items");
3798+ return NULL;
3799+ }
3800+ self = (StaticTuple *)StaticTuple_New(len);
3801+ if (self == NULL) {
3802+ return NULL;
3803+ }
3804+ for (i = 0; i < len; ++i) {
3805+ obj = PyTuple_GET_ITEM(args, i);
3806+ Py_INCREF(obj);
3807+ self->items[i] = obj;
3808+ }
3809+ if (!StaticTuple_check_items(self)) {
3810+ type->tp_dealloc((PyObject *)self);
3811+ return NULL;
3812+ }
3813+ return (PyObject *)self;
3814+}
3815+
3816+static PyObject *
3817+StaticTuple_repr(StaticTuple *self)
3818+{
3819+ PyObject *as_tuple, *tuple_repr, *result;
3820+
3821+ as_tuple = StaticTuple_as_tuple(self);
3822+ if (as_tuple == NULL) {
3823+ return NULL;
3824+ }
3825+ tuple_repr = PyObject_Repr(as_tuple);
3826+ Py_DECREF(as_tuple);
3827+ if (tuple_repr == NULL) {
3828+ return NULL;
3829+ }
3830+ result = PyString_FromFormat("StaticTuple%s",
3831+ PyString_AsString(tuple_repr));
3832+ return result;
3833+}
3834+
3835+static long
3836+StaticTuple_hash(StaticTuple *self)
3837+{
3838+ /* adapted from tuplehash(), is the specific hash value considered
3839+ * 'stable'?
3840+ */
3841+ register long x, y;
3842+ Py_ssize_t len = self->size;
3843+ PyObject **p;
3844+ long mult = 1000003L;
3845+
3846+#if STATIC_TUPLE_HAS_HASH
3847+ if (self->hash != -1) {
3848+ return self->hash;
3849+ }
3850+#endif
3851+ x = 0x345678L;
3852+ p = self->items;
3853+ // TODO: We could set specific flags if we know that, for example, all the
3854+ // items are strings. I haven't seen a real-world benefit to that
3855+ // yet, though.
3856+ while (--len >= 0) {
3857+ y = PyObject_Hash(*p++);
3858+ if (y == -1) /* failure */
3859+ return -1;
3860+ x = (x ^ y) * mult;
3861+ /* the cast might truncate len; that doesn't change hash stability */
3862+ mult += (long)(82520L + len + len);
3863+ }
3864+ x += 97531L;
3865+ if (x == -1)
3866+ x = -2;
3867+#if STATIC_TUPLE_HAS_HASH
3868+ self->hash = x;
3869+#endif
3870+ return x;
3871+}
3872+
3873+static PyObject *
3874+StaticTuple_richcompare_to_tuple(StaticTuple *v, PyObject *wt, int op)
3875+{
3876+ PyObject *vt;
3877+ PyObject *result = NULL;
3878+
3879+ vt = StaticTuple_as_tuple((StaticTuple *)v);
3880+ if (vt == NULL) {
3881+ goto done;
3882+ }
3883+ if (!PyTuple_Check(wt)) {
3884+ PyErr_BadInternalCall();
3885+ goto done;
3886+ }
3887+ /* Now we have 2 tuples to compare, do it */
3888+ result = PyTuple_Type.tp_richcompare(vt, wt, op);
3889+done:
3890+ Py_XDECREF(vt);
3891+ return result;
3892+}
3893+
3894+/** Compare two objects to determine if they are equivalent.
3895+ * The basic flow is as follows
3896+ * 1) First make sure that both objects are StaticTuple instances. If they
3897+ * aren't then cast self to a tuple, and have the tuple do the comparison.
3898+ * 2) Special case comparison to Py_None, because it happens to occur fairly
3899+ * often in the test suite.
3900+ * 3) Special case when v and w are the same pointer. As we know the answer to
3901+ * all queries without walking individual items.
3902+ * 4) For all operations, we then walk the items to find the first paired
3903+ * items that are not equal.
3904+ * 5) If all items found are equal, we then check the length of self and
3905+ * other to determine equality.
3906+ * 6) If an item differs, then we apply "op" to those last two items. (eg.
3907+ * StaticTuple(A, B) > StaticTuple(A, C) iff B > C)
3908+ */
3909+
3910+static PyObject *
3911+StaticTuple_richcompare(PyObject *v, PyObject *w, int op)
3912+{
3913+ StaticTuple *v_st, *w_st;
3914+ Py_ssize_t vlen, wlen, min_len, i;
3915+ PyObject *v_obj, *w_obj;
3916+ richcmpfunc string_richcompare;
3917+
3918+ if (!StaticTuple_CheckExact(v)) {
3919+ /* This has never triggered, according to python-dev it seems this
3920+ * might trigger if '__op__' is defined but '__rop__' is not, sort of
3921+ * case. Such as "None == StaticTuple()"
3922+ */
3923+ fprintf(stderr, "self is not StaticTuple\n");
3924+ Py_INCREF(Py_NotImplemented);
3925+ return Py_NotImplemented;
3926+ }
3927+ v_st = (StaticTuple *)v;
3928+ if (StaticTuple_CheckExact(w)) {
3929+ /* The most common case */
3930+ w_st = (StaticTuple*)w;
3931+ } else if (PyTuple_Check(w)) {
3932+ /* One of v or w is a tuple, so we go the 'slow' route and cast up to
3933+ * tuples to compare.
3934+ */
3935+ /* TODO: This seems to be triggering more than I thought it would...
3936+ * We probably want to optimize comparing self to other when
3937+ * other is a tuple.
3938+ */
3939+ return StaticTuple_richcompare_to_tuple(v_st, w, op);
3940+ } else if (w == Py_None) {
3941+ // None is always less than the object
3942+ switch (op) {
3943+ case Py_NE:case Py_GT:case Py_GE:
3944+ Py_INCREF(Py_True);
3945+ return Py_True;
3946+ case Py_EQ:case Py_LT:case Py_LE:
3947+ Py_INCREF(Py_False);
3948+ return Py_False;
3949+ default: // Should never happen
3950+ return Py_NotImplemented;
3951+ }
3952+ } else {
3953+ /* We don't special case this comparison, we just let python handle
3954+ * it.
3955+ */
3956+ Py_INCREF(Py_NotImplemented);
3957+ return Py_NotImplemented;
3958+ }
3959+ /* Now we know that we have 2 StaticTuple objects, so let's compare them.
3960+ * This code is inspired from tuplerichcompare, except we know our
3961+ * objects are limited in scope, so we can inline some comparisons.
3962+ */
3963+ if (v == w) {
3964+ /* Identical pointers, we can shortcut this easily. */
3965+ switch (op) {
3966+ case Py_EQ:case Py_LE:case Py_GE:
3967+ Py_INCREF(Py_True);
3968+ return Py_True;
3969+ case Py_NE:case Py_LT:case Py_GT:
3970+ Py_INCREF(Py_False);
3971+ return Py_False;
3972+ }
3973+ }
3974+ if (op == Py_EQ
3975+ && _StaticTuple_is_interned(v_st)
3976+ && _StaticTuple_is_interned(w_st))
3977+ {
3978+ /* If both objects are interned, we know they are different if the
3979+ * pointer is not the same, which would have been handled by the
3980+ * previous if. No need to compare the entries.
3981+ */
3982+ Py_INCREF(Py_False);
3983+ return Py_False;
3984+ }
3985+
3986+ /* The only time we are likely to compare items of different lengths is in
3987+ * something like the interned_keys set. However, the hash is good enough
3988+ * that it is rare. Note that 'tuple_richcompare' also does not compare
3989+ * lengths here.
3990+ */
3991+ vlen = v_st->size;
3992+ wlen = w_st->size;
3993+ min_len = (vlen < wlen) ? vlen : wlen;
3994+ string_richcompare = PyString_Type.tp_richcompare;
3995+ for (i = 0; i < min_len; i++) {
3996+ PyObject *result = NULL;
3997+ v_obj = StaticTuple_GET_ITEM(v_st, i);
3998+ w_obj = StaticTuple_GET_ITEM(w_st, i);
3999+ if (v_obj == w_obj) {
4000+ /* Shortcut case, these must be identical */
4001+ continue;
4002+ }
4003+ if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj)) {
4004+ result = string_richcompare(v_obj, w_obj, Py_EQ);
4005+ } else if (StaticTuple_CheckExact(v_obj) &&
4006+ StaticTuple_CheckExact(w_obj))
4007+ {
4008+ /* Both are StaticTuple types, so recurse */
4009+ result = StaticTuple_richcompare(v_obj, w_obj, Py_EQ);
4010+ } else {
4011+ /* Fall back to generic richcompare */
4012+ result = PyObject_RichCompare(v_obj, w_obj, Py_EQ);
4013+ }
4014+ if (result == NULL) {
4015+ return NULL; /* There seems to be an error */
4016+ }
4017+ if (result == Py_False) {
4018+ // This entry is not identical, Shortcut for Py_EQ
4019+ if (op == Py_EQ) {
4020+ return result;
4021+ }
4022+ Py_DECREF(result);
4023+ break;
4024+ }
4025+ if (result != Py_True) {
4026+ /* We don't know *what* richcompare is returning, but it
4027+ * isn't something we recognize
4028+ */
4029+ PyErr_BadInternalCall();
4030+ Py_DECREF(result);
4031+ return NULL;
4032+ }
4033+ Py_DECREF(result);
4034+ }
4035+ if (i >= min_len) {
4036+ /* We walked off one of the lists, but everything compared equal so
4037+ * far. Just compare the size.
4038+ */
4039+ int cmp;
4040+ PyObject *res;
4041+ switch (op) {
4042+ case Py_LT: cmp = vlen < wlen; break;
4043+ case Py_LE: cmp = vlen <= wlen; break;
4044+ case Py_EQ: cmp = vlen == wlen; break;
4045+ case Py_NE: cmp = vlen != wlen; break;
4046+ case Py_GT: cmp = vlen > wlen; break;
4047+ case Py_GE: cmp = vlen >= wlen; break;
4048+ default: return NULL; /* cannot happen */
4049+ }
4050+ if (cmp)
4051+ res = Py_True;
4052+ else
4053+ res = Py_False;
4054+ Py_INCREF(res);
4055+ return res;
4056+ }
4057+ /* The last item differs, shortcut the Py_NE case */
4058+ if (op == Py_NE) {
4059+ Py_INCREF(Py_True);
4060+ return Py_True;
4061+ }
4062+ /* It is some other comparison, go ahead and do the real check. */
4063+ if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj))
4064+ {
4065+ return string_richcompare(v_obj, w_obj, op);
4066+ } else if (StaticTuple_CheckExact(v_obj) &&
4067+ StaticTuple_CheckExact(w_obj))
4068+ {
4069+ /* Both are StaticTuple types, so recurse */
4070+ return StaticTuple_richcompare(v_obj, w_obj, op);
4071+ } else {
4072+ return PyObject_RichCompare(v_obj, w_obj, op);
4073+ }
4074+}
4075+
4076+
4077+static Py_ssize_t
4078+StaticTuple_length(StaticTuple *self)
4079+{
4080+ return self->size;
4081+}
4082+
4083+
4084+static PyObject *
4085+StaticTuple__is_interned(StaticTuple *self)
4086+{
4087+ if (_StaticTuple_is_interned(self)) {
4088+ Py_INCREF(Py_True);
4089+ return Py_True;
4090+ }
4091+ Py_INCREF(Py_False);
4092+ return Py_False;
4093+}
4094+
4095+static char StaticTuple__is_interned_doc[] = "_is_interned() => True/False\n"
4096+ "Check to see if this tuple has been interned.\n";
4097+
4098+
4099+static PyObject *
4100+StaticTuple_reduce(StaticTuple *self)
4101+{
4102+ PyObject *result = NULL, *as_tuple = NULL;
4103+
4104+ result = PyTuple_New(2);
4105+ if (!result) {
4106+ return NULL;
4107+ }
4108+ as_tuple = StaticTuple_as_tuple(self);
4109+ if (as_tuple == NULL) {
4110+ Py_DECREF(result);
4111+ return NULL;
4112+ }
4113+ Py_INCREF(&StaticTuple_Type);
4114+ PyTuple_SET_ITEM(result, 0, (PyObject *)&StaticTuple_Type);
4115+ PyTuple_SET_ITEM(result, 1, as_tuple);
4116+ return result;
4117+}
4118+
4119+static char StaticTuple_reduce_doc[] = "__reduce__() => tuple\n";
4120+
4121+
4122+static PyObject *
4123+StaticTuple_add(PyObject *v, PyObject *w)
4124+{
4125+ Py_ssize_t i, len_v, len_w;
4126+ PyObject *item;
4127+ StaticTuple *result;
4128+ /* StaticTuples and plain tuples may be added (concatenated) to
4129+ * StaticTuples.
4130+ */
4131+ if (StaticTuple_CheckExact(v)) {
4132+ len_v = ((StaticTuple*)v)->size;
4133+ } else if (PyTuple_Check(v)) {
4134+ len_v = PyTuple_GET_SIZE(v);
4135+ } else {
4136+ Py_INCREF(Py_NotImplemented);
4137+ return Py_NotImplemented;
4138+ }
4139+ if (StaticTuple_CheckExact(w)) {
4140+ len_w = ((StaticTuple*)w)->size;
4141+ } else if (PyTuple_Check(w)) {
4142+ len_w = PyTuple_GET_SIZE(w);
4143+ } else {
4144+ Py_INCREF(Py_NotImplemented);
4145+ return Py_NotImplemented;
4146+ }
4147+ result = StaticTuple_New(len_v + len_w);
4148+ if (result == NULL)
4149+ return NULL;
4150+ for (i = 0; i < len_v; ++i) {
4151+ // This returns a new reference, which we then 'steal' with
4152+ // StaticTuple_SET_ITEM
4153+ item = PySequence_GetItem(v, i);
4154+ if (item == NULL) {
4155+ Py_DECREF(result);
4156+ return NULL;
4157+ }
4158+ StaticTuple_SET_ITEM(result, i, item);
4159+ }
4160+ for (i = 0; i < len_w; ++i) {
4161+ item = PySequence_GetItem(w, i);
4162+ if (item == NULL) {
4163+ Py_DECREF(result);
4164+ return NULL;
4165+ }
4166+ StaticTuple_SET_ITEM(result, i+len_v, item);
4167+ }
4168+ if (!StaticTuple_check_items(result)) {
4169+ Py_DECREF(result);
4170+ return NULL;
4171+ }
4172+ return (PyObject *)result;
4173+}
4174+
4175+static PyObject *
4176+StaticTuple_item(StaticTuple *self, Py_ssize_t offset)
4177+{
4178+ PyObject *obj;
4179+ /* We cast to (int) to avoid worrying about whether Py_ssize_t is a
4180+ * long long, etc. offsets should never be >2**31 anyway.
4181+ */
4182+ if (offset < 0) {
4183+ PyErr_Format(PyExc_IndexError, "StaticTuple_item does not support"
4184+ " negative indices: %d\n", (int)offset);
4185+ } else if (offset >= self->size) {
4186+ PyErr_Format(PyExc_IndexError, "StaticTuple index out of range"
4187+ " %d >= %d", (int)offset, (int)self->size);
4188+ return NULL;
4189+ }
4190+ obj = (PyObject *)self->items[offset];
4191+ Py_INCREF(obj);
4192+ return obj;
4193+}
4194+
4195+static PyObject *
4196+StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)
4197+{
4198+ PyObject *as_tuple, *result;
4199+
4200+ as_tuple = StaticTuple_as_tuple(self);
4201+ if (as_tuple == NULL) {
4202+ return NULL;
4203+ }
4204+ result = PyTuple_Type.tp_as_sequence->sq_slice(as_tuple, ilow, ihigh);
4205+ Py_DECREF(as_tuple);
4206+ return result;
4207+}
4208+
4209+static int
4210+StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)
4211+{
4212+ Py_ssize_t i;
4213+ for (i = self->size; --i >= 0;) {
4214+ Py_VISIT(self->items[i]);
4215+ }
4216+ return 0;
4217+}
4218+
4219+static char StaticTuple_doc[] =
4220+ "C implementation of a StaticTuple structure."
4221+ "\n This is used as StaticTuple(item1, item2, item3)"
4222+ "\n This is similar to tuple, less flexible in what it"
4223+ "\n supports, but also lighter memory consumption."
4224+ "\n Note that the constructor mimics the () form of tuples"
4225+ "\n Rather than the 'tuple()' constructor."
4226+ "\n eg. StaticTuple(a, b) == (a, b) == tuple((a, b))";
4227+
4228+static PyMethodDef StaticTuple_methods[] = {
4229+ {"as_tuple", (PyCFunction)StaticTuple_as_tuple, METH_NOARGS, StaticTuple_as_tuple_doc},
4230+ {"intern", (PyCFunction)StaticTuple_Intern, METH_NOARGS, StaticTuple_Intern_doc},
4231+ {"_is_interned", (PyCFunction)StaticTuple__is_interned, METH_NOARGS,
4232+ StaticTuple__is_interned_doc},
4233+ {"from_sequence", (PyCFunction)StaticTuple_from_sequence,
4234+ METH_STATIC | METH_VARARGS,
4235+ "Create a StaticTuple from a given sequence. This functions"
4236+ " the same as the tuple() constructor."},
4237+ {"__reduce__", (PyCFunction)StaticTuple_reduce, METH_NOARGS, StaticTuple_reduce_doc},
4238+ {NULL, NULL} /* sentinel */
4239+};
4240+
4241+
4242+static PyNumberMethods StaticTuple_as_number = {
4243+ (binaryfunc) StaticTuple_add, /* nb_add */
4244+ 0, /* nb_subtract */
4245+ 0, /* nb_multiply */
4246+ 0, /* nb_divide */
4247+ 0, /* nb_remainder */
4248+ 0, /* nb_divmod */
4249+ 0, /* nb_power */
4250+ 0, /* nb_negative */
4251+ 0, /* nb_positive */
4252+ 0, /* nb_absolute */
4253+ 0, /* nb_nonzero */
4254+ 0, /* nb_invert */
4255+ 0, /* nb_lshift */
4256+ 0, /* nb_rshift */
4257+ 0, /* nb_and */
4258+ 0, /* nb_xor */
4259+ 0, /* nb_or */
4260+ 0, /* nb_coerce */
4261+};
4262+
4263+
4264+static PySequenceMethods StaticTuple_as_sequence = {
4265+ (lenfunc)StaticTuple_length, /* sq_length */
4266+ 0, /* sq_concat */
4267+ 0, /* sq_repeat */
4268+ (ssizeargfunc)StaticTuple_item, /* sq_item */
4269+ (ssizessizeargfunc)StaticTuple_slice, /* sq_slice */
4270+ 0, /* sq_ass_item */
4271+ 0, /* sq_ass_slice */
4272+ 0, /* sq_contains */
4273+};
4274+
4275+/* TODO: Implement StaticTuple_as_mapping.
4276+ * The only thing we really want to support from there is mp_subscript,
4277+ * so that we could support extended slicing (foo[::2]). Not worth it
4278+ * yet, though.
4279+ */
4280+
4281+
4282+PyTypeObject StaticTuple_Type = {
4283+ PyObject_HEAD_INIT(NULL)
4284+ 0, /* ob_size */
4285+ "bzrlib._static_tuple_c.StaticTuple", /* tp_name */
4286+ sizeof(StaticTuple), /* tp_basicsize */
4287+ sizeof(PyObject *), /* tp_itemsize */
4288+ (destructor)StaticTuple_dealloc, /* tp_dealloc */
4289+ 0, /* tp_print */
4290+ 0, /* tp_getattr */
4291+ 0, /* tp_setattr */
4292+ 0, /* tp_compare */
4293+ (reprfunc)StaticTuple_repr, /* tp_repr */
4294+ &StaticTuple_as_number, /* tp_as_number */
4295+ &StaticTuple_as_sequence, /* tp_as_sequence */
4296+ 0, /* tp_as_mapping */
4297+ (hashfunc)StaticTuple_hash, /* tp_hash */
4298+ 0, /* tp_call */
4299+ 0, /* tp_str */
4300+ 0, /* tp_getattro */
4301+ 0, /* tp_setattro */
4302+ 0, /* tp_as_buffer */
4303+ /* Py_TPFLAGS_CHECKTYPES tells the number operations that they shouldn't
4304+ * try to 'coerce' but instead stuff like 'add' will check it arguments.
4305+ */
4306+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags*/
4307+ StaticTuple_doc, /* tp_doc */
4308+ /* gc.get_referents checks the IS_GC flag before it calls tp_traverse
4309+ * And we don't include this object in the garbage collector because we
4310+ * know it doesn't create cycles. However, 'meliae' will follow
4311+ * tp_traverse, even if the object isn't GC, and we want that.
4312+ */
4313+ (traverseproc)StaticTuple_traverse, /* tp_traverse */
4314+ 0, /* tp_clear */
4315+ StaticTuple_richcompare, /* tp_richcompare */
4316+ 0, /* tp_weaklistoffset */
4317+ // without implementing tp_iter, Python will fall back to PySequence*
4318+ // which seems to work ok, we may need something faster/lighter in the
4319+ // future.
4320+ 0, /* tp_iter */
4321+ 0, /* tp_iternext */
4322+ StaticTuple_methods, /* tp_methods */
4323+ 0, /* tp_members */
4324+ 0, /* tp_getset */
4325+ 0, /* tp_base */
4326+ 0, /* tp_dict */
4327+ 0, /* tp_descr_get */
4328+ 0, /* tp_descr_set */
4329+ 0, /* tp_dictoffset */
4330+ 0, /* tp_init */
4331+ 0, /* tp_alloc */
4332+ StaticTuple_new_constructor, /* tp_new */
4333+};
4334+
4335+
4336+static PyMethodDef static_tuple_c_methods[] = {
4337+ {NULL, NULL}
4338+};
4339+
4340+
4341+static void
4342+setup_interned_tuples(PyObject *m)
4343+{
4344+ _interned_tuples = (PyObject *)SimpleSet_New();
4345+ if (_interned_tuples != NULL) {
4346+ Py_INCREF(_interned_tuples);
4347+ PyModule_AddObject(m, "_interned_tuples", _interned_tuples);
4348+ }
4349+}
4350+
4351+
4352+static void
4353+setup_empty_tuple(PyObject *m)
4354+{
4355+ StaticTuple *stuple;
4356+ if (_interned_tuples == NULL) {
4357+ fprintf(stderr, "You need to call setup_interned_tuples() before"
4358+ " setup_empty_tuple, because we intern it.\n");
4359+ }
4360+ // We need to create the empty tuple
4361+ stuple = (StaticTuple *)StaticTuple_New(0);
4362+ _empty_tuple = StaticTuple_Intern(stuple);
4363+ assert(_empty_tuple == stuple);
4364+ // At this point, refcnt is 2: 1 from New(), and 1 from the return from
4365+ // intern(). We will keep 1 for the _empty_tuple global, and use the other
4366+ // for the module reference.
4367+ PyModule_AddObject(m, "_empty_tuple", (PyObject *)_empty_tuple);
4368+}
4369+
4370+static int
4371+_StaticTuple_CheckExact(PyObject *obj)
4372+{
4373+ return StaticTuple_CheckExact(obj);
4374+}
4375+
4376+static void
4377+setup_c_api(PyObject *m)
4378+{
4379+ _export_function(m, "StaticTuple_New", StaticTuple_New,
4380+ "StaticTuple *(Py_ssize_t)");
4381+ _export_function(m, "StaticTuple_Intern", StaticTuple_Intern,
4382+ "StaticTuple *(StaticTuple *)");
4383+ _export_function(m, "StaticTuple_FromSequence", StaticTuple_FromSequence,
4384+ "StaticTuple *(PyObject *)");
4385+ _export_function(m, "_StaticTuple_CheckExact", _StaticTuple_CheckExact,
4386+ "int(PyObject *)");
4387+}
4388+
4389+
4390+static int
4391+_workaround_pyrex_096(void)
4392+{
4393+ /* Work around an incompatibility in how pyrex 0.9.6 exports a module,
4394+ * versus how pyrex 0.9.8 and cython 0.11 export it.
4395+ * Namely 0.9.6 exports import__simple_set_pyx and tries to
4396+ * "import _simple_set_pyx" but it is available only as
4397+ * "import bzrlib._simple_set_pyx"
4398+ * It is a shame to hack up sys.modules, but that is what we've got to do.
4399+ */
4400+ PyObject *sys_module = NULL, *modules = NULL, *set_module = NULL;
4401+ int retval = -1;
4402+
4403+ /* Clear out the current ImportError exception, and try again. */
4404+ PyErr_Clear();
4405+ /* Note that this only seems to work if somewhere else imports
4406+ * bzrlib._simple_set_pyx before importing bzrlib._static_tuple_c
4407+ */
4408+ set_module = PyImport_ImportModule("bzrlib._simple_set_pyx");
4409+ if (set_module == NULL) {
4410+ goto end;
4411+ }
4412+ /* Add the _simple_set_pyx into sys.modules at the appropriate location. */
4413+ sys_module = PyImport_ImportModule("sys");
4414+ if (sys_module == NULL) {
4415+ goto end;
4416+ }
4417+ modules = PyObject_GetAttrString(sys_module, "modules");
4418+ if (modules == NULL || !PyDict_Check(modules)) {
4419+ goto end;
4420+ }
4421+ PyDict_SetItemString(modules, "_simple_set_pyx", set_module);
4422+ /* Now that we have hacked it in, try the import again. */
4423+ retval = import_bzrlib___simple_set_pyx();
4424+end:
4425+ Py_XDECREF(set_module);
4426+ Py_XDECREF(sys_module);
4427+ Py_XDECREF(modules);
4428+ return retval;
4429+}
4430+
4431+
4432+PyMODINIT_FUNC
4433+init_static_tuple_c(void)
4434+{
4435+ PyObject* m;
4436+
4437+ StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;
4438+ if (PyType_Ready(&StaticTuple_Type) < 0)
4439+ return;
4440+
4441+ m = Py_InitModule3("_static_tuple_c", static_tuple_c_methods,
4442+ "C implementation of a StaticTuple structure");
4443+ if (m == NULL)
4444+ return;
4445+
4446+ Py_INCREF(&StaticTuple_Type);
4447+ PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);
4448+ if (import_bzrlib___simple_set_pyx() == -1
4449+ && _workaround_pyrex_096() == -1)
4450+ {
4451+ return;
4452+ }
4453+ setup_interned_tuples(m);
4454+ setup_empty_tuple(m);
4455+ setup_c_api(m);
4456+}
4457+
4458+// vim: tabstop=4 sw=4 expandtab
4459
4460=== added file 'bzrlib/_static_tuple_c.h'
4461--- bzrlib/_static_tuple_c.h 1970-01-01 00:00:00 +0000
4462+++ bzrlib/_static_tuple_c.h 2010-02-18 00:00:51 +0000
4463@@ -0,0 +1,116 @@
4464+/* Copyright (C) 2009 Canonical Ltd
4465+ *
4466+ * This program is free software; you can redistribute it and/or modify
4467+ * it under the terms of the GNU General Public License as published by
4468+ * the Free Software Foundation; either version 2 of the License, or
4469+ * (at your option) any later version.
4470+ *
4471+ * This program is distributed in the hope that it will be useful,
4472+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4473+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4474+ * GNU General Public License for more details.
4475+ *
4476+ * You should have received a copy of the GNU General Public License
4477+ * along with this program; if not, write to the Free Software
4478+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
4479+ */
4480+
4481+#ifndef _STATIC_TUPLE_H_
4482+#define _STATIC_TUPLE_H_
4483+#include <Python.h>
4484+#include <string.h>
4485+
4486+#define STATIC_TUPLE_HAS_HASH 0
4487+/* Caching the hash adds memory, but allows us to save a little time during
4488+ * lookups. TIMEIT hash(key) shows it as
4489+ * 0.108usec w/ hash
4490+ * 0.160usec w/o hash
4491+ * Note that the entries themselves are strings, which already cache their
4492+ * hashes. So while there is a 1.5:1 difference in the time for hash(), it is
4493+ * already a function which is quite fast. Probably the only reason we might
4494+ * want to do so, is if we customized SimpleSet to the point that the item
4495+ * pointers were exactly certain types, and then accessed table[i]->hash
4496+ * directly. So far StaticTuple_hash() is fast enough to not warrant the memory
4497+ * difference.
4498+ */
4499+
4500+/* This defines a single variable-width key.
4501+ * It is basically the same as a tuple, but
4502+ * 1) Lighter weight in memory
4503+ * 2) Only supports strings or other static types (that don't reference other
4504+ * objects.)
4505+ */
4506+
4507+#define STATIC_TUPLE_INTERNED_FLAG 0x01
4508+typedef struct {
4509+ PyObject_HEAD
4510+ // We could go with unsigned short here, and support 64k width tuples
4511+ // without any memory impact, might be worthwhile
4512+ unsigned char size;
4513+ unsigned char flags;
4514+ unsigned char _unused0;
4515+ unsigned char _unused1;
4516+ // Note that on 64-bit, we actually have 4-more unused bytes
4517+ // because items will always be aligned to a 64-bit boundary
4518+#if STATIC_TUPLE_HAS_HASH
4519+ long hash;
4520+#endif
4521+ PyObject *items[0];
4522+} StaticTuple;
4523+extern PyTypeObject StaticTuple_Type;
4524+
4525+typedef struct {
4526+ PyObject_VAR_HEAD
4527+ PyObject *table[0];
4528+} KeyIntern;
4529+
4530+#define StaticTuple_SET_ITEM(key, offset, val) \
4531+ ((((StaticTuple*)(key))->items[(offset)]) = ((PyObject *)(val)))
4532+#define StaticTuple_GET_ITEM(key, offset) (((StaticTuple*)key)->items[offset])
4533+
4534+
4535+#ifdef STATIC_TUPLE_MODULE
4536+/* Used when compiling _static_tuple_c.c */
4537+
4538+static StaticTuple * StaticTuple_New(Py_ssize_t);
4539+static StaticTuple * StaticTuple_Intern(StaticTuple *self);
4540+static StaticTuple * StaticTuple_FromSequence(PyObject *);
4541+#define StaticTuple_CheckExact(op) (Py_TYPE(op) == &StaticTuple_Type)
4542+
4543+#else
4544+/* Used as the foreign api */
4545+
4546+#include "_import_c_api.h"
4547+
4548+static StaticTuple *(*StaticTuple_New)(Py_ssize_t);
4549+static StaticTuple *(*StaticTuple_Intern)(StaticTuple *);
4550+static StaticTuple *(*StaticTuple_FromSequence)(PyObject *);
4551+static PyTypeObject *_p_StaticTuple_Type;
4552+
4553+#define StaticTuple_CheckExact(op) (Py_TYPE(op) == _p_StaticTuple_Type)
4554+static int (*_StaticTuple_CheckExact)(PyObject *);
4555+
4556+
4557+/* Return -1 and set exception on error, 0 on success */
4558+static int
4559+import_static_tuple_c(void)
4560+{
4561+ struct function_description functions[] = {
4562+ {"StaticTuple_New", (void **)&StaticTuple_New,
4563+ "StaticTuple *(Py_ssize_t)"},
4564+ {"StaticTuple_Intern", (void **)&StaticTuple_Intern,
4565+ "StaticTuple *(StaticTuple *)"},
4566+ {"StaticTuple_FromSequence", (void **)&StaticTuple_FromSequence,
4567+ "StaticTuple *(PyObject *)"},
4568+ {"_StaticTuple_CheckExact", (void **)&_StaticTuple_CheckExact,
4569+ "int(PyObject *)"},
4570+ {NULL}};
4571+ struct type_description types[] = {
4572+ {"StaticTuple", &_p_StaticTuple_Type},
4573+ {NULL}};
4574+ return _import_extension_module("bzrlib._static_tuple_c",
4575+ functions, types);
4576+}
4577+
4578+#endif // !STATIC_TUPLE_MODULE
4579+#endif // !_STATIC_TUPLE_H_
4580
4581=== added file 'bzrlib/_static_tuple_c.pxd'
4582--- bzrlib/_static_tuple_c.pxd 1970-01-01 00:00:00 +0000
4583+++ bzrlib/_static_tuple_c.pxd 2010-02-18 00:00:51 +0000
4584@@ -0,0 +1,44 @@
4585+# Copyright (C) 2009, 2010 Canonical Ltd
4586+#
4587+# This program is free software; you can redistribute it and/or modify
4588+# it under the terms of the GNU General Public License as published by
4589+# the Free Software Foundation; either version 2 of the License, or
4590+# (at your option) any later version.
4591+#
4592+# This program is distributed in the hope that it will be useful,
4593+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4594+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4595+# GNU General Public License for more details.
4596+#
4597+# You should have received a copy of the GNU General Public License
4598+# along with this program; if not, write to the Free Software
4599+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
4600+
4601+"""The interface definition file for the StaticTuple class."""
4602+
4603+
4604+cdef extern from "Python.h":
4605+ ctypedef int Py_ssize_t # Required for older pyrex versions
4606+ ctypedef struct PyObject:
4607+ pass
4608+
4609+cdef extern from "_static_tuple_c.h":
4610+ ctypedef class bzrlib._static_tuple_c.StaticTuple [object StaticTuple]:
4611+ cdef unsigned char size
4612+ cdef unsigned char flags
4613+ cdef PyObject *items[0]
4614+
4615+ # Must be called before using any of the C api, as it sets the function
4616+ # pointers in memory.
4617+ int import_static_tuple_c() except -1
4618+ StaticTuple StaticTuple_New(Py_ssize_t)
4619+ StaticTuple StaticTuple_Intern(StaticTuple)
4620+
4621+ # Steals a reference and val must be a valid type, no checking is done
4622+ void StaticTuple_SET_ITEM(StaticTuple key, Py_ssize_t offset, object val)
4623+ # We would normally use PyObject * here. However it seems that cython/pyrex
4624+ # treat the PyObject defined in this header as something different than one
4625+ # defined in a .pyx file. And since we don't INCREF, we need a raw pointer,
4626+ # not an 'object' return value.
4627+ void *StaticTuple_GET_ITEM(StaticTuple key, Py_ssize_t offset)
4628+ int StaticTuple_CheckExact(object)
4629
4630=== added file 'bzrlib/_static_tuple_py.py'
4631--- bzrlib/_static_tuple_py.py 1970-01-01 00:00:00 +0000
4632+++ bzrlib/_static_tuple_py.py 2010-02-18 00:00:52 +0000
4633@@ -0,0 +1,80 @@
4634+# Copyright (C) 2009, 2010 Canonical Ltd
4635+#
4636+# This program is free software; you can redistribute it and/or modify
4637+# it under the terms of the GNU General Public License as published by
4638+# the Free Software Foundation; either version 2 of the License, or
4639+# (at your option) any later version.
4640+#
4641+# This program is distributed in the hope that it will be useful,
4642+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4643+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4644+# GNU General Public License for more details.
4645+#
4646+# You should have received a copy of the GNU General Public License
4647+# along with this program; if not, write to the Free Software
4648+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
4649+
4650+"""The pure-python implementation of the StaticTuple type.
4651+
4652+Note that it is generally just implemented as using tuples of tuples of
4653+strings.
4654+"""
4655+
4656+
4657+class StaticTuple(tuple):
4658+ """A static type, similar to a tuple of strings."""
4659+
4660+ __slots__ = ()
4661+
4662+ def __new__(cls, *args):
4663+ # Make the empty StaticTuple a singleton
4664+ if not args and _empty_tuple is not None:
4665+ return _empty_tuple
4666+ return tuple.__new__(cls, args)
4667+
4668+ def __init__(self, *args):
4669+ """Create a new 'StaticTuple'"""
4670+ num_keys = len(args)
4671+ if num_keys < 0 or num_keys > 255:
4672+ raise TypeError('StaticTuple(...) takes from 0 to 255 items')
4673+ for bit in args:
4674+ if type(bit) not in (str, StaticTuple, unicode, int, long, float,
4675+ None.__class__, bool):
4676+ raise TypeError('StaticTuple can only point to'
4677+ ' StaticTuple, str, unicode, int, long, float, bool, or'
4678+ ' None not %s' % (type(bit),))
4679+ # We don't need to pass args to tuple.__init__, because that was
4680+ # already handled in __new__.
4681+ tuple.__init__(self)
4682+
4683+ def __repr__(self):
4684+ return '%s%s' % (self.__class__.__name__, tuple.__repr__(self))
4685+
4686+ def __reduce__(self):
4687+ return (StaticTuple, tuple(self))
4688+
4689+ def __add__(self, other):
4690+ """Concatenate self with other"""
4691+ return StaticTuple.from_sequence(tuple.__add__(self,other))
4692+
4693+ def as_tuple(self):
4694+ return tuple(self)
4695+
4696+ def intern(self):
4697+ return _interned_tuples.setdefault(self, self)
4698+
4699+ @staticmethod
4700+ def from_sequence(seq):
4701+ """Convert a sequence object into a StaticTuple instance."""
4702+ if isinstance(seq, StaticTuple):
4703+ # it already is
4704+ return seq
4705+ return StaticTuple(*seq)
4706+
4707+
4708+
4709+# Have to set it to None first, so that __new__ can determine whether
4710+# the _empty_tuple singleton has been created yet or not.
4711+_empty_tuple = None
4712+_empty_tuple = StaticTuple()
4713+_interned_tuples = {}
4714
4715=== modified file 'bzrlib/_walkdirs_win32.pyx'
4716--- bzrlib/_walkdirs_win32.pyx 2010-01-05 04:59:57 +0000
4717+++ bzrlib/_walkdirs_win32.pyx 2010-02-18 00:00:51 +0000
4718@@ -1,4 +1,4 @@
4719-# Copyright (C) 2008 Canonical Ltd
4720+# Copyright (C) 2008, 2009, 2010 Canonical Ltd
4721 #
4722 # This program is free software; you can redistribute it and/or modify
4723 # it under the terms of the GNU General Public License as published by
4724
4725=== modified file 'bzrlib/annotate.py'
4726--- bzrlib/annotate.py 2009-08-17 18:52:01 +0000
4727+++ bzrlib/annotate.py 2010-02-18 00:00:51 +0000
4728@@ -458,5 +458,6 @@
4729
4730 try:
4731 from bzrlib._annotator_pyx import Annotator
4732-except ImportError:
4733+except ImportError, e:
4734+ osutils.failed_to_load_extension(e)
4735 from bzrlib._annotator_py import Annotator
4736
4737=== modified file 'bzrlib/benchmarks/bench_dirstate.py'
4738--- bzrlib/benchmarks/bench_dirstate.py 2009-06-22 15:39:42 +0000
4739+++ bzrlib/benchmarks/bench_dirstate.py 2010-02-18 00:00:52 +0000
4740@@ -1,4 +1,4 @@
4741-# Copyright (C) 2007 Canonical Ltd
4742+# Copyright (C) 2007, 2009, 2010 Canonical Ltd
4743 #
4744 # This program is free software; you can redistribute it and/or modify
4745 # it under the terms of the GNU General Public License as published by
4746@@ -26,7 +26,7 @@
4747 tests,
4748 )
4749 from bzrlib.tests.test__dirstate_helpers import (
4750- CompiledDirstateHelpersFeature,
4751+ compiled_dirstate_helpers_feature,
4752 )
4753
4754
4755@@ -206,7 +206,7 @@
4756 state.unlock()
4757
4758 def test__read_dirblocks_20k_tree_0_parents_pyx(self):
4759- self.requireFeature(CompiledDirstateHelpersFeature)
4760+ self.requireFeature(compiled_dirstate_helpers_feature)
4761 from bzrlib._dirstate_helpers_pyx import _read_dirblocks
4762 state = self.build_20k_dirstate()
4763 state.lock_read()
4764@@ -231,7 +231,7 @@
4765 state.unlock()
4766
4767 def test__read_dirblocks_20k_tree_1_parent_pyx(self):
4768- self.requireFeature(CompiledDirstateHelpersFeature)
4769+ self.requireFeature(compiled_dirstate_helpers_feature)
4770 from bzrlib._dirstate_helpers_pyx import _read_dirblocks
4771 state = self.build_20k_dirstate_with_parents(1)
4772 state.lock_read()
4773@@ -256,7 +256,7 @@
4774 state.unlock()
4775
4776 def test__read_dirblocks_20k_tree_2_parents_pyx(self):
4777- self.requireFeature(CompiledDirstateHelpersFeature)
4778+ self.requireFeature(compiled_dirstate_helpers_feature)
4779 from bzrlib._dirstate_helpers_pyx import _read_dirblocks
4780 state = self.build_20k_dirstate_with_parents(2)
4781 state.lock_read()
4782@@ -337,7 +337,7 @@
4783 state.unlock()
4784
4785 def test_bisect_dirblock_pyx(self):
4786- self.requireFeature(CompiledDirstateHelpersFeature)
4787+ self.requireFeature(compiled_dirstate_helpers_feature)
4788 from bzrlib._dirstate_helpers_pyx import bisect_dirblock
4789 state = self.build_10k_dirstate_dirs()
4790 state.lock_read()
4791@@ -420,7 +420,7 @@
4792 [(3, 1), (3, 1), (3, 1), (3, 2)])
4793
4794 def test_cmp_by_dirs_pyrex(self):
4795- self.requireFeature(CompiledDirstateHelpersFeature)
4796+ self.requireFeature(compiled_dirstate_helpers_feature)
4797 from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
4798 self.compareAllPaths(cmp_by_dirs,
4799 [(3, 1), (3, 1), (3, 1), (3, 2)])
4800
4801=== modified file 'bzrlib/bencode.py'
4802--- bzrlib/bencode.py 2009-06-03 14:14:31 +0000
4803+++ bzrlib/bencode.py 2010-02-18 00:00:52 +0000
4804@@ -16,7 +16,10 @@
4805
4806 """Wrapper around the bencode pyrex and python implementation"""
4807
4808+from bzrlib import osutils
4809+
4810 try:
4811 from bzrlib._bencode_pyx import bdecode, bdecode_as_tuple, bencode, Bencached
4812-except ImportError:
4813+except ImportError, e:
4814+ osutils.failed_to_load_extension(e)
4815 from bzrlib.util._bencode_py import bdecode, bdecode_as_tuple, bencode, Bencached
4816
4817=== modified file 'bzrlib/branch.py'
4818--- bzrlib/branch.py 2009-11-23 07:10:47 +0000
4819+++ bzrlib/branch.py 2010-02-18 00:00:52 +0000
4820@@ -1,4 +1,4 @@
4821-# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
4822+# Copyright (C) 2005-2010 Canonical Ltd
4823 #
4824 # This program is free software; you can redistribute it and/or modify
4825 # it under the terms of the GNU General Public License as published by
4826@@ -46,9 +46,10 @@
4827 )
4828 """)
4829
4830-from bzrlib.decorators import needs_read_lock, needs_write_lock
4831+from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
4832 from bzrlib.hooks import HookPoint, Hooks
4833 from bzrlib.inter import InterObject
4834+from bzrlib.lock import _RelockDebugMixin
4835 from bzrlib import registry
4836 from bzrlib.symbol_versioning import (
4837 deprecated_in,
4838@@ -502,12 +503,25 @@
4839 left_parent = stop_rev.parent_ids[0]
4840 else:
4841 left_parent = _mod_revision.NULL_REVISION
4842+ # left_parent is the actual revision we want to stop logging at,
4843+ # since we want to show the merged revisions after the stop_rev too
4844+ reached_stop_revision_id = False
4845+ revision_id_whitelist = []
4846 for node in rev_iter:
4847 rev_id = node.key[-1]
4848 if rev_id == left_parent:
4849+ # reached the left parent after the stop_revision
4850 return
4851- yield (rev_id, node.merge_depth, node.revno,
4852+ if (not reached_stop_revision_id or
4853+ rev_id in revision_id_whitelist):
4854+ yield (rev_id, node.merge_depth, node.revno,
4855 node.end_of_merge)
4856+ if reached_stop_revision_id or rev_id == stop_revision_id:
4857+ # only do the merged revs of rev_id from now on
4858+ rev = self.repository.get_revision(rev_id)
4859+ if rev.parent_ids:
4860+ reached_stop_revision_id = True
4861+ revision_id_whitelist.extend(rev.parent_ids)
4862 else:
4863 raise ValueError('invalid stop_rule %r' % stop_rule)
4864
4865@@ -1089,15 +1103,7 @@
4866 params = ChangeBranchTipParams(
4867 self, old_revno, new_revno, old_revid, new_revid)
4868 for hook in hooks:
4869- try:
4870- hook(params)
4871- except errors.TipChangeRejected:
4872- raise
4873- except Exception:
4874- exc_info = sys.exc_info()
4875- hook_name = Branch.hooks.get_hook_name(hook)
4876- raise errors.HookFailed(
4877- 'pre_change_branch_tip', hook_name, exc_info)
4878+ hook(params)
4879
4880 @needs_write_lock
4881 def update(self):
4882@@ -1432,10 +1438,10 @@
4883 """Return the format for the branch object in a_bzrdir."""
4884 try:
4885 transport = a_bzrdir.get_branch_transport(None)
4886- format_string = transport.get("format").read()
4887+ format_string = transport.get_bytes("format")
4888 return klass._formats[format_string]
4889 except errors.NoSuchFile:
4890- raise errors.NotBranchError(path=transport.base)
4891+ raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
4892 except KeyError:
4893 raise errors.UnknownFormatError(format=format_string, kind='branch')
4894
4895@@ -1790,7 +1796,7 @@
4896 _repository=a_bzrdir.find_repository(),
4897 ignore_fallbacks=ignore_fallbacks)
4898 except errors.NoSuchFile:
4899- raise errors.NotBranchError(path=transport.base)
4900+ raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
4901
4902 def __init__(self):
4903 super(BranchFormatMetadir, self).__init__()
4904@@ -1971,7 +1977,7 @@
4905 def get_reference(self, a_bzrdir):
4906 """See BranchFormat.get_reference()."""
4907 transport = a_bzrdir.get_branch_transport(None)
4908- return transport.get('location').read()
4909+ return transport.get_bytes('location')
4910
4911 def set_reference(self, a_bzrdir, to_branch):
4912 """See BranchFormat.set_reference()."""
4913@@ -2072,7 +2078,7 @@
4914 _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
4915
4916
4917-class BzrBranch(Branch):
4918+class BzrBranch(Branch, _RelockDebugMixin):
4919 """A branch stored in the actual filesystem.
4920
4921 Note that it's "local" in the context of the filesystem; it doesn't
4922@@ -2124,9 +2130,12 @@
4923 return self.control_files.is_locked()
4924
4925 def lock_write(self, token=None):
4926+ if not self.is_locked():
4927+ self._note_lock('w')
4928 # All-in-one needs to always unlock/lock.
4929 repo_control = getattr(self.repository, 'control_files', None)
4930 if self.control_files == repo_control or not self.is_locked():
4931+ self.repository._warn_if_deprecated(self)
4932 self.repository.lock_write()
4933 took_lock = True
4934 else:
4935@@ -2139,9 +2148,12 @@
4936 raise
4937
4938 def lock_read(self):
4939+ if not self.is_locked():
4940+ self._note_lock('r')
4941 # All-in-one needs to always unlock/lock.
4942 repo_control = getattr(self.repository, 'control_files', None)
4943 if self.control_files == repo_control or not self.is_locked():
4944+ self.repository._warn_if_deprecated(self)
4945 self.repository.lock_read()
4946 took_lock = True
4947 else:
4948@@ -2153,6 +2165,7 @@
4949 self.repository.unlock()
4950 raise
4951
4952+ @only_raises(errors.LockNotHeld, errors.LockBroken)
4953 def unlock(self):
4954 try:
4955 self.control_files.unlock()
4956
4957=== modified file 'bzrlib/btree_index.py'
4958--- bzrlib/btree_index.py 2009-09-09 19:32:27 +0000
4959+++ bzrlib/btree_index.py 2010-02-18 00:00:50 +0000
4960@@ -1,4 +1,4 @@
4961-# Copyright (C) 2008 Canonical Ltd
4962+# Copyright (C) 2008, 2009, 2010 Canonical Ltd
4963 #
4964 # This program is free software; you can redistribute it and/or modify
4965 # it under the terms of the GNU General Public License as published by
4966@@ -17,6 +17,7 @@
4967
4968 """B+Tree indices"""
4969
4970+import cStringIO
4971 from bisect import bisect_right
4972 import math
4973 import tempfile
4974@@ -30,6 +31,7 @@
4975 index,
4976 lru_cache,
4977 osutils,
4978+ static_tuple,
4979 trace,
4980 )
4981 from bzrlib.index import _OPTION_NODE_REFS, _OPTION_KEY_ELEMENTS, _OPTION_LEN
4982@@ -60,14 +62,20 @@
4983 def __init__(self):
4984 """Create a _BuilderRow."""
4985 self.nodes = 0
4986- self.spool = tempfile.TemporaryFile()
4987+ self.spool = None# tempfile.TemporaryFile(prefix='bzr-index-row-')
4988 self.writer = None
4989
4990 def finish_node(self, pad=True):
4991 byte_lines, _, padding = self.writer.finish()
4992 if self.nodes == 0:
4993+ self.spool = cStringIO.StringIO()
4994 # padded note:
4995 self.spool.write("\x00" * _RESERVED_HEADER_BYTES)
4996+ elif self.nodes == 1:
4997+ # We got bigger than 1 node, switch to a temp file
4998+ spool = tempfile.TemporaryFile(prefix='bzr-index-row-')
4999+ spool.write(self.spool.getvalue())
5000+ self.spool = spool
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches