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

SupportĀ settingĀ tags.

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
import os
21
21
 
22
22
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
 
23
from errors import (
 
24
        MissingCommitError, 
 
25
        NotBlobError, 
 
26
        NotCommitError, 
 
27
        NotGitRepository,
 
28
        NotTreeError, 
 
29
        )
 
30
from object_store import ObjectStore
 
31
from objects import (
 
32
        ShaFile,
 
33
        Commit,
 
34
        Tree,
 
35
        Blob,
 
36
        )
31
37
 
32
38
OBJECTDIR = 'objects'
33
 
PACKDIR = 'pack'
34
39
SYMREF = 'ref: '
35
40
 
36
41
 
37
 
class Tag(object):
38
 
 
39
 
    def __init__(self, name, ref):
40
 
        self.name = name
41
 
        self.ref = ref
 
42
class Tags(object):
 
43
 
 
44
    def __init__(self, tagdir, tags):
 
45
        self.tagdir = tagdir
 
46
        self.tags = tags
 
47
 
 
48
    def __getitem__(self, name):
 
49
        return self.tags[name]
 
50
    
 
51
    def __setitem__(self, name, ref):
 
52
        self.tags[name] = ref
 
53
        f = open(os.path.join(self.tagdir, name), 'wb')
 
54
        try:
 
55
            f.write("%s\n" % ref)
 
56
        finally:
 
57
            f.close()
 
58
 
 
59
    def __len__(self):
 
60
        return len(self.tags)
 
61
 
 
62
    def iteritems(self):
 
63
        for k in self.tags:
 
64
            yield k, self[k]
42
65
 
43
66
 
44
67
class Repo(object):
55
78
    else:
56
79
      raise NotGitRepository(root)
57
80
    self.path = root
58
 
    self.tags = [Tag(name, ref) for name, ref in self.get_tags().items()]
 
81
    self.tags = Tags(self.tagdir(), self.get_tags())
59
82
    self._object_store = None
60
83
 
61
84
  def controldir(self):
62
85
    return self._controldir
63
86
 
64
 
  def fetch_objects(self, determine_wants, graph_walker, progress):
65
 
    wants = determine_wants(self.heads())
66
 
    commits_to_send = []
 
87
  def find_missing_objects(self, determine_wants, graph_walker, progress):
 
88
    """Fetch the missing objects required for a set of revisions.
 
89
 
 
90
    :param determine_wants: Function that takes a dictionary with heads 
 
91
        and returns the list of heads to fetch.
 
92
    :param graph_walker: Object that can iterate over the list of revisions 
 
93
        to fetch and has an "ack" method that will be called to acknowledge 
 
94
        that a revision is present.
 
95
    :param progress: Simple progress function that will be called with 
 
96
        updated progress strings.
 
97
    """
 
98
    wants = determine_wants(self.get_refs())
 
99
    commits_to_send = set(wants)
 
100
    sha_done = set()
67
101
    ref = graph_walker.next()
68
102
    while ref:
69
 
        commits_to_send.append(ref)
 
103
        sha_done.add(ref)
70
104
        if ref in self.object_store:
71
105
            graph_walker.ack(ref)
72
106
        ref = graph_walker.next()
73
 
    sha_done = set()
74
 
    for sha in commits_to_send:
 
107
    while commits_to_send:
 
108
        sha = commits_to_send.pop()
75
109
        if sha in sha_done:
76
110
            continue
77
111
 
78
112
        c = self.commit(sha)
 
113
        assert isinstance(c, Commit)
79
114
        sha_done.add(sha)
80
115
 
 
116
        commits_to_send.update([p for p in c.parents if not p in sha_done])
 
117
 
81
118
        def parse_tree(tree, sha_done):
82
119
            for mode, name, x in tree.entries():
83
120
                if not x in sha_done:
86
123
                        sha_done.add(x)
87
124
                        parse_tree(t, sha_done)
88
125
                    except:
89
 
                        sha_done.append(x)
 
126
                        sha_done.add(x)
90
127
 
91
128
        treesha = c.tree
92
129
        if treesha not in sha_done:
95
132
            parse_tree(t, sha_done)
96
133
 
97
134
        progress("counting objects: %d\r" % len(sha_done))
98
 
 
99
 
        for sha in sha_done:
100
 
            yield self.get_object(sha)
 
135
    return sha_done
 
136
 
 
137
  def fetch_objects(self, determine_wants, graph_walker, progress):
 
138
    """Fetch the missing objects required for a set of revisions.
 
139
 
 
140
    :param determine_wants: Function that takes a dictionary with heads 
 
141
        and returns the list of heads to fetch.
 
142
    :param graph_walker: Object that can iterate over the list of revisions 
 
143
        to fetch and has an "ack" method that will be called to acknowledge 
 
144
        that a revision is present.
 
145
    :param progress: Simple progress function that will be called with 
 
146
        updated progress strings.
 
147
    """
 
148
    shas = self.find_missing_objects(determine_wants, graph_walker, progress)
 
149
    for sha in shas:
 
150
        yield self.get_object(sha)
101
151
 
102
152
  def object_dir(self):
103
153
    return os.path.join(self.controldir(), OBJECTDIR)
132
182
        return self._get_ref(file)
133
183
 
134
184
  def get_refs(self):
135
 
    ret = {"HEAD": self.head()}
 
185
    ret = {}
 
186
    if self.head():
 
187
        ret['HEAD'] = self.head()
136
188
    for dir in ["refs/heads", "refs/tags"]:
137
189
        for name in os.listdir(os.path.join(self.controldir(), dir)):
138
190
          path = os.path.join(self.controldir(), dir, name)
150
202
      os.remove(file)
151
203
      return
152
204
 
 
205
  def tagdir(self):
 
206
    return os.path.join(self.controldir(), 'refs', 'tags')
 
207
 
153
208
  def get_tags(self):
154
209
    ret = {}
155
 
    for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'tags')):
 
210
    for root, dirs, files in os.walk(self.tagdir()):
156
211
      for name in files:
157
212
        ret[name] = self._get_ref(os.path.join(root, name))
158
213
    return ret
234
289
      return "<Repo at %r>" % self.path
235
290
 
236
291
  @classmethod
 
292
  def init(cls, path, mkdir=True):
 
293
      controldir = os.path.join(path, ".git")
 
294
      os.mkdir(controldir)
 
295
      cls.init_bare(controldir)
 
296
 
 
297
  @classmethod
237
298
  def init_bare(cls, path, mkdir=True):
238
299
      for d in [["objects"], 
239
300
                ["objects", "info"], 
252
313
  create = init_bare
253
314
 
254
315
 
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
 
316