/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 generate_version_info.py

  • Committer: John Arbash Meinel
  • Date: 2006-06-22 21:40:22 UTC
  • mto: (2022.1.1 version-info-55794)
  • mto: This revision was merged to the branch mainline in revision 2028.
  • Revision ID: john@arbash-meinel.com-20060622214022-009a731bee5dce1c
Updated version-info to the latest bzr.dev codebase. Changed to using VersionInfoBuilder, and made tests pass.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import time
22
22
import pprint
23
23
 
24
 
from StringIO import StringIO
 
24
from cStringIO import StringIO
25
25
 
26
 
from bzrlib.errors import NoWorkingTree
 
26
import bzrlib.delta
 
27
from bzrlib.osutils import local_time_offset, format_date
 
28
import bzrlib.revision
27
29
from bzrlib.rio import RioReader, RioWriter, Stanza
28
 
from bzrlib.osutils import local_time_offset, format_date
29
 
 
30
 
 
31
 
def get_file_revisions(branch, check=False):
32
 
    """Get the last changed revision for all files.
33
 
 
34
 
    :param branch: The branch we are checking.
35
 
    :param check: See if there are uncommitted changes.
36
 
    :return: ({file_path => last changed revision}, Tree_is_clean)
37
 
    """
38
 
    clean = True
39
 
    file_revisions = {}
40
 
    basis_tree = branch.basis_tree()
41
 
    for path, ie in basis_tree.inventory.iter_entries():
42
 
        file_revisions[path] = ie.revision
43
 
 
44
 
    if not check:
45
 
        # Without checking, the tree looks clean
46
 
        return file_revisions, clean
47
 
    try:
48
 
        new_tree = branch.working_tree()
49
 
    except NoWorkingTree:
50
 
        # Without a working tree, everything is clean
51
 
        return file_revisions, clean
52
 
 
53
 
    from bzrlib.diff import compare_trees
54
 
    delta = compare_trees(basis_tree, new_tree, want_unchanged=False)
55
 
 
56
 
    # Using a 2-pass algorithm for renames. This is because you might have
57
 
    # renamed something out of the way, and then created a new file
58
 
    # in which case we would rather see the new marker
59
 
    # Or you might have removed the target, and then renamed
60
 
    # in which case we would rather see the renamed marker
61
 
    for old_path, new_path, file_id, kind, text_mod, meta_mod in delta.renamed:
62
 
        clean = False
63
 
        file_revisions[old_path] = u'renamed to %s' % (new_path,)
64
 
    for path, file_id, kind in delta.removed:
65
 
        clean = False
66
 
        file_revisions[path] = 'removed'
67
 
    for path, file_id, kind in delta.added:
68
 
        clean = False
69
 
        file_revisions[path] = 'new'
70
 
    for old_path, new_path, file_id, kind, text_mod, meta_mod in delta.renamed:
71
 
        clean = False
72
 
        file_revisions[new_path] = u'renamed from %s' % (old_path,)
73
 
    for path, file_id, kind, text_mod, meta_mod in delta.modified:
74
 
        clean = False
75
 
        file_revisions[path] = 'modified'
76
 
 
77
 
    for info in new_tree.list_files():
78
 
        path, status = info[0:2]
79
 
        if status == '?':
80
 
            file_revisions[path] = 'unversioned'
81
 
            clean = False
82
 
 
83
 
    return file_revisions, clean
84
30
 
85
31
 
86
32
# This contains a map of format id => formatter
103
49
                       timezone='original', show_offset=True)
104
50
 
105
51
 
106
 
def generate_rio_version(branch, to_file,
107
 
        check_for_clean=False,
108
 
        include_revision_history=False,
109
 
        include_file_revisions=False):
110
 
    """Create the version file for this project.
111
 
 
112
 
    :param branch: The branch to write information about
113
 
    :param to_file: The file to write the information
114
 
    :param check_for_clean: If true, check if the branch is clean.
115
 
        This can be expensive for large trees. This is also only
116
 
        valid for branches with working trees.
117
 
    :param include_revision_history: Write out the list of revisions, and
118
 
        the commit message associated with each
119
 
    :param include_file_revisions: Write out the set of last changed revision
120
 
        for each file.
121
 
    """
122
 
    info = Stanza()
123
 
    info.add('build-date', create_date_str())
124
 
    info.add('revno', str(branch.revno()))
125
 
 
126
 
    # XXX: Compatibility pre/post storage
127
 
    repo = getattr(branch, 'repository', branch)
128
 
 
129
 
    last_rev_id = branch.last_revision()
130
 
    if last_rev_id is not None:
131
 
        info.add('revision-id', last_rev_id)
132
 
        rev = repo.get_revision(last_rev_id)
133
 
        info.add('date', create_date_str(rev.timestamp, rev.timezone))
134
 
 
135
 
    if branch.nick is not None:
136
 
        info.add('branch-nick', branch.nick)
137
 
 
138
 
    file_revisions = {}
139
 
    clean = True
140
 
    if check_for_clean or include_file_revisions:
141
 
        file_revisions, clean = get_file_revisions(branch, check=check_for_clean)
142
 
 
143
 
    if check_for_clean:
144
 
        if clean:
145
 
            info.add('clean', 'True')
146
 
        else:
147
 
            info.add('clean', 'False')
148
 
 
149
 
    if include_revision_history:
150
 
        revs = branch.revision_history()
151
 
        log = Stanza()
152
 
        for rev_id in revs:
153
 
            rev = repo.get_revision(rev_id)
154
 
            log.add('id', rev_id)
155
 
            log.add('message', rev.message)
156
 
            log.add('date', create_date_str(rev.timestamp, rev.timezone))
157
 
        sio = StringIO()
158
 
        log_writer = RioWriter(to_file=sio)
159
 
        log_writer.write_stanza(log)
160
 
        info.add('revisions', sio.getvalue())
161
 
 
162
 
    if include_file_revisions:
163
 
        files = Stanza()
164
 
        for path in sorted(file_revisions.keys()):
165
 
            files.add('path', path)
166
 
            files.add('revision', file_revisions[path])
167
 
        sio = StringIO()
168
 
        file_writer = RioWriter(to_file=sio)
169
 
        file_writer.write_stanza(files)
170
 
        info.add('file-revisions', sio.getvalue())
171
 
 
172
 
    writer = RioWriter(to_file=to_file)
173
 
    writer.write_stanza(info)
174
 
 
175
 
 
176
 
version_formats['rio'] = generate_rio_version
 
52
class VersionInfoBuilder(object):
 
53
    """A class which lets you build up information about a revision.""" 
 
54
 
 
55
    def __init__(self, branch, working_tree=None,
 
56
                check_for_clean=False,
 
57
                include_revision_history=False,
 
58
                include_file_revisions=False,
 
59
                ):
 
60
        """Build up information about the given branch.
 
61
        If working_tree is given, it can be checked for changes.
 
62
 
 
63
        :param branch: The branch to work on
 
64
        :param working_tree: If supplied, preferentially check
 
65
            the working tree for changes.
 
66
        :param check_for_clean: If False, we will skip the expense
 
67
            of looking for changes.
 
68
        :param include_revision_history: If True, the output
 
69
            will include the full mainline revision history, including
 
70
            date and message
 
71
        :param include_file_revisions: The output should
 
72
            include the explicit last-changed revision for each file.
 
73
        """
 
74
        self._branch = branch
 
75
        self._working_tree = working_tree
 
76
        self._check = check_for_clean
 
77
        self._include_history = include_revision_history
 
78
        self._include_file_revs = include_file_revisions
 
79
 
 
80
        self._clean = None
 
81
        self._file_revisions = {}
 
82
        self._revision_history_info= []
 
83
 
 
84
    def _extract_file_revisions(self):
 
85
        """Extract the working revisions for all files"""
 
86
 
 
87
        # Things seem clean if we never look :)
 
88
        self._clean = True
 
89
 
 
90
        if self._working_tree is not None:
 
91
            basis_tree = self._working_tree.basis_tree()
 
92
        else:
 
93
            basis_tree = self._branch.basis_tree()
 
94
 
 
95
        # Build up the list from the basis inventory
 
96
        for info in basis_tree.list_files():
 
97
            self._file_revisions[info[0]] = info[-1].revision
 
98
 
 
99
        if not self._check or self._working_tree is None:
 
100
            return
 
101
 
 
102
        # We have both a working tree, and we are checking
 
103
        delta = bzrlib.delta.compare_trees(basis_tree, self._working_tree,
 
104
                                          want_unchanged=False)
 
105
 
 
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:
 
113
            self._clean = False
 
114
            self._file_revisions[old_path] = u'renamed to %s' % (new_path,)
 
115
        for path, file_id, kind in delta.removed:
 
116
            self._clean = False
 
117
            self._file_revisions[path] = 'removed'
 
118
        for path, file_id, kind in delta.added:
 
119
            self._clean = False
 
120
            self._file_revisions[path] = 'new'
 
121
        for (old_path, new_path, file_id,
 
122
             kind, text_mod, meta_mod) in delta.renamed:
 
123
            self._clean = False
 
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:
 
126
            self._clean = False
 
127
            self._file_revisions[path] = 'modified'
 
128
 
 
129
        for path in self._working_tree.unknowns():
 
130
            self._clean = False
 
131
            self._file_revisions[path] = 'unversioned'
 
132
 
 
133
    def _extract_revision_history(self):
 
134
        """Find the messages for all revisions in history."""
 
135
 
 
136
        # Unfortunately, there is no WorkingTree.revision_history
 
137
        rev_hist = self._branch.revision_history()
 
138
        if self._working_tree is not None:
 
139
            last_rev = self._working_tree.last_revision()
 
140
            assert last_rev in rev_hist, \
 
141
                "Working Tree's last revision not in branch.revision_history"
 
142
            rev_hist = rev_hist[:rev_hist.index(last_rev)+1]
 
143
 
 
144
        repository =  self._branch.repository
 
145
        repository.lock_read()
 
146
        try:
 
147
            for revision_id in rev_hist:
 
148
                rev = repository.get_revision(revision_id)
 
149
                self._revision_history_info.append(
 
150
                    (rev.revision_id, rev.message,
 
151
                     rev.timestamp, rev.timezone))
 
152
        finally:
 
153
            repository.unlock()
 
154
 
 
155
    def _get_revision_id(self):
 
156
        """Get the revision id we are working on."""
 
157
        if self._working_tree is not None:
 
158
            return self._working_tree.last_revision()
 
159
        return self._branch.last_revision()
 
160
 
 
161
    def generate(self, to_file):
 
162
        """Output the version information to the supplied file.
 
163
 
 
164
        :param to_file: The file to write the stream to. The output
 
165
                will already be encoded, so to_file should not try
 
166
                to change encodings.
 
167
        :return: None
 
168
        """
 
169
        raise NotImplementedError(VersionInfoBuilder.generate)
 
170
            
 
171
 
 
172
class RioVersionInfoBuilder(VersionInfoBuilder):
 
173
    """This writes a rio stream out."""
 
174
 
 
175
    def generate(self, to_file):
 
176
        info = Stanza()
 
177
        revision_id = self._get_revision_id()
 
178
        if revision_id is not None:
 
179
            info.add('revision-id', revision_id)
 
180
            rev = self._branch.repository.get_revision(revision_id)
 
181
            info.add('date', create_date_str(rev.timestamp, rev.timezone))
 
182
            revno = str(self._branch.revision_id_to_revno(revision_id))
 
183
        else:
 
184
            revno = '0'
 
185
 
 
186
        info.add('build-date', create_date_str())
 
187
        info.add('revno', revno)
 
188
 
 
189
        if self._branch.nick is not None:
 
190
            info.add('branch-nick', self._branch.nick)
 
191
 
 
192
        if self._check or self._include_file_revs:
 
193
            self._extract_file_revisions()
 
194
 
 
195
        if self._check:
 
196
            if self._clean:
 
197
                info.add('clean', 'True')
 
198
            else:
 
199
                info.add('clean', 'False')
 
200
 
 
201
        if self._include_history:
 
202
            self._extract_revision_history()
 
203
            log = Stanza()
 
204
            for (revision_id, message,
 
205
                 timestamp, timezone) in self._revision_history_info:
 
206
                log.add('id', revision_id)
 
207
                log.add('message', message)
 
208
                log.add('date', create_date_str(timestamp, timezone))
 
209
            sio = StringIO()
 
210
            log_writer = RioWriter(to_file=sio)
 
211
            log_writer.write_stanza(log)
 
212
            info.add('revisions', sio.getvalue())
 
213
 
 
214
        if self._include_file_revs:
 
215
            files = Stanza()
 
216
            for path in sorted(self._file_revisions.keys()):
 
217
                files.add('path', path)
 
218
                files.add('revision', self._file_revisions[path])
 
219
            sio = StringIO()
 
220
            file_writer = RioWriter(to_file=sio)
 
221
            file_writer.write_stanza(files)
 
222
            info.add('file-revisions', sio.getvalue())
 
223
 
 
224
        writer = RioWriter(to_file=to_file)
 
225
        writer.write_stanza(info)
 
226
 
 
227
 
 
228
version_formats['rio'] = RioVersionInfoBuilder
177
229
# Default format is rio
178
 
version_formats[None] = generate_rio_version
 
230
version_formats[None] = RioVersionInfoBuilder
179
231
 
180
232
 
181
233
# Header and footer for the python format
182
234
_py_version_header = '''#!/usr/bin/env python
183
 
"""\\
184
 
This file is automatically generated by generate_version_info
 
235
"""This file is automatically generated by generate_version_info
185
236
It uses the current working tree to determine the revision.
186
237
So don't edit it. :)
187
238
"""
198
249
'''
199
250
 
200
251
 
201
 
def generate_python_version(branch, to_file,
202
 
        check_for_clean=False,
203
 
        include_revision_history=False,
204
 
        include_file_revisions=False):
205
 
    """Create a python version file for this project.
206
 
 
207
 
    :param branch: The branch to write information about
208
 
    :param to_file: The file to write the information
209
 
    :param check_for_clean: If true, check if the branch is clean.
210
 
        This can be expensive for large trees. This is also only
211
 
        valid for branches with working trees.
212
 
    :param include_revision_history: Write out the list of revisions, and
213
 
        the commit message associated with each
214
 
    :param include_file_revisions: Write out the set of last changed revision
215
 
        for each file.
216
 
    """
217
 
    # TODO: jam 20051228 The python output doesn't actually need to be
218
 
    #       encoded, because it should only generate ascii safe output.
219
 
    info = {'build_date':create_date_str()
220
 
              , 'revno':branch.revno()
221
 
              , 'revision_id':None
222
 
              , 'branch_nick':branch.nick
223
 
              , 'clean':None
224
 
              , 'date':None
225
 
    }
226
 
    revisions = []
227
 
 
228
 
    # XXX: Compatibility pre/post storage
229
 
    repo = getattr(branch, 'repository', branch)
230
 
 
231
 
    last_rev_id = branch.last_revision()
232
 
    if last_rev_id:
233
 
        rev = repo.get_revision(last_rev_id)
234
 
        info['revision_id'] = last_rev_id
235
 
        info['date'] = create_date_str(rev.timestamp, rev.timezone)
236
 
 
237
 
    file_revisions = {}
238
 
    clean = True
239
 
    if check_for_clean or include_file_revisions:
240
 
        file_revisions, clean = get_file_revisions(branch, check=check_for_clean)
241
 
 
242
 
    if check_for_clean:
243
 
        if clean:
244
 
            info['clean'] = True
245
 
        else:
246
 
            info['clean'] = False
247
 
 
248
 
    info_str = pprint.pformat(info)
249
 
    to_file.write(_py_version_header)
250
 
    to_file.write('version_info = ')
251
 
    to_file.write(info_str)
252
 
    to_file.write('\n\n')
253
 
 
254
 
    if include_revision_history:
255
 
        revs = branch.revision_history()
256
 
        for rev_id in revs:
257
 
            rev = repo.get_revision(rev_id)
258
 
            revisions.append((rev_id, rev.message, rev.timestamp, rev.timezone))
259
 
        revision_str = pprint.pformat(revisions)
260
 
        to_file.write('revisions = ')
261
 
        to_file.write(revision_str)
262
 
        to_file.write('\n\n')
263
 
    else:
264
 
        to_file.write('revisions = {}\n\n')
265
 
 
266
 
    if include_file_revisions:
267
 
        file_rev_str = pprint.pformat(file_revisions)
268
 
        to_file.write('file_revisions = ')
269
 
        to_file.write(file_rev_str)
270
 
        to_file.write('\n\n')
271
 
    else:
272
 
        to_file.write('file_revisions = {}\n\n')
273
 
 
274
 
    to_file.write(_py_version_footer)
275
 
 
276
 
 
277
 
version_formats['python'] = generate_python_version
 
252
class PythonVersionInfoBuilder(VersionInfoBuilder):
 
253
    """Create a version file which is a python source module."""
 
254
 
 
255
    def generate(self, to_file):
 
256
        info = {'build_date':create_date_str()
 
257
                  , 'revno':None
 
258
                  , 'revision_id':None
 
259
                  , 'branch_nick':self._branch.nick
 
260
                  , 'clean':None
 
261
                  , 'date':None
 
262
        }
 
263
        revisions = []
 
264
 
 
265
        revision_id = self._get_revision_id()
 
266
        if revision_id is None:
 
267
            info['revno'] = 0
 
268
        else:
 
269
            info['revno'] = self._branch.revision_id_to_revno(revision_id)
 
270
            info['revision_id'] = revision_id
 
271
            rev = self._branch.repository.get_revision(revision_id)
 
272
            info['date'] = create_date_str(rev.timestamp, rev.timezone)
 
273
 
 
274
        if self._check or self._include_file_revs:
 
275
            self._extract_file_revisions()
 
276
 
 
277
        if self._check:
 
278
            if self._clean:
 
279
                info['clean'] = True
 
280
            else:
 
281
                info['clean'] = False
 
282
 
 
283
        info_str = pprint.pformat(info)
 
284
        to_file.write(_py_version_header)
 
285
        to_file.write('version_info = ')
 
286
        to_file.write(info_str)
 
287
        to_file.write('\n\n')
 
288
 
 
289
        if self._include_history:
 
290
            self._extract_revision_history()
 
291
            revision_str = pprint.pformat(self._revision_history_info)
 
292
            to_file.write('revisions = ')
 
293
            to_file.write(revision_str)
 
294
            to_file.write('\n\n')
 
295
        else:
 
296
            to_file.write('revisions = {}\n\n')
 
297
 
 
298
        if self._include_file_revs:
 
299
            file_rev_str = pprint.pformat(self._file_revisions)
 
300
            to_file.write('file_revisions = ')
 
301
            to_file.write(file_rev_str)
 
302
            to_file.write('\n\n')
 
303
        else:
 
304
            to_file.write('file_revisions = {}\n\n')
 
305
 
 
306
        to_file.write(_py_version_footer)
 
307
 
 
308
 
 
309
version_formats['python'] = PythonVersionInfoBuilder
278
310