Mercurial > hg
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 |