1
# Copyright (C) 2010 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
"""Logic to create commit templates."""
19
from __future__ import absolute_import
21
from ... import bugtracker, osutils, patiencediff
24
_BUG_MATCH = re.compile(r'lp:(\d+)')
27
class CommitTemplate(object):
29
def __init__(self, commit, message, filespec):
30
"""Create a commit template for commit with initial message message.
32
:param commit: A Commit object for the in progress commit.
33
:param message: The current message (which may be None).
34
:param filespec: List of files to match
37
self.message = message
38
self.filespec = filespec
43
If NEWS is missing or not not modified, the original template is
44
returned unaltered. Otherwise the changes from NEWS are concatenated
45
with whatever message was provided to __init__.
47
delta = self.commit.builder.get_basis_delta()
50
for old_path, new_path, fileid, entry in delta:
51
if new_path in self.filespec:
53
found_old_path = old_path
57
if found_old_path is None:
60
self.commit.builder.repository.iter_files_bytes(
61
[(found_entry.file_id, found_entry.revision, None)]))[0]
62
content = ''.join(new_chunks)
63
return self.merge_message(content)
65
# Get a diff. XXX Is this hookable? I thought it was, can't find it
66
# though.... add DiffTree.diff_factories. Sadly thats not at the
67
# right level: we want to identify the changed lines, not have the
68
# final diff: because we want to grab the sections for regions
69
# changed in new version of the file. So for now a direct diff
70
# using patiencediff is done.
71
old_revision = self.commit.basis_tree.get_file_revision(
73
needed = [(found_entry.file_id, found_entry.revision, 'new'),
74
(found_entry.file_id, old_revision, 'old')]
75
contents = self.commit.builder.repository.iter_files_bytes(needed)
77
for name, chunks in contents:
78
lines[name] = osutils.chunks_to_lines(chunks)
80
sequence_matcher = patiencediff.PatienceSequenceMatcher(
81
None, lines['old'], new)
83
for group in sequence_matcher.get_opcodes():
84
tag, i1, i2, j1, j2 = group
89
new_lines.extend(new[j1:j2])
90
if not self.commit.revprops.get('bugs'):
91
# TODO: Allow the user to configure the bug tracker to use
92
# rather than hardcoding Launchpad.
93
bt = bugtracker.tracker_registry.get('launchpad')
95
for line in new_lines:
96
bugids.extend(_BUG_MATCH.findall(line))
97
self.commit.revprops['bugs'] = \
98
bugtracker.encode_fixes_bug_urls(
99
[bt.get_bug_url(bugid) for bugid in bugids])
100
return self.merge_message(''.join(new_lines))
102
def merge_message(self, new_message):
103
"""Merge new_message with self.message.
105
:param new_message: A string message to merge with self.message.
106
:return: A string with the merged messages.
108
if self.message is None:
110
return self.message + new_message