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
23
from ... import bugtracker, osutils
26
_BUG_MATCH = re.compile(r'lp:(\d+)')
29
class CommitTemplate(object):
31
def __init__(self, commit, message, filespec):
32
"""Create a commit template for commit with initial message message.
34
:param commit: A Commit object for the in progress commit.
35
:param message: The current message (which may be None).
36
:param filespec: List of files to match
39
self.message = message
40
self.filespec = filespec
45
If NEWS is missing or not not modified, the original template is
46
returned unaltered. Otherwise the changes from NEWS are concatenated
47
with whatever message was provided to __init__.
49
delta = self.commit.builder.get_basis_delta()
52
for old_path, new_path, fileid, entry in delta:
53
if new_path in self.filespec:
55
found_old_path = old_path
59
if found_old_path is None:
62
self.commit.builder.repository.iter_files_bytes(
63
[(found_entry.file_id, found_entry.revision, None)]))[0]
64
content = b''.join(new_chunks).decode('utf-8')
65
return self.merge_message(content)
67
# Get a diff. XXX Is this hookable? I thought it was, can't find it
68
# though.... add DiffTree.diff_factories. Sadly thats not at the
69
# right level: we want to identify the changed lines, not have the
70
# final diff: because we want to grab the sections for regions
71
# changed in new version of the file. So for now a direct diff
72
# using patiencediff is done.
73
old_revision = self.commit.basis_tree.get_file_revision(old_path)
74
needed = [(found_entry.file_id, found_entry.revision, 'new'),
75
(found_entry.file_id, old_revision, 'old')]
76
contents = self.commit.builder.repository.iter_files_bytes(needed)
78
for name, chunks in contents:
79
lines[name] = osutils.chunks_to_lines(chunks)
81
sequence_matcher = patiencediff.PatienceSequenceMatcher(
82
None, lines['old'], new)
84
for group in sequence_matcher.get_opcodes():
85
tag, i1, i2, j1, j2 = group
90
new_lines.extend([l.decode('utf-8') for l in new[j1:j2]])
91
if not self.commit.revprops.get('bugs'):
92
# TODO: Allow the user to configure the bug tracker to use
93
# rather than hardcoding Launchpad.
94
bt = bugtracker.tracker_registry.get('launchpad')
96
for line in new_lines:
97
bugids.extend(_BUG_MATCH.findall(line))
98
self.commit.revprops['bugs'] = \
99
bugtracker.encode_fixes_bug_urls(
100
[(bt.get_bug_url(bugid), bugtracker.FIXED)
101
for bugid in bugids])
102
return self.merge_message(''.join(new_lines))
104
def merge_message(self, new_message):
105
"""Merge new_message with self.message.
107
:param new_message: A string message to merge with self.message.
108
:return: A string with the merged messages.
110
if self.message is None:
112
return self.message + new_message