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
31
from ..sixish import PY3
37
class cmd_dump_btree(Command):
38
__doc__ = """Dump the contents of a btree index file to stdout.
40
PATH is a btree index file, it can be any URL. This includes things like
41
.bzr/repository/pack-names, or .bzr/repository/indices/a34b3a...ca4a4.iix
43
By default, the tuples stored in the index file will be displayed. With
44
--raw, we will uncompress the pages, but otherwise display the raw bytes
48
# TODO: Do we want to dump the internal nodes as well?
49
# TODO: It would be nice to be able to dump the un-parsed information,
50
# rather than only going through iter_all_entries. However, this is
51
# good enough for a start
53
encoding_type = 'exact'
55
takes_options = [Option('raw', help='Write the uncompressed bytes out,'
56
' rather than the parsed tuples.'),
59
def run(self, path, raw=False):
60
dirname, basename = osutils.split(path)
61
t = transport.get_transport(dirname)
63
self._dump_raw_bytes(t, basename)
65
self._dump_entries(t, basename)
67
def _get_index_and_bytes(self, trans, basename):
68
"""Create a BTreeGraphIndex and raw bytes."""
69
bt = btree_index.BTreeGraphIndex(trans, basename, None)
70
bytes = trans.get_bytes(basename)
71
bt._file = BytesIO(bytes)
75
def _dump_raw_bytes(self, trans, basename):
78
# We need to parse at least the root node.
79
# This is because the first page of every row starts with an
80
# uncompressed header.
81
bt, bytes = self._get_index_and_bytes(trans, basename)
82
for page_idx, page_start in enumerate(range(0, len(bytes),
83
btree_index._PAGE_SIZE)):
84
page_end = min(page_start + btree_index._PAGE_SIZE, len(bytes))
85
page_bytes = bytes[page_start:page_end]
87
self.outf.write('Root node:\n')
88
header_end, data = bt._parse_header_from_bytes(page_bytes)
89
self.outf.write(page_bytes[:header_end])
91
self.outf.write('\nPage %d\n' % (page_idx,))
92
if len(page_bytes) == 0:
93
self.outf.write('(empty)\n');
95
decomp_bytes = zlib.decompress(page_bytes)
96
self.outf.write(decomp_bytes)
99
def _dump_entries(self, trans, basename):
101
st = trans.stat(basename)
102
except errors.TransportNotPossible:
103
# We can't stat, so we'll fake it because we have to do the 'get()'
105
bt, _ = self._get_index_and_bytes(trans, basename)
107
bt = btree_index.BTreeGraphIndex(trans, basename, st.st_size)
108
for node in bt.iter_all_entries():
109
# Node is made up of:
110
# (index, key, value, [references])
114
refs_as_tuples = None
116
refs_as_tuples = static_tuple.as_tuples(refs)
118
if refs_as_tuples is not None:
119
refs_as_tuples = tuple(
120
tuple(tuple(r.decode('utf-8') for r in t1) for t1 in t2)
121
for t2 in refs_as_tuples)
123
tuple([r.decode('utf-8') for r in node[1]]),
124
node[2].decode('utf-8'),
127
as_tuple = (tuple(node[1]), node[2], refs_as_tuples)
128
self.outf.write('%s\n' % (as_tuple,))