14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
from __future__ import absolute_import
20
from bzrlib.errors import (
20
32
binary_files_re = 'Binary files (.*) and (.*) differ\n'
23
class BinaryFiles(Exception):
25
def __init__(self, orig_name, mod_name):
26
self.orig_name = orig_name
27
self.mod_name = mod_name
28
Exception.__init__(self, 'Binary files section encountered.')
31
class PatchSyntax(Exception):
32
def __init__(self, msg):
33
Exception.__init__(self, msg)
36
class MalformedPatchHeader(PatchSyntax):
37
def __init__(self, desc, line):
40
msg = "Malformed patch header. %s\n%r" % (self.desc, self.line)
41
PatchSyntax.__init__(self, msg)
44
class MalformedHunkHeader(PatchSyntax):
45
def __init__(self, desc, line):
48
msg = "Malformed hunk header. %s\n%r" % (self.desc, self.line)
49
PatchSyntax.__init__(self, msg)
52
class MalformedLine(PatchSyntax):
53
def __init__(self, desc, line):
56
msg = "Malformed line. %s\n%s" % (self.desc, self.line)
57
PatchSyntax.__init__(self, msg)
60
class PatchConflict(Exception):
61
def __init__(self, line_no, orig_line, patch_line):
62
orig = orig_line.rstrip('\n')
63
patch = str(patch_line).rstrip('\n')
64
msg = 'Text contents mismatch at line %d. Original has "%s",'\
65
' but patch says it should be "%s"' % (line_no, orig, patch)
66
Exception.__init__(self, msg)
69
34
def get_patch_names(iter_lines):
71
36
line = iter_lines.next()
253
def iter_hunks(iter_lines):
218
def iter_hunks(iter_lines, allow_dirty=False):
220
:arg iter_lines: iterable of lines to parse for hunks
221
:kwarg allow_dirty: If True, when we encounter something that is not
222
a hunk header when we're looking for one, assume the rest of the lines
223
are not part of the patch (comments or other junk). Default False
255
226
for line in iter_lines:
261
232
if hunk is not None:
263
hunk = hunk_from_header(line)
235
hunk = hunk_from_header(line)
236
except MalformedHunkHeader:
238
# If the line isn't a hunk header, then we've reached the end
239
# of this patch and there's "junk" at the end. Ignore the
240
# rest of this patch.
266
245
while orig_size < hunk.orig_range or mod_size < hunk.mod_range:
342
def parse_patch(iter_lines):
321
def parse_patch(iter_lines, allow_dirty=False):
323
:arg iter_lines: iterable of lines to parse
324
:kwarg allow_dirty: If True, allow the patch to have trailing junk.
343
327
iter_lines = iter_lines_handle_nl(iter_lines)
345
329
(orig_name, mod_name) = get_patch_names(iter_lines)
347
331
return BinaryPatch(e.orig_name, e.mod_name)
349
333
patch = Patch(orig_name, mod_name)
350
for hunk in iter_hunks(iter_lines):
334
for hunk in iter_hunks(iter_lines, allow_dirty):
351
335
patch.hunks.append(hunk)
355
def iter_file_patch(iter_lines):
339
def iter_file_patch(iter_lines, allow_dirty=False):
341
:arg iter_lines: iterable of lines to parse for patches
342
:kwarg allow_dirty: If True, allow comments and other non-patch text
343
before the first patch. Note that the algorithm here can only find
344
such text before any patches have been found. Comments after the
345
first patch are stripped away in iter_hunks() if it is also passed
346
allow_dirty=True. Default False.
348
### FIXME: Docstring is not quite true. We allow certain comments no
349
# matter what, If they startwith '===', '***', or '#' Someone should
350
# reexamine this logic and decide if we should include those in
351
# allow_dirty or restrict those to only being before the patch is found
352
# (as allow_dirty does).
356
353
regex = re.compile(binary_files_re)
359
357
for line in iter_lines:
360
358
if line.startswith('=== ') or line.startswith('*** '):
365
363
if line.startswith('-') or line.startswith(' '):
367
365
elif line.startswith('--- ') or regex.match(line):
368
if len(saved_lines) > 0:
366
if allow_dirty and beginning:
367
# Patches can have "junk" at the beginning
368
# Stripping junk from the end of patches is handled when we
371
elif len(saved_lines) > 0:
369
372
yield saved_lines
371
374
elif line.startswith('@@'):
400
def parse_patches(iter_lines):
401
return [parse_patch(f.__iter__()) for f in iter_file_patch(iter_lines)]
403
def parse_patches(iter_lines, allow_dirty=False):
405
:arg iter_lines: iterable of lines to parse for patches
406
:kwarg allow_dirty: If True, allow text that's not part of the patch at
407
selected places. This includes comments before and after a patch
408
for instance. Default False.
410
return [parse_patch(f.__iter__(), allow_dirty) for f in
411
iter_file_patch(iter_lines, allow_dirty)]
404
414
def difference_index(atext, btext):