/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 dulwich/repo.py

  • Committer: Jelmer Vernooij
  • Date: 2009-01-14 18:24:38 UTC
  • mto: (0.222.3 dulwich)
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@samba.org-20090114182438-c0tn5eczyupi4ztn
Fix download url, add version number.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
# This program is free software; you can redistribute it and/or
6
6
# modify it under the terms of the GNU General Public License
7
7
# as published by the Free Software Foundation; version 2
8
 
# of the License.
 
8
# of the License or (at your option) any later version of 
 
9
# the License.
9
10
10
11
# This program is distributed in the hope that it will be useful,
11
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
20
21
import os
21
22
 
22
23
from commit import Commit
23
 
from errors import MissingCommitError, NotBlobError, NotTreeError, NotCommitError, NotGitRepository
24
 
from objects import (ShaFile,
25
 
                     Commit,
26
 
                     Tree,
27
 
                     Blob,
28
 
                     )
29
 
from pack import load_packs, iter_sha1, PackData, write_pack_index_v2
30
 
import tempfile
 
24
from errors import (
 
25
        MissingCommitError, 
 
26
        NotBlobError, 
 
27
        NotCommitError, 
 
28
        NotGitRepository,
 
29
        NotTreeError, 
 
30
        )
 
31
from object_store import ObjectStore
 
32
from objects import (
 
33
        ShaFile,
 
34
        Commit,
 
35
        Tree,
 
36
        Blob,
 
37
        )
31
38
 
32
39
OBJECTDIR = 'objects'
33
 
PACKDIR = 'pack'
34
40
SYMREF = 'ref: '
35
41
 
36
42
 
37
 
class Tag(object):
38
 
 
39
 
    def __init__(self, name, ref):
40
 
        self.name = name
41
 
        self.ref = ref
 
43
class Tags(object):
 
44
 
 
45
    def __init__(self, tagdir, tags):
 
46
        self.tagdir = tagdir
 
47
        self.tags = tags
 
48
 
 
49
    def __getitem__(self, name):
 
50
        return self.tags[name]
 
51
    
 
52
    def __setitem__(self, name, ref):
 
53
        self.tags[name] = ref
 
54
        f = open(os.path.join(self.tagdir, name), 'wb')
 
55
        try:
 
56
            f.write("%s\n" % ref)
 
57
        finally:
 
58
            f.close()
 
59
 
 
60
    def __len__(self):
 
61
        return len(self.tags)
 
62
 
 
63
    def iteritems(self):
 
64
        for k in self.tags:
 
65
            yield k, self[k]
42
66
 
43
67
 
44
68
class Repo(object):
55
79
    else:
56
80
      raise NotGitRepository(root)
57
81
    self.path = root
58
 
    self.tags = [Tag(name, ref) for name, ref in self.get_tags().items()]
 
82
    self.tags = Tags(self.tagdir(), self.get_tags())
59
83
    self._object_store = None
60
84
 
61
85
  def controldir(self):
62
86
    return self._controldir
63
87
 
64
 
  def fetch_objects(self, determine_wants, graph_walker, progress):
65
 
    wants = determine_wants(self.heads())
66
 
    commits_to_send = []
 
88
  def find_missing_objects(self, determine_wants, graph_walker, progress):
 
89
    """Fetch the missing objects required for a set of revisions.
 
90
 
 
91
    :param determine_wants: Function that takes a dictionary with heads 
 
92
        and returns the list of heads to fetch.
 
93
    :param graph_walker: Object that can iterate over the list of revisions 
 
94
        to fetch and has an "ack" method that will be called to acknowledge 
 
95
        that a revision is present.
 
96
    :param progress: Simple progress function that will be called with 
 
97
        updated progress strings.
 
98
    """
 
99
    wants = determine_wants(self.get_refs())
 
100
    commits_to_send = set(wants)
 
101
    sha_done = set()
67
102
    ref = graph_walker.next()
68
103
    while ref:
69
 
        commits_to_send.append(ref)
 
104
        sha_done.add(ref)
70
105
        if ref in self.object_store:
71
106
            graph_walker.ack(ref)
72
107
        ref = graph_walker.next()
73
 
    sha_done = set()
74
 
    for sha in commits_to_send:
 
108
    while commits_to_send:
 
109
        sha = commits_to_send.pop()
75
110
        if sha in sha_done:
76
111
            continue
77
112
 
78
113
        c = self.commit(sha)
 
114
        assert isinstance(c, Commit)
79
115
        sha_done.add(sha)
80
116
 
 
117
        commits_to_send.update([p for p in c.parents if not p in sha_done])
 
118
 
81
119
        def parse_tree(tree, sha_done):
82
120
            for mode, name, x in tree.entries():
83
121
                if not x in sha_done:
86
124
                        sha_done.add(x)
87
125
                        parse_tree(t, sha_done)
88
126
                    except:
89
 
                        sha_done.append(x)
 
127
                        sha_done.add(x)
90
128
 
91
129
        treesha = c.tree
92
130
        if treesha not in sha_done:
95
133
            parse_tree(t, sha_done)
96
134
 
97
135
        progress("counting objects: %d\r" % len(sha_done))
98
 
 
99
 
        for sha in sha_done:
100
 
            yield self.get_object(sha)
 
136
    return sha_done
 
137
 
 
138
  def fetch_objects(self, determine_wants, graph_walker, progress):
 
139
    """Fetch the missing objects required for a set of revisions.
 
140
 
 
141
    :param determine_wants: Function that takes a dictionary with heads 
 
142
        and returns the list of heads to fetch.
 
143
    :param graph_walker: Object that can iterate over the list of revisions 
 
144
        to fetch and has an "ack" method that will be called to acknowledge 
 
145
        that a revision is present.
 
146
    :param progress: Simple progress function that will be called with 
 
147
        updated progress strings.
 
148
    """
 
149
    shas = self.find_missing_objects(determine_wants, graph_walker, progress)
 
150
    for sha in shas:
 
151
        yield self.get_object(sha)
101
152
 
102
153
  def object_dir(self):
103
154
    return os.path.join(self.controldir(), OBJECTDIR)
132
183
        return self._get_ref(file)
133
184
 
134
185
  def get_refs(self):
135
 
    ret = {"HEAD": self.head()}
 
186
    ret = {}
 
187
    if self.head():
 
188
        ret['HEAD'] = self.head()
136
189
    for dir in ["refs/heads", "refs/tags"]:
137
190
        for name in os.listdir(os.path.join(self.controldir(), dir)):
138
191
          path = os.path.join(self.controldir(), dir, name)
150
203
      os.remove(file)
151
204
      return
152
205
 
 
206
  def tagdir(self):
 
207
    return os.path.join(self.controldir(), 'refs', 'tags')
 
208
 
153
209
  def get_tags(self):
154
210
    ret = {}
155
 
    for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'tags')):
 
211
    for root, dirs, files in os.walk(self.tagdir()):
156
212
      for name in files:
157
213
        ret[name] = self._get_ref(os.path.join(root, name))
158
214
    return ret
234
290
      return "<Repo at %r>" % self.path
235
291
 
236
292
  @classmethod
 
293
  def init(cls, path, mkdir=True):
 
294
      controldir = os.path.join(path, ".git")
 
295
      os.mkdir(controldir)
 
296
      cls.init_bare(controldir)
 
297
 
 
298
  @classmethod
237
299
  def init_bare(cls, path, mkdir=True):
238
300
      for d in [["objects"], 
239
301
                ["objects", "info"], 
252
314
  create = init_bare
253
315
 
254
316
 
255
 
class ObjectStore(object):
256
 
 
257
 
    def __init__(self, path):
258
 
        self.path = path
259
 
        self._packs = None
260
 
 
261
 
    def pack_dir(self):
262
 
        return os.path.join(self.path, PACKDIR)
263
 
 
264
 
    def __contains__(self, sha):
265
 
        # TODO: This can be more efficient
266
 
        try:
267
 
            self[sha]
268
 
            return True
269
 
        except KeyError:
270
 
            return False
271
 
 
272
 
    @property
273
 
    def packs(self):
274
 
        """List with pack objects."""
275
 
        if self._packs is None:
276
 
            self._packs = list(load_packs(self.pack_dir()))
277
 
        return self._packs
278
 
 
279
 
    def _get_shafile(self, sha):
280
 
        dir = sha[:2]
281
 
        file = sha[2:]
282
 
        # Check from object dir
283
 
        path = os.path.join(self.path, dir, file)
284
 
        if os.path.exists(path):
285
 
          return ShaFile.from_file(path)
286
 
        return None
287
 
 
288
 
    def get_raw(self, sha):
289
 
        """Obtain the raw text for an object.
290
 
        
291
 
        :param sha: Sha for the object.
292
 
        :return: tuple with object type and object contents.
293
 
        """
294
 
        for pack in self.packs:
295
 
            if sha in pack:
296
 
                return pack.get_raw(sha, self.get_raw)
297
 
        # FIXME: Are pack deltas ever against on-disk shafiles ?
298
 
        ret = self._get_shafile(sha)
299
 
        if ret is not None:
300
 
            return ret.as_raw_string()
301
 
        raise KeyError(sha)
302
 
 
303
 
    def __getitem__(self, sha):
304
 
        assert len(sha) == 40, "Incorrect length sha: %s" % str(sha)
305
 
        ret = self._get_shafile(sha)
306
 
        if ret is not None:
307
 
            return ret
308
 
        # Check from packs
309
 
        type, uncomp = self.get_raw(sha)
310
 
        return ShaFile.from_raw_string(type, uncomp)
311
 
 
312
 
    def move_in_pack(self, path):
313
 
        """Move a specific file containing a pack into the pack directory.
314
 
 
315
 
        :note: The file should be on the same file system as the 
316
 
            packs directory.
317
 
 
318
 
        :param path: Path to the pack file.
319
 
        """
320
 
        p = PackData(path)
321
 
        entries = p.sorted_entries(self.get_raw)
322
 
        basename = os.path.join(self.pack_dir(), 
323
 
            "pack-%s" % iter_sha1(entry[0] for entry in entries))
324
 
        write_pack_index_v2(basename+".idx", entries, p.calculate_checksum())
325
 
        os.rename(path, basename + ".pack")
326
 
 
327
 
    def add_pack(self):
328
 
        """Add a new pack to this object store. 
329
 
 
330
 
        :return: Fileobject to write to and a commit function to 
331
 
            call when the pack is finished.
332
 
        """
333
 
        fd, path = tempfile.mkstemp(dir=self.pack_dir(), suffix=".pack")
334
 
        f = os.fdopen(fd, 'w')
335
 
        def commit():
336
 
            if os.path.getsize(path) > 0:
337
 
                self.move_in_pack(path)
338
 
        return f, commit
 
317