59
class TransportRefsContainer(RefsContainer):
60
"""Refs container that reads refs from a transport."""
62
def __init__(self, transport):
63
self.transport = transport
64
self._packed_refs = None
65
self._peeled_refs = None
68
return "%s(%r)" % (self.__class__.__name__, self.transport)
70
def subkeys(self, base):
72
path = self.refpath(base)
74
iter_files = self.transport.clone(base).iter_files_recursive()
75
except TransportNotPossible:
78
for refname in iter_files:
79
# check_ref_format requires at least one /, so we prepend the
80
# base before calling it.
81
if check_ref_format("%s/%s" % (base, refname)):
82
keys.add(("%s/%s" % refname).strip("/"))
83
for key in self.get_packed_refs():
84
if key.startswith(base):
85
keys.add(key[len(base):].strip("/"))
90
if self.transport.has("HEAD"):
94
iter_files = self.transport.clone("refs").iter_files_recursive()
95
except TransportNotPossible:
98
for refname in iter_files:
99
if check_ref_format(refname):
101
keys.update(self.get_packed_refs())
104
def get_packed_refs(self):
105
"""Get contents of the packed-refs file.
107
:return: Dictionary mapping ref names to SHA1s
109
:note: Will return an empty dictionary when no packed-refs file is
112
# TODO: invalidate the cache on repacking
113
if self._packed_refs is None:
114
# set both to empty because we want _peeled_refs to be
115
# None if and only if _packed_refs is also None.
116
self._packed_refs = {}
117
self._peeled_refs = {}
119
f = self.transport.get("packed-refs")
123
first_line = iter(f).next().rstrip()
124
if (first_line.startswith("# pack-refs") and " peeled" in
126
for sha, name, peeled in read_packed_refs_with_peeled(f):
127
self._packed_refs[name] = sha
129
self._peeled_refs[name] = peeled
132
for sha, name in read_packed_refs(f):
133
self._packed_refs[name] = sha
136
return self._packed_refs
138
def get_peeled(self, name):
139
"""Return the cached peeled value of a ref, if available.
141
:param name: Name of the ref to peel
142
:return: The peeled value of the ref. If the ref is known not point to a
143
tag, this will be the SHA the ref refers to. If the ref may point to
144
a tag, but no cached information is available, None is returned.
146
self.get_packed_refs()
147
if self._peeled_refs is None or name not in self._packed_refs:
148
# No cache: no peeled refs were read, or this ref is loose
150
if name in self._peeled_refs:
151
return self._peeled_refs[name]
156
def read_loose_ref(self, name):
157
"""Read a reference file and return its contents.
159
If the reference file a symbolic reference, only read the first line of
160
the file. Otherwise, only read the first 40 bytes.
162
:param name: the refname to read, relative to refpath
163
:return: The contents of the ref file, or None if the file does not
165
:raises IOError: if any other error occurs
168
f = self.transport.get(name)
172
header = f.read(len(SYMREF))
174
# Read only the first line
175
return header + iter(f).next().rstrip("\r\n")
177
# Read only the first 40 bytes
178
return header + f.read(40-len(SYMREF))
182
def _remove_packed_ref(self, name):
183
if self._packed_refs is None:
185
# reread cached refs from disk, while holding the lock
187
self._packed_refs = None
188
self.get_packed_refs()
190
if name not in self._packed_refs:
193
del self._packed_refs[name]
194
if name in self._peeled_refs:
195
del self._peeled_refs[name]
197
write_packed_refs(f, self._packed_refs, self._peeled_refs)
199
self.transport.put_file("packed-refs", f)
201
def set_symbolic_ref(self, name, other):
202
"""Make a ref point at another ref.
204
:param name: Name of the ref to set
205
:param other: Name of the ref to point at
207
self._check_refname(name)
208
self._check_refname(other)
209
self.transport.put_bytes(name, SYMREF + other + '\n')
211
def set_if_equals(self, name, old_ref, new_ref):
212
"""Set a refname to new_ref only if it currently equals old_ref.
214
This method follows all symbolic references, and can be used to perform
215
an atomic compare-and-swap operation.
217
:param name: The refname to set.
218
:param old_ref: The old sha the refname must refer to, or None to set
220
:param new_ref: The new sha the refname will refer to.
221
:return: True if the set was successful, False otherwise.
224
realname, _ = self._follow(name)
227
self.transport.put_bytes_non_atomic(realname, new_ref+"\n",
228
create_parent_dir=True)
231
def add_if_new(self, name, ref):
232
"""Add a new reference only if it does not already exist.
234
This method follows symrefs, and only ensures that the last ref in the
235
chain does not exist.
237
:param name: The refname to set.
238
:param ref: The new sha the refname will refer to.
239
:return: True if the add was successful, False otherwise.
242
realname, contents = self._follow(name)
243
if contents is not None:
247
self._check_refname(realname)
248
self.transport.put_bytes_non_atomic(realname, ref+"\n",
249
create_parent_dir=True)
252
def remove_if_equals(self, name, old_ref):
253
"""Remove a refname only if it currently equals old_ref.
255
This method does not follow symbolic references. It can be used to
256
perform an atomic compare-and-delete operation.
258
:param name: The refname to delete.
259
:param old_ref: The old sha the refname must refer to, or None to delete
261
:return: True if the delete was successful, False otherwise.
263
self._check_refname(name)
266
self.transport.remove(name)
269
self._remove_packed_ref(name)
53
273
class TransportRepo(BaseRepo):
55
275
def __init__(self, transport):
56
276
self.transport = transport
58
if self.transport.has(".git/info/refs"):
278
if self.transport.has(".git/%s" % OBJECTDIR):
60
280
self._controltransport = self.transport.clone('.git')
61
elif self.transport.has("info/refs"):
281
elif self.transport.has(OBJECTDIR) or self.transport.has(REFSDIR):
63
283
self._controltransport = self.transport