/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to transportgit.py

  • Committer: Jelmer Vernooij
  • Date: 2010-06-28 22:30:34 UTC
  • mto: (0.200.953 trunk)
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@samba.org-20100628223034-vylrgdyakmqoupl6
use transport repo objects even for local access.

Show diffs side-by-side

added added

removed removed

Lines of Context:
38
38
    )
39
39
from dulwich.repo import (
40
40
    BaseRepo,
41
 
    DictRefsContainer,
 
41
    RefsContainer,
 
42
    INDEX_FILENAME,
42
43
    OBJECTDIR,
43
 
    read_info_refs,
 
44
    REFSDIR,
 
45
    SYMREF,
 
46
    check_ref_format,
 
47
    read_packed_refs_with_peeled,
 
48
    read_packed_refs,
 
49
    write_packed_refs,
44
50
    )
45
51
 
46
52
from bzrlib.errors import (
50
56
    )
51
57
 
52
58
 
 
59
class TransportRefsContainer(RefsContainer):
 
60
    """Refs container that reads refs from a transport."""
 
61
 
 
62
    def __init__(self, transport):
 
63
        self.transport = transport
 
64
        self._packed_refs = None
 
65
        self._peeled_refs = None
 
66
 
 
67
    def __repr__(self):
 
68
        return "%s(%r)" % (self.__class__.__name__, self.transport)
 
69
 
 
70
    def subkeys(self, base):
 
71
        keys = set()
 
72
        path = self.refpath(base)
 
73
        try:
 
74
            iter_files = self.transport.clone(base).iter_files_recursive()
 
75
        except TransportNotPossible:
 
76
            pass
 
77
        else:
 
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("/"))
 
86
        return keys
 
87
 
 
88
    def allkeys(self):
 
89
        keys = set()
 
90
        if self.transport.has("HEAD"):
 
91
            keys.add("HEAD")
 
92
        path = ""
 
93
        try:
 
94
            iter_files = self.transport.clone("refs").iter_files_recursive()
 
95
        except TransportNotPossible:
 
96
            pass
 
97
        else:
 
98
            for refname in iter_files:
 
99
                if check_ref_format(refname):
 
100
                    keys.add(refname)
 
101
        keys.update(self.get_packed_refs())
 
102
        return keys
 
103
 
 
104
    def get_packed_refs(self):
 
105
        """Get contents of the packed-refs file.
 
106
 
 
107
        :return: Dictionary mapping ref names to SHA1s
 
108
 
 
109
        :note: Will return an empty dictionary when no packed-refs file is
 
110
            present.
 
111
        """
 
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 = {}
 
118
            try:
 
119
                f = self.transport.get("packed-refs")
 
120
            except NoSuchFile:
 
121
                return {}
 
122
            try:
 
123
                first_line = iter(f).next().rstrip()
 
124
                if (first_line.startswith("# pack-refs") and " peeled" in
 
125
                        first_line):
 
126
                    for sha, name, peeled in read_packed_refs_with_peeled(f):
 
127
                        self._packed_refs[name] = sha
 
128
                        if peeled:
 
129
                            self._peeled_refs[name] = peeled
 
130
                else:
 
131
                    f.seek(0)
 
132
                    for sha, name in read_packed_refs(f):
 
133
                        self._packed_refs[name] = sha
 
134
            finally:
 
135
                f.close()
 
136
        return self._packed_refs
 
137
 
 
138
    def get_peeled(self, name):
 
139
        """Return the cached peeled value of a ref, if available.
 
140
 
 
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.
 
145
        """
 
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
 
149
            return None
 
150
        if name in self._peeled_refs:
 
151
            return self._peeled_refs[name]
 
152
        else:
 
153
            # Known not peelable
 
154
            return self[name]
 
155
 
 
156
    def read_loose_ref(self, name):
 
157
        """Read a reference file and return its contents.
 
158
 
 
159
        If the reference file a symbolic reference, only read the first line of
 
160
        the file. Otherwise, only read the first 40 bytes.
 
161
 
 
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
 
164
            exist.
 
165
        :raises IOError: if any other error occurs
 
166
        """
 
167
        try:
 
168
            f = self.transport.get(name)
 
169
        except NoSuchFile:
 
170
            return None
 
171
        try:
 
172
            header = f.read(len(SYMREF))
 
173
            if header == SYMREF:
 
174
                # Read only the first line
 
175
                return header + iter(f).next().rstrip("\r\n")
 
176
            else:
 
177
                # Read only the first 40 bytes
 
178
                return header + f.read(40-len(SYMREF))
 
179
        finally:
 
180
            f.close()
 
181
 
 
182
    def _remove_packed_ref(self, name):
 
183
        if self._packed_refs is None:
 
184
            return
 
185
        # reread cached refs from disk, while holding the lock
 
186
 
 
187
        self._packed_refs = None
 
188
        self.get_packed_refs()
 
189
 
 
190
        if name not in self._packed_refs:
 
191
            return
 
192
 
 
193
        del self._packed_refs[name]
 
194
        if name in self._peeled_refs:
 
195
            del self._peeled_refs[name]
 
196
        f = StringIO()
 
197
        write_packed_refs(f, self._packed_refs, self._peeled_refs)
 
198
        f.seek(0)
 
199
        self.transport.put_file("packed-refs", f)
 
200
 
 
201
    def set_symbolic_ref(self, name, other):
 
202
        """Make a ref point at another ref.
 
203
 
 
204
        :param name: Name of the ref to set
 
205
        :param other: Name of the ref to point at
 
206
        """
 
207
        self._check_refname(name)
 
208
        self._check_refname(other)
 
209
        self.transport.put_bytes(name, SYMREF + other + '\n')
 
210
 
 
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.
 
213
 
 
214
        This method follows all symbolic references, and can be used to perform
 
215
        an atomic compare-and-swap operation.
 
216
 
 
217
        :param name: The refname to set.
 
218
        :param old_ref: The old sha the refname must refer to, or None to set
 
219
            unconditionally.
 
220
        :param new_ref: The new sha the refname will refer to.
 
221
        :return: True if the set was successful, False otherwise.
 
222
        """
 
223
        try:
 
224
            realname, _ = self._follow(name)
 
225
        except KeyError:
 
226
            realname = name
 
227
        self.transport.put_bytes_non_atomic(realname, new_ref+"\n",
 
228
                create_parent_dir=True)
 
229
        return True
 
230
 
 
231
    def add_if_new(self, name, ref):
 
232
        """Add a new reference only if it does not already exist.
 
233
 
 
234
        This method follows symrefs, and only ensures that the last ref in the
 
235
        chain does not exist.
 
236
 
 
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.
 
240
        """
 
241
        try:
 
242
            realname, contents = self._follow(name)
 
243
            if contents is not None:
 
244
                return False
 
245
        except KeyError:
 
246
            realname = name
 
247
        self._check_refname(realname)
 
248
        self.transport.put_bytes_non_atomic(realname, ref+"\n",
 
249
                create_parent_dir=True)
 
250
        return True
 
251
 
 
252
    def remove_if_equals(self, name, old_ref):
 
253
        """Remove a refname only if it currently equals old_ref.
 
254
 
 
255
        This method does not follow symbolic references. It can be used to
 
256
        perform an atomic compare-and-delete operation.
 
257
 
 
258
        :param name: The refname to delete.
 
259
        :param old_ref: The old sha the refname must refer to, or None to delete
 
260
            unconditionally.
 
261
        :return: True if the delete was successful, False otherwise.
 
262
        """
 
263
        self._check_refname(name)
 
264
        # may only be packed
 
265
        try:
 
266
            self.transport.remove(name)
 
267
        except NoSuchFile:
 
268
            pass
 
269
        self._remove_packed_ref(name)
 
270
        return True
 
271
 
 
272
 
53
273
class TransportRepo(BaseRepo):
54
274
 
55
275
    def __init__(self, transport):
56
276
        self.transport = transport
57
277
        try:
58
 
            if self.transport.has(".git/info/refs"):
 
278
            if self.transport.has(".git/%s" % OBJECTDIR):
59
279
                self.bare = False
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):
62
282
                self.bare = True
63
283
                self._controltransport = self.transport
64
284
            else:
67
287
            raise NotGitRepository(self.transport)
68
288
        object_store = TransportObjectStore(
69
289
            self._controltransport.clone(OBJECTDIR))
70
 
        refs = {}
71
 
        refs["HEAD"] = self._controltransport.get_bytes("HEAD").rstrip("\n")
72
 
        refs.update(read_info_refs(self._controltransport.get('info/refs')))
73
290
        super(TransportRepo, self).__init__(object_store, 
74
 
                DictRefsContainer(refs))
 
291
                TransportRefsContainer(self._controltransport))
75
292
 
76
293
    def get_named_file(self, path):
77
294
        """Get a file from the control dir with a specific name.
88
305
        except NoSuchFile:
89
306
            return None
90
307
 
 
308
    def index_path(self):
 
309
        """Return the path to the index file."""
 
310
        return self._controltransport.local_abspath(INDEX_FILENAME)
 
311
 
91
312
    def open_index(self):
92
313
        """Open the index for this repository."""
93
 
        raise NoIndexPresent()
 
314
        from dulwich.index import Index
 
315
        if not self.has_index():
 
316
            raise NoIndexPresent()
 
317
        return Index(self.index_path())
 
318
 
 
319
    def has_index(self):
 
320
        return self._controltransport.has(INDEX_FILENAME)
94
321
 
95
322
    def __repr__(self):
96
323
        return "<TransportRepo for %r>" % self.transport
159
386
    def _split_loose_object(self, sha):
160
387
        return (sha[:2], sha[2:])
161
388
 
 
389
    def _remove_loose_object(self, sha):
 
390
        path = '%s/%s' % self._split_loose_object(sha)
 
391
        self.transport.remove(path)
 
392
 
162
393
    def _get_loose_object(self, sha):
163
394
        path = '%s/%s' % self._split_loose_object(sha)
164
395
        try: