51
43
ref_locs = ['', 'refs', 'refs/tags', 'refs/heads', 'refs/remotes']
53
45
def __init__(self, root):
54
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")):
56
self._controldir = os.path.join(root, ".git")
57
elif os.path.isdir(os.path.join(root, "objects")):
49
self._basedir = controldir
59
self._controldir = root
61
raise NotGitRepository(root)
53
self.path = controldir
63
54
self.tags = [Tag(name, ref) for name, ref in self.get_tags().items()]
64
self._object_store = None
67
return self._controldir
69
def find_missing_objects(self, determine_wants, graph_walker, progress):
70
"""Fetch the missing objects required for a set of revisions.
72
:param determine_wants: Function that takes a dictionary with heads
73
and returns the list of heads to fetch.
74
:param graph_walker: Object that can iterate over the list of revisions
75
to fetch and has an "ack" method that will be called to acknowledge
76
that a revision is present.
77
:param progress: Simple progress function that will be called with
78
updated progress strings.
80
wants = determine_wants(self.get_refs())
81
commits_to_send = set(wants)
83
ref = graph_walker.next()
86
if ref in self.object_store:
88
ref = graph_walker.next()
89
while commits_to_send:
90
sha = commits_to_send.pop()
95
assert isinstance(c, Commit)
98
commits_to_send.update([p for p in c.parents if not p in sha_done])
100
def parse_tree(tree, sha_done):
101
for mode, name, x in tree.entries():
102
if not x in sha_done:
106
parse_tree(t, sha_done)
111
if treesha not in sha_done:
112
t = self.tree(treesha)
113
sha_done.add(treesha)
114
parse_tree(t, sha_done)
116
progress("counting objects: %d\r" % len(sha_done))
119
def fetch_objects(self, determine_wants, graph_walker, progress):
120
"""Fetch the missing objects required for a set of revisions.
122
:param determine_wants: Function that takes a dictionary with heads
123
and returns the list of heads to fetch.
124
:param graph_walker: Object that can iterate over the list of revisions
125
to fetch and has an "ack" method that will be called to acknowledge
126
that a revision is present.
127
:param progress: Simple progress function that will be called with
128
updated progress strings.
130
shas = self.find_missing_objects(determine_wants, graph_walker, progress)
132
yield self.get_object(sha)
134
59
def object_dir(self):
135
return os.path.join(self.controldir(), OBJECTDIR)
138
def object_store(self):
139
if self._object_store is None:
140
self._object_store = ObjectStore(self.object_dir())
141
return self._object_store
144
return os.path.join(self.object_dir(), PACKDIR)
60
return os.path.join(self.basedir(), objectdir)
146
62
def _get_ref(self, file):
147
63
f = open(file, 'rb')
149
65
contents = f.read()
150
if contents.startswith(SYMREF):
151
ref = contents[len(SYMREF):]
66
if contents.startswith(symref):
67
ref = contents[len(symref):]
152
68
if ref[-1] == '\n':
154
70
return self.ref(ref)
155
assert len(contents) == 41, 'Invalid ref in %s' % file
71
assert len(contents) == 41, 'Invalid ref'
156
72
return contents[:-1]
160
76
def ref(self, name):
161
77
for dir in self.ref_locs:
162
file = os.path.join(self.controldir(), dir, name)
78
file = os.path.join(self.basedir(), dir, name)
163
79
if os.path.exists(file):
164
80
return self._get_ref(file)
169
ret['HEAD'] = self.head()
170
for dir in ["refs/heads", "refs/tags"]:
171
for name in os.listdir(os.path.join(self.controldir(), dir)):
172
path = os.path.join(self.controldir(), dir, name)
173
if os.path.isfile(path):
174
ret["/".join([dir, name])] = self._get_ref(path)
177
def set_ref(self, name, value):
178
file = os.path.join(self.controldir(), name)
179
open(file, 'w').write(value+"\n")
181
def remove_ref(self, name):
182
file = os.path.join(self.controldir(), name)
183
if os.path.exists(file):
187
82
def get_tags(self):
189
for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'tags')):
191
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))
196
for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'heads')):
198
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))
202
95
return self.ref('HEAD')
204
97
def _get_object(self, sha, cls):
205
assert len(sha) in (20, 40)
206
ret = self.get_object(sha)
207
if ret._type != cls._type:
209
raise NotCommitError(ret)
211
raise NotBlobError(ret)
213
raise NotTreeError(ret)
215
raise Exception("Type invalid: %r != %r" % (ret._type, cls._type))
98
assert len(sha) == 40, "Incorrect length sha: %s" % str(sha)
101
path = os.path.join(self.object_dir(), dir, file)
102
if not os.path.exists(path):
103
# Should this raise instead?
105
return cls.from_file(path)
218
107
def get_object(self, sha):
219
return self.object_store[sha]
221
def get_parents(self, sha):
222
return self.commit(sha).parents
108
return self._get_object(sha, ShaFile)
224
110
def commit(self, sha):
225
111
return self._get_object(sha, Commit)
113
def get_tree(self, sha):
228
114
return self._get_object(sha, Tree)
230
116
def get_blob(self, sha):