mercurial/subrepo.py
changeset 18109 9e3910db4e78
parent 18031 54f063acc5ea
child 18263 9aa6bee6e9f9
equal deleted inserted replaced
18108:bc694d78d843 18109:9e3910db4e78
    11 import config, scmutil, util, node, error, cmdutil, bookmarks, match as matchmod
    11 import config, scmutil, util, node, error, cmdutil, bookmarks, match as matchmod
    12 hg = None
    12 hg = None
    13 propertycache = util.propertycache
    13 propertycache = util.propertycache
    14 
    14 
    15 nullstate = ('', '', 'empty')
    15 nullstate = ('', '', 'empty')
       
    16 
       
    17 class SubrepoAbort(error.Abort):
       
    18     """Exception class used to avoid handling a subrepo error more than once"""
       
    19 
       
    20 def annotatesubrepoerror(func):
       
    21     def decoratedmethod(self, *args, **kargs):
       
    22         try:
       
    23             res = func(self, *args, **kargs)
       
    24         except SubrepoAbort, ex:
       
    25             # This exception has already been handled
       
    26             raise ex
       
    27         except error.Abort, ex:
       
    28             errormsg = _('%s (in subrepo %s)') % (str(ex), subrelpath(self))
       
    29             # avoid handling this exception by raising a SubrepoAbort exception
       
    30             raise SubrepoAbort(errormsg, hint=ex.hint)
       
    31         return res
       
    32     return decoratedmethod
    16 
    33 
    17 def state(ctx, ui):
    34 def state(ctx, ui):
    18     """return a state dict, mapping subrepo paths configured in .hgsub
    35     """return a state dict, mapping subrepo paths configured in .hgsub
    19     to tuple: (source from .hgsub, revision from .hgsubstate, kind
    36     to tuple: (source from .hgsub, revision from .hgsubstate, kind
    20     (key in types dict))
    37     (key in types dict))
   242         if push and repo.ui.config('paths', 'default-push'):
   259         if push and repo.ui.config('paths', 'default-push'):
   243             return repo.ui.config('paths', 'default-push')
   260             return repo.ui.config('paths', 'default-push')
   244         if repo.ui.config('paths', 'default'):
   261         if repo.ui.config('paths', 'default'):
   245             return repo.ui.config('paths', 'default')
   262             return repo.ui.config('paths', 'default')
   246     if abort:
   263     if abort:
   247         raise util.Abort(_("default path for subrepository %s not found") %
   264         raise util.Abort(_("default path for subrepository not found"))
   248             reporelpath(repo))
       
   249 
   265 
   250 def itersubrepos(ctx1, ctx2):
   266 def itersubrepos(ctx1, ctx2):
   251     """find subrepos in ctx1 or ctx2"""
   267     """find subrepos in ctx1 or ctx2"""
   252     # Create a (subpath, ctx) mapping where we prefer subpaths from
   268     # Create a (subpath, ctx) mapping where we prefer subpaths from
   253     # ctx1. The subpaths from ctx2 are important when the .hgsub file
   269     # ctx1. The subpaths from ctx2 are important when the .hgsub file
   400             v = r.ui.config(s, k)
   416             v = r.ui.config(s, k)
   401             if v:
   417             if v:
   402                 self._repo.ui.setconfig(s, k, v)
   418                 self._repo.ui.setconfig(s, k, v)
   403         self._initrepo(r, state[0], create)
   419         self._initrepo(r, state[0], create)
   404 
   420 
       
   421     @annotatesubrepoerror
   405     def _initrepo(self, parentrepo, source, create):
   422     def _initrepo(self, parentrepo, source, create):
   406         self._repo._subparent = parentrepo
   423         self._repo._subparent = parentrepo
   407         self._repo._subsource = source
   424         self._repo._subsource = source
   408 
   425 
   409         if create:
   426         if create:
   420             addpathconfig('default', defpath)
   437             addpathconfig('default', defpath)
   421             if defpath != defpushpath:
   438             if defpath != defpushpath:
   422                 addpathconfig('default-push', defpushpath)
   439                 addpathconfig('default-push', defpushpath)
   423             fp.close()
   440             fp.close()
   424 
   441 
       
   442     @annotatesubrepoerror
   425     def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
   443     def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
   426         return cmdutil.add(ui, self._repo, match, dryrun, listsubrepos,
   444         return cmdutil.add(ui, self._repo, match, dryrun, listsubrepos,
   427                            os.path.join(prefix, self._path), explicitonly)
   445                            os.path.join(prefix, self._path), explicitonly)
   428 
   446 
       
   447     @annotatesubrepoerror
   429     def status(self, rev2, **opts):
   448     def status(self, rev2, **opts):
   430         try:
   449         try:
   431             rev1 = self._state[1]
   450             rev1 = self._state[1]
   432             ctx1 = self._repo[rev1]
   451             ctx1 = self._repo[rev1]
   433             ctx2 = self._repo[rev2]
   452             ctx2 = self._repo[rev2]
   435         except error.RepoLookupError, inst:
   454         except error.RepoLookupError, inst:
   436             self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
   455             self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
   437                                % (inst, subrelpath(self)))
   456                                % (inst, subrelpath(self)))
   438             return [], [], [], [], [], [], []
   457             return [], [], [], [], [], [], []
   439 
   458 
       
   459     @annotatesubrepoerror
   440     def diff(self, ui, diffopts, node2, match, prefix, **opts):
   460     def diff(self, ui, diffopts, node2, match, prefix, **opts):
   441         try:
   461         try:
   442             node1 = node.bin(self._state[1])
   462             node1 = node.bin(self._state[1])
   443             # We currently expect node2 to come from substate and be
   463             # We currently expect node2 to come from substate and be
   444             # in hex format
   464             # in hex format
   450                                    listsubrepos=True, **opts)
   470                                    listsubrepos=True, **opts)
   451         except error.RepoLookupError, inst:
   471         except error.RepoLookupError, inst:
   452             self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
   472             self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
   453                                % (inst, subrelpath(self)))
   473                                % (inst, subrelpath(self)))
   454 
   474 
       
   475     @annotatesubrepoerror
   455     def archive(self, ui, archiver, prefix, match=None):
   476     def archive(self, ui, archiver, prefix, match=None):
   456         self._get(self._state + ('hg',))
   477         self._get(self._state + ('hg',))
   457         abstractsubrepo.archive(self, ui, archiver, prefix, match)
   478         abstractsubrepo.archive(self, ui, archiver, prefix, match)
   458 
   479 
   459         rev = self._state[1]
   480         rev = self._state[1]
   461         for subpath in ctx.substate:
   482         for subpath in ctx.substate:
   462             s = subrepo(ctx, subpath)
   483             s = subrepo(ctx, subpath)
   463             submatch = matchmod.narrowmatcher(subpath, match)
   484             submatch = matchmod.narrowmatcher(subpath, match)
   464             s.archive(ui, archiver, os.path.join(prefix, self._path), submatch)
   485             s.archive(ui, archiver, os.path.join(prefix, self._path), submatch)
   465 
   486 
       
   487     @annotatesubrepoerror
   466     def dirty(self, ignoreupdate=False):
   488     def dirty(self, ignoreupdate=False):
   467         r = self._state[1]
   489         r = self._state[1]
   468         if r == '' and not ignoreupdate: # no state recorded
   490         if r == '' and not ignoreupdate: # no state recorded
   469             return True
   491             return True
   470         w = self._repo[None]
   492         w = self._repo[None]
   477         return self._repo['.'].hex()
   499         return self._repo['.'].hex()
   478 
   500 
   479     def checknested(self, path):
   501     def checknested(self, path):
   480         return self._repo._checknested(self._repo.wjoin(path))
   502         return self._repo._checknested(self._repo.wjoin(path))
   481 
   503 
       
   504     @annotatesubrepoerror
   482     def commit(self, text, user, date):
   505     def commit(self, text, user, date):
   483         # don't bother committing in the subrepo if it's only been
   506         # don't bother committing in the subrepo if it's only been
   484         # updated
   507         # updated
   485         if not self.dirty(True):
   508         if not self.dirty(True):
   486             return self._repo['.'].hex()
   509             return self._repo['.'].hex()
   488         n = self._repo.commit(text, user, date)
   511         n = self._repo.commit(text, user, date)
   489         if not n:
   512         if not n:
   490             return self._repo['.'].hex() # different version checked out
   513             return self._repo['.'].hex() # different version checked out
   491         return node.hex(n)
   514         return node.hex(n)
   492 
   515 
       
   516     @annotatesubrepoerror
   493     def remove(self):
   517     def remove(self):
   494         # we can't fully delete the repository as it may contain
   518         # we can't fully delete the repository as it may contain
   495         # local-only history
   519         # local-only history
   496         self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
   520         self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
   497         hg.clean(self._repo, node.nullid, False)
   521         hg.clean(self._repo, node.nullid, False)
   517                                      % (subrelpath(self), srcurl))
   541                                      % (subrelpath(self), srcurl))
   518                 self._repo.pull(other)
   542                 self._repo.pull(other)
   519                 bookmarks.updatefromremote(self._repo.ui, self._repo, other,
   543                 bookmarks.updatefromremote(self._repo.ui, self._repo, other,
   520                                            srcurl)
   544                                            srcurl)
   521 
   545 
       
   546     @annotatesubrepoerror
   522     def get(self, state, overwrite=False):
   547     def get(self, state, overwrite=False):
   523         self._get(state)
   548         self._get(state)
   524         source, revision, kind = state
   549         source, revision, kind = state
   525         self._repo.ui.debug("getting subrepo %s\n" % self._path)
   550         self._repo.ui.debug("getting subrepo %s\n" % self._path)
   526         hg.updaterepo(self._repo, revision, overwrite)
   551         hg.updaterepo(self._repo, revision, overwrite)
   527 
   552 
       
   553     @annotatesubrepoerror
   528     def merge(self, state):
   554     def merge(self, state):
   529         self._get(state)
   555         self._get(state)
   530         cur = self._repo['.']
   556         cur = self._repo['.']
   531         dst = self._repo[state[1]]
   557         dst = self._repo[state[1]]
   532         anc = dst.ancestor(cur)
   558         anc = dst.ancestor(cur)
   549             else:
   575             else:
   550                 mergefunc()
   576                 mergefunc()
   551         else:
   577         else:
   552             mergefunc()
   578             mergefunc()
   553 
   579 
       
   580     @annotatesubrepoerror
   554     def push(self, opts):
   581     def push(self, opts):
   555         force = opts.get('force')
   582         force = opts.get('force')
   556         newbranch = opts.get('new_branch')
   583         newbranch = opts.get('new_branch')
   557         ssh = opts.get('ssh')
   584         ssh = opts.get('ssh')
   558 
   585 
   567         self._repo.ui.status(_('pushing subrepo %s to %s\n') %
   594         self._repo.ui.status(_('pushing subrepo %s to %s\n') %
   568             (subrelpath(self), dsturl))
   595             (subrelpath(self), dsturl))
   569         other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
   596         other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
   570         return self._repo.push(other, force, newbranch=newbranch)
   597         return self._repo.push(other, force, newbranch=newbranch)
   571 
   598 
       
   599     @annotatesubrepoerror
   572     def outgoing(self, ui, dest, opts):
   600     def outgoing(self, ui, dest, opts):
   573         return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
   601         return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
   574 
   602 
       
   603     @annotatesubrepoerror
   575     def incoming(self, ui, source, opts):
   604     def incoming(self, ui, source, opts):
   576         return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
   605         return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
   577 
   606 
       
   607     @annotatesubrepoerror
   578     def files(self):
   608     def files(self):
   579         rev = self._state[1]
   609         rev = self._state[1]
   580         ctx = self._repo[rev]
   610         ctx = self._repo[rev]
   581         return ctx.manifest()
   611         return ctx.manifest()
   582 
   612 
   591 
   621 
   592     def walk(self, match):
   622     def walk(self, match):
   593         ctx = self._repo[None]
   623         ctx = self._repo[None]
   594         return ctx.walk(match)
   624         return ctx.walk(match)
   595 
   625 
       
   626     @annotatesubrepoerror
   596     def forget(self, ui, match, prefix):
   627     def forget(self, ui, match, prefix):
   597         return cmdutil.forget(ui, self._repo, match,
   628         return cmdutil.forget(ui, self._repo, match,
   598                               os.path.join(prefix, self._path), True)
   629                               os.path.join(prefix, self._path), True)
   599 
   630 
       
   631     @annotatesubrepoerror
   600     def revert(self, ui, substate, *pats, **opts):
   632     def revert(self, ui, substate, *pats, **opts):
   601         # reverting a subrepo is a 2 step process:
   633         # reverting a subrepo is a 2 step process:
   602         # 1. if the no_backup is not set, revert all modified
   634         # 1. if the no_backup is not set, revert all modified
   603         #    files inside the subrepo
   635         #    files inside the subrepo
   604         # 2. update the subrepo to the revision specified in
   636         # 2. update the subrepo to the revision specified in
   749                 return lastrev
   781                 return lastrev
   750             except error.Abort:
   782             except error.Abort:
   751                 pass
   783                 pass
   752         return rev
   784         return rev
   753 
   785 
       
   786     @annotatesubrepoerror
   754     def commit(self, text, user, date):
   787     def commit(self, text, user, date):
   755         # user and date are out of our hands since svn is centralized
   788         # user and date are out of our hands since svn is centralized
   756         changed, extchanged, missing = self._wcchanged()
   789         changed, extchanged, missing = self._wcchanged()
   757         if not changed:
   790         if not changed:
   758             return self.basestate()
   791             return self.basestate()
   776             raise util.Abort(commitinfo.splitlines()[-1])
   809             raise util.Abort(commitinfo.splitlines()[-1])
   777         newrev = newrev.groups()[0]
   810         newrev = newrev.groups()[0]
   778         self._ui.status(self._svncommand(['update', '-r', newrev])[0])
   811         self._ui.status(self._svncommand(['update', '-r', newrev])[0])
   779         return newrev
   812         return newrev
   780 
   813 
       
   814     @annotatesubrepoerror
   781     def remove(self):
   815     def remove(self):
   782         if self.dirty():
   816         if self.dirty():
   783             self._ui.warn(_('not removing repo %s because '
   817             self._ui.warn(_('not removing repo %s because '
   784                             'it has changes.\n' % self._path))
   818                             'it has changes.\n' % self._path))
   785             return
   819             return
   800         try:
   834         try:
   801             os.removedirs(os.path.dirname(path))
   835             os.removedirs(os.path.dirname(path))
   802         except OSError:
   836         except OSError:
   803             pass
   837             pass
   804 
   838 
       
   839     @annotatesubrepoerror
   805     def get(self, state, overwrite=False):
   840     def get(self, state, overwrite=False):
   806         if overwrite:
   841         if overwrite:
   807             self._svncommand(['revert', '--recursive'])
   842             self._svncommand(['revert', '--recursive'])
   808         args = ['checkout']
   843         args = ['checkout']
   809         if self._svnversion >= (1, 5):
   844         if self._svnversion >= (1, 5):
   820                 self.get(state, overwrite=False)
   855                 self.get(state, overwrite=False)
   821                 return
   856                 return
   822             raise util.Abort((status or err).splitlines()[-1])
   857             raise util.Abort((status or err).splitlines()[-1])
   823         self._ui.status(status)
   858         self._ui.status(status)
   824 
   859 
       
   860     @annotatesubrepoerror
   825     def merge(self, state):
   861     def merge(self, state):
   826         old = self._state[1]
   862         old = self._state[1]
   827         new = state[1]
   863         new = state[1]
   828         wcrev = self._wcrev()
   864         wcrev = self._wcrev()
   829         if new != wcrev:
   865         if new != wcrev:
   833 
   869 
   834     def push(self, opts):
   870     def push(self, opts):
   835         # push is a no-op for SVN
   871         # push is a no-op for SVN
   836         return True
   872         return True
   837 
   873 
       
   874     @annotatesubrepoerror
   838     def files(self):
   875     def files(self):
   839         output = self._svncommand(['list', '--recursive', '--xml'])[0]
   876         output = self._svncommand(['list', '--recursive', '--xml'])[0]
   840         doc = xml.dom.minidom.parseString(output)
   877         doc = xml.dom.minidom.parseString(output)
   841         paths = []
   878         paths = []
   842         for e in doc.getElementsByTagName('entry'):
   879         for e in doc.getElementsByTagName('entry'):
  1019         self._gitcommand(['fetch'])
  1056         self._gitcommand(['fetch'])
  1020         if not self._githavelocally(revision):
  1057         if not self._githavelocally(revision):
  1021             raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
  1058             raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
  1022                                (revision, self._relpath))
  1059                                (revision, self._relpath))
  1023 
  1060 
       
  1061     @annotatesubrepoerror
  1024     def dirty(self, ignoreupdate=False):
  1062     def dirty(self, ignoreupdate=False):
  1025         if self._gitmissing():
  1063         if self._gitmissing():
  1026             return self._state[1] != ''
  1064             return self._state[1] != ''
  1027         if self._gitisbare():
  1065         if self._gitisbare():
  1028             return True
  1066             return True
  1035         return code == 1
  1073         return code == 1
  1036 
  1074 
  1037     def basestate(self):
  1075     def basestate(self):
  1038         return self._gitstate()
  1076         return self._gitstate()
  1039 
  1077 
       
  1078     @annotatesubrepoerror
  1040     def get(self, state, overwrite=False):
  1079     def get(self, state, overwrite=False):
  1041         source, revision, kind = state
  1080         source, revision, kind = state
  1042         if not revision:
  1081         if not revision:
  1043             self.remove()
  1082             self.remove()
  1044             return
  1083             return
  1118             self._gitcommand(['merge', '--ff', remote])
  1157             self._gitcommand(['merge', '--ff', remote])
  1119         else:
  1158         else:
  1120             # a real merge would be required, just checkout the revision
  1159             # a real merge would be required, just checkout the revision
  1121             rawcheckout()
  1160             rawcheckout()
  1122 
  1161 
       
  1162     @annotatesubrepoerror
  1123     def commit(self, text, user, date):
  1163     def commit(self, text, user, date):
  1124         if self._gitmissing():
  1164         if self._gitmissing():
  1125             raise util.Abort(_("subrepo %s is missing") % self._relpath)
  1165             raise util.Abort(_("subrepo %s is missing") % self._relpath)
  1126         cmd = ['commit', '-a', '-m', text]
  1166         cmd = ['commit', '-a', '-m', text]
  1127         env = os.environ.copy()
  1167         env = os.environ.copy()
  1135         self._gitcommand(cmd, env=env)
  1175         self._gitcommand(cmd, env=env)
  1136         # make sure commit works otherwise HEAD might not exist under certain
  1176         # make sure commit works otherwise HEAD might not exist under certain
  1137         # circumstances
  1177         # circumstances
  1138         return self._gitstate()
  1178         return self._gitstate()
  1139 
  1179 
       
  1180     @annotatesubrepoerror
  1140     def merge(self, state):
  1181     def merge(self, state):
  1141         source, revision, kind = state
  1182         source, revision, kind = state
  1142         self._fetch(source, revision)
  1183         self._fetch(source, revision)
  1143         base = self._gitcommand(['merge-base', revision, self._state[1]])
  1184         base = self._gitcommand(['merge-base', revision, self._state[1]])
  1144         self._gitupdatestat()
  1185         self._gitupdatestat()
  1157                                  self._state[1][:7], revision[:7]):
  1198                                  self._state[1][:7], revision[:7]):
  1158                     mergefunc()
  1199                     mergefunc()
  1159         else:
  1200         else:
  1160             mergefunc()
  1201             mergefunc()
  1161 
  1202 
       
  1203     @annotatesubrepoerror
  1162     def push(self, opts):
  1204     def push(self, opts):
  1163         force = opts.get('force')
  1205         force = opts.get('force')
  1164 
  1206 
  1165         if not self._state[1]:
  1207         if not self._state[1]:
  1166             return True
  1208             return True
  1196             self._ui.warn(_('no branch checked out in subrepo %s\n'
  1238             self._ui.warn(_('no branch checked out in subrepo %s\n'
  1197                             'cannot push revision %s\n') %
  1239                             'cannot push revision %s\n') %
  1198                           (self._relpath, self._state[1]))
  1240                           (self._relpath, self._state[1]))
  1199             return False
  1241             return False
  1200 
  1242 
       
  1243     @annotatesubrepoerror
  1201     def remove(self):
  1244     def remove(self):
  1202         if self._gitmissing():
  1245         if self._gitmissing():
  1203             return
  1246             return
  1204         if self.dirty():
  1247         if self.dirty():
  1205             self._ui.warn(_('not removing repo %s because '
  1248             self._ui.warn(_('not removing repo %s because '
  1245             ui.progress(_('archiving (%s)') % relpath, i + 1,
  1288             ui.progress(_('archiving (%s)') % relpath, i + 1,
  1246                         unit=_('files'))
  1289                         unit=_('files'))
  1247         ui.progress(_('archiving (%s)') % relpath, None)
  1290         ui.progress(_('archiving (%s)') % relpath, None)
  1248 
  1291 
  1249 
  1292 
       
  1293     @annotatesubrepoerror
  1250     def status(self, rev2, **opts):
  1294     def status(self, rev2, **opts):
  1251         rev1 = self._state[1]
  1295         rev1 = self._state[1]
  1252         if self._gitmissing() or not rev1:
  1296         if self._gitmissing() or not rev1:
  1253             # if the repo is missing, return no results
  1297             # if the repo is missing, return no results
  1254             return [], [], [], [], [], [], []
  1298             return [], [], [], [], [], [], []