comparison hgext/largefiles/lfutil.py @ 45942:89a2afe31e82

formating: upgrade to black 20.8b1 This required a couple of small tweaks to un-confuse black, but now it works. Big formatting changes come from: * Dramatically improved collection-splitting logic upstream * Black having a strong (correct IMO) opinion that """ is better than ''' Differential Revision: https://phab.mercurial-scm.org/D9430
author Augie Fackler <raf@durin42.com>
date Fri, 27 Nov 2020 17:03:29 -0500
parents ca82929e433d
children 59fa3890d40a
comparison
equal deleted inserted replaced
45941:346af7687c6f 45942:89a2afe31e82
78 dstf.write(chunk) 78 dstf.write(chunk)
79 os.chmod(dest, os.stat(src).st_mode) 79 os.chmod(dest, os.stat(src).st_mode)
80 80
81 81
82 def usercachepath(ui, hash): 82 def usercachepath(ui, hash):
83 '''Return the correct location in the "global" largefiles cache for a file 83 """Return the correct location in the "global" largefiles cache for a file
84 with the given hash. 84 with the given hash.
85 This cache is used for sharing of largefiles across repositories - both 85 This cache is used for sharing of largefiles across repositories - both
86 to preserve download bandwidth and storage space.''' 86 to preserve download bandwidth and storage space."""
87 return os.path.join(_usercachedir(ui), hash) 87 return os.path.join(_usercachedir(ui), hash)
88 88
89 89
90 def _usercachedir(ui, name=longname): 90 def _usercachedir(ui, name=longname):
91 '''Return the location of the "global" largefiles cache.''' 91 '''Return the location of the "global" largefiles cache.'''
141 path = usercachepath(ui, hash) 141 path = usercachepath(ui, hash)
142 return os.path.exists(path) 142 return os.path.exists(path)
143 143
144 144
145 def findfile(repo, hash): 145 def findfile(repo, hash):
146 '''Return store path of the largefile with the specified hash. 146 """Return store path of the largefile with the specified hash.
147 As a side effect, the file might be linked from user cache. 147 As a side effect, the file might be linked from user cache.
148 Return None if the file can't be found locally.''' 148 Return None if the file can't be found locally."""
149 path, exists = findstorepath(repo, hash) 149 path, exists = findstorepath(repo, hash)
150 if exists: 150 if exists:
151 repo.ui.note(_(b'found %s in store\n') % hash) 151 repo.ui.note(_(b'found %s in store\n') % hash)
152 return path 152 return path
153 elif inusercache(repo.ui, hash): 153 elif inusercache(repo.ui, hash):
189 # (2) avoid develwarn 'use dirstate.write with ....' 189 # (2) avoid develwarn 'use dirstate.write with ....'
190 super(largefilesdirstate, self).write(None) 190 super(largefilesdirstate, self).write(None)
191 191
192 192
193 def openlfdirstate(ui, repo, create=True): 193 def openlfdirstate(ui, repo, create=True):
194 ''' 194 """
195 Return a dirstate object that tracks largefiles: i.e. its root is 195 Return a dirstate object that tracks largefiles: i.e. its root is
196 the repo root, but it is saved in .hg/largefiles/dirstate. 196 the repo root, but it is saved in .hg/largefiles/dirstate.
197 ''' 197 """
198 vfs = repo.vfs 198 vfs = repo.vfs
199 lfstoredir = longname 199 lfstoredir = longname
200 opener = vfsmod.vfs(vfs.join(lfstoredir)) 200 opener = vfsmod.vfs(vfs.join(lfstoredir))
201 lfdirstate = largefilesdirstate( 201 lfdirstate = largefilesdirstate(
202 opener, 202 opener,
243 lfdirstate.normal(lfile) 243 lfdirstate.normal(lfile)
244 return s 244 return s
245 245
246 246
247 def listlfiles(repo, rev=None, matcher=None): 247 def listlfiles(repo, rev=None, matcher=None):
248 '''return a list of largefiles in the working copy or the 248 """return a list of largefiles in the working copy or the
249 specified changeset''' 249 specified changeset"""
250 250
251 if matcher is None: 251 if matcher is None:
252 matcher = getstandinmatcher(repo) 252 matcher = getstandinmatcher(repo)
253 253
254 # ignore unknown files in working directory 254 # ignore unknown files in working directory
263 '''Return true if a largefile with the given hash exists in the store''' 263 '''Return true if a largefile with the given hash exists in the store'''
264 return os.path.exists(storepath(repo, hash, forcelocal)) 264 return os.path.exists(storepath(repo, hash, forcelocal))
265 265
266 266
267 def storepath(repo, hash, forcelocal=False): 267 def storepath(repo, hash, forcelocal=False):
268 '''Return the correct location in the repository largefiles store for a 268 """Return the correct location in the repository largefiles store for a
269 file with the given hash.''' 269 file with the given hash."""
270 if not forcelocal and repo.shared(): 270 if not forcelocal and repo.shared():
271 return repo.vfs.reljoin(repo.sharedpath, longname, hash) 271 return repo.vfs.reljoin(repo.sharedpath, longname, hash)
272 return repo.vfs.join(longname, hash) 272 return repo.vfs.join(longname, hash)
273 273
274 274
275 def findstorepath(repo, hash): 275 def findstorepath(repo, hash):
276 '''Search through the local store path(s) to find the file for the given 276 """Search through the local store path(s) to find the file for the given
277 hash. If the file is not found, its path in the primary store is returned. 277 hash. If the file is not found, its path in the primary store is returned.
278 The return value is a tuple of (path, exists(path)). 278 The return value is a tuple of (path, exists(path)).
279 ''' 279 """
280 # For shared repos, the primary store is in the share source. But for 280 # For shared repos, the primary store is in the share source. But for
281 # backward compatibility, force a lookup in the local store if it wasn't 281 # backward compatibility, force a lookup in the local store if it wasn't
282 # found in the share source. 282 # found in the share source.
283 path = storepath(repo, hash, False) 283 path = storepath(repo, hash, False)
284 284
289 289
290 return (path, False) 290 return (path, False)
291 291
292 292
293 def copyfromcache(repo, hash, filename): 293 def copyfromcache(repo, hash, filename):
294 '''Copy the specified largefile from the repo or system cache to 294 """Copy the specified largefile from the repo or system cache to
295 filename in the repository. Return true on success or false if the 295 filename in the repository. Return true on success or false if the
296 file was not found in either cache (which should not happened: 296 file was not found in either cache (which should not happened:
297 this is meant to be called only after ensuring that the needed 297 this is meant to be called only after ensuring that the needed
298 largefile exists in the cache).''' 298 largefile exists in the cache)."""
299 wvfs = repo.wvfs 299 wvfs = repo.wvfs
300 path = findfile(repo, hash) 300 path = findfile(repo, hash)
301 if path is None: 301 if path is None:
302 return False 302 return False
303 wvfs.makedirs(wvfs.dirname(wvfs.join(filename))) 303 wvfs.makedirs(wvfs.dirname(wvfs.join(filename)))
352 dstf.write(chunk) 352 dstf.write(chunk)
353 linktousercache(repo, hash) 353 linktousercache(repo, hash)
354 354
355 355
356 def linktousercache(repo, hash): 356 def linktousercache(repo, hash):
357 '''Link / copy the largefile with the specified hash from the store 357 """Link / copy the largefile with the specified hash from the store
358 to the cache.''' 358 to the cache."""
359 path = usercachepath(repo.ui, hash) 359 path = usercachepath(repo.ui, hash)
360 link(storepath(repo, hash), path) 360 link(storepath(repo, hash), path)
361 361
362 362
363 def getstandinmatcher(repo, rmatcher=None): 363 def getstandinmatcher(repo, rmatcher=None):
378 match = scmutil.match(repo[None], [wvfs.join(standindir)], badfn=badfn) 378 match = scmutil.match(repo[None], [wvfs.join(standindir)], badfn=badfn)
379 return match 379 return match
380 380
381 381
382 def composestandinmatcher(repo, rmatcher): 382 def composestandinmatcher(repo, rmatcher):
383 '''Return a matcher that accepts standins corresponding to the 383 """Return a matcher that accepts standins corresponding to the
384 files accepted by rmatcher. Pass the list of files in the matcher 384 files accepted by rmatcher. Pass the list of files in the matcher
385 as the paths specified by the user.''' 385 as the paths specified by the user."""
386 smatcher = getstandinmatcher(repo, rmatcher) 386 smatcher = getstandinmatcher(repo, rmatcher)
387 isstandin = smatcher.matchfn 387 isstandin = smatcher.matchfn
388 388
389 def composedmatchfn(f): 389 def composedmatchfn(f):
390 return isstandin(f) and rmatcher.matchfn(splitstandin(f)) 390 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
393 393
394 return smatcher 394 return smatcher
395 395
396 396
397 def standin(filename): 397 def standin(filename):
398 '''Return the repo-relative path to the standin for the specified big 398 """Return the repo-relative path to the standin for the specified big
399 file.''' 399 file."""
400 # Notes: 400 # Notes:
401 # 1) Some callers want an absolute path, but for instance addlargefiles 401 # 1) Some callers want an absolute path, but for instance addlargefiles
402 # needs it repo-relative so it can be passed to repo[None].add(). So 402 # needs it repo-relative so it can be passed to repo[None].add(). So
403 # leave it up to the caller to use repo.wjoin() to get an absolute path. 403 # leave it up to the caller to use repo.wjoin() to get an absolute path.
404 # 2) Join with '/' because that's what dirstate always uses, even on 404 # 2) Join with '/' because that's what dirstate always uses, even on
406 # passed filenames from an external source (like the command line). 406 # passed filenames from an external source (like the command line).
407 return shortnameslash + util.pconvert(filename) 407 return shortnameslash + util.pconvert(filename)
408 408
409 409
410 def isstandin(filename): 410 def isstandin(filename):
411 '''Return true if filename is a big file standin. filename must be 411 """Return true if filename is a big file standin. filename must be
412 in Mercurial's internal form (slash-separated).''' 412 in Mercurial's internal form (slash-separated)."""
413 return filename.startswith(shortnameslash) 413 return filename.startswith(shortnameslash)
414 414
415 415
416 def splitstandin(filename): 416 def splitstandin(filename):
417 # Split on / because that's what dirstate always uses, even on Windows. 417 # Split on / because that's what dirstate always uses, even on Windows.
437 else: 437 else:
438 raise error.Abort(_(b'%s: file not found!') % lfile) 438 raise error.Abort(_(b'%s: file not found!') % lfile)
439 439
440 440
441 def readasstandin(fctx): 441 def readasstandin(fctx):
442 '''read hex hash from given filectx of standin file 442 """read hex hash from given filectx of standin file
443 443
444 This encapsulates how "standin" data is stored into storage layer.''' 444 This encapsulates how "standin" data is stored into storage layer."""
445 return fctx.data().strip() 445 return fctx.data().strip()
446 446
447 447
448 def writestandin(repo, standin, hash, executable): 448 def writestandin(repo, standin, hash, executable):
449 '''write hash to <repo.root>/<standin>''' 449 '''write hash to <repo.root>/<standin>'''
450 repo.wwrite(standin, hash + b'\n', executable and b'x' or b'') 450 repo.wwrite(standin, hash + b'\n', executable and b'x' or b'')
451 451
452 452
453 def copyandhash(instream, outfile): 453 def copyandhash(instream, outfile):
454 '''Read bytes from instream (iterable) and write them to outfile, 454 """Read bytes from instream (iterable) and write them to outfile,
455 computing the SHA-1 hash of the data along the way. Return the hash.''' 455 computing the SHA-1 hash of the data along the way. Return the hash."""
456 hasher = hashutil.sha1(b'') 456 hasher = hashutil.sha1(b'')
457 for data in instream: 457 for data in instream:
458 hasher.update(data) 458 hasher.update(data)
459 outfile.write(data) 459 outfile.write(data)
460 return hex(hasher.digest()) 460 return hex(hasher.digest())
633 if isstandin(fn) and fn in ctx: 633 if isstandin(fn) and fn in ctx:
634 addfunc(fn, readasstandin(ctx[fn])) 634 addfunc(fn, readasstandin(ctx[fn]))
635 635
636 636
637 def updatestandinsbymatch(repo, match): 637 def updatestandinsbymatch(repo, match):
638 '''Update standins in the working directory according to specified match 638 """Update standins in the working directory according to specified match
639 639
640 This returns (possibly modified) ``match`` object to be used for 640 This returns (possibly modified) ``match`` object to be used for
641 subsequent commit process. 641 subsequent commit process.
642 ''' 642 """
643 643
644 ui = repo.ui 644 ui = repo.ui
645 645
646 # Case 1: user calls commit with no specific files or 646 # Case 1: user calls commit with no specific files or
647 # include/exclude patterns: refresh and commit all files that 647 # include/exclude patterns: refresh and commit all files that
739 739
740 return match 740 return match
741 741
742 742
743 class automatedcommithook(object): 743 class automatedcommithook(object):
744 '''Stateful hook to update standins at the 1st commit of resuming 744 """Stateful hook to update standins at the 1st commit of resuming
745 745
746 For efficiency, updating standins in the working directory should 746 For efficiency, updating standins in the working directory should
747 be avoided while automated committing (like rebase, transplant and 747 be avoided while automated committing (like rebase, transplant and
748 so on), because they should be updated before committing. 748 so on), because they should be updated before committing.
749 749
750 But the 1st commit of resuming automated committing (e.g. ``rebase 750 But the 1st commit of resuming automated committing (e.g. ``rebase
751 --continue``) should update them, because largefiles may be 751 --continue``) should update them, because largefiles may be
752 modified manually. 752 modified manually.
753 ''' 753 """
754 754
755 def __init__(self, resuming): 755 def __init__(self, resuming):
756 self.resuming = resuming 756 self.resuming = resuming
757 757
758 def __call__(self, repo, match): 758 def __call__(self, repo, match):
762 else: 762 else:
763 return match 763 return match
764 764
765 765
766 def getstatuswriter(ui, repo, forcibly=None): 766 def getstatuswriter(ui, repo, forcibly=None):
767 '''Return the function to write largefiles specific status out 767 """Return the function to write largefiles specific status out
768 768
769 If ``forcibly`` is ``None``, this returns the last element of 769 If ``forcibly`` is ``None``, this returns the last element of
770 ``repo._lfstatuswriters`` as "default" writer function. 770 ``repo._lfstatuswriters`` as "default" writer function.
771 771
772 Otherwise, this returns the function to always write out (or 772 Otherwise, this returns the function to always write out (or
773 ignore if ``not forcibly``) status. 773 ignore if ``not forcibly``) status.
774 ''' 774 """
775 if forcibly is None and util.safehasattr(repo, b'_largefilesenabled'): 775 if forcibly is None and util.safehasattr(repo, b'_largefilesenabled'):
776 return repo._lfstatuswriters[-1] 776 return repo._lfstatuswriters[-1]
777 else: 777 else:
778 if forcibly: 778 if forcibly:
779 return ui.status # forcibly WRITE OUT 779 return ui.status # forcibly WRITE OUT