##// END OF EJS Templates
git: fix probable missing return...
Romain DEP. -
r45361:288328c6 default
parent child Browse files
Show More
@@ -1,297 +1,297 b''
1 1 from __future__ import absolute_import
2 2
3 3 from mercurial import (
4 4 match as matchmod,
5 5 pathutil,
6 6 pycompat,
7 7 util,
8 8 )
9 9 from mercurial.interfaces import (
10 10 repository,
11 11 util as interfaceutil,
12 12 )
13 13 from . import gitutil
14 14
15 15
16 16 pygit2 = gitutil.get_pygit2()
17 17
18 18
19 19 @interfaceutil.implementer(repository.imanifestdict)
20 20 class gittreemanifest(object):
21 21 """Expose git trees (and optionally a builder's overlay) as a manifestdict.
22 22
23 23 Very similar to mercurial.manifest.treemanifest.
24 24 """
25 25
26 26 def __init__(self, git_repo, root_tree, pending_changes):
27 27 """Initializer.
28 28
29 29 Args:
30 30 git_repo: The git_repo we're walking (required to look up child
31 31 trees).
32 32 root_tree: The root Git tree object for this manifest.
33 33 pending_changes: A dict in which pending changes will be
34 34 tracked. The enclosing memgittreemanifestctx will use this to
35 35 construct any required Tree objects in Git during it's
36 36 `write()` method.
37 37 """
38 38 self._git_repo = git_repo
39 39 self._tree = root_tree
40 40 if pending_changes is None:
41 41 pending_changes = {}
42 42 # dict of path: Optional[Tuple(node, flags)]
43 43 self._pending_changes = pending_changes
44 44
45 45 def _resolve_entry(self, path):
46 46 """Given a path, load its node and flags, or raise KeyError if missing.
47 47
48 48 This takes into account any pending writes in the builder.
49 49 """
50 50 upath = pycompat.fsdecode(path)
51 51 ent = None
52 52 if path in self._pending_changes:
53 53 val = self._pending_changes[path]
54 54 if val is None:
55 55 raise KeyError
56 56 return val
57 57 t = self._tree
58 58 comps = upath.split('/')
59 59 for comp in comps[:-1]:
60 60 te = self._tree[comp]
61 61 t = self._git_repo[te.id]
62 62 ent = t[comps[-1]]
63 63 if ent.filemode == pygit2.GIT_FILEMODE_BLOB:
64 64 flags = b''
65 65 elif ent.filemode == pygit2.GIT_FILEMODE_BLOB_EXECUTABLE:
66 66 flags = b'x'
67 67 elif ent.filemode == pygit2.GIT_FILEMODE_LINK:
68 68 flags = b'l'
69 69 else:
70 70 raise ValueError('unsupported mode %s' % oct(ent.filemode))
71 71 return ent.id.raw, flags
72 72
73 73 def __getitem__(self, path):
74 74 return self._resolve_entry(path)[0]
75 75
76 76 def find(self, path):
77 77 return self._resolve_entry(path)
78 78
79 79 def __len__(self):
80 80 return len(list(self.walk(matchmod.always())))
81 81
82 82 def __nonzero__(self):
83 83 try:
84 84 next(iter(self))
85 85 return True
86 86 except StopIteration:
87 87 return False
88 88
89 89 __bool__ = __nonzero__
90 90
91 91 def __contains__(self, path):
92 92 try:
93 93 self._resolve_entry(path)
94 94 return True
95 95 except KeyError:
96 96 return False
97 97
98 98 def iterkeys(self):
99 99 return self.walk(matchmod.always())
100 100
101 101 def keys(self):
102 102 return list(self.iterkeys())
103 103
104 104 def __iter__(self):
105 105 return self.iterkeys()
106 106
107 107 def __setitem__(self, path, node):
108 108 self._pending_changes[path] = node, self.flags(path)
109 109
110 110 def __delitem__(self, path):
111 111 # TODO: should probably KeyError for already-deleted files?
112 112 self._pending_changes[path] = None
113 113
114 114 def filesnotin(self, other, match=None):
115 115 if match is not None:
116 116 match = matchmod.badmatch(match, lambda path, msg: None)
117 117 sm2 = set(other.walk(match))
118 118 return {f for f in self.walk(match) if f not in sm2}
119 119 return {f for f in self if f not in other}
120 120
121 121 @util.propertycache
122 122 def _dirs(self):
123 123 return pathutil.dirs(self)
124 124
125 125 def hasdir(self, dir):
126 126 return dir in self._dirs
127 127
128 128 def diff(self, other, match=None, clean=False):
129 129 # TODO
130 130 assert False
131 131
132 132 def setflag(self, path, flag):
133 133 node, unused_flag = self._resolve_entry(path)
134 134 self._pending_changes[path] = node, flag
135 135
136 136 def get(self, path, default=None):
137 137 try:
138 138 return self._resolve_entry(path)[0]
139 139 except KeyError:
140 140 return default
141 141
142 142 def flags(self, path):
143 143 try:
144 144 return self._resolve_entry(path)[1]
145 145 except KeyError:
146 146 return b''
147 147
148 148 def copy(self):
149 149 pass
150 150
151 151 def items(self):
152 152 for f in self:
153 153 # TODO: build a proper iterator version of this
154 154 yield self[f]
155 155
156 156 def iteritems(self):
157 157 return self.items()
158 158
159 159 def iterentries(self):
160 160 for f in self:
161 161 # TODO: build a proper iterator version of this
162 162 yield self._resolve_entry(f)
163 163
164 164 def text(self):
165 165 assert False # TODO can this method move out of the manifest iface?
166 166
167 167 def _walkonetree(self, tree, match, subdir):
168 168 for te in tree:
169 169 # TODO: can we prune dir walks with the matcher?
170 170 realname = subdir + pycompat.fsencode(te.name)
171 171 if te.type == r'tree':
172 172 for inner in self._walkonetree(
173 173 self._git_repo[te.id], match, realname + b'/'
174 174 ):
175 175 yield inner
176 176 if not match(realname):
177 177 continue
178 178 yield pycompat.fsencode(realname)
179 179
180 180 def walk(self, match):
181 181 # TODO: this is a very lazy way to merge in the pending
182 182 # changes. There is absolutely room for optimization here by
183 183 # being clever about walking over the sets...
184 184 baseline = set(self._walkonetree(self._tree, match, b''))
185 185 deleted = {p for p, v in self._pending_changes.items() if v is None}
186 186 pend = {p for p in self._pending_changes if match(p)}
187 187 return iter(sorted((baseline | pend) - deleted))
188 188
189 189
190 190 @interfaceutil.implementer(repository.imanifestrevisionstored)
191 191 class gittreemanifestctx(object):
192 192 def __init__(self, repo, gittree):
193 193 self._repo = repo
194 194 self._tree = gittree
195 195
196 196 def read(self):
197 197 return gittreemanifest(self._repo, self._tree, None)
198 198
199 199 def readfast(self, shallow=False):
200 200 return self.read()
201 201
202 202 def copy(self):
203 203 # NB: it's important that we return a memgittreemanifestctx
204 204 # because the caller expects a mutable manifest.
205 205 return memgittreemanifestctx(self._repo, self._tree)
206 206
207 207 def find(self, path):
208 self.read()[path]
208 return self.read()[path]
209 209
210 210
211 211 @interfaceutil.implementer(repository.imanifestrevisionwritable)
212 212 class memgittreemanifestctx(object):
213 213 def __init__(self, repo, tree):
214 214 self._repo = repo
215 215 self._tree = tree
216 216 # dict of path: Optional[Tuple(node, flags)]
217 217 self._pending_changes = {}
218 218
219 219 def read(self):
220 220 return gittreemanifest(self._repo, self._tree, self._pending_changes)
221 221
222 222 def copy(self):
223 223 # TODO: if we have a builder in play, what should happen here?
224 224 # Maybe we can shuffle copy() into the immutable interface.
225 225 return memgittreemanifestctx(self._repo, self._tree)
226 226
227 227 def write(self, transaction, link, p1, p2, added, removed, match=None):
228 228 # We're not (for now, anyway) going to audit filenames, so we
229 229 # can ignore added and removed.
230 230
231 231 # TODO what does this match argument get used for? hopefully
232 232 # just narrow?
233 233 assert not match or isinstance(match, matchmod.alwaysmatcher)
234 234
235 235 touched_dirs = pathutil.dirs(list(self._pending_changes))
236 236 trees = {
237 237 b'': self._tree,
238 238 }
239 239 # path: treebuilder
240 240 builders = {
241 241 b'': self._repo.TreeBuilder(self._tree),
242 242 }
243 243 # get a TreeBuilder for every tree in the touched_dirs set
244 244 for d in sorted(touched_dirs, key=lambda x: (len(x), x)):
245 245 if d == b'':
246 246 # loaded root tree above
247 247 continue
248 248 comps = d.split(b'/')
249 249 full = b''
250 250 for part in comps:
251 251 parent = trees[full]
252 252 try:
253 253 new = self._repo[parent[pycompat.fsdecode(part)]]
254 254 except KeyError:
255 255 # new directory
256 256 new = None
257 257 full += b'/' + part
258 258 if new is not None:
259 259 # existing directory
260 260 trees[full] = new
261 261 builders[full] = self._repo.TreeBuilder(new)
262 262 else:
263 263 # new directory, use an empty dict to easily
264 264 # generate KeyError as any nested new dirs get
265 265 # created.
266 266 trees[full] = {}
267 267 builders[full] = self._repo.TreeBuilder()
268 268 for f, info in self._pending_changes.items():
269 269 if b'/' not in f:
270 270 dirname = b''
271 271 basename = f
272 272 else:
273 273 dirname, basename = f.rsplit(b'/', 1)
274 274 dirname = b'/' + dirname
275 275 if info is None:
276 276 builders[dirname].remove(pycompat.fsdecode(basename))
277 277 else:
278 278 n, fl = info
279 279 mode = {
280 280 b'': pygit2.GIT_FILEMODE_BLOB,
281 281 b'x': pygit2.GIT_FILEMODE_BLOB_EXECUTABLE,
282 282 b'l': pygit2.GIT_FILEMODE_LINK,
283 283 }[fl]
284 284 builders[dirname].insert(
285 285 pycompat.fsdecode(basename), gitutil.togitnode(n), mode
286 286 )
287 287 # This visits the buffered TreeBuilders in deepest-first
288 288 # order, bubbling up the edits.
289 289 for b in sorted(builders, key=len, reverse=True):
290 290 if b == b'':
291 291 break
292 292 cb = builders[b]
293 293 dn, bn = b.rsplit(b'/', 1)
294 294 builders[dn].insert(
295 295 pycompat.fsdecode(bn), cb.write(), pygit2.GIT_FILEMODE_TREE
296 296 )
297 297 return builders[b''].write().raw
General Comments 0
You need to be logged in to leave comments. Login now