~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revision.py

Change the use of run_bzr to run_bzr_captured in blackbox tests - inspired by David Clymers patch to change run_bzr usage to runbzr

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import bzrlib.errors
19
19
from bzrlib.graph import node_distances, select_farthest, all_descendants
20
 
from bzrlib.osutils import contains_whitespace
21
20
 
22
21
NULL_REVISION="null:"
23
22
 
 
23
class RevisionReference(object):
 
24
    """
 
25
    Reference to a stored revision.
 
26
 
 
27
    Includes the revision_id and revision_sha1.
 
28
    """
 
29
    revision_id = None
 
30
    revision_sha1 = None
 
31
    def __init__(self, revision_id, revision_sha1=None):
 
32
        if revision_id == None \
 
33
           or isinstance(revision_id, basestring):
 
34
            self.revision_id = revision_id
 
35
        else:
 
36
            raise ValueError('bad revision_id %r' % revision_id)
 
37
 
 
38
        if revision_sha1 != None:
 
39
            if isinstance(revision_sha1, basestring) \
 
40
               and len(revision_sha1) == 40:
 
41
                self.revision_sha1 = revision_sha1
 
42
            else:
 
43
                raise ValueError('bad revision_sha1 %r' % revision_sha1)
 
44
                
 
45
 
 
46
 
24
47
class Revision(object):
25
48
    """Single revision on a branch.
26
49
 
30
53
 
31
54
    After bzr 0.0.5 revisions are allowed to have multiple parents.
32
55
 
33
 
    parent_ids
34
 
        List of parent revision_ids
35
 
 
36
 
    properties
37
 
        Dictionary of revision properties.  These are attached to the
38
 
        revision as extra metadata.  The name must be a single 
39
 
        word; the value can be an arbitrary string.
 
56
    parents
 
57
        List of parent revisions, each is a RevisionReference.
40
58
    """
 
59
    inventory_id = None
 
60
    inventory_sha1 = None
 
61
    revision_id = None
 
62
    timestamp = None
 
63
    message = None
 
64
    timezone = None
 
65
    committer = None
41
66
    
42
 
    def __init__(self, revision_id, properties=None, **args):
43
 
        self.revision_id = revision_id
44
 
        self.properties = properties or {}
45
 
        self._check_properties()
 
67
    def __init__(self, **args):
46
68
        self.__dict__.update(args)
47
 
        self.parent_ids = []
48
 
        self.parent_sha1s = []
 
69
        self.parents = []
 
70
 
49
71
 
50
72
    def __repr__(self):
51
73
        return "<Revision id %s>" % self.revision_id
53
75
    def __eq__(self, other):
54
76
        if not isinstance(other, Revision):
55
77
            return False
56
 
        # FIXME: rbc 20050930 parent_ids are not being compared
57
 
        return (
58
 
                self.inventory_sha1 == other.inventory_sha1
 
78
        return (self.inventory_id == other.inventory_id
 
79
                and self.inventory_sha1 == other.inventory_sha1
59
80
                and self.revision_id == other.revision_id
60
81
                and self.timestamp == other.timestamp
61
82
                and self.message == other.message
62
83
                and self.timezone == other.timezone
63
 
                and self.committer == other.committer
64
 
                and self.properties == other.properties)
 
84
                and self.committer == other.committer)
65
85
 
66
86
    def __ne__(self, other):
67
87
        return not self.__eq__(other)
68
88
 
69
 
    def _check_properties(self):
70
 
        """Verify that all revision properties are OK.
71
 
        """
72
 
        for name, value in self.properties.iteritems():
73
 
            if not isinstance(name, basestring) or contains_whitespace(name):
74
 
                raise ValueError("invalid property name %r" % name)
75
 
            if not isinstance(value, basestring):
76
 
                raise ValueError("invalid property value %r for %r" % 
77
 
                                 (name, value))
78
 
 
79
 
 
80
 
def is_ancestor(revision_id, candidate_id, branch):
 
89
        
 
90
 
 
91
REVISION_ID_RE = None
 
92
 
 
93
def validate_revision_id(rid):
 
94
    """Check rid is syntactically valid for a revision id."""
 
95
    global REVISION_ID_RE
 
96
    if not REVISION_ID_RE:
 
97
        import re
 
98
        REVISION_ID_RE = re.compile('[\w.-]+@[\w.-]+--?\d+--?[0-9a-f]+\Z')
 
99
 
 
100
    if not REVISION_ID_RE.match(rid):
 
101
        raise ValueError("malformed revision-id %r" % rid)
 
102
 
 
103
def is_ancestor(revision_id, candidate_id, revision_source):
81
104
    """Return true if candidate_id is an ancestor of revision_id.
82
 
 
83
105
    A false negative will be returned if any intermediate descendent of
84
106
    candidate_id is not present in any of the revision_sources.
85
107
    
86
108
    revisions_source is an object supporting a get_revision operation that
87
109
    behaves like Branch's.
88
110
    """
89
 
    return candidate_id in branch.get_ancestry(revision_id)
90
 
 
 
111
    if candidate_id is None:
 
112
        return True
 
113
    for ancestor_id, distance in iter_ancestors(revision_id, revision_source):
 
114
        if ancestor_id == candidate_id:
 
115
            return True
 
116
    return False
91
117
 
92
118
def iter_ancestors(revision_id, revision_source, only_present=False):
93
119
    ancestors = (revision_id,)
106
132
                    continue
107
133
            if only_present:
108
134
                yield ancestor, distance
109
 
            new_ancestors.extend(revision.parent_ids)
 
135
            new_ancestors.extend([p.revision_id for p in revision.parents])
110
136
        ancestors = new_ancestors
111
137
        distance += 1
112
138
 
189
215
            else:
190
216
                try:
191
217
                    rev = revision_source.get_revision(line)
192
 
                    parents = list(rev.parent_ids)
 
218
                    parents = [p.revision_id for p in rev.parents]
193
219
                    if len(parents) == 0:
194
220
                        parents = [NULL_REVISION]
195
221
                except bzrlib.errors.NoSuchRevision:
210
236
    assert root not in ancestors[root]
211
237
    return root, ancestors, descendants
212
238
 
213
 
 
214
239
def combined_graph(revision_a, revision_b, revision_source):
215
240
    """Produce a combined ancestry graph.
216
241
    Return graph root, ancestors map, descendants map, set of common nodes"""
228
253
        ancestors[node].update(node_anc)
229
254
    for node, node_dec in descendants_b.iteritems():
230
255
        if node not in descendants:
231
 
            descendants[node] = {}
 
256
            descendants[node] = set()
232
257
        descendants[node].update(node_dec)
233
258
    return root, ancestors, descendants, common
234
259
 
235
 
 
236
260
def common_ancestor(revision_a, revision_b, revision_source):
237
261
    try:
238
262
        root, ancestors, descendants, common = \
246
270
        raise bzrlib.errors.NoCommonAncestor(revision_a, revision_b)
247
271
    return farthest
248
272
 
249
 
 
250
273
class MultipleRevisionSources(object):
251
274
    """Proxy that looks in multiple branches for revisions."""
252
275
    def __init__(self, *args):