1
# Copyright (C) 2005, 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Routines for extracting all version information from a bzr branch."""
21
from bzrlib.osutils import local_time_offset, format_date
22
from bzrlib import registry
23
from bzrlib.symbol_versioning import (
29
def create_date_str(timestamp=None, offset=None):
30
"""Just a wrapper around format_date to provide the right format.
32
We don't want to use '%a' in the time string, because it is locale
33
dependant. We also want to force timezone original, and show_offset
35
Without parameters this function yields the current date in the local
38
if timestamp is None and offset is None:
39
timestamp = time.time()
40
offset = local_time_offset()
41
return format_date(timestamp, offset, date_fmt='%Y-%m-%d %H:%M:%S',
42
timezone='original', show_offset=True)
45
class VersionInfoBuilder(object):
46
"""A class which lets you build up information about a revision."""
48
def __init__(self, branch, working_tree=None,
49
check_for_clean=False,
50
include_revision_history=False,
51
include_file_revisions=False,
53
"""Build up information about the given branch.
54
If working_tree is given, it can be checked for changes.
56
:param branch: The branch to work on
57
:param working_tree: If supplied, preferentially check
58
the working tree for changes.
59
:param check_for_clean: If False, we will skip the expense
60
of looking for changes.
61
:param include_revision_history: If True, the output
62
will include the full mainline revision history, including
64
:param include_file_revisions: The output should
65
include the explicit last-changed revision for each file.
66
:param template: Template for the output formatting, not used by
70
self._working_tree = working_tree
71
self._check = check_for_clean
72
self._include_history = include_revision_history
73
self._include_file_revs = include_file_revisions
74
self._template = template
77
self._file_revisions = {}
78
self._revision_history_info= []
80
def _extract_file_revisions(self):
81
"""Extract the working revisions for all files"""
83
# Things seem clean if we never look :)
86
if self._working_tree is not None:
87
basis_tree = self._working_tree.basis_tree()
88
# TODO: jam 20070215 The working tree should actually be locked at
89
# a higher level, but this will do for now.
90
self._working_tree.lock_read()
92
basis_tree = self._branch.basis_tree()
94
basis_tree.lock_read()
96
# Build up the list from the basis inventory
97
for info in basis_tree.list_files(include_root=True):
98
self._file_revisions[info[0]] = info[-1].revision
100
if not self._check or self._working_tree is None:
103
delta = self._working_tree.changes_from(basis_tree,
106
# Using a 2-pass algorithm for renames. This is because you might have
107
# renamed something out of the way, and then created a new file
108
# in which case we would rather see the new marker
109
# Or you might have removed the target, and then renamed
110
# in which case we would rather see the renamed marker
111
for (old_path, new_path, file_id,
112
kind, text_mod, meta_mod) in delta.renamed:
114
self._file_revisions[old_path] = u'renamed to %s' % (new_path,)
115
for path, file_id, kind in delta.removed:
117
self._file_revisions[path] = 'removed'
118
for path, file_id, kind in delta.added:
120
self._file_revisions[path] = 'new'
121
for (old_path, new_path, file_id,
122
kind, text_mod, meta_mod) in delta.renamed:
124
self._file_revisions[new_path] = u'renamed from %s' % (old_path,)
125
for path, file_id, kind, text_mod, meta_mod in delta.modified:
127
self._file_revisions[path] = 'modified'
129
for path in self._working_tree.unknowns():
131
self._file_revisions[path] = 'unversioned'
134
if self._working_tree is not None:
135
self._working_tree.unlock()
137
def _extract_revision_history(self):
138
"""Find the messages for all revisions in history."""
140
# Unfortunately, there is no WorkingTree.revision_history
141
rev_hist = self._branch.revision_history()
142
if self._working_tree is not None:
143
last_rev = self._working_tree.last_revision()
144
if last_rev not in rev_hist:
145
raise AssertionError(
146
"Working Tree's last revision not in branch.revision_history")
147
rev_hist = rev_hist[:rev_hist.index(last_rev)+1]
149
repository = self._branch.repository
150
repository.lock_read()
152
for revision_id in rev_hist:
153
rev = repository.get_revision(revision_id)
154
self._revision_history_info.append(
155
(rev.revision_id, rev.message,
156
rev.timestamp, rev.timezone))
160
def _get_revision_id(self):
161
"""Get the revision id we are working on."""
162
if self._working_tree is not None:
163
return self._working_tree.last_revision()
164
return self._branch.last_revision()
166
def generate(self, to_file):
167
"""Output the version information to the supplied file.
169
:param to_file: The file to write the stream to. The output
170
will already be encoded, so to_file should not try
174
raise NotImplementedError(VersionInfoBuilder.generate)
177
format_registry = registry.Registry()
180
format_registry.register_lazy(
182
'bzrlib.version_info_formats.format_rio',
183
'RioVersionInfoBuilder',
184
'Version info in RIO (simple text) format (default).')
185
format_registry.default_key = 'rio'
186
format_registry.register_lazy(
188
'bzrlib.version_info_formats.format_python',
189
'PythonVersionInfoBuilder',
190
'Version info in Python format.')
191
format_registry.register_lazy(
193
'bzrlib.version_info_formats.format_custom',
194
'CustomVersionInfoBuilder',
195
'Version info in Custom template-based format.')