6
6
import bzrlib, bzrlib.changeset
9
class BadChangeset(Exception):
11
class MalformedHeader(BadChangeset):
13
class MalformedFooter(BadChangeset):
9
class BadChangeset(Exception): pass
10
class MalformedHeader(BadChangeset): pass
11
class MalformedPatches(BadChangeset): pass
12
class MalformedFooter(BadChangeset): pass
17
15
class ChangesetInfo(object):
18
"""This is the intermediate class that gets filled out as the file is read.
16
"""This is the intermediate class that gets filled out as
20
19
def __init__(self):
21
20
self.committer = None
42
41
return pprint.pformat(self.__dict__)
44
43
def create_maps(self):
45
"""Go through the individual id sections, and generate the id2path and path2id maps.
44
"""Go through the individual id sections, and generate the
45
id2path and path2id maps.
47
47
# Rather than use an empty path, the changeset code seems
48
48
# to like to use "./." for the tree root.
58
58
self.path2id[path] = f_id
59
59
self.id2parent[f_id] = parent_id
62
61
class ChangesetReader(object):
63
"""This class reads in a changeset from a file, and returns a Changeset object,
64
which can then be applied against a tree.
62
"""This class reads in a changeset from a file, and returns
63
a Changeset object, which can then be applied against a tree.
66
65
def __init__(self, from_file):
67
66
"""Read in the changeset from the file.
90
90
"""Read the bzr header"""
91
91
header = common.get_header()
92
92
for head_line, line in zip(header, self.from_file):
93
if line[:2] != '# ' or line[-1] != '\n' or line[2:-1] != head_line:
94
raise MalformedHeader('Did not read the opening header information.')
95
or line[2:-1] != head_line):
96
raise MalformedHeader('Did not read the opening'
97
' header information.')
96
99
for line in self.from_file:
97
100
if self._handle_info_line(line) is not None:
100
103
def _handle_info_line(self, line, in_footer=False):
101
104
"""Handle reading a single line.
103
This may call itself, in the case that we read_multi, and then had a dangling
106
This may call itself, in the case that we read_multi,
107
and then had a dangling line on the end.
106
# The bzr header is terminated with a blank line which does not start with #
109
# The bzr header is terminated with a blank line
110
# which does not start with #
108
112
if line[:1] == '\n':
123
127
value = line[loc+2:]
129
value, next_line = self._read_many()
125
131
if line[-1:] == ':':
127
133
value, next_line = self._read_many()
129
raise MalformedHeader('While looking for key: value pairs, did not find the : %r' % (line))
135
raise MalformedHeader('While looking for key: value pairs,'
136
' did not find the colon %r' % (line))
131
138
key = key.replace(' ', '_')
132
139
if hasattr(self.info, key):
142
149
self._handle_info_line(next_line, in_footer=in_footer)
144
151
def _read_many(self):
145
"""If a line ends with no entry, that means that it should be followed with
146
multiple lines of values.
152
"""If a line ends with no entry, that means that it should be
153
followed with multiple lines of values.
148
This detects the end of the list, because it will be a line that does not
149
start with '# '. Because it has to read that extra line, it returns the
150
tuple: (values, next_line)
155
This detects the end of the list, because it will be a line that
156
does not start with '# '. Because it has to read that extra
157
line, it returns the tuple: (values, next_line)
153
160
for line in self.from_file:
156
163
values.append(line[5:-1])
157
164
return values, None
166
def _read_one_patch(self, first_line=None):
167
"""Read in one patch, return the complete patch, along with
170
:return: action, lines, next_line, do_continue
175
def parse_firstline(line):
178
if line[:3] != '***':
179
raise MalformedPatches('The first line of all patches'
180
' should be a bzr meta line "***"')
183
if first_line is not None:
184
action = parse_firstline(first_line)
187
return None, [], first_line, False
190
for line in self.from_file:
192
action = parse_firstline(line)
195
return None, [], line, False
197
if line[:3] == '***':
198
return action, lines, line, True
199
elif line[:1] == '#':
200
return action, lines, line, False
202
return action, lines, None, False
159
204
def _read_patches(self):
160
for line in self.from_file:
161
if line[:3] == '***': # This is a bzr meta field
162
self._parse_meta(line)
163
elif line[:3] == '---': # This is the 'pre' line for a pre+post patch
165
elif line[:3] == '+++': # This is the 'post' line for the pre+post patch
208
action, lines, next_line, do_continue = \
209
self._read_one_patch(next_line)
210
if action is not None:
211
self.info.actions.append((action, lines))
170
214
def _read_footer(self, first_line=None):
171
215
"""Read the rest of the meta information.
173
:param first_line: The previous step may iterate passed what it can handle.
174
That extra line can be passed here.
217
:param first_line: The previous step iterates past what it
218
can handle. That extra line is given here.
176
220
if first_line is not None:
177
self._handle_info_line(first_line, in_footer=True)
221
if self._handle_info_line(first_line, in_footer=True) is not None:
178
223
for line in self.from_file:
179
224
if self._handle_info_line(line, in_footer=True) is not None: