1
# Copyright (C) 2005-2012 Canonical Ltd
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.
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.
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
17
"""Debug commands for the bzr formats."""
19
from __future__ import absolute_import
21
from io import BytesIO
29
from ..commands import Command
30
from ..option import Option
36
class cmd_dump_btree(Command):
37
__doc__ = """Dump the contents of a btree index file to stdout.
39
PATH is a btree index file, it can be any URL. This includes things like
40
.bzr/repository/pack-names, or .bzr/repository/indices/a34b3a...ca4a4.iix
42
By default, the tuples stored in the index file will be displayed. With
43
--raw, we will uncompress the pages, but otherwise display the raw bytes
47
# TODO: Do we want to dump the internal nodes as well?
48
# TODO: It would be nice to be able to dump the un-parsed information,
49
# rather than only going through iter_all_entries. However, this is
50
# good enough for a start
52
encoding_type = 'exact'
54
takes_options = [Option('raw', help='Write the uncompressed bytes out,'
55
' rather than the parsed tuples.'),
58
def run(self, path, raw=False):
59
dirname, basename = osutils.split(path)
60
t = transport.get_transport(dirname)
62
self._dump_raw_bytes(t, basename)
64
self._dump_entries(t, basename)
66
def _get_index_and_bytes(self, trans, basename):
67
"""Create a BTreeGraphIndex and raw bytes."""
68
bt = btree_index.BTreeGraphIndex(trans, basename, None)
69
bytes = trans.get_bytes(basename)
70
bt._file = BytesIO(bytes)
74
def _dump_raw_bytes(self, trans, basename):
77
# We need to parse at least the root node.
78
# This is because the first page of every row starts with an
79
# uncompressed header.
80
bt, bytes = self._get_index_and_bytes(trans, basename)
81
for page_idx, page_start in enumerate(range(0, len(bytes),
82
btree_index._PAGE_SIZE)):
83
page_end = min(page_start + btree_index._PAGE_SIZE, len(bytes))
84
page_bytes = bytes[page_start:page_end]
86
self.outf.write('Root node:\n')
87
header_end, data = bt._parse_header_from_bytes(page_bytes)
88
self.outf.write(page_bytes[:header_end])
90
self.outf.write('\nPage %d\n' % (page_idx,))
91
if len(page_bytes) == 0:
92
self.outf.write('(empty)\n');
94
decomp_bytes = zlib.decompress(page_bytes)
95
self.outf.write(decomp_bytes)
98
def _dump_entries(self, trans, basename):
100
st = trans.stat(basename)
101
except errors.TransportNotPossible:
102
# We can't stat, so we'll fake it because we have to do the 'get()'
104
bt, _ = self._get_index_and_bytes(trans, basename)
106
bt = btree_index.BTreeGraphIndex(trans, basename, st.st_size)
107
for node in bt.iter_all_entries():
108
# Node is made up of:
109
# (index, key, value, [references])
113
refs_as_tuples = None
115
refs_as_tuples = static_tuple.as_tuples(refs)
116
as_tuple = (tuple(node[1]), node[2], refs_as_tuples)
117
self.outf.write('%s\n' % (as_tuple,))