20
class RevisionReference(object):
22
Reference to a stored revision.
24
Includes the revision_id and revision_sha1.
28
def __init__(self, revision_id, revision_sha1=None):
29
if revision_id == None \
30
or isinstance(revision_id, basestring):
31
self.revision_id = revision_id
33
raise ValueError('bad revision_id %r' % revision_id)
35
if revision_sha1 != None:
36
if isinstance(revision_sha1, basestring) \
37
and len(revision_sha1) == 40:
38
self.revision_sha1 = revision_sha1
40
raise ValueError('bad revision_sha1 %r' % revision_sha1)
44
class Revision(object):
20
from xml import XMLMixin
23
from cElementTree import Element, ElementTree, SubElement
25
from elementtree.ElementTree import Element, ElementTree, SubElement
27
from errors import BzrError
30
class Revision(XMLMixin):
45
31
"""Single revision on a branch.
47
33
Revisions may know their revision_hash, but only once they've been
48
34
written out. This is not stored because you cannot write the hash
49
35
into the file it describes.
51
After bzr 0.0.5 revisions are allowed to have multiple parents.
54
List of parent revisions, each is a RevisionReference.
37
TODO: Perhaps make predecessor be a child element, not an attribute?
64
39
def __init__(self, **args):
40
self.inventory_id = None
41
self.revision_id = None
65
47
self.__dict__.update(args)
69
50
def __repr__(self):
73
54
def to_element(self):
74
from bzrlib.xml import Element, SubElement
76
55
root = Element('revision',
77
56
committer = self.committer,
78
57
timestamp = '%.9f' % self.timestamp,
79
58
revision_id = self.revision_id,
80
59
inventory_id = self.inventory_id,
81
inventory_sha1 = self.inventory_sha1,
84
root.set('timezone', str(self.timezone))
60
timezone = str(self.timezone))
62
root.set('precursor', self.precursor)
87
65
msg = SubElement(root, 'message')
88
66
msg.text = self.message
92
pelts = SubElement(root, 'parents')
93
pelts.tail = pelts.text = '\n'
94
for rr in self.parents:
95
assert isinstance(rr, RevisionReference)
96
p = SubElement(pelts, 'revision_ref')
99
p.set('revision_id', rr.revision_id)
101
p.set('revision_sha1', rr.revision_sha1)
106
72
def from_element(cls, elt):
107
return unpack_revision(elt)
73
# <changeset> is deprecated...
74
if elt.tag not in ('revision', 'changeset'):
75
raise BzrError("unexpected tag in revision file: %r" % elt)
77
cs = cls(committer = elt.get('committer'),
78
timestamp = float(elt.get('timestamp')),
79
precursor = elt.get('precursor'),
80
revision_id = elt.get('revision_id'),
81
inventory_id = elt.get('inventory_id'))
83
v = elt.get('timezone')
84
cs.timezone = v and int(v)
86
cs.message = elt.findtext('message') # text of <message>
109
89
from_element = classmethod(from_element)
113
def unpack_revision(elt):
114
"""Convert XML element into Revision object."""
115
# <changeset> is deprecated...
116
from bzrlib.errors import BzrError
118
if elt.tag not in ('revision', 'changeset'):
119
raise BzrError("unexpected tag in revision file: %r" % elt)
121
rev = Revision(committer = elt.get('committer'),
122
timestamp = float(elt.get('timestamp')),
123
revision_id = elt.get('revision_id'),
124
inventory_id = elt.get('inventory_id'),
125
inventory_sha1 = elt.get('inventory_sha1')
128
precursor = elt.get('precursor')
129
precursor_sha1 = elt.get('precursor_sha1')
131
pelts = elt.find('parents')
135
assert p.tag == 'revision_ref', \
136
"bad parent node tag %r" % p.tag
137
rev_ref = RevisionReference(p.get('revision_id'),
138
p.get('revision_sha1'))
139
rev.parents.append(rev_ref)
143
prec_parent = rev.parents[0].revision_id
144
assert prec_parent == precursor
146
# revisions written prior to 0.0.5 have a single precursor
147
# give as an attribute
148
rev_ref = RevisionReference(precursor, precursor_sha1)
149
rev.parents.append(rev_ref)
151
v = elt.get('timezone')
152
rev.timezone = v and int(v)
154
rev.message = elt.findtext('message') # text of <message>
159
REVISION_ID_RE = None
161
def validate_revision_id(rid):
162
"""Check rid is syntactically valid for a revision id."""
163
global REVISION_ID_RE
164
if not REVISION_ID_RE:
166
REVISION_ID_RE = re.compile('[\w.-]+@[\w.-]+--?\d+--?[0-9a-f]+\Z')
168
if not REVISION_ID_RE.match(rid):
169
raise ValueError("malformed revision-id %r" % rid)
171
def is_ancestor(revision_id, candidate_id, revision_source):
172
"""Return true if candidate_id is an ancestor of revision_id.
173
A false negative will be returned if any intermediate descendent of
174
candidate_id is not present in any of the revision_sources.
176
revisions_source is an object supporting a get_revision operation that
177
behaves like Branch's.
180
from bzrlib.branch import NoSuchRevision
181
ancestors = (revision_id,)
182
while len(ancestors) > 0:
184
for ancestor in ancestors:
185
if ancestor == candidate_id:
188
revision = revision_source.get_revision(ancestor)
189
except NoSuchRevision, e:
190
if e.revision == revision_id:
194
new_ancestors.extend([p.revision_id for p in revision.parents])
195
ancestors = new_ancestors
198
class MultipleRevisionSources(object):
199
def __init__(self, *args):
200
object.__init__(self)
201
assert len(args) != 0
202
self._revision_sources = args
204
def get_revision(self, revision_id):
205
from bzrlib.branch import NoSuchRevision
206
for source in self._revision_sources:
208
return source.get_revision(revision_id)
209
except NoSuchRevision, e: