--- 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,
+}