~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revision.py

  • Committer: Aaron Bentley
  • Date: 2005-10-03 16:53:39 UTC
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1419.
  • Revision ID: abentley@panoramicfeedback.com-20051003165339-9ee4d484477fd164
Ignored user-installed plugins

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
# TODO: Some kind of command-line display of revision properties: 
18
 
# perhaps show them in log -v and allow them as options to the commit command.
19
17
 
20
18
import bzrlib.errors
21
19
from bzrlib.graph import node_distances, select_farthest, all_descendants
22
 
from bzrlib.osutils import contains_whitespace
23
20
 
24
21
NULL_REVISION="null:"
25
22
 
34
31
 
35
32
    parent_ids
36
33
        List of parent revision_ids
37
 
 
38
 
    properties
39
 
        Dictionary of revision properties.  These are attached to the
40
 
        revision as extra metadata.  The name must be a single 
41
 
        word; the value can be an arbitrary string.
42
34
    """
 
35
    inventory_id = None
 
36
    inventory_sha1 = None
 
37
    revision_id = None
 
38
    timestamp = None
 
39
    message = None
 
40
    timezone = None
 
41
    committer = None
43
42
    
44
 
    def __init__(self, revision_id, properties=None, **args):
45
 
        self.revision_id = revision_id
46
 
        self.properties = properties or {}
47
 
        self._check_properties()
 
43
    def __init__(self, **args):
 
44
        self.__dict__.update(args)
48
45
        self.parent_ids = []
49
46
        self.parent_sha1s = []
50
 
        self.__dict__.update(args)
 
47
 
51
48
 
52
49
    def __repr__(self):
53
50
        return "<Revision id %s>" % self.revision_id
55
52
    def __eq__(self, other):
56
53
        if not isinstance(other, Revision):
57
54
            return False
58
 
        # FIXME: rbc 20050930 parent_ids are not being compared
59
 
        return (
60
 
                self.inventory_sha1 == other.inventory_sha1
 
55
        return (self.inventory_id == other.inventory_id
 
56
                and self.inventory_sha1 == other.inventory_sha1
61
57
                and self.revision_id == other.revision_id
62
58
                and self.timestamp == other.timestamp
63
59
                and self.message == other.message
64
60
                and self.timezone == other.timezone
65
 
                and self.committer == other.committer
66
 
                and self.properties == other.properties)
 
61
                and self.committer == other.committer)
67
62
 
68
63
    def __ne__(self, other):
69
64
        return not self.__eq__(other)
70
65
 
71
 
    def _check_properties(self):
72
 
        """Verify that all revision properties are OK.
73
 
        """
74
 
        for name, value in self.properties.iteritems():
75
 
            if not isinstance(name, basestring) or contains_whitespace(name):
76
 
                raise ValueError("invalid property name %r" % name)
77
 
            if not isinstance(value, basestring):
78
 
                raise ValueError("invalid property value %r for %r" % 
79
 
                                 (name, value))
 
66
        
 
67
 
 
68
REVISION_ID_RE = None
 
69
 
 
70
def validate_revision_id(rid):
 
71
    """Check rid is syntactically valid for a revision id."""
 
72
    global REVISION_ID_RE
 
73
    if not REVISION_ID_RE:
 
74
        import re
 
75
        REVISION_ID_RE = re.compile('[\w.-]+@[\w.-]+--?\d+--?[0-9a-f]+\Z')
 
76
 
 
77
    if not REVISION_ID_RE.match(rid):
 
78
        raise ValueError("malformed revision-id %r" % rid)
80
79
 
81
80
 
82
81
def is_ancestor(revision_id, candidate_id, branch):
151
150
        if b_ancestors.has_key(revision):
152
151
            a_intersection.append((a_distance, a_order, revision))
153
152
            b_intersection.append((b_ancestors[revision][1], a_order, revision))
154
 
    mutter("a intersection: %r", a_intersection)
155
 
    mutter("b intersection: %r", b_intersection)
 
153
    mutter("a intersection: %r" % a_intersection)
 
154
    mutter("b intersection: %r" % b_intersection)
156
155
 
157
156
    a_closest = __get_closest(a_intersection)
158
157
    if len(a_closest) == 0:
159
158
        return None
160
159
    b_closest = __get_closest(b_intersection)
161
160
    assert len(b_closest) != 0
162
 
    mutter ("a_closest %r", a_closest)
163
 
    mutter ("b_closest %r", b_closest)
 
161
    mutter ("a_closest %r" % a_closest)
 
162
    mutter ("b_closest %r" % b_closest)
164
163
    if a_closest[0] in b_closest:
165
164
        return a_closest[0]
166
165
    elif b_closest[0] in a_closest:
176
175
    TODO: Produce graphs with the NULL revision as root, so that we can find
177
176
    a common even when trees are not branches don't represent a single line
178
177
    of descent.
179
 
    RBC: 20051024: note that when we have two partial histories, this may not
180
 
         be possible. But if we are willing to pretend :)... sure.
181
178
    """
182
179
    ancestors = {}
183
180
    descendants = {}
210
207
            if parents is not None:
211
208
                ancestors[line] = set(parents)
212
209
        lines = new_lines
213
 
    if root is None:
214
 
        # The history for revision becomes inaccessible without
215
 
        # actually hitting a no-parents revision. This then
216
 
        # makes these asserts below trigger. So, if root is None
217
 
        # determine the actual root by walking the accessible tree
218
 
        # and then stash NULL_REVISION at the end.
219
 
        root = NULL_REVISION
220
 
        descendants[root] = {}
221
 
        # for every revision, check we can access at least
222
 
        # one parent, if we cant, add NULL_REVISION and
223
 
        # a link
224
 
        for rev in ancestors:
225
 
            if len(ancestors[rev]) == 0:
226
 
                raise RuntimeError('unreachable code ?!')
227
 
            ok = False
228
 
            for parent in ancestors[rev]:
229
 
                if parent in ancestors:
230
 
                    ok = True
231
 
            if ok:
232
 
                continue
233
 
            descendants[root][rev] = 1
234
 
            ancestors[rev].add(root)
235
 
        ancestors[root] = set()
236
210
    assert root not in descendants[root]
237
211
    assert root not in ancestors[root]
238
212
    return root, ancestors, descendants
239
213
 
240
 
 
241
214
def combined_graph(revision_a, revision_b, revision_source):
242
215
    """Produce a combined ancestry graph.
243
216
    Return graph root, ancestors map, descendants map, set of common nodes"""
259
232
        descendants[node].update(node_dec)
260
233
    return root, ancestors, descendants, common
261
234
 
262
 
 
263
235
def common_ancestor(revision_a, revision_b, revision_source):
264
236
    try:
265
237
        root, ancestors, descendants, common = \
273
245
        raise bzrlib.errors.NoCommonAncestor(revision_a, revision_b)
274
246
    return farthest
275
247
 
276
 
 
277
248
class MultipleRevisionSources(object):
278
249
    """Proxy that looks in multiple branches for revisions."""
279
250
    def __init__(self, *args):