mercurial/subrepo.py
changeset 45957 89a2afe31e82
parent 45682 d2e1dcd4490d
child 46114 59fa3890d40a
equal deleted inserted replaced
45956:346af7687c6f 45957:89a2afe31e82
    47 _abssource = subrepoutil._abssource
    47 _abssource = subrepoutil._abssource
    48 propertycache = util.propertycache
    48 propertycache = util.propertycache
    49 
    49 
    50 
    50 
    51 def _expandedabspath(path):
    51 def _expandedabspath(path):
    52     '''
    52     """
    53     get a path or url and if it is a path expand it and return an absolute path
    53     get a path or url and if it is a path expand it and return an absolute path
    54     '''
    54     """
    55     expandedpath = util.urllocalpath(util.expandpath(path))
    55     expandedpath = util.urllocalpath(util.expandpath(path))
    56     u = util.url(expandedpath)
    56     u = util.url(expandedpath)
    57     if not u.scheme:
    57     if not u.scheme:
    58         path = util.normpath(os.path.abspath(u.path))
    58         path = util.normpath(os.path.abspath(u.path))
    59     return path
    59     return path
   266             return _(b'uncommitted changes in subrepository "%s"') % subrelpath(
   266             return _(b'uncommitted changes in subrepository "%s"') % subrelpath(
   267                 self
   267                 self
   268             )
   268             )
   269 
   269 
   270     def bailifchanged(self, ignoreupdate=False, hint=None):
   270     def bailifchanged(self, ignoreupdate=False, hint=None):
   271         """raise Abort if subrepository is ``dirty()``
   271         """raise Abort if subrepository is ``dirty()``"""
   272         """
       
   273         dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate, missing=True)
   272         dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate, missing=True)
   274         if dirtyreason:
   273         if dirtyreason:
   275             raise error.Abort(dirtyreason, hint=hint)
   274             raise error.Abort(dirtyreason, hint=hint)
   276 
   275 
   277     def basestate(self):
   276     def basestate(self):
   289         new state of the subrepo.
   288         new state of the subrepo.
   290         """
   289         """
   291         raise NotImplementedError
   290         raise NotImplementedError
   292 
   291 
   293     def phase(self, state):
   292     def phase(self, state):
   294         """returns phase of specified state in the subrepository.
   293         """returns phase of specified state in the subrepository."""
   295         """
       
   296         return phases.public
   294         return phases.public
   297 
   295 
   298     def remove(self):
   296     def remove(self):
   299         """remove the subrepo
   297         """remove the subrepo
   300 
   298 
   382             progress.increment()
   380             progress.increment()
   383         progress.complete()
   381         progress.complete()
   384         return total
   382         return total
   385 
   383 
   386     def walk(self, match):
   384     def walk(self, match):
   387         '''
   385         """
   388         walk recursively through the directory tree, finding all files
   386         walk recursively through the directory tree, finding all files
   389         matched by the match function
   387         matched by the match function
   390         '''
   388         """
   391 
   389 
   392     def forget(self, match, prefix, uipathfn, dryrun, interactive):
   390     def forget(self, match, prefix, uipathfn, dryrun, interactive):
   393         return ([], [])
   391         return ([], [])
   394 
   392 
   395     def removefiles(
   393     def removefiles(
   421 
   419 
   422     def shortid(self, revid):
   420     def shortid(self, revid):
   423         return revid
   421         return revid
   424 
   422 
   425     def unshare(self):
   423     def unshare(self):
   426         '''
   424         """
   427         convert this repository from shared to normal storage.
   425         convert this repository from shared to normal storage.
   428         '''
   426         """
   429 
   427 
   430     def verify(self, onpush=False):
   428     def verify(self, onpush=False):
   431         """verify the revision of this repository that is held in `_state` is
   429         """verify the revision of this repository that is held in `_state` is
   432         present and not hidden.  Return 0 on success or warning, 1 on any
   430         present and not hidden.  Return 0 on success or warning, 1 on any
   433         error.  In the case of ``onpush``, warnings or errors will raise an
   431         error.  In the case of ``onpush``, warnings or errors will raise an
   435         """
   433         """
   436         return 0
   434         return 0
   437 
   435 
   438     @propertycache
   436     @propertycache
   439     def wvfs(self):
   437     def wvfs(self):
   440         """return vfs to access the working directory of this subrepository
   438         """return vfs to access the working directory of this subrepository"""
   441         """
       
   442         return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
   439         return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
   443 
   440 
   444     @propertycache
   441     @propertycache
   445     def _relpath(self):
   442     def _relpath(self):
   446         """return path to this subrepository as seen from outermost repository
   443         """return path to this subrepository as seen from outermost repository"""
   447         """
       
   448         return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
   444         return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
   449 
   445 
   450 
   446 
   451 class hgsubrepo(abstractsubrepo):
   447 class hgsubrepo(abstractsubrepo):
   452     def __init__(self, ctx, path, state, allowcreate):
   448     def __init__(self, ctx, path, state, allowcreate):
   501             # the cached and current pull states have a different size
   497             # the cached and current pull states have a different size
   502             clean = next(itercache, None) is None
   498             clean = next(itercache, None) is None
   503         return clean
   499         return clean
   504 
   500 
   505     def _calcstorehash(self, remotepath):
   501     def _calcstorehash(self, remotepath):
   506         '''calculate a unique "store hash"
   502         """calculate a unique "store hash"
   507 
   503 
   508         This method is used to to detect when there are changes that may
   504         This method is used to to detect when there are changes that may
   509         require a push to a given remote path.'''
   505         require a push to a given remote path."""
   510         # sort the files that will be hashed in increasing (likely) file size
   506         # sort the files that will be hashed in increasing (likely) file size
   511         filelist = (b'bookmarks', b'store/phaseroots', b'store/00changelog.i')
   507         filelist = (b'bookmarks', b'store/phaseroots', b'store/00changelog.i')
   512         yield b'# %s\n' % _expandedabspath(remotepath)
   508         yield b'# %s\n' % _expandedabspath(remotepath)
   513         vfs = self._repo.vfs
   509         vfs = self._repo.vfs
   514         for relname in filelist:
   510         for relname in filelist:
   523         '''read the store hash cache for a given remote repository'''
   519         '''read the store hash cache for a given remote repository'''
   524         cachefile = _getstorehashcachename(remotepath)
   520         cachefile = _getstorehashcachename(remotepath)
   525         return self._cachestorehashvfs.tryreadlines(cachefile, b'r')
   521         return self._cachestorehashvfs.tryreadlines(cachefile, b'r')
   526 
   522 
   527     def _cachestorehash(self, remotepath):
   523     def _cachestorehash(self, remotepath):
   528         '''cache the current store hash
   524         """cache the current store hash
   529 
   525 
   530         Each remote repo requires its own store hash cache, because a subrepo
   526         Each remote repo requires its own store hash cache, because a subrepo
   531         store may be "clean" versus a given remote repo, but not versus another
   527         store may be "clean" versus a given remote repo, but not versus another
   532         '''
   528         """
   533         cachefile = _getstorehashcachename(remotepath)
   529         cachefile = _getstorehashcachename(remotepath)
   534         with self._repo.lock():
   530         with self._repo.lock():
   535             storehash = list(self._calcstorehash(remotepath))
   531             storehash = list(self._calcstorehash(remotepath))
   536             vfs = self._cachestorehashvfs
   532             vfs = self._cachestorehashvfs
   537             vfs.writelines(cachefile, storehash, mode=b'wb', notindexed=True)
   533             vfs.writelines(cachefile, storehash, mode=b'wb', notindexed=True)
   538 
   534 
   539     def _getctx(self):
   535     def _getctx(self):
   540         '''fetch the context for this subrepo revision, possibly a workingctx
   536         """fetch the context for this subrepo revision, possibly a workingctx"""
   541         '''
       
   542         if self._ctx.rev() is None:
   537         if self._ctx.rev() is None:
   543             return self._repo[None]  # workingctx if parent is workingctx
   538             return self._repo[None]  # workingctx if parent is workingctx
   544         else:
   539         else:
   545             rev = self._state[1]
   540             rev = self._state[1]
   546             return self._repo[rev]
   541             return self._repo[rev]
  1046                 self._repo.ui.warn(b'%s\n' % msg)
  1041                 self._repo.ui.warn(b'%s\n' % msg)
  1047             return 0
  1042             return 0
  1048 
  1043 
  1049     @propertycache
  1044     @propertycache
  1050     def wvfs(self):
  1045     def wvfs(self):
  1051         """return own wvfs for efficiency and consistency
  1046         """return own wvfs for efficiency and consistency"""
  1052         """
       
  1053         return self._repo.wvfs
  1047         return self._repo.wvfs
  1054 
  1048 
  1055     @propertycache
  1049     @propertycache
  1056     def _relpath(self):
  1050     def _relpath(self):
  1057         """return path to this subrepository as seen from outermost repository
  1051         """return path to this subrepository as seen from outermost repository"""
  1058         """
       
  1059         # Keep consistent dir separators by avoiding vfs.join(self._path)
  1052         # Keep consistent dir separators by avoiding vfs.join(self._path)
  1060         return reporelpath(self._repo)
  1053         return reporelpath(self._repo)
  1061 
  1054 
  1062 
  1055 
  1063 class svnsubrepo(abstractsubrepo):
  1056 class svnsubrepo(abstractsubrepo):
  1168             path = e.getAttribute('path').encode('utf8')
  1161             path = e.getAttribute('path').encode('utf8')
  1169             if item == 'external':
  1162             if item == 'external':
  1170                 externals.append(path)
  1163                 externals.append(path)
  1171             elif item == 'missing':
  1164             elif item == 'missing':
  1172                 missing.append(path)
  1165                 missing.append(path)
  1173             if item not in (
  1166             if (
  1174                 '',
  1167                 item
  1175                 'normal',
  1168                 not in (
  1176                 'unversioned',
  1169                     '',
  1177                 'external',
  1170                     'normal',
  1178             ) or props not in ('', 'none', 'normal'):
  1171                     'unversioned',
       
  1172                     'external',
       
  1173                 )
       
  1174                 or props not in ('', 'none', 'normal')
       
  1175             ):
  1179                 changes.append(path)
  1176                 changes.append(path)
  1180         for path in changes:
  1177         for path in changes:
  1181             for ext in externals:
  1178             for ext in externals:
  1182                 if path == ext or path.startswith(ext + pycompat.ossep):
  1179                 if path == ext or path.startswith(ext + pycompat.ossep):
  1183                     return True, True, bool(missing)
  1180                     return True, True, bool(missing)
  1382 
  1379 
  1383         return -1
  1380         return -1
  1384 
  1381 
  1385     @staticmethod
  1382     @staticmethod
  1386     def _checkversion(out):
  1383     def _checkversion(out):
  1387         '''ensure git version is new enough
  1384         """ensure git version is new enough
  1388 
  1385 
  1389         >>> _checkversion = gitsubrepo._checkversion
  1386         >>> _checkversion = gitsubrepo._checkversion
  1390         >>> _checkversion(b'git version 1.6.0')
  1387         >>> _checkversion(b'git version 1.6.0')
  1391         'ok'
  1388         'ok'
  1392         >>> _checkversion(b'git version 1.8.5')
  1389         >>> _checkversion(b'git version 1.8.5')
  1403         'ok'
  1400         'ok'
  1404         >>> _checkversion(b'git version 12345')
  1401         >>> _checkversion(b'git version 12345')
  1405         'unknown'
  1402         'unknown'
  1406         >>> _checkversion(b'no')
  1403         >>> _checkversion(b'no')
  1407         'unknown'
  1404         'unknown'
  1408         '''
  1405         """
  1409         version = gitsubrepo._gitversion(out)
  1406         version = gitsubrepo._gitversion(out)
  1410         # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
  1407         # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
  1411         # despite the docstring comment.  For now, error on 1.4.0, warn on
  1408         # despite the docstring comment.  For now, error on 1.4.0, warn on
  1412         # 1.5.0 but attempt to continue.
  1409         # 1.5.0 but attempt to continue.
  1413         if version == -1:
  1410         if version == -1:
  1514         diff-index only looks at changes to file stat;
  1511         diff-index only looks at changes to file stat;
  1515         this command looks at file contents and updates the stat."""
  1512         this command looks at file contents and updates the stat."""
  1516         self._gitcommand([b'update-index', b'-q', b'--refresh'])
  1513         self._gitcommand([b'update-index', b'-q', b'--refresh'])
  1517 
  1514 
  1518     def _gitbranchmap(self):
  1515     def _gitbranchmap(self):
  1519         '''returns 2 things:
  1516         """returns 2 things:
  1520         a map from git branch to revision
  1517         a map from git branch to revision
  1521         a map from revision to branches'''
  1518         a map from revision to branches"""
  1522         branch2rev = {}
  1519         branch2rev = {}
  1523         rev2branch = {}
  1520         rev2branch = {}
  1524 
  1521 
  1525         out = self._gitcommand(
  1522         out = self._gitcommand(
  1526             [b'for-each-ref', b'--format', b'%(objectname) %(refname)']
  1523             [b'for-each-ref', b'--format', b'%(objectname) %(refname)']