mercurial/subrepo.py
branchstable
changeset 43306 59338f956109
parent 43117 8ff1ecfadcd1
child 43506 9f70512ae2cf
--- a/mercurial/subrepo.py	Wed Oct 02 12:20:36 2019 -0400
+++ b/mercurial/subrepo.py	Mon Oct 21 11:09:48 2019 -0400
@@ -19,6 +19,7 @@
 import xml.dom.minidom
 
 from .i18n import _
+from .pycompat import open
 from . import (
     cmdutil,
     encoding,
@@ -47,6 +48,7 @@
 _abssource = subrepoutil._abssource
 propertycache = util.propertycache
 
+
 def _expandedabspath(path):
     '''
     get a path or url and if it is a path expand it and return an absolute path
@@ -57,17 +59,21 @@
         path = util.normpath(os.path.abspath(u.path))
     return path
 
+
 def _getstorehashcachename(remotepath):
     '''get a unique filename for the store hash cache of a remote repository'''
     return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12]
 
+
 class SubrepoAbort(error.Abort):
     """Exception class used to avoid handling a subrepo error more than once"""
+
     def __init__(self, *args, **kw):
         self.subrepo = kw.pop(r'subrepo', None)
         self.cause = kw.pop(r'cause', None)
         error.Abort.__init__(self, *args, **kw)
 
+
 def annotatesubrepoerror(func):
     def decoratedmethod(self, *args, **kargs):
         try:
@@ -77,74 +83,97 @@
             raise ex
         except error.Abort as ex:
             subrepo = subrelpath(self)
-            errormsg = (stringutil.forcebytestr(ex) + ' '
-                        + _('(in subrepository "%s")') % subrepo)
+            errormsg = (
+                stringutil.forcebytestr(ex)
+                + b' '
+                + _(b'(in subrepository "%s")') % subrepo
+            )
             # avoid handling this exception by raising a SubrepoAbort exception
-            raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
-                               cause=sys.exc_info())
+            raise SubrepoAbort(
+                errormsg, hint=ex.hint, subrepo=subrepo, cause=sys.exc_info()
+            )
         return res
+
     return decoratedmethod
 
+
 def _updateprompt(ui, sub, dirty, local, remote):
     if dirty:
-        msg = (_(' subrepository sources for %s differ\n'
-                 'you can use (l)ocal source (%s) or (r)emote source (%s).\n'
-                 'what do you want to do?'
-                 '$$ &Local $$ &Remote')
-               % (subrelpath(sub), local, remote))
+        msg = _(
+            b' subrepository sources for %s differ\n'
+            b'you can use (l)ocal source (%s) or (r)emote source (%s).\n'
+            b'what do you want to do?'
+            b'$$ &Local $$ &Remote'
+        ) % (subrelpath(sub), local, remote)
     else:
-        msg = (_(' subrepository sources for %s differ (in checked out '
-                 'version)\n'
-                 'you can use (l)ocal source (%s) or (r)emote source (%s).\n'
-                 'what do you want to do?'
-                 '$$ &Local $$ &Remote')
-               % (subrelpath(sub), local, remote))
+        msg = _(
+            b' subrepository sources for %s differ (in checked out '
+            b'version)\n'
+            b'you can use (l)ocal source (%s) or (r)emote source (%s).\n'
+            b'what do you want to do?'
+            b'$$ &Local $$ &Remote'
+        ) % (subrelpath(sub), local, remote)
     return ui.promptchoice(msg, 0)
 
+
 def _sanitize(ui, vfs, ignore):
     for dirname, dirs, names in vfs.walk():
         for i, d in enumerate(dirs):
             if d.lower() == ignore:
                 del dirs[i]
                 break
-        if vfs.basename(dirname).lower() != '.hg':
+        if vfs.basename(dirname).lower() != b'.hg':
             continue
         for f in names:
-            if f.lower() == 'hgrc':
-                ui.warn(_("warning: removing potentially hostile 'hgrc' "
-                          "in '%s'\n") % vfs.join(dirname))
+            if f.lower() == b'hgrc':
+                ui.warn(
+                    _(
+                        b"warning: removing potentially hostile 'hgrc' "
+                        b"in '%s'\n"
+                    )
+                    % vfs.join(dirname)
+                )
                 vfs.unlink(vfs.reljoin(dirname, f))
 
+
 def _auditsubrepopath(repo, path):
     # sanity check for potentially unsafe paths such as '~' and '$FOO'
-    if path.startswith('~') or '$' in path or util.expandpath(path) != path:
-        raise error.Abort(_('subrepo path contains illegal component: %s')
-                          % path)
+    if path.startswith(b'~') or b'$' in path or util.expandpath(path) != path:
+        raise error.Abort(
+            _(b'subrepo path contains illegal component: %s') % path
+        )
     # auditor doesn't check if the path itself is a symlink
     pathutil.pathauditor(repo.root)(path)
     if repo.wvfs.islink(path):
-        raise error.Abort(_("subrepo '%s' traverses symbolic link") % path)
+        raise error.Abort(_(b"subrepo '%s' traverses symbolic link") % path)
+
 
 SUBREPO_ALLOWED_DEFAULTS = {
-    'hg': True,
-    'git': False,
-    'svn': False,
+    b'hg': True,
+    b'git': False,
+    b'svn': False,
 }
 
+
 def _checktype(ui, kind):
     # subrepos.allowed is a master kill switch. If disabled, subrepos are
     # disabled period.
-    if not ui.configbool('subrepos', 'allowed', True):
-        raise error.Abort(_('subrepos not enabled'),
-                          hint=_("see 'hg help config.subrepos' for details"))
+    if not ui.configbool(b'subrepos', b'allowed', True):
+        raise error.Abort(
+            _(b'subrepos not enabled'),
+            hint=_(b"see 'hg help config.subrepos' for details"),
+        )
 
     default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False)
-    if not ui.configbool('subrepos', '%s:allowed' % kind, default):
-        raise error.Abort(_('%s subrepos not allowed') % kind,
-                          hint=_("see 'hg help config.subrepos' for details"))
+    if not ui.configbool(b'subrepos', b'%s:allowed' % kind, default):
+        raise error.Abort(
+            _(b'%s subrepos not allowed') % kind,
+            hint=_(b"see 'hg help config.subrepos' for details"),
+        )
 
     if kind not in types:
-        raise error.Abort(_('unknown subrepo type %s') % kind)
+        raise error.Abort(_(b'unknown subrepo type %s') % kind)
+
 
 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
     """return instance of the right subrepo class for subrepo in path"""
@@ -154,6 +183,7 @@
     # scripts that don't use our demand-loading
     global hg
     from . import hg as h
+
     hg = h
 
     repo = ctx.repo()
@@ -164,6 +194,7 @@
         state = (state[0], ctx.subrev(path), state[2])
     return types[state[2]](ctx, path, state[:2], allowcreate)
 
+
 def nullsubrepo(ctx, path, pctx):
     """return an empty subrepo in pctx for the extant subrepo in ctx"""
     # subrepo inherently violates our import layering rules
@@ -172,21 +203,23 @@
     # scripts that don't use our demand-loading
     global hg
     from . import hg as h
+
     hg = h
 
     repo = ctx.repo()
     _auditsubrepopath(repo, path)
     state = ctx.substate[path]
     _checktype(repo.ui, state[2])
-    subrev = ''
-    if state[2] == 'hg':
-        subrev = "0" * 40
+    subrev = b''
+    if state[2] == b'hg':
+        subrev = b"0" * 40
     return types[state[2]](pctx, path, (state[0], subrev), True)
 
+
 # subrepo classes need to implement the following abstract class:
 
+
 class abstractsubrepo(object):
-
     def __init__(self, ctx, path):
         """Initialize abstractsubrepo part
 
@@ -233,14 +266,14 @@
         This returns None, otherwise.
         """
         if self.dirty(ignoreupdate=ignoreupdate, missing=missing):
-            return _('uncommitted changes in subrepository "%s"'
-                     ) % subrelpath(self)
+            return _(b'uncommitted changes in subrepository "%s"') % subrelpath(
+                self
+            )
 
     def bailifchanged(self, ignoreupdate=False, hint=None):
         """raise Abort if subrepository is ``dirty()``
         """
-        dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate,
-                                       missing=True)
+        dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate, missing=True)
         if dirtyreason:
             raise error.Abort(dirtyreason, hint=hint)
 
@@ -293,7 +326,7 @@
         return []
 
     def addremove(self, matcher, prefix, uipathfn, opts):
-        self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
+        self.ui.warn(b"%s: %s" % (prefix, _(b"addremove is not supported")))
         return 1
 
     def cat(self, match, fm, fntemplate, prefix, **opts):
@@ -321,7 +354,7 @@
 
     def fileflags(self, name):
         """return file flags"""
-        return ''
+        return b''
 
     def matchfileset(self, expr, badfn=None):
         """Resolve the fileset expression for this repo"""
@@ -338,15 +371,17 @@
             files = self.files()
         total = len(files)
         relpath = subrelpath(self)
-        progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
-                                        unit=_('files'), total=total)
+        progress = self.ui.makeprogress(
+            _(b'archiving (%s)') % relpath, unit=_(b'files'), total=total
+        )
         progress.update(0)
         for name in files:
             flags = self.fileflags(name)
-            mode = 'x' in flags and 0o755 or 0o644
-            symlink = 'l' in flags
-            archiver.addfile(prefix + name, mode, symlink,
-                             self.filedata(name, decode))
+            mode = b'x' in flags and 0o755 or 0o644
+            symlink = b'l' in flags
+            archiver.addfile(
+                prefix + name, mode, symlink, self.filedata(name, decode)
+            )
             progress.increment()
         progress.complete()
         return total
@@ -360,19 +395,31 @@
     def forget(self, match, prefix, uipathfn, dryrun, interactive):
         return ([], [])
 
-    def removefiles(self, matcher, prefix, uipathfn, after, force, subrepos,
-                    dryrun, warnings):
+    def removefiles(
+        self,
+        matcher,
+        prefix,
+        uipathfn,
+        after,
+        force,
+        subrepos,
+        dryrun,
+        warnings,
+    ):
         """remove the matched files from the subrepository and the filesystem,
         possibly by force and/or after the file has been removed from the
         filesystem.  Return 0 on success, 1 on any warning.
         """
-        warnings.append(_("warning: removefiles not implemented (%s)")
-                        % self._path)
+        warnings.append(
+            _(b"warning: removefiles not implemented (%s)") % self._path
+        )
         return 1
 
     def revert(self, substate, *pats, **opts):
-        self.ui.warn(_('%s: reverting %s subrepos is unsupported\n')
-                     % (substate[0], substate[2]))
+        self.ui.warn(
+            _(b'%s: reverting %s subrepos is unsupported\n')
+            % (substate[0], substate[2])
+        )
         return []
 
     def shortid(self, revid):
@@ -401,35 +448,38 @@
         """
         return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
 
+
 class hgsubrepo(abstractsubrepo):
     def __init__(self, ctx, path, state, allowcreate):
         super(hgsubrepo, self).__init__(ctx, path)
         self._state = state
         r = ctx.repo()
         root = r.wjoin(util.localpath(path))
-        create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
+        create = allowcreate and not r.wvfs.exists(b'%s/.hg' % path)
         # repository constructor does expand variables in path, which is
         # unsafe since subrepo path might come from untrusted source.
         if os.path.realpath(util.expandpath(root)) != root:
-            raise error.Abort(_('subrepo path contains illegal component: %s')
-                              % path)
+            raise error.Abort(
+                _(b'subrepo path contains illegal component: %s') % path
+            )
         self._repo = hg.repository(r.baseui, root, create=create)
         if self._repo.root != root:
-            raise error.ProgrammingError('failed to reject unsafe subrepo '
-                                         'path: %s (expanded to %s)'
-                                         % (root, self._repo.root))
+            raise error.ProgrammingError(
+                b'failed to reject unsafe subrepo '
+                b'path: %s (expanded to %s)' % (root, self._repo.root)
+            )
 
         # Propagate the parent's --hidden option
         if r is r.unfiltered():
             self._repo = self._repo.unfiltered()
 
         self.ui = self._repo.ui
-        for s, k in [('ui', 'commitsubrepos')]:
+        for s, k in [(b'ui', b'commitsubrepos')]:
             v = r.ui.config(s, k)
             if v:
-                self.ui.setconfig(s, k, v, 'subrepo')
+                self.ui.setconfig(s, k, v, b'subrepo')
         # internal config: ui._usedassubrepo
-        self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
+        self.ui.setconfig(b'ui', b'_usedassubrepo', b'True', b'subrepo')
         self._initrepo(r, state[0], create)
 
     @annotatesubrepoerror
@@ -459,21 +509,21 @@
         This method is used to to detect when there are changes that may
         require a push to a given remote path.'''
         # sort the files that will be hashed in increasing (likely) file size
-        filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
-        yield '# %s\n' % _expandedabspath(remotepath)
+        filelist = (b'bookmarks', b'store/phaseroots', b'store/00changelog.i')
+        yield b'# %s\n' % _expandedabspath(remotepath)
         vfs = self._repo.vfs
         for relname in filelist:
             filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest())
-            yield '%s = %s\n' % (relname, filehash)
+            yield b'%s = %s\n' % (relname, filehash)
 
     @propertycache
     def _cachestorehashvfs(self):
-        return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
+        return vfsmod.vfs(self._repo.vfs.join(b'cache/storehash'))
 
     def _readstorehashcache(self, remotepath):
         '''read the store hash cache for a given remote repository'''
         cachefile = _getstorehashcachename(remotepath)
-        return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
+        return self._cachestorehashvfs.tryreadlines(cachefile, b'r')
 
     def _cachestorehash(self, remotepath):
         '''cache the current store hash
@@ -485,7 +535,7 @@
         with self._repo.lock():
             storehash = list(self._calcstorehash(remotepath))
             vfs = self._cachestorehashvfs
-            vfs.writelines(cachefile, storehash, mode='wb', notindexed=True)
+            vfs.writelines(cachefile, storehash, mode=b'wb', notindexed=True)
 
     def _getctx(self):
         '''fetch the context for this subrepo revision, possibly a workingctx
@@ -502,25 +552,26 @@
         self._repo._subsource = source
 
         if create:
-            lines = ['[paths]\n']
+            lines = [b'[paths]\n']
 
             def addpathconfig(key, value):
                 if value:
-                    lines.append('%s = %s\n' % (key, value))
-                    self.ui.setconfig('paths', key, value, 'subrepo')
+                    lines.append(b'%s = %s\n' % (key, value))
+                    self.ui.setconfig(b'paths', key, value, b'subrepo')
 
             defpath = _abssource(self._repo, abort=False)
             defpushpath = _abssource(self._repo, True, abort=False)
-            addpathconfig('default', defpath)
+            addpathconfig(b'default', defpath)
             if defpath != defpushpath:
-                addpathconfig('default-push', defpushpath)
+                addpathconfig(b'default-push', defpushpath)
 
-            self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines)))
+            self._repo.vfs.write(b'hgrc', util.tonativeeol(b''.join(lines)))
 
     @annotatesubrepoerror
     def add(self, ui, match, prefix, uipathfn, explicitonly, **opts):
-        return cmdutil.add(ui, self._repo, match, prefix, uipathfn,
-                           explicitonly, **opts)
+        return cmdutil.add(
+            ui, self._repo, match, prefix, uipathfn, explicitonly, **opts
+        )
 
     @annotatesubrepoerror
     def addremove(self, m, prefix, uipathfn, opts):
@@ -528,15 +579,16 @@
         # always entry any of its subrepos.  Don't corrupt the options that will
         # be used to process sibling subrepos however.
         opts = copy.copy(opts)
-        opts['subrepos'] = True
+        opts[b'subrepos'] = True
         return scmutil.addremove(self._repo, m, prefix, uipathfn, opts)
 
     @annotatesubrepoerror
     def cat(self, match, fm, fntemplate, prefix, **opts):
         rev = self._state[1]
         ctx = self._repo[rev]
-        return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate,
-                           prefix, **opts)
+        return cmdutil.cat(
+            self.ui, self._repo, ctx, match, fm, fntemplate, prefix, **opts
+        )
 
     @annotatesubrepoerror
     def status(self, rev2, **opts):
@@ -546,8 +598,10 @@
             ctx2 = self._repo[rev2]
             return self._repo.status(ctx1, ctx2, **opts)
         except error.RepoLookupError as inst:
-            self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
-                         % (inst, subrelpath(self)))
+            self.ui.warn(
+                _(b'warning: error "%s" in subrepository "%s"\n')
+                % (inst, subrelpath(self))
+            )
             return scmutil.status([], [], [], [], [], [], [])
 
     @annotatesubrepoerror
@@ -558,45 +612,55 @@
             # in hex format
             if node2 is not None:
                 node2 = node.bin(node2)
-            logcmdutil.diffordiffstat(ui, self._repo, diffopts, node1, node2,
-                                      match, prefix=prefix, listsubrepos=True,
-                                      **opts)
+            logcmdutil.diffordiffstat(
+                ui,
+                self._repo,
+                diffopts,
+                node1,
+                node2,
+                match,
+                prefix=prefix,
+                listsubrepos=True,
+                **opts
+            )
         except error.RepoLookupError as inst:
-            self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
-                          % (inst, subrelpath(self)))
+            self.ui.warn(
+                _(b'warning: error "%s" in subrepository "%s"\n')
+                % (inst, subrelpath(self))
+            )
 
     @annotatesubrepoerror
     def archive(self, archiver, prefix, match=None, decode=True):
-        self._get(self._state + ('hg',))
+        self._get(self._state + (b'hg',))
         files = self.files()
         if match:
             files = [f for f in files if match(f)]
         rev = self._state[1]
         ctx = self._repo[rev]
-        scmutil.prefetchfiles(self._repo, [ctx.rev()],
-                              scmutil.matchfiles(self._repo, files))
+        scmutil.prefetchfiles(
+            self._repo, [ctx.rev()], scmutil.matchfiles(self._repo, files)
+        )
         total = abstractsubrepo.archive(self, archiver, prefix, match)
         for subpath in ctx.substate:
             s = subrepo(ctx, subpath, True)
             submatch = matchmod.subdirmatcher(subpath, match)
-            subprefix = prefix + subpath + '/'
-            total += s.archive(archiver, subprefix, submatch,
-                               decode)
+            subprefix = prefix + subpath + b'/'
+            total += s.archive(archiver, subprefix, submatch, decode)
         return total
 
     @annotatesubrepoerror
     def dirty(self, ignoreupdate=False, missing=False):
         r = self._state[1]
-        if r == '' and not ignoreupdate: # no state recorded
+        if r == b'' and not ignoreupdate:  # no state recorded
             return True
         w = self._repo[None]
         if r != w.p1().hex() and not ignoreupdate:
             # different version checked out
             return True
-        return w.dirty(missing=missing) # working directory changed
+        return w.dirty(missing=missing)  # working directory changed
 
     def basestate(self):
-        return self._repo['.'].hex()
+        return self._repo[b'.'].hex()
 
     def checknested(self, path):
         return self._repo._checknested(self._repo.wjoin(path))
@@ -606,22 +670,22 @@
         # don't bother committing in the subrepo if it's only been
         # updated
         if not self.dirty(True):
-            return self._repo['.'].hex()
-        self.ui.debug("committing subrepo %s\n" % subrelpath(self))
+            return self._repo[b'.'].hex()
+        self.ui.debug(b"committing subrepo %s\n" % subrelpath(self))
         n = self._repo.commit(text, user, date)
         if not n:
-            return self._repo['.'].hex() # different version checked out
+            return self._repo[b'.'].hex()  # different version checked out
         return node.hex(n)
 
     @annotatesubrepoerror
     def phase(self, state):
-        return self._repo[state or '.'].phase()
+        return self._repo[state or b'.'].phase()
 
     @annotatesubrepoerror
     def remove(self):
         # we can't fully delete the repository as it may contain
         # local-only history
-        self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
+        self.ui.note(_(b'removing subrepo %s\n') % subrelpath(self))
         hg.clean(self._repo, node.nullid, False)
 
     def _get(self, state):
@@ -649,38 +713,53 @@
             # A simpler option is for the user to configure clone pooling, and
             # work with that.
             if parentrepo.shared() and hg.islocal(srcurl):
-                self.ui.status(_('sharing subrepo %s from %s\n')
-                               % (subrelpath(self), srcurl))
-                shared = hg.share(self._repo._subparent.baseui,
-                                  getpeer(), self._repo.root,
-                                  update=False, bookmarks=False)
+                self.ui.status(
+                    _(b'sharing subrepo %s from %s\n')
+                    % (subrelpath(self), srcurl)
+                )
+                shared = hg.share(
+                    self._repo._subparent.baseui,
+                    getpeer(),
+                    self._repo.root,
+                    update=False,
+                    bookmarks=False,
+                )
                 self._repo = shared.local()
             else:
                 # TODO: find a common place for this and this code in the
                 # share.py wrap of the clone command.
                 if parentrepo.shared():
-                    pool = self.ui.config('share', 'pool')
+                    pool = self.ui.config(b'share', b'pool')
                     if pool:
                         pool = util.expandpath(pool)
 
                     shareopts = {
-                        'pool': pool,
-                        'mode': self.ui.config('share', 'poolnaming'),
+                        b'pool': pool,
+                        b'mode': self.ui.config(b'share', b'poolnaming'),
                     }
                 else:
                     shareopts = {}
 
-                self.ui.status(_('cloning subrepo %s from %s\n')
-                               % (subrelpath(self), util.hidepassword(srcurl)))
-                other, cloned = hg.clone(self._repo._subparent.baseui, {},
-                                         getpeer(), self._repo.root,
-                                         update=False, shareopts=shareopts)
+                self.ui.status(
+                    _(b'cloning subrepo %s from %s\n')
+                    % (subrelpath(self), util.hidepassword(srcurl))
+                )
+                other, cloned = hg.clone(
+                    self._repo._subparent.baseui,
+                    {},
+                    getpeer(),
+                    self._repo.root,
+                    update=False,
+                    shareopts=shareopts,
+                )
                 self._repo = cloned.local()
             self._initrepo(parentrepo, source, create=True)
             self._cachestorehash(srcurl)
         else:
-            self.ui.status(_('pulling subrepo %s from %s\n')
-                           % (subrelpath(self), util.hidepassword(srcurl)))
+            self.ui.status(
+                _(b'pulling subrepo %s from %s\n')
+                % (subrelpath(self), util.hidepassword(srcurl))
+            )
             cleansub = self.storeclean(srcurl)
             exchange.pull(self._repo, getpeer())
             if cleansub:
@@ -693,34 +772,39 @@
         inrepo = self._get(state)
         source, revision, kind = state
         repo = self._repo
-        repo.ui.debug("getting subrepo %s\n" % self._path)
+        repo.ui.debug(b"getting subrepo %s\n" % self._path)
         if inrepo:
             urepo = repo.unfiltered()
             ctx = urepo[revision]
             if ctx.hidden():
                 urepo.ui.warn(
-                    _('revision %s in subrepository "%s" is hidden\n')
-                    % (revision[0:12], self._path))
+                    _(b'revision %s in subrepository "%s" is hidden\n')
+                    % (revision[0:12], self._path)
+                )
                 repo = urepo
         hg.updaterepo(repo, revision, overwrite)
 
     @annotatesubrepoerror
     def merge(self, state):
         self._get(state)
-        cur = self._repo['.']
+        cur = self._repo[b'.']
         dst = self._repo[state[1]]
         anc = dst.ancestor(cur)
 
         def mergefunc():
             if anc == cur and dst.branch() == cur.branch():
-                self.ui.debug('updating subrepository "%s"\n'
-                              % subrelpath(self))
+                self.ui.debug(
+                    b'updating subrepository "%s"\n' % subrelpath(self)
+                )
                 hg.update(self._repo, state[1])
             elif anc == dst:
-                self.ui.debug('skipping subrepository "%s"\n'
-                              % subrelpath(self))
+                self.ui.debug(
+                    b'skipping subrepository "%s"\n' % subrelpath(self)
+                )
             else:
-                self.ui.debug('merging subrepository "%s"\n' % subrelpath(self))
+                self.ui.debug(
+                    b'merging subrepository "%s"\n' % subrelpath(self)
+                )
                 hg.merge(self._repo, state[1], remind=False)
 
         wctx = self._repo[None]
@@ -735,13 +819,13 @@
 
     @annotatesubrepoerror
     def push(self, opts):
-        force = opts.get('force')
-        newbranch = opts.get('new_branch')
-        ssh = opts.get('ssh')
+        force = opts.get(b'force')
+        newbranch = opts.get(b'new_branch')
+        ssh = opts.get(b'ssh')
 
         # push subrepos depth-first for coherent ordering
-        c = self._repo['.']
-        subs = c.substate # only repos that are committed
+        c = self._repo[b'.']
+        subs = c.substate  # only repos that are committed
         for s in sorted(subs):
             if c.sub(s).push(opts) == 0:
                 return False
@@ -750,12 +834,15 @@
         if not force:
             if self.storeclean(dsturl):
                 self.ui.status(
-                    _('no changes made to subrepo %s since last push to %s\n')
-                    % (subrelpath(self), util.hidepassword(dsturl)))
+                    _(b'no changes made to subrepo %s since last push to %s\n')
+                    % (subrelpath(self), util.hidepassword(dsturl))
+                )
                 return None
-        self.ui.status(_('pushing subrepo %s to %s\n') %
-            (subrelpath(self), util.hidepassword(dsturl)))
-        other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
+        self.ui.status(
+            _(b'pushing subrepo %s to %s\n')
+            % (subrelpath(self), util.hidepassword(dsturl))
+        )
+        other = hg.peer(self._repo, {b'ssh': ssh}, dsturl)
         res = exchange.push(self._repo, other, force, newbranch=newbranch)
 
         # the repo is now clean
@@ -764,18 +851,18 @@
 
     @annotatesubrepoerror
     def outgoing(self, ui, dest, opts):
-        if 'rev' in opts or 'branch' in opts:
+        if b'rev' in opts or b'branch' in opts:
             opts = copy.copy(opts)
-            opts.pop('rev', None)
-            opts.pop('branch', None)
+            opts.pop(b'rev', None)
+            opts.pop(b'branch', None)
         return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
 
     @annotatesubrepoerror
     def incoming(self, ui, source, opts):
-        if 'rev' in opts or 'branch' in opts:
+        if b'rev' in opts or b'branch' in opts:
             opts = copy.copy(opts)
-            opts.pop('rev', None)
-            opts.pop('branch', None)
+            opts.pop(b'rev', None)
+            opts.pop(b'branch', None)
         return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
 
     @annotatesubrepoerror
@@ -825,8 +912,10 @@
                 pm = matchmod.prefixdirmatcher(subpath, sm, badfn=badfn)
                 matchers.append(pm)
             except error.LookupError:
-                self.ui.status(_("skipping missing subrepository: %s\n")
-                               % self.wvfs.reljoin(reporelpath(self), subpath))
+                self.ui.status(
+                    _(b"skipping missing subrepository: %s\n")
+                    % self.wvfs.reljoin(reporelpath(self), subpath)
+                )
         if len(matchers) == 1:
             return matchers[0]
         return matchmod.unionmatcher(matchers)
@@ -837,14 +926,40 @@
 
     @annotatesubrepoerror
     def forget(self, match, prefix, uipathfn, dryrun, interactive):
-        return cmdutil.forget(self.ui, self._repo, match, prefix, uipathfn,
-                              True, dryrun=dryrun, interactive=interactive)
+        return cmdutil.forget(
+            self.ui,
+            self._repo,
+            match,
+            prefix,
+            uipathfn,
+            True,
+            dryrun=dryrun,
+            interactive=interactive,
+        )
 
     @annotatesubrepoerror
-    def removefiles(self, matcher, prefix, uipathfn, after, force, subrepos,
-                    dryrun, warnings):
-        return cmdutil.remove(self.ui, self._repo, matcher, prefix, uipathfn,
-                              after, force, subrepos, dryrun)
+    def removefiles(
+        self,
+        matcher,
+        prefix,
+        uipathfn,
+        after,
+        force,
+        subrepos,
+        dryrun,
+        warnings,
+    ):
+        return cmdutil.remove(
+            self.ui,
+            self._repo,
+            matcher,
+            prefix,
+            uipathfn,
+            after,
+            force,
+            subrepos,
+            dryrun,
+        )
 
     @annotatesubrepoerror
     def revert(self, substate, *pats, **opts):
@@ -853,7 +968,7 @@
         #    files inside the subrepo
         # 2. update the subrepo to the revision specified in
         #    the corresponding substate dictionary
-        self.ui.status(_('reverting subrepo %s\n') % substate[0])
+        self.ui.status(_(b'reverting subrepo %s\n') % substate[0])
         if not opts.get(r'no_backup'):
             # Revert all files on the subrepo, creating backups
             # Note that this will not recursively revert subrepos
@@ -872,7 +987,7 @@
         ctx = self._repo[opts[r'rev']]
         parents = self._repo.dirstate.parents()
         if opts.get(r'all'):
-            pats = ['set:modified()']
+            pats = [b'set:modified()']
         else:
             pats = []
         cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
@@ -888,13 +1003,14 @@
         # scripts that don't use our demand-loading
         global hg
         from . import hg as h
+
         hg = h
 
         # Nothing prevents a user from sharing in a repo, and then making that a
         # subrepo.  Alternately, the previous unshare attempt may have failed
         # part way through.  So recurse whether or not this layer is shared.
         if self._repo.shared():
-            self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath)
+            self.ui.status(_(b"unsharing subrepo '%s'\n") % self._relpath)
 
         hg.unshare(self.ui, self._repo)
 
@@ -906,14 +1022,18 @@
                 # Since hidden revisions aren't pushed/pulled, it seems worth an
                 # explicit warning.
                 ui = self._repo.ui
-                ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
-                        (self._relpath, node.short(self._ctx.node())))
+                ui.warn(
+                    _(b"subrepo '%s' is hidden in revision %s\n")
+                    % (self._relpath, node.short(self._ctx.node()))
+                )
             return 0
         except error.RepoLookupError:
             # A missing subrepo revision may be a case of needing to pull it, so
             # don't treat this as an error.
-            self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
-                               (self._relpath, node.short(self._ctx.node())))
+            self._repo.ui.warn(
+                _(b"subrepo '%s' not found in revision %s\n")
+                % (self._relpath, node.short(self._ctx.node()))
+            )
             return 0
 
     @propertycache
@@ -929,16 +1049,18 @@
         # Keep consistent dir separators by avoiding vfs.join(self._path)
         return reporelpath(self._repo)
 
+
 class svnsubrepo(abstractsubrepo):
     def __init__(self, ctx, path, state, allowcreate):
         super(svnsubrepo, self).__init__(ctx, path)
         self._state = state
-        self._exe = procutil.findexe('svn')
+        self._exe = procutil.findexe(b'svn')
         if not self._exe:
-            raise error.Abort(_("'svn' executable not found for subrepo '%s'")
-                             % self._path)
+            raise error.Abort(
+                _(b"'svn' executable not found for subrepo '%s'") % self._path
+            )
 
-    def _svncommand(self, commands, filename='', failok=False):
+    def _svncommand(self, commands, filename=b'', failok=False):
         cmd = [self._exe]
         extrakw = {}
         if not self.ui.interactive():
@@ -949,59 +1071,70 @@
             # instead of being per-command, but we need to support 1.4 so
             # we have to be intelligent about what commands take
             # --non-interactive.
-            if commands[0] in ('update', 'checkout', 'commit'):
-                cmd.append('--non-interactive')
+            if commands[0] in (b'update', b'checkout', b'commit'):
+                cmd.append(b'--non-interactive')
         cmd.extend(commands)
         if filename is not None:
-            path = self.wvfs.reljoin(self._ctx.repo().origroot,
-                                     self._path, filename)
+            path = self.wvfs.reljoin(
+                self._ctx.repo().origroot, self._path, filename
+            )
             cmd.append(path)
         env = dict(encoding.environ)
         # Avoid localized output, preserve current locale for everything else.
-        lc_all = env.get('LC_ALL')
+        lc_all = env.get(b'LC_ALL')
         if lc_all:
-            env['LANG'] = lc_all
-            del env['LC_ALL']
-        env['LC_MESSAGES'] = 'C'
-        p = subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmd),
-                             bufsize=-1, close_fds=procutil.closefds,
-                             stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                             env=procutil.tonativeenv(env), **extrakw)
+            env[b'LANG'] = lc_all
+            del env[b'LC_ALL']
+        env[b'LC_MESSAGES'] = b'C'
+        p = subprocess.Popen(
+            pycompat.rapply(procutil.tonativestr, cmd),
+            bufsize=-1,
+            close_fds=procutil.closefds,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            env=procutil.tonativeenv(env),
+            **extrakw
+        )
         stdout, stderr = map(util.fromnativeeol, p.communicate())
         stderr = stderr.strip()
         if not failok:
             if p.returncode:
-                raise error.Abort(stderr or 'exited with code %d'
-                                  % p.returncode)
+                raise error.Abort(
+                    stderr or b'exited with code %d' % p.returncode
+                )
             if stderr:
-                self.ui.warn(stderr + '\n')
+                self.ui.warn(stderr + b'\n')
         return stdout, stderr
 
     @propertycache
     def _svnversion(self):
-        output, err = self._svncommand(['--version', '--quiet'], filename=None)
+        output, err = self._svncommand(
+            [b'--version', b'--quiet'], filename=None
+        )
         m = re.search(br'^(\d+)\.(\d+)', output)
         if not m:
-            raise error.Abort(_('cannot retrieve svn tool version'))
+            raise error.Abort(_(b'cannot retrieve svn tool version'))
         return (int(m.group(1)), int(m.group(2)))
 
     def _svnmissing(self):
-        return not self.wvfs.exists('.svn')
+        return not self.wvfs.exists(b'.svn')
 
     def _wcrevs(self):
         # Get the working directory revision as well as the last
         # commit revision so we can compare the subrepo state with
         # both. We used to store the working directory one.
-        output, err = self._svncommand(['info', '--xml'])
+        output, err = self._svncommand([b'info', b'--xml'])
         doc = xml.dom.minidom.parseString(output)
         entries = doc.getElementsByTagName(r'entry')
-        lastrev, rev = '0', '0'
+        lastrev, rev = b'0', b'0'
         if entries:
-            rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or '0'
+            rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or b'0'
             commits = entries[0].getElementsByTagName(r'commit')
             if commits:
-                lastrev = pycompat.bytestr(
-                    commits[0].getAttribute(r'revision')) or '0'
+                lastrev = (
+                    pycompat.bytestr(commits[0].getAttribute(r'revision'))
+                    or b'0'
+                )
         return (lastrev, rev)
 
     def _wcrev(self):
@@ -1013,7 +1146,7 @@
         True if any of these changes concern an external entry and missing
         is True if any change is a missing entry.
         """
-        output, err = self._svncommand(['status', '--xml'])
+        output, err = self._svncommand([b'status', b'--xml'])
         externals, changes, missing = [], [], []
         doc = xml.dom.minidom.parseString(output)
         for e in doc.getElementsByTagName(r'entry'):
@@ -1027,8 +1160,12 @@
                 externals.append(path)
             elif item == r'missing':
                 missing.append(path)
-            if (item not in (r'', r'normal', r'unversioned', r'external')
-                or props not in (r'', r'none', r'normal')):
+            if item not in (
+                r'',
+                r'normal',
+                r'unversioned',
+                r'external',
+            ) or props not in (r'', r'none', r'normal'):
                 changes.append(path)
         for path in changes:
             for ext in externals:
@@ -1039,7 +1176,7 @@
     @annotatesubrepoerror
     def dirty(self, ignoreupdate=False, missing=False):
         if self._svnmissing():
-            return self._state[1] != ''
+            return self._state[1] != b''
         wcchanged = self._wcchanged()
         changed = wcchanged[0] or (missing and wcchanged[2])
         if not changed:
@@ -1055,7 +1192,9 @@
             # URL exists at lastrev.  Test it and fallback to rev it
             # is not there.
             try:
-                self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
+                self._svncommand(
+                    [b'list', b'%s@%s' % (self._state[0], lastrev)]
+                )
                 return lastrev
             except error.Abort:
                 pass
@@ -1069,33 +1208,35 @@
             return self.basestate()
         if extchanged:
             # Do not try to commit externals
-            raise error.Abort(_('cannot commit svn externals'))
+            raise error.Abort(_(b'cannot commit svn externals'))
         if missing:
             # svn can commit with missing entries but aborting like hg
             # seems a better approach.
-            raise error.Abort(_('cannot commit missing svn entries'))
-        commitinfo, err = self._svncommand(['commit', '-m', text])
+            raise error.Abort(_(b'cannot commit missing svn entries'))
+        commitinfo, err = self._svncommand([b'commit', b'-m', text])
         self.ui.status(commitinfo)
-        newrev = re.search('Committed revision ([0-9]+).', commitinfo)
+        newrev = re.search(b'Committed revision ([0-9]+).', commitinfo)
         if not newrev:
             if not commitinfo.strip():
                 # Sometimes, our definition of "changed" differs from
                 # svn one. For instance, svn ignores missing files
                 # when committing. If there are only missing files, no
                 # commit is made, no output and no error code.
-                raise error.Abort(_('failed to commit svn changes'))
+                raise error.Abort(_(b'failed to commit svn changes'))
             raise error.Abort(commitinfo.splitlines()[-1])
         newrev = newrev.groups()[0]
-        self.ui.status(self._svncommand(['update', '-r', newrev])[0])
+        self.ui.status(self._svncommand([b'update', b'-r', newrev])[0])
         return newrev
 
     @annotatesubrepoerror
     def remove(self):
         if self.dirty():
-            self.ui.warn(_('not removing repo %s because '
-                           'it has changes.\n') % self._path)
+            self.ui.warn(
+                _(b'not removing repo %s because it has changes.\n')
+                % self._path
+            )
             return
-        self.ui.note(_('removing subrepo %s\n') % self._path)
+        self.ui.note(_(b'removing subrepo %s\n') % self._path)
 
         self.wvfs.rmtree(forcibly=True)
         try:
@@ -1107,22 +1248,23 @@
     @annotatesubrepoerror
     def get(self, state, overwrite=False):
         if overwrite:
-            self._svncommand(['revert', '--recursive'])
-        args = ['checkout']
+            self._svncommand([b'revert', b'--recursive'])
+        args = [b'checkout']
         if self._svnversion >= (1, 5):
-            args.append('--force')
+            args.append(b'--force')
         # The revision must be specified at the end of the URL to properly
         # update to a directory which has since been deleted and recreated.
-        args.append('%s@%s' % (state[0], state[1]))
+        args.append(b'%s@%s' % (state[0], state[1]))
 
         # SEC: check that the ssh url is safe
         util.checksafessh(state[0])
 
         status, err = self._svncommand(args, failok=True)
-        _sanitize(self.ui, self.wvfs, '.svn')
-        if not re.search('Checked out revision [0-9]+.', status):
-            if ('is already a working copy for a different URL' in err
-                and (self._wcchanged()[:2] == (False, False))):
+        _sanitize(self.ui, self.wvfs, b'.svn')
+        if not re.search(b'Checked out revision [0-9]+.', status):
+            if b'is already a working copy for a different URL' in err and (
+                self._wcchanged()[:2] == (False, False)
+            ):
                 # obstructed but clean working copy, so just blow it away.
                 self.remove()
                 self.get(state, overwrite=False)
@@ -1146,21 +1288,23 @@
 
     @annotatesubrepoerror
     def files(self):
-        output = self._svncommand(['list', '--recursive', '--xml'])[0]
+        output = self._svncommand([b'list', b'--recursive', b'--xml'])[0]
         doc = xml.dom.minidom.parseString(output)
         paths = []
         for e in doc.getElementsByTagName(r'entry'):
             kind = pycompat.bytestr(e.getAttribute(r'kind'))
-            if kind != 'file':
+            if kind != b'file':
                 continue
-            name = r''.join(c.data for c
-                            in e.getElementsByTagName(r'name')[0].childNodes
-                            if c.nodeType == c.TEXT_NODE)
+            name = r''.join(
+                c.data
+                for c in e.getElementsByTagName(r'name')[0].childNodes
+                if c.nodeType == c.TEXT_NODE
+            )
             paths.append(name.encode('utf8'))
         return paths
 
     def filedata(self, name, decode):
-        return self._svncommand(['cat'], name)[0]
+        return self._svncommand([b'cat'], name)[0]
 
 
 class gitsubrepo(abstractsubrepo):
@@ -1173,36 +1317,48 @@
 
     def _ensuregit(self):
         try:
-            self._gitexecutable = 'git'
-            out, err = self._gitnodir(['--version'])
+            self._gitexecutable = b'git'
+            out, err = self._gitnodir([b'--version'])
         except OSError as e:
-            genericerror = _("error executing git for subrepo '%s': %s")
-            notfoundhint = _("check git is installed and in your PATH")
+            genericerror = _(b"error executing git for subrepo '%s': %s")
+            notfoundhint = _(b"check git is installed and in your PATH")
             if e.errno != errno.ENOENT:
-                raise error.Abort(genericerror % (
-                    self._path, encoding.strtolocal(e.strerror)))
+                raise error.Abort(
+                    genericerror % (self._path, encoding.strtolocal(e.strerror))
+                )
             elif pycompat.iswindows:
                 try:
-                    self._gitexecutable = 'git.cmd'
-                    out, err = self._gitnodir(['--version'])
+                    self._gitexecutable = b'git.cmd'
+                    out, err = self._gitnodir([b'--version'])
                 except OSError as e2:
                     if e2.errno == errno.ENOENT:
-                        raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
-                            " for subrepo '%s'") % self._path,
-                            hint=notfoundhint)
+                        raise error.Abort(
+                            _(
+                                b"couldn't find 'git' or 'git.cmd'"
+                                b" for subrepo '%s'"
+                            )
+                            % self._path,
+                            hint=notfoundhint,
+                        )
                     else:
-                        raise error.Abort(genericerror % (self._path,
-                            encoding.strtolocal(e2.strerror)))
+                        raise error.Abort(
+                            genericerror
+                            % (self._path, encoding.strtolocal(e2.strerror))
+                        )
             else:
-                raise error.Abort(_("couldn't find git for subrepo '%s'")
-                    % self._path, hint=notfoundhint)
+                raise error.Abort(
+                    _(b"couldn't find git for subrepo '%s'") % self._path,
+                    hint=notfoundhint,
+                )
         versionstatus = self._checkversion(out)
-        if versionstatus == 'unknown':
-            self.ui.warn(_('cannot retrieve git version\n'))
-        elif versionstatus == 'abort':
-            raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
-        elif versionstatus == 'warning':
-            self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
+        if versionstatus == b'unknown':
+            self.ui.warn(_(b'cannot retrieve git version\n'))
+        elif versionstatus == b'abort':
+            raise error.Abort(
+                _(b'git subrepo requires at least 1.6.0 or later')
+            )
+        elif versionstatus == b'warning':
+            self.ui.warn(_(b'git subrepo requires at least 1.6.0 or later\n'))
 
     @staticmethod
     def _gitversion(out):
@@ -1245,19 +1401,20 @@
         # despite the docstring comment.  For now, error on 1.4.0, warn on
         # 1.5.0 but attempt to continue.
         if version == -1:
-            return 'unknown'
+            return b'unknown'
         if version < (1, 5, 0):
-            return 'abort'
+            return b'abort'
         elif version < (1, 6, 0):
-            return 'warning'
-        return 'ok'
+            return b'warning'
+        return b'ok'
 
     def _gitcommand(self, commands, env=None, stream=False):
         return self._gitdir(commands, env=env, stream=stream)[0]
 
     def _gitdir(self, commands, env=None, stream=False):
-        return self._gitnodir(commands, env=env, stream=stream,
-                              cwd=self._abspath)
+        return self._gitnodir(
+            commands, env=env, stream=stream, cwd=self._abspath
+        )
 
     def _gitnodir(self, commands, env=None, stream=False, cwd=None):
         """Calls the git command
@@ -1265,30 +1422,34 @@
         The methods tries to call the git command. versions prior to 1.6.0
         are not supported and very probably fail.
         """
-        self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
+        self.ui.debug(b'%s: git %s\n' % (self._relpath, b' '.join(commands)))
         if env is None:
             env = encoding.environ.copy()
         # disable localization for Git output (issue5176)
-        env['LC_ALL'] = 'C'
+        env[b'LC_ALL'] = b'C'
         # fix for Git CVE-2015-7545
-        if 'GIT_ALLOW_PROTOCOL' not in env:
-            env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
+        if b'GIT_ALLOW_PROTOCOL' not in env:
+            env[b'GIT_ALLOW_PROTOCOL'] = b'file:git:http:https:ssh'
         # unless ui.quiet is set, print git's stderr,
         # which is mostly progress and useful info
         errpipe = None
         if self.ui.quiet:
-            errpipe = open(os.devnull, 'w')
-        if self.ui._colormode and len(commands) and commands[0] == "diff":
+            errpipe = open(os.devnull, b'w')
+        if self.ui._colormode and len(commands) and commands[0] == b"diff":
             # insert the argument in the front,
             # the end of git diff arguments is used for paths
-            commands.insert(1, '--color')
-        p = subprocess.Popen(pycompat.rapply(procutil.tonativestr,
-                                             [self._gitexecutable] + commands),
-                             bufsize=-1,
-                             cwd=pycompat.rapply(procutil.tonativestr, cwd),
-                             env=procutil.tonativeenv(env),
-                             close_fds=procutil.closefds,
-                             stdout=subprocess.PIPE, stderr=errpipe)
+            commands.insert(1, b'--color')
+        p = subprocess.Popen(
+            pycompat.rapply(
+                procutil.tonativestr, [self._gitexecutable] + commands
+            ),
+            bufsize=-1,
+            cwd=pycompat.rapply(procutil.tonativestr, cwd),
+            env=procutil.tonativeenv(env),
+            close_fds=procutil.closefds,
+            stdout=subprocess.PIPE,
+            stderr=errpipe,
+        )
         if stream:
             return p.stdout, None
 
@@ -1299,48 +1460,50 @@
         if p.returncode != 0 and p.returncode != 1:
             # there are certain error codes that are ok
             command = commands[0]
-            if command in ('cat-file', 'symbolic-ref'):
+            if command in (b'cat-file', b'symbolic-ref'):
                 return retdata, p.returncode
             # for all others, abort
-            raise error.Abort(_('git %s error %d in %s') %
-                             (command, p.returncode, self._relpath))
+            raise error.Abort(
+                _(b'git %s error %d in %s')
+                % (command, p.returncode, self._relpath)
+            )
 
         return retdata, p.returncode
 
     def _gitmissing(self):
-        return not self.wvfs.exists('.git')
+        return not self.wvfs.exists(b'.git')
 
     def _gitstate(self):
-        return self._gitcommand(['rev-parse', 'HEAD'])
+        return self._gitcommand([b'rev-parse', b'HEAD'])
 
     def _gitcurrentbranch(self):
-        current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
+        current, err = self._gitdir([b'symbolic-ref', b'HEAD', b'--quiet'])
         if err:
             current = None
         return current
 
     def _gitremote(self, remote):
-        out = self._gitcommand(['remote', 'show', '-n', remote])
-        line = out.split('\n')[1]
-        i = line.index('URL: ') + len('URL: ')
+        out = self._gitcommand([b'remote', b'show', b'-n', remote])
+        line = out.split(b'\n')[1]
+        i = line.index(b'URL: ') + len(b'URL: ')
         return line[i:]
 
     def _githavelocally(self, revision):
-        out, code = self._gitdir(['cat-file', '-e', revision])
+        out, code = self._gitdir([b'cat-file', b'-e', revision])
         return code == 0
 
     def _gitisancestor(self, r1, r2):
-        base = self._gitcommand(['merge-base', r1, r2])
+        base = self._gitcommand([b'merge-base', r1, r2])
         return base == r1
 
     def _gitisbare(self):
-        return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
+        return self._gitcommand([b'config', b'--bool', b'core.bare']) == b'true'
 
     def _gitupdatestat(self):
         """This must be run before git diff-index.
         diff-index only looks at changes to file stat;
         this command looks at file contents and updates the stat."""
-        self._gitcommand(['update-index', '-q', '--refresh'])
+        self._gitcommand([b'update-index', b'-q', b'--refresh'])
 
     def _gitbranchmap(self):
         '''returns 2 things:
@@ -1349,39 +1512,42 @@
         branch2rev = {}
         rev2branch = {}
 
-        out = self._gitcommand(['for-each-ref', '--format',
-                                '%(objectname) %(refname)'])
-        for line in out.split('\n'):
-            revision, ref = line.split(' ')
-            if (not ref.startswith('refs/heads/') and
-                not ref.startswith('refs/remotes/')):
+        out = self._gitcommand(
+            [b'for-each-ref', b'--format', b'%(objectname) %(refname)']
+        )
+        for line in out.split(b'\n'):
+            revision, ref = line.split(b' ')
+            if not ref.startswith(b'refs/heads/') and not ref.startswith(
+                b'refs/remotes/'
+            ):
                 continue
-            if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
-                continue # ignore remote/HEAD redirects
+            if ref.startswith(b'refs/remotes/') and ref.endswith(b'/HEAD'):
+                continue  # ignore remote/HEAD redirects
             branch2rev[ref] = revision
             rev2branch.setdefault(revision, []).append(ref)
         return branch2rev, rev2branch
 
     def _gittracking(self, branches):
-        'return map of remote branch to local tracking branch'
+        b'return map of remote branch to local tracking branch'
         # assumes no more than one local tracking branch for each remote
         tracking = {}
         for b in branches:
-            if b.startswith('refs/remotes/'):
+            if b.startswith(b'refs/remotes/'):
                 continue
-            bname = b.split('/', 2)[2]
-            remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
+            bname = b.split(b'/', 2)[2]
+            remote = self._gitcommand([b'config', b'branch.%s.remote' % bname])
             if remote:
-                ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
-                tracking['refs/remotes/%s/%s' %
-                         (remote, ref.split('/', 2)[2])] = b
+                ref = self._gitcommand([b'config', b'branch.%s.merge' % bname])
+                tracking[
+                    b'refs/remotes/%s/%s' % (remote, ref.split(b'/', 2)[2])
+                ] = b
         return tracking
 
     def _abssource(self, source):
-        if '://' not in source:
+        if b'://' not in source:
             # recognize the scp syntax as an absolute source
-            colon = source.find(':')
-            if colon != -1 and '/' not in source[:colon]:
+            colon = source.find(b':')
+            if colon != -1 and b'/' not in source[:colon]:
                 return source
         self._subsource = source
         return _abssource(self)
@@ -1392,23 +1558,28 @@
             util.checksafessh(source)
 
             source = self._abssource(source)
-            self.ui.status(_('cloning subrepo %s from %s\n') %
-                            (self._relpath, source))
-            self._gitnodir(['clone', source, self._abspath])
+            self.ui.status(
+                _(b'cloning subrepo %s from %s\n') % (self._relpath, source)
+            )
+            self._gitnodir([b'clone', source, self._abspath])
         if self._githavelocally(revision):
             return
-        self.ui.status(_('pulling subrepo %s from %s\n') %
-                        (self._relpath, self._gitremote('origin')))
+        self.ui.status(
+            _(b'pulling subrepo %s from %s\n')
+            % (self._relpath, self._gitremote(b'origin'))
+        )
         # try only origin: the originally cloned repo
-        self._gitcommand(['fetch'])
+        self._gitcommand([b'fetch'])
         if not self._githavelocally(revision):
-            raise error.Abort(_('revision %s does not exist in subrepository '
-                                '"%s"\n') % (revision, self._relpath))
+            raise error.Abort(
+                _(b'revision %s does not exist in subrepository "%s"\n')
+                % (revision, self._relpath)
+            )
 
     @annotatesubrepoerror
     def dirty(self, ignoreupdate=False, missing=False):
         if self._gitmissing():
-            return self._state[1] != ''
+            return self._state[1] != b''
         if self._gitisbare():
             return True
         if not ignoreupdate and self._state[1] != self._gitstate():
@@ -1416,7 +1587,7 @@
             return True
         # check for staged changes or modified files; ignore untracked files
         self._gitupdatestat()
-        out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
+        out, code = self._gitdir([b'diff-index', b'--quiet', b'HEAD'])
         return code == 1
 
     def basestate(self):
@@ -1431,38 +1602,41 @@
         self._fetch(source, revision)
         # if the repo was set to be bare, unbare it
         if self._gitisbare():
-            self._gitcommand(['config', 'core.bare', 'false'])
+            self._gitcommand([b'config', b'core.bare', b'false'])
             if self._gitstate() == revision:
-                self._gitcommand(['reset', '--hard', 'HEAD'])
+                self._gitcommand([b'reset', b'--hard', b'HEAD'])
                 return
         elif self._gitstate() == revision:
             if overwrite:
                 # first reset the index to unmark new files for commit, because
                 # reset --hard will otherwise throw away files added for commit,
                 # not just unmark them.
-                self._gitcommand(['reset', 'HEAD'])
-                self._gitcommand(['reset', '--hard', 'HEAD'])
+                self._gitcommand([b'reset', b'HEAD'])
+                self._gitcommand([b'reset', b'--hard', b'HEAD'])
             return
         branch2rev, rev2branch = self._gitbranchmap()
 
         def checkout(args):
-            cmd = ['checkout']
+            cmd = [b'checkout']
             if overwrite:
                 # first reset the index to unmark new files for commit, because
                 # the -f option will otherwise throw away files added for
                 # commit, not just unmark them.
-                self._gitcommand(['reset', 'HEAD'])
-                cmd.append('-f')
+                self._gitcommand([b'reset', b'HEAD'])
+                cmd.append(b'-f')
             self._gitcommand(cmd + args)
-            _sanitize(self.ui, self.wvfs, '.git')
+            _sanitize(self.ui, self.wvfs, b'.git')
 
         def rawcheckout():
             # no branch to checkout, check it out with no branch
-            self.ui.warn(_('checking out detached HEAD in '
-                           'subrepository "%s"\n') % self._relpath)
-            self.ui.warn(_('check out a git branch if you intend '
-                            'to make changes\n'))
-            checkout(['-q', revision])
+            self.ui.warn(
+                _(b'checking out detached HEAD in subrepository "%s"\n')
+                % self._relpath
+            )
+            self.ui.warn(
+                _(b'check out a git branch if you intend to make changes\n')
+            )
+            checkout([b'-q', revision])
 
         if revision not in rev2branch:
             rawcheckout()
@@ -1470,11 +1644,11 @@
         branches = rev2branch[revision]
         firstlocalbranch = None
         for b in branches:
-            if b == 'refs/heads/master':
+            if b == b'refs/heads/master':
                 # master trumps all other branches
-                checkout(['refs/heads/master'])
+                checkout([b'refs/heads/master'])
                 return
-            if not firstlocalbranch and not b.startswith('refs/remotes/'):
+            if not firstlocalbranch and not b.startswith(b'refs/remotes/'):
                 firstlocalbranch = b
         if firstlocalbranch:
             checkout([firstlocalbranch])
@@ -1491,8 +1665,8 @@
 
         if remote not in tracking:
             # create a new local tracking branch
-            local = remote.split('/', 3)[3]
-            checkout(['-b', local, remote])
+            local = remote.split(b'/', 3)[3]
+            checkout([b'-b', local, remote])
         elif self._gitisancestor(branch2rev[tracking[remote]], remote):
             # When updating to a tracked remote branch,
             # if the local tracking branch is downstream of it,
@@ -1502,8 +1676,8 @@
             # detect this situation and perform this action lazily.
             if tracking[remote] != self._gitcurrentbranch():
                 checkout([tracking[remote]])
-            self._gitcommand(['merge', '--ff', remote])
-            _sanitize(self.ui, self.wvfs, '.git')
+            self._gitcommand([b'merge', b'--ff', remote])
+            _sanitize(self.ui, self.wvfs, b'.git')
         else:
             # a real merge would be required, just checkout the revision
             rawcheckout()
@@ -1511,16 +1685,17 @@
     @annotatesubrepoerror
     def commit(self, text, user, date):
         if self._gitmissing():
-            raise error.Abort(_("subrepo %s is missing") % self._relpath)
-        cmd = ['commit', '-a', '-m', text]
+            raise error.Abort(_(b"subrepo %s is missing") % self._relpath)
+        cmd = [b'commit', b'-a', b'-m', text]
         env = encoding.environ.copy()
         if user:
-            cmd += ['--author', user]
+            cmd += [b'--author', user]
         if date:
             # git's date parser silently ignores when seconds < 1e9
             # convert to ISO8601
-            env['GIT_AUTHOR_DATE'] = dateutil.datestr(date,
-                                                  '%Y-%m-%dT%H:%M:%S %1%2')
+            env[b'GIT_AUTHOR_DATE'] = dateutil.datestr(
+                date, b'%Y-%m-%dT%H:%M:%S %1%2'
+            )
         self._gitcommand(cmd, env=env)
         # make sure commit works otherwise HEAD might not exist under certain
         # circumstances
@@ -1530,64 +1705,76 @@
     def merge(self, state):
         source, revision, kind = state
         self._fetch(source, revision)
-        base = self._gitcommand(['merge-base', revision, self._state[1]])
+        base = self._gitcommand([b'merge-base', revision, self._state[1]])
         self._gitupdatestat()
-        out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
+        out, code = self._gitdir([b'diff-index', b'--quiet', b'HEAD'])
 
         def mergefunc():
             if base == revision:
-                self.get(state) # fast forward merge
+                self.get(state)  # fast forward merge
             elif base != self._state[1]:
-                self._gitcommand(['merge', '--no-commit', revision])
-            _sanitize(self.ui, self.wvfs, '.git')
+                self._gitcommand([b'merge', b'--no-commit', revision])
+            _sanitize(self.ui, self.wvfs, b'.git')
 
         if self.dirty():
             if self._gitstate() != revision:
                 dirty = self._gitstate() == self._state[1] or code != 0
-                if _updateprompt(self.ui, self, dirty,
-                                 self._state[1][:7], revision[:7]):
+                if _updateprompt(
+                    self.ui, self, dirty, self._state[1][:7], revision[:7]
+                ):
                     mergefunc()
         else:
             mergefunc()
 
     @annotatesubrepoerror
     def push(self, opts):
-        force = opts.get('force')
+        force = opts.get(b'force')
 
         if not self._state[1]:
             return True
         if self._gitmissing():
-            raise error.Abort(_("subrepo %s is missing") % self._relpath)
+            raise error.Abort(_(b"subrepo %s is missing") % self._relpath)
         # if a branch in origin contains the revision, nothing to do
         branch2rev, rev2branch = self._gitbranchmap()
         if self._state[1] in rev2branch:
             for b in rev2branch[self._state[1]]:
-                if b.startswith('refs/remotes/origin/'):
+                if b.startswith(b'refs/remotes/origin/'):
                     return True
-        for b, revision in branch2rev.iteritems():
-            if b.startswith('refs/remotes/origin/'):
+        for b, revision in pycompat.iteritems(branch2rev):
+            if b.startswith(b'refs/remotes/origin/'):
                 if self._gitisancestor(self._state[1], revision):
                     return True
         # otherwise, try to push the currently checked out branch
-        cmd = ['push']
+        cmd = [b'push']
         if force:
-            cmd.append('--force')
+            cmd.append(b'--force')
 
         current = self._gitcurrentbranch()
         if current:
             # determine if the current branch is even useful
             if not self._gitisancestor(self._state[1], current):
-                self.ui.warn(_('unrelated git branch checked out '
-                               'in subrepository "%s"\n') % self._relpath)
+                self.ui.warn(
+                    _(
+                        b'unrelated git branch checked out '
+                        b'in subrepository "%s"\n'
+                    )
+                    % self._relpath
+                )
                 return False
-            self.ui.status(_('pushing branch %s of subrepository "%s"\n') %
-                           (current.split('/', 2)[2], self._relpath))
-            ret = self._gitdir(cmd + ['origin', current])
+            self.ui.status(
+                _(b'pushing branch %s of subrepository "%s"\n')
+                % (current.split(b'/', 2)[2], self._relpath)
+            )
+            ret = self._gitdir(cmd + [b'origin', current])
             return ret[1] == 0
         else:
-            self.ui.warn(_('no branch checked out in subrepository "%s"\n'
-                           'cannot push revision %s\n') %
-                          (self._relpath, self._state[1]))
+            self.ui.warn(
+                _(
+                    b'no branch checked out in subrepository "%s"\n'
+                    b'cannot push revision %s\n'
+                )
+                % (self._relpath, self._state[1])
+            )
             return False
 
     @annotatesubrepoerror
@@ -1611,11 +1798,11 @@
         files = [f for f in sorted(set(files)) if match(f)]
         for f in files:
             exact = match.exact(f)
-            command = ["add"]
+            command = [b"add"]
             if exact:
-                command.append("-f") #should be added, even if ignored
+                command.append(b"-f")  # should be added, even if ignored
             if ui.verbose or not exact:
-                ui.status(_('adding %s\n') % uipathfn(f))
+                ui.status(_(b'adding %s\n') % uipathfn(f))
 
             if f in tracked:  # hg prints 'adding' even if already tracked
                 if exact:
@@ -1625,7 +1812,7 @@
                 self._gitcommand(command + [f])
 
         for f in rejected:
-            ui.warn(_("%s already tracked!\n") % uipathfn(f))
+            ui.warn(_(b"%s already tracked!\n") % uipathfn(f))
 
         return rejected
 
@@ -1634,15 +1821,17 @@
         if self._gitmissing():
             return
         if self.dirty():
-            self.ui.warn(_('not removing repo %s because '
-                           'it has changes.\n') % self._relpath)
+            self.ui.warn(
+                _(b'not removing repo %s because it has changes.\n')
+                % self._relpath
+            )
             return
         # we can't fully delete the repository as it may contain
         # local-only history
-        self.ui.note(_('removing subrepo %s\n') % self._relpath)
-        self._gitcommand(['config', 'core.bare', 'true'])
+        self.ui.note(_(b'removing subrepo %s\n') % self._relpath)
+        self._gitcommand([b'config', b'core.bare', b'true'])
         for f, kind in self.wvfs.readdir():
-            if f == '.git':
+            if f == b'.git':
                 continue
             if kind == stat.S_IFDIR:
                 self.wvfs.rmtree(f)
@@ -1659,11 +1848,12 @@
         # Parse git's native archive command.
         # This should be much faster than manually traversing the trees
         # and objects with many subprocess calls.
-        tarstream = self._gitcommand(['archive', revision], stream=True)
+        tarstream = self._gitcommand([b'archive', revision], stream=True)
         tar = tarfile.open(fileobj=tarstream, mode=r'r|')
         relpath = subrelpath(self)
-        progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
-                                        unit=_('files'))
+        progress = self.ui.makeprogress(
+            _(b'archiving (%s)') % relpath, unit=_(b'files')
+        )
         progress.update(0)
         for info in tar:
             if info.isdir():
@@ -1681,26 +1871,25 @@
         progress.complete()
         return total
 
-
     @annotatesubrepoerror
     def cat(self, match, fm, fntemplate, prefix, **opts):
         rev = self._state[1]
         if match.anypats():
-            return 1 #No support for include/exclude yet
+            return 1  # No support for include/exclude yet
 
         if not match.files():
             return 1
 
         # TODO: add support for non-plain formatter (see cmdutil.cat())
         for f in match.files():
-            output = self._gitcommand(["show", "%s:%s" % (rev, f)])
-            fp = cmdutil.makefileobj(self._ctx, fntemplate,
-                                     pathname=self.wvfs.reljoin(prefix, f))
+            output = self._gitcommand([b"show", b"%s:%s" % (rev, f)])
+            fp = cmdutil.makefileobj(
+                self._ctx, fntemplate, pathname=self.wvfs.reljoin(prefix, f)
+            )
             fp.write(output)
             fp.close()
         return 0
 
-
     @annotatesubrepoerror
     def status(self, rev2, **opts):
         rev1 = self._state[1]
@@ -1710,42 +1899,42 @@
         modified, added, removed = [], [], []
         self._gitupdatestat()
         if rev2:
-            command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
+            command = [b'diff-tree', b'--no-renames', b'-r', rev1, rev2]
         else:
-            command = ['diff-index', '--no-renames', rev1]
+            command = [b'diff-index', b'--no-renames', rev1]
         out = self._gitcommand(command)
-        for line in out.split('\n'):
-            tab = line.find('\t')
+        for line in out.split(b'\n'):
+            tab = line.find(b'\t')
             if tab == -1:
                 continue
-            status, f = line[tab - 1:tab], line[tab + 1:]
-            if status == 'M':
+            status, f = line[tab - 1 : tab], line[tab + 1 :]
+            if status == b'M':
                 modified.append(f)
-            elif status == 'A':
+            elif status == b'A':
                 added.append(f)
-            elif status == 'D':
+            elif status == b'D':
                 removed.append(f)
 
         deleted, unknown, ignored, clean = [], [], [], []
 
-        command = ['status', '--porcelain', '-z']
+        command = [b'status', b'--porcelain', b'-z']
         if opts.get(r'unknown'):
-            command += ['--untracked-files=all']
+            command += [b'--untracked-files=all']
         if opts.get(r'ignored'):
-            command += ['--ignored']
+            command += [b'--ignored']
         out = self._gitcommand(command)
 
         changedfiles = set()
         changedfiles.update(modified)
         changedfiles.update(added)
         changedfiles.update(removed)
-        for line in out.split('\0'):
+        for line in out.split(b'\0'):
             if not line:
                 continue
             st = line[0:2]
-            #moves and copies show 2 files on one line
-            if line.find('\0') >= 0:
-                filename1, filename2 = line[3:].split('\0')
+            # moves and copies show 2 files on one line
+            if line.find(b'\0') >= 0:
+                filename1, filename2 = line[3:].split(b'\0')
             else:
                 filename1 = line[3:]
                 filename2 = None
@@ -1754,65 +1943,70 @@
             if filename2:
                 changedfiles.add(filename2)
 
-            if st == '??':
+            if st == b'??':
                 unknown.append(filename1)
-            elif st == '!!':
+            elif st == b'!!':
                 ignored.append(filename1)
 
         if opts.get(r'clean'):
-            out = self._gitcommand(['ls-files'])
-            for f in out.split('\n'):
+            out = self._gitcommand([b'ls-files'])
+            for f in out.split(b'\n'):
                 if not f in changedfiles:
                     clean.append(f)
 
-        return scmutil.status(modified, added, removed, deleted,
-                              unknown, ignored, clean)
+        return scmutil.status(
+            modified, added, removed, deleted, unknown, ignored, clean
+        )
 
     @annotatesubrepoerror
     def diff(self, ui, diffopts, node2, match, prefix, **opts):
         node1 = self._state[1]
-        cmd = ['diff', '--no-renames']
+        cmd = [b'diff', b'--no-renames']
         if opts[r'stat']:
-            cmd.append('--stat')
+            cmd.append(b'--stat')
         else:
             # for Git, this also implies '-p'
-            cmd.append('-U%d' % diffopts.context)
+            cmd.append(b'-U%d' % diffopts.context)
 
         if diffopts.noprefix:
-            cmd.extend(['--src-prefix=%s/' % prefix,
-                        '--dst-prefix=%s/' % prefix])
+            cmd.extend(
+                [b'--src-prefix=%s/' % prefix, b'--dst-prefix=%s/' % prefix]
+            )
         else:
-            cmd.extend(['--src-prefix=a/%s/' % prefix,
-                        '--dst-prefix=b/%s/' % prefix])
+            cmd.extend(
+                [b'--src-prefix=a/%s/' % prefix, b'--dst-prefix=b/%s/' % prefix]
+            )
 
         if diffopts.ignorews:
-            cmd.append('--ignore-all-space')
+            cmd.append(b'--ignore-all-space')
         if diffopts.ignorewsamount:
-            cmd.append('--ignore-space-change')
-        if (self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4)
-            and diffopts.ignoreblanklines):
-            cmd.append('--ignore-blank-lines')
+            cmd.append(b'--ignore-space-change')
+        if (
+            self._gitversion(self._gitcommand([b'--version'])) >= (1, 8, 4)
+            and diffopts.ignoreblanklines
+        ):
+            cmd.append(b'--ignore-blank-lines')
 
         cmd.append(node1)
         if node2:
             cmd.append(node2)
 
-        output = ""
+        output = b""
         if match.always():
-            output += self._gitcommand(cmd) + '\n'
+            output += self._gitcommand(cmd) + b'\n'
         else:
             st = self.status(node2)[:3]
             files = [f for sublist in st for f in sublist]
             for f in files:
                 if match(f):
-                    output += self._gitcommand(cmd + ['--', f]) + '\n'
+                    output += self._gitcommand(cmd + [b'--', f]) + b'\n'
 
         if output.strip():
             ui.write(output)
 
     @annotatesubrepoerror
     def revert(self, substate, *pats, **opts):
-        self.ui.status(_('reverting subrepo %s\n') % substate[0])
+        self.ui.status(_(b'reverting subrepo %s\n') % substate[0])
         if not opts.get(r'no_backup'):
             status = self.status(None)
             names = status.modified
@@ -1820,10 +2014,13 @@
                 # backuppath() expects a path relative to the parent repo (the
                 # repo that ui.origbackuppath is relative to)
                 parentname = os.path.join(self._path, name)
-                bakname = scmutil.backuppath(self.ui, self._subparent,
-                                             parentname)
-                self.ui.note(_('saving current version of %s as %s\n') %
-                        (name, os.path.relpath(bakname)))
+                bakname = scmutil.backuppath(
+                    self.ui, self._subparent, parentname
+                )
+                self.ui.note(
+                    _(b'saving current version of %s as %s\n')
+                    % (name, os.path.relpath(bakname))
+                )
                 util.rename(self.wvfs.join(name), bakname)
 
         if not opts.get(r'dry_run'):
@@ -1833,8 +2030,9 @@
     def shortid(self, revid):
         return revid[:7]
 
+
 types = {
-    'hg': hgsubrepo,
-    'svn': svnsubrepo,
-    'git': gitsubrepo,
-    }
+    b'hg': hgsubrepo,
+    b'svn': svnsubrepo,
+    b'git': gitsubrepo,
+}