/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

Add convenience method for getting missing objects iterator.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""A Git repository implementation that uses a Bazaar transport."""
18
18
 
19
 
from cStringIO import StringIO
20
19
 
21
20
from dulwich.errors import (
22
21
    NotGitRepository,
38
37
    )
39
38
from dulwich.repo import (
40
39
    BaseRepo,
41
 
    RefsContainer,
42
 
    INDEX_FILENAME,
 
40
    DictRefsContainer,
43
41
    OBJECTDIR,
44
 
    REFSDIR,
45
 
    SYMREF,
46
 
    check_ref_format,
47
 
    read_packed_refs_with_peeled,
48
 
    read_packed_refs,
49
 
    write_packed_refs,
 
42
    read_info_refs,
50
43
    )
51
44
 
52
45
from bzrlib.errors import (
56
49
    )
57
50
 
58
51
 
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 _ensure_dir_exists(self, path):
71
 
        for n in range(path.count("/")):
72
 
            dirname = "/".join(path.split("/")[:n+1])
73
 
            try:
74
 
                self.transport.mkdir(dirname)
75
 
            except FileExists:
76
 
                pass
77
 
 
78
 
    def subkeys(self, base):
79
 
        keys = set()
80
 
        try:
81
 
            iter_files = self.transport.clone(base).iter_files_recursive()
82
 
            keys.update(("%s/%s" % (base, refname)).strip("/") for 
83
 
                    refname in iter_files if check_ref_format("%s/%s" % (base, refname)))
84
 
        except (TransportNotPossible, NoSuchFile):
85
 
            pass
86
 
        for key in self.get_packed_refs():
87
 
            if key.startswith(base):
88
 
                keys.add(key[len(base):].strip("/"))
89
 
        return keys
90
 
 
91
 
    def allkeys(self):
92
 
        keys = set()
93
 
        if self.transport.has("HEAD"):
94
 
            keys.add("HEAD")
95
 
        try:
96
 
            iter_files = list(self.transport.clone("refs").iter_files_recursive())
97
 
            for filename in iter_files:
98
 
                refname = "refs/%s" % filename
99
 
                if check_ref_format(refname):
100
 
                    keys.add(refname)
101
 
        except (TransportNotPossible, NoSuchFile):
102
 
            pass
103
 
        keys.update(self.get_packed_refs())
104
 
        return keys
105
 
 
106
 
    def get_packed_refs(self):
107
 
        """Get contents of the packed-refs file.
108
 
 
109
 
        :return: Dictionary mapping ref names to SHA1s
110
 
 
111
 
        :note: Will return an empty dictionary when no packed-refs file is
112
 
            present.
113
 
        """
114
 
        # TODO: invalidate the cache on repacking
115
 
        if self._packed_refs is None:
116
 
            # set both to empty because we want _peeled_refs to be
117
 
            # None if and only if _packed_refs is also None.
118
 
            self._packed_refs = {}
119
 
            self._peeled_refs = {}
120
 
            try:
121
 
                f = self.transport.get("packed-refs")
122
 
            except NoSuchFile:
123
 
                return {}
124
 
            try:
125
 
                first_line = iter(f).next().rstrip()
126
 
                if (first_line.startswith("# pack-refs") and " peeled" in
127
 
                        first_line):
128
 
                    for sha, name, peeled in read_packed_refs_with_peeled(f):
129
 
                        self._packed_refs[name] = sha
130
 
                        if peeled:
131
 
                            self._peeled_refs[name] = peeled
132
 
                else:
133
 
                    f.seek(0)
134
 
                    for sha, name in read_packed_refs(f):
135
 
                        self._packed_refs[name] = sha
136
 
            finally:
137
 
                f.close()
138
 
        return self._packed_refs
139
 
 
140
 
    def get_peeled(self, name):
141
 
        """Return the cached peeled value of a ref, if available.
142
 
 
143
 
        :param name: Name of the ref to peel
144
 
        :return: The peeled value of the ref. If the ref is known not point to a
145
 
            tag, this will be the SHA the ref refers to. If the ref may point to
146
 
            a tag, but no cached information is available, None is returned.
147
 
        """
148
 
        self.get_packed_refs()
149
 
        if self._peeled_refs is None or name not in self._packed_refs:
150
 
            # No cache: no peeled refs were read, or this ref is loose
151
 
            return None
152
 
        if name in self._peeled_refs:
153
 
            return self._peeled_refs[name]
154
 
        else:
155
 
            # Known not peelable
156
 
            return self[name]
157
 
 
158
 
    def read_loose_ref(self, name):
159
 
        """Read a reference file and return its contents.
160
 
 
161
 
        If the reference file a symbolic reference, only read the first line of
162
 
        the file. Otherwise, only read the first 40 bytes.
163
 
 
164
 
        :param name: the refname to read, relative to refpath
165
 
        :return: The contents of the ref file, or None if the file does not
166
 
            exist.
167
 
        :raises IOError: if any other error occurs
168
 
        """
169
 
        try:
170
 
            f = self.transport.get(name)
171
 
        except NoSuchFile:
172
 
            return None
173
 
        try:
174
 
            header = f.read(len(SYMREF))
175
 
            if header == SYMREF:
176
 
                # Read only the first line
177
 
                return header + iter(f).next().rstrip("\r\n")
178
 
            else:
179
 
                # Read only the first 40 bytes
180
 
                return header + f.read(40-len(SYMREF))
181
 
        finally:
182
 
            f.close()
183
 
 
184
 
    def _remove_packed_ref(self, name):
185
 
        if self._packed_refs is None:
186
 
            return
187
 
        # reread cached refs from disk, while holding the lock
188
 
 
189
 
        self._packed_refs = None
190
 
        self.get_packed_refs()
191
 
 
192
 
        if name not in self._packed_refs:
193
 
            return
194
 
 
195
 
        del self._packed_refs[name]
196
 
        if name in self._peeled_refs:
197
 
            del self._peeled_refs[name]
198
 
        f = self.transport.open_write_stream("packed-refs")
199
 
        try:
200
 
            write_packed_refs(f, self._packed_refs, self._peeled_refs)
201
 
        finally:
202
 
            f.close()
203
 
 
204
 
    def set_symbolic_ref(self, name, other):
205
 
        """Make a ref point at another ref.
206
 
 
207
 
        :param name: Name of the ref to set
208
 
        :param other: Name of the ref to point at
209
 
        """
210
 
        self._check_refname(name)
211
 
        self._check_refname(other)
212
 
        self._ensure_dir_exists(name)
213
 
        self.transport.put_bytes(name, SYMREF + other + '\n')
214
 
 
215
 
    def set_if_equals(self, name, old_ref, new_ref):
216
 
        """Set a refname to new_ref only if it currently equals old_ref.
217
 
 
218
 
        This method follows all symbolic references, and can be used to perform
219
 
        an atomic compare-and-swap operation.
220
 
 
221
 
        :param name: The refname to set.
222
 
        :param old_ref: The old sha the refname must refer to, or None to set
223
 
            unconditionally.
224
 
        :param new_ref: The new sha the refname will refer to.
225
 
        :return: True if the set was successful, False otherwise.
226
 
        """
227
 
        try:
228
 
            realname, _ = self._follow(name)
229
 
        except KeyError:
230
 
            realname = name
231
 
        self._ensure_dir_exists(realname)
232
 
        self.transport.put_bytes(realname, new_ref+"\n")
233
 
        return True
234
 
 
235
 
    def add_if_new(self, name, ref):
236
 
        """Add a new reference only if it does not already exist.
237
 
 
238
 
        This method follows symrefs, and only ensures that the last ref in the
239
 
        chain does not exist.
240
 
 
241
 
        :param name: The refname to set.
242
 
        :param ref: The new sha the refname will refer to.
243
 
        :return: True if the add was successful, False otherwise.
244
 
        """
245
 
        try:
246
 
            realname, contents = self._follow(name)
247
 
            if contents is not None:
248
 
                return False
249
 
        except KeyError:
250
 
            realname = name
251
 
        self._check_refname(realname)
252
 
        self._ensure_dir_exists(realname)
253
 
        self.transport.put_bytes(realname, ref+"\n")
254
 
        return True
255
 
 
256
 
    def remove_if_equals(self, name, old_ref):
257
 
        """Remove a refname only if it currently equals old_ref.
258
 
 
259
 
        This method does not follow symbolic references. It can be used to
260
 
        perform an atomic compare-and-delete operation.
261
 
 
262
 
        :param name: The refname to delete.
263
 
        :param old_ref: The old sha the refname must refer to, or None to delete
264
 
            unconditionally.
265
 
        :return: True if the delete was successful, False otherwise.
266
 
        """
267
 
        self._check_refname(name)
268
 
        # may only be packed
269
 
        try:
270
 
            self.transport.delete(name)
271
 
        except NoSuchFile:
272
 
            pass
273
 
        self._remove_packed_ref(name)
274
 
        return True
275
 
 
276
 
 
277
52
class TransportRepo(BaseRepo):
278
53
 
279
54
    def __init__(self, transport):
280
55
        self.transport = transport
281
56
        try:
282
 
            if self.transport.has(".git/%s" % OBJECTDIR):
 
57
            if self.transport.has(".git/info/refs"):
283
58
                self.bare = False
284
59
                self._controltransport = self.transport.clone('.git')
285
 
            elif self.transport.has(OBJECTDIR) or self.transport.has(REFSDIR):
 
60
            elif self.transport.has("info/refs"):
286
61
                self.bare = True
287
62
                self._controltransport = self.transport
288
63
            else:
291
66
            raise NotGitRepository(self.transport)
292
67
        object_store = TransportObjectStore(
293
68
            self._controltransport.clone(OBJECTDIR))
 
69
        refs = {}
 
70
        refs["HEAD"] = self._controltransport.get_bytes("HEAD").rstrip("\n")
 
71
        refs.update(read_info_refs(self._controltransport.get('info/refs')))
294
72
        super(TransportRepo, self).__init__(object_store, 
295
 
                TransportRefsContainer(self._controltransport))
 
73
                DictRefsContainer(refs))
296
74
 
297
75
    def get_named_file(self, path):
298
76
        """Get a file from the control dir with a specific name.
309
87
        except NoSuchFile:
310
88
            return None
311
89
 
312
 
    def index_path(self):
313
 
        """Return the path to the index file."""
314
 
        return self._controltransport.local_abspath(INDEX_FILENAME)
315
 
 
316
90
    def open_index(self):
317
91
        """Open the index for this repository."""
318
 
        from dulwich.index import Index
319
 
        if not self.has_index():
320
 
            raise NoIndexPresent()
321
 
        return Index(self.index_path())
322
 
 
323
 
    def has_index(self):
324
 
        """Check if an index is present."""
325
 
        # Bare repos must never have index files; non-bare repos may have a
326
 
        # missing index file, which is treated as empty.
327
 
        return not self.bare
 
92
        raise NoIndexPresent()
328
93
 
329
94
    def __repr__(self):
330
95
        return "<TransportRepo for %r>" % self.transport
370
135
                    size = self.pack_transport.stat(name).st_size
371
136
                except TransportNotPossible:
372
137
                    # FIXME: This reads the whole pack file at once
 
138
                    from cStringIO import StringIO
373
139
                    f = self.pack_transport.get(name)
374
140
                    contents = f.read()
375
141
                    pd = PackData(name, StringIO(contents), size=len(contents))
378
144
                            size=size)
379
145
                idxname = name.replace(".pack", ".idx")
380
146
                idx = load_pack_index_file(idxname, self.pack_transport.get(idxname))
381
 
                pack = Pack.from_objects(pd, idx)
382
 
                ret.append(pack)
 
147
                ret.append(Pack.from_objects(pd, idx))
383
148
        return ret
384
149
 
385
150
    def _iter_loose_objects(self):
392
157
    def _split_loose_object(self, sha):
393
158
        return (sha[:2], sha[2:])
394
159
 
395
 
    def _remove_loose_object(self, sha):
396
 
        path = '%s/%s' % self._split_loose_object(sha)
397
 
        self.transport.delete(path)
398
 
 
399
160
    def _get_loose_object(self, sha):
400
161
        path = '%s/%s' % self._split_loose_object(sha)
401
162
        try:
432
193
        basename = "pack-%s" % iter_sha1(entry[0] for entry in entries)
433
194
        f.seek(0)
434
195
        self.pack_transport.put_file(basename + ".pack", f)
435
 
        idxfile = self.pack_transport.open_write_stream(basename + ".idx")
436
 
        try:
437
 
            write_pack_index_v2(idxfile, entries, p.get_stored_checksum())
438
 
        finally:
439
 
            idxfile.close()
440
 
        idxfile = self.pack_transport.get(basename + ".idx")
441
 
        idx = load_pack_index_file(basename+".idx", idxfile)
 
196
        index_path = self.pack_transport.local_abspath(basename+".idx")
 
197
        write_pack_index_v2(index_path, entries, p.get_stored_checksum())
 
198
        idx = load_pack_index_file(basename+".idx",
 
199
                self.pack_transport.get(basename+".idx"))
442
200
        final_pack = Pack.from_objects(p, idx)
443
201
        self._add_known_pack(final_pack)
444
202
        return final_pack