comparison mercurial/subrepo.py @ 51879:3b8d92f71d92

archive: defer opening the output until a file is matched Before, if no file is matched, an error is thrown, but the archive is created anyway. When using hgweb, an error 500 is returned as the response body already exists when the error is seen. Afterwards, the archive is created before the first match is emitted. If no match is found, no archive is created. This is more consistent behavior as an empty archive is not a representable in all output formats, e.g. tar archives.
author Joerg Sonnenberger <joerg@bec.de>
date Wed, 15 Nov 2023 22:11:34 +0100
parents a1e4fa9330d8
children f4733654f144
comparison
equal deleted inserted replaced
51878:7933bcb02bfc 51879:3b8d92f71d92
361 361
362 def printfiles(self, ui, m, uipathfn, fm, fmt, subrepos): 362 def printfiles(self, ui, m, uipathfn, fm, fmt, subrepos):
363 """handle the files command for this subrepo""" 363 """handle the files command for this subrepo"""
364 return 1 364 return 1
365 365
366 def archive( 366 def archive(self, opener, prefix, match: matchmod.basematcher, decode=True):
367 self, archiver, prefix, match: matchmod.basematcher, decode=True
368 ):
369 files = [f for f in self.files() if match(f)] 367 files = [f for f in self.files() if match(f)]
370 total = len(files) 368 total = len(files)
371 relpath = subrelpath(self) 369 relpath = subrelpath(self)
372 progress = self.ui.makeprogress( 370 progress = self.ui.makeprogress(
373 _(b'archiving (%s)') % relpath, unit=_(b'files'), total=total 371 _(b'archiving (%s)') % relpath, unit=_(b'files'), total=total
374 ) 372 )
375 progress.update(0) 373 progress.update(0)
374 archiver = None
376 for name in files: 375 for name in files:
377 flags = self.fileflags(name) 376 flags = self.fileflags(name)
378 mode = b'x' in flags and 0o755 or 0o644 377 mode = b'x' in flags and 0o755 or 0o644
379 symlink = b'l' in flags 378 symlink = b'l' in flags
379 if archiver is None:
380 archiver = opener()
380 archiver.addfile( 381 archiver.addfile(
381 prefix + name, mode, symlink, self.filedata(name, decode) 382 prefix + name, mode, symlink, self.filedata(name, decode)
382 ) 383 )
383 progress.increment() 384 progress.increment()
384 progress.complete() 385 progress.complete()
649 _(b'warning: error "%s" in subrepository "%s"\n') 650 _(b'warning: error "%s" in subrepository "%s"\n')
650 % (inst, subrelpath(self)) 651 % (inst, subrelpath(self))
651 ) 652 )
652 653
653 @annotatesubrepoerror 654 @annotatesubrepoerror
654 def archive( 655 def archive(self, opener, prefix, match: matchmod.basematcher, decode=True):
655 self, archiver, prefix, match: matchmod.basematcher, decode=True
656 ):
657 self._get(self._state + (b'hg',)) 656 self._get(self._state + (b'hg',))
658 files = [f for f in self.files() if match(f)] 657 files = [f for f in self.files() if match(f)]
659 rev = self._state[1] 658 rev = self._state[1]
660 ctx = self._repo[rev] 659 ctx = self._repo[rev]
661 scmutil.prefetchfiles( 660 scmutil.prefetchfiles(
662 self._repo, [(ctx.rev(), scmutil.matchfiles(self._repo, files))] 661 self._repo, [(ctx.rev(), scmutil.matchfiles(self._repo, files))]
663 ) 662 )
664 total = abstractsubrepo.archive(self, archiver, prefix, match) 663 total = abstractsubrepo.archive(self, opener, prefix, match)
665 for subpath in ctx.substate: 664 for subpath in ctx.substate:
666 s = subrepo(ctx, subpath, True) 665 s = subrepo(ctx, subpath, True)
667 submatch = matchmod.subdirmatcher(subpath, match) 666 submatch = matchmod.subdirmatcher(subpath, match)
668 subprefix = prefix + subpath + b'/' 667 subprefix = prefix + subpath + b'/'
669 total += s.archive(archiver, subprefix, submatch, decode) 668 total += s.archive(opener, subprefix, submatch, decode)
670 return total 669 return total
671 670
672 @annotatesubrepoerror 671 @annotatesubrepoerror
673 def dirty(self, ignoreupdate=False, missing=False): 672 def dirty(self, ignoreupdate=False, missing=False):
674 r = self._state[1] 673 r = self._state[1]
1908 if kind == stat.S_IFDIR: 1907 if kind == stat.S_IFDIR:
1909 self.wvfs.rmtree(f) 1908 self.wvfs.rmtree(f)
1910 else: 1909 else:
1911 self.wvfs.unlink(f) 1910 self.wvfs.unlink(f)
1912 1911
1913 def archive( 1912 def archive(self, opener, prefix, match: matchmod.basematcher, decode=True):
1914 self, archiver, prefix, match: matchmod.basematcher, decode=True
1915 ):
1916 total = 0 1913 total = 0
1917 source, revision = self._state 1914 source, revision = self._state
1918 if not revision: 1915 if not revision:
1919 return total 1916 return total
1920 self._fetch(source, revision) 1917 self._fetch(source, revision)
1926 tar = tarfile.open(fileobj=tarstream, mode='r|') 1923 tar = tarfile.open(fileobj=tarstream, mode='r|')
1927 relpath = subrelpath(self) 1924 relpath = subrelpath(self)
1928 progress = self.ui.makeprogress( 1925 progress = self.ui.makeprogress(
1929 _(b'archiving (%s)') % relpath, unit=_(b'files') 1926 _(b'archiving (%s)') % relpath, unit=_(b'files')
1930 ) 1927 )
1928 archiver = None
1931 progress.update(0) 1929 progress.update(0)
1932 for info in tar: 1930 for info in tar:
1933 if info.isdir(): 1931 if info.isdir():
1934 continue 1932 continue
1935 bname = pycompat.fsencode(info.name) 1933 bname = pycompat.fsencode(info.name)
1942 if f: 1940 if f:
1943 data = f.read() 1941 data = f.read()
1944 else: 1942 else:
1945 self.ui.warn(_(b'skipping "%s" (unknown type)') % bname) 1943 self.ui.warn(_(b'skipping "%s" (unknown type)') % bname)
1946 continue 1944 continue
1945 if archiver is None:
1946 archiver = opener()
1947 archiver.addfile(prefix + bname, info.mode, info.issym(), data) 1947 archiver.addfile(prefix + bname, info.mode, info.issym(), data)
1948 total += 1 1948 total += 1
1949 progress.increment() 1949 progress.increment()
1950 progress.complete() 1950 progress.complete()
1951 return total 1951 return total