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 ..workingtree import WorkingTree
30
from ..commands import (
34
from ..option import Option
35
from ..sixish import PY3
41
class cmd_dump_btree(Command):
42
__doc__ = """Dump the contents of a btree index file to stdout.
44
PATH is a btree index file, it can be any URL. This includes things like
45
.bzr/repository/pack-names, or .bzr/repository/indices/a34b3a...ca4a4.iix
47
By default, the tuples stored in the index file will be displayed. With
48
--raw, we will uncompress the pages, but otherwise display the raw bytes
52
# TODO: Do we want to dump the internal nodes as well?
53
# TODO: It would be nice to be able to dump the un-parsed information,
54
# rather than only going through iter_all_entries. However, this is
55
# good enough for a start
57
encoding_type = 'exact'
59
takes_options = [Option('raw', help='Write the uncompressed bytes out,'
60
' rather than the parsed tuples.'),
63
def run(self, path, raw=False):
64
dirname, basename = osutils.split(path)
65
t = transport.get_transport(dirname)
67
self._dump_raw_bytes(t, basename)
69
self._dump_entries(t, basename)
71
def _get_index_and_bytes(self, trans, basename):
72
"""Create a BTreeGraphIndex and raw bytes."""
73
bt = btree_index.BTreeGraphIndex(trans, basename, None)
74
bytes = trans.get_bytes(basename)
75
bt._file = BytesIO(bytes)
79
def _dump_raw_bytes(self, trans, basename):
82
# We need to parse at least the root node.
83
# This is because the first page of every row starts with an
84
# uncompressed header.
85
bt, bytes = self._get_index_and_bytes(trans, basename)
86
for page_idx, page_start in enumerate(range(0, len(bytes),
87
btree_index._PAGE_SIZE)):
88
page_end = min(page_start + btree_index._PAGE_SIZE, len(bytes))
89
page_bytes = bytes[page_start:page_end]
91
self.outf.write('Root node:\n')
92
header_end, data = bt._parse_header_from_bytes(page_bytes)
93
self.outf.write(page_bytes[:header_end])
95
self.outf.write('\nPage %d\n' % (page_idx,))
96
if len(page_bytes) == 0:
97
self.outf.write('(empty)\n')
99
decomp_bytes = zlib.decompress(page_bytes)
100
self.outf.write(decomp_bytes)
101
self.outf.write('\n')
103
def _dump_entries(self, trans, basename):
105
st = trans.stat(basename)
106
except errors.TransportNotPossible:
107
# We can't stat, so we'll fake it because we have to do the 'get()'
109
bt, _ = self._get_index_and_bytes(trans, basename)
111
bt = btree_index.BTreeGraphIndex(trans, basename, st.st_size)
112
for node in bt.iter_all_entries():
113
# Node is made up of:
114
# (index, key, value, [references])
118
refs_as_tuples = None
120
refs_as_tuples = static_tuple.as_tuples(refs)
122
if refs_as_tuples is not None:
123
refs_as_tuples = tuple(
124
tuple(tuple(r.decode('utf-8')
125
for r in t1) for t1 in t2)
126
for t2 in refs_as_tuples)
128
tuple([r.decode('utf-8') for r in node[1]]),
129
node[2].decode('utf-8'),
132
as_tuple = (tuple(node[1]), node[2], refs_as_tuples)
133
self.outf.write('%s\n' % (as_tuple,))
136
class cmd_file_id(Command):
137
__doc__ = """Print file_id of a particular file or directory.
139
The file_id is assigned when the file is first added and remains the
140
same through all revisions where the file exists, even when it is
145
_see_also = ['inventory', 'ls']
146
takes_args = ['filename']
149
def run(self, filename):
150
tree, relpath = WorkingTree.open_containing(filename)
151
file_id = tree.path2id(relpath)
153
raise errors.NotVersionedError(filename)
155
self.outf.write(file_id.decode('utf-8') + '\n')
158
class cmd_file_path(Command):
159
__doc__ = """Print path of file_ids to a file or directory.
161
This prints one line for each directory down to the target,
162
starting at the branch root.
166
takes_args = ['filename']
169
def run(self, filename):
170
tree, relpath = WorkingTree.open_containing(filename)
171
fid = tree.path2id(relpath)
173
raise errors.NotVersionedError(filename)
174
segments = osutils.splitpath(relpath)
175
for pos in range(1, len(segments) + 1):
176
path = osutils.joinpath(segments[:pos])
177
self.outf.write("%s\n" % tree.path2id(path))