hgext/largefiles/reposetup.py
changeset 43076 2372284d9457
parent 42926 34ed651ba7e4
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    23 from . import (
    23 from . import (
    24     lfcommands,
    24     lfcommands,
    25     lfutil,
    25     lfutil,
    26 )
    26 )
    27 
    27 
       
    28 
    28 def reposetup(ui, repo):
    29 def reposetup(ui, repo):
    29     # wire repositories should be given new wireproto functions
    30     # wire repositories should be given new wireproto functions
    30     # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
    31     # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
    31     if not repo.local():
    32     if not repo.local():
    32         return
    33         return
    34     class lfilesrepo(repo.__class__):
    35     class lfilesrepo(repo.__class__):
    35         # the mark to examine whether "repo" object enables largefiles or not
    36         # the mark to examine whether "repo" object enables largefiles or not
    36         _largefilesenabled = True
    37         _largefilesenabled = True
    37 
    38 
    38         lfstatus = False
    39         lfstatus = False
       
    40 
    39         def status_nolfiles(self, *args, **kwargs):
    41         def status_nolfiles(self, *args, **kwargs):
    40             return super(lfilesrepo, self).status(*args, **kwargs)
    42             return super(lfilesrepo, self).status(*args, **kwargs)
    41 
    43 
    42         # When lfstatus is set, return a context that gives the names
    44         # When lfstatus is set, return a context that gives the names
    43         # of largefiles instead of their corresponding standins and
    45         # of largefiles instead of their corresponding standins and
    44         # identifies the largefiles as always binary, regardless of
    46         # identifies the largefiles as always binary, regardless of
    45         # their actual contents.
    47         # their actual contents.
    46         def __getitem__(self, changeid):
    48         def __getitem__(self, changeid):
    47             ctx = super(lfilesrepo, self).__getitem__(changeid)
    49             ctx = super(lfilesrepo, self).__getitem__(changeid)
    48             if self.lfstatus:
    50             if self.lfstatus:
       
    51 
    49                 class lfilesctx(ctx.__class__):
    52                 class lfilesctx(ctx.__class__):
    50                     def files(self):
    53                     def files(self):
    51                         filenames = super(lfilesctx, self).files()
    54                         filenames = super(lfilesctx, self).files()
    52                         return [lfutil.splitstandin(f) or f for f in filenames]
    55                         return [lfutil.splitstandin(f) or f for f in filenames]
       
    56 
    53                     def manifest(self):
    57                     def manifest(self):
    54                         man1 = super(lfilesctx, self).manifest()
    58                         man1 = super(lfilesctx, self).manifest()
       
    59 
    55                         class lfilesmanifest(man1.__class__):
    60                         class lfilesmanifest(man1.__class__):
    56                             def __contains__(self, filename):
    61                             def __contains__(self, filename):
    57                                 orig = super(lfilesmanifest, self).__contains__
    62                                 orig = super(lfilesmanifest, self).__contains__
    58                                 return (orig(filename) or
    63                                 return orig(filename) or orig(
    59                                         orig(lfutil.standin(filename)))
    64                                     lfutil.standin(filename)
       
    65                                 )
       
    66 
    60                         man1.__class__ = lfilesmanifest
    67                         man1.__class__ = lfilesmanifest
    61                         return man1
    68                         return man1
       
    69 
    62                     def filectx(self, path, fileid=None, filelog=None):
    70                     def filectx(self, path, fileid=None, filelog=None):
    63                         orig = super(lfilesctx, self).filectx
    71                         orig = super(lfilesctx, self).filectx
    64                         try:
    72                         try:
    65                             if filelog is not None:
    73                             if filelog is not None:
    66                                 result = orig(path, fileid, filelog)
    74                                 result = orig(path, fileid, filelog)
    68                                 result = orig(path, fileid)
    76                                 result = orig(path, fileid)
    69                         except error.LookupError:
    77                         except error.LookupError:
    70                             # Adding a null character will cause Mercurial to
    78                             # Adding a null character will cause Mercurial to
    71                             # identify this as a binary file.
    79                             # identify this as a binary file.
    72                             if filelog is not None:
    80                             if filelog is not None:
    73                                 result = orig(lfutil.standin(path), fileid,
    81                                 result = orig(
    74                                               filelog)
    82                                     lfutil.standin(path), fileid, filelog
       
    83                                 )
    75                             else:
    84                             else:
    76                                 result = orig(lfutil.standin(path), fileid)
    85                                 result = orig(lfutil.standin(path), fileid)
    77                             olddata = result.data
    86                             olddata = result.data
    78                             result.data = lambda: olddata() + '\0'
    87                             result.data = lambda: olddata() + '\0'
    79                         return result
    88                         return result
       
    89 
    80                 ctx.__class__ = lfilesctx
    90                 ctx.__class__ = lfilesctx
    81             return ctx
    91             return ctx
    82 
    92 
    83         # Figure out the status of big files and insert them into the
    93         # Figure out the status of big files and insert them into the
    84         # appropriate list in the result. Also removes standin files
    94         # appropriate list in the result. Also removes standin files
    85         # from the listing. Revert to the original status if
    95         # from the listing. Revert to the original status if
    86         # self.lfstatus is False.
    96         # self.lfstatus is False.
    87         # XXX large file status is buggy when used on repo proxy.
    97         # XXX large file status is buggy when used on repo proxy.
    88         # XXX this needs to be investigated.
    98         # XXX this needs to be investigated.
    89         @localrepo.unfilteredmethod
    99         @localrepo.unfilteredmethod
    90         def status(self, node1='.', node2=None, match=None, ignored=False,
   100         def status(
    91                 clean=False, unknown=False, listsubrepos=False):
   101             self,
       
   102             node1='.',
       
   103             node2=None,
       
   104             match=None,
       
   105             ignored=False,
       
   106             clean=False,
       
   107             unknown=False,
       
   108             listsubrepos=False,
       
   109         ):
    92             listignored, listclean, listunknown = ignored, clean, unknown
   110             listignored, listclean, listunknown = ignored, clean, unknown
    93             orig = super(lfilesrepo, self).status
   111             orig = super(lfilesrepo, self).status
    94             if not self.lfstatus:
   112             if not self.lfstatus:
    95                 return orig(node1, node2, match, listignored, listclean,
   113                 return orig(
    96                             listunknown, listsubrepos)
   114                     node1,
       
   115                     node2,
       
   116                     match,
       
   117                     listignored,
       
   118                     listclean,
       
   119                     listunknown,
       
   120                     listsubrepos,
       
   121                 )
    97 
   122 
    98             # some calls in this function rely on the old version of status
   123             # some calls in this function rely on the old version of status
    99             self.lfstatus = False
   124             self.lfstatus = False
   100             ctx1 = self[node1]
   125             ctx1 = self[node1]
   101             ctx2 = self[node2]
   126             ctx2 = self[node2]
   122                 if not match.always():
   147                 if not match.always():
   123                     for f in lfdirstate:
   148                     for f in lfdirstate:
   124                         if match(f):
   149                         if match(f):
   125                             break
   150                             break
   126                     else:
   151                     else:
   127                         return orig(node1, node2, match, listignored, listclean,
   152                         return orig(
   128                                     listunknown, listsubrepos)
   153                             node1,
       
   154                             node2,
       
   155                             match,
       
   156                             listignored,
       
   157                             listclean,
       
   158                             listunknown,
       
   159                             listsubrepos,
       
   160                         )
   129 
   161 
   130                 # Create a copy of match that matches standins instead
   162                 # Create a copy of match that matches standins instead
   131                 # of largefiles.
   163                 # of largefiles.
   132                 def tostandins(files):
   164                 def tostandins(files):
   133                     if not working:
   165                     if not working:
   147                     return newfiles
   179                     return newfiles
   148 
   180 
   149                 m = copy.copy(match)
   181                 m = copy.copy(match)
   150                 m._files = tostandins(m._files)
   182                 m._files = tostandins(m._files)
   151 
   183 
   152                 result = orig(node1, node2, m, ignored, clean, unknown,
   184                 result = orig(
   153                               listsubrepos)
   185                     node1, node2, m, ignored, clean, unknown, listsubrepos
       
   186                 )
   154                 if working:
   187                 if working:
   155 
   188 
   156                     def sfindirstate(f):
   189                     def sfindirstate(f):
   157                         sf = lfutil.standin(f)
   190                         sf = lfutil.standin(f)
   158                         dirstate = self.dirstate
   191                         dirstate = self.dirstate
   159                         return sf in dirstate or dirstate.hasdir(sf)
   192                         return sf in dirstate or dirstate.hasdir(sf)
   160 
   193 
   161                     match._files = [f for f in match._files
   194                     match._files = [f for f in match._files if sfindirstate(f)]
   162                                     if sfindirstate(f)]
       
   163                     # Don't waste time getting the ignored and unknown
   195                     # Don't waste time getting the ignored and unknown
   164                     # files from lfdirstate
   196                     # files from lfdirstate
   165                     unsure, s = lfdirstate.status(match, subrepos=[],
   197                     unsure, s = lfdirstate.status(
   166                                                   ignored=False,
   198                         match,
   167                                                   clean=listclean,
   199                         subrepos=[],
   168                                                   unknown=False)
   200                         ignored=False,
       
   201                         clean=listclean,
       
   202                         unknown=False,
       
   203                     )
   169                     (modified, added, removed, deleted, clean) = (
   204                     (modified, added, removed, deleted, clean) = (
   170                         s.modified, s.added, s.removed, s.deleted, s.clean)
   205                         s.modified,
       
   206                         s.added,
       
   207                         s.removed,
       
   208                         s.deleted,
       
   209                         s.clean,
       
   210                     )
   171                     if parentworking:
   211                     if parentworking:
   172                         for lfile in unsure:
   212                         for lfile in unsure:
   173                             standin = lfutil.standin(lfile)
   213                             standin = lfutil.standin(lfile)
   174                             if standin not in ctx1:
   214                             if standin not in ctx1:
   175                                 # from second parent
   215                                 # from second parent
   176                                 modified.append(lfile)
   216                                 modified.append(lfile)
   177                             elif (lfutil.readasstandin(ctx1[standin])
   217                             elif lfutil.readasstandin(
   178                                   != lfutil.hashfile(self.wjoin(lfile))):
   218                                 ctx1[standin]
       
   219                             ) != lfutil.hashfile(self.wjoin(lfile)):
   179                                 modified.append(lfile)
   220                                 modified.append(lfile)
   180                             else:
   221                             else:
   181                                 if listclean:
   222                                 if listclean:
   182                                     clean.append(lfile)
   223                                     clean.append(lfile)
   183                                 lfdirstate.normal(lfile)
   224                                 lfdirstate.normal(lfile)
   188 
   229 
   189                         for lfile in tocheck:
   230                         for lfile in tocheck:
   190                             standin = lfutil.standin(lfile)
   231                             standin = lfutil.standin(lfile)
   191                             if standin in ctx1:
   232                             if standin in ctx1:
   192                                 abslfile = self.wjoin(lfile)
   233                                 abslfile = self.wjoin(lfile)
   193                                 if ((lfutil.readasstandin(ctx1[standin]) !=
   234                                 if (
   194                                      lfutil.hashfile(abslfile)) or
   235                                     lfutil.readasstandin(ctx1[standin])
   195                                     (checkexec and
   236                                     != lfutil.hashfile(abslfile)
   196                                      ('x' in ctx1.flags(standin)) !=
   237                                 ) or (
   197                                      bool(lfutil.getexecutable(abslfile)))):
   238                                     checkexec
       
   239                                     and ('x' in ctx1.flags(standin))
       
   240                                     != bool(lfutil.getexecutable(abslfile))
       
   241                                 ):
   198                                     modified.append(lfile)
   242                                     modified.append(lfile)
   199                                 elif listclean:
   243                                 elif listclean:
   200                                     clean.append(lfile)
   244                                     clean.append(lfile)
   201                             else:
   245                             else:
   202                                 added.append(lfile)
   246                                 added.append(lfile)
   203 
   247 
   204                         # at this point, 'removed' contains largefiles
   248                         # at this point, 'removed' contains largefiles
   205                         # marked as 'R' in the working context.
   249                         # marked as 'R' in the working context.
   206                         # then, largefiles not managed also in the target
   250                         # then, largefiles not managed also in the target
   207                         # context should be excluded from 'removed'.
   251                         # context should be excluded from 'removed'.
   208                         removed = [lfile for lfile in removed
   252                         removed = [
   209                                    if lfutil.standin(lfile) in ctx1]
   253                             lfile
       
   254                             for lfile in removed
       
   255                             if lfutil.standin(lfile) in ctx1
       
   256                         ]
   210 
   257 
   211                     # Standins no longer found in lfdirstate have been deleted
   258                     # Standins no longer found in lfdirstate have been deleted
   212                     for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
   259                     for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
   213                         lfile = lfutil.splitstandin(standin)
   260                         lfile = lfutil.splitstandin(standin)
   214                         if not match(lfile):
   261                         if not match(lfile):
   227                     # Largefiles are not really removed when they're
   274                     # Largefiles are not really removed when they're
   228                     # still in the normal dirstate. Likewise, normal
   275                     # still in the normal dirstate. Likewise, normal
   229                     # files are not really removed if they are still in
   276                     # files are not really removed if they are still in
   230                     # lfdirstate. This happens in merges where files
   277                     # lfdirstate. This happens in merges where files
   231                     # change type.
   278                     # change type.
   232                     removed = [f for f in removed
   279                     removed = [f for f in removed if f not in self.dirstate]
   233                                if f not in self.dirstate]
   280                     result[2] = [f for f in result[2] if f not in lfdirstate]
   234                     result[2] = [f for f in result[2]
       
   235                                  if f not in lfdirstate]
       
   236 
   281 
   237                     lfiles = set(lfdirstate)
   282                     lfiles = set(lfdirstate)
   238                     # Unknown files
   283                     # Unknown files
   239                     result[4] = set(result[4]).difference(lfiles)
   284                     result[4] = set(result[4]).difference(lfiles)
   240                     # Ignored files
   285                     # Ignored files
   241                     result[5] = set(result[5]).difference(lfiles)
   286                     result[5] = set(result[5]).difference(lfiles)
   242                     # combine normal files and largefiles
   287                     # combine normal files and largefiles
   243                     normals = [[fn for fn in filelist
   288                     normals = [
   244                                 if not lfutil.isstandin(fn)]
   289                         [fn for fn in filelist if not lfutil.isstandin(fn)]
   245                                for filelist in result]
   290                         for filelist in result
   246                     lfstatus = (modified, added, removed, deleted, [], [],
   291                     ]
   247                                 clean)
   292                     lfstatus = (
   248                     result = [sorted(list1 + list2)
   293                         modified,
   249                               for (list1, list2) in zip(normals, lfstatus)]
   294                         added,
   250                 else: # not against working directory
   295                         removed,
   251                     result = [[lfutil.splitstandin(f) or f for f in items]
   296                         deleted,
   252                               for items in result]
   297                         [],
       
   298                         [],
       
   299                         clean,
       
   300                     )
       
   301                     result = [
       
   302                         sorted(list1 + list2)
       
   303                         for (list1, list2) in zip(normals, lfstatus)
       
   304                     ]
       
   305                 else:  # not against working directory
       
   306                     result = [
       
   307                         [lfutil.splitstandin(f) or f for f in items]
       
   308                         for items in result
       
   309                     ]
   253 
   310 
   254                 if wlock:
   311                 if wlock:
   255                     lfdirstate.write()
   312                     lfdirstate.write()
   256 
   313 
   257             finally:
   314             finally:
   261             self.lfstatus = True
   318             self.lfstatus = True
   262             return scmutil.status(*result)
   319             return scmutil.status(*result)
   263 
   320 
   264         def commitctx(self, ctx, *args, **kwargs):
   321         def commitctx(self, ctx, *args, **kwargs):
   265             node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs)
   322             node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs)
       
   323 
   266             class lfilesctx(ctx.__class__):
   324             class lfilesctx(ctx.__class__):
   267                 def markcommitted(self, node):
   325                 def markcommitted(self, node):
   268                     orig = super(lfilesctx, self).markcommitted
   326                     orig = super(lfilesctx, self).markcommitted
   269                     return lfutil.markcommitted(orig, self, node)
   327                     return lfutil.markcommitted(orig, self, node)
       
   328 
   270             ctx.__class__ = lfilesctx
   329             ctx.__class__ = lfilesctx
   271             return node
   330             return node
   272 
   331 
   273         # Before commit, largefile standins have not had their
   332         # Before commit, largefile standins have not had their
   274         # contents updated to reflect the hash of their largefile.
   333         # contents updated to reflect the hash of their largefile.
   275         # Do that here.
   334         # Do that here.
   276         def commit(self, text="", user=None, date=None, match=None,
   335         def commit(
   277                 force=False, editor=False, extra=None):
   336             self,
       
   337             text="",
       
   338             user=None,
       
   339             date=None,
       
   340             match=None,
       
   341             force=False,
       
   342             editor=False,
       
   343             extra=None,
       
   344         ):
   278             if extra is None:
   345             if extra is None:
   279                 extra = {}
   346                 extra = {}
   280             orig = super(lfilesrepo, self).commit
   347             orig = super(lfilesrepo, self).commit
   281 
   348 
   282             with self.wlock():
   349             with self.wlock():
   283                 lfcommithook = self._lfcommithooks[-1]
   350                 lfcommithook = self._lfcommithooks[-1]
   284                 match = lfcommithook(self, match)
   351                 match = lfcommithook(self, match)
   285                 result = orig(text=text, user=user, date=date, match=match,
   352                 result = orig(
   286                                 force=force, editor=editor, extra=extra)
   353                     text=text,
       
   354                     user=user,
       
   355                     date=date,
       
   356                     match=match,
       
   357                     force=force,
       
   358                     editor=editor,
       
   359                     extra=extra,
       
   360                 )
   287                 return result
   361                 return result
   288 
   362 
   289         def push(self, remote, force=False, revs=None, newbranch=False):
   363         def push(self, remote, force=False, revs=None, newbranch=False):
   290             if remote.local():
   364             if remote.local():
   291                 missing = set(self.requirements) - remote.local().supported
   365                 missing = set(self.requirements) - remote.local().supported
   292                 if missing:
   366                 if missing:
   293                     msg = _("required features are not"
   367                     msg = _(
   294                             " supported in the destination:"
   368                         "required features are not"
   295                             " %s") % (', '.join(sorted(missing)))
   369                         " supported in the destination:"
       
   370                         " %s"
       
   371                     ) % (', '.join(sorted(missing)))
   296                     raise error.Abort(msg)
   372                     raise error.Abort(msg)
   297             return super(lfilesrepo, self).push(remote, force=force, revs=revs,
   373             return super(lfilesrepo, self).push(
   298                 newbranch=newbranch)
   374                 remote, force=force, revs=revs, newbranch=newbranch
       
   375             )
   299 
   376 
   300         # TODO: _subdirlfs should be moved into "lfutil.py", because
   377         # TODO: _subdirlfs should be moved into "lfutil.py", because
   301         # it is referred only from "lfutil.updatestandinsbymatch"
   378         # it is referred only from "lfutil.updatestandinsbymatch"
   302         def _subdirlfs(self, files, lfiles):
   379         def _subdirlfs(self, files, lfiles):
   303             '''
   380             '''
   317 
   394 
   318             for f in files:
   395             for f in files:
   319                 if lfutil.isstandin(f + '/'):
   396                 if lfutil.isstandin(f + '/'):
   320                     raise error.Abort(
   397                     raise error.Abort(
   321                         _('file "%s" is a largefile standin') % f,
   398                         _('file "%s" is a largefile standin') % f,
   322                         hint=('commit the largefile itself instead'))
   399                         hint='commit the largefile itself instead',
       
   400                     )
   323                 # Scan directories
   401                 # Scan directories
   324                 if self.wvfs.isdir(f):
   402                 if self.wvfs.isdir(f):
   325                     dirs.append(f)
   403                     dirs.append(f)
   326                 else:
   404                 else:
   327                     regulars.append(f)
   405                     regulars.append(f)
   375         if lfrevs is None:
   453         if lfrevs is None:
   376             lfrevs = pushop.outgoing.missing
   454             lfrevs = pushop.outgoing.missing
   377         if lfrevs:
   455         if lfrevs:
   378             toupload = set()
   456             toupload = set()
   379             addfunc = lambda fn, lfhash: toupload.add(lfhash)
   457             addfunc = lambda fn, lfhash: toupload.add(lfhash)
   380             lfutil.getlfilestoupload(pushop.repo, lfrevs,
   458             lfutil.getlfilestoupload(pushop.repo, lfrevs, addfunc)
   381                                      addfunc)
       
   382             lfcommands.uploadlfiles(ui, pushop.repo, pushop.remote, toupload)
   459             lfcommands.uploadlfiles(ui, pushop.repo, pushop.remote, toupload)
       
   460 
   383     repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
   461     repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
   384 
   462 
   385     def checkrequireslfiles(ui, repo, **kwargs):
   463     def checkrequireslfiles(ui, repo, **kwargs):
   386         if 'largefiles' not in repo.requirements and any(
   464         if 'largefiles' not in repo.requirements and any(
   387                 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
   465             lfutil.shortname + '/' in f[0] for f in repo.store.datafiles()
       
   466         ):
   388             repo.requirements.add('largefiles')
   467             repo.requirements.add('largefiles')
   389             repo._writerequirements()
   468             repo._writerequirements()
   390 
   469 
   391     ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
   470     ui.setconfig(
   392                  'largefiles')
   471         'hooks', 'changegroup.lfiles', checkrequireslfiles, 'largefiles'
       
   472     )
   393     ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
   473     ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')