##// END OF EJS Templates
largefiles: remove unnecessary check of instance...
Sean Farley -
r19570:f69ebcb0 default
parent child Browse files
Show More
@@ -1,513 +1,506 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''setup for largefiles repositories: reposetup'''
10 10 import copy
11 11 import os
12 12
13 from mercurial import context, error, manifest, match as match_, util, \
14 discovery
13 from mercurial import error, manifest, match as match_, util, discovery
15 14 from mercurial import node as node_
16 15 from mercurial.i18n import _
17 16 from mercurial import localrepo
18 17
19 18 import lfcommands
20 19 import proto
21 20 import lfutil
22 21
23 22 def reposetup(ui, repo):
24 23 # wire repositories should be given new wireproto functions but not the
25 24 # other largefiles modifications
26 25 if not repo.local():
27 26 return proto.wirereposetup(ui, repo)
28 27
29 28 class lfilesrepo(repo.__class__):
30 29 lfstatus = False
31 30 def status_nolfiles(self, *args, **kwargs):
32 31 return super(lfilesrepo, self).status(*args, **kwargs)
33 32
34 33 # When lfstatus is set, return a context that gives the names
35 34 # of largefiles instead of their corresponding standins and
36 35 # identifies the largefiles as always binary, regardless of
37 36 # their actual contents.
38 37 def __getitem__(self, changeid):
39 38 ctx = super(lfilesrepo, self).__getitem__(changeid)
40 39 if self.lfstatus:
41 40 class lfilesmanifestdict(manifest.manifestdict):
42 41 def __contains__(self, filename):
43 42 if super(lfilesmanifestdict,
44 43 self).__contains__(filename):
45 44 return True
46 45 return super(lfilesmanifestdict,
47 46 self).__contains__(lfutil.standin(filename))
48 47 class lfilesctx(ctx.__class__):
49 48 def files(self):
50 49 filenames = super(lfilesctx, self).files()
51 50 return [lfutil.splitstandin(f) or f for f in filenames]
52 51 def manifest(self):
53 52 man1 = super(lfilesctx, self).manifest()
54 53 man1.__class__ = lfilesmanifestdict
55 54 return man1
56 55 def filectx(self, path, fileid=None, filelog=None):
57 56 try:
58 57 if filelog is not None:
59 58 result = super(lfilesctx, self).filectx(
60 59 path, fileid, filelog)
61 60 else:
62 61 result = super(lfilesctx, self).filectx(
63 62 path, fileid)
64 63 except error.LookupError:
65 64 # Adding a null character will cause Mercurial to
66 65 # identify this as a binary file.
67 66 if filelog is not None:
68 67 result = super(lfilesctx, self).filectx(
69 68 lfutil.standin(path), fileid, filelog)
70 69 else:
71 70 result = super(lfilesctx, self).filectx(
72 71 lfutil.standin(path), fileid)
73 72 olddata = result.data
74 73 result.data = lambda: olddata() + '\0'
75 74 return result
76 75 ctx.__class__ = lfilesctx
77 76 return ctx
78 77
79 78 # Figure out the status of big files and insert them into the
80 79 # appropriate list in the result. Also removes standin files
81 80 # from the listing. Revert to the original status if
82 81 # self.lfstatus is False.
83 82 # XXX large file status is buggy when used on repo proxy.
84 83 # XXX this needs to be investigated.
85 84 @localrepo.unfilteredmethod
86 85 def status(self, node1='.', node2=None, match=None, ignored=False,
87 86 clean=False, unknown=False, listsubrepos=False):
88 87 listignored, listclean, listunknown = ignored, clean, unknown
89 88 if not self.lfstatus:
90 89 return super(lfilesrepo, self).status(node1, node2, match,
91 90 listignored, listclean, listunknown, listsubrepos)
92 91 else:
93 92 # some calls in this function rely on the old version of status
94 93 self.lfstatus = False
95 if isinstance(node1, context.changectx):
96 ctx1 = node1
97 else:
98 ctx1 = self[node1]
99 if isinstance(node2, context.changectx):
100 ctx2 = node2
101 else:
102 ctx2 = self[node2]
94 ctx1 = self[node1]
95 ctx2 = self[node2]
103 96 working = ctx2.rev() is None
104 97 parentworking = working and ctx1 == self['.']
105 98
106 99 def inctx(file, ctx):
107 100 try:
108 101 if ctx.rev() is None:
109 102 return file in ctx.manifest()
110 103 ctx[file]
111 104 return True
112 105 except KeyError:
113 106 return False
114 107
115 108 if match is None:
116 109 match = match_.always(self.root, self.getcwd())
117 110
118 111 wlock = None
119 112 try:
120 113 try:
121 114 # updating the dirstate is optional
122 115 # so we don't wait on the lock
123 116 wlock = self.wlock(False)
124 117 except error.LockError:
125 118 pass
126 119
127 120 # First check if there were files specified on the
128 121 # command line. If there were, and none of them were
129 122 # largefiles, we should just bail here and let super
130 123 # handle it -- thus gaining a big performance boost.
131 124 lfdirstate = lfutil.openlfdirstate(ui, self)
132 125 if match.files() and not match.anypats():
133 126 for f in lfdirstate:
134 127 if match(f):
135 128 break
136 129 else:
137 130 return super(lfilesrepo, self).status(node1, node2,
138 131 match, listignored, listclean,
139 132 listunknown, listsubrepos)
140 133
141 134 # Create a copy of match that matches standins instead
142 135 # of largefiles.
143 136 def tostandins(files):
144 137 if not working:
145 138 return files
146 139 newfiles = []
147 140 dirstate = self.dirstate
148 141 for f in files:
149 142 sf = lfutil.standin(f)
150 143 if sf in dirstate:
151 144 newfiles.append(sf)
152 145 elif sf in dirstate.dirs():
153 146 # Directory entries could be regular or
154 147 # standin, check both
155 148 newfiles.extend((f, sf))
156 149 else:
157 150 newfiles.append(f)
158 151 return newfiles
159 152
160 153 m = copy.copy(match)
161 154 m._files = tostandins(m._files)
162 155
163 156 result = super(lfilesrepo, self).status(node1, node2, m,
164 157 ignored, clean, unknown, listsubrepos)
165 158 if working:
166 159
167 160 def sfindirstate(f):
168 161 sf = lfutil.standin(f)
169 162 dirstate = self.dirstate
170 163 return sf in dirstate or sf in dirstate.dirs()
171 164
172 165 match._files = [f for f in match._files
173 166 if sfindirstate(f)]
174 167 # Don't waste time getting the ignored and unknown
175 168 # files from lfdirstate
176 169 s = lfdirstate.status(match, [], False,
177 170 listclean, False)
178 171 (unsure, modified, added, removed, missing, _unknown,
179 172 _ignored, clean) = s
180 173 if parentworking:
181 174 for lfile in unsure:
182 175 standin = lfutil.standin(lfile)
183 176 if standin not in ctx1:
184 177 # from second parent
185 178 modified.append(lfile)
186 179 elif ctx1[standin].data().strip() \
187 180 != lfutil.hashfile(self.wjoin(lfile)):
188 181 modified.append(lfile)
189 182 else:
190 183 clean.append(lfile)
191 184 lfdirstate.normal(lfile)
192 185 else:
193 186 tocheck = unsure + modified + added + clean
194 187 modified, added, clean = [], [], []
195 188
196 189 for lfile in tocheck:
197 190 standin = lfutil.standin(lfile)
198 191 if inctx(standin, ctx1):
199 192 if ctx1[standin].data().strip() != \
200 193 lfutil.hashfile(self.wjoin(lfile)):
201 194 modified.append(lfile)
202 195 else:
203 196 clean.append(lfile)
204 197 else:
205 198 added.append(lfile)
206 199
207 200 # Standins no longer found in lfdirstate has been
208 201 # removed
209 202 for standin in ctx1.manifest():
210 203 if not lfutil.isstandin(standin):
211 204 continue
212 205 lfile = lfutil.splitstandin(standin)
213 206 if not match(lfile):
214 207 continue
215 208 if lfile not in lfdirstate:
216 209 removed.append(lfile)
217 210
218 211 # Filter result lists
219 212 result = list(result)
220 213
221 214 # Largefiles are not really removed when they're
222 215 # still in the normal dirstate. Likewise, normal
223 216 # files are not really removed if they are still in
224 217 # lfdirstate. This happens in merges where files
225 218 # change type.
226 219 removed = [f for f in removed
227 220 if f not in self.dirstate]
228 221 result[2] = [f for f in result[2]
229 222 if f not in lfdirstate]
230 223
231 224 lfiles = set(lfdirstate._map)
232 225 # Unknown files
233 226 result[4] = set(result[4]).difference(lfiles)
234 227 # Ignored files
235 228 result[5] = set(result[5]).difference(lfiles)
236 229 # combine normal files and largefiles
237 230 normals = [[fn for fn in filelist
238 231 if not lfutil.isstandin(fn)]
239 232 for filelist in result]
240 233 lfiles = (modified, added, removed, missing, [], [],
241 234 clean)
242 235 result = [sorted(list1 + list2)
243 236 for (list1, list2) in zip(normals, lfiles)]
244 237 else:
245 238 def toname(f):
246 239 if lfutil.isstandin(f):
247 240 return lfutil.splitstandin(f)
248 241 return f
249 242 result = [[toname(f) for f in items]
250 243 for items in result]
251 244
252 245 if wlock:
253 246 lfdirstate.write()
254 247
255 248 finally:
256 249 if wlock:
257 250 wlock.release()
258 251
259 252 if not listunknown:
260 253 result[4] = []
261 254 if not listignored:
262 255 result[5] = []
263 256 if not listclean:
264 257 result[6] = []
265 258 self.lfstatus = True
266 259 return result
267 260
268 261 # As part of committing, copy all of the largefiles into the
269 262 # cache.
270 263 def commitctx(self, *args, **kwargs):
271 264 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
272 265 lfutil.copyalltostore(self, node)
273 266 return node
274 267
275 268 # Before commit, largefile standins have not had their
276 269 # contents updated to reflect the hash of their largefile.
277 270 # Do that here.
278 271 def commit(self, text="", user=None, date=None, match=None,
279 272 force=False, editor=False, extra={}):
280 273 orig = super(lfilesrepo, self).commit
281 274
282 275 wlock = self.wlock()
283 276 try:
284 277 # Case 0: Rebase or Transplant
285 278 # We have to take the time to pull down the new largefiles now.
286 279 # Otherwise, any largefiles that were modified in the
287 280 # destination changesets get overwritten, either by the rebase
288 281 # or in the first commit after the rebase or transplant.
289 282 # updatelfiles will update the dirstate to mark any pulled
290 283 # largefiles as modified
291 284 if getattr(self, "_isrebasing", False) or \
292 285 getattr(self, "_istransplanting", False):
293 286 lfcommands.updatelfiles(self.ui, self, filelist=None,
294 287 printmessage=False)
295 288 result = orig(text=text, user=user, date=date, match=match,
296 289 force=force, editor=editor, extra=extra)
297 290 return result
298 291 # Case 1: user calls commit with no specific files or
299 292 # include/exclude patterns: refresh and commit all files that
300 293 # are "dirty".
301 294 if ((match is None) or
302 295 (not match.anypats() and not match.files())):
303 296 # Spend a bit of time here to get a list of files we know
304 297 # are modified so we can compare only against those.
305 298 # It can cost a lot of time (several seconds)
306 299 # otherwise to update all standins if the largefiles are
307 300 # large.
308 301 lfdirstate = lfutil.openlfdirstate(ui, self)
309 302 dirtymatch = match_.always(self.root, self.getcwd())
310 303 s = lfdirstate.status(dirtymatch, [], False, False, False)
311 304 (unsure, modified, added, removed, _missing, _unknown,
312 305 _ignored, _clean) = s
313 306 modifiedfiles = unsure + modified + added + removed
314 307 lfiles = lfutil.listlfiles(self)
315 308 # this only loops through largefiles that exist (not
316 309 # removed/renamed)
317 310 for lfile in lfiles:
318 311 if lfile in modifiedfiles:
319 312 if os.path.exists(
320 313 self.wjoin(lfutil.standin(lfile))):
321 314 # this handles the case where a rebase is being
322 315 # performed and the working copy is not updated
323 316 # yet.
324 317 if os.path.exists(self.wjoin(lfile)):
325 318 lfutil.updatestandin(self,
326 319 lfutil.standin(lfile))
327 320 lfdirstate.normal(lfile)
328 321
329 322 result = orig(text=text, user=user, date=date, match=match,
330 323 force=force, editor=editor, extra=extra)
331 324
332 325 if result is not None:
333 326 for lfile in lfdirstate:
334 327 if lfile in modifiedfiles:
335 328 if (not os.path.exists(self.wjoin(
336 329 lfutil.standin(lfile)))) or \
337 330 (not os.path.exists(self.wjoin(lfile))):
338 331 lfdirstate.drop(lfile)
339 332
340 333 # This needs to be after commit; otherwise precommit hooks
341 334 # get the wrong status
342 335 lfdirstate.write()
343 336 return result
344 337
345 338 lfiles = lfutil.listlfiles(self)
346 339 match._files = self._subdirlfs(match.files(), lfiles)
347 340
348 341 # Case 2: user calls commit with specified patterns: refresh
349 342 # any matching big files.
350 343 smatcher = lfutil.composestandinmatcher(self, match)
351 344 standins = self.dirstate.walk(smatcher, [], False, False)
352 345
353 346 # No matching big files: get out of the way and pass control to
354 347 # the usual commit() method.
355 348 if not standins:
356 349 return orig(text=text, user=user, date=date, match=match,
357 350 force=force, editor=editor, extra=extra)
358 351
359 352 # Refresh all matching big files. It's possible that the
360 353 # commit will end up failing, in which case the big files will
361 354 # stay refreshed. No harm done: the user modified them and
362 355 # asked to commit them, so sooner or later we're going to
363 356 # refresh the standins. Might as well leave them refreshed.
364 357 lfdirstate = lfutil.openlfdirstate(ui, self)
365 358 for standin in standins:
366 359 lfile = lfutil.splitstandin(standin)
367 360 if lfdirstate[lfile] != 'r':
368 361 lfutil.updatestandin(self, standin)
369 362 lfdirstate.normal(lfile)
370 363 else:
371 364 lfdirstate.drop(lfile)
372 365
373 366 # Cook up a new matcher that only matches regular files or
374 367 # standins corresponding to the big files requested by the
375 368 # user. Have to modify _files to prevent commit() from
376 369 # complaining "not tracked" for big files.
377 370 match = copy.copy(match)
378 371 origmatchfn = match.matchfn
379 372
380 373 # Check both the list of largefiles and the list of
381 374 # standins because if a largefile was removed, it
382 375 # won't be in the list of largefiles at this point
383 376 match._files += sorted(standins)
384 377
385 378 actualfiles = []
386 379 for f in match._files:
387 380 fstandin = lfutil.standin(f)
388 381
389 382 # ignore known largefiles and standins
390 383 if f in lfiles or fstandin in standins:
391 384 continue
392 385
393 386 # append directory separator to avoid collisions
394 387 if not fstandin.endswith(os.sep):
395 388 fstandin += os.sep
396 389
397 390 actualfiles.append(f)
398 391 match._files = actualfiles
399 392
400 393 def matchfn(f):
401 394 if origmatchfn(f):
402 395 return f not in lfiles
403 396 else:
404 397 return f in standins
405 398
406 399 match.matchfn = matchfn
407 400 result = orig(text=text, user=user, date=date, match=match,
408 401 force=force, editor=editor, extra=extra)
409 402 # This needs to be after commit; otherwise precommit hooks
410 403 # get the wrong status
411 404 lfdirstate.write()
412 405 return result
413 406 finally:
414 407 wlock.release()
415 408
416 409 def push(self, remote, force=False, revs=None, newbranch=False):
417 410 outgoing = discovery.findcommonoutgoing(repo, remote.peer(),
418 411 force=force)
419 412 if outgoing.missing:
420 413 toupload = set()
421 414 o = self.changelog.nodesbetween(outgoing.missing, revs)[0]
422 415 for n in o:
423 416 parents = [p for p in self.changelog.parents(n)
424 417 if p != node_.nullid]
425 418 ctx = self[n]
426 419 files = set(ctx.files())
427 420 if len(parents) == 2:
428 421 mc = ctx.manifest()
429 422 mp1 = ctx.parents()[0].manifest()
430 423 mp2 = ctx.parents()[1].manifest()
431 424 for f in mp1:
432 425 if f not in mc:
433 426 files.add(f)
434 427 for f in mp2:
435 428 if f not in mc:
436 429 files.add(f)
437 430 for f in mc:
438 431 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
439 432 None):
440 433 files.add(f)
441 434
442 435 toupload = toupload.union(
443 436 set([ctx[f].data().strip()
444 437 for f in files
445 438 if lfutil.isstandin(f) and f in ctx]))
446 439 lfcommands.uploadlfiles(ui, self, remote, toupload)
447 440 return super(lfilesrepo, self).push(remote, force, revs,
448 441 newbranch)
449 442
450 443 def _subdirlfs(self, files, lfiles):
451 444 '''
452 445 Adjust matched file list
453 446 If we pass a directory to commit whose only commitable files
454 447 are largefiles, the core commit code aborts before finding
455 448 the largefiles.
456 449 So we do the following:
457 450 For directories that only have largefiles as matches,
458 451 we explicitly add the largefiles to the match list and remove
459 452 the directory.
460 453 In other cases, we leave the match list unmodified.
461 454 '''
462 455 actualfiles = []
463 456 dirs = []
464 457 regulars = []
465 458
466 459 for f in files:
467 460 if lfutil.isstandin(f + '/'):
468 461 raise util.Abort(
469 462 _('file "%s" is a largefile standin') % f,
470 463 hint=('commit the largefile itself instead'))
471 464 # Scan directories
472 465 if os.path.isdir(self.wjoin(f)):
473 466 dirs.append(f)
474 467 else:
475 468 regulars.append(f)
476 469
477 470 for f in dirs:
478 471 matcheddir = False
479 472 d = self.dirstate.normalize(f) + '/'
480 473 # Check for matched normal files
481 474 for mf in regulars:
482 475 if self.dirstate.normalize(mf).startswith(d):
483 476 actualfiles.append(f)
484 477 matcheddir = True
485 478 break
486 479 if not matcheddir:
487 480 # If no normal match, manually append
488 481 # any matching largefiles
489 482 for lf in lfiles:
490 483 if self.dirstate.normalize(lf).startswith(d):
491 484 actualfiles.append(lf)
492 485 if not matcheddir:
493 486 actualfiles.append(lfutil.standin(f))
494 487 matcheddir = True
495 488 # Nothing in dir, so readd it
496 489 # and let commit reject it
497 490 if not matcheddir:
498 491 actualfiles.append(f)
499 492
500 493 # Always add normal files
501 494 actualfiles += regulars
502 495 return actualfiles
503 496
504 497 repo.__class__ = lfilesrepo
505 498
506 499 def checkrequireslfiles(ui, repo, **kwargs):
507 500 if 'largefiles' not in repo.requirements and util.any(
508 501 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
509 502 repo.requirements.add('largefiles')
510 503 repo._writerequirements()
511 504
512 505 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
513 506 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
General Comments 0
You need to be logged in to leave comments. Login now