1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2008 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module contains classes describing the sources of symbol fills."""
20 from cvs2svn_lib
.common
import InternalError
21 from cvs2svn_lib
.common
import FatalError
22 from cvs2svn_lib
.common
import SVN_INVALID_REVNUM
23 from cvs2svn_lib
.svn_revision_range
import SVNRevisionRange
24 from cvs2svn_lib
.svn_revision_range
import RevisionScores
28 """Representation of a fill source.
30 A FillSource keeps track of the paths that have to be filled in a
31 particular symbol fill.
33 This class holds a SVNRevisionRange instance for each CVSFile that
34 has to be filled within the subtree of the repository rooted at
35 self.cvs_path. The SVNRevisionRange objects are stored in a tree
36 in which the directory nodes are dictionaries mapping CVSPaths to
37 subnodes and the leaf nodes are the SVNRevisionRange objects telling
38 for what source_lod and what range of revisions the leaf could serve
41 FillSource objects are able to compute the score for arbitrary
42 source LODs and source revision numbers.
44 These objects are used by the symbol filler in SVNOutputOption."""
46 def __init__(self
, cvs_path
, symbol
, node_tree
):
47 """Create a fill source.
49 The best LOD and SVN REVNUM to use as the copy source can be
50 determined by calling compute_best_source().
54 cvs_path -- (CVSPath): the CVSPath described by this FillSource.
56 _symbol -- (Symbol) the symbol to be filled.
58 _node_tree -- (dict) a tree stored as a map { CVSPath : node },
59 where subnodes have the same form. Leaves are
60 SVNRevisionRange instances telling the source_lod and range
61 of SVN revision numbers from which the CVSPath can be
66 self
.cvs_path
= cvs_path
68 self
._node
_tree
= node_tree
70 def _set_node(self
, cvs_file
, svn_revision_range
):
71 parent_node
= self
._get
_node
(cvs_file
.parent_directory
, create
=True)
72 if cvs_file
in parent_node
:
74 '%s appeared twice in sources for %s' % (cvs_file
, self
._symbol
)
76 parent_node
[cvs_file
] = svn_revision_range
78 def _get_node(self
, cvs_path
, create
=False):
79 if cvs_path
== self
.cvs_path
:
80 return self
._node
_tree
82 parent_node
= self
._get
_node
(cvs_path
.parent_directory
, create
=create
)
84 return parent_node
[cvs_path
]
88 parent_node
[cvs_path
] = node
93 def compute_best_source(self
, preferred_source
):
94 """Determine the best source_lod and subversion revision number to copy.
96 Return the best source found, as an SVNRevisionRange instance. If
97 PREFERRED_SOURCE is not None and its opening is among the sources
98 with the best scores, return it; otherwise, return the oldest such
99 revision on the first such source_lod (ordered by the natural LOD
100 sort order). The return value's source_lod is the best LOD to
101 copy from, and its opening_revnum is the best SVN revision."""
103 # Aggregate openings and closings from our rev tree
104 svn_revision_ranges
= self
._get
_revision
_ranges
(self
._node
_tree
)
107 revision_scores
= RevisionScores(svn_revision_ranges
)
109 best_source_lod
, best_revnum
, best_score
= \
110 revision_scores
.get_best_revnum()
113 preferred_source
is not None
114 and revision_scores
.get_score(preferred_source
) == best_score
116 best_source_lod
= preferred_source
.source_lod
117 best_revnum
= preferred_source
.opening_revnum
119 if best_revnum
== SVN_INVALID_REVNUM
:
121 "failed to find a revision to copy from when copying %s"
125 return SVNRevisionRange(best_source_lod
, best_revnum
)
127 def _get_revision_ranges(self
, node
):
128 """Return a list of all the SVNRevisionRanges at and under NODE.
130 Include duplicates. This is a helper method used by
131 compute_best_source()."""
133 if isinstance(node
, SVNRevisionRange
):
137 # It is an intermediate node.
139 for key
, subnode
in node
.items():
140 revision_ranges
.extend(self
._get
_revision
_ranges
(subnode
))
141 return revision_ranges
143 def get_subsources(self
):
144 """Generate (CVSPath, FillSource) for all direct subsources."""
146 if not isinstance(self
._node
_tree
, SVNRevisionRange
):
147 for cvs_path
, node
in self
._node
_tree
.items():
148 fill_source
= FillSource(cvs_path
, self
._symbol
, node
)
149 yield (cvs_path
, fill_source
)
151 def get_subsource_map(self
):
152 """Return the map {CVSPath : FillSource} of direct subsources."""
156 for (cvs_path
, fill_subsource
) in self
.get_subsources():
157 src_entries
[cvs_path
] = fill_subsource
162 """For convenience only. The format is subject to change at any time."""
164 return '%s(%s:%s)' % (
165 self
.__class
__.__name
__, self
._symbol
, self
.cvs_path
,
169 """For convenience only. The format is subject to change at any time."""
171 return '%s%r' % (self
, self
._node
_tree
,)
174 def get_source_set(symbol
, range_map
):
175 """Return a FillSource describing the fill sources for RANGE_MAP.
177 SYMBOL is either a Branch or a Tag. RANGE_MAP is a map { CVSSymbol
178 : SVNRevisionRange } as returned by
179 SymbolingsReader.get_range_map().
181 Use the SVNRevisionRanges from RANGE_MAP to create a FillSource
182 instance describing the sources for filling SYMBOL."""
184 root_cvs_directory
= symbol
.project
.get_root_cvs_directory()
185 fill_source
= FillSource(root_cvs_directory
, symbol
, {})
187 for cvs_symbol
, svn_revision_range
in range_map
.items():
188 fill_source
._set
_node
(cvs_symbol
.cvs_file
, svn_revision_range
)