mercurial/subrepo.py
changeset 26587 56b2bcea2529
parent 25980 38c585c2f8cc
child 26781 1aee2ab0f902
equal deleted inserted replaced
26586:d51c658d3f04 26587:56b2bcea2529
    93                 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
    93                 ui.warn(_("warning: subrepo spec file \'%s\' not found\n") %
    94                         repo.pathto(f))
    94                         repo.pathto(f))
    95                 return
    95                 return
    96             p.parse(f, data, sections, remap, read)
    96             p.parse(f, data, sections, remap, read)
    97         else:
    97         else:
    98             raise util.Abort(_("subrepo spec file \'%s\' not found") %
    98             raise error.Abort(_("subrepo spec file \'%s\' not found") %
    99                              repo.pathto(f))
    99                              repo.pathto(f))
   100     if '.hgsub' in ctx:
   100     if '.hgsub' in ctx:
   101         read('.hgsub')
   101         read('.hgsub')
   102 
   102 
   103     for path, src in ui.configitems('subpaths'):
   103     for path, src in ui.configitems('subpaths'):
   111                 if not l:
   111                 if not l:
   112                     continue
   112                     continue
   113                 try:
   113                 try:
   114                     revision, path = l.split(" ", 1)
   114                     revision, path = l.split(" ", 1)
   115                 except ValueError:
   115                 except ValueError:
   116                     raise util.Abort(_("invalid subrepository revision "
   116                     raise error.Abort(_("invalid subrepository revision "
   117                                        "specifier in \'%s\' line %d")
   117                                        "specifier in \'%s\' line %d")
   118                                      % (repo.pathto('.hgsubstate'), (i + 1)))
   118                                      % (repo.pathto('.hgsubstate'), (i + 1)))
   119                 rev[path] = revision
   119                 rev[path] = revision
   120         except IOError as err:
   120         except IOError as err:
   121             if err.errno != errno.ENOENT:
   121             if err.errno != errno.ENOENT:
   131             # extra escapes are needed because re.sub string decodes.
   131             # extra escapes are needed because re.sub string decodes.
   132             repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
   132             repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
   133             try:
   133             try:
   134                 src = re.sub(pattern, repl, src, 1)
   134                 src = re.sub(pattern, repl, src, 1)
   135             except re.error as e:
   135             except re.error as e:
   136                 raise util.Abort(_("bad subrepository pattern in %s: %s")
   136                 raise error.Abort(_("bad subrepository pattern in %s: %s")
   137                                  % (p.source('subpaths', pattern), e))
   137                                  % (p.source('subpaths', pattern), e))
   138         return src
   138         return src
   139 
   139 
   140     state = {}
   140     state = {}
   141     for path, src in p[''].items():
   141     for path, src in p[''].items():
   142         kind = 'hg'
   142         kind = 'hg'
   143         if src.startswith('['):
   143         if src.startswith('['):
   144             if ']' not in src:
   144             if ']' not in src:
   145                 raise util.Abort(_('missing ] in subrepo source'))
   145                 raise error.Abort(_('missing ] in subrepo source'))
   146             kind, src = src.split(']', 1)
   146             kind, src = src.split(']', 1)
   147             kind = kind[1:]
   147             kind = kind[1:]
   148             src = src.lstrip() # strip any extra whitespace after ']'
   148             src = src.lstrip() # strip any extra whitespace after ']'
   149 
   149 
   150         if not util.url(src).isabs():
   150         if not util.url(src).isabs():
   322             return repo.ui.config('paths', 'default')
   322             return repo.ui.config('paths', 'default')
   323         if repo.shared():
   323         if repo.shared():
   324             # chop off the .hg component to get the default path form
   324             # chop off the .hg component to get the default path form
   325             return os.path.dirname(repo.sharedpath)
   325             return os.path.dirname(repo.sharedpath)
   326     if abort:
   326     if abort:
   327         raise util.Abort(_("default path for subrepository not found"))
   327         raise error.Abort(_("default path for subrepository not found"))
   328 
   328 
   329 def _sanitize(ui, vfs, ignore):
   329 def _sanitize(ui, vfs, ignore):
   330     for dirname, dirs, names in vfs.walk():
   330     for dirname, dirs, names in vfs.walk():
   331         for i, d in enumerate(dirs):
   331         for i, d in enumerate(dirs):
   332             if d.lower() == ignore:
   332             if d.lower() == ignore:
   351     hg = h
   351     hg = h
   352 
   352 
   353     pathutil.pathauditor(ctx.repo().root)(path)
   353     pathutil.pathauditor(ctx.repo().root)(path)
   354     state = ctx.substate[path]
   354     state = ctx.substate[path]
   355     if state[2] not in types:
   355     if state[2] not in types:
   356         raise util.Abort(_('unknown subrepo type %s') % state[2])
   356         raise error.Abort(_('unknown subrepo type %s') % state[2])
   357     if allowwdir:
   357     if allowwdir:
   358         state = (state[0], ctx.subrev(path), state[2])
   358         state = (state[0], ctx.subrev(path), state[2])
   359     return types[state[2]](ctx, path, state[:2])
   359     return types[state[2]](ctx, path, state[:2])
   360 
   360 
   361 def nullsubrepo(ctx, path, pctx):
   361 def nullsubrepo(ctx, path, pctx):
   369     hg = h
   369     hg = h
   370 
   370 
   371     pathutil.pathauditor(ctx.repo().root)(path)
   371     pathutil.pathauditor(ctx.repo().root)(path)
   372     state = ctx.substate[path]
   372     state = ctx.substate[path]
   373     if state[2] not in types:
   373     if state[2] not in types:
   374         raise util.Abort(_('unknown subrepo type %s') % state[2])
   374         raise error.Abort(_('unknown subrepo type %s') % state[2])
   375     subrev = ''
   375     subrev = ''
   376     if state[2] == 'hg':
   376     if state[2] == 'hg':
   377         subrev = "0" * 40
   377         subrev = "0" * 40
   378     return types[state[2]](pctx, path, (state[0], subrev))
   378     return types[state[2]](pctx, path, (state[0], subrev))
   379 
   379 
   382     substate = getattr(ctx, "substate", None)
   382     substate = getattr(ctx, "substate", None)
   383     if not substate:
   383     if not substate:
   384         return commitphase
   384         return commitphase
   385     check = ui.config('phases', 'checksubrepos', 'follow')
   385     check = ui.config('phases', 'checksubrepos', 'follow')
   386     if check not in ('ignore', 'follow', 'abort'):
   386     if check not in ('ignore', 'follow', 'abort'):
   387         raise util.Abort(_('invalid phases.checksubrepos configuration: %s')
   387         raise error.Abort(_('invalid phases.checksubrepos configuration: %s')
   388                          % (check))
   388                          % (check))
   389     if check == 'ignore':
   389     if check == 'ignore':
   390         return commitphase
   390         return commitphase
   391     maxphase = phases.public
   391     maxphase = phases.public
   392     maxsub = None
   392     maxsub = None
   396         if maxphase < subphase:
   396         if maxphase < subphase:
   397             maxphase = subphase
   397             maxphase = subphase
   398             maxsub = s
   398             maxsub = s
   399     if commitphase < maxphase:
   399     if commitphase < maxphase:
   400         if check == 'abort':
   400         if check == 'abort':
   401             raise util.Abort(_("can't commit in %s phase"
   401             raise error.Abort(_("can't commit in %s phase"
   402                                " conflicting %s from subrepository %s") %
   402                                " conflicting %s from subrepository %s") %
   403                              (phases.phasenames[commitphase],
   403                              (phases.phasenames[commitphase],
   404                               phases.phasenames[maxphase], maxsub))
   404                               phases.phasenames[maxphase], maxsub))
   405         ui.warn(_("warning: changes are committed in"
   405         ui.warn(_("warning: changes are committed in"
   406                   " %s phase from subrepository %s\n") %
   406                   " %s phase from subrepository %s\n") %
   454     def bailifchanged(self, ignoreupdate=False):
   454     def bailifchanged(self, ignoreupdate=False):
   455         """raise Abort if subrepository is ``dirty()``
   455         """raise Abort if subrepository is ``dirty()``
   456         """
   456         """
   457         dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate)
   457         dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate)
   458         if dirtyreason:
   458         if dirtyreason:
   459             raise util.Abort(dirtyreason)
   459             raise error.Abort(dirtyreason)
   460 
   460 
   461     def basestate(self):
   461     def basestate(self):
   462         """current working directory base state, disregarding .hgsubstate
   462         """current working directory base state, disregarding .hgsubstate
   463         state and working directory modifications"""
   463         state and working directory modifications"""
   464         raise NotImplementedError
   464         raise NotImplementedError
  1071     def __init__(self, ctx, path, state):
  1071     def __init__(self, ctx, path, state):
  1072         super(svnsubrepo, self).__init__(ctx, path)
  1072         super(svnsubrepo, self).__init__(ctx, path)
  1073         self._state = state
  1073         self._state = state
  1074         self._exe = util.findexe('svn')
  1074         self._exe = util.findexe('svn')
  1075         if not self._exe:
  1075         if not self._exe:
  1076             raise util.Abort(_("'svn' executable not found for subrepo '%s'")
  1076             raise error.Abort(_("'svn' executable not found for subrepo '%s'")
  1077                              % self._path)
  1077                              % self._path)
  1078 
  1078 
  1079     def _svncommand(self, commands, filename='', failok=False):
  1079     def _svncommand(self, commands, filename='', failok=False):
  1080         cmd = [self._exe]
  1080         cmd = [self._exe]
  1081         extrakw = {}
  1081         extrakw = {}
  1106                               universal_newlines=True, env=env, **extrakw)
  1106                               universal_newlines=True, env=env, **extrakw)
  1107         stdout, stderr = p.communicate()
  1107         stdout, stderr = p.communicate()
  1108         stderr = stderr.strip()
  1108         stderr = stderr.strip()
  1109         if not failok:
  1109         if not failok:
  1110             if p.returncode:
  1110             if p.returncode:
  1111                 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
  1111                 raise error.Abort(stderr or 'exited with code %d'
       
  1112                                   % p.returncode)
  1112             if stderr:
  1113             if stderr:
  1113                 self.ui.warn(stderr + '\n')
  1114                 self.ui.warn(stderr + '\n')
  1114         return stdout, stderr
  1115         return stdout, stderr
  1115 
  1116 
  1116     @propertycache
  1117     @propertycache
  1117     def _svnversion(self):
  1118     def _svnversion(self):
  1118         output, err = self._svncommand(['--version', '--quiet'], filename=None)
  1119         output, err = self._svncommand(['--version', '--quiet'], filename=None)
  1119         m = re.search(r'^(\d+)\.(\d+)', output)
  1120         m = re.search(r'^(\d+)\.(\d+)', output)
  1120         if not m:
  1121         if not m:
  1121             raise util.Abort(_('cannot retrieve svn tool version'))
  1122             raise error.Abort(_('cannot retrieve svn tool version'))
  1122         return (int(m.group(1)), int(m.group(2)))
  1123         return (int(m.group(1)), int(m.group(2)))
  1123 
  1124 
  1124     def _wcrevs(self):
  1125     def _wcrevs(self):
  1125         # Get the working directory revision as well as the last
  1126         # Get the working directory revision as well as the last
  1126         # commit revision so we can compare the subrepo state with
  1127         # commit revision so we can compare the subrepo state with
  1194         changed, extchanged, missing = self._wcchanged()
  1195         changed, extchanged, missing = self._wcchanged()
  1195         if not changed:
  1196         if not changed:
  1196             return self.basestate()
  1197             return self.basestate()
  1197         if extchanged:
  1198         if extchanged:
  1198             # Do not try to commit externals
  1199             # Do not try to commit externals
  1199             raise util.Abort(_('cannot commit svn externals'))
  1200             raise error.Abort(_('cannot commit svn externals'))
  1200         if missing:
  1201         if missing:
  1201             # svn can commit with missing entries but aborting like hg
  1202             # svn can commit with missing entries but aborting like hg
  1202             # seems a better approach.
  1203             # seems a better approach.
  1203             raise util.Abort(_('cannot commit missing svn entries'))
  1204             raise error.Abort(_('cannot commit missing svn entries'))
  1204         commitinfo, err = self._svncommand(['commit', '-m', text])
  1205         commitinfo, err = self._svncommand(['commit', '-m', text])
  1205         self.ui.status(commitinfo)
  1206         self.ui.status(commitinfo)
  1206         newrev = re.search('Committed revision ([0-9]+).', commitinfo)
  1207         newrev = re.search('Committed revision ([0-9]+).', commitinfo)
  1207         if not newrev:
  1208         if not newrev:
  1208             if not commitinfo.strip():
  1209             if not commitinfo.strip():
  1209                 # Sometimes, our definition of "changed" differs from
  1210                 # Sometimes, our definition of "changed" differs from
  1210                 # svn one. For instance, svn ignores missing files
  1211                 # svn one. For instance, svn ignores missing files
  1211                 # when committing. If there are only missing files, no
  1212                 # when committing. If there are only missing files, no
  1212                 # commit is made, no output and no error code.
  1213                 # commit is made, no output and no error code.
  1213                 raise util.Abort(_('failed to commit svn changes'))
  1214                 raise error.Abort(_('failed to commit svn changes'))
  1214             raise util.Abort(commitinfo.splitlines()[-1])
  1215             raise error.Abort(commitinfo.splitlines()[-1])
  1215         newrev = newrev.groups()[0]
  1216         newrev = newrev.groups()[0]
  1216         self.ui.status(self._svncommand(['update', '-r', newrev])[0])
  1217         self.ui.status(self._svncommand(['update', '-r', newrev])[0])
  1217         return newrev
  1218         return newrev
  1218 
  1219 
  1219     @annotatesubrepoerror
  1220     @annotatesubrepoerror
  1248                 and (self._wcchanged()[:2] == (False, False))):
  1249                 and (self._wcchanged()[:2] == (False, False))):
  1249                 # obstructed but clean working copy, so just blow it away.
  1250                 # obstructed but clean working copy, so just blow it away.
  1250                 self.remove()
  1251                 self.remove()
  1251                 self.get(state, overwrite=False)
  1252                 self.get(state, overwrite=False)
  1252                 return
  1253                 return
  1253             raise util.Abort((status or err).splitlines()[-1])
  1254             raise error.Abort((status or err).splitlines()[-1])
  1254         self.ui.status(status)
  1255         self.ui.status(status)
  1255 
  1256 
  1256     @annotatesubrepoerror
  1257     @annotatesubrepoerror
  1257     def merge(self, state):
  1258     def merge(self, state):
  1258         old = self._state[1]
  1259         old = self._state[1]
  1305             out, err = self._gitnodir(['--version'])
  1306             out, err = self._gitnodir(['--version'])
  1306         versionstatus = self._checkversion(out)
  1307         versionstatus = self._checkversion(out)
  1307         if versionstatus == 'unknown':
  1308         if versionstatus == 'unknown':
  1308             self.ui.warn(_('cannot retrieve git version\n'))
  1309             self.ui.warn(_('cannot retrieve git version\n'))
  1309         elif versionstatus == 'abort':
  1310         elif versionstatus == 'abort':
  1310             raise util.Abort(_('git subrepo requires at least 1.6.0 or later'))
  1311             raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
  1311         elif versionstatus == 'warning':
  1312         elif versionstatus == 'warning':
  1312             self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
  1313             self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
  1313 
  1314 
  1314     @staticmethod
  1315     @staticmethod
  1315     def _gitversion(out):
  1316     def _gitversion(out):
  1392             # there are certain error codes that are ok
  1393             # there are certain error codes that are ok
  1393             command = commands[0]
  1394             command = commands[0]
  1394             if command in ('cat-file', 'symbolic-ref'):
  1395             if command in ('cat-file', 'symbolic-ref'):
  1395                 return retdata, p.returncode
  1396                 return retdata, p.returncode
  1396             # for all others, abort
  1397             # for all others, abort
  1397             raise util.Abort('git %s error %d in %s' %
  1398             raise error.Abort('git %s error %d in %s' %
  1398                              (command, p.returncode, self._relpath))
  1399                              (command, p.returncode, self._relpath))
  1399 
  1400 
  1400         return retdata, p.returncode
  1401         return retdata, p.returncode
  1401 
  1402 
  1402     def _gitmissing(self):
  1403     def _gitmissing(self):
  1489         self.ui.status(_('pulling subrepo %s from %s\n') %
  1490         self.ui.status(_('pulling subrepo %s from %s\n') %
  1490                         (self._relpath, self._gitremote('origin')))
  1491                         (self._relpath, self._gitremote('origin')))
  1491         # try only origin: the originally cloned repo
  1492         # try only origin: the originally cloned repo
  1492         self._gitcommand(['fetch'])
  1493         self._gitcommand(['fetch'])
  1493         if not self._githavelocally(revision):
  1494         if not self._githavelocally(revision):
  1494             raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
  1495             raise error.Abort(_("revision %s does not exist in subrepo %s\n") %
  1495                                (revision, self._relpath))
  1496                                (revision, self._relpath))
  1496 
  1497 
  1497     @annotatesubrepoerror
  1498     @annotatesubrepoerror
  1498     def dirty(self, ignoreupdate=False):
  1499     def dirty(self, ignoreupdate=False):
  1499         if self._gitmissing():
  1500         if self._gitmissing():
  1598             rawcheckout()
  1599             rawcheckout()
  1599 
  1600 
  1600     @annotatesubrepoerror
  1601     @annotatesubrepoerror
  1601     def commit(self, text, user, date):
  1602     def commit(self, text, user, date):
  1602         if self._gitmissing():
  1603         if self._gitmissing():
  1603             raise util.Abort(_("subrepo %s is missing") % self._relpath)
  1604             raise error.Abort(_("subrepo %s is missing") % self._relpath)
  1604         cmd = ['commit', '-a', '-m', text]
  1605         cmd = ['commit', '-a', '-m', text]
  1605         env = os.environ.copy()
  1606         env = os.environ.copy()
  1606         if user:
  1607         if user:
  1607             cmd += ['--author', user]
  1608             cmd += ['--author', user]
  1608         if date:
  1609         if date:
  1644         force = opts.get('force')
  1645         force = opts.get('force')
  1645 
  1646 
  1646         if not self._state[1]:
  1647         if not self._state[1]:
  1647             return True
  1648             return True
  1648         if self._gitmissing():
  1649         if self._gitmissing():
  1649             raise util.Abort(_("subrepo %s is missing") % self._relpath)
  1650             raise error.Abort(_("subrepo %s is missing") % self._relpath)
  1650         # if a branch in origin contains the revision, nothing to do
  1651         # if a branch in origin contains the revision, nothing to do
  1651         branch2rev, rev2branch = self._gitbranchmap()
  1652         branch2rev, rev2branch = self._gitbranchmap()
  1652         if self._state[1] in rev2branch:
  1653         if self._state[1] in rev2branch:
  1653             for b in rev2branch[self._state[1]]:
  1654             for b in rev2branch[self._state[1]]:
  1654                 if b.startswith('refs/remotes/origin/'):
  1655                 if b.startswith('refs/remotes/origin/'):