/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

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-02-11 04:02:41 UTC
  • mfrom: (5017.2.2 tariff)
  • Revision ID: pqm@pqm.ubuntu.com-20100211040241-w6n021dz0uus341n
(mbp) add import-tariff tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# repo.py -- For dealing wih git repositories.
2
 
# Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
3
 
# Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
4
 
5
 
# This program is free software; you can redistribute it and/or
6
 
# modify it under the terms of the GNU General Public License
7
 
# as published by the Free Software Foundation; version 2
8
 
# of the License.
9
 
10
 
# This program is distributed in the hope that it will be useful,
11
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
# GNU General Public License for more details.
14
 
15
 
# You should have received a copy of the GNU General Public License
16
 
# along with this program; if not, write to the Free Software
17
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
 
# MA  02110-1301, USA.
19
 
 
20
 
import os
21
 
 
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
31
 
 
32
 
OBJECTDIR = 'objects'
33
 
PACKDIR = 'pack'
34
 
SYMREF = 'ref: '
35
 
 
36
 
 
37
 
class Tag(object):
38
 
 
39
 
    def __init__(self, name, ref):
40
 
        self.name = name
41
 
        self.ref = ref
42
 
 
43
 
 
44
 
class Repo(object):
45
 
 
46
 
  ref_locs = ['', 'refs', 'refs/tags', 'refs/heads', 'refs/remotes']
47
 
 
48
 
  def __init__(self, root):
49
 
    if os.path.isdir(os.path.join(root, ".git", "objects")):
50
 
      self.bare = False
51
 
      self._controldir = os.path.join(root, ".git")
52
 
    elif os.path.isdir(os.path.join(root, "objects")):
53
 
      self.bare = True
54
 
      self._controldir = root
55
 
    else:
56
 
      raise NotGitRepository(root)
57
 
    self.path = root
58
 
    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)
101
 
 
102
 
  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)
113
 
 
114
 
  def _get_ref(self, file):
115
 
    f = open(file, 'rb')
116
 
    try:
117
 
      contents = f.read()
118
 
      if contents.startswith(SYMREF):
119
 
        ref = contents[len(SYMREF):]
120
 
        if ref[-1] == '\n':
121
 
          ref = ref[:-1]
122
 
        return self.ref(ref)
123
 
      assert len(contents) == 41, 'Invalid ref in %s' % file
124
 
      return contents[:-1]
125
 
    finally:
126
 
      f.close()
127
 
 
128
 
  def ref(self, name):
129
 
    for dir in self.ref_locs:
130
 
      file = os.path.join(self.controldir(), dir, name)
131
 
      if os.path.exists(file):
132
 
        return self._get_ref(file)
133
 
 
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
 
  def get_tags(self):
154
 
    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))
158
 
    return ret
159
 
 
160
 
  def heads(self):
161
 
    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))
165
 
    return ret
166
 
 
167
 
  def head(self):
168
 
    return self.ref('HEAD')
169
 
 
170
 
  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
183
 
 
184
 
  def get_object(self, sha):
185
 
    return self.object_store[sha]
186
 
 
187
 
  def get_parents(self, sha):
188
 
    return self.commit(sha).parents
189
 
 
190
 
  def commit(self, sha):
191
 
    return self._get_object(sha, Commit)
192
 
 
193
 
  def tree(self, sha):
194
 
    return self._get_object(sha, Tree)
195
 
 
196
 
  def get_blob(self, sha):
197
 
    return self._get_object(sha, Blob)
198
 
 
199
 
  def revision_history(self, head):
200
 
    """Returns a list of the commits reachable from head.
201
 
 
202
 
    Returns a list of commit objects. the first of which will be the commit
203
 
    of head, then following theat will be the parents.
204
 
 
205
 
    Raises NotCommitError if any no commits are referenced, including if the
206
 
    head parameter isn't the sha of a commit.
207
 
 
208
 
    XXX: work out how to handle merges.
209
 
    """
210
 
    # We build the list backwards, as parents are more likely to be older
211
 
    # than children
212
 
    pending_commits = [head]
213
 
    history = []
214
 
    while pending_commits != []:
215
 
      head = pending_commits.pop(0)
216
 
      try:
217
 
          commit = self.commit(head)
218
 
      except KeyError:
219
 
        raise MissingCommitError(head)
220
 
      if commit in history:
221
 
        continue
222
 
      i = 0
223
 
      for known_commit in history:
224
 
        if known_commit.commit_time > commit.commit_time:
225
 
          break
226
 
        i += 1
227
 
      history.insert(i, commit)
228
 
      parents = commit.parents
229
 
      pending_commits += parents
230
 
    history.reverse()
231
 
    return history
232
 
 
233
 
  def __repr__(self):
234
 
      return "<Repo at %r>" % self.path
235
 
 
236
 
  @classmethod
237
 
  def init_bare(cls, path, mkdir=True):
238
 
      for d in [["objects"], 
239
 
                ["objects", "info"], 
240
 
                ["objects", "pack"],
241
 
                ["branches"],
242
 
                ["refs"],
243
 
                ["refs", "tags"],
244
 
                ["refs", "heads"],
245
 
                ["hooks"],
246
 
                ["info"]]:
247
 
          os.mkdir(os.path.join(path, *d))
248
 
      open(os.path.join(path, 'HEAD'), 'w').write("ref: refs/heads/master\n")
249
 
      open(os.path.join(path, 'description'), 'w').write("Unnamed repository")
250
 
      open(os.path.join(path, 'info', 'excludes'), 'w').write("")
251
 
 
252
 
  create = init_bare
253
 
 
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