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