~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/version_info_formats/__init__.py

  • Committer: John Arbash Meinel
  • Date: 2006-09-20 14:51:03 UTC
  • mfrom: (0.8.23 version_info)
  • mto: This revision was merged to the branch mainline in revision 2028.
  • Revision ID: john@arbash-meinel.com-20060920145103-02725c6d6c886040
[merge] version-info plugin, and cleanup for layout in bzr

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Routines for extracting all version information from a bzr branch."""
 
18
 
 
19
import time
 
20
 
 
21
from bzrlib.osutils import local_time_offset, format_date
 
22
 
 
23
 
 
24
# This contains a map of format id => formatter
 
25
# None is considered the default formatter
 
26
_version_formats = {}
 
27
 
 
28
def create_date_str(timestamp=None, offset=None):
 
29
    """Just a wrapper around format_date to provide the right format.
 
30
 
 
31
    We don't want to use '%a' in the time string, because it is locale
 
32
    dependant. We also want to force timezone original, and show_offset
 
33
 
 
34
    Without parameters this function yields the current date in the local
 
35
    time zone.
 
36
    """
 
37
    if timestamp is None and offset is None:
 
38
        timestamp = time.time()
 
39
        offset = local_time_offset()
 
40
    return format_date(timestamp, offset, date_fmt='%Y-%m-%d %H:%M:%S',
 
41
                       timezone='original', show_offset=True)
 
42
 
 
43
 
 
44
class VersionInfoBuilder(object):
 
45
    """A class which lets you build up information about a revision."""
 
46
 
 
47
    def __init__(self, branch, working_tree=None,
 
48
                check_for_clean=False,
 
49
                include_revision_history=False,
 
50
                include_file_revisions=False,
 
51
                ):
 
52
        """Build up information about the given branch.
 
53
        If working_tree is given, it can be checked for changes.
 
54
 
 
55
        :param branch: The branch to work on
 
56
        :param working_tree: If supplied, preferentially check
 
57
            the working tree for changes.
 
58
        :param check_for_clean: If False, we will skip the expense
 
59
            of looking for changes.
 
60
        :param include_revision_history: If True, the output
 
61
            will include the full mainline revision history, including
 
62
            date and message
 
63
        :param include_file_revisions: The output should
 
64
            include the explicit last-changed revision for each file.
 
65
        """
 
66
        self._branch = branch
 
67
        self._working_tree = working_tree
 
68
        self._check = check_for_clean
 
69
        self._include_history = include_revision_history
 
70
        self._include_file_revs = include_file_revisions
 
71
 
 
72
        self._clean = None
 
73
        self._file_revisions = {}
 
74
        self._revision_history_info= []
 
75
 
 
76
    def _extract_file_revisions(self):
 
77
        """Extract the working revisions for all files"""
 
78
 
 
79
        # Things seem clean if we never look :)
 
80
        self._clean = True
 
81
 
 
82
        if self._working_tree is not None:
 
83
            basis_tree = self._working_tree.basis_tree()
 
84
        else:
 
85
            basis_tree = self._branch.basis_tree()
 
86
 
 
87
        # Build up the list from the basis inventory
 
88
        for info in basis_tree.list_files():
 
89
            self._file_revisions[info[0]] = info[-1].revision
 
90
 
 
91
        if not self._check or self._working_tree is None:
 
92
            return
 
93
 
 
94
        # We have both a working tree, and we are checking
 
95
        # COMPATIBILITY:
 
96
        # bzr < 0.9 did not have Tree.changes_from
 
97
        try:
 
98
            delta = self._working_tree.changes_from(basis_tree)
 
99
        except AttributeError:
 
100
            import bzrlib.delta
 
101
            delta = bzrlib.delta.compare_trees(basis_tree, self._working_tree)
 
102
 
 
103
        # Using a 2-pass algorithm for renames. This is because you might have
 
104
        # renamed something out of the way, and then created a new file
 
105
        # in which case we would rather see the new marker
 
106
        # Or you might have removed the target, and then renamed
 
107
        # in which case we would rather see the renamed marker
 
108
        for (old_path, new_path, file_id,
 
109
             kind, text_mod, meta_mod) in delta.renamed:
 
110
            self._clean = False
 
111
            self._file_revisions[old_path] = u'renamed to %s' % (new_path,)
 
112
        for path, file_id, kind in delta.removed:
 
113
            self._clean = False
 
114
            self._file_revisions[path] = 'removed'
 
115
        for path, file_id, kind in delta.added:
 
116
            self._clean = False
 
117
            self._file_revisions[path] = 'new'
 
118
        for (old_path, new_path, file_id,
 
119
             kind, text_mod, meta_mod) in delta.renamed:
 
120
            self._clean = False
 
121
            self._file_revisions[new_path] = u'renamed from %s' % (old_path,)
 
122
        for path, file_id, kind, text_mod, meta_mod in delta.modified:
 
123
            self._clean = False
 
124
            self._file_revisions[path] = 'modified'
 
125
 
 
126
        for path in self._working_tree.unknowns():
 
127
            self._clean = False
 
128
            self._file_revisions[path] = 'unversioned'
 
129
 
 
130
    def _extract_revision_history(self):
 
131
        """Find the messages for all revisions in history."""
 
132
 
 
133
        # Unfortunately, there is no WorkingTree.revision_history
 
134
        rev_hist = self._branch.revision_history()
 
135
        if self._working_tree is not None:
 
136
            last_rev = self._working_tree.last_revision()
 
137
            assert last_rev in rev_hist, \
 
138
                "Working Tree's last revision not in branch.revision_history"
 
139
            rev_hist = rev_hist[:rev_hist.index(last_rev)+1]
 
140
 
 
141
        repository =  self._branch.repository
 
142
        repository.lock_read()
 
143
        try:
 
144
            for revision_id in rev_hist:
 
145
                rev = repository.get_revision(revision_id)
 
146
                self._revision_history_info.append(
 
147
                    (rev.revision_id, rev.message,
 
148
                     rev.timestamp, rev.timezone))
 
149
        finally:
 
150
            repository.unlock()
 
151
 
 
152
    def _get_revision_id(self):
 
153
        """Get the revision id we are working on."""
 
154
        if self._working_tree is not None:
 
155
            return self._working_tree.last_revision()
 
156
        return self._branch.last_revision()
 
157
 
 
158
    def generate(self, to_file):
 
159
        """Output the version information to the supplied file.
 
160
 
 
161
        :param to_file: The file to write the stream to. The output
 
162
                will already be encoded, so to_file should not try
 
163
                to change encodings.
 
164
        :return: None
 
165
        """
 
166
        raise NotImplementedError(VersionInfoBuilder.generate)
 
167
 
 
168
 
 
169
 
 
170
def register_builder(format, module, class_name):
 
171
    """Register a version info format.
 
172
 
 
173
    :param format: The short name of the format, this will be used as the
 
174
        lookup key.
 
175
    :param module: The string name to the module where the format class
 
176
        can be found
 
177
    :param class_name: The string name of the class to instantiate
 
178
    """
 
179
    if len(_version_formats) == 0:
 
180
        _version_formats[None] = (module, class_name)
 
181
    _version_formats[format] = (module, class_name)
 
182
 
 
183
 
 
184
def get_builder(format):
 
185
    """Get a handle to the version info builder class
 
186
 
 
187
    :param format: The lookup key supplied to register_builder
 
188
    :return: A class, which follows the VersionInfoBuilder api.
 
189
    """
 
190
    builder_module, builder_class_name = _version_formats[format]
 
191
    module = __import__(builder_module, globals(), locals(),
 
192
                        [builder_class_name])
 
193
    klass = getattr(module, builder_class_name)
 
194
    return klass
 
195
 
 
196
 
 
197
def get_builder_formats():
 
198
    """Get the possible list of formats"""
 
199
    formats = _version_formats.keys()
 
200
    formats.remove(None)
 
201
    return formats
 
202
 
 
203
 
 
204
register_builder('rio',
 
205
                 'bzrlib.version_info_formats.format_rio',
 
206
                 'RioVersionInfoBuilder')
 
207
register_builder('python',
 
208
                 'bzrlib.version_info_formats.format_python',
 
209
                 'PythonVersionInfoBuilder')