/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: 2008-12-12 15:37:07 UTC
  • mto: (0.215.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@samba.org-20081212153707-usxaq2v8agvmp91k
Change README to be about Dulwich rather than Python-git.

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
 
23
from errors import MissingCommitError
24
24
from objects import (ShaFile,
25
25
                     Commit,
26
26
                     Tree,
27
27
                     Blob,
28
28
                     )
29
 
from pack import load_packs, iter_sha1, PackData, write_pack_index_v2
30
 
import tempfile
31
29
 
32
 
OBJECTDIR = 'objects'
33
 
PACKDIR = 'pack'
34
 
SYMREF = 'ref: '
 
30
objectdir = 'objects'
 
31
symref = 'ref: '
35
32
 
36
33
 
37
34
class Tag(object):
38
35
 
39
36
    def __init__(self, name, ref):
40
37
        self.name = name
41
 
        self.ref = ref
 
38
        self.commit = Commit(ref)
42
39
 
43
40
 
44
41
class Repo(object):
46
43
  ref_locs = ['', 'refs', 'refs/tags', 'refs/heads', 'refs/remotes']
47
44
 
48
45
  def __init__(self, root):
49
 
    if os.path.isdir(os.path.join(root, ".git", "objects")):
 
46
    controldir = os.path.join(root, ".git")
 
47
    if os.path.exists(os.path.join(controldir, "objects")):
50
48
      self.bare = False
51
 
      self._controldir = os.path.join(root, ".git")
52
 
    elif os.path.isdir(os.path.join(root, "objects")):
 
49
      self._basedir = controldir
 
50
    else:
53
51
      self.bare = True
54
 
      self._controldir = root
55
 
    else:
56
 
      raise NotGitRepository(root)
57
 
    self.path = root
 
52
      self._basedir = root
 
53
    self.path = controldir
58
54
    self.tags = [Tag(name, ref) for name, ref in self.get_tags().items()]
59
 
    self._object_store = None
60
 
 
61
 
  def controldir(self):
62
 
    return self._controldir
63
 
 
64
 
  def fetch_objects(self, determine_wants, graph_walker, progress):
65
 
    wants = determine_wants(self.heads())
66
 
    commits_to_send = []
67
 
    ref = graph_walker.next()
68
 
    while ref:
69
 
        commits_to_send.append(ref)
70
 
        if ref in self.object_store:
71
 
            graph_walker.ack(ref)
72
 
        ref = graph_walker.next()
73
 
    sha_done = set()
74
 
    for sha in commits_to_send:
75
 
        if sha in sha_done:
76
 
            continue
77
 
 
78
 
        c = self.commit(sha)
79
 
        sha_done.add(sha)
80
 
 
81
 
        def parse_tree(tree, sha_done):
82
 
            for mode, name, x in tree.entries():
83
 
                if not x in sha_done:
84
 
                    try:
85
 
                        t = self.tree(x)
86
 
                        sha_done.add(x)
87
 
                        parse_tree(t, sha_done)
88
 
                    except:
89
 
                        sha_done.append(x)
90
 
 
91
 
        treesha = c.tree
92
 
        if treesha not in sha_done:
93
 
            t = self.tree(treesha)
94
 
            sha_done.add(treesha)
95
 
            parse_tree(t, sha_done)
96
 
 
97
 
        progress("counting objects: %d\r" % len(sha_done))
98
 
 
99
 
        for sha in sha_done:
100
 
            yield self.get_object(sha)
 
55
 
 
56
  def basedir(self):
 
57
    return self._basedir
101
58
 
102
59
  def object_dir(self):
103
 
    return os.path.join(self.controldir(), OBJECTDIR)
104
 
 
105
 
  @property
106
 
  def object_store(self):
107
 
    if self._object_store is None:
108
 
        self._object_store = ObjectStore(self.object_dir())
109
 
    return self._object_store
110
 
 
111
 
  def pack_dir(self):
112
 
    return os.path.join(self.object_dir(), PACKDIR)
 
60
    return os.path.join(self.basedir(), objectdir)
113
61
 
114
62
  def _get_ref(self, file):
115
63
    f = open(file, 'rb')
116
64
    try:
117
65
      contents = f.read()
118
 
      if contents.startswith(SYMREF):
119
 
        ref = contents[len(SYMREF):]
 
66
      if contents.startswith(symref):
 
67
        ref = contents[len(symref):]
120
68
        if ref[-1] == '\n':
121
69
          ref = ref[:-1]
122
70
        return self.ref(ref)
123
 
      assert len(contents) == 41, 'Invalid ref in %s' % file
 
71
      assert len(contents) == 41, 'Invalid ref'
124
72
      return contents[:-1]
125
73
    finally:
126
74
      f.close()
127
75
 
128
76
  def ref(self, name):
129
77
    for dir in self.ref_locs:
130
 
      file = os.path.join(self.controldir(), dir, name)
 
78
      file = os.path.join(self.basedir(), dir, name)
131
79
      if os.path.exists(file):
132
80
        return self._get_ref(file)
133
81
 
134
 
  def get_refs(self):
135
 
    ret = {"HEAD": self.head()}
136
 
    for dir in ["refs/heads", "refs/tags"]:
137
 
        for name in os.listdir(os.path.join(self.controldir(), dir)):
138
 
          path = os.path.join(self.controldir(), dir, name)
139
 
          if os.path.isfile(path):
140
 
            ret["/".join([dir, name])] = self._get_ref(path)
141
 
    return ret
142
 
 
143
 
  def set_ref(self, name, value):
144
 
    file = os.path.join(self.controldir(), name)
145
 
    open(file, 'w').write(value+"\n")
146
 
 
147
 
  def remove_ref(self, name):
148
 
    file = os.path.join(self.controldir(), name)
149
 
    if os.path.exists(file):
150
 
      os.remove(file)
151
 
      return
152
 
 
153
82
  def get_tags(self):
154
83
    ret = {}
155
 
    for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'tags')):
156
 
      for name in files:
157
 
        ret[name] = self._get_ref(os.path.join(root, name))
 
84
    for name in os.listdir(os.path.join(self.basedir(), 'refs', 'tags')):
 
85
      ret[name] = self._get_ref(os.path.join(self.basedir(), 'refs', 'tags', name))
158
86
    return ret
159
87
 
160
88
  def heads(self):
161
89
    ret = {}
162
 
    for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'heads')):
163
 
      for name in files:
164
 
        ret[name] = self._get_ref(os.path.join(root, name))
 
90
    for name in os.listdir(os.path.join(self.basedir(), 'refs', 'heads')):
 
91
      ret[name] = self._get_ref(os.path.join(self.basedir(), 'refs', 'heads', name))
165
92
    return ret
166
93
 
167
94
  def head(self):
168
95
    return self.ref('HEAD')
169
96
 
170
97
  def _get_object(self, sha, cls):
171
 
    assert len(sha) in (20, 40)
172
 
    ret = self.get_object(sha)
173
 
    if ret._type != cls._type:
174
 
        if cls is Commit:
175
 
            raise NotCommitError(ret)
176
 
        elif cls is Blob:
177
 
            raise NotBlobError(ret)
178
 
        elif cls is Tree:
179
 
            raise NotTreeError(ret)
180
 
        else:
181
 
            raise Exception("Type invalid: %r != %r" % (ret._type, cls._type))
182
 
    return ret
 
98
    assert len(sha) == 40, "Incorrect length sha: %s" % str(sha)
 
99
    dir = sha[:2]
 
100
    file = sha[2:]
 
101
    path = os.path.join(self.object_dir(), dir, file)
 
102
    if not os.path.exists(path):
 
103
      # Should this raise instead?
 
104
      return None
 
105
    return cls.from_file(path)
183
106
 
184
107
  def get_object(self, sha):
185
 
    return self.object_store[sha]
186
 
 
187
 
  def get_parents(self, sha):
188
 
    return self.commit(sha).parents
 
108
    return self._get_object(sha, ShaFile)
189
109
 
190
110
  def commit(self, sha):
191
111
    return self._get_object(sha, Commit)
192
112
 
193
 
  def tree(self, sha):
 
113
  def get_tree(self, sha):
194
114
    return self._get_object(sha, Tree)
195
115
 
196
116
  def get_blob(self, sha):
213
133
    history = []
214
134
    while pending_commits != []:
215
135
      head = pending_commits.pop(0)
216
 
      try:
217
 
          commit = self.commit(head)
218
 
      except KeyError:
 
136
      commit = self.commit(head)
 
137
      if commit is None:
219
138
        raise MissingCommitError(head)
220
139
      if commit in history:
221
140
        continue
222
141
      i = 0
223
142
      for known_commit in history:
224
 
        if known_commit.commit_time > commit.commit_time:
 
143
        if known_commit.commit_time() > commit.commit_time():
225
144
          break
226
145
        i += 1
227
146
      history.insert(i, commit)
228
 
      parents = commit.parents
 
147
      parents = commit.parents()
229
148
      pending_commits += parents
230
149
    history.reverse()
231
150
    return history
232
151
 
233
 
  def __repr__(self):
234
 
      return "<Repo at %r>" % self.path
235
 
 
236
152
  @classmethod
237
153
  def init_bare(cls, path, mkdir=True):
238
154
      for d in [["objects"], 
251
167
 
252
168
  create = init_bare
253
169
 
254
 
 
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