File: svn_commit.py

package info (click to toggle)
cvs2svn 2.3.0-2
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 3,528 kB
  • ctags: 2,828
  • sloc: python: 20,721; sh: 509; perl: 121; makefile: 79
file content (381 lines) | stat: -rw-r--r-- 11,307 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# (Be in -*- python -*- mode.)
#
# ====================================================================
# Copyright (c) 2000-2008 CollabNet.  All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.  The terms
# are also available at http://subversion.tigris.org/license-1.html.
# If newer versions of this license are posted there, you may use a
# newer version instead, at your option.
#
# This software consists of voluntary contributions made by many
# individuals.  For exact contribution history, see the revision
# history and logs, available at http://cvs2svn.tigris.org/.
# ====================================================================

"""This module contains the SVNCommit classes.

There are five types of SVNCommits:

  SVNInitialProjectCommit -- Initializes a project (creates its trunk,
      branches, and tags directories).

  SVNPrimaryCommit -- Commits one or more CVSRevisions on one or more
      lines of development.

  SVNBranchCommit -- Creates or fills a branch; that is, copies files
      from a source line of development to a target branch.

  SVNTagCommit -- Creates or fills a tag; that is, copies files from a
      source line of development to a target tag.

  SVNPostCommit -- Updates trunk to reflect changes on a non-trunk
      default branch.

"""


import textwrap

from cvs2svn_lib.common import InternalError
from cvs2svn_lib.context import Ctx
from cvs2svn_lib.symbol import Branch
from cvs2svn_lib.symbol import Tag


class SVNCommit:
  """This represents one commit to the Subversion Repository."""

  # textwrap.TextWrapper instance to be used for wrapping log messages:
  text_wrapper = textwrap.TextWrapper(width=76)

  def __init__(self, date, revnum):
    """Instantiate an SVNCommit.

    REVNUM is the SVN revision number of this commit."""

    # The date of the commit, as an integer.  While the SVNCommit is
    # being built up, this contains the latest date seen so far.  This
    # member is set externally.
    self.date = date

    # The SVN revision number of this commit, as an integer.
    self.revnum = revnum

  def __getstate__(self):
    return (self.date, self.revnum,)

  def __setstate__(self, state):
    (self.date, self.revnum,) = state

  def get_cvs_items(self):
    """Return a list containing the CVSItems in this commit."""

    raise NotImplementedError()

  def get_author(self):
    """Return the author or this commit, or None if none is to be used.

    The return value is exactly as the author appeared in the RCS
    file, with undefined character encoding."""

    raise NotImplementedError()

  def get_log_msg(self):
    """Return a log message for this commit.

    The return value is exactly as the log message appeared in the RCS
    file, with undefined character encoding."""

    raise NotImplementedError()

  def get_warning_summary(self):
    """Return a summary of this commit that can be used in warnings."""

    return '(subversion rev %s)' % (self.revnum,)

  def get_description(self):
    """Return a partial description of this SVNCommit, for logging."""

    raise NotImplementedError()

  def output(self, output_option):
    """Cause this commit to be output to OUTPUT_OPTION.

    This method is used for double-dispatch.  Derived classes should
    call the OutputOption.process_*_commit() method appropriate for
    the type of SVNCommit."""

    raise NotImplementedError()

  def __str__(self):
    """ Print a human-readable description of this SVNCommit.

    This description is not intended to be machine-parseable."""

    ret = "SVNCommit #: " + str(self.revnum) + "\n"
    ret += "   debug description: " + self.get_description() + "\n"
    return ret


class SVNInitialProjectCommit(SVNCommit):
  def __init__(self, date, projects, revnum):
    SVNCommit.__init__(self, date, revnum)
    self.projects = list(projects)

  def __getstate__(self):
    return (
        SVNCommit.__getstate__(self),
        [project.id for project in self.projects],
        )

  def __setstate__(self, state):
    (svn_commit_state, project_ids,) = state
    SVNCommit.__setstate__(self, svn_commit_state)
    self.projects = [
        Ctx()._projects[project_id] for project_id in project_ids
        ]

  def get_cvs_items(self):
    return []

  def get_author(self):
    return Ctx().username

  def get_log_msg(self):
    return self.text_wrapper.fill(
        Ctx().initial_project_commit_message % {}
        )

  def get_description(self):
    return 'Project initialization'

  def output(self, output_option):
    output_option.process_initial_project_commit(self)


class SVNRevisionCommit(SVNCommit):
  """A SVNCommit that includes actual CVS revisions."""

  def __init__(self, cvs_revs, date, revnum):
    SVNCommit.__init__(self, date, revnum)

    self.cvs_revs = list(cvs_revs)

    # This value is set lazily by _get_metadata():
    self._metadata = None

  def __getstate__(self):
    """Return the part of the state represented by this mixin."""

    return (
        SVNCommit.__getstate__(self),
        [cvs_rev.id for cvs_rev in self.cvs_revs],
        )

  def __setstate__(self, state):
    """Restore the part of the state represented by this mixin."""

    (svn_commit_state, cvs_rev_ids) = state
    SVNCommit.__setstate__(self, svn_commit_state)

    self.cvs_revs = [
        cvs_rev
        for (id, cvs_rev) in Ctx()._cvs_items_db.get_many(cvs_rev_ids)
        ]
    self._metadata = None

  def get_cvs_items(self):
    return self.cvs_revs

  def _get_metadata(self):
    """Return the Metadata instance for this commit."""

    if self._metadata is None:
      # Set self._metadata for this commit from that of the first cvs
      # revision.
      if not self.cvs_revs:
        raise InternalError('SVNPrimaryCommit contains no CVS revisions')

      metadata_id = self.cvs_revs[0].metadata_id
      self._metadata = Ctx()._metadata_db[metadata_id]

    return self._metadata

  def get_author(self):
    return self._get_metadata().author

  def get_warning_summary(self):
    retval = []
    retval.append(SVNCommit.get_warning_summary(self) + '  Related files:')
    for cvs_rev in self.cvs_revs:
      retval.append('  ' + cvs_rev.cvs_file.filename)
    return '\n'.join(retval)

  def __str__(self):
    """Return the revision part of a description of this SVNCommit.

    Derived classes should append the output of this method to the
    output of SVNCommit.__str__()."""

    ret = []
    ret.append(SVNCommit.__str__(self))
    ret.append('   cvs_revs:\n')
    for cvs_rev in self.cvs_revs:
      ret.append('     %x\n' % (cvs_rev.id,))
    return ''.join(ret)


class SVNPrimaryCommit(SVNRevisionCommit):
  def __init__(self, cvs_revs, date, revnum):
    SVNRevisionCommit.__init__(self, cvs_revs, date, revnum)

  def get_log_msg(self):
    """Return the actual log message for this commit."""

    return self._get_metadata().log_msg

  def get_description(self):
    return 'commit'

  def output(self, output_option):
    output_option.process_primary_commit(self)


class SVNPostCommit(SVNRevisionCommit):
  def __init__(self, motivating_revnum, cvs_revs, date, revnum):
    SVNRevisionCommit.__init__(self, cvs_revs, date, revnum)

    # The subversion revision number of the *primary* commit where the
    # default branch changes actually happened.  (NOTE: Secondary
    # commits that fill branches and tags also have a motivating
    # commit, but we do not record it because it is (currently) not
    # needed for anything.)  motivating_revnum is used when generating
    # the log message for the commit that synchronizes the default
    # branch with trunk.
    #
    # It is possible for multiple synchronization commits to refer to
    # the same motivating commit revision number, and it is possible
    # for a single synchronization commit to contain CVSRevisions on
    # multiple different default branches.
    self.motivating_revnum = motivating_revnum

  def __getstate__(self):
    return (
        SVNRevisionCommit.__getstate__(self),
        self.motivating_revnum,
        )

  def __setstate__(self, state):
    (rev_state, self.motivating_revnum,) = state
    SVNRevisionCommit.__setstate__(self, rev_state)

  def get_cvs_items(self):
    # It might seem that we should return
    # SVNRevisionCommit.get_cvs_items(self) here, but this commit
    # doesn't really include those CVSItems, but rather followup
    # commits to those.
    return []

  def get_log_msg(self):
    """Return a manufactured log message for this commit."""

    return self.text_wrapper.fill(
        Ctx().post_commit_message % {'revnum' : self.motivating_revnum}
        )

  def get_description(self):
    return 'post-commit default branch(es)'

  def output(self, output_option):
    output_option.process_post_commit(self)


class SVNSymbolCommit(SVNCommit):
  def __init__(self, symbol, cvs_symbol_ids, date, revnum):
    SVNCommit.__init__(self, date, revnum)

    # The TypedSymbol that is filled in this SVNCommit.
    self.symbol = symbol

    self.cvs_symbol_ids = cvs_symbol_ids

  def __getstate__(self):
    return (
        SVNCommit.__getstate__(self),
        self.symbol.id, self.cvs_symbol_ids,
        )

  def __setstate__(self, state):
    (svn_commit_state, symbol_id, self.cvs_symbol_ids) = state
    SVNCommit.__setstate__(self, svn_commit_state)
    self.symbol = Ctx()._symbol_db.get_symbol(symbol_id)

  def get_cvs_items(self):
    return [
        cvs_symbol
        for (id, cvs_symbol)
            in Ctx()._cvs_items_db.get_many(self.cvs_symbol_ids)
        ]

  def _get_symbol_type(self):
    """Return the type of the self.symbol ('branch' or 'tag')."""

    raise NotImplementedError()

  def get_author(self):
    return Ctx().username

  def get_log_msg(self):
    """Return a manufactured log message for this commit."""

    return self.text_wrapper.fill(
        Ctx().symbol_commit_message % {
            'symbol_type' : self._get_symbol_type(),
            'symbol_name' : self.symbol.name,
            }
        )

  def get_description(self):
    return 'copying to %s %r' % (self._get_symbol_type(), self.symbol.name,)

  def __str__(self):
    """ Print a human-readable description of this SVNCommit.

    This description is not intended to be machine-parseable."""

    return (
        SVNCommit.__str__(self)
        + "   symbolic name: %s\n" % (self.symbol.name,)
        )


class SVNBranchCommit(SVNSymbolCommit):
  def __init__(self, symbol, cvs_symbol_ids, date, revnum):
    if not isinstance(symbol, Branch):
      raise InternalError('Incorrect symbol type %r' % (symbol,))

    SVNSymbolCommit.__init__(self, symbol, cvs_symbol_ids, date, revnum)

  def _get_symbol_type(self):
    return 'branch'

  def output(self, output_option):
    output_option.process_branch_commit(self)


class SVNTagCommit(SVNSymbolCommit):
  def __init__(self, symbol, cvs_symbol_ids, date, revnum):
    if not isinstance(symbol, Tag):
      raise InternalError('Incorrect symbol type %r' % (symbol,))

    SVNSymbolCommit.__init__(self, symbol, cvs_symbol_ids, date, revnum)

  def _get_symbol_type(self):
    return 'tag'

  def output(self, output_option):
    output_option.process_tag_commit(self)