/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/plugins/commitfromnews/committemplate.py

  • Committer: Robert Collins
  • Date: 2010-05-06 23:41:35 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506234135-yivbzczw1sejxnxc
Lock methods on ``Tree``, ``Branch`` and ``Repository`` are now
expected to return an object which can be used to unlock them. This reduces
duplicate code when using cleanups. The previous 'tokens's returned by
``Branch.lock_write`` and ``Repository.lock_write`` are now attributes
on the result of the lock_write. ``repository.RepositoryWriteLockResult``
and ``branch.BranchWriteLockResult`` document this. (Robert Collins)

``log._get_info_for_log_files`` now takes an add_cleanup callable.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2010 Canonical Ltd
2
 
#
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.
7
 
#
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.
12
 
#
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
16
 
 
17
 
"""Logic to create commit templates."""
18
 
 
19
 
from __future__ import absolute_import
20
 
 
21
 
import patiencediff
22
 
 
23
 
from ... import bugtracker, osutils
24
 
import re
25
 
 
26
 
_BUG_MATCH = re.compile(r'lp:(\d+)')
27
 
 
28
 
 
29
 
class CommitTemplate(object):
30
 
 
31
 
    def __init__(self, commit, message, filespec):
32
 
        """Create a commit template for commit with initial message message.
33
 
 
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
37
 
        """
38
 
        self.commit = commit
39
 
        self.message = message
40
 
        self.filespec = filespec
41
 
 
42
 
    def make(self):
43
 
        """Make the template.
44
 
 
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__.
48
 
        """
49
 
        delta = self.commit.builder.get_basis_delta()
50
 
        found_old_path = None
51
 
        found_entry = None
52
 
        for old_path, new_path, fileid, entry in delta:
53
 
            if new_path in self.filespec:
54
 
                found_entry = entry
55
 
                found_old_path = old_path
56
 
                break
57
 
        if not found_entry:
58
 
            return self.message
59
 
        if found_old_path is None:
60
 
            # New file
61
 
            _, new_chunks = list(
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)
66
 
        else:
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)
77
 
            lines = {}
78
 
            for name, chunks in contents:
79
 
                lines[name] = osutils.chunks_to_lines(chunks)
80
 
            new = lines['new']
81
 
            sequence_matcher = patiencediff.PatienceSequenceMatcher(
82
 
                None, lines['old'], new)
83
 
            new_lines = []
84
 
            for group in sequence_matcher.get_opcodes():
85
 
                tag, i1, i2, j1, j2 = group
86
 
                if tag == 'equal':
87
 
                    continue
88
 
                if tag == 'delete':
89
 
                    continue
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')
95
 
                bugids = []
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))
103
 
 
104
 
    def merge_message(self, new_message):
105
 
        """Merge new_message with self.message.
106
 
 
107
 
        :param new_message: A string message to merge with self.message.
108
 
        :return: A string with the merged messages.
109
 
        """
110
 
        if self.message is None:
111
 
            return new_message
112
 
        return self.message + new_message