51
45
ref_locs = ['', 'refs', 'refs/tags', 'refs/heads', 'refs/remotes']
53
47
def __init__(self, root):
54
if os.path.isdir(os.path.join(root, ".git", "objects")):
48
controldir = os.path.join(root, ".git")
49
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")):
51
self._basedir = controldir
59
self._controldir = root
61
raise NotGitRepository(root)
55
self.path = controldir
63
56
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
62
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
63
return os.path.join(self.basedir(), OBJECTDIR)
143
65
def pack_dir(self):
144
66
return os.path.join(self.object_dir(), PACKDIR)
69
if self._packs is None:
70
self._packs = list(load_packs(self.pack_dir()))
146
73
def _get_ref(self, file):
147
74
f = open(file, 'rb')
152
79
if ref[-1] == '\n':
154
81
return self.ref(ref)
155
assert len(contents) == 41, 'Invalid ref in %s' % file
82
assert len(contents) == 41, 'Invalid ref'
156
83
return contents[:-1]
160
87
def ref(self, name):
161
88
for dir in self.ref_locs:
162
file = os.path.join(self.controldir(), dir, name)
89
file = os.path.join(self.basedir(), dir, name)
163
90
if os.path.exists(file):
164
91
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
93
def get_tags(self):
189
for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'tags')):
95
for root, dirs, files in os.walk(os.path.join(self.basedir(), 'refs', 'tags')):
190
96
for name in files:
191
97
ret[name] = self._get_ref(os.path.join(root, name))
196
for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'heads')):
102
for root, dirs, files in os.walk(os.path.join(self.basedir(), 'refs', 'heads')):
197
103
for name in files:
198
104
ret[name] = self._get_ref(os.path.join(root, name))
202
108
return self.ref('HEAD')
204
110
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))
111
assert len(sha) == 40, "Incorrect length sha: %s" % str(sha)
114
# Check from object dir
115
path = os.path.join(self.object_dir(), dir, file)
116
if os.path.exists(path):
117
return cls.from_file(path)
119
for pack in self._get_packs():
122
# Should this raise instead?
218
125
def get_object(self, sha):
219
return self.object_store[sha]
221
def get_parents(self, sha):
222
return self.commit(sha).parents
126
return self._get_object(sha, ShaFile)
224
128
def commit(self, sha):
225
129
return self._get_object(sha, Commit)
131
def get_tree(self, sha):
228
132
return self._get_object(sha, Tree)
230
134
def get_blob(self, sha):