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
=== modified file '.bzrignore'
--- .bzrignore 2009-09-09 11:43:10 +0000
+++ .bzrignore 2010-02-18 00:00:52 +0000
@@ -58,6 +58,9 @@
58bzrlib/_known_graph_pyx.c58bzrlib/_known_graph_pyx.c
59bzrlib/_readdir_pyx.c59bzrlib/_readdir_pyx.c
60bzrlib/_rio_pyx.c60bzrlib/_rio_pyx.c
61bzrlib/_simple_set_pyx.c
62bzrlib/_simple_set_pyx.h
63bzrlib/_simple_set_pyx_api.h
61bzrlib/_walkdirs_win32.c64bzrlib/_walkdirs_win32.c
62# built extension modules65# built extension modules
63bzrlib/_*.dll66bzrlib/_*.dll
6467
=== modified file 'Makefile'
--- Makefile 2009-11-18 04:29:56 +0000
+++ Makefile 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd1# Copyright (C) 2005-2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -40,8 +40,6 @@
4040
41check-nodocs: extensions41check-nodocs: extensions
42 $(PYTHON) -Werror -O ./bzr selftest -1v $(tests)42 $(PYTHON) -Werror -O ./bzr selftest -1v $(tests)
43 @echo "Running all tests with no locale."
44 LC_CTYPE= LANG=C LC_ALL= ./bzr selftest -1v $(tests) 2>&1 | sed -e 's/^/[ascii] /'
4543
46# Run Python style checker (apt-get install pyflakes)44# Run Python style checker (apt-get install pyflakes)
47#45#
@@ -201,7 +199,6 @@
201199
202# translate txt docs to html200# translate txt docs to html
203derived_txt_files = \201derived_txt_files = \
204 doc/en/user-reference/bzr_man.txt \
205 doc/en/release-notes/NEWS.txt202 doc/en/release-notes/NEWS.txt
206txt_all = \203txt_all = \
207 doc/en/tutorials/tutorial.txt \204 doc/en/tutorials/tutorial.txt \
@@ -214,6 +211,7 @@
214 doc/ja/tutorials/centralized_workflow.txt \211 doc/ja/tutorials/centralized_workflow.txt \
215 $(wildcard doc/*/mini-tutorial/index.txt) \212 $(wildcard doc/*/mini-tutorial/index.txt) \
216 $(wildcard doc/*/user-guide/index-plain.txt) \213 $(wildcard doc/*/user-guide/index-plain.txt) \
214 doc/en/admin-guide/index-plain.txt \
217 $(wildcard doc/es/guia-usario/*.txt) \215 $(wildcard doc/es/guia-usario/*.txt) \
218 $(derived_txt_files) \216 $(derived_txt_files) \
219 doc/en/upgrade-guide/index.txt \217 doc/en/upgrade-guide/index.txt \
@@ -223,7 +221,8 @@
223 doc/en/user-guide/index.txt \221 doc/en/user-guide/index.txt \
224 doc/es/user-guide/index.txt \222 doc/es/user-guide/index.txt \
225 doc/ja/user-guide/index.txt \223 doc/ja/user-guide/index.txt \
226 doc/ru/user-guide/index.txt224 doc/ru/user-guide/index.txt \
225 doc/en/admin-guide/index.txt
227txt_files = $(filter-out $(txt_nohtml), $(txt_all))226txt_files = $(filter-out $(txt_nohtml), $(txt_all))
228htm_files = $(patsubst %.txt, %.html, $(txt_files)) 227htm_files = $(patsubst %.txt, %.html, $(txt_files))
229228
@@ -281,6 +280,9 @@
281#doc/ru/user-guide/index.html: $(wildcard $(addsuffix /*.txt, doc/ru/user-guide)) 280#doc/ru/user-guide/index.html: $(wildcard $(addsuffix /*.txt, doc/ru/user-guide))
282# $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@281# $(rst2html) --stylesheet=../../default.css $(dir $@)index.txt $@
283#282#
283doc/en/admin-guide/index-plain.html: $(wildcard $(addsuffix /*.txt, doc/en/admin-guide))
284 $(rst2html) --stylesheet=../../default.css $(dir $@)index-plain.txt $@
285
284doc/developers/%.html: doc/developers/%.txt286doc/developers/%.html: doc/developers/%.txt
285 $(rst2html) --stylesheet=../default.css $< $@287 $(rst2html) --stylesheet=../default.css $< $@
286288
@@ -293,9 +295,6 @@
293%.html: %.txt295%.html: %.txt
294 $(rst2html) --stylesheet=../../default.css $< $@296 $(rst2html) --stylesheet=../../default.css $< $@
295297
296doc/en/user-reference/bzr_man.txt: $(MAN_DEPENDENCIES)
297 $(PYTHON) tools/generate_docs.py -o $@ rstx
298
299doc/en/release-notes/NEWS.txt: NEWS298doc/en/release-notes/NEWS.txt: NEWS
300 $(PYTHON) -c "import shutil; shutil.copyfile('$<', '$@')"299 $(PYTHON) -c "import shutil; shutil.copyfile('$<', '$@')"
301300
@@ -409,7 +408,7 @@
409408
410.PHONY: dist dist-upload-escudero check-dist-tarball409.PHONY: dist dist-upload-escudero check-dist-tarball
411410
412# build a distribution tarball and zip file.411# build a distribution source tarball
413#412#
414# this method of copying the pyrex generated files is a bit ugly; it would be413# this method of copying the pyrex generated files is a bit ugly; it would be
415# nicer to generate it from distutils.414# nicer to generate it from distutils.
@@ -419,15 +418,12 @@
419 expbasedir=`mktemp -t -d tmp_bzr_dist.XXXXXXXXXX` && \418 expbasedir=`mktemp -t -d tmp_bzr_dist.XXXXXXXXXX` && \
420 expdir=$$expbasedir/bzr-$$version && \419 expdir=$$expbasedir/bzr-$$version && \
421 tarball=$$PWD/../bzr-$$version.tar.gz && \420 tarball=$$PWD/../bzr-$$version.tar.gz && \
422 zipball=$$PWD/../bzr-$$version.zip && \
423 $(MAKE) clean && \421 $(MAKE) clean && \
424 $(MAKE) && \422 $(MAKE) && \
425 bzr export $$expdir && \423 bzr export $$expdir && \
426 cp bzrlib/*.c $$expdir/bzrlib/. && \424 cp bzrlib/*.c bzrlib/*.h $$expdir/bzrlib/. && \
427 tar cfz $$tarball -C $$expbasedir bzr-$$version && \425 tar cfz $$tarball -C $$expbasedir bzr-$$version && \
428 (cd $$expbasedir && zip -r $$zipball bzr-$$version) && \
429 gpg --detach-sign $$tarball && \426 gpg --detach-sign $$tarball && \
430 gpg --detach-sign $$zipball && \
431 rm -rf $$expbasedir427 rm -rf $$expbasedir
432428
433# run all tests in a previously built tarball429# run all tests in a previously built tarball
@@ -445,15 +441,10 @@
445dist-upload-escudero:441dist-upload-escudero:
446 version=`./bzr version --short` && \442 version=`./bzr version --short` && \
447 tarball=../bzr-$$version.tar.gz && \443 tarball=../bzr-$$version.tar.gz && \
448 zipball=../bzr-$$version.zip && \444 scp $$tarball $$tarball.sig \
449 scp $$zipball $$zipball.sig $$tarball $$tarball.sig \
450 escudero.ubuntu.com:/srv/bazaar.canonical.com/www/releases/src \445 escudero.ubuntu.com:/srv/bazaar.canonical.com/www/releases/src \
451 && \446 && \
452 echo verifying over http... && \447 echo verifying over http... && \
453 curl http://bazaar-vcs.org/releases/src/bzr-$$version.zip \
454 | diff -s - $$zipball && \
455 curl http://bazaar-vcs.org/releases/src/bzr-$$version.zip.sig \
456 | diff -s - $$zipball.sig
457 curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz \448 curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz \
458 | diff -s - $$tarball && \449 | diff -s - $$tarball && \
459 curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz.sig \450 curl http://bazaar-vcs.org/releases/src/bzr-$$version.tar.gz.sig \
460451
=== modified file 'NEWS'
--- NEWS 2010-02-12 06:00:33 +0000
+++ NEWS 2010-02-18 00:00:52 +0000
@@ -5,6 +5,348 @@
5.. contents:: List of Releases5.. contents:: List of Releases
6 :depth: 16 :depth: 1
77
8
9bzr 2.1.1
10#########
11
12:2.1.1: not released yet
13
14Bug Fixes
15*********
16
17* Register SIGWINCH handler only when creating a ``TextUIFactory``; avoids
18 problems importing bzrlib from a non-main thread.
19 (Elliot Murphy, #521989)
20
21* Standardize the error handling when creating a new ``StaticTuple``
22 (problems will raise TypeError). (Matt Nordhoff, #457979)
23
24* Merge correctly when this_tree is not a WorkingTree. (Aaron Bentley)
25
26
27bzr 2.1.0
28#########
29
30:Codename: Strasbourg
31:2.1.0: 2010-02-11
32
33This release marks our second long-term-stable series. The Bazaar team
34has decided that we will continue to make bugfix-only 2.0.x and 2.1.x
35releases, along with 2.2 development releases.
36
37This is a fairly incremental update, focusing on polish and bugfixing.
38There are no changes for supported disk formats. Key updates include
39reduced memory consumption for many operations, a new per-file merge
40hook, ignore patterns can now include '!' to exclude files, globbing
41support for all commands on Windows, and support for addressing home
42directories via ``bzr+ssh://host/~/`` syntax.
43
44Users are encouraged to upgrade from the 2.0 stable series.
45
46Bug Fixes
47*********
48
49* Don't require testtools to use sftp.
50 (Vincent Ladeuil, #516183)
51
52* Fix "AttributeError in Inter1and2Helper" during fetch.
53 (Martin Pool, #513432)
54
55* Ignore ``KeyError`` from ``remove_index`` during ``_abort_write_group``
56 in a pack repository, which can happen harmlessly if the abort occurs during
57 finishing the write group. Also use ``bzrlib.cleanup`` so that any
58 other errors that occur while aborting the individual packs won't be
59 hidden by secondary failures when removing the corresponding indices.
60 (Andrew Bennetts, #423015)
61
62* Using the ``bzrlib.chk_map`` module from within multiple threads at the
63 same time was broken due to race conditions with a module level page
64 cache. This shows up as a KeyError in the ``bzrlib.lru_cache`` code with
65 ``bzrlib.chk_map`` in the backtrace, and can be triggered without using
66 the same high level objects such as ``bzrlib.repository.Repository``
67 from different threads. chk_map now uses a thread local cache which may
68 increase memory pressure on processes using threads.
69 (Robert Collins, John Arbash Meinel, #514090)
70
71* The new ``merge_file_content`` should now be ok with tests to avoid
72 regressions.
73 (Vincent Ladeuil, #515597)
74
75Internals
76*********
77
78* Use ``bzrlib.cleanup`` rather than less robust ``try``/``finally``
79 blocks in several places in ``bzrlib.merge``. This avoids masking prior
80 errors when errors like ``ImmortalPendingDeletion`` occur during cleanup
81 in ``do_merge``.
82 (Andrew Bennetts, #517275)
83
84API Changes
85***********
86
87* The ``remove_index`` method of
88 ``bzrlib.repofmt.pack_repo.AggregateIndex`` no longer takes a ``pack``
89 argument. This argument was always ignored.
90 (Andrew Bennetts, #423015)
91
92bzr 2.1.0rc2
93############
94
95:Codename: after the bubbles
96:2.1.0rc2: 2010-01-29
97
98This is a quick-turn-around to update a small issue with our new per-file
99merge hook. We expect no major changes from this to the final 2.1.0.
100
101API Changes
102***********
103
104* The new ``merge_file_content`` hook point has been altered to provide a
105 better API where state for extensions can be stored rather than the
106 too-simple function based approach. This fixes a performance regression
107 where branch configuration would be parsed per-file during merge. As
108 part of this the included news_merger has been refactored into a base
109 helper class ``bzrlib.merge.ConfigurableFileMerger``.
110 (Robert Collins, John Arbash Meinel, #513822)
111
112
113bzr 2.1.0rc1
114############
115
116:Codename: the 'new' stable
117:2.1.0rc1: 2009-01-21
118
119This is the first stable release candidate for Bazaar's 2.1 series. From
120this point onwards, the 2.1 series will be considered stable (as the 2.0
121series) and only bugfixes are expected to be incorporated. The dozen or so
122bugfixes in the 2.0.4 release are also included in this release (along
123with more than 15 more bugfixes). Some of the interesting features are
124support for per-file merge hooks, ``bzr unshelve --preview``, support
125for using ! in ignore files to exclude files from being ignored, a small
126memory leak was squashed, and many ``ObjectNotLocked`` errors were fixed.
127This looks to be a very good start for a new stable series.
128
129
130New Features
131************
132
133* Add bug information to log output when available.
134 (Neil Martinsen-Burrell, Guillermo Gonzalez, #251729)
135
136* Added ``merge_file_content`` hook point to ``Merger``, allowing plugins
137 to register custom merge logic, e.g. to provide smarter merging for
138 particular files.
139
140* Bazaar now includes the ``news_merge`` plugin. It is disabled by
141 default, to enable it add a ``news_merge_files`` option to your
142 configuration. Consult ``bzr help news_merge`` for more information.
143 (Andrew Bennetts)
144
145* ``bzr branch`` now takes a ``--bind`` option. This lets you
146 branch and bind all in one command. (Ian Clatworthy)
147
148* ``bzr switch`` now takes a ``--revision`` option, to allow switching to
149 a specific revision of a branch. (Daniel Watkins, #183559)
150
151* ``bzr unshelve --preview`` can now be used to show how a patch on the
152 shelf would be applied to the working tree.
153 (Guilherme Salgado, #308122)
154
155* ``bzr update`` now takes a ``--revision`` argument. This lets you
156 change the revision of the working tree to any revision in the
157 ancestry of the current or master branch. (Matthieu Moy, Mark Hammond,
158 Martin Pool, #45719)
159
160* ``-Dbytes`` can now be used to display the total number of bytes
161 transferred for the current command. This information is always logged
162 to ``.bzr.log`` for later inspection. (John Arbash Meinel)
163
164* New ignore patterns. Patterns prefixed with '!' are exceptions to
165 ignore patterns and take precedence over regular ignores. Such
166 exceptions are used to specify files that should be versioned which
167 would otherwise be ignored. Patterns prefixed with '!!' act as regular
168 ignore patterns, but have highest precedence, even over the '!'
169 exception patterns. (John Whitley, #428031)
170
171* The ``supress_warnings`` configuration option has been introduced to disable
172 various warnings (it currently only supports the ``format_deprecation``
173 warning). The new option can be set in any of the following locations:
174 ``bazaar.conf``, ``locations.conf`` and/or ``branch.conf``.
175 (Ted Gould, Matthew Fuller, Vincent Ladeuil)
176
177Bug Fixes
178*********
179
180* Always show a message if an OS error occurs while trying to run a
181 user-specified commit message editor.
182 (Martin Pool, #504842)
183
184* ``bzr diff`` will now use the epoch when it is unable to determine
185 the timestamp of a file, if the revision it was introduced in is a
186 ghost. (Jelmer Vernooij, #295611)
187
188* ``bzr switch -b`` can now create branches that are located using directory
189 services such as ``lp:``, even when the branch name doesn't contain a
190 '/'. (Neil Martinsen-Burrell, #495263)
191
192* ``bzr unshelve`` has improved messages about what it is doing.
193 (Neil Martinsen-Burrell, #496917)
194
195* Concurrent autopacking is more resilient to already-renamed pack files.
196 If we find that a file we are about to obsolete is already obsoleted, we
197 do not try to rename it, and we leave the file in ``obsolete_packs``.
198 The code is also fault tolerant if a file goes missing, assuming that
199 another process already removed the file.
200 (John Arbash Meinel, Gareth White, #507557)
201
202* Fix "Too many concurrent requests" in reconcile when network connection
203 fails. (Andrew Bennetts, #503878)
204
205* Fixed a side effect mutation of ``RemoteBzrDirFormat._network_name``
206 that caused some tests to fail when run in a non-default order.
207 Probably no user impact. (Martin Pool, #504102)
208
209* Fixed ``ObjectNotLocked`` error in ``bzr cat -rbranch:../foo FILE``.
210 (Andrew Bennetts, #506274)
211
212* FTP transports support Unicode paths by encoding/decoding them as utf8.
213 (Vincent Ladeuil, #472161)
214
215* Listen to the SIGWINCH signal to update the terminal width.
216 (Vincent Ladeuil, #316357)
217
218* Progress bars are now hidden when ``--quiet`` is given.
219 (Martin Pool, #320035)
220
221* ``SilentUIFactory`` now supports ``make_output_stream`` and discards
222 whatever is written to it. This un-breaks some plugin tests that
223 depended on this behaviour.
224 (Martin Pool, #499757)
225
226* When operations update the working tree, all affected files should end
227 up with the same mtime. (eg. when versioning a generated file, if you
228 update the source and the generated file together, the generated file
229 should appear up-to-date.)
230 (John Arbash Meinel, Martin <gzlist>, #488724)
231
232Improvements
233************
234
235* Added ``add_cleanup`` and ``cleanup_now`` to ``bzrlib.command.Command``.
236 All the builtin commands now use ``add_cleanup`` rather than
237 ``try``/``finally`` blocks where applicable as it is simpler and more
238 robust. (Andrew Bennetts)
239
240* All except a small number of storage formats are now hidden, making
241 the help for numerous commands far more digestible. (Ian Clatworthy)
242
243* Attempts to open a shared repository as a branch (e.g. ``bzr branch
244 path/to/repo``) will now include "location is a repository" as a hint in
245 the error message. (Brian de Alwis, Andrew Bennetts, #440952)
246
247* Push will now inform the user when they are trying to push to a foreign
248 VCS for which roundtripping is not supported, and will suggest them to
249 use dpush. (Jelmer Vernooij)
250
251* The version of bzr being run is now written to the log file.
252 (__monty__, #257170)
253
254* Transport network activity indicator is shown more of the time when
255 Bazaar is doing network IO.
256 (Martin Pool)
257
258Documentation
259*************
260
261* Add documentation on creating merges with more than one parent.
262 (Neil Martinsen-Burrell, #481526)
263
264* Better explain the --uncommitted option of merge.
265 (Neil Martinsen-Burrell, #505088)
266
267* Improve discussion of pending merges in the documentation for
268 ``revert``. (Neil Martinsen-Burrell, #505093)
269
270* Improved help for ``bzr send``.
271 (Martin Pool, Bojan Nikolic)
272
273* There is a System Administrator's Guide in ``doc/en/admin-guide``,
274 including discussions of installation, relevant plugins, security and
275 backup. (Neil Martinsen-Burrell)
276
277* The ``conflicts`` help topic has been renamed to ``conflict-types``.
278 (Ian Clatworthy)
279
280* The User Reference is now presented as a series of topics.
281 Many of the included topics have link and format tweaks applied.
282 (Ian Clatworthy)
283
284API Changes
285***********
286
287* Added ``cachedproperty`` decorator to ``bzrlib.decorators``.
288 (Andrew Bennetts)
289
290* Many test features were renamed from ``FooFeature`` to ``foo_feature``
291 to be consistent with instances being lower case and classes being
292 CamelCase. For the features that were more likely to be used, we added a
293 deprecation thunk, but not all. (John Arbash Meinel)
294
295* Merger classes (such as ``Merge3Merger``) now expect a ``this_branch``
296 parameter in their constructors, and provide ``this_branch`` as an
297 attribute. (Andrew Bennetts)
298
299* The Branch hooks pre_change_branch_tip no longer masks exceptions raised
300 by plugins - the original exceptions are now preserved. (Robert Collins)
301
302* The Transport ``Server.tearDown`` method is now renamed to
303 ``stop_server`` and ``setUp`` to ``start_server`` for consistency with
304 our normal naming pattern, and to avoid confusion with Python's
305 ``TestCase.tearDown``. (Martin Pool)
306
307* ``WorkingTree.update`` implementations must now accept a ``revision``
308 parameter.
309
310Internals
311*********
312
313* Added ``BzrDir.open_branchV3`` smart server request, which can receive
314 a string of details (such as "location is a repository") as part of a
315 ``nobranch`` response. (Andrew Bennetts, #440952)
316
317* New helper osutils.UnicodeOrBytesToBytesWriter which encodes unicode
318 objects but passes str objects straight through. This is used for
319 selftest but may be useful for diff and other operations that generate
320 mixed output. (Robert Collins)
321
322* New exception ``NoRoundtrippingSupport``, for use by foreign branch
323 plugins. (Jelmer Vernooij)
324
325Testing
326*******
327
328* ``bzrlib.tests.permute_for_extension`` is a helper that simplifies
329 running all tests in the current module, once against a pure python
330 implementation, and once against an extension (pyrex/C) implementation.
331 It can be used to dramatically simplify the implementation of
332 ``load_tests``. (John Arbash Meinel)
333
334* ``bzrlib.tests.TestCase`` now subclasses ``testtools.testcase.TestCase``.
335 This permits features in testtools such as getUniqueInteger and
336 getUniqueString to be used. Because of this, testtools version 0.9.2 or
337 newer is now a dependency to run bzr selftest. Running with versions of
338 testtools less than 0.9.2 will cause bzr to error while loading the test
339 suite. (Robert Collins)
340
341* Shell-like tests now support the command "mv" for moving files. The
342 syntax for ``mv file1 file2``, ``mv dir1 dir2`` and ``mv file dir`` is
343 supported. (Neil Martinsen-Burrell)
344
345* The test progress bar no longer distinguishes tests that 'errored' from
346 tests that 'failed' - they're all just failures.
347 (Martin Pool)
348
349
8bzr 2.0.5 (not released yet)350bzr 2.0.5 (not released yet)
9############################351############################
10352
@@ -111,6 +453,190 @@
111 bug #495023. (John Arbash Meinel)453 bug #495023. (John Arbash Meinel)
112454
113455
456bzr 2.1.0b4
457###########
458
459:Codename: san francisco airport
460:2.1.0b4: 2009-12-14
461
462The fourth beta release in the 2.1 series brings with it a significant
463number of bugfixes (~20). The test suite is once again (finally) "green"
464on Windows, and should remain that way for future releases. There are a
465few performance related updates (faster upgrade and log), and several UI
466tweaks. There has also been a significant number of tweaks to the runtime
467documentation. 2.1.0b4 include everything from the 2.0.3 release.
468
469
470Compatibility Breaks
471********************
472
473* The BZR_SSH environmental variable may now be set to the path of a secure
474 shell client. If currently set to the value ``ssh`` it will now guess the
475 vendor of the program with that name, to restore the old behaviour that
476 indicated the SSH Corporation client use ``sshcorp`` instead as the magic
477 string. (Martin <gzlist@googlemail.com>, #176292)
478
479New Features
480************
481
482* ``bzr commit`` now has a ``--commit-time`` option.
483 (Alexander Sack, #459276)
484
485* ``-Dhpss`` now increases logging done when run on the bzr server,
486 similarly to how it works on the client. (John Arbash Meinel)
487
488* New option ``bzr unshelve --keep`` applies the changes and leaves them
489 on the shelf. (Martin Pool, Oscar Fuentes, #492091)
490
491* The ``BZR_COLUMNS`` envrionment variable can be set to force bzr to
492 respect a given terminal width. This can be useful when output is
493 redirected or in obscure cases where the default value is not
494 appropriate. Pagers can use it to get a better control of the line
495 lengths.
496 (Vincent Ladeuil)
497
498* The new command ``bzr lp-mirror`` will request that Launchpad update its
499 mirror of a local branch. This command will only function if launchpadlib
500 is installed.
501 (Jonathan Lange)
502
503
504Bug Fixes
505*********
506
507* After renaming a file, the dirstate could accidentally reference
508 ``source\\path`` rather than ``source/path`` on Windows. This might be a
509 source of some dirstate-related failures. (John Arbash Meinel)
510
511* ``bzr commit`` now detects commit messages that looks like file names
512 and issues a warning.
513 (Gioele Barabucci, #73073)
514
515* ``bzr ignore /`` no longer causes an IndexError. (Gorder Tyler, #456036)
516
517* ``bzr log -n0 -rN`` should not return revisions beyond its merged revisions.
518 (#325618, #484109, Marius Kruger)
519
520* ``bzr merge --weave`` and ``--lca`` will now create ``.BASE`` files for
521 files with conflicts (similar to ``--merge3``). The contents of the file
522 is a synthesis of all bases used for the merge.
523 (John Arbash Meinel, #40412)
524
525* ``bzr mv --quiet`` really is quiet now. (Gordon Tyler, #271790)
526
527* ``bzr serve`` is more clear about the risk of supplying --allow-writes.
528 (Robert Collins, #84659)
529
530* ``bzr serve --quiet`` really is quiet now. (Gordon Tyler, #252834)
531
532* Fix bug with redirected URLs over authenticated HTTP.
533 (Glen Mailer, Neil Martinsen-Burrell, Vincent Ladeuil, #395714)
534
535* Interactive merge doesn't leave branch locks behind. (Aaron Bentley)
536
537* Lots of bugfixes for the test suite on Windows. We should once again
538 have a test suite with no failures on Windows. (John Arbash Meinel)
539
540* ``osutils.terminal_width()`` obeys the BZR_COLUMNS environment
541 variable but returns None if the terminal is not a tty (when output is
542 redirected for example). Also fixes its usage under OSes that doesn't
543 provide termios.TIOCGWINSZ. Make sure the corresponding tests runs on
544 windows too.
545 (Joke de Buhr, Vincent Ladeuil, #353370, #62539)
546 (John Arbash Meinel, Vincent Ladeuil, #492561)
547
548* Terminate ssh subprocesses when no references to them remain, fixing
549 subprocess and file descriptor leaks. (Andrew Bennetts, #426662)
550
551* The ``--hardlink`` option of ``bzr branch`` and ``bzr checkout`` now
552 works for 2a format trees. Only files unaffected by content filters
553 will be hardlinked. (Andrew Bennetts, #408193)
554
555* The new glob expansion on Windows would replace all ``\`` characters
556 with ``/`` even if it there wasn't a glob to expand, the arg was quoted,
557 etc. Now only change slashes if there is something being glob expanded.
558 (John Arbash Meinel, #485771)
559
560* Use our faster ``KnownGraph.heads()`` functionality when computing the
561 new rich-root heads. This can cut a conversion time in half (mysql from
562 13.5h => 6.2h) (John Arbash Meinel, #487632)
563
564* When launching a external diff tool via bzr diff --using, temporary files
565 are no longer created, rather, the path to the file in the working tree is
566 passed to the external diff tool. This allows the file to be edited if the
567 diff tool provides for this. (Gary van der Merwe, #490738)
568
569* The launchpad-open command can now be used from a subdirectory of a
570 branch, not just from the root of the branch.
571 (Neil Martinsen-Burrell, #489102)
572
573
574Improvements
575************
576
577* ``bzr log`` is now faster. (Ian Clatworthy)
578
579* ``bzr update`` provides feedback on which branch it is up to date with.
580 (Neil Martinsen-Burrell)
581
582* ``bzr upgrade`` from pre-2a to 2a can be significantly faster (4x).
583 For details see the xml8 patch and heads() improvements.
584 (John Arbash Meinel)
585
586* ``bzrlib.urlutils.local_path_from_url`` now accepts
587 'file://localhost/' as well as 'file:///' URLs on POSIX. (Michael
588 Hudson)
589
590* The progress bar now shows only a spinner and per-operation counts,
591 not an overall progress bar. The previous bar was often not correlated
592 with real overall operation progress, either because the operations take
593 nonlinear time, or because at the start of the operation Bazaar couldn't
594 estimate how much work there was to do. (Martin Pool)
595
596Documentation
597*************
598
599* Lots of documentation tweaks for inline help topics and command help
600 information.
601
602API Changes
603***********
604
605* ``bzrlib.textui`` (vestigial module) removed. (Martin Pool)
606
607* The Launchpad plugin now has a function ``login`` which will log in to
608 Launchpad with launchpadlib, and ``load_branch`` which will return the
609 Launchpad Branch object corresponding to a given Bazaar Branch object.
610 (Jonathan Lange)
611
612Internals
613*********
614
615* New test Feature: ``ModuleAvailableFeature``. It is designed to make it
616 easier to handle what tests you want to run based on what modules can be
617 imported. (Rather than lots of custom-implemented features that were
618 basically copy-and-pasted.) (John Arbash Meinel)
619
620* ``osutils.timer_func()`` can be used to get either ``time.time()`` or
621 ``time.clock()`` when you want to do performance timing.
622 ``time.time()`` is limited to 15ms resolution on Windows, but
623 ``time.clock()`` gives CPU and not wall-clock time on other platforms.
624 (John Arbash Meinel)
625
626* Several code paths that were calling ``Transport.get().read()`` have
627 been changed to the equalivent ``Transport.get_bytes()``. The main
628 difference is that the latter will explicitly call ``file.close()``,
629 rather than expecting the garbage collector to handle it. This helps
630 with some race conditions on Windows during the test suite and sftp
631 tests. (John Arbash Meinel)
632
633Testing
634*******
635
636* TestCaseWithMemoryTransport no longer sets $HOME and $BZR_HOME to
637 unicode strings. (Michael Hudson, #464174)
638
639
114bzr 2.0.3640bzr 2.0.3
115#########641#########
116642
@@ -140,6 +666,233 @@
140* Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325)666* Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325)
141667
142668
669bzr 2.1.0b3
670###########
671
672:Codename: after sprint recovery
673:2.1.0b3: 2009-11-16
674
675This release was pushed up from its normal release cycle due to a
676regression in python 2.4 compatibility in 2.1.0b2. Since this regression
677was caught before 2.1.0b2 was officially announced, the full changelog
678includes both 2.1.0b3 and 2.1.0b2 changes.
679
680Highlights of 2.1.0b3 are: new globbing code for all commands on Windows,
681the test suite now conforms to python's trunk enhanced semantics (skip,
682etc.), and ``bzr info -v`` will now report the correct branch and repo
683formats for Remote objects.
684
685
686New Features
687************
688
689* Users can define a shelve editor to provide shelf functionality at a
690 granularity finer than per-patch-hunk. (Aaron Bentley)
691
692Bug Fixes
693*********
694
695* Fix for shell completion and short options. (Benoît PIERRE)
696
697* Hooks daughter classes should always call the base constructor.
698 (Alexander Belchenko, Vincent Ladeuil, #389648)
699
700* Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325)
701
702* On Windows, do glob expansion at the command-line level (as is usually
703 done in bash, etc.) This means that *all* commands get glob expansion
704 (bzr status, bzr add, bzr mv, etc). It uses a custom command line
705 parser, which allows us to know if a given section was quoted. It means
706 you can now do ``bzr ignore "*.py"``.
707 (John Arbash Meinel, #425510, #426410, #194450)
708
709* Sanitize commit messages that come in from the '-m' flag. We translate
710 '\r\n' => '\n' and a plain '\r' => '\n'. The storage layer doesn't
711 allow those because XML store silently translate it anyway. (The parser
712 auto-translates \r\n => \n in ways that are hard for us to catch.)
713
714* Show correct branch and repository format descriptions in
715 ``bzr info -v`` on a smart server location. (Andrew Bennetts, #196080)
716
717* The fix for bug #186920 accidentally broke compatibility with python
718 2.4. (Vincent Ladeuil, #475585)
719
720* Using ``Repository.get_commit_builder().record_iter_changes()`` now
721 correctly sets ``self.inv_sha1`` to a sha1 string and
722 ``self.new_inventory`` to an Inventory instance after calling
723 ``self.finish_inventory()``. (Previously it accidently set both values
724 as a tuple on ``self.inv_sha1``. This was missed because
725 ``repo.add_revision`` ignores the supplied inventory sha1 and recomputes
726 the sha1 from the repo directly. (John Arbash Meinel)
727
728* Shelve command refuse to run if there is no real terminal.
729 (Alexander Belchenko)
730
731* Avoid unnecessarily flushing of trace file; it's now unbuffered at the
732 Python level. (Martin Pool)
733
734Documentation
735*************
736
737* Include Japanese translations for documentation (Inada Naoki)
738
739* New API ``ui_factory.make_output_stream`` to be used for sending bulk
740 (rather than user-interaction) data to stdout. This automatically
741 coordinates with progress bars or other terminal activity, and can be
742 overridden by GUIs.
743 (Martin Pool, 493944)
744
745Internals
746*********
747
748* Some of the core groupcompress functionality now releases the GIL before
749 operation. Similar to how zlib and bz2 operate without the GIL in the
750 core compression and decompression routines. (John Arbash Meinel)
751
752Testing
753*******
754
755* -Dhpssvfs will now trigger on ``RemoteBzrDir._ensure_real``, providing
756 more debugging of VFS access triggers. (Robert Collins)
757
758* KnownFailure is now signalled to ``ExtendedTestResult`` using the same
759 method that Python 2.7 uses - ``addExpectedFailure``. (Robert Collins)
760
761* ``--parallel=fork`` is now compatible with --subunit.
762 (Robert Collins, Vincent Ladeuil, #419776)
763
764* Reporting of failures shows test ids not descriptions and thus shows
765 parameterised tests correctly. (Robert Collins)
766
767* TestNotApplicable is now handled within the TestCase.run method rather
768 than being looked for within ``ExtendedTestResult.addError``. This
769 provides better handling with other ``TestResult`` objects, degrading to
770 sucess rather than error. (Robert Collins)
771
772* The private method ``_testConcluded`` on ``ExtendedTestResult`` has been
773 removed - it was empty and unused. (Robert Collins)
774
775* UnavailableFeature is now handled within the TestCase.run method rather
776 than being looked for within addError. If the Result object does not
777 have an addNotSupported method, addSkip is attempted instead, and
778 failing that addSuccess. (Robert Collins)
779
780* When a TestResult does not have an addSkip method, skipped tests are now
781 reported as successful tests, rather than as errors. This change is
782 to make it possible to get a clean test run with a less capable
783 TestResult. (Robert Collins)
784
785
786
787bzr 2.1.0b2
788###########
789
790:Codename: a load off my mind
791:2.1.0b2: 2009-11-02
792
793This is our second feature-filled release since 2.0, pushing us down the
794path to a 2.1.0. Once again, all bugfixes in 2.0.2 are present in 2.1.0b2.
795
796Key highlights in this release are: improved handling of
797failures-during-cleanup for commit, fixing a long-standing bug with
798``bzr+http`` and shared repositories, all ``lp:`` urls to be resolved
799behind proxies, and a new StaticTuple datatype, allowing us to reduce
800memory consumption (50%) and garbage collector overhead (40% faster) for
801many operations.
802
803* A new ``--concurrency`` option has been added as well as an associated
804 BZR_CONCURRENCY environment variable to specify the number of
805 processes that can be run concurrently when running ``bzr selftest``. The
806 command-line option overrides the environment variable if both are
807 specified. If none is specified. the number of processes is obtained
808 from the OS as before. (Matt Nordhoff, Vincent Ladeuil)
809
810Bug Fixes
811*********
812
813* ``bzr+http`` servers no longer give spurious jail break errors when
814 serving branches inside a shared repository. (Andrew Bennetts, #348308)
815
816* Errors during commit are handled more robustly so that knock-on errors
817 are less likely to occur, and will not obscure the original error if
818 they do occur. This fixes some causes of ``TooManyConcurrentRequests``
819 and similar errors. (Andrew Bennetts, #429747, #243391)
820
821* Launchpad urls can now be resolved from behind proxies.
822 (Gordon Tyler, Vincent Ladeuil, #186920)
823
824* Reduce the strictness for StaticTuple, instead add a debug flag
825 ``-Dstatic_tuple`` which will change apis to be strict and raise errors.
826 This way, most users won't see failures, but developers can improve
827 internals. (John Arbash Meinel, #471193)
828
829* TreeTransform.adjust_path updates the limbo paths of descendants of adjusted
830 files. (Aaron Bentley)
831
832* Unicode paths are now handled correctly and consistently by the smart
833 server. (Andrew Bennetts, Michael Hudson, #458762)
834
835Improvements
836************
837
838* When reading index files, we now use a ``StaticTuple`` rather than a
839 plain ``tuple`` object. This generally gives a 20% decrease in peak
840 memory, and can give a performance boost up to 40% on large projects.
841 (John Arbash Meinel)
842
843* Peak memory under certain operations has been reduced significantly.
844 (eg, 'bzr branch launchpad standalone' is cut in half)
845 (John Arbash Meinel)
846
847Documentation
848*************
849
850* Filtered views user documentation upgraded to refer to format 2a
851 instead of pre-2.0 formats. (Ian Clatworthy)
852
853API Changes
854***********
855
856* Remove deprecated ``CLIUIFactory``. (Martin Pool)
857
858* ``UIFactory`` now has new ``show_error``, ``show_message`` and
859 ``show_warning`` methods, which can be hooked by non-text UIs.
860 (Martin Pool)
861
862Internals
863*********
864
865* Added ``bzrlib._simple_set_pyx``. This is a hybrid between a Set and a
866 Dict (it only holds keys, but you can lookup the object located at a
867 given key). It has significantly reduced memory consumption versus the
868 builtin objects (1/2 the size of Set, 1/3rd the size of Dict). This is
869 used as the interning structure for StaticTuple objects.
870 (John Arbash Meinel)
871
872* ``bzrlib._static_tuple_c.StaticTuple`` is now available and used by
873 the btree index parser and the chk map parser. This class functions
874 similarly to ``tuple`` objects. However, it can only point to a limited
875 collection of types. (Currently StaticTuple, str, unicode, None, bool,
876 int, long, float, but not subclasses). This allows us to remove it from
877 the garbage collector (it cannot be in a cycle), it also allows us to
878 intern the objects. In testing, this can reduce peak memory by 20-40%,
879 and significantly improve performance by removing objects from being
880 inspected by the garbage collector. (John Arbash Meinel)
881
882* ``GroupCompressBlock._ensure_content()`` will now release the
883 ``zlib.decompressobj()`` when the first request is for all of the
884 content. (Previously it would only be released if you made a request for
885 part of the content, and then all of it later.) This turns out to be a
886 significant memory savings, as a ``zstream`` carries around approx 260kB
887 of internal state and buffers. (For branching bzr.dev this drops peak
888 memory from 382MB => 345MB.) (John Arbash Meinel)
889
890* When streaming content between ``2a`` format repositories, we now clear
891 caches from earlier versioned files. (So 'revisions' is cleared when we
892 start reading 'inventories', etc.) This can have a significant impact on
893 peak memory for initial copies (~200MB). (John Arbash Meinel)
894
895
143bzr 2.0.2896bzr 2.0.2
144#########897#########
145898
@@ -187,6 +940,219 @@
187 instead of pre-2.0 formats. (Ian Clatworthy)940 instead of pre-2.0 formats. (Ian Clatworthy)
188941
189942
943bzr 2.1.0b1
944###########
945
946:Codename: While the cat is away
947:2.1.0b1: 2009-10-14
948
949This is the first development release in the new split "stable" and
950"development" series. As such, the release is a snapshot of bzr.dev
951without creating a release candidate first. This release includes a
952fair amount of internal changes, with deprecated code being removed,
953and several new feature developments. People looking for a stable code
954base with only bugfixes should focus on the 2.0.1 release. All bugfixes
955present in 2.0.1 are present in 2.1.0b1.
956
957Highlights include support for ``bzr+ssh://host/~/homedir`` style urls,
958finer control over the plugin search path via extended BZR_PLUGIN_PATH
959syntax, visible warnings when extension modules fail to load, and improved
960error handling during unlocking.
961
962
963New Features
964************
965
966* Bazaar can now send mail through Apple OS X Mail.app.
967 (Brian de Alwis)
968
969* ``bzr+ssh`` and ``bzr`` paths can now be relative to home directories
970 specified in the URL. Paths starting with a path segment of ``~`` are
971 relative to the home directory of the user running the server, and paths
972 starting with ``~user`` are relative to the home directory of the named
973 user. For example, for a user "bob" with a home directory of
974 ``/home/bob``, these URLs are all equivalent:
975
976 * ``bzr+ssh://bob@host/~/repo``
977 * ``bzr+ssh://bob@host/~bob/repo``
978 * ``bzr+ssh://bob@host/home/bob/repo``
979
980 If ``bzr serve`` was invoked with a ``--directory`` argument, then no
981 home directories outside that directory will be accessible via this
982 method.
983
984 This is a feature of ``bzr serve``, so pre-2.1 clients will
985 automatically benefit from this feature when ``bzr`` on the server is
986 upgraded. (Andrew Bennetts, #109143)
987
988* Extensions can now be compiled if either Cython or Pyrex is available.
989 Currently Pyrex is preferred, but that may change in the future.
990 (Arkanes)
991
992* Give more control on BZR_PLUGIN_PATH by providing a way to refer to or
993 disable the user, site and core plugin directories.
994 (Vincent Ladeuil, #412930, #316192, #145612)
995
996Bug Fixes
997*********
998
999* Bazaar's native protocol code now correctly handles EINTR, which most
1000 noticeably occurs if you break in to the debugger while connected to a
1001 bzr+ssh server. You can now can continue from the debugger (by typing
1002 'c') and the process continues. However, note that pressing C-\ in the
1003 shell may still kill the SSH process, which is bug 162509, so you must
1004 sent a signal to the bzr process specifically, for example by typing
1005 ``kill -QUIT PID`` in another shell. (Martin Pool, #341535)
1006
1007* ``bzr add`` in a tree that has files with ``\r`` or ``\n`` in the
1008 filename will issue a warning and skip over those files.
1009 (Robert Collins, #3918)
1010
1011* ``bzr dpush`` now aborts if uncommitted changes (including pending merges)
1012 are present in the working tree. The configuration option ``dpush_strict``
1013 can be used to set the default for this behavior.
1014 (Vincent Ladeuil, #438158)
1015
1016* ``bzr merge`` and ``bzr remove-tree`` now requires --force if pending
1017 merges are present in the working tree.
1018 (Vincent Ladeuil, #426344)
1019
1020* Clearer message when Bazaar runs out of memory, instead of a ``MemoryError``
1021 traceback. (Martin Pool, #109115)
1022
1023* Don't give a warning on Windows when failing to import ``_readdir_pyx``
1024 as it is never built. (John Arbash Meinel, #430645)
1025
1026* Don't restrict the command name used to run the test suite.
1027 (Vincent Ladeuil, #419950)
1028
1029* ftp transports were built differently when the kerberos python module was
1030 present leading to obscure failures related to ASCII/BINARY modes.
1031 (Vincent Ladeuil, #443041)
1032
1033* Network streams now decode adjacent records of the same type into a
1034 single stream, reducing layering churn. (Robert Collins)
1035
1036* PreviewTree behaves correctly when get_file_mtime is invoked on an unmodified
1037 file. (Aaron Bentley, #251532)
1038
1039* Registry objects should not use iteritems() when asked to use items().
1040 (Vincent Ladeuil, #430510)
1041
1042* Weave based repositories couldn't be cloned when committers were using
1043 domains or user ids embedding '.sig'. Now they can.
1044 (Matthew Fuller, Vincent Ladeuil, #430868)
1045
1046Improvements
1047************
1048
1049* Revision specifiers can now be given in a more DWIM form, without
1050 needing explicit prefixes for specifiers like tags or revision id's.
1051 See ``bzr help revisionspec`` for full details. (Matthew Fuller)
1052
1053* Bazaar gives a warning before exiting, and writes into ``.bzr.log``, if
1054 compiled extensions can't be loaded. This typically indicates a
1055 packaging or installation problem. In this case Bazaar will keep
1056 running using pure-Python versions, but this may be substantially
1057 slower. The warning can be disabled by setting
1058 ``ignore_missing_extensions = True`` in ``bazaar.conf``.
1059 See also <https://answers.launchpad.net/bzr/+faq/703>.
1060 (Martin Pool, #406113, #430529)
1061
1062* Secondary errors that occur during Branch.unlock and Repository.unlock
1063 no longer obscure the original error. These methods now use a new
1064 decorator, ``only_raises``. This fixes many causes of
1065 ``TooManyConcurrentRequests`` and similar errors.
1066 (Andrew Bennetts, #429747)
1067
1068Documentation
1069*************
1070
1071* Describe the new shell-like test feature. (Vincent Ladeuil)
1072
1073* Help on hooks no longer says 'Not deprecated' for hooks that are
1074 currently supported. (Ian Clatworthy, #422415)
1075
1076API Changes
1077***********
1078
1079* ``bzrlib.user_encoding`` has been removed; use
1080 ``bzrlib.osutils.get_user_encoding`` instead. (Martin Pool)
1081
1082* ``bzrlib.tests`` now uses ``stopTestRun`` for its ``TestResult``
1083 subclasses - the same as python's unittest module. (Robert Collins)
1084
1085* ``diff._get_trees_to_diff`` has been renamed to
1086 ``diff.get_trees_and_branches_to_diff``. It is now a public API, and it
1087 returns the old and new branches. (Gary van der Merwe)
1088
1089* ``bzrlib.trace.log_error``, ``error`` and ``info`` have been deprecated.
1090 (Martin Pool)
1091
1092* ``MutableTree.has_changes()`` does not require a tree parameter anymore. It
1093 now defaults to comparing to the basis tree. It now checks for pending
1094 merges too. ``Merger.check_basis`` has been deprecated and replaced by the
1095 corresponding has_changes() calls. ``Merge.compare_basis``,
1096 ``Merger.file_revisions`` and ``Merger.ensure_revision_trees`` have also
1097 been deprecated.
1098 (Vincent Ladeuil, #440631)
1099
1100* ``ProgressTask.note`` is deprecated.
1101 (Martin Pool)
1102
1103Internals
1104*********
1105
1106* Added ``-Drelock`` debug flag. It will ``note`` a message every time a
1107 repository or branch object is unlocked then relocked the same way.
1108 (Andrew Bennetts)
1109
1110* ``BTreeLeafParser.extract_key`` has been tweaked slightly to reduce
1111 mallocs while parsing the index (approx 3=>1 mallocs per key read).
1112 This results in a 10% speedup while reading an index.
1113 (John Arbash Meinel)
1114
1115* The ``bzrlib.lsprof`` module has a new class ``BzrProfiler`` which makes
1116 profiling in some situations like callbacks and generators easier.
1117 (Robert Collins)
1118
1119Testing
1120*******
1121
1122* Passing ``--lsprof-tests -v`` to bzr selftest will cause lsprof output to
1123 be output for every test. Note that this is very verbose! (Robert Collins)
1124
1125* Setting ``BZR_TEST_PDB=1`` when running selftest will cause a pdb
1126 post_mortem to be triggered when a test failure occurs. (Robert Collins)
1127
1128* Shell-like tests can now be written. Code in ``bzrlib/tests/script.py`` ,
1129 documentation in ``developers/testing.txt`` for details.
1130 (Vincent Ladeuil)
1131
1132* Some tests could end up with the same id, that was dormant for
1133 a long time.
1134 (Vincent Ladeuil, #442980)
1135
1136* Stop showing the number of tests due to missing features in the test
1137 progress bar. (Martin Pool)
1138
1139* Test parameterisation now does a shallow copy, not a deep copy of the test
1140 to be parameterised. This is not expected to break external use of test
1141 parameterisation, and is substantially faster. (Robert Collins)
1142
1143* Tests that try to open a bzr dir on an arbitrary transport will now
1144 fail unless they have explicitly permitted the transport via
1145 ``self.permit_url``. The standard test factories such as ``self.get_url``
1146 will permit the urls they provide automatically, so only exceptional
1147 tests should need to do this. (Robert Collins)
1148
1149* The break-in test no longer cares about clean shutdown of the child,
1150 instead it is happy if the debugger starts up. (Robert Collins)
1151
1152* The full test suite is expected to pass when the C extensions are not
1153 present. (Vincent Ladeuil, #430749)
1154
1155
190bzr 2.0.11156bzr 2.0.1
191#########1157#########
1921158
@@ -290,6 +1256,7 @@
290 to "land in 2.0.0". (Changes how bzrlib._format_version_tuple() handles1256 to "land in 2.0.0". (Changes how bzrlib._format_version_tuple() handles
291 micro = 0.) (John Arbash Meinel)1257 micro = 0.) (John Arbash Meinel)
2921258
1259
293bzr 2.0.0rc21260bzr 2.0.0rc2
294############1261############
2951262
@@ -346,10 +1313,6 @@
346 ghosts in its mainline. (Evaluating None as a tuple is bad.)1313 ghosts in its mainline. (Evaluating None as a tuple is bad.)
347 (John Arbash Meinel, #419241)1314 (John Arbash Meinel, #419241)
3481315
349* Fix a segmentation fault when computing the ``merge_sort`` of a graph
350 that has a ghost in the mainline ancestry.
351 (John Arbash Meinel, #419241)
352
353* ``groupcompress`` sort order is now more stable, rather than relying on1316* ``groupcompress`` sort order is now more stable, rather than relying on
354 ``topo_sort`` ordering. The implementation is now1317 ``topo_sort`` ordering. The implementation is now
355 ``KnownGraph.gc_sort``. (John Arbash Meinel)1318 ``KnownGraph.gc_sort``. (John Arbash Meinel)
@@ -417,6 +1380,9 @@
417Bug Fixes1380Bug Fixes
418*********1381*********
4191382
1383* Further tweaks to handling of ``bzr add`` messages about ignored files.
1384 (Jason Spashett, #76616)
1385
420* Fetches were being requested in 'groupcompress' order, but weren't1386* Fetches were being requested in 'groupcompress' order, but weren't
421 recombining the groups. Thus they would 'fragment' to get the correct1387 recombining the groups. Thus they would 'fragment' to get the correct
422 order, but not 'recombine' to actually benefit from it. Until we get1388 order, but not 'recombine' to actually benefit from it. Until we get
@@ -462,7 +1428,7 @@
462* ``bzr shelve`` and ``bzr unshelve`` now work on windows.1428* ``bzr shelve`` and ``bzr unshelve`` now work on windows.
463 (Robert Collins, #305006)1429 (Robert Collins, #305006)
4641430
465* Commit of specific files no longer prevents using the the iter_changes1431* Commit of specific files no longer prevents using the iter_changes
466 codepath. On 2a repositories, commit of specific files should now be as1432 codepath. On 2a repositories, commit of specific files should now be as
467 fast, or slightly faster, than a full commit. (Robert Collins)1433 fast, or slightly faster, than a full commit. (Robert Collins)
4681434
@@ -486,9 +1452,6 @@
486 classes changed to manage lock lifetime of the trees they open in a way1452 classes changed to manage lock lifetime of the trees they open in a way
487 consistent with reader-exclusive locks. (Robert Collins, #305006)1453 consistent with reader-exclusive locks. (Robert Collins, #305006)
4881454
489Internals
490*********
491
492Testing1455Testing
493*******1456*******
4941457
@@ -535,6 +1498,9 @@
535 a tree with content filtering where the size of the canonical form1498 a tree with content filtering where the size of the canonical form
536 cannot be cheaply determined. (Martin Pool)1499 cannot be cheaply determined. (Martin Pool)
5371500
1501* When manually creating transport servers in test cases, a new helper
1502 ``TestCase.start_server`` that registers a cleanup and starts the server
1503 should be used. (Robert Collins)
5381504
539bzr 1.181505bzr 1.18
540########1506########
@@ -872,6 +1838,17 @@
872 ``countTestsCases``. (Robert Collins)1838 ``countTestsCases``. (Robert Collins)
8731839
8741840
1841bzr 1.17.1 (unreleased)
1842#######################
1843
1844Bug Fixes
1845*********
1846
1847* The optional ``_knit_load_data_pyx`` C extension was never being
1848 imported. This caused significant slowdowns when reading data from
1849 knit format repositories. (Andrew Bennetts, #405653)
1850
1851
875bzr 1.171852bzr 1.17
876########1853########
877:Codename: so-late-its-brunch1854:Codename: so-late-its-brunch
@@ -1220,7 +2197,7 @@
1220************2197************
12212198
1222* A new repository format ``2a`` has been added. This is a beta release2199* A new repository format ``2a`` has been added. This is a beta release
1223 of the the brisbane-core (aka group-compress) project. This format now2200 of the brisbane-core (aka group-compress) project. This format now
1224 suitable for wider testing by advanced users willing to deal with some2201 suitable for wider testing by advanced users willing to deal with some
1225 bugs. We would appreciate test reports, either positive or negative.2202 bugs. We would appreciate test reports, either positive or negative.
1226 Format 2a is substantially smaller and faster for many operations on2203 Format 2a is substantially smaller and faster for many operations on
@@ -1372,6 +2349,9 @@
1372Testing2349Testing
1373*******2350*******
13742351
2352* ``make check`` no longer repeats the test run in ``LANG=C``.
2353 (Martin Pool, #386180)
2354
1375* The number of cores is now correctly detected on OSX. (John Szakmeister)2355* The number of cores is now correctly detected on OSX. (John Szakmeister)
13762356
1377* The number of cores is also detected on Solaris and win32. (Vincent Ladeuil)2357* The number of cores is also detected on Solaris and win32. (Vincent Ladeuil)
@@ -1946,7 +2926,7 @@
19462926
1947* Added ``bzrlib.inventory_delta`` module. This will be used for2927* Added ``bzrlib.inventory_delta`` module. This will be used for
1948 serializing and deserializing inventory deltas for more efficient2928 serializing and deserializing inventory deltas for more efficient
1949 streaming on the the network. (Robert Collins, Andrew Bennetts)2929 streaming on the network. (Robert Collins, Andrew Bennetts)
19502930
1951* ``Branch._get_config`` has been added, which splits out access to the2931* ``Branch._get_config`` has been added, which splits out access to the
1952 specific config file from the branch. This is used to let RemoteBranch2932 specific config file from the branch. This is used to let RemoteBranch
@@ -2141,7 +3121,7 @@
2141* Multiple authors for a commit can now be recorded by using the "--author"3121* Multiple authors for a commit can now be recorded by using the "--author"
2142 option multiple times. (James Westby, #185772)3122 option multiple times. (James Westby, #185772)
21433123
2144* New clean-tree command, from bzrtools. (Aaron Bentley, Jelmer Vernoij)3124* New clean-tree command, from bzrtools. (Aaron Bentley, Jelmer Vernooij)
21453125
2146* New command ``bzr launchpad-open`` opens a Launchpad web page for that3126* New command ``bzr launchpad-open`` opens a Launchpad web page for that
2147 branch in your web browser, as long as the branch is on Launchpad at all.3127 branch in your web browser, as long as the branch is on Launchpad at all.
@@ -4670,7 +5650,7 @@
4670 exception. (Andrew Bennetts)5650 exception. (Andrew Bennetts)
46715651
4672* New ``--debugflag``/``-E`` option to ``bzr selftest`` for setting5652* New ``--debugflag``/``-E`` option to ``bzr selftest`` for setting
4673 options for debugging tests, these are complementary to the the -D5653 options for debugging tests, these are complementary to the -D
4674 options. The ``-Dselftest_debug`` global option has been replaced by the5654 options. The ``-Dselftest_debug`` global option has been replaced by the
4675 ``-E=allow_debug`` option for selftest. (Andrew Bennetts)5655 ``-E=allow_debug`` option for selftest. (Andrew Bennetts)
46765656
@@ -5425,7 +6405,7 @@
5425 checkouts. (Aaron Bentley, #182040)6405 checkouts. (Aaron Bentley, #182040)
54266406
5427* Stop polluting /tmp when running selftest.6407* Stop polluting /tmp when running selftest.
5428 (Vincent Ladeuil, #123623)6408 (Vincent Ladeuil, #123363)
54296409
5430* Switch from NFKC => NFC for normalization checks. NFC allows a few6410* Switch from NFKC => NFC for normalization checks. NFC allows a few
5431 more characters which should be considered valid.6411 more characters which should be considered valid.
54326412
=== modified file 'bzr'
--- bzr 2009-12-15 16:56:18 +0000
+++ bzr 2010-02-18 00:00:51 +0000
@@ -23,7 +23,7 @@
23import warnings23import warnings
2424
25# update this on each release25# update this on each release
26_script_version = (2, 0, 4)26_script_version = (2, 1, 1)
2727
28if __doc__ is None:28if __doc__ is None:
29 print "bzr does not support python -OO."29 print "bzr does not support python -OO."
3030
=== modified file 'bzrlib/__init__.py'
--- bzrlib/__init__.py 2010-01-21 19:35:57 +0000
+++ bzrlib/__init__.py 2010-02-18 00:00:52 +0000
@@ -31,16 +31,10 @@
31 import bzrlib.lazy_regex31 import bzrlib.lazy_regex
32 bzrlib.lazy_regex.install_lazy_compile()32 bzrlib.lazy_regex.install_lazy_compile()
3333
34from bzrlib.osutils import get_user_encoding
35
3634
37IGNORE_FILENAME = ".bzrignore"35IGNORE_FILENAME = ".bzrignore"
3836
3937
40# XXX: Deprecated as of bzr-1.17 use osutils.get_user_encoding() directly
41user_encoding = get_user_encoding()
42
43
44__copyright__ = "Copyright 2005, 2006, 2007, 2008, 2009 Canonical Ltd."38__copyright__ = "Copyright 2005, 2006, 2007, 2008, 2009 Canonical Ltd."
4539
46# same format as sys.version_info: "A tuple containing the five components of40# same format as sys.version_info: "A tuple containing the five components of
@@ -50,10 +44,11 @@
50# Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a44# Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a
51# releaselevel of 'dev' for unreleased under-development code.45# releaselevel of 'dev' for unreleased under-development code.
5246
53version_info = (2, 0, 4, 'final', 0)47version_info = (2, 1, 1, 'dev', 0)
5448
55# API compatibility version: bzrlib is currently API compatible with 1.15.49# API compatibility version: bzrlib is currently API compatible with 1.15.
56api_minimum_version = (1, 17, 0)50api_minimum_version = (2, 1, 0)
51
5752
58def _format_version_tuple(version_info):53def _format_version_tuple(version_info):
59 """Turn a version number 2, 3 or 5-tuple into a short string.54 """Turn a version number 2, 3 or 5-tuple into a short string.
6055
=== modified file 'bzrlib/_annotator_pyx.pyx'
--- bzrlib/_annotator_pyx.pyx 2010-01-05 04:59:57 +0000
+++ bzrlib/_annotator_pyx.pyx 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009 Canonical Ltd1# Copyright (C) 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
55
=== modified file 'bzrlib/_bencode_pyx.pyx'
--- bzrlib/_bencode_pyx.pyx 2009-12-18 21:58:32 +0000
+++ bzrlib/_bencode_pyx.pyx 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2007,2009 Canonical Ltd1# Copyright (C) 2007, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -58,6 +58,13 @@
58 void D_UPDATE_TAIL(Decoder, int n)58 void D_UPDATE_TAIL(Decoder, int n)
59 void E_UPDATE_TAIL(Encoder, int n)59 void E_UPDATE_TAIL(Encoder, int n)
6060
61# To maintain compatibility with older versions of pyrex, we have to use the
62# relative import here, rather than 'bzrlib._static_tuple_c'
63from _static_tuple_c cimport StaticTuple, StaticTuple_CheckExact, \
64 import_static_tuple_c
65
66import_static_tuple_c()
67
6168
62cdef class Decoder:69cdef class Decoder:
63 """Bencode decoder"""70 """Bencode decoder"""
@@ -371,7 +378,8 @@
371 self._encode_int(x)378 self._encode_int(x)
372 elif PyLong_CheckExact(x):379 elif PyLong_CheckExact(x):
373 self._encode_long(x)380 self._encode_long(x)
374 elif PyList_CheckExact(x) or PyTuple_CheckExact(x):381 elif (PyList_CheckExact(x) or PyTuple_CheckExact(x)
382 or StaticTuple_CheckExact(x)):
375 self._encode_list(x)383 self._encode_list(x)
376 elif PyDict_CheckExact(x):384 elif PyDict_CheckExact(x):
377 self._encode_dict(x)385 self._encode_dict(x)
378386
=== modified file 'bzrlib/_btree_serializer_py.py'
--- bzrlib/_btree_serializer_py.py 2009-03-23 14:59:43 +0000
+++ bzrlib/_btree_serializer_py.py 2010-02-18 00:00:50 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2008 Canonical Ltd1# Copyright (C) 2008, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -17,27 +17,33 @@
1717
18"""B+Tree index parsing."""18"""B+Tree index parsing."""
1919
20from bzrlib import static_tuple
21
22
20def _parse_leaf_lines(bytes, key_length, ref_list_length):23def _parse_leaf_lines(bytes, key_length, ref_list_length):
21 lines = bytes.split('\n')24 lines = bytes.split('\n')
22 nodes = []25 nodes = []
26 as_st = static_tuple.StaticTuple.from_sequence
27 stuple = static_tuple.StaticTuple
23 for line in lines[1:]:28 for line in lines[1:]:
24 if line == '':29 if line == '':
25 return nodes30 return nodes
26 elements = line.split('\0', key_length)31 elements = line.split('\0', key_length)
27 # keys are tuples32 # keys are tuples
28 key = tuple(elements[:key_length])33 key = as_st(elements[:key_length]).intern()
29 line = elements[-1]34 line = elements[-1]
30 references, value = line.rsplit('\0', 1)35 references, value = line.rsplit('\0', 1)
31 if ref_list_length:36 if ref_list_length:
32 ref_lists = []37 ref_lists = []
33 for ref_string in references.split('\t'):38 for ref_string in references.split('\t'):
34 ref_lists.append(tuple([39 ref_list = as_st([as_st(ref.split('\0')).intern()
35 tuple(ref.split('\0')) for ref in ref_string.split('\r') if ref40 for ref in ref_string.split('\r') if ref])
36 ]))41 ref_lists.append(ref_list)
37 ref_lists = tuple(ref_lists)42 ref_lists = as_st(ref_lists)
38 node_value = (value, ref_lists)43 node_value = stuple(value, ref_lists)
39 else:44 else:
40 node_value = (value, ())45 node_value = stuple(value, stuple())
46 # No need for StaticTuple here as it is put into a dict
41 nodes.append((key, node_value))47 nodes.append((key, node_value))
42 return nodes48 return nodes
4349
4450
=== modified file 'bzrlib/_btree_serializer_pyx.pyx'
--- bzrlib/_btree_serializer_pyx.pyx 2010-01-05 04:59:57 +0000
+++ bzrlib/_btree_serializer_pyx.pyx 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2008, 2009 Canonical Ltd1# Copyright (C) 2008, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -38,11 +38,16 @@
38 Py_ssize_t PyString_Size(object p)38 Py_ssize_t PyString_Size(object p)
39 Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *)39 Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *)
40 char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *)40 char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *)
41 char * PyString_AS_STRING(object)
42 Py_ssize_t PyString_GET_SIZE(object)
41 int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len)43 int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len)
42 void PyString_InternInPlace(PyObject **)44 void PyString_InternInPlace(PyObject **)
43 int PyTuple_CheckExact(object t)45 int PyTuple_CheckExact(object t)
46 object PyTuple_New(Py_ssize_t n_entries)
47 void PyTuple_SET_ITEM(object, Py_ssize_t offset, object) # steals the ref
44 Py_ssize_t PyTuple_GET_SIZE(object t)48 Py_ssize_t PyTuple_GET_SIZE(object t)
45 PyObject *PyTuple_GET_ITEM_ptr_object "PyTuple_GET_ITEM" (object tpl, int index)49 PyObject *PyTuple_GET_ITEM_ptr_object "PyTuple_GET_ITEM" (object tpl, int index)
50 void Py_INCREF(object)
46 void Py_DECREF_ptr "Py_DECREF" (PyObject *)51 void Py_DECREF_ptr "Py_DECREF" (PyObject *)
4752
48cdef extern from "string.h":53cdef extern from "string.h":
@@ -52,6 +57,12 @@
52 # void *memrchr(void *s, int c, size_t n)57 # void *memrchr(void *s, int c, size_t n)
53 int strncmp(char *s1, char *s2, size_t n)58 int strncmp(char *s1, char *s2, size_t n)
5459
60# It seems we need to import the definitions so that the pyrex compiler has
61# local names to access them.
62from _static_tuple_c cimport StaticTuple, \
63 import_static_tuple_c, StaticTuple_New, \
64 StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact
65
5566
56# TODO: Find some way to import this from _dirstate_helpers67# TODO: Find some way to import this from _dirstate_helpers
57cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise68cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
@@ -68,6 +79,7 @@
68 pos = pos - 179 pos = pos - 1
69 return NULL80 return NULL
7081
82
71# TODO: Import this from _dirstate_helpers when it is merged83# TODO: Import this from _dirstate_helpers when it is merged
72cdef object safe_string_from_size(char *s, Py_ssize_t size):84cdef object safe_string_from_size(char *s, Py_ssize_t size):
73 if size < 0:85 if size < 0:
@@ -91,6 +103,10 @@
91 Py_DECREF_ptr(py_str)103 Py_DECREF_ptr(py_str)
92 return result104 return result
93105
106from bzrlib import _static_tuple_c
107# This sets up the StaticTuple C_API functionality
108import_static_tuple_c()
109
94110
95cdef class BTreeLeafParser:111cdef class BTreeLeafParser:
96 """Parse the leaf nodes of a BTree index.112 """Parse the leaf nodes of a BTree index.
@@ -130,6 +146,7 @@
130 self._cur_str = NULL146 self._cur_str = NULL
131 self._end_str = NULL147 self._end_str = NULL
132 self._header_found = 0148 self._header_found = 0
149 # keys are tuples
133150
134 cdef extract_key(self, char * last):151 cdef extract_key(self, char * last):
135 """Extract a key.152 """Extract a key.
@@ -139,15 +156,14 @@
139 """156 """
140 cdef char *temp_ptr157 cdef char *temp_ptr
141 cdef int loop_counter158 cdef int loop_counter
142 # keys are tuples159 cdef StaticTuple key
143 loop_counter = 0160
144 key_segments = []161 key = StaticTuple_New(self.key_length)
145 while loop_counter < self.key_length:162 for loop_counter from 0 <= loop_counter < self.key_length:
146 loop_counter = loop_counter + 1
147 # grab a key segment163 # grab a key segment
148 temp_ptr = <char*>memchr(self._start, c'\0', last - self._start)164 temp_ptr = <char*>memchr(self._start, c'\0', last - self._start)
149 if temp_ptr == NULL:165 if temp_ptr == NULL:
150 if loop_counter == self.key_length:166 if loop_counter + 1 == self.key_length:
151 # capture to last167 # capture to last
152 temp_ptr = last168 temp_ptr = last
153 else:169 else:
@@ -157,15 +173,20 @@
157 last - self._start)))173 last - self._start)))
158 raise AssertionError(failure_string)174 raise AssertionError(failure_string)
159 # capture the key string175 # capture the key string
160 # TODO: Consider using PyIntern_FromString, the only caveat is that176 if (self.key_length == 1
161 # it assumes a NULL-terminated string, so we have to check if177 and (temp_ptr - self._start) == 45
162 # temp_ptr[0] == c'\0' or some other char.178 and strncmp(self._start, 'sha1:', 5) == 0):
163 key_element = safe_interned_string_from_size(self._start,179 key_element = safe_string_from_size(self._start,
180 temp_ptr - self._start)
181 else:
182 key_element = safe_interned_string_from_size(self._start,
164 temp_ptr - self._start)183 temp_ptr - self._start)
165 # advance our pointer184 # advance our pointer
166 self._start = temp_ptr + 1185 self._start = temp_ptr + 1
167 PyList_Append(key_segments, key_element)186 Py_INCREF(key_element)
168 return tuple(key_segments)187 StaticTuple_SET_ITEM(key, loop_counter, key_element)
188 key = StaticTuple_Intern(key)
189 return key
169190
170 cdef int process_line(self) except -1:191 cdef int process_line(self) except -1:
171 """Process a line in the bytes."""192 """Process a line in the bytes."""
@@ -174,6 +195,7 @@
174 cdef char *ref_ptr195 cdef char *ref_ptr
175 cdef char *next_start196 cdef char *next_start
176 cdef int loop_counter197 cdef int loop_counter
198 cdef Py_ssize_t str_len
177199
178 self._start = self._cur_str200 self._start = self._cur_str
179 # Find the next newline201 # Find the next newline
@@ -209,12 +231,25 @@
209 # Invalid line231 # Invalid line
210 raise AssertionError("Failed to find the value area")232 raise AssertionError("Failed to find the value area")
211 else:233 else:
212 # capture the value string234 # Because of how conversions were done, we ended up with *lots* of
213 value = safe_string_from_size(temp_ptr + 1, last - temp_ptr - 1)235 # values that are identical. These are all of the 0-length nodes
236 # that are referred to by the TREE_ROOT (and likely some other
237 # directory nodes.) For example, bzr has 25k references to
238 # something like '12607215 328306 0 0', which ends up consuming 1MB
239 # of memory, just for those strings.
240 str_len = last - temp_ptr - 1
241 if (str_len > 4
242 and strncmp(" 0 0", last - 4, 4) == 0):
243 # This drops peak mem for bzr.dev from 87.4MB => 86.2MB
244 # For Launchpad 236MB => 232MB
245 value = safe_interned_string_from_size(temp_ptr + 1, str_len)
246 else:
247 value = safe_string_from_size(temp_ptr + 1, str_len)
214 # shrink the references end point248 # shrink the references end point
215 last = temp_ptr249 last = temp_ptr
250
216 if self.ref_list_length:251 if self.ref_list_length:
217 ref_lists = []252 ref_lists = StaticTuple_New(self.ref_list_length)
218 loop_counter = 0253 loop_counter = 0
219 while loop_counter < self.ref_list_length:254 while loop_counter < self.ref_list_length:
220 ref_list = []255 ref_list = []
@@ -246,18 +281,20 @@
246 if temp_ptr == NULL:281 if temp_ptr == NULL:
247 # key runs to the end282 # key runs to the end
248 temp_ptr = ref_ptr283 temp_ptr = ref_ptr
284
249 PyList_Append(ref_list, self.extract_key(temp_ptr))285 PyList_Append(ref_list, self.extract_key(temp_ptr))
250 PyList_Append(ref_lists, tuple(ref_list))286 ref_list = StaticTuple_Intern(StaticTuple(*ref_list))
287 Py_INCREF(ref_list)
288 StaticTuple_SET_ITEM(ref_lists, loop_counter - 1, ref_list)
251 # prepare for the next reference list289 # prepare for the next reference list
252 self._start = next_start290 self._start = next_start
253 ref_lists = tuple(ref_lists)291 node_value = StaticTuple(value, ref_lists)
254 node_value = (value, ref_lists)
255 else:292 else:
256 if last != self._start:293 if last != self._start:
257 # unexpected reference data present294 # unexpected reference data present
258 raise AssertionError("unexpected reference data present")295 raise AssertionError("unexpected reference data present")
259 node_value = (value, ())296 node_value = StaticTuple(value, StaticTuple())
260 PyList_Append(self.keys, (key, node_value))297 PyList_Append(self.keys, StaticTuple(key, node_value))
261 return 0298 return 0
262299
263 def parse(self):300 def parse(self):
@@ -292,7 +329,6 @@
292 cdef Py_ssize_t flat_len329 cdef Py_ssize_t flat_len
293 cdef Py_ssize_t key_len330 cdef Py_ssize_t key_len
294 cdef Py_ssize_t node_len331 cdef Py_ssize_t node_len
295 cdef PyObject * val
296 cdef char * value332 cdef char * value
297 cdef Py_ssize_t value_len333 cdef Py_ssize_t value_len
298 cdef char * out334 cdef char * out
@@ -301,13 +337,12 @@
301 cdef int first_ref_list337 cdef int first_ref_list
302 cdef int first_reference338 cdef int first_reference
303 cdef int i339 cdef int i
304 cdef PyObject *ref_bit
305 cdef Py_ssize_t ref_bit_len340 cdef Py_ssize_t ref_bit_len
306341
307 if not PyTuple_CheckExact(node):342 if not PyTuple_CheckExact(node) and not StaticTuple_CheckExact(node):
308 raise TypeError('We expected a tuple() for node not: %s'343 raise TypeError('We expected a tuple() or StaticTuple() for node not: %s'
309 % type(node))344 % type(node))
310 node_len = PyTuple_GET_SIZE(node)345 node_len = len(node)
311 have_reference_lists = reference_lists346 have_reference_lists = reference_lists
312 if have_reference_lists:347 if have_reference_lists:
313 if node_len != 4:348 if node_len != 4:
@@ -316,8 +351,17 @@
316 elif node_len < 3:351 elif node_len < 3:
317 raise ValueError('Without ref_lists, we need at least 3 entries not: %s'352 raise ValueError('Without ref_lists, we need at least 3 entries not: %s'
318 % len(node))353 % len(node))
319 # I don't expect that we can do faster than string.join()354 # TODO: We can probably do better than string.join(), namely
320 string_key = '\0'.join(<object>PyTuple_GET_ITEM_ptr_object(node, 1))355 # when key has only 1 item, we can just grab that string
356 # And when there are 2 items, we could do a single malloc + len() + 1
357 # also, doing .join() requires a PyObject_GetAttrString call, which
358 # we could also avoid.
359 # TODO: Note that pyrex 0.9.6 generates fairly crummy code here, using the
360 # python object interface, versus 0.9.8+ which uses a helper that
361 # checks if this supports the sequence interface.
362 # We *could* do more work on our own, and grab the actual items
363 # lists. For now, just ask people to use a better compiler. :)
364 string_key = '\0'.join(node[1])
321365
322 # TODO: instead of using string joins, precompute the final string length,366 # TODO: instead of using string joins, precompute the final string length,
323 # and then malloc a single string and copy everything in.367 # and then malloc a single string and copy everything in.
@@ -334,7 +378,7 @@
334 refs_len = 0378 refs_len = 0
335 if have_reference_lists:379 if have_reference_lists:
336 # Figure out how many bytes it will take to store the references380 # Figure out how many bytes it will take to store the references
337 ref_lists = <object>PyTuple_GET_ITEM_ptr_object(node, 3)381 ref_lists = node[3]
338 next_len = len(ref_lists) # TODO: use a Py function382 next_len = len(ref_lists) # TODO: use a Py function
339 if next_len > 0:383 if next_len > 0:
340 # If there are no nodes, we don't need to do any work384 # If there are no nodes, we don't need to do any work
@@ -348,31 +392,31 @@
348 # references392 # references
349 refs_len = refs_len + (next_len - 1)393 refs_len = refs_len + (next_len - 1)
350 for reference in ref_list:394 for reference in ref_list:
351 if not PyTuple_CheckExact(reference):395 if (not PyTuple_CheckExact(reference)
396 and not StaticTuple_CheckExact(reference)):
352 raise TypeError(397 raise TypeError(
353 'We expect references to be tuples not: %s'398 'We expect references to be tuples not: %s'
354 % type(reference))399 % type(reference))
355 next_len = PyTuple_GET_SIZE(reference)400 next_len = len(reference)
356 if next_len > 0:401 if next_len > 0:
357 # We will need (len - 1) '\x00' characters to402 # We will need (len - 1) '\x00' characters to
358 # separate the reference key403 # separate the reference key
359 refs_len = refs_len + (next_len - 1)404 refs_len = refs_len + (next_len - 1)
360 for i from 0 <= i < next_len:405 for ref_bit in reference:
361 ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i)406 if not PyString_CheckExact(ref_bit):
362 if not PyString_CheckExact_ptr(ref_bit):
363 raise TypeError('We expect reference bits'407 raise TypeError('We expect reference bits'
364 ' to be strings not: %s'408 ' to be strings not: %s'
365 % type(<object>ref_bit))409 % type(<object>ref_bit))
366 refs_len = refs_len + PyString_GET_SIZE_ptr(ref_bit)410 refs_len = refs_len + PyString_GET_SIZE(ref_bit)
367411
368 # So we have the (key NULL refs NULL value LF)412 # So we have the (key NULL refs NULL value LF)
369 key_len = PyString_Size(string_key)413 key_len = PyString_Size(string_key)
370 val = PyTuple_GET_ITEM_ptr_object(node, 2)414 val = node[2]
371 if not PyString_CheckExact_ptr(val):415 if not PyString_CheckExact(val):
372 raise TypeError('Expected a plain str for value not: %s'416 raise TypeError('Expected a plain str for value not: %s'
373 % type(<object>val))417 % type(val))
374 value = PyString_AS_STRING_ptr(val)418 value = PyString_AS_STRING(val)
375 value_len = PyString_GET_SIZE_ptr(val)419 value_len = PyString_GET_SIZE(val)
376 flat_len = (key_len + 1 + refs_len + 1 + value_len + 1)420 flat_len = (key_len + 1 + refs_len + 1 + value_len + 1)
377 line = PyString_FromStringAndSize(NULL, flat_len)421 line = PyString_FromStringAndSize(NULL, flat_len)
378 # Get a pointer to the new buffer422 # Get a pointer to the new buffer
@@ -394,14 +438,14 @@
394 out[0] = c'\r'438 out[0] = c'\r'
395 out = out + 1439 out = out + 1
396 first_reference = 0440 first_reference = 0
397 next_len = PyTuple_GET_SIZE(reference)441 next_len = len(reference)
398 for i from 0 <= i < next_len:442 for i from 0 <= i < next_len:
399 if i != 0:443 if i != 0:
400 out[0] = c'\x00'444 out[0] = c'\x00'
401 out = out + 1445 out = out + 1
402 ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i)446 ref_bit = reference[i]
403 ref_bit_len = PyString_GET_SIZE_ptr(ref_bit)447 ref_bit_len = PyString_GET_SIZE(ref_bit)
404 memcpy(out, PyString_AS_STRING_ptr(ref_bit), ref_bit_len)448 memcpy(out, PyString_AS_STRING(ref_bit), ref_bit_len)
405 out = out + ref_bit_len449 out = out + ref_bit_len
406 out[0] = c'\0'450 out[0] = c'\0'
407 out = out + 1451 out = out + 1
408452
=== modified file 'bzrlib/_chk_map_py.py'
--- bzrlib/_chk_map_py.py 2009-04-09 20:23:07 +0000
+++ bzrlib/_chk_map_py.py 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009 Canonical Ltd1# Copyright (C) 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,8 @@
19import zlib19import zlib
20import struct20import struct
2121
22from bzrlib.static_tuple import StaticTuple
23
22_LeafNode = None24_LeafNode = None
23_InternalNode = None25_InternalNode = None
24_unknown = None26_unknown = None
@@ -93,7 +95,7 @@
93 value_lines = lines[pos:pos+num_value_lines]95 value_lines = lines[pos:pos+num_value_lines]
94 pos += num_value_lines96 pos += num_value_lines
95 value = '\n'.join(value_lines)97 value = '\n'.join(value_lines)
96 items[tuple(elements[:-1])] = value98 items[StaticTuple.from_sequence(elements[:-1])] = value
97 if len(items) != length:99 if len(items) != length:
98 raise AssertionError("item count (%d) mismatch for key %s,"100 raise AssertionError("item count (%d) mismatch for key %s,"
99 " bytes %r" % (length, key, bytes))101 " bytes %r" % (length, key, bytes))
@@ -141,7 +143,7 @@
141 for line in lines[5:]:143 for line in lines[5:]:
142 line = common_prefix + line144 line = common_prefix + line
143 prefix, flat_key = line.rsplit('\x00', 1)145 prefix, flat_key = line.rsplit('\x00', 1)
144 items[prefix] = (flat_key,)146 items[prefix] = StaticTuple(flat_key,)
145 if len(items) == 0:147 if len(items) == 0:
146 raise AssertionError("We didn't find any item for %s" % key)148 raise AssertionError("We didn't find any item for %s" % key)
147 result._items = items149 result._items = items
@@ -155,4 +157,3 @@
155 result._node_width = len(prefix)157 result._node_width = len(prefix)
156 result._search_prefix = common_prefix158 result._search_prefix = common_prefix
157 return result159 return result
158
159160
=== modified file 'bzrlib/_chk_map_pyx.pyx'
--- bzrlib/_chk_map_pyx.pyx 2010-01-05 04:59:57 +0000
+++ bzrlib/_chk_map_pyx.pyx 2010-02-18 00:00:51 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009 Canonical Ltd1# Copyright (C) 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -29,9 +29,8 @@
2929
30cdef extern from "Python.h":30cdef extern from "Python.h":
31 ctypedef int Py_ssize_t # Required for older pyrex versions31 ctypedef int Py_ssize_t # Required for older pyrex versions
32 struct _PyObject:32 ctypedef struct PyObject:
33 pass33 pass
34 ctypedef _PyObject PyObject
35 int PyTuple_CheckExact(object p)34 int PyTuple_CheckExact(object p)
36 Py_ssize_t PyTuple_GET_SIZE(object t)35 Py_ssize_t PyTuple_GET_SIZE(object t)
37 int PyString_CheckExact(object)36 int PyString_CheckExact(object)
@@ -52,6 +51,18 @@
52 char *PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *s)51 char *PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *s)
53 object PyString_FromStringAndSize(char*, Py_ssize_t)52 object PyString_FromStringAndSize(char*, Py_ssize_t)
5453
54# cimport all of the definitions we will need to access
55from _static_tuple_c cimport StaticTuple,\
56 import_static_tuple_c, StaticTuple_New, \
57 StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact
58
59cdef extern from "_static_tuple_c.h":
60 # Defined explicitly rather than cimport-ing. Trying to use cimport, the
61 # type for PyObject is a different class that happens to have the same
62 # name...
63 PyObject * StaticTuple_GET_ITEM_ptr "StaticTuple_GET_ITEM" (StaticTuple,
64 Py_ssize_t)
65
55cdef extern from "zlib.h":66cdef extern from "zlib.h":
56 ctypedef unsigned long uLong67 ctypedef unsigned long uLong
57 ctypedef unsigned int uInt68 ctypedef unsigned int uInt
@@ -60,8 +71,14 @@
60 uLong crc32(uLong crc, Bytef *buf, uInt len)71 uLong crc32(uLong crc, Bytef *buf, uInt len)
6172
6273
74# Set up the StaticTuple C_API functionality
75import_static_tuple_c()
76
77cdef object _LeafNode
63_LeafNode = None78_LeafNode = None
79cdef object _InternalNode
64_InternalNode = None80_InternalNode = None
81cdef object _unknown
65_unknown = None82_unknown = None
6683
67# We shouldn't just copy this from _dirstate_helpers_pyx84# We shouldn't just copy this from _dirstate_helpers_pyx
@@ -91,9 +108,9 @@
91 cdef char *c_out108 cdef char *c_out
92 cdef PyObject *bit109 cdef PyObject *bit
93110
94 if not PyTuple_CheckExact(key):111 if not StaticTuple_CheckExact(key):
95 raise TypeError('key %r is not a tuple' % (key,))112 raise TypeError('key %r is not a StaticTuple' % (key,))
96 num_bits = PyTuple_GET_SIZE(key)113 num_bits = len(key)
97 # 4 bytes per crc32, and another 1 byte between bits114 # 4 bytes per crc32, and another 1 byte between bits
98 num_out_bytes = (9 * num_bits) - 1115 num_out_bytes = (9 * num_bits) - 1
99 out = PyString_FromStringAndSize(NULL, num_out_bytes)116 out = PyString_FromStringAndSize(NULL, num_out_bytes)
@@ -105,7 +122,7 @@
105 # We use the _ptr variant, because GET_ITEM returns a borrowed122 # We use the _ptr variant, because GET_ITEM returns a borrowed
106 # reference, and Pyrex assumes that returned 'object' are a new123 # reference, and Pyrex assumes that returned 'object' are a new
107 # reference124 # reference
108 bit = PyTuple_GET_ITEM_ptr(key, i)125 bit = StaticTuple_GET_ITEM_ptr(key, i)
109 if not PyString_CheckExact_ptr(bit):126 if not PyString_CheckExact_ptr(bit):
110 raise TypeError('Bit %d of %r is not a string' % (i, key))127 raise TypeError('Bit %d of %r is not a string' % (i, key))
111 c_bit = <Bytef *>PyString_AS_STRING_ptr(bit)128 c_bit = <Bytef *>PyString_AS_STRING_ptr(bit)
@@ -129,9 +146,9 @@
129 cdef char *c_out146 cdef char *c_out
130 cdef PyObject *bit147 cdef PyObject *bit
131148
132 if not PyTuple_CheckExact(key):149 if not StaticTuple_CheckExact(key):
133 raise TypeError('key %r is not a tuple' % (key,))150 raise TypeError('key %r is not a StaticTuple' % (key,))
134 num_bits = PyTuple_GET_SIZE(key)151 num_bits = len(key)
135 # 4 bytes per crc32, and another 1 byte between bits152 # 4 bytes per crc32, and another 1 byte between bits
136 num_out_bytes = (5 * num_bits) - 1153 num_out_bytes = (5 * num_bits) - 1
137 out = PyString_FromStringAndSize(NULL, num_out_bytes)154 out = PyString_FromStringAndSize(NULL, num_out_bytes)
@@ -140,10 +157,10 @@
140 if i > 0:157 if i > 0:
141 c_out[0] = c'\x00'158 c_out[0] = c'\x00'
142 c_out = c_out + 1159 c_out = c_out + 1
143 bit = PyTuple_GET_ITEM_ptr(key, i)160 bit = StaticTuple_GET_ITEM_ptr(key, i)
144 if not PyString_CheckExact_ptr(bit):161 if not PyString_CheckExact_ptr(bit):
145 raise TypeError('Bit %d of %r is not a string: %r' % (i, key,162 raise TypeError('Bit %d of %r is not a string: %r'
146 <object>bit))163 % (i, key, <object>bit))
147 c_bit = <Bytef *>PyString_AS_STRING_ptr(bit)164 c_bit = <Bytef *>PyString_AS_STRING_ptr(bit)
148 c_len = PyString_GET_SIZE_ptr(bit)165 c_len = PyString_GET_SIZE_ptr(bit)
149 crc_val = crc32(0, c_bit, c_len)166 crc_val = crc32(0, c_bit, c_len)
@@ -195,6 +212,7 @@
195 cdef char *prefix, *value_start, *prefix_tail212 cdef char *prefix, *value_start, *prefix_tail
196 cdef char *next_null, *last_null, *line_start213 cdef char *next_null, *last_null, *line_start
197 cdef char *c_entry, *entry_start214 cdef char *c_entry, *entry_start
215 cdef StaticTuple entry_bits
198216
199 if _LeafNode is None:217 if _LeafNode is None:
200 from bzrlib import chk_map218 from bzrlib import chk_map
@@ -265,12 +283,14 @@
265 if next_line == NULL:283 if next_line == NULL:
266 raise ValueError('missing trailing newline')284 raise ValueError('missing trailing newline')
267 cur = next_line + 1285 cur = next_line + 1
268 entry_bits = PyTuple_New(width)286 entry_bits = StaticTuple_New(width)
269 for i from 0 <= i < num_prefix_bits:287 for i from 0 <= i < num_prefix_bits:
288 # TODO: Use PyList_GetItem, or turn prefix_bits into a
289 # tuple/StaticTuple
270 entry = prefix_bits[i]290 entry = prefix_bits[i]
271 # SET_ITEM 'steals' a reference291 # SET_ITEM 'steals' a reference
272 Py_INCREF(entry)292 Py_INCREF(entry)
273 PyTuple_SET_ITEM(entry_bits, i, entry)293 StaticTuple_SET_ITEM(entry_bits, i, entry)
274 value = PyString_FromStringAndSize(value_start, next_line - value_start)294 value = PyString_FromStringAndSize(value_start, next_line - value_start)
275 # The next entry bit needs the 'tail' from the prefix, and first part295 # The next entry bit needs the 'tail' from the prefix, and first part
276 # of the line296 # of the line
@@ -288,7 +308,7 @@
288 memcpy(c_entry + prefix_tail_len, line_start, next_null - line_start)308 memcpy(c_entry + prefix_tail_len, line_start, next_null - line_start)
289 Py_INCREF(entry)309 Py_INCREF(entry)
290 i = num_prefix_bits310 i = num_prefix_bits
291 PyTuple_SET_ITEM(entry_bits, i, entry)311 StaticTuple_SET_ITEM(entry_bits, i, entry)
292 while next_null != last_null: # We have remaining bits312 while next_null != last_null: # We have remaining bits
293 i = i + 1313 i = i + 1
294 if i > width:314 if i > width:
@@ -301,11 +321,12 @@
301 entry = PyString_FromStringAndSize(entry_start,321 entry = PyString_FromStringAndSize(entry_start,
302 next_null - entry_start)322 next_null - entry_start)
303 Py_INCREF(entry)323 Py_INCREF(entry)
304 PyTuple_SET_ITEM(entry_bits, i, entry)324 StaticTuple_SET_ITEM(entry_bits, i, entry)
305 if len(entry_bits) != width:325 if len(entry_bits) != width:
306 raise AssertionError(326 raise AssertionError(
307 'Incorrect number of elements (%d vs %d)'327 'Incorrect number of elements (%d vs %d)'
308 % (len(entry_bits)+1, width + 1))328 % (len(entry_bits)+1, width + 1))
329 entry_bits = StaticTuple_Intern(entry_bits)
309 PyDict_SetItem(items, entry_bits, value)330 PyDict_SetItem(items, entry_bits, value)
310 if len(items) != length:331 if len(items) != length:
311 raise ValueError("item count (%d) mismatch for key %s,"332 raise ValueError("item count (%d) mismatch for key %s,"
@@ -343,6 +364,8 @@
343 _unknown = chk_map._unknown364 _unknown = chk_map._unknown
344 result = _InternalNode(search_key_func=search_key_func)365 result = _InternalNode(search_key_func=search_key_func)
345366
367 if not StaticTuple_CheckExact(key):
368 raise TypeError('key %r is not a StaticTuple' % (key,))
346 if not PyString_CheckExact(bytes):369 if not PyString_CheckExact(bytes):
347 raise TypeError('bytes must be a plain string not %s' % (type(bytes),))370 raise TypeError('bytes must be a plain string not %s' % (type(bytes),))
348371
@@ -384,7 +407,8 @@
384 memcpy(c_item_prefix + prefix_length, cur, next_null - cur)407 memcpy(c_item_prefix + prefix_length, cur, next_null - cur)
385 flat_key = PyString_FromStringAndSize(next_null + 1,408 flat_key = PyString_FromStringAndSize(next_null + 1,
386 next_line - next_null - 1)409 next_line - next_null - 1)
387 PyDict_SetItem(items, item_prefix, (flat_key,))410 flat_key = StaticTuple(flat_key).intern()
411 PyDict_SetItem(items, item_prefix, flat_key)
388 cur = next_line + 1412 cur = next_line + 1
389 assert len(items) > 0413 assert len(items) > 0
390 result._items = items414 result._items = items
@@ -398,4 +422,3 @@
398 result._node_width = len(item_prefix)422 result._node_width = len(item_prefix)
399 result._search_prefix = PyString_FromStringAndSize(prefix, prefix_length)423 result._search_prefix = PyString_FromStringAndSize(prefix, prefix_length)
400 return result424 return result
401
402425
=== modified file 'bzrlib/_dirstate_helpers_pyx.pyx'
--- bzrlib/_dirstate_helpers_pyx.pyx 2010-01-05 04:59:57 +0000
+++ bzrlib/_dirstate_helpers_pyx.pyx 2010-02-18 00:00:51 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2007, 2008 Canonical Ltd1# Copyright (C) 2007-2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
55
=== added file 'bzrlib/_export_c_api.h'
--- bzrlib/_export_c_api.h 1970-01-01 00:00:00 +0000
+++ bzrlib/_export_c_api.h 2010-02-18 00:00:51 +0000
@@ -0,0 +1,104 @@
1/* Copyright (C) 2009 Canonical Ltd
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18
19/* This file contains helper functions for exporting a C API for a CPython
20 * extension module.
21 */
22
23#ifndef _EXPORT_C_API_H_
24#define _EXPORT_C_API_H_
25
26static const char *_C_API_NAME = "_C_API";
27
28/**
29 * Add a C function to the modules _C_API
30 * This wraps the function in a PyCObject, and inserts that into a dict.
31 * The key of the dict is the function name, and the description is the
32 * signature of the function.
33 * This is generally called during a modules init_MODULE function.
34 *
35 * @param module A Python module (the one being initialized)
36 * @param funcname The name of the function being exported
37 * @param func A pointer to the function
38 * @param signature The C signature of the function
39 * @return 0 if everything is successful, -1 if there is a problem. An
40 * exception should also be set
41 */
42static int
43_export_function(PyObject *module, char *funcname, void *func, char *signature)
44{
45 PyObject *d = NULL;
46 PyObject *c_obj = NULL;
47
48 /* (char *) is because python2.4 declares this api as 'char *' rather than
49 * const char* which it really is.
50 */
51 d = PyObject_GetAttrString(module, (char *)_C_API_NAME);
52 if (!d) {
53 PyErr_Clear();
54 d = PyDict_New();
55 if (!d)
56 goto bad;
57 Py_INCREF(d);
58 if (PyModule_AddObject(module, (char *)_C_API_NAME, d) < 0)
59 goto bad;
60 }
61 c_obj = PyCObject_FromVoidPtrAndDesc(func, signature, 0);
62 if (!c_obj)
63 goto bad;
64 if (PyDict_SetItemString(d, funcname, c_obj) < 0)
65 goto bad;
66 Py_DECREF(d);
67 return 0;
68bad:
69 Py_XDECREF(c_obj);
70 Py_XDECREF(d);
71 return -1;
72}
73
74/* Note:
75 * It feels like more could be done here. Specifically, if you look at
76 * _static_tuple_c.h you can see some boilerplate where we have:
77 * #ifdef STATIC_TUPLE_MODULE // are we exporting or importing
78 * static RETVAL FUNCNAME PROTO;
79 * #else
80 * static RETVAL (*FUNCNAME) PROTO;
81 * #endif
82 *
83 * And then in _static_tuple_c.c we have
84 * int setup_c_api()
85 * {
86 * _export_function(module, #FUNCNAME, FUNCNAME, #PROTO);
87 * }
88 *
89 * And then in _static_tuple_c.h import_##MODULE
90 * struct function_definition functions[] = {
91 * {#FUNCNAME, (void **)&FUNCNAME, #RETVAL #PROTO},
92 * ...
93 * {NULL}};
94 *
95 * And some similar stuff for types. However, this would mean that we would
96 * need a way for the C preprocessor to build up a list of definitions to be
97 * generated, and then expand that list at the appropriate time.
98 * I would guess there would be a way to do this, but probably not without a
99 * lot of magic, and the end result probably wouldn't be very pretty to
100 * maintain. Perhaps python's dynamic nature has left me jaded about writing
101 * boilerplate....
102 */
103
104#endif // _EXPORT_C_API_H_
0105
=== modified file 'bzrlib/_groupcompress_pyx.pyx'
--- bzrlib/_groupcompress_pyx.pyx 2010-01-05 04:59:57 +0000
+++ bzrlib/_groupcompress_pyx.pyx 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009 Canonical Ltd1# Copyright (C) 2008, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -31,10 +31,10 @@
3131
32cdef extern from *:32cdef extern from *:
33 ctypedef unsigned long size_t33 ctypedef unsigned long size_t
34 void * malloc(size_t)34 void * malloc(size_t) nogil
35 void * realloc(void *, size_t)35 void * realloc(void *, size_t) nogil
36 void free(void *)36 void free(void *) nogil
37 void memcpy(void *, void *, size_t)37 void memcpy(void *, void *, size_t) nogil
3838
3939
40cdef extern from "delta.h":40cdef extern from "delta.h":
@@ -44,19 +44,16 @@
44 unsigned long agg_offset44 unsigned long agg_offset
45 struct delta_index:45 struct delta_index:
46 pass46 pass
47 delta_index * create_delta_index(source_info *src, delta_index *old)47 delta_index * create_delta_index(source_info *src, delta_index *old) nogil
48 delta_index * create_delta_index_from_delta(source_info *delta,48 delta_index * create_delta_index_from_delta(source_info *delta,
49 delta_index *old)49 delta_index *old) nogil
50 void free_delta_index(delta_index *index)50 void free_delta_index(delta_index *index) nogil
51 void *create_delta(delta_index *indexes,51 void *create_delta(delta_index *indexes,
52 void *buf, unsigned long bufsize,52 void *buf, unsigned long bufsize,
53 unsigned long *delta_size, unsigned long max_delta_size)53 unsigned long *delta_size, unsigned long max_delta_size) nogil
54 unsigned long get_delta_hdr_size(unsigned char **datap,54 unsigned long get_delta_hdr_size(unsigned char **datap,
55 unsigned char *top)55 unsigned char *top) nogil
56 Py_ssize_t DELTA_SIZE_MIN56 Py_ssize_t DELTA_SIZE_MIN
57 void *patch_delta(void *src_buf, unsigned long src_size,
58 void *delta_buf, unsigned long delta_size,
59 unsigned long *dst_size)
6057
6158
62cdef void *safe_malloc(size_t count) except NULL:59cdef void *safe_malloc(size_t count) except NULL:
@@ -148,7 +145,8 @@
148 src.buf = c_delta145 src.buf = c_delta
149 src.size = c_delta_size146 src.size = c_delta_size
150 src.agg_offset = self._source_offset + unadded_bytes147 src.agg_offset = self._source_offset + unadded_bytes
151 index = create_delta_index_from_delta(src, self._index)148 with nogil:
149 index = create_delta_index_from_delta(src, self._index)
152 self._source_offset = src.agg_offset + src.size150 self._source_offset = src.agg_offset + src.size
153 if index != NULL:151 if index != NULL:
154 free_delta_index(self._index)152 free_delta_index(self._index)
@@ -188,7 +186,8 @@
188 self._source_offset = src.agg_offset + src.size186 self._source_offset = src.agg_offset + src.size
189 # We delay creating the index on the first insert187 # We delay creating the index on the first insert
190 if source_location != 0:188 if source_location != 0:
191 index = create_delta_index(src, self._index)189 with nogil:
190 index = create_delta_index(src, self._index)
192 if index != NULL:191 if index != NULL:
193 free_delta_index(self._index)192 free_delta_index(self._index)
194 self._index = index193 self._index = index
@@ -201,7 +200,8 @@
201200
202 # We know that self._index is already NULL, so whatever201 # We know that self._index is already NULL, so whatever
203 # create_delta_index returns is fine202 # create_delta_index returns is fine
204 self._index = create_delta_index(&self._source_infos[0], NULL)203 with nogil:
204 self._index = create_delta_index(&self._source_infos[0], NULL)
205 assert self._index != NULL205 assert self._index != NULL
206206
207 cdef _expand_sources(self):207 cdef _expand_sources(self):
@@ -218,6 +218,7 @@
218 cdef Py_ssize_t target_size218 cdef Py_ssize_t target_size
219 cdef void * delta219 cdef void * delta
220 cdef unsigned long delta_size220 cdef unsigned long delta_size
221 cdef unsigned long c_max_delta_size
221222
222 if self._index == NULL:223 if self._index == NULL:
223 if len(self._sources) == 0:224 if len(self._sources) == 0:
@@ -234,9 +235,11 @@
234 # TODO: inline some of create_delta so we at least don't have to double235 # TODO: inline some of create_delta so we at least don't have to double
235 # malloc, and can instead use PyString_FromStringAndSize, to236 # malloc, and can instead use PyString_FromStringAndSize, to
236 # allocate the bytes into the final string237 # allocate the bytes into the final string
237 delta = create_delta(self._index,238 c_max_delta_size = max_delta_size
238 target, target_size,239 with nogil:
239 &delta_size, max_delta_size)240 delta = create_delta(self._index,
241 target, target_size,
242 &delta_size, c_max_delta_size)
240 result = None243 result = None
241 if delta:244 if delta:
242 result = PyString_FromStringAndSize(<char *>delta, delta_size)245 result = PyString_FromStringAndSize(<char *>delta, delta_size)
@@ -276,7 +279,8 @@
276279
277280
278cdef unsigned char *_decode_copy_instruction(unsigned char *bytes,281cdef unsigned char *_decode_copy_instruction(unsigned char *bytes,
279 unsigned char cmd, unsigned int *offset, unsigned int *length): # cannot_raise282 unsigned char cmd, unsigned int *offset,
283 unsigned int *length) nogil: # cannot_raise
280 """Decode a copy instruction from the next few bytes.284 """Decode a copy instruction from the next few bytes.
281285
282 A copy instruction is a variable number of bytes, so we will parse the286 A copy instruction is a variable number of bytes, so we will parse the
@@ -326,6 +330,7 @@
326 cdef unsigned char *dst_buf, *out, cmd330 cdef unsigned char *dst_buf, *out, cmd
327 cdef Py_ssize_t size331 cdef Py_ssize_t size
328 cdef unsigned int cp_off, cp_size332 cdef unsigned int cp_off, cp_size
333 cdef int failed
329334
330 data = <unsigned char *>delta335 data = <unsigned char *>delta
331 top = data + delta_size336 top = data + delta_size
@@ -335,37 +340,49 @@
335 result = PyString_FromStringAndSize(NULL, size)340 result = PyString_FromStringAndSize(NULL, size)
336 dst_buf = <unsigned char*>PyString_AS_STRING(result)341 dst_buf = <unsigned char*>PyString_AS_STRING(result)
337342
338 out = dst_buf343 failed = 0
339 while (data < top):344 with nogil:
340 cmd = data[0]345 out = dst_buf
341 data = data + 1346 while (data < top):
342 if (cmd & 0x80):347 cmd = data[0]
343 # Copy instruction348 data = data + 1
344 data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)349 if (cmd & 0x80):
345 if (cp_off + cp_size < cp_size or350 # Copy instruction
346 cp_off + cp_size > source_size or351 data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)
347 cp_size > size):352 if (cp_off + cp_size < cp_size or
348 raise RuntimeError('Something wrong with:'353 cp_off + cp_size > source_size or
349 ' cp_off = %s, cp_size = %s'354 cp_size > size):
350 ' source_size = %s, size = %s'355 failed = 1
351 % (cp_off, cp_size, source_size, size))356 break
352 memcpy(out, source + cp_off, cp_size)357 memcpy(out, source + cp_off, cp_size)
353 out = out + cp_size358 out = out + cp_size
354 size = size - cp_size359 size = size - cp_size
355 else:360 else:
356 # Insert instruction361 # Insert instruction
357 if cmd == 0:362 if cmd == 0:
358 # cmd == 0 is reserved for future encoding363 # cmd == 0 is reserved for future encoding
359 # extensions. In the mean time we must fail when364 # extensions. In the mean time we must fail when
360 # encountering them (might be data corruption).365 # encountering them (might be data corruption).
361 raise RuntimeError('Got delta opcode: 0, not supported')366 failed = 2
362 if (cmd > size):367 break
363 raise RuntimeError('Insert instruction longer than remaining'368 if cmd > size:
364 ' bytes: %d > %d' % (cmd, size))369 failed = 3
365 memcpy(out, data, cmd)370 break
366 out = out + cmd371 memcpy(out, data, cmd)
367 data = data + cmd372 out = out + cmd
368 size = size - cmd373 data = data + cmd
374 size = size - cmd
375 if failed:
376 if failed == 1:
377 raise ValueError('Something wrong with:'
378 ' cp_off = %s, cp_size = %s'
379 ' source_size = %s, size = %s'
380 % (cp_off, cp_size, source_size, size))
381 elif failed == 2:
382 raise ValueError('Got delta opcode: 0, not supported')
383 elif failed == 3:
384 raise ValueError('Insert instruction longer than remaining'
385 ' bytes: %d > %d' % (cmd, size))
369386
370 # sanity check387 # sanity check
371 if (data != top or size != 0):388 if (data != top or size != 0):
372389
=== added file 'bzrlib/_import_c_api.h'
--- bzrlib/_import_c_api.h 1970-01-01 00:00:00 +0000
+++ bzrlib/_import_c_api.h 2010-02-18 00:00:51 +0000
@@ -0,0 +1,189 @@
1/* Copyright (C) 2009 Canonical Ltd
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18#ifndef _IMPORT_C_API_H_
19#define _IMPORT_C_API_H_
20
21/**
22 * Helper functions to eliminate some of the boilerplate when importing a C API
23 * from a CPython extension module.
24 *
25 * For more information see _export_c_api.h
26 */
27
28static const char *_C_API_NAME = "_C_API";
29
30/**
31 * Import a function from the _C_API_NAME dict that is part of module.
32 *
33 * @param module The Python module we are importing from
34 * the attribute _C_API_NAME will be used as a dictionary
35 * containing the function pointer we are looking for.
36 * @param funcname Name of the function we want to import
37 * @param func A pointer to the function handle where we will store the
38 * function.
39 * @param signature The C signature of the function. This is validated
40 * against the signature stored in the C api, to make sure
41 * there is no versioning skew.
42 */
43static int _import_function(PyObject *module, const char *funcname,
44 void **func, const char *signature)
45{
46 PyObject *d = NULL;
47 PyObject *c_obj = NULL;
48 const char *desc = NULL;
49
50 /* (char *) because Python2.4 defines this as (char *) rather than
51 * (const char *)
52 */
53 d = PyObject_GetAttrString(module, (char *)_C_API_NAME);
54 if (!d) {
55 // PyObject_GetAttrString sets an appropriate exception
56 goto bad;
57 }
58 c_obj = PyDict_GetItemString(d, funcname);
59 if (!c_obj) {
60 // PyDict_GetItemString does not set an exception
61 PyErr_Format(PyExc_AttributeError,
62 "Module %s did not export a function named %s\n",
63 PyModule_GetName(module), funcname);
64 goto bad;
65 }
66 desc = (char *)PyCObject_GetDesc(c_obj);
67 if (!desc || strcmp(desc, signature) != 0) {
68 if (desc == NULL) {
69 desc = "<null>";
70 }
71 PyErr_Format(PyExc_TypeError,
72 "C function %s.%s has wrong signature (expected %s, got %s)",
73 PyModule_GetName(module), funcname, signature, desc);
74 goto bad;
75 }
76 *func = PyCObject_AsVoidPtr(c_obj);
77 Py_DECREF(d);
78 return 0;
79bad:
80 Py_XDECREF(d);
81 return -1;
82}
83
84
85/**
86 * Get a pointer to an exported PyTypeObject.
87 *
88 * @param module The Python module we are importing from
89 * @param class_name Attribute of the module that should reference the
90 * Type object. Note that a PyTypeObject is the python
91 * description of the type, not the raw C structure.
92 * @return A Pointer to the requested type object. On error NULL will be
93 * returned and an exception will be set.
94 */
95static PyTypeObject *
96_import_type(PyObject *module, const char *class_name)
97{
98 PyObject *type = NULL;
99
100 type = PyObject_GetAttrString(module, (char *)class_name);
101 if (!type) {
102 goto bad;
103 }
104 if (!PyType_Check(type)) {
105 PyErr_Format(PyExc_TypeError,
106 "%s.%s is not a type object",
107 PyModule_GetName(module), class_name);
108 goto bad;
109 }
110 return (PyTypeObject *)type;
111bad:
112 Py_XDECREF(type);
113 return NULL;
114}
115
116
117struct function_description
118{
119 const char *name;
120 void **pointer;
121 const char *signature;
122};
123
124struct type_description
125{
126 const char *name;
127 PyTypeObject **pointer;
128};
129
130/**
131 * Helper for importing several functions and types in a data-driven manner.
132 *
133 * @param module The name of the module we will be importing
134 * @param functions A list of function_description objects, describing the
135 * functions being imported.
136 * The list should be terminated with {NULL} to indicate
137 * there are no more functions to import.
138 * @param types A list of type_description objects describing type
139 * objects that we want to import. The list should be
140 * terminated with {NULL} to indicate there are no more
141 * types to import.
142 * @return 0 on success, -1 on error and an exception should be set.
143 */
144
145static int
146_import_extension_module(const char *module_name,
147 struct function_description *functions,
148 struct type_description *types)
149{
150 PyObject *module = NULL;
151 struct function_description *cur_func;
152 struct type_description *cur_type;
153 int ret_code;
154
155 module = PyImport_ImportModule((char *)module_name);
156 if (!module)
157 goto bad;
158 if (functions != NULL) {
159 cur_func = functions;
160 while (cur_func->name != NULL) {
161 ret_code = _import_function(module, cur_func->name,
162 cur_func->pointer,
163 cur_func->signature);
164 if (ret_code < 0)
165 goto bad;
166 cur_func++;
167 }
168 }
169 if (types != NULL) {
170 PyTypeObject *type_p = NULL;
171 cur_type = types;
172 while (cur_type->name != NULL) {
173 type_p = _import_type(module, cur_type->name);
174 if (type_p == NULL)
175 goto bad;
176 *(cur_type->pointer) = type_p;
177 cur_type++;
178 }
179 }
180
181 Py_XDECREF(module);
182 return 0;
183bad:
184 Py_XDECREF(module);
185 return -1;
186}
187
188
189#endif // _IMPORT_C_API_H_
0190
=== modified file 'bzrlib/_knit_load_data_pyx.pyx'
--- bzrlib/_knit_load_data_pyx.pyx 2010-01-04 23:13:20 +0000
+++ bzrlib/_knit_load_data_pyx.pyx 2010-02-18 00:00:51 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2007 Canonical Ltd1# Copyright (C) 2007-2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
55
=== modified file 'bzrlib/_known_graph_py.py'
--- bzrlib/_known_graph_py.py 2009-08-25 18:45:40 +0000
+++ bzrlib/_known_graph_py.py 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009 Canonical Ltd1# Copyright (C) 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
17"""Implementation of Graph algorithms when we have already loaded everything.17"""Implementation of Graph algorithms when we have already loaded everything.
18"""18"""
1919
20from collections import deque
20from bzrlib import (21from bzrlib import (
21 errors,22 errors,
22 revision,23 revision,
@@ -62,7 +63,7 @@
62 :param parent_map: A dictionary mapping key => parent_keys63 :param parent_map: A dictionary mapping key => parent_keys
63 """64 """
64 self._nodes = {}65 self._nodes = {}
65 # Maps {sorted(revision_id, revision_id): heads}66 # Maps {frozenset(revision_id, revision_id): heads}
66 self._known_heads = {}67 self._known_heads = {}
67 self.do_cache = do_cache68 self.do_cache = do_cache
68 self._initialize_nodes(parent_map)69 self._initialize_nodes(parent_map)
@@ -132,6 +133,71 @@
132 # Update known_parent_gdfos for a key we couldn't process133 # Update known_parent_gdfos for a key we couldn't process
133 known_parent_gdfos[child_key] = known_gdfo134 known_parent_gdfos[child_key] = known_gdfo
134135
136 def add_node(self, key, parent_keys):
137 """Add a new node to the graph.
138
139 If this fills in a ghost, then the gdfos of all children will be
140 updated accordingly.
141
142 :param key: The node being added. If this is a duplicate, this is a
143 no-op.
144 :param parent_keys: The parents of the given node.
145 :return: None (should we return if this was a ghost, etc?)
146 """
147 nodes = self._nodes
148 if key in nodes:
149 node = nodes[key]
150 if node.parent_keys is None:
151 node.parent_keys = parent_keys
152 # A ghost is being added, we can no-longer trust the heads
153 # cache, so clear it
154 self._known_heads.clear()
155 else:
156 # Make sure we compare a list to a list, as tuple != list.
157 parent_keys = list(parent_keys)
158 existing_parent_keys = list(node.parent_keys)
159 if parent_keys == existing_parent_keys:
160 return # Identical content
161 else:
162 raise ValueError('Parent key mismatch, existing node %s'
163 ' has parents of %s not %s'
164 % (key, existing_parent_keys, parent_keys))
165 else:
166 node = _KnownGraphNode(key, parent_keys)
167 nodes[key] = node
168 parent_gdfo = 0
169 for parent_key in parent_keys:
170 try:
171 parent_node = nodes[parent_key]
172 except KeyError:
173 parent_node = _KnownGraphNode(parent_key, None)
174 # Ghosts and roots have gdfo 1
175 parent_node.gdfo = 1
176 nodes[parent_key] = parent_node
177 if parent_gdfo < parent_node.gdfo:
178 parent_gdfo = parent_node.gdfo
179 parent_node.child_keys.append(key)
180 node.gdfo = parent_gdfo + 1
181 # Now fill the gdfo to all children
182 # Note that this loop is slightly inefficient, in that we may visit the
183 # same child (and its decendents) more than once, however, it is
184 # 'efficient' in that we only walk to nodes that would be updated,
185 # rather than all nodes
186 # We use a deque rather than a simple list stack, to go for BFD rather
187 # than DFD. So that if a longer path is possible, we walk it before we
188 # get to the final child
189 pending = deque([node])
190 while pending:
191 node = pending.popleft()
192 next_gdfo = node.gdfo + 1
193 for child_key in node.child_keys:
194 child = nodes[child_key]
195 if child.gdfo < next_gdfo:
196 # This child is being updated, we need to check its
197 # children
198 child.gdfo = next_gdfo
199 pending.append(child)
200
135 def heads(self, keys):201 def heads(self, keys):
136 """Return the heads from amongst keys.202 """Return the heads from amongst keys.
137203
@@ -281,3 +347,26 @@
281 in tsort.merge_sort(as_parent_map, tip_key,347 in tsort.merge_sort(as_parent_map, tip_key,
282 mainline_revisions=None,348 mainline_revisions=None,
283 generate_revno=True)]349 generate_revno=True)]
350
351 def get_parent_keys(self, key):
352 """Get the parents for a key
353
354 Returns a list containg the parents keys. If the key is a ghost,
355 None is returned. A KeyError will be raised if the key is not in
356 the graph.
357
358 :param keys: Key to check (eg revision_id)
359 :return: A list of parents
360 """
361 return self._nodes[key].parent_keys
362
363 def get_child_keys(self, key):
364 """Get the children for a key
365
366 Returns a list containg the children keys. A KeyError will be raised
367 if the key is not in the graph.
368
369 :param keys: Key to check (eg revision_id)
370 :return: A list of children
371 """
372 return self._nodes[key].child_keys
284373
=== modified file 'bzrlib/_known_graph_pyx.pyx'
--- bzrlib/_known_graph_pyx.pyx 2010-01-05 05:03:51 +0000
+++ bzrlib/_known_graph_pyx.pyx 2010-02-18 00:00:51 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009 Canonical Ltd1# Copyright (C) 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -51,6 +51,7 @@
5151
52 void Py_INCREF(object)52 void Py_INCREF(object)
5353
54from collections import deque
54import gc55import gc
5556
56from bzrlib import errors, revision57from bzrlib import errors, revision
@@ -88,6 +89,18 @@
88 PyList_Append(keys, child.key)89 PyList_Append(keys, child.key)
89 return keys90 return keys
9091
92 property parent_keys:
93 def __get__(self):
94 if self.parents is None:
95 return None
96
97 cdef _KnownGraphNode parent
98
99 keys = []
100 for parent in self.parents:
101 PyList_Append(keys, parent.key)
102 return keys
103
91 cdef clear_references(self):104 cdef clear_references(self):
92 self.parents = None105 self.parents = None
93 self.children = None106 self.children = None
@@ -180,7 +193,7 @@
180 """This is a class which assumes we already know the full graph."""193 """This is a class which assumes we already know the full graph."""
181194
182 cdef public object _nodes195 cdef public object _nodes
183 cdef object _known_heads196 cdef public object _known_heads
184 cdef public int do_cache197 cdef public int do_cache
185198
186 def __init__(self, parent_map, do_cache=True):199 def __init__(self, parent_map, do_cache=True):
@@ -220,6 +233,28 @@
220 node = <_KnownGraphNode>temp_node233 node = <_KnownGraphNode>temp_node
221 return node234 return node
222235
236 cdef _populate_parents(self, _KnownGraphNode node, parent_keys):
237 cdef Py_ssize_t num_parent_keys, pos
238 cdef _KnownGraphNode parent_node
239
240 num_parent_keys = len(parent_keys)
241 # We know how many parents, so we pre allocate the tuple
242 parent_nodes = PyTuple_New(num_parent_keys)
243 for pos from 0 <= pos < num_parent_keys:
244 # Note: it costs us 10ms out of 40ms to lookup all of these
245 # parents, it doesn't seem to be an allocation overhead,
246 # but rather a lookup overhead. There doesn't seem to be
247 # a way around it, and that is one reason why
248 # KnownGraphNode maintains a direct pointer to the parent
249 # node.
250 # We use [] because parent_keys may be a tuple or list
251 parent_node = self._get_or_create_node(parent_keys[pos])
252 # PyTuple_SET_ITEM will steal a reference, so INCREF first
253 Py_INCREF(parent_node)
254 PyTuple_SET_ITEM(parent_nodes, pos, parent_node)
255 PyList_Append(parent_node.children, node)
256 node.parents = parent_nodes
257
223 def _initialize_nodes(self, parent_map):258 def _initialize_nodes(self, parent_map):
224 """Populate self._nodes.259 """Populate self._nodes.
225260
@@ -230,7 +265,7 @@
230 child keys,265 child keys,
231 """266 """
232 cdef PyObject *temp_key, *temp_parent_keys, *temp_node267 cdef PyObject *temp_key, *temp_parent_keys, *temp_node
233 cdef Py_ssize_t pos, pos2, num_parent_keys268 cdef Py_ssize_t pos
234 cdef _KnownGraphNode node269 cdef _KnownGraphNode node
235 cdef _KnownGraphNode parent_node270 cdef _KnownGraphNode parent_node
236271
@@ -241,24 +276,8 @@
241 while PyDict_Next(parent_map, &pos, &temp_key, &temp_parent_keys):276 while PyDict_Next(parent_map, &pos, &temp_key, &temp_parent_keys):
242 key = <object>temp_key277 key = <object>temp_key
243 parent_keys = <object>temp_parent_keys278 parent_keys = <object>temp_parent_keys
244 num_parent_keys = len(parent_keys)
245 node = self._get_or_create_node(key)279 node = self._get_or_create_node(key)
246 # We know how many parents, so we pre allocate the tuple280 self._populate_parents(node, parent_keys)
247 parent_nodes = PyTuple_New(num_parent_keys)
248 for pos2 from 0 <= pos2 < num_parent_keys:
249 # Note: it costs us 10ms out of 40ms to lookup all of these
250 # parents, it doesn't seem to be an allocation overhead,
251 # but rather a lookup overhead. There doesn't seem to be
252 # a way around it, and that is one reason why
253 # KnownGraphNode maintains a direct pointer to the parent
254 # node.
255 # We use [] because parent_keys may be a tuple or list
256 parent_node = self._get_or_create_node(parent_keys[pos2])
257 # PyTuple_SET_ITEM will steal a reference, so INCREF first
258 Py_INCREF(parent_node)
259 PyTuple_SET_ITEM(parent_nodes, pos2, parent_node)
260 PyList_Append(parent_node.children, node)
261 node.parents = parent_nodes
262281
263 def _find_tails(self):282 def _find_tails(self):
264 cdef PyObject *temp_node283 cdef PyObject *temp_node
@@ -322,6 +341,76 @@
322 # anymore341 # anymore
323 child.seen = 0342 child.seen = 0
324343
344 def add_node(self, key, parent_keys):
345 """Add a new node to the graph.
346
347 If this fills in a ghost, then the gdfos of all children will be
348 updated accordingly.
349
350 :param key: The node being added. If this is a duplicate, this is a
351 no-op.
352 :param parent_keys: The parents of the given node.
353 :return: None (should we return if this was a ghost, etc?)
354 """
355 cdef PyObject *maybe_node
356 cdef _KnownGraphNode node, parent_node, child_node
357 cdef long parent_gdfo, next_gdfo
358
359 maybe_node = PyDict_GetItem(self._nodes, key)
360 if maybe_node != NULL:
361 node = <_KnownGraphNode>maybe_node
362 if node.parents is None:
363 # We are filling in a ghost
364 self._populate_parents(node, parent_keys)
365 # We can't trust cached heads anymore
366 self._known_heads.clear()
367 else: # Ensure that the parent_key list matches
368 existing_parent_keys = []
369 for parent_node in node.parents:
370 existing_parent_keys.append(parent_node.key)
371 # Make sure we use a list for the comparison, in case it was a
372 # tuple, etc
373 parent_keys = list(parent_keys)
374 if existing_parent_keys == parent_keys:
375 # Exact match, nothing more to do
376 return
377 else:
378 raise ValueError('Parent key mismatch, existing node %s'
379 ' has parents of %s not %s'
380 % (key, existing_parent_keys, parent_keys))
381 else:
382 node = _KnownGraphNode(key)
383 PyDict_SetItem(self._nodes, key, node)
384 self._populate_parents(node, parent_keys)
385 parent_gdfo = 0
386 for parent_node in node.parents:
387 if parent_node.gdfo == -1:
388 # This is a newly introduced ghost, so it gets gdfo of 1
389 parent_node.gdfo = 1
390 if parent_gdfo < parent_node.gdfo:
391 parent_gdfo = parent_node.gdfo
392 node.gdfo = parent_gdfo + 1
393 # Now fill the gdfo to all children
394 # Note that this loop is slightly inefficient, in that we may visit the
395 # same child (and its decendents) more than once, however, it is
396 # 'efficient' in that we only walk to nodes that would be updated,
397 # rather than all nodes
398 # We use a deque rather than a simple list stack, to go for BFD rather
399 # than DFD. So that if a longer path is possible, we walk it before we
400 # get to the final child
401 pending = deque([node])
402 pending_popleft = pending.popleft
403 pending_append = pending.append
404 while pending:
405 node = pending_popleft()
406 next_gdfo = node.gdfo + 1
407 for child_node in node.children:
408 if child_node.gdfo < next_gdfo:
409 # This child is being updated, we need to check its
410 # children
411 child_node.gdfo = next_gdfo
412 pending_append(child_node)
413
325 def heads(self, keys):414 def heads(self, keys):
326 """Return the heads from amongst keys.415 """Return the heads from amongst keys.
327416
@@ -549,6 +638,29 @@
549 # shown a specific impact, yet.638 # shown a specific impact, yet.
550 sorter = _MergeSorter(self, tip_key)639 sorter = _MergeSorter(self, tip_key)
551 return sorter.topo_order()640 return sorter.topo_order()
641
642 def get_parent_keys(self, key):
643 """Get the parents for a key
644
645 Returns a list containg the parents keys. If the key is a ghost,
646 None is returned. A KeyError will be raised if the key is not in
647 the graph.
648
649 :param keys: Key to check (eg revision_id)
650 :return: A list of parents
651 """
652 return self._nodes[key].parent_keys
653
654 def get_child_keys(self, key):
655 """Get the children for a key
656
657 Returns a list containg the children keys. A KeyError will be raised
658 if the key is not in the graph.
659
660 :param keys: Key to check (eg revision_id)
661 :return: A list of children
662 """
663 return self._nodes[key].child_keys
552664
553665
554cdef class _MergeSortNode:666cdef class _MergeSortNode:
555667
=== modified file 'bzrlib/_patiencediff_c.c'
--- bzrlib/_patiencediff_c.c 2009-03-23 14:59:43 +0000
+++ bzrlib/_patiencediff_c.c 2010-02-18 00:00:51 +0000
@@ -298,7 +298,7 @@
298 apos = SENTINEL;298 apos = SENTINEL;
299 /* loop through all lines in the linked list */299 /* loop through all lines in the linked list */
300 for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) {300 for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) {
301 /* the index is lower than alo, the the next line */301 /* the index is lower than alo, continue to the next line */
302 if (i < alo) {302 if (i < alo) {
303 h[equiv].a_pos = i;303 h[equiv].a_pos = i;
304 continue;304 continue;
@@ -319,7 +319,7 @@
319 /* check for duplicates of this line in lines_b[blo:bhi] */319 /* check for duplicates of this line in lines_b[blo:bhi] */
320 /* loop through all lines in the linked list */320 /* loop through all lines in the linked list */
321 for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) {321 for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) {
322 /* the index is lower than blo, the the next line */322 /* the index is lower than blo, continue to the next line */
323 if (i < blo) {323 if (i < blo) {
324 h[equiv].b_pos = i;324 h[equiv].b_pos = i;
325 continue;325 continue;
326326
=== modified file 'bzrlib/_readdir_pyx.pyx'
--- bzrlib/_readdir_pyx.pyx 2009-12-23 02:19:04 +0000
+++ bzrlib/_readdir_pyx.pyx 2010-02-18 00:00:50 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2006, 2008, 2009 Canonical Ltd1# Copyright (C) 2006, 2008, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
55
=== modified file 'bzrlib/_rio_pyx.pyx'
--- bzrlib/_rio_pyx.pyx 2010-01-05 04:59:57 +0000
+++ bzrlib/_rio_pyx.pyx 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009 Canonical Ltd1# Copyright (C) 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
55
=== added file 'bzrlib/_simple_set_pyx.pxd'
--- bzrlib/_simple_set_pyx.pxd 1970-01-01 00:00:00 +0000
+++ bzrlib/_simple_set_pyx.pxd 2010-02-18 00:00:50 +0000
@@ -0,0 +1,91 @@
1# Copyright (C) 2009, 2010 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""Interface definition of a class like PySet but without caching the hash.
18
19This is generally useful when you want to 'intern' objects, etc. Note that this
20differs from Set in that we:
21 1) Don't have all of the .intersection, .difference, etc functions
22 2) Do return the object from the set via queries
23 eg. SimpleSet.add(key) => saved_key and SimpleSet[key] => saved_key
24"""
25
26cdef extern from "Python.h":
27 ctypedef struct PyObject:
28 pass
29
30
31cdef public api class SimpleSet [object SimpleSetObject, type SimpleSet_Type]:
32 """A class similar to PySet, but with simpler implementation.
33
34 The main advantage is that this class uses only 2N memory to store N
35 objects rather than 4N memory. The main trade-off is that we do not cache
36 the hash value of saved objects. As such, it is assumed that computing the
37 hash will be cheap (such as strings or tuples of strings, etc.)
38
39 This also differs in that you can get back the objects that are stored
40 (like a dict), but we also don't implement the complete list of 'set'
41 operations (difference, intersection, etc).
42 """
43 # Data structure definition:
44 # This is a basic hash table using open addressing.
45 # http://en.wikipedia.org/wiki/Open_addressing
46 # Basically that means we keep an array of pointers to Python objects
47 # (called a table). Each location in the array is called a 'slot'.
48 #
49 # An empty slot holds a NULL pointer, a slot where there was an item
50 # which was then deleted will hold a pointer to _dummy, and a filled slot
51 # points at the actual object which fills that slot.
52 #
53 # The table is always a power of two, and the default location where an
54 # object is inserted is at hash(object) & (table_size - 1)
55 #
56 # If there is a collision, then we search for another location. The
57 # specific algorithm is in _lookup. We search until we:
58 # find the object
59 # find an equivalent object (by tp_richcompare(obj1, obj2, Py_EQ))
60 # find a NULL slot
61 #
62 # When an object is deleted, we set its slot to _dummy. this way we don't
63 # have to track whether there was a collision, and find the corresponding
64 # keys. (The collision resolution algorithm makes that nearly impossible
65 # anyway, because it depends on the upper bits of the hash.)
66 # The main effect of this, is that if we find _dummy, then we can insert
67 # an object there, but we have to keep searching until we find NULL to
68 # know that the object is not present elsewhere.
69
70 cdef Py_ssize_t _used # active
71 cdef Py_ssize_t _fill # active + dummy
72 cdef Py_ssize_t _mask # Table contains (mask+1) slots, a power of 2
73 cdef PyObject **_table # Pyrex/Cython doesn't support arrays to 'object'
74 # so we manage it manually
75
76 cdef PyObject *_get(self, object key) except? NULL
77 cdef object _add(self, key)
78 cdef int _discard(self, key) except -1
79 cdef int _insert_clean(self, PyObject *key) except -1
80 cdef Py_ssize_t _resize(self, Py_ssize_t min_unused) except -1
81
82
83# TODO: might want to export the C api here, though it is all available from
84# the class object...
85cdef api SimpleSet SimpleSet_New()
86cdef api object SimpleSet_Add(object self, object key)
87cdef api int SimpleSet_Contains(object self, object key) except -1
88cdef api int SimpleSet_Discard(object self, object key) except -1
89cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL
90cdef api Py_ssize_t SimpleSet_Size(object self) except -1
91cdef api int SimpleSet_Next(object self, Py_ssize_t *pos, PyObject **key) except -1
092
=== added file 'bzrlib/_simple_set_pyx.pyx'
--- bzrlib/_simple_set_pyx.pyx 1970-01-01 00:00:00 +0000
+++ bzrlib/_simple_set_pyx.pyx 2010-02-18 00:00:50 +0000
@@ -0,0 +1,592 @@
1# Copyright (C) 2009, 2010 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""Definition of a class that is similar to Set with some small changes."""
18
19cdef extern from "python-compat.h":
20 pass
21
22cdef extern from "Python.h":
23 ctypedef unsigned long size_t
24 ctypedef long (*hashfunc)(PyObject*) except -1
25 ctypedef object (*richcmpfunc)(PyObject *, PyObject *, int)
26 ctypedef int (*visitproc)(PyObject *, void *)
27 ctypedef int (*traverseproc)(PyObject *, visitproc, void *)
28 int Py_EQ
29 void Py_INCREF(PyObject *)
30 void Py_DECREF(PyObject *)
31 ctypedef struct PyTypeObject:
32 hashfunc tp_hash
33 richcmpfunc tp_richcompare
34 traverseproc tp_traverse
35
36 PyTypeObject *Py_TYPE(PyObject *)
37 # Note: *Don't* use hash(), Pyrex 0.9.8.5 thinks it returns an 'int', and
38 # thus silently truncates to 32-bits on 64-bit machines.
39 long PyObject_Hash(PyObject *) except -1
40
41 void *PyMem_Malloc(size_t nbytes)
42 void PyMem_Free(void *)
43 void memset(void *, int, size_t)
44
45
46# Dummy is an object used to mark nodes that have been deleted. Since
47# collisions require us to move a node to an alternative location, if we just
48# set an entry to NULL on delete, we won't find any relocated nodes.
49# We have to use _dummy_obj because we need to keep a refcount to it, but we
50# also use _dummy as a pointer, because it avoids having to put <PyObject*> all
51# over the code base.
52cdef object _dummy_obj
53cdef PyObject *_dummy
54_dummy_obj = object()
55_dummy = <PyObject *>_dummy_obj
56
57
58cdef object _NotImplemented
59_NotImplemented = NotImplemented
60
61
62cdef int _is_equal(PyObject *this, long this_hash, PyObject *other) except -1:
63 cdef long other_hash
64
65 if this == other:
66 return 1
67 other_hash = PyObject_Hash(other)
68 if other_hash != this_hash:
69 return 0
70
71 # This implements a subset of the PyObject_RichCompareBool functionality.
72 # Namely it:
73 # 1) Doesn't try to do anything with old-style classes
74 # 2) Assumes that both objects have a tp_richcompare implementation, and
75 # that if that is not enough to compare equal, then they are not
76 # equal. (It doesn't try to cast them both to some intermediate form
77 # that would compare equal.)
78 res = Py_TYPE(this).tp_richcompare(this, other, Py_EQ)
79 if res is _NotImplemented:
80 res = Py_TYPE(other).tp_richcompare(other, this, Py_EQ)
81 if res is _NotImplemented:
82 return 0
83 if res:
84 return 1
85 return 0
86
87
88cdef public api class SimpleSet [object SimpleSetObject, type SimpleSet_Type]:
89 """This class can be used to track canonical forms for objects.
90
91 It is similar in function to the interned dictionary that is used by
92 strings. However:
93
94 1) It assumes that hash(obj) is cheap, so does not need to inline a copy
95 of it
96 2) It only stores one reference to the object, rather than 2 (key vs
97 key:value)
98
99 As such, it uses 1/3rd the amount of memory to store a pointer to the
100 interned object.
101 """
102 # Attributes are defined in the .pxd file
103 DEF DEFAULT_SIZE=1024
104
105 def __init__(self):
106 cdef Py_ssize_t size, n_bytes
107
108 size = DEFAULT_SIZE
109 self._mask = size - 1
110 self._used = 0
111 self._fill = 0
112 n_bytes = sizeof(PyObject*) * size;
113 self._table = <PyObject **>PyMem_Malloc(n_bytes)
114 if self._table == NULL:
115 raise MemoryError()
116 memset(self._table, 0, n_bytes)
117
118 def __dealloc__(self):
119 if self._table != NULL:
120 PyMem_Free(self._table)
121 self._table = NULL
122
123 property used:
124 def __get__(self):
125 return self._used
126
127 property fill:
128 def __get__(self):
129 return self._fill
130
131 property mask:
132 def __get__(self):
133 return self._mask
134
135 def _memory_size(self):
136 """Return the number of bytes of memory consumed by this class."""
137 return sizeof(self) + (sizeof(PyObject*)*(self._mask + 1))
138
139 def __len__(self):
140 return self._used
141
142 def _test_lookup(self, key):
143 cdef PyObject **slot
144
145 slot = _lookup(self, key)
146 if slot[0] == NULL:
147 res = '<null>'
148 elif slot[0] == _dummy:
149 res = '<dummy>'
150 else:
151 res = <object>slot[0]
152 return <int>(slot - self._table), res
153
154 def __contains__(self, key):
155 """Is key present in this SimpleSet."""
156 cdef PyObject **slot
157
158 slot = _lookup(self, key)
159 if slot[0] == NULL or slot[0] == _dummy:
160 return False
161 return True
162
163 cdef PyObject *_get(self, object key) except? NULL:
164 """Return the object (or nothing) define at the given location."""
165 cdef PyObject **slot
166
167 slot = _lookup(self, key)
168 if slot[0] == NULL or slot[0] == _dummy:
169 return NULL
170 return slot[0]
171
172 def __getitem__(self, key):
173 """Return a stored item that is equivalent to key."""
174 cdef PyObject *py_val
175
176 py_val = self._get(key)
177 if py_val == NULL:
178 raise KeyError("Key %s is not present" % key)
179 val = <object>(py_val)
180 return val
181
182 cdef int _insert_clean(self, PyObject *key) except -1:
183 """Insert a key into self.table.
184
185 This is only meant to be used during times like '_resize',
186 as it makes a lot of assuptions about keys not already being present,
187 and there being no dummy entries.
188 """
189 cdef size_t i, n_lookup
190 cdef long the_hash
191 cdef PyObject **table, **slot
192 cdef Py_ssize_t mask
193
194 mask = self._mask
195 table = self._table
196
197 the_hash = PyObject_Hash(key)
198 i = the_hash
199 for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever
200 slot = &table[i & mask]
201 if slot[0] == NULL:
202 slot[0] = key
203 self._fill = self._fill + 1
204 self._used = self._used + 1
205 return 1
206 i = i + 1 + n_lookup
207 raise RuntimeError('ran out of slots.')
208
209 def _py_resize(self, min_used):
210 """Do not use this directly, it is only exposed for testing."""
211 return self._resize(min_used)
212
213 cdef Py_ssize_t _resize(self, Py_ssize_t min_used) except -1:
214 """Resize the internal table.
215
216 The final table will be big enough to hold at least min_used entries.
217 We will copy the data from the existing table over, leaving out dummy
218 entries.
219
220 :return: The new size of the internal table
221 """
222 cdef Py_ssize_t new_size, n_bytes, remaining
223 cdef PyObject **new_table, **old_table, **slot
224
225 new_size = DEFAULT_SIZE
226 while new_size <= min_used and new_size > 0:
227 new_size = new_size << 1
228 # We rolled over our signed size field
229 if new_size <= 0:
230 raise MemoryError()
231 # Even if min_used == self._mask + 1, and we aren't changing the actual
232 # size, we will still run the algorithm so that dummy entries are
233 # removed
234 # TODO: Test this
235 # if new_size < self._used:
236 # raise RuntimeError('cannot shrink SimpleSet to something'
237 # ' smaller than the number of used slots.')
238 n_bytes = sizeof(PyObject*) * new_size;
239 new_table = <PyObject **>PyMem_Malloc(n_bytes)
240 if new_table == NULL:
241 raise MemoryError()
242
243 old_table = self._table
244 self._table = new_table
245 memset(self._table, 0, n_bytes)
246 self._mask = new_size - 1
247 self._used = 0
248 remaining = self._fill
249 self._fill = 0
250
251 # Moving everything to the other table is refcount neutral, so we don't
252 # worry about it.
253 slot = old_table
254 while remaining > 0:
255 if slot[0] == NULL: # unused slot
256 pass
257 elif slot[0] == _dummy: # dummy slot
258 remaining = remaining - 1
259 else: # active slot
260 remaining = remaining - 1
261 self._insert_clean(slot[0])
262 slot = slot + 1
263 PyMem_Free(old_table)
264 return new_size
265
266 def add(self, key):
267 """Similar to set.add(), start tracking this key.
268
269 There is one small difference, which is that we return the object that
270 is stored at the given location. (which is closer to the
271 dict.setdefault() functionality.)
272 """
273 return self._add(key)
274
275 cdef object _add(self, key):
276 cdef PyObject **slot, *py_key
277 cdef int added
278
279 py_key = <PyObject *>key
280 if (Py_TYPE(py_key).tp_richcompare == NULL
281 or Py_TYPE(py_key).tp_hash == NULL):
282 raise TypeError('Types added to SimpleSet must implement'
283 ' both tp_richcompare and tp_hash')
284 added = 0
285 # We need at least one empty slot
286 assert self._used < self._mask
287 slot = _lookup(self, key)
288 if (slot[0] == NULL):
289 Py_INCREF(py_key)
290 self._fill = self._fill + 1
291 self._used = self._used + 1
292 slot[0] = py_key
293 added = 1
294 elif (slot[0] == _dummy):
295 Py_INCREF(py_key)
296 self._used = self._used + 1
297 slot[0] = py_key
298 added = 1
299 # No else: clause. If _lookup returns a pointer to
300 # a live object, then we already have a value at this location.
301 retval = <object>(slot[0])
302 # PySet and PyDict use a 2-3rds full algorithm, we'll follow suit
303 if added and (self._fill * 3) >= ((self._mask + 1) * 2):
304 # However, we always work for a load factor of 2:1
305 self._resize(self._used * 2)
306 # Even if we resized and ended up moving retval into a different slot,
307 # it is still the value that is held at the slot equivalent to 'key',
308 # so we can still return it
309 return retval
310
311 def discard(self, key):
312 """Remove key from the set, whether it exists or not.
313
314 :return: False if the item did not exist, True if it did
315 """
316 if self._discard(key):
317 return True
318 return False
319
320 cdef int _discard(self, key) except -1:
321 cdef PyObject **slot, *py_key
322
323 slot = _lookup(self, key)
324 if slot[0] == NULL or slot[0] == _dummy:
325 return 0
326 self._used = self._used - 1
327 Py_DECREF(slot[0])
328 slot[0] = _dummy
329 # PySet uses the heuristic: If more than 1/5 are dummies, then resize
330 # them away
331 # if ((so->_fill - so->_used) * 5 < so->mask)
332 # However, we are planning on using this as an interning structure, in
333 # which we will be putting a lot of objects. And we expect that large
334 # groups of them are going to have the same lifetime.
335 # Dummy entries hurt a little bit because they cause the lookup to keep
336 # searching, but resizing is also rather expensive
337 # For now, we'll just use their algorithm, but we may want to revisit
338 # it
339 if ((self._fill - self._used) * 5 > self._mask):
340 self._resize(self._used * 2)
341 return 1
342
343 def __iter__(self):
344 return _SimpleSet_iterator(self)
345
346
347cdef class _SimpleSet_iterator:
348 """Iterator over the SimpleSet structure."""
349
350 cdef Py_ssize_t pos
351 cdef SimpleSet set
352 cdef Py_ssize_t _used # track if things have been mutated while iterating
353 cdef Py_ssize_t len # number of entries left
354
355 def __init__(self, obj):
356 self.set = obj
357 self.pos = 0
358 self._used = self.set._used
359 self.len = self.set._used
360
361 def __iter__(self):
362 return self
363
364 def __next__(self):
365 cdef Py_ssize_t mask, i
366 cdef PyObject *key
367
368 if self.set is None:
369 raise StopIteration
370 if self.set._used != self._used:
371 # Force this exception to continue to be raised
372 self._used = -1
373 raise RuntimeError("Set size changed during iteration")
374 if not SimpleSet_Next(self.set, &self.pos, &key):
375 self.set = None
376 raise StopIteration
377 # we found something
378 the_key = <object>key # INCREF
379 self.len = self.len - 1
380 return the_key
381
382 def __length_hint__(self):
383 if self.set is not None and self._used == self.set._used:
384 return self.len
385 return 0
386
387
388
389cdef api SimpleSet SimpleSet_New():
390 """Create a new SimpleSet object."""
391 return SimpleSet()
392
393
394cdef SimpleSet _check_self(object self):
395 """Check that the parameter is not None.
396
397 Pyrex/Cython will do type checking, but only to ensure that an object is
398 either the right type or None. You can say "object foo not None" for pure
399 python functions, but not for C functions.
400 So this is just a helper for all the apis that need to do the check.
401 """
402 cdef SimpleSet true_self
403 if self is None:
404 raise TypeError('self must not be None')
405 true_self = self
406 return true_self
407
408
409cdef PyObject **_lookup(SimpleSet self, object key) except NULL:
410 """Find the slot where 'key' would fit.
411
412 This is the same as a dicts 'lookup' function.
413
414 :param key: An object we are looking up
415 :param hash: The hash for key
416 :return: The location in self.table where key should be put.
417 location == NULL is an exception, but (*location) == NULL just
418 indicates the slot is empty and can be used.
419 """
420 # This uses Quadratic Probing:
421 # http://en.wikipedia.org/wiki/Quadratic_probing
422 # with c1 = c2 = 1/2
423 # This leads to probe locations at:
424 # h0 = hash(k1)
425 # h1 = h0 + 1
426 # h2 = h0 + 3 = h1 + 1 + 1
427 # h3 = h0 + 6 = h2 + 1 + 2
428 # h4 = h0 + 10 = h2 + 1 + 3
429 # Note that all of these are '& mask', but that is computed *after* the
430 # offset.
431 # This differs from the algorithm used by Set and Dict. Which, effectively,
432 # use double-hashing, and a step size that starts large, but dwindles to
433 # stepping one-by-one.
434 # This gives more 'locality' in that if you have a collision at offset X,
435 # the first fallback is X+1, which is fast to check. However, that means
436 # that an object w/ hash X+1 will also check there, and then X+2 next.
437 # However, for objects with differing hashes, their chains are different.
438 # The former checks X, X+1, X+3, ... the latter checks X+1, X+2, X+4, ...
439 # So different hashes diverge quickly.
440 # A bigger problem is that we *only* ever use the lowest bits of the hash
441 # So all integers (x + SIZE*N) will resolve into the same bucket, and all
442 # use the same collision resolution. We may want to try to find a way to
443 # incorporate the upper bits of the hash with quadratic probing. (For
444 # example, X, X+1, X+3+some_upper_bits, X+6+more_upper_bits, etc.)
445 cdef size_t i, n_lookup
446 cdef Py_ssize_t mask
447 cdef long key_hash
448 cdef PyObject **table, **slot, *cur, **free_slot, *py_key
449
450 py_key = <PyObject *>key
451 # Note: avoid using hash(obj) because of a bug w/ pyrex 0.9.8.5 and 64-bit
452 # (it treats hash() as returning an 'int' rather than a 'long')
453 key_hash = PyObject_Hash(py_key)
454 i = <size_t>key_hash
455 mask = self._mask
456 table = self._table
457 free_slot = NULL
458 for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever
459 slot = &table[i & mask]
460 cur = slot[0]
461 if cur == NULL:
462 # Found a blank spot
463 if free_slot != NULL:
464 # Did we find an earlier _dummy entry?
465 return free_slot
466 else:
467 return slot
468 if cur == py_key:
469 # Found an exact pointer to the key
470 return slot
471 if cur == _dummy:
472 if free_slot == NULL:
473 free_slot = slot
474 elif _is_equal(py_key, key_hash, cur):
475 # Both py_key and cur belong in this slot, return it
476 return slot
477 i = i + 1 + n_lookup
478 raise AssertionError('should never get here')
479
480
481cdef api PyObject **_SimpleSet_Lookup(object self, object key) except NULL:
482 """Find the slot where 'key' would fit.
483
484 This is the same as a dicts 'lookup' function. This is a private
485 api because mutating what you get without maintaing the other invariants
486 is a 'bad thing'.
487
488 :param key: An object we are looking up
489 :param hash: The hash for key
490 :return: The location in self._table where key should be put
491 should never be NULL, but may reference a NULL (PyObject*)
492 """
493 return _lookup(_check_self(self), key)
494
495
496cdef api object SimpleSet_Add(object self, object key):
497 """Add a key to the SimpleSet (set).
498
499 :param self: The SimpleSet to add the key to.
500 :param key: The key to be added. If the key is already present,
501 self will not be modified
502 :return: The current key stored at the location defined by 'key'.
503 This may be the same object, or it may be an equivalent object.
504 (consider dict.setdefault(key, key))
505 """
506 return _check_self(self)._add(key)
507
508
509cdef api int SimpleSet_Contains(object self, object key) except -1:
510 """Is key present in self?"""
511 return (key in _check_self(self))
512
513
514cdef api int SimpleSet_Discard(object self, object key) except -1:
515 """Remove the object referenced at location 'key'.
516
517 :param self: The SimpleSet being modified
518 :param key: The key we are checking on
519 :return: 1 if there was an object present, 0 if there was not, and -1 on
520 error.
521 """
522 return _check_self(self)._discard(key)
523
524
525cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL:
526 """Get a pointer to the object present at location 'key'.
527
528 This returns an object which is equal to key which was previously added to
529 self. This returns a borrowed reference, as it may also return NULL if no
530 value is present at that location.
531
532 :param key: The value we are looking for
533 :return: The object present at that location
534 """
535 return _check_self(self)._get(key)
536
537
538cdef api Py_ssize_t SimpleSet_Size(object self) except -1:
539 """Get the number of active entries in 'self'"""
540 return _check_self(self)._used
541
542
543cdef api int SimpleSet_Next(object self, Py_ssize_t *pos,
544 PyObject **key) except -1:
545 """Walk over items in a SimpleSet.
546
547 :param pos: should be initialized to 0 by the caller, and will be updated
548 by this function
549 :param key: Will return a borrowed reference to key
550 :return: 0 if nothing left, 1 if we are returning a new value
551 """
552 cdef Py_ssize_t i, mask
553 cdef SimpleSet true_self
554 cdef PyObject **table
555 true_self = _check_self(self)
556 i = pos[0]
557 if (i < 0):
558 return 0
559 mask = true_self._mask
560 table= true_self._table
561 while (i <= mask and (table[i] == NULL or table[i] == _dummy)):
562 i = i + 1
563 pos[0] = i + 1
564 if (i > mask):
565 return 0 # All done
566 if (key != NULL):
567 key[0] = table[i]
568 return 1
569
570
571cdef int SimpleSet_traverse(SimpleSet self, visitproc visit,
572 void *arg) except -1:
573 """This is an implementation of 'tp_traverse' that hits the whole table.
574
575 Cython/Pyrex don't seem to let you define a tp_traverse, and they only
576 define one for you if you have an 'object' attribute. Since they don't
577 support C arrays of objects, we access the PyObject * directly.
578 """
579 cdef Py_ssize_t pos
580 cdef PyObject *next_key
581 cdef int ret
582
583 pos = 0
584 while SimpleSet_Next(self, &pos, &next_key):
585 ret = visit(next_key, arg)
586 if ret:
587 return ret
588 return 0
589
590# It is a little bit ugly to do this, but it works, and means that Meliae can
591# dump the total memory consumed by all child objects.
592(<PyTypeObject *>SimpleSet).tp_traverse = <traverseproc>SimpleSet_traverse
0593
=== added file 'bzrlib/_static_tuple_c.c'
--- bzrlib/_static_tuple_c.c 1970-01-01 00:00:00 +0000
+++ bzrlib/_static_tuple_c.c 2010-02-18 00:00:52 +0000
@@ -0,0 +1,945 @@
1/* Copyright (C) 2009, 2010 Canonical Ltd
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18/* Must be defined before importing _static_tuple_c.h so that we get the right
19 * linkage.
20 */
21#define STATIC_TUPLE_MODULE
22
23#include <Python.h>
24#include "python-compat.h"
25
26#include "_static_tuple_c.h"
27#include "_export_c_api.h"
28
29/* Pyrex 0.9.6.4 exports _simple_set_pyx_api as
30 * import__simple_set_pyx(), while Pyrex 0.9.8.5 and Cython 0.11.3 export them
31 * as import_bzrlib___simple_set_pyx(). As such, we just #define one to be
32 * equivalent to the other in our internal code.
33 */
34#define import__simple_set_pyx import_bzrlib___simple_set_pyx
35#include "_simple_set_pyx_api.h"
36
37#if defined(__GNUC__)
38# define inline __inline__
39#elif defined(_MSC_VER)
40# define inline __inline
41#else
42# define inline
43#endif
44
45
46/* The one and only StaticTuple with no values */
47static StaticTuple *_empty_tuple = NULL;
48static PyObject *_interned_tuples = NULL;
49
50
51static inline int
52_StaticTuple_is_interned(StaticTuple *self)
53{
54 return self->flags & STATIC_TUPLE_INTERNED_FLAG;
55}
56
57
58
59static PyObject *
60StaticTuple_as_tuple(StaticTuple *self)
61{
62 PyObject *tpl = NULL, *obj = NULL;
63 int i, len;
64
65 len = self->size;
66 tpl = PyTuple_New(len);
67 if (!tpl) {
68 /* Malloc failure */
69 return NULL;
70 }
71 for (i = 0; i < len; ++i) {
72 obj = (PyObject *)self->items[i];
73 Py_INCREF(obj);
74 PyTuple_SET_ITEM(tpl, i, obj);
75 }
76 return tpl;
77}
78
79
80static char StaticTuple_as_tuple_doc[] = "as_tuple() => tuple";
81
82static StaticTuple *
83StaticTuple_Intern(StaticTuple *self)
84{
85 PyObject *canonical_tuple = NULL;
86
87 if (_interned_tuples == NULL || _StaticTuple_is_interned(self)) {
88 Py_INCREF(self);
89 return self;
90 }
91 /* SimpleSet_Add returns whatever object is present at self
92 * or the new object if it needs to add it.
93 */
94 canonical_tuple = SimpleSet_Add(_interned_tuples, (PyObject *)self);
95 if (!canonical_tuple) {
96 // Some sort of exception, propogate it.
97 return NULL;
98 }
99 if (canonical_tuple != (PyObject *)self) {
100 // There was already a tuple with that value
101 return (StaticTuple *)canonical_tuple;
102 }
103 self->flags |= STATIC_TUPLE_INTERNED_FLAG;
104 // The two references in the dict do not count, so that the StaticTuple
105 // object does not become immortal just because it was interned.
106 Py_REFCNT(self) -= 1;
107 return self;
108}
109
110static char StaticTuple_Intern_doc[] = "intern() => unique StaticTuple\n"
111 "Return a 'canonical' StaticTuple object.\n"
112 "Similar to intern() for strings, this makes sure there\n"
113 "is only one StaticTuple object for a given value\n."
114 "Common usage is:\n"
115 " key = StaticTuple('foo', 'bar').intern()\n";
116
117
118static void
119StaticTuple_dealloc(StaticTuple *self)
120{
121 int i, len;
122
123 if (_StaticTuple_is_interned(self)) {
124 /* revive dead object temporarily for Discard */
125 Py_REFCNT(self) = 2;
126 if (SimpleSet_Discard(_interned_tuples, (PyObject*)self) != 1)
127 Py_FatalError("deletion of interned StaticTuple failed");
128 self->flags &= ~STATIC_TUPLE_INTERNED_FLAG;
129 }
130 len = self->size;
131 for (i = 0; i < len; ++i) {
132 Py_XDECREF(self->items[i]);
133 }
134 Py_TYPE(self)->tp_free((PyObject *)self);
135}
136
137
138/* Similar to PyTuple_New() */
139static StaticTuple *
140StaticTuple_New(Py_ssize_t size)
141{
142 StaticTuple *stuple;
143
144 if (size < 0 || size > 255) {
145 /* Too big or too small */
146 PyErr_SetString(PyExc_ValueError, "StaticTuple(...)"
147 " takes from 0 to 255 items");
148 return NULL;
149 }
150 if (size == 0 && _empty_tuple != NULL) {
151 Py_INCREF(_empty_tuple);
152 return _empty_tuple;
153 }
154 /* Note that we use PyObject_NewVar because we want to allocate a variable
155 * width entry. However we *aren't* truly a PyVarObject because we don't
156 * use a long for ob_size. Instead we use a plain 'size' that is an int,
157 * and will be overloaded with flags in the future.
158 * As such we do the alloc, and then have to clean up anything it does
159 * incorrectly.
160 */
161 stuple = PyObject_NewVar(StaticTuple, &StaticTuple_Type, size);
162 if (stuple == NULL) {
163 return NULL;
164 }
165 stuple->size = size;
166 stuple->flags = 0;
167 stuple->_unused0 = 0;
168 stuple->_unused1 = 0;
169 if (size > 0) {
170 memset(stuple->items, 0, sizeof(PyObject *) * size);
171 }
172#if STATIC_TUPLE_HAS_HASH
173 stuple->hash = -1;
174#endif
175 return stuple;
176}
177
178
179static StaticTuple *
180StaticTuple_FromSequence(PyObject *sequence)
181{
182 StaticTuple *new = NULL;
183 PyObject *as_tuple = NULL;
184 PyObject *item;
185 Py_ssize_t i, size;
186
187 if (StaticTuple_CheckExact(sequence)) {
188 Py_INCREF(sequence);
189 return (StaticTuple *)sequence;
190 }
191 if (!PySequence_Check(sequence)) {
192 as_tuple = PySequence_Tuple(sequence);
193 if (as_tuple == NULL)
194 goto done;
195 sequence = as_tuple;
196 }
197 size = PySequence_Size(sequence);
198 if (size == -1) {
199 goto done;
200 }
201 new = StaticTuple_New(size);
202 if (new == NULL) {
203 goto done;
204 }
205 for (i = 0; i < size; ++i) {
206 // This returns a new reference, which we then 'steal' with
207 // StaticTuple_SET_ITEM
208 item = PySequence_GetItem(sequence, i);
209 if (item == NULL) {
210 Py_DECREF(new);
211 new = NULL;
212 goto done;
213 }
214 StaticTuple_SET_ITEM(new, i, item);
215 }
216done:
217 Py_XDECREF(as_tuple);
218 return (StaticTuple *)new;
219}
220
221static StaticTuple *
222StaticTuple_from_sequence(PyObject *self, PyObject *args, PyObject *kwargs)
223{
224 PyObject *sequence;
225 if (!PyArg_ParseTuple(args, "O", &sequence))
226 return NULL;
227 return StaticTuple_FromSequence(sequence);
228}
229
230
231/* Check that all items we point to are 'valid' */
232static int
233StaticTuple_check_items(StaticTuple *self)
234{
235 int i;
236 PyObject *obj;
237
238 for (i = 0; i < self->size; ++i) {
239 obj = self->items[i];
240 if (obj == NULL) {
241 PyErr_SetString(PyExc_RuntimeError, "StaticTuple(...)"
242 " should not have a NULL entry.");
243 return 0;
244 }
245 if (PyString_CheckExact(obj)
246 || StaticTuple_CheckExact(obj)
247 || obj == Py_None
248 || PyBool_Check(obj)
249 || PyInt_CheckExact(obj)
250 || PyLong_CheckExact(obj)
251 || PyFloat_CheckExact(obj)
252 || PyUnicode_CheckExact(obj)
253 ) continue;
254 PyErr_Format(PyExc_TypeError, "StaticTuple(...)"
255 " requires that all items are one of"
256 " str, StaticTuple, None, bool, int, long, float, or unicode"
257 " not %s.", Py_TYPE(obj)->tp_name);
258 return 0;
259 }
260 return 1;
261}
262
263static PyObject *
264StaticTuple_new_constructor(PyTypeObject *type, PyObject *args, PyObject *kwds)
265{
266 StaticTuple *self;
267 PyObject *obj = NULL;
268 Py_ssize_t i, len = 0;
269
270 if (type != &StaticTuple_Type) {
271 PyErr_SetString(PyExc_TypeError, "we only support creating StaticTuple");
272 return NULL;
273 }
274 if (!PyTuple_CheckExact(args)) {
275 PyErr_SetString(PyExc_TypeError, "args must be a tuple");
276 return NULL;
277 }
278 len = PyTuple_GET_SIZE(args);
279 if (len < 0 || len > 255) {
280 /* Check the length here so we can raise a TypeError instead of
281 * StaticTuple_New's ValueError.
282 */
283 PyErr_SetString(PyExc_TypeError, "StaticTuple(...)"
284 " takes from 0 to 255 items");
285 return NULL;
286 }
287 self = (StaticTuple *)StaticTuple_New(len);
288 if (self == NULL) {
289 return NULL;
290 }
291 for (i = 0; i < len; ++i) {
292 obj = PyTuple_GET_ITEM(args, i);
293 Py_INCREF(obj);
294 self->items[i] = obj;
295 }
296 if (!StaticTuple_check_items(self)) {
297 type->tp_dealloc((PyObject *)self);
298 return NULL;
299 }
300 return (PyObject *)self;
301}
302
303static PyObject *
304StaticTuple_repr(StaticTuple *self)
305{
306 PyObject *as_tuple, *tuple_repr, *result;
307
308 as_tuple = StaticTuple_as_tuple(self);
309 if (as_tuple == NULL) {
310 return NULL;
311 }
312 tuple_repr = PyObject_Repr(as_tuple);
313 Py_DECREF(as_tuple);
314 if (tuple_repr == NULL) {
315 return NULL;
316 }
317 result = PyString_FromFormat("StaticTuple%s",
318 PyString_AsString(tuple_repr));
319 return result;
320}
321
322static long
323StaticTuple_hash(StaticTuple *self)
324{
325 /* adapted from tuplehash(), is the specific hash value considered
326 * 'stable'?
327 */
328 register long x, y;
329 Py_ssize_t len = self->size;
330 PyObject **p;
331 long mult = 1000003L;
332
333#if STATIC_TUPLE_HAS_HASH
334 if (self->hash != -1) {
335 return self->hash;
336 }
337#endif
338 x = 0x345678L;
339 p = self->items;
340 // TODO: We could set specific flags if we know that, for example, all the
341 // items are strings. I haven't seen a real-world benefit to that
342 // yet, though.
343 while (--len >= 0) {
344 y = PyObject_Hash(*p++);
345 if (y == -1) /* failure */
346 return -1;
347 x = (x ^ y) * mult;
348 /* the cast might truncate len; that doesn't change hash stability */
349 mult += (long)(82520L + len + len);
350 }
351 x += 97531L;
352 if (x == -1)
353 x = -2;
354#if STATIC_TUPLE_HAS_HASH
355 self->hash = x;
356#endif
357 return x;
358}
359
360static PyObject *
361StaticTuple_richcompare_to_tuple(StaticTuple *v, PyObject *wt, int op)
362{
363 PyObject *vt;
364 PyObject *result = NULL;
365
366 vt = StaticTuple_as_tuple((StaticTuple *)v);
367 if (vt == NULL) {
368 goto done;
369 }
370 if (!PyTuple_Check(wt)) {
371 PyErr_BadInternalCall();
372 goto done;
373 }
374 /* Now we have 2 tuples to compare, do it */
375 result = PyTuple_Type.tp_richcompare(vt, wt, op);
376done:
377 Py_XDECREF(vt);
378 return result;
379}
380
381/** Compare two objects to determine if they are equivalent.
382 * The basic flow is as follows
383 * 1) First make sure that both objects are StaticTuple instances. If they
384 * aren't then cast self to a tuple, and have the tuple do the comparison.
385 * 2) Special case comparison to Py_None, because it happens to occur fairly
386 * often in the test suite.
387 * 3) Special case when v and w are the same pointer. As we know the answer to
388 * all queries without walking individual items.
389 * 4) For all operations, we then walk the items to find the first paired
390 * items that are not equal.
391 * 5) If all items found are equal, we then check the length of self and
392 * other to determine equality.
393 * 6) If an item differs, then we apply "op" to those last two items. (eg.
394 * StaticTuple(A, B) > StaticTuple(A, C) iff B > C)
395 */
396
397static PyObject *
398StaticTuple_richcompare(PyObject *v, PyObject *w, int op)
399{
400 StaticTuple *v_st, *w_st;
401 Py_ssize_t vlen, wlen, min_len, i;
402 PyObject *v_obj, *w_obj;
403 richcmpfunc string_richcompare;
404
405 if (!StaticTuple_CheckExact(v)) {
406 /* This has never triggered, according to python-dev it seems this
407 * might trigger if '__op__' is defined but '__rop__' is not, sort of
408 * case. Such as "None == StaticTuple()"
409 */
410 fprintf(stderr, "self is not StaticTuple\n");
411 Py_INCREF(Py_NotImplemented);
412 return Py_NotImplemented;
413 }
414 v_st = (StaticTuple *)v;
415 if (StaticTuple_CheckExact(w)) {
416 /* The most common case */
417 w_st = (StaticTuple*)w;
418 } else if (PyTuple_Check(w)) {
419 /* One of v or w is a tuple, so we go the 'slow' route and cast up to
420 * tuples to compare.
421 */
422 /* TODO: This seems to be triggering more than I thought it would...
423 * We probably want to optimize comparing self to other when
424 * other is a tuple.
425 */
426 return StaticTuple_richcompare_to_tuple(v_st, w, op);
427 } else if (w == Py_None) {
428 // None is always less than the object
429 switch (op) {
430 case Py_NE:case Py_GT:case Py_GE:
431 Py_INCREF(Py_True);
432 return Py_True;
433 case Py_EQ:case Py_LT:case Py_LE:
434 Py_INCREF(Py_False);
435 return Py_False;
436 default: // Should never happen
437 return Py_NotImplemented;
438 }
439 } else {
440 /* We don't special case this comparison, we just let python handle
441 * it.
442 */
443 Py_INCREF(Py_NotImplemented);
444 return Py_NotImplemented;
445 }
446 /* Now we know that we have 2 StaticTuple objects, so let's compare them.
447 * This code is inspired from tuplerichcompare, except we know our
448 * objects are limited in scope, so we can inline some comparisons.
449 */
450 if (v == w) {
451 /* Identical pointers, we can shortcut this easily. */
452 switch (op) {
453 case Py_EQ:case Py_LE:case Py_GE:
454 Py_INCREF(Py_True);
455 return Py_True;
456 case Py_NE:case Py_LT:case Py_GT:
457 Py_INCREF(Py_False);
458 return Py_False;
459 }
460 }
461 if (op == Py_EQ
462 && _StaticTuple_is_interned(v_st)
463 && _StaticTuple_is_interned(w_st))
464 {
465 /* If both objects are interned, we know they are different if the
466 * pointer is not the same, which would have been handled by the
467 * previous if. No need to compare the entries.
468 */
469 Py_INCREF(Py_False);
470 return Py_False;
471 }
472
473 /* The only time we are likely to compare items of different lengths is in
474 * something like the interned_keys set. However, the hash is good enough
475 * that it is rare. Note that 'tuple_richcompare' also does not compare
476 * lengths here.
477 */
478 vlen = v_st->size;
479 wlen = w_st->size;
480 min_len = (vlen < wlen) ? vlen : wlen;
481 string_richcompare = PyString_Type.tp_richcompare;
482 for (i = 0; i < min_len; i++) {
483 PyObject *result = NULL;
484 v_obj = StaticTuple_GET_ITEM(v_st, i);
485 w_obj = StaticTuple_GET_ITEM(w_st, i);
486 if (v_obj == w_obj) {
487 /* Shortcut case, these must be identical */
488 continue;
489 }
490 if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj)) {
491 result = string_richcompare(v_obj, w_obj, Py_EQ);
492 } else if (StaticTuple_CheckExact(v_obj) &&
493 StaticTuple_CheckExact(w_obj))
494 {
495 /* Both are StaticTuple types, so recurse */
496 result = StaticTuple_richcompare(v_obj, w_obj, Py_EQ);
497 } else {
498 /* Fall back to generic richcompare */
499 result = PyObject_RichCompare(v_obj, w_obj, Py_EQ);
500 }
501 if (result == NULL) {
502 return NULL; /* There seems to be an error */
503 }
504 if (result == Py_False) {
505 // This entry is not identical, Shortcut for Py_EQ
506 if (op == Py_EQ) {
507 return result;
508 }
509 Py_DECREF(result);
510 break;
511 }
512 if (result != Py_True) {
513 /* We don't know *what* richcompare is returning, but it
514 * isn't something we recognize
515 */
516 PyErr_BadInternalCall();
517 Py_DECREF(result);
518 return NULL;
519 }
520 Py_DECREF(result);
521 }
522 if (i >= min_len) {
523 /* We walked off one of the lists, but everything compared equal so
524 * far. Just compare the size.
525 */
526 int cmp;
527 PyObject *res;
528 switch (op) {
529 case Py_LT: cmp = vlen < wlen; break;
530 case Py_LE: cmp = vlen <= wlen; break;
531 case Py_EQ: cmp = vlen == wlen; break;
532 case Py_NE: cmp = vlen != wlen; break;
533 case Py_GT: cmp = vlen > wlen; break;
534 case Py_GE: cmp = vlen >= wlen; break;
535 default: return NULL; /* cannot happen */
536 }
537 if (cmp)
538 res = Py_True;
539 else
540 res = Py_False;
541 Py_INCREF(res);
542 return res;
543 }
544 /* The last item differs, shortcut the Py_NE case */
545 if (op == Py_NE) {
546 Py_INCREF(Py_True);
547 return Py_True;
548 }
549 /* It is some other comparison, go ahead and do the real check. */
550 if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj))
551 {
552 return string_richcompare(v_obj, w_obj, op);
553 } else if (StaticTuple_CheckExact(v_obj) &&
554 StaticTuple_CheckExact(w_obj))
555 {
556 /* Both are StaticTuple types, so recurse */
557 return StaticTuple_richcompare(v_obj, w_obj, op);
558 } else {
559 return PyObject_RichCompare(v_obj, w_obj, op);
560 }
561}
562
563
564static Py_ssize_t
565StaticTuple_length(StaticTuple *self)
566{
567 return self->size;
568}
569
570
571static PyObject *
572StaticTuple__is_interned(StaticTuple *self)
573{
574 if (_StaticTuple_is_interned(self)) {
575 Py_INCREF(Py_True);
576 return Py_True;
577 }
578 Py_INCREF(Py_False);
579 return Py_False;
580}
581
582static char StaticTuple__is_interned_doc[] = "_is_interned() => True/False\n"
583 "Check to see if this tuple has been interned.\n";
584
585
586static PyObject *
587StaticTuple_reduce(StaticTuple *self)
588{
589 PyObject *result = NULL, *as_tuple = NULL;
590
591 result = PyTuple_New(2);
592 if (!result) {
593 return NULL;
594 }
595 as_tuple = StaticTuple_as_tuple(self);
596 if (as_tuple == NULL) {
597 Py_DECREF(result);
598 return NULL;
599 }
600 Py_INCREF(&StaticTuple_Type);
601 PyTuple_SET_ITEM(result, 0, (PyObject *)&StaticTuple_Type);
602 PyTuple_SET_ITEM(result, 1, as_tuple);
603 return result;
604}
605
606static char StaticTuple_reduce_doc[] = "__reduce__() => tuple\n";
607
608
609static PyObject *
610StaticTuple_add(PyObject *v, PyObject *w)
611{
612 Py_ssize_t i, len_v, len_w;
613 PyObject *item;
614 StaticTuple *result;
615 /* StaticTuples and plain tuples may be added (concatenated) to
616 * StaticTuples.
617 */
618 if (StaticTuple_CheckExact(v)) {
619 len_v = ((StaticTuple*)v)->size;
620 } else if (PyTuple_Check(v)) {
621 len_v = PyTuple_GET_SIZE(v);
622 } else {
623 Py_INCREF(Py_NotImplemented);
624 return Py_NotImplemented;
625 }
626 if (StaticTuple_CheckExact(w)) {
627 len_w = ((StaticTuple*)w)->size;
628 } else if (PyTuple_Check(w)) {
629 len_w = PyTuple_GET_SIZE(w);
630 } else {
631 Py_INCREF(Py_NotImplemented);
632 return Py_NotImplemented;
633 }
634 result = StaticTuple_New(len_v + len_w);
635 if (result == NULL)
636 return NULL;
637 for (i = 0; i < len_v; ++i) {
638 // This returns a new reference, which we then 'steal' with
639 // StaticTuple_SET_ITEM
640 item = PySequence_GetItem(v, i);
641 if (item == NULL) {
642 Py_DECREF(result);
643 return NULL;
644 }
645 StaticTuple_SET_ITEM(result, i, item);
646 }
647 for (i = 0; i < len_w; ++i) {
648 item = PySequence_GetItem(w, i);
649 if (item == NULL) {
650 Py_DECREF(result);
651 return NULL;
652 }
653 StaticTuple_SET_ITEM(result, i+len_v, item);
654 }
655 if (!StaticTuple_check_items(result)) {
656 Py_DECREF(result);
657 return NULL;
658 }
659 return (PyObject *)result;
660}
661
662static PyObject *
663StaticTuple_item(StaticTuple *self, Py_ssize_t offset)
664{
665 PyObject *obj;
666 /* We cast to (int) to avoid worrying about whether Py_ssize_t is a
667 * long long, etc. offsets should never be >2**31 anyway.
668 */
669 if (offset < 0) {
670 PyErr_Format(PyExc_IndexError, "StaticTuple_item does not support"
671 " negative indices: %d\n", (int)offset);
672 } else if (offset >= self->size) {
673 PyErr_Format(PyExc_IndexError, "StaticTuple index out of range"
674 " %d >= %d", (int)offset, (int)self->size);
675 return NULL;
676 }
677 obj = (PyObject *)self->items[offset];
678 Py_INCREF(obj);
679 return obj;
680}
681
682static PyObject *
683StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)
684{
685 PyObject *as_tuple, *result;
686
687 as_tuple = StaticTuple_as_tuple(self);
688 if (as_tuple == NULL) {
689 return NULL;
690 }
691 result = PyTuple_Type.tp_as_sequence->sq_slice(as_tuple, ilow, ihigh);
692 Py_DECREF(as_tuple);
693 return result;
694}
695
696static int
697StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)
698{
699 Py_ssize_t i;
700 for (i = self->size; --i >= 0;) {
701 Py_VISIT(self->items[i]);
702 }
703 return 0;
704}
705
706static char StaticTuple_doc[] =
707 "C implementation of a StaticTuple structure."
708 "\n This is used as StaticTuple(item1, item2, item3)"
709 "\n This is similar to tuple, less flexible in what it"
710 "\n supports, but also lighter memory consumption."
711 "\n Note that the constructor mimics the () form of tuples"
712 "\n Rather than the 'tuple()' constructor."
713 "\n eg. StaticTuple(a, b) == (a, b) == tuple((a, b))";
714
715static PyMethodDef StaticTuple_methods[] = {
716 {"as_tuple", (PyCFunction)StaticTuple_as_tuple, METH_NOARGS, StaticTuple_as_tuple_doc},
717 {"intern", (PyCFunction)StaticTuple_Intern, METH_NOARGS, StaticTuple_Intern_doc},
718 {"_is_interned", (PyCFunction)StaticTuple__is_interned, METH_NOARGS,
719 StaticTuple__is_interned_doc},
720 {"from_sequence", (PyCFunction)StaticTuple_from_sequence,
721 METH_STATIC | METH_VARARGS,
722 "Create a StaticTuple from a given sequence. This functions"
723 " the same as the tuple() constructor."},
724 {"__reduce__", (PyCFunction)StaticTuple_reduce, METH_NOARGS, StaticTuple_reduce_doc},
725 {NULL, NULL} /* sentinel */
726};
727
728
729static PyNumberMethods StaticTuple_as_number = {
730 (binaryfunc) StaticTuple_add, /* nb_add */
731 0, /* nb_subtract */
732 0, /* nb_multiply */
733 0, /* nb_divide */
734 0, /* nb_remainder */
735 0, /* nb_divmod */
736 0, /* nb_power */
737 0, /* nb_negative */
738 0, /* nb_positive */
739 0, /* nb_absolute */
740 0, /* nb_nonzero */
741 0, /* nb_invert */
742 0, /* nb_lshift */
743 0, /* nb_rshift */
744 0, /* nb_and */
745 0, /* nb_xor */
746 0, /* nb_or */
747 0, /* nb_coerce */
748};
749
750
751static PySequenceMethods StaticTuple_as_sequence = {
752 (lenfunc)StaticTuple_length, /* sq_length */
753 0, /* sq_concat */
754 0, /* sq_repeat */
755 (ssizeargfunc)StaticTuple_item, /* sq_item */
756 (ssizessizeargfunc)StaticTuple_slice, /* sq_slice */
757 0, /* sq_ass_item */
758 0, /* sq_ass_slice */
759 0, /* sq_contains */
760};
761
762/* TODO: Implement StaticTuple_as_mapping.
763 * The only thing we really want to support from there is mp_subscript,
764 * so that we could support extended slicing (foo[::2]). Not worth it
765 * yet, though.
766 */
767
768
769PyTypeObject StaticTuple_Type = {
770 PyObject_HEAD_INIT(NULL)
771 0, /* ob_size */
772 "bzrlib._static_tuple_c.StaticTuple", /* tp_name */
773 sizeof(StaticTuple), /* tp_basicsize */
774 sizeof(PyObject *), /* tp_itemsize */
775 (destructor)StaticTuple_dealloc, /* tp_dealloc */
776 0, /* tp_print */
777 0, /* tp_getattr */
778 0, /* tp_setattr */
779 0, /* tp_compare */
780 (reprfunc)StaticTuple_repr, /* tp_repr */
781 &StaticTuple_as_number, /* tp_as_number */
782 &StaticTuple_as_sequence, /* tp_as_sequence */
783 0, /* tp_as_mapping */
784 (hashfunc)StaticTuple_hash, /* tp_hash */
785 0, /* tp_call */
786 0, /* tp_str */
787 0, /* tp_getattro */
788 0, /* tp_setattro */
789 0, /* tp_as_buffer */
790 /* Py_TPFLAGS_CHECKTYPES tells the number operations that they shouldn't
791 * try to 'coerce' but instead stuff like 'add' will check it arguments.
792 */
793 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags*/
794 StaticTuple_doc, /* tp_doc */
795 /* gc.get_referents checks the IS_GC flag before it calls tp_traverse
796 * And we don't include this object in the garbage collector because we
797 * know it doesn't create cycles. However, 'meliae' will follow
798 * tp_traverse, even if the object isn't GC, and we want that.
799 */
800 (traverseproc)StaticTuple_traverse, /* tp_traverse */
801 0, /* tp_clear */
802 StaticTuple_richcompare, /* tp_richcompare */
803 0, /* tp_weaklistoffset */
804 // without implementing tp_iter, Python will fall back to PySequence*
805 // which seems to work ok, we may need something faster/lighter in the
806 // future.
807 0, /* tp_iter */
808 0, /* tp_iternext */
809 StaticTuple_methods, /* tp_methods */
810 0, /* tp_members */
811 0, /* tp_getset */
812 0, /* tp_base */
813 0, /* tp_dict */
814 0, /* tp_descr_get */
815 0, /* tp_descr_set */
816 0, /* tp_dictoffset */
817 0, /* tp_init */
818 0, /* tp_alloc */
819 StaticTuple_new_constructor, /* tp_new */
820};
821
822
823static PyMethodDef static_tuple_c_methods[] = {
824 {NULL, NULL}
825};
826
827
828static void
829setup_interned_tuples(PyObject *m)
830{
831 _interned_tuples = (PyObject *)SimpleSet_New();
832 if (_interned_tuples != NULL) {
833 Py_INCREF(_interned_tuples);
834 PyModule_AddObject(m, "_interned_tuples", _interned_tuples);
835 }
836}
837
838
839static void
840setup_empty_tuple(PyObject *m)
841{
842 StaticTuple *stuple;
843 if (_interned_tuples == NULL) {
844 fprintf(stderr, "You need to call setup_interned_tuples() before"
845 " setup_empty_tuple, because we intern it.\n");
846 }
847 // We need to create the empty tuple
848 stuple = (StaticTuple *)StaticTuple_New(0);
849 _empty_tuple = StaticTuple_Intern(stuple);
850 assert(_empty_tuple == stuple);
851 // At this point, refcnt is 2: 1 from New(), and 1 from the return from
852 // intern(). We will keep 1 for the _empty_tuple global, and use the other
853 // for the module reference.
854 PyModule_AddObject(m, "_empty_tuple", (PyObject *)_empty_tuple);
855}
856
857static int
858_StaticTuple_CheckExact(PyObject *obj)
859{
860 return StaticTuple_CheckExact(obj);
861}
862
863static void
864setup_c_api(PyObject *m)
865{
866 _export_function(m, "StaticTuple_New", StaticTuple_New,
867 "StaticTuple *(Py_ssize_t)");
868 _export_function(m, "StaticTuple_Intern", StaticTuple_Intern,
869 "StaticTuple *(StaticTuple *)");
870 _export_function(m, "StaticTuple_FromSequence", StaticTuple_FromSequence,
871 "StaticTuple *(PyObject *)");
872 _export_function(m, "_StaticTuple_CheckExact", _StaticTuple_CheckExact,
873 "int(PyObject *)");
874}
875
876
877static int
878_workaround_pyrex_096(void)
879{
880 /* Work around an incompatibility in how pyrex 0.9.6 exports a module,
881 * versus how pyrex 0.9.8 and cython 0.11 export it.
882 * Namely 0.9.6 exports import__simple_set_pyx and tries to
883 * "import _simple_set_pyx" but it is available only as
884 * "import bzrlib._simple_set_pyx"
885 * It is a shame to hack up sys.modules, but that is what we've got to do.
886 */
887 PyObject *sys_module = NULL, *modules = NULL, *set_module = NULL;
888 int retval = -1;
889
890 /* Clear out the current ImportError exception, and try again. */
891 PyErr_Clear();
892 /* Note that this only seems to work if somewhere else imports
893 * bzrlib._simple_set_pyx before importing bzrlib._static_tuple_c
894 */
895 set_module = PyImport_ImportModule("bzrlib._simple_set_pyx");
896 if (set_module == NULL) {
897 goto end;
898 }
899 /* Add the _simple_set_pyx into sys.modules at the appropriate location. */
900 sys_module = PyImport_ImportModule("sys");
901 if (sys_module == NULL) {
902 goto end;
903 }
904 modules = PyObject_GetAttrString(sys_module, "modules");
905 if (modules == NULL || !PyDict_Check(modules)) {
906 goto end;
907 }
908 PyDict_SetItemString(modules, "_simple_set_pyx", set_module);
909 /* Now that we have hacked it in, try the import again. */
910 retval = import_bzrlib___simple_set_pyx();
911end:
912 Py_XDECREF(set_module);
913 Py_XDECREF(sys_module);
914 Py_XDECREF(modules);
915 return retval;
916}
917
918
919PyMODINIT_FUNC
920init_static_tuple_c(void)
921{
922 PyObject* m;
923
924 StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;
925 if (PyType_Ready(&StaticTuple_Type) < 0)
926 return;
927
928 m = Py_InitModule3("_static_tuple_c", static_tuple_c_methods,
929 "C implementation of a StaticTuple structure");
930 if (m == NULL)
931 return;
932
933 Py_INCREF(&StaticTuple_Type);
934 PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);
935 if (import_bzrlib___simple_set_pyx() == -1
936 && _workaround_pyrex_096() == -1)
937 {
938 return;
939 }
940 setup_interned_tuples(m);
941 setup_empty_tuple(m);
942 setup_c_api(m);
943}
944
945// vim: tabstop=4 sw=4 expandtab
0946
=== added file 'bzrlib/_static_tuple_c.h'
--- bzrlib/_static_tuple_c.h 1970-01-01 00:00:00 +0000
+++ bzrlib/_static_tuple_c.h 2010-02-18 00:00:51 +0000
@@ -0,0 +1,116 @@
1/* Copyright (C) 2009 Canonical Ltd
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18#ifndef _STATIC_TUPLE_H_
19#define _STATIC_TUPLE_H_
20#include <Python.h>
21#include <string.h>
22
23#define STATIC_TUPLE_HAS_HASH 0
24/* Caching the hash adds memory, but allows us to save a little time during
25 * lookups. TIMEIT hash(key) shows it as
26 * 0.108usec w/ hash
27 * 0.160usec w/o hash
28 * Note that the entries themselves are strings, which already cache their
29 * hashes. So while there is a 1.5:1 difference in the time for hash(), it is
30 * already a function which is quite fast. Probably the only reason we might
31 * want to do so, is if we customized SimpleSet to the point that the item
32 * pointers were exactly certain types, and then accessed table[i]->hash
33 * directly. So far StaticTuple_hash() is fast enough to not warrant the memory
34 * difference.
35 */
36
37/* This defines a single variable-width key.
38 * It is basically the same as a tuple, but
39 * 1) Lighter weight in memory
40 * 2) Only supports strings or other static types (that don't reference other
41 * objects.)
42 */
43
44#define STATIC_TUPLE_INTERNED_FLAG 0x01
45typedef struct {
46 PyObject_HEAD
47 // We could go with unsigned short here, and support 64k width tuples
48 // without any memory impact, might be worthwhile
49 unsigned char size;
50 unsigned char flags;
51 unsigned char _unused0;
52 unsigned char _unused1;
53 // Note that on 64-bit, we actually have 4-more unused bytes
54 // because items will always be aligned to a 64-bit boundary
55#if STATIC_TUPLE_HAS_HASH
56 long hash;
57#endif
58 PyObject *items[0];
59} StaticTuple;
60extern PyTypeObject StaticTuple_Type;
61
62typedef struct {
63 PyObject_VAR_HEAD
64 PyObject *table[0];
65} KeyIntern;
66
67#define StaticTuple_SET_ITEM(key, offset, val) \
68 ((((StaticTuple*)(key))->items[(offset)]) = ((PyObject *)(val)))
69#define StaticTuple_GET_ITEM(key, offset) (((StaticTuple*)key)->items[offset])
70
71
72#ifdef STATIC_TUPLE_MODULE
73/* Used when compiling _static_tuple_c.c */
74
75static StaticTuple * StaticTuple_New(Py_ssize_t);
76static StaticTuple * StaticTuple_Intern(StaticTuple *self);
77static StaticTuple * StaticTuple_FromSequence(PyObject *);
78#define StaticTuple_CheckExact(op) (Py_TYPE(op) == &StaticTuple_Type)
79
80#else
81/* Used as the foreign api */
82
83#include "_import_c_api.h"
84
85static StaticTuple *(*StaticTuple_New)(Py_ssize_t);
86static StaticTuple *(*StaticTuple_Intern)(StaticTuple *);
87static StaticTuple *(*StaticTuple_FromSequence)(PyObject *);
88static PyTypeObject *_p_StaticTuple_Type;
89
90#define StaticTuple_CheckExact(op) (Py_TYPE(op) == _p_StaticTuple_Type)
91static int (*_StaticTuple_CheckExact)(PyObject *);
92
93
94/* Return -1 and set exception on error, 0 on success */
95static int
96import_static_tuple_c(void)
97{
98 struct function_description functions[] = {
99 {"StaticTuple_New", (void **)&StaticTuple_New,
100 "StaticTuple *(Py_ssize_t)"},
101 {"StaticTuple_Intern", (void **)&StaticTuple_Intern,
102 "StaticTuple *(StaticTuple *)"},
103 {"StaticTuple_FromSequence", (void **)&StaticTuple_FromSequence,
104 "StaticTuple *(PyObject *)"},
105 {"_StaticTuple_CheckExact", (void **)&_StaticTuple_CheckExact,
106 "int(PyObject *)"},
107 {NULL}};
108 struct type_description types[] = {
109 {"StaticTuple", &_p_StaticTuple_Type},
110 {NULL}};
111 return _import_extension_module("bzrlib._static_tuple_c",
112 functions, types);
113}
114
115#endif // !STATIC_TUPLE_MODULE
116#endif // !_STATIC_TUPLE_H_
0117
=== added file 'bzrlib/_static_tuple_c.pxd'
--- bzrlib/_static_tuple_c.pxd 1970-01-01 00:00:00 +0000
+++ bzrlib/_static_tuple_c.pxd 2010-02-18 00:00:51 +0000
@@ -0,0 +1,44 @@
1# Copyright (C) 2009, 2010 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""The interface definition file for the StaticTuple class."""
18
19
20cdef extern from "Python.h":
21 ctypedef int Py_ssize_t # Required for older pyrex versions
22 ctypedef struct PyObject:
23 pass
24
25cdef extern from "_static_tuple_c.h":
26 ctypedef class bzrlib._static_tuple_c.StaticTuple [object StaticTuple]:
27 cdef unsigned char size
28 cdef unsigned char flags
29 cdef PyObject *items[0]
30
31 # Must be called before using any of the C api, as it sets the function
32 # pointers in memory.
33 int import_static_tuple_c() except -1
34 StaticTuple StaticTuple_New(Py_ssize_t)
35 StaticTuple StaticTuple_Intern(StaticTuple)
36
37 # Steals a reference and val must be a valid type, no checking is done
38 void StaticTuple_SET_ITEM(StaticTuple key, Py_ssize_t offset, object val)
39 # We would normally use PyObject * here. However it seems that cython/pyrex
40 # treat the PyObject defined in this header as something different than one
41 # defined in a .pyx file. And since we don't INCREF, we need a raw pointer,
42 # not an 'object' return value.
43 void *StaticTuple_GET_ITEM(StaticTuple key, Py_ssize_t offset)
44 int StaticTuple_CheckExact(object)
045
=== added file 'bzrlib/_static_tuple_py.py'
--- bzrlib/_static_tuple_py.py 1970-01-01 00:00:00 +0000
+++ bzrlib/_static_tuple_py.py 2010-02-18 00:00:52 +0000
@@ -0,0 +1,80 @@
1# Copyright (C) 2009, 2010 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""The pure-python implementation of the StaticTuple type.
18
19Note that it is generally just implemented as using tuples of tuples of
20strings.
21"""
22
23
24class StaticTuple(tuple):
25 """A static type, similar to a tuple of strings."""
26
27 __slots__ = ()
28
29 def __new__(cls, *args):
30 # Make the empty StaticTuple a singleton
31 if not args and _empty_tuple is not None:
32 return _empty_tuple
33 return tuple.__new__(cls, args)
34
35 def __init__(self, *args):
36 """Create a new 'StaticTuple'"""
37 num_keys = len(args)
38 if num_keys < 0 or num_keys > 255:
39 raise TypeError('StaticTuple(...) takes from 0 to 255 items')
40 for bit in args:
41 if type(bit) not in (str, StaticTuple, unicode, int, long, float,
42 None.__class__, bool):
43 raise TypeError('StaticTuple can only point to'
44 ' StaticTuple, str, unicode, int, long, float, bool, or'
45 ' None not %s' % (type(bit),))
46 # We don't need to pass args to tuple.__init__, because that was
47 # already handled in __new__.
48 tuple.__init__(self)
49
50 def __repr__(self):
51 return '%s%s' % (self.__class__.__name__, tuple.__repr__(self))
52
53 def __reduce__(self):
54 return (StaticTuple, tuple(self))
55
56 def __add__(self, other):
57 """Concatenate self with other"""
58 return StaticTuple.from_sequence(tuple.__add__(self,other))
59
60 def as_tuple(self):
61 return tuple(self)
62
63 def intern(self):
64 return _interned_tuples.setdefault(self, self)
65
66 @staticmethod
67 def from_sequence(seq):
68 """Convert a sequence object into a StaticTuple instance."""
69 if isinstance(seq, StaticTuple):
70 # it already is
71 return seq
72 return StaticTuple(*seq)
73
74
75
76# Have to set it to None first, so that __new__ can determine whether
77# the _empty_tuple singleton has been created yet or not.
78_empty_tuple = None
79_empty_tuple = StaticTuple()
80_interned_tuples = {}
081
=== modified file 'bzrlib/_walkdirs_win32.pyx'
--- bzrlib/_walkdirs_win32.pyx 2010-01-05 04:59:57 +0000
+++ bzrlib/_walkdirs_win32.pyx 2010-02-18 00:00:51 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2008 Canonical Ltd1# Copyright (C) 2008, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
55
=== modified file 'bzrlib/annotate.py'
--- bzrlib/annotate.py 2009-08-17 18:52:01 +0000
+++ bzrlib/annotate.py 2010-02-18 00:00:51 +0000
@@ -458,5 +458,6 @@
458458
459try:459try:
460 from bzrlib._annotator_pyx import Annotator460 from bzrlib._annotator_pyx import Annotator
461except ImportError:461except ImportError, e:
462 osutils.failed_to_load_extension(e)
462 from bzrlib._annotator_py import Annotator463 from bzrlib._annotator_py import Annotator
463464
=== modified file 'bzrlib/benchmarks/bench_dirstate.py'
--- bzrlib/benchmarks/bench_dirstate.py 2009-06-22 15:39:42 +0000
+++ bzrlib/benchmarks/bench_dirstate.py 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2007 Canonical Ltd1# Copyright (C) 2007, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
26 tests,26 tests,
27 )27 )
28from bzrlib.tests.test__dirstate_helpers import (28from bzrlib.tests.test__dirstate_helpers import (
29 CompiledDirstateHelpersFeature,29 compiled_dirstate_helpers_feature,
30 )30 )
3131
3232
@@ -206,7 +206,7 @@
206 state.unlock()206 state.unlock()
207207
208 def test__read_dirblocks_20k_tree_0_parents_pyx(self):208 def test__read_dirblocks_20k_tree_0_parents_pyx(self):
209 self.requireFeature(CompiledDirstateHelpersFeature)209 self.requireFeature(compiled_dirstate_helpers_feature)
210 from bzrlib._dirstate_helpers_pyx import _read_dirblocks210 from bzrlib._dirstate_helpers_pyx import _read_dirblocks
211 state = self.build_20k_dirstate()211 state = self.build_20k_dirstate()
212 state.lock_read()212 state.lock_read()
@@ -231,7 +231,7 @@
231 state.unlock()231 state.unlock()
232232
233 def test__read_dirblocks_20k_tree_1_parent_pyx(self):233 def test__read_dirblocks_20k_tree_1_parent_pyx(self):
234 self.requireFeature(CompiledDirstateHelpersFeature)234 self.requireFeature(compiled_dirstate_helpers_feature)
235 from bzrlib._dirstate_helpers_pyx import _read_dirblocks235 from bzrlib._dirstate_helpers_pyx import _read_dirblocks
236 state = self.build_20k_dirstate_with_parents(1)236 state = self.build_20k_dirstate_with_parents(1)
237 state.lock_read()237 state.lock_read()
@@ -256,7 +256,7 @@
256 state.unlock()256 state.unlock()
257257
258 def test__read_dirblocks_20k_tree_2_parents_pyx(self):258 def test__read_dirblocks_20k_tree_2_parents_pyx(self):
259 self.requireFeature(CompiledDirstateHelpersFeature)259 self.requireFeature(compiled_dirstate_helpers_feature)
260 from bzrlib._dirstate_helpers_pyx import _read_dirblocks260 from bzrlib._dirstate_helpers_pyx import _read_dirblocks
261 state = self.build_20k_dirstate_with_parents(2)261 state = self.build_20k_dirstate_with_parents(2)
262 state.lock_read()262 state.lock_read()
@@ -337,7 +337,7 @@
337 state.unlock()337 state.unlock()
338338
339 def test_bisect_dirblock_pyx(self):339 def test_bisect_dirblock_pyx(self):
340 self.requireFeature(CompiledDirstateHelpersFeature)340 self.requireFeature(compiled_dirstate_helpers_feature)
341 from bzrlib._dirstate_helpers_pyx import bisect_dirblock341 from bzrlib._dirstate_helpers_pyx import bisect_dirblock
342 state = self.build_10k_dirstate_dirs()342 state = self.build_10k_dirstate_dirs()
343 state.lock_read()343 state.lock_read()
@@ -420,7 +420,7 @@
420 [(3, 1), (3, 1), (3, 1), (3, 2)])420 [(3, 1), (3, 1), (3, 1), (3, 2)])
421421
422 def test_cmp_by_dirs_pyrex(self):422 def test_cmp_by_dirs_pyrex(self):
423 self.requireFeature(CompiledDirstateHelpersFeature)423 self.requireFeature(compiled_dirstate_helpers_feature)
424 from bzrlib._dirstate_helpers_pyx import cmp_by_dirs424 from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
425 self.compareAllPaths(cmp_by_dirs,425 self.compareAllPaths(cmp_by_dirs,
426 [(3, 1), (3, 1), (3, 1), (3, 2)])426 [(3, 1), (3, 1), (3, 1), (3, 2)])
427427
=== modified file 'bzrlib/bencode.py'
--- bzrlib/bencode.py 2009-06-03 14:14:31 +0000
+++ bzrlib/bencode.py 2010-02-18 00:00:52 +0000
@@ -16,7 +16,10 @@
1616
17"""Wrapper around the bencode pyrex and python implementation"""17"""Wrapper around the bencode pyrex and python implementation"""
1818
19from bzrlib import osutils
20
19try:21try:
20 from bzrlib._bencode_pyx import bdecode, bdecode_as_tuple, bencode, Bencached22 from bzrlib._bencode_pyx import bdecode, bdecode_as_tuple, bencode, Bencached
21except ImportError:23except ImportError, e:
24 osutils.failed_to_load_extension(e)
22 from bzrlib.util._bencode_py import bdecode, bdecode_as_tuple, bencode, Bencached25 from bzrlib.util._bencode_py import bdecode, bdecode_as_tuple, bencode, Bencached
2326
=== modified file 'bzrlib/branch.py'
--- bzrlib/branch.py 2009-11-23 07:10:47 +0000
+++ bzrlib/branch.py 2010-02-18 00:00:52 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd1# Copyright (C) 2005-2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -46,9 +46,10 @@
46 )46 )
47""")47""")
4848
49from bzrlib.decorators import needs_read_lock, needs_write_lock49from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50from bzrlib.hooks import HookPoint, Hooks50from bzrlib.hooks import HookPoint, Hooks
51from bzrlib.inter import InterObject51from bzrlib.inter import InterObject
52from bzrlib.lock import _RelockDebugMixin
52from bzrlib import registry53from bzrlib import registry
53from bzrlib.symbol_versioning import (54from bzrlib.symbol_versioning import (
54 deprecated_in,55 deprecated_in,
@@ -502,12 +503,25 @@
502 left_parent = stop_rev.parent_ids[0]503 left_parent = stop_rev.parent_ids[0]
503 else:504 else:
504 left_parent = _mod_revision.NULL_REVISION505 left_parent = _mod_revision.NULL_REVISION
506 # left_parent is the actual revision we want to stop logging at,
507 # since we want to show the merged revisions after the stop_rev too
508 reached_stop_revision_id = False
509 revision_id_whitelist = []
505 for node in rev_iter:510 for node in rev_iter:
506 rev_id = node.key[-1]511 rev_id = node.key[-1]
507 if rev_id == left_parent:512 if rev_id == left_parent:
513 # reached the left parent after the stop_revision
508 return514 return
509 yield (rev_id, node.merge_depth, node.revno,515 if (not reached_stop_revision_id or
516 rev_id in revision_id_whitelist):
517 yield (rev_id, node.merge_depth, node.revno,
510 node.end_of_merge)518 node.end_of_merge)
519 if reached_stop_revision_id or rev_id == stop_revision_id:
520 # only do the merged revs of rev_id from now on
521 rev = self.repository.get_revision(rev_id)
522 if rev.parent_ids:
523 reached_stop_revision_id = True
524 revision_id_whitelist.extend(rev.parent_ids)
511 else:525 else:
512 raise ValueError('invalid stop_rule %r' % stop_rule)526 raise ValueError('invalid stop_rule %r' % stop_rule)
513527
@@ -1089,15 +1103,7 @@
1089 params = ChangeBranchTipParams(1103 params = ChangeBranchTipParams(
1090 self, old_revno, new_revno, old_revid, new_revid)1104 self, old_revno, new_revno, old_revid, new_revid)
1091 for hook in hooks:1105 for hook in hooks:
1092 try:1106 hook(params)
1093 hook(params)
1094 except errors.TipChangeRejected:
1095 raise
1096 except Exception:
1097 exc_info = sys.exc_info()
1098 hook_name = Branch.hooks.get_hook_name(hook)
1099 raise errors.HookFailed(
1100 'pre_change_branch_tip', hook_name, exc_info)
11011107
1102 @needs_write_lock1108 @needs_write_lock
1103 def update(self):1109 def update(self):
@@ -1432,10 +1438,10 @@
1432 """Return the format for the branch object in a_bzrdir."""1438 """Return the format for the branch object in a_bzrdir."""
1433 try:1439 try:
1434 transport = a_bzrdir.get_branch_transport(None)1440 transport = a_bzrdir.get_branch_transport(None)
1435 format_string = transport.get("format").read()1441 format_string = transport.get_bytes("format")
1436 return klass._formats[format_string]1442 return klass._formats[format_string]
1437 except errors.NoSuchFile:1443 except errors.NoSuchFile:
1438 raise errors.NotBranchError(path=transport.base)1444 raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1439 except KeyError:1445 except KeyError:
1440 raise errors.UnknownFormatError(format=format_string, kind='branch')1446 raise errors.UnknownFormatError(format=format_string, kind='branch')
14411447
@@ -1790,7 +1796,7 @@
1790 _repository=a_bzrdir.find_repository(),1796 _repository=a_bzrdir.find_repository(),
1791 ignore_fallbacks=ignore_fallbacks)1797 ignore_fallbacks=ignore_fallbacks)
1792 except errors.NoSuchFile:1798 except errors.NoSuchFile:
1793 raise errors.NotBranchError(path=transport.base)1799 raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
17941800
1795 def __init__(self):1801 def __init__(self):
1796 super(BranchFormatMetadir, self).__init__()1802 super(BranchFormatMetadir, self).__init__()
@@ -1971,7 +1977,7 @@
1971 def get_reference(self, a_bzrdir):1977 def get_reference(self, a_bzrdir):
1972 """See BranchFormat.get_reference()."""1978 """See BranchFormat.get_reference()."""
1973 transport = a_bzrdir.get_branch_transport(None)1979 transport = a_bzrdir.get_branch_transport(None)
1974 return transport.get('location').read()1980 return transport.get_bytes('location')
19751981
1976 def set_reference(self, a_bzrdir, to_branch):1982 def set_reference(self, a_bzrdir, to_branch):
1977 """See BranchFormat.set_reference()."""1983 """See BranchFormat.set_reference()."""
@@ -2072,7 +2078,7 @@
2072 _legacy_formats[0].network_name(), _legacy_formats[0].__class__)2078 _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
20732079
20742080
2075class BzrBranch(Branch):2081class BzrBranch(Branch, _RelockDebugMixin):
2076 """A branch stored in the actual filesystem.2082 """A branch stored in the actual filesystem.
20772083
2078 Note that it's "local" in the context of the filesystem; it doesn't2084 Note that it's "local" in the context of the filesystem; it doesn't
@@ -2124,9 +2130,12 @@
2124 return self.control_files.is_locked()2130 return self.control_files.is_locked()
21252131
2126 def lock_write(self, token=None):2132 def lock_write(self, token=None):
2133 if not self.is_locked():
2134 self._note_lock('w')
2127 # All-in-one needs to always unlock/lock.2135 # All-in-one needs to always unlock/lock.
2128 repo_control = getattr(self.repository, 'control_files', None)2136 repo_control = getattr(self.repository, 'control_files', None)
2129 if self.control_files == repo_control or not self.is_locked():2137 if self.control_files == repo_control or not self.is_locked():
2138 self.repository._warn_if_deprecated(self)
2130 self.repository.lock_write()2139 self.repository.lock_write()
2131 took_lock = True2140 took_lock = True
2132 else:2141 else:
@@ -2139,9 +2148,12 @@
2139 raise2148 raise
21402149
2141 def lock_read(self):2150 def lock_read(self):
2151 if not self.is_locked():
2152 self._note_lock('r')
2142 # All-in-one needs to always unlock/lock.2153 # All-in-one needs to always unlock/lock.
2143 repo_control = getattr(self.repository, 'control_files', None)2154 repo_control = getattr(self.repository, 'control_files', None)
2144 if self.control_files == repo_control or not self.is_locked():2155 if self.control_files == repo_control or not self.is_locked():
2156 self.repository._warn_if_deprecated(self)
2145 self.repository.lock_read()2157 self.repository.lock_read()
2146 took_lock = True2158 took_lock = True
2147 else:2159 else:
@@ -2153,6 +2165,7 @@
2153 self.repository.unlock()2165 self.repository.unlock()
2154 raise2166 raise
21552167
2168 @only_raises(errors.LockNotHeld, errors.LockBroken)
2156 def unlock(self):2169 def unlock(self):
2157 try:2170 try:
2158 self.control_files.unlock()2171 self.control_files.unlock()
21592172
=== modified file 'bzrlib/btree_index.py'
--- bzrlib/btree_index.py 2009-09-09 19:32:27 +0000
+++ bzrlib/btree_index.py 2010-02-18 00:00:50 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2008 Canonical Ltd1# Copyright (C) 2008, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
1717
18"""B+Tree indices"""18"""B+Tree indices"""
1919
20import cStringIO
20from bisect import bisect_right21from bisect import bisect_right
21import math22import math
22import tempfile23import tempfile
@@ -30,6 +31,7 @@
30 index,31 index,
31 lru_cache,32 lru_cache,
32 osutils,33 osutils,
34 static_tuple,
33 trace,35 trace,
34 )36 )
35from bzrlib.index import _OPTION_NODE_REFS, _OPTION_KEY_ELEMENTS, _OPTION_LEN37from bzrlib.index import _OPTION_NODE_REFS, _OPTION_KEY_ELEMENTS, _OPTION_LEN
@@ -60,14 +62,20 @@
60 def __init__(self):62 def __init__(self):
61 """Create a _BuilderRow."""63 """Create a _BuilderRow."""
62 self.nodes = 064 self.nodes = 0
63 self.spool = tempfile.TemporaryFile()65 self.spool = None# tempfile.TemporaryFile(prefix='bzr-index-row-')
64 self.writer = None66 self.writer = None
6567
66 def finish_node(self, pad=True):68 def finish_node(self, pad=True):
67 byte_lines, _, padding = self.writer.finish()69 byte_lines, _, padding = self.writer.finish()
68 if self.nodes == 0:70 if self.nodes == 0:
71 self.spool = cStringIO.StringIO()
69 # padded note:72 # padded note:
70 self.spool.write("\x00" * _RESERVED_HEADER_BYTES)73 self.spool.write("\x00" * _RESERVED_HEADER_BYTES)
74 elif self.nodes == 1:
75 # We got bigger than 1 node, switch to a temp file
76 spool = tempfile.TemporaryFile(prefix='bzr-index-row-')
77 spool.write(self.spool.getvalue())
78 self.spool = spool
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches