mercurial/subrepo.py
changeset 43554 9f70512ae2cf
parent 43117 8ff1ecfadcd1
child 43695 aeed2f106213
equal deleted inserted replaced
43553:47fac1692ede 43554:9f70512ae2cf
    67 
    67 
    68 class SubrepoAbort(error.Abort):
    68 class SubrepoAbort(error.Abort):
    69     """Exception class used to avoid handling a subrepo error more than once"""
    69     """Exception class used to avoid handling a subrepo error more than once"""
    70 
    70 
    71     def __init__(self, *args, **kw):
    71     def __init__(self, *args, **kw):
    72         self.subrepo = kw.pop(r'subrepo', None)
    72         self.subrepo = kw.pop('subrepo', None)
    73         self.cause = kw.pop(r'cause', None)
    73         self.cause = kw.pop('cause', None)
    74         error.Abort.__init__(self, *args, **kw)
    74         error.Abort.__init__(self, *args, **kw)
    75 
    75 
    76 
    76 
    77 def annotatesubrepoerror(func):
    77 def annotatesubrepoerror(func):
    78     def decoratedmethod(self, *args, **kargs):
    78     def decoratedmethod(self, *args, **kargs):
   967         # 1. if the no_backup is not set, revert all modified
   967         # 1. if the no_backup is not set, revert all modified
   968         #    files inside the subrepo
   968         #    files inside the subrepo
   969         # 2. update the subrepo to the revision specified in
   969         # 2. update the subrepo to the revision specified in
   970         #    the corresponding substate dictionary
   970         #    the corresponding substate dictionary
   971         self.ui.status(_(b'reverting subrepo %s\n') % substate[0])
   971         self.ui.status(_(b'reverting subrepo %s\n') % substate[0])
   972         if not opts.get(r'no_backup'):
   972         if not opts.get('no_backup'):
   973             # Revert all files on the subrepo, creating backups
   973             # Revert all files on the subrepo, creating backups
   974             # Note that this will not recursively revert subrepos
   974             # Note that this will not recursively revert subrepos
   975             # We could do it if there was a set:subrepos() predicate
   975             # We could do it if there was a set:subrepos() predicate
   976             opts = opts.copy()
   976             opts = opts.copy()
   977             opts[r'date'] = None
   977             opts['date'] = None
   978             opts[r'rev'] = substate[1]
   978             opts['rev'] = substate[1]
   979 
   979 
   980             self.filerevert(*pats, **opts)
   980             self.filerevert(*pats, **opts)
   981 
   981 
   982         # Update the repo to the revision specified in the given substate
   982         # Update the repo to the revision specified in the given substate
   983         if not opts.get(r'dry_run'):
   983         if not opts.get('dry_run'):
   984             self.get(substate, overwrite=True)
   984             self.get(substate, overwrite=True)
   985 
   985 
   986     def filerevert(self, *pats, **opts):
   986     def filerevert(self, *pats, **opts):
   987         ctx = self._repo[opts[r'rev']]
   987         ctx = self._repo[opts['rev']]
   988         parents = self._repo.dirstate.parents()
   988         parents = self._repo.dirstate.parents()
   989         if opts.get(r'all'):
   989         if opts.get('all'):
   990             pats = [b'set:modified()']
   990             pats = [b'set:modified()']
   991         else:
   991         else:
   992             pats = []
   992             pats = []
   993         cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
   993         cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
   994 
   994 
  1064         cmd = [self._exe]
  1064         cmd = [self._exe]
  1065         extrakw = {}
  1065         extrakw = {}
  1066         if not self.ui.interactive():
  1066         if not self.ui.interactive():
  1067             # Making stdin be a pipe should prevent svn from behaving
  1067             # Making stdin be a pipe should prevent svn from behaving
  1068             # interactively even if we can't pass --non-interactive.
  1068             # interactively even if we can't pass --non-interactive.
  1069             extrakw[r'stdin'] = subprocess.PIPE
  1069             extrakw['stdin'] = subprocess.PIPE
  1070             # Starting in svn 1.5 --non-interactive is a global flag
  1070             # Starting in svn 1.5 --non-interactive is a global flag
  1071             # instead of being per-command, but we need to support 1.4 so
  1071             # instead of being per-command, but we need to support 1.4 so
  1072             # we have to be intelligent about what commands take
  1072             # we have to be intelligent about what commands take
  1073             # --non-interactive.
  1073             # --non-interactive.
  1074             if commands[0] in (b'update', b'checkout', b'commit'):
  1074             if commands[0] in (b'update', b'checkout', b'commit'):
  1123         # Get the working directory revision as well as the last
  1123         # Get the working directory revision as well as the last
  1124         # commit revision so we can compare the subrepo state with
  1124         # commit revision so we can compare the subrepo state with
  1125         # both. We used to store the working directory one.
  1125         # both. We used to store the working directory one.
  1126         output, err = self._svncommand([b'info', b'--xml'])
  1126         output, err = self._svncommand([b'info', b'--xml'])
  1127         doc = xml.dom.minidom.parseString(output)
  1127         doc = xml.dom.minidom.parseString(output)
  1128         entries = doc.getElementsByTagName(r'entry')
  1128         entries = doc.getElementsByTagName('entry')
  1129         lastrev, rev = b'0', b'0'
  1129         lastrev, rev = b'0', b'0'
  1130         if entries:
  1130         if entries:
  1131             rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or b'0'
  1131             rev = pycompat.bytestr(entries[0].getAttribute('revision')) or b'0'
  1132             commits = entries[0].getElementsByTagName(r'commit')
  1132             commits = entries[0].getElementsByTagName('commit')
  1133             if commits:
  1133             if commits:
  1134                 lastrev = (
  1134                 lastrev = (
  1135                     pycompat.bytestr(commits[0].getAttribute(r'revision'))
  1135                     pycompat.bytestr(commits[0].getAttribute('revision'))
  1136                     or b'0'
  1136                     or b'0'
  1137                 )
  1137                 )
  1138         return (lastrev, rev)
  1138         return (lastrev, rev)
  1139 
  1139 
  1140     def _wcrev(self):
  1140     def _wcrev(self):
  1147         is True if any change is a missing entry.
  1147         is True if any change is a missing entry.
  1148         """
  1148         """
  1149         output, err = self._svncommand([b'status', b'--xml'])
  1149         output, err = self._svncommand([b'status', b'--xml'])
  1150         externals, changes, missing = [], [], []
  1150         externals, changes, missing = [], [], []
  1151         doc = xml.dom.minidom.parseString(output)
  1151         doc = xml.dom.minidom.parseString(output)
  1152         for e in doc.getElementsByTagName(r'entry'):
  1152         for e in doc.getElementsByTagName('entry'):
  1153             s = e.getElementsByTagName(r'wc-status')
  1153             s = e.getElementsByTagName('wc-status')
  1154             if not s:
  1154             if not s:
  1155                 continue
  1155                 continue
  1156             item = s[0].getAttribute(r'item')
  1156             item = s[0].getAttribute('item')
  1157             props = s[0].getAttribute(r'props')
  1157             props = s[0].getAttribute('props')
  1158             path = e.getAttribute(r'path').encode('utf8')
  1158             path = e.getAttribute('path').encode('utf8')
  1159             if item == r'external':
  1159             if item == 'external':
  1160                 externals.append(path)
  1160                 externals.append(path)
  1161             elif item == r'missing':
  1161             elif item == 'missing':
  1162                 missing.append(path)
  1162                 missing.append(path)
  1163             if item not in (
  1163             if item not in (
  1164                 r'',
  1164                 '',
  1165                 r'normal',
  1165                 'normal',
  1166                 r'unversioned',
  1166                 'unversioned',
  1167                 r'external',
  1167                 'external',
  1168             ) or props not in (r'', r'none', r'normal'):
  1168             ) or props not in ('', 'none', 'normal'):
  1169                 changes.append(path)
  1169                 changes.append(path)
  1170         for path in changes:
  1170         for path in changes:
  1171             for ext in externals:
  1171             for ext in externals:
  1172                 if path == ext or path.startswith(ext + pycompat.ossep):
  1172                 if path == ext or path.startswith(ext + pycompat.ossep):
  1173                     return True, True, bool(missing)
  1173                     return True, True, bool(missing)
  1289     @annotatesubrepoerror
  1289     @annotatesubrepoerror
  1290     def files(self):
  1290     def files(self):
  1291         output = self._svncommand([b'list', b'--recursive', b'--xml'])[0]
  1291         output = self._svncommand([b'list', b'--recursive', b'--xml'])[0]
  1292         doc = xml.dom.minidom.parseString(output)
  1292         doc = xml.dom.minidom.parseString(output)
  1293         paths = []
  1293         paths = []
  1294         for e in doc.getElementsByTagName(r'entry'):
  1294         for e in doc.getElementsByTagName('entry'):
  1295             kind = pycompat.bytestr(e.getAttribute(r'kind'))
  1295             kind = pycompat.bytestr(e.getAttribute('kind'))
  1296             if kind != b'file':
  1296             if kind != b'file':
  1297                 continue
  1297                 continue
  1298             name = r''.join(
  1298             name = ''.join(
  1299                 c.data
  1299                 c.data
  1300                 for c in e.getElementsByTagName(r'name')[0].childNodes
  1300                 for c in e.getElementsByTagName('name')[0].childNodes
  1301                 if c.nodeType == c.TEXT_NODE
  1301                 if c.nodeType == c.TEXT_NODE
  1302             )
  1302             )
  1303             paths.append(name.encode('utf8'))
  1303             paths.append(name.encode('utf8'))
  1304         return paths
  1304         return paths
  1305 
  1305 
  1806 
  1806 
  1807             if f in tracked:  # hg prints 'adding' even if already tracked
  1807             if f in tracked:  # hg prints 'adding' even if already tracked
  1808                 if exact:
  1808                 if exact:
  1809                     rejected.append(f)
  1809                     rejected.append(f)
  1810                 continue
  1810                 continue
  1811             if not opts.get(r'dry_run'):
  1811             if not opts.get('dry_run'):
  1812                 self._gitcommand(command + [f])
  1812                 self._gitcommand(command + [f])
  1813 
  1813 
  1814         for f in rejected:
  1814         for f in rejected:
  1815             ui.warn(_(b"%s already tracked!\n") % uipathfn(f))
  1815             ui.warn(_(b"%s already tracked!\n") % uipathfn(f))
  1816 
  1816 
  1847 
  1847 
  1848         # Parse git's native archive command.
  1848         # Parse git's native archive command.
  1849         # This should be much faster than manually traversing the trees
  1849         # This should be much faster than manually traversing the trees
  1850         # and objects with many subprocess calls.
  1850         # and objects with many subprocess calls.
  1851         tarstream = self._gitcommand([b'archive', revision], stream=True)
  1851         tarstream = self._gitcommand([b'archive', revision], stream=True)
  1852         tar = tarfile.open(fileobj=tarstream, mode=r'r|')
  1852         tar = tarfile.open(fileobj=tarstream, mode='r|')
  1853         relpath = subrelpath(self)
  1853         relpath = subrelpath(self)
  1854         progress = self.ui.makeprogress(
  1854         progress = self.ui.makeprogress(
  1855             _(b'archiving (%s)') % relpath, unit=_(b'files')
  1855             _(b'archiving (%s)') % relpath, unit=_(b'files')
  1856         )
  1856         )
  1857         progress.update(0)
  1857         progress.update(0)
  1916                 removed.append(f)
  1916                 removed.append(f)
  1917 
  1917 
  1918         deleted, unknown, ignored, clean = [], [], [], []
  1918         deleted, unknown, ignored, clean = [], [], [], []
  1919 
  1919 
  1920         command = [b'status', b'--porcelain', b'-z']
  1920         command = [b'status', b'--porcelain', b'-z']
  1921         if opts.get(r'unknown'):
  1921         if opts.get('unknown'):
  1922             command += [b'--untracked-files=all']
  1922             command += [b'--untracked-files=all']
  1923         if opts.get(r'ignored'):
  1923         if opts.get('ignored'):
  1924             command += [b'--ignored']
  1924             command += [b'--ignored']
  1925         out = self._gitcommand(command)
  1925         out = self._gitcommand(command)
  1926 
  1926 
  1927         changedfiles = set()
  1927         changedfiles = set()
  1928         changedfiles.update(modified)
  1928         changedfiles.update(modified)
  1946             if st == b'??':
  1946             if st == b'??':
  1947                 unknown.append(filename1)
  1947                 unknown.append(filename1)
  1948             elif st == b'!!':
  1948             elif st == b'!!':
  1949                 ignored.append(filename1)
  1949                 ignored.append(filename1)
  1950 
  1950 
  1951         if opts.get(r'clean'):
  1951         if opts.get('clean'):
  1952             out = self._gitcommand([b'ls-files'])
  1952             out = self._gitcommand([b'ls-files'])
  1953             for f in out.split(b'\n'):
  1953             for f in out.split(b'\n'):
  1954                 if not f in changedfiles:
  1954                 if not f in changedfiles:
  1955                     clean.append(f)
  1955                     clean.append(f)
  1956 
  1956 
  1960 
  1960 
  1961     @annotatesubrepoerror
  1961     @annotatesubrepoerror
  1962     def diff(self, ui, diffopts, node2, match, prefix, **opts):
  1962     def diff(self, ui, diffopts, node2, match, prefix, **opts):
  1963         node1 = self._state[1]
  1963         node1 = self._state[1]
  1964         cmd = [b'diff', b'--no-renames']
  1964         cmd = [b'diff', b'--no-renames']
  1965         if opts[r'stat']:
  1965         if opts['stat']:
  1966             cmd.append(b'--stat')
  1966             cmd.append(b'--stat')
  1967         else:
  1967         else:
  1968             # for Git, this also implies '-p'
  1968             # for Git, this also implies '-p'
  1969             cmd.append(b'-U%d' % diffopts.context)
  1969             cmd.append(b'-U%d' % diffopts.context)
  1970 
  1970 
  2005             ui.write(output)
  2005             ui.write(output)
  2006 
  2006 
  2007     @annotatesubrepoerror
  2007     @annotatesubrepoerror
  2008     def revert(self, substate, *pats, **opts):
  2008     def revert(self, substate, *pats, **opts):
  2009         self.ui.status(_(b'reverting subrepo %s\n') % substate[0])
  2009         self.ui.status(_(b'reverting subrepo %s\n') % substate[0])
  2010         if not opts.get(r'no_backup'):
  2010         if not opts.get('no_backup'):
  2011             status = self.status(None)
  2011             status = self.status(None)
  2012             names = status.modified
  2012             names = status.modified
  2013             for name in names:
  2013             for name in names:
  2014                 # backuppath() expects a path relative to the parent repo (the
  2014                 # backuppath() expects a path relative to the parent repo (the
  2015                 # repo that ui.origbackuppath is relative to)
  2015                 # repo that ui.origbackuppath is relative to)
  2021                     _(b'saving current version of %s as %s\n')
  2021                     _(b'saving current version of %s as %s\n')
  2022                     % (name, os.path.relpath(bakname))
  2022                     % (name, os.path.relpath(bakname))
  2023                 )
  2023                 )
  2024                 util.rename(self.wvfs.join(name), bakname)
  2024                 util.rename(self.wvfs.join(name), bakname)
  2025 
  2025 
  2026         if not opts.get(r'dry_run'):
  2026         if not opts.get('dry_run'):
  2027             self.get(substate, overwrite=True)
  2027             self.get(substate, overwrite=True)
  2028         return []
  2028         return []
  2029 
  2029 
  2030     def shortid(self, revid):
  2030     def shortid(self, revid):
  2031         return revid[:7]
  2031         return revid[:7]