--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -36,9 +36,8 @@ from mercurial import ui, hg, revlog, co
versionstr = "0.45"
-repomap = {}
+commands.norepo += " qclone qversion"
-commands.norepo += " qversion"
class queue:
def __init__(self, ui, path, patchdir=None):
self.basepath = path
@@ -187,8 +186,7 @@ class queue:
return (err, n)
if n is None:
- self.ui.warn("apply failed for patch %s\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("apply failed for patch %s") % patch)
self.ui.warn("patch didn't work out, merging %s\n" % patch)
@@ -199,17 +197,14 @@ class queue:
c = repo.changelog.read(rev)
ret = repo.update(rev, allow=True, wlock=wlock)
if ret:
- self.ui.warn("update returned %d\n" % ret)
- sys.exit(1)
+ raise util.Abort(_("update returned %d") % ret)
n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
if n == None:
- self.ui.warn("repo commit failed\n")
- sys.exit(1)
+ raise util.Abort(_("repo commit failed"))
try:
message, comments, user, date, patchfound = mergeq.readheaders(patch)
except:
- self.ui.warn("Unable to read %s\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("unable to read %s") % patch)
patchf = self.opener(patch, "w")
if comments:
@@ -280,8 +275,6 @@ class queue:
# TODO unify with commands.py
if not patchdir:
patchdir = self.path
- pwd = os.getcwd()
- os.chdir(repo.root)
err = 0
if not wlock:
wlock = repo.wlock()
@@ -308,7 +301,8 @@ class queue:
try:
pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
- f = os.popen("%s -p1 --no-backup-if-mismatch < '%s'" % (pp, pf))
+ f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
+ (pp, repo.root, pf))
except:
self.ui.warn("patch failed, unable to continue (try -v)\n")
err = 1
@@ -356,8 +350,7 @@ class queue:
wlock=wlock)
if n == None:
- self.ui.warn("repo commit failed\n")
- sys.exit(1)
+ raise util.Abort(_("repo commit failed"))
if update_status:
self.applied.append(revlog.hex(n) + ":" + patch)
@@ -376,18 +369,15 @@ class queue:
err = 1
break
tr.close()
- os.chdir(pwd)
return (err, n)
def delete(self, repo, patch):
patch = self.lookup(patch, strict=True)
info = self.isapplied(patch)
if info:
- self.ui.warn("cannot delete applied patch %s\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("cannot delete applied patch %s") % patch)
if patch not in self.series:
- self.ui.warn("patch %s not in series file\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("patch %s not in series file") % patch)
i = self.find_series(patch)
del self.full_series[i]
self.read_series(self.full_series)
@@ -399,23 +389,22 @@ class queue:
top = revlog.bin(top)
pp = repo.dirstate.parents()
if top not in pp:
- self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1])))
- sys.exit(1)
+ raise util.Abort(_("queue top not at same revision as working directory"))
return top
return None
def check_localchanges(self, repo):
(c, a, r, d, u) = repo.changes(None, None)
if c or a or d or r:
- self.ui.write("Local changes found, refresh first\n")
- sys.exit(1)
+ raise util.Abort(_("local changes found, refresh first"))
def new(self, repo, patch, msg=None, force=None):
+ if os.path.exists(os.path.join(self.path, patch)):
+ raise util.Abort(_('patch "%s" already exists') % patch)
commitfiles = []
(c, a, r, d, u) = repo.changes(None, None)
if c or a or d or r:
if not force:
- raise util.Abort(_("Local changes found, refresh first"))
- else:
- commitfiles = c + a + r
+ raise util.Abort(_("local changes found, refresh first"))
+ commitfiles = c + a + r
self.check_toppatch(repo)
wlock = repo.wlock()
insert = self.full_series_end()
@@ -426,8 +415,7 @@ class queue:
n = repo.commit(commitfiles,
"New patch: %s" % patch, force=True, wlock=wlock)
if n == None:
- self.ui.warn("repo commit failed\n")
- sys.exit(1)
+ raise util.Abort(_("repo commit failed"))
self.full_series[insert:insert] = [patch]
self.applied.append(revlog.hex(n) + ":" + patch)
self.read_series(self.full_series)
@@ -532,7 +520,7 @@ class queue:
if update:
(c, a, r, d, u) = repo.changes(None, None)
if c or a or d or r:
- raise util.Abort(_("Local changes found"))
+ raise util.Abort(_("local changes found"))
urev = self.qparents(repo, rev)
repo.update(urev, allow=False, force=True, wlock=wlock)
repo.dirstate.write()
@@ -672,8 +660,7 @@ class queue:
else:
if i + off < len(self.series):
return self.series[i + off]
- self.ui.warn("patch %s not in series\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("patch %s not in series") % patch)
def push(self, repo, patch=None, force=False, list=False,
mergeq=None, wlock=None):
@@ -681,10 +668,10 @@ class queue:
wlock = repo.wlock()
patch = self.lookup(patch)
if patch and self.isapplied(patch):
- self.ui.warn("patch %s is already applied\n" % patch)
+ self.ui.warn(_("patch %s is already applied\n") % patch)
sys.exit(1)
if self.series_end() == len(self.series):
- self.ui.warn("File series fully applied\n")
+ self.ui.warn(_("patch series fully applied\n"))
sys.exit(1)
if not force:
self.check_localchanges(repo)
@@ -733,10 +720,9 @@ class queue:
patch = self.lookup(patch)
info = self.isapplied(patch)
if not info:
- self.ui.warn("patch %s is not applied\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("patch %s is not applied") % patch)
if len(self.applied) == 0:
- self.ui.warn("No patches applied\n")
+ self.ui.warn(_("no patches applied\n"))
sys.exit(1)
if not update:
@@ -910,15 +896,14 @@ class queue:
def init(self, repo, create=False):
if os.path.isdir(self.path):
- raise util.Abort("patch queue directory already exists")
+ raise util.Abort(_("patch queue directory already exists"))
os.mkdir(self.path)
if create:
return self.qrepo(create=True)
def unapplied(self, repo, patch=None):
if patch and patch not in self.series:
- self.ui.warn("%s not in the series file\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("patch %s is not in series file") % patch)
if not patch:
start = self.series_end()
else:
@@ -1070,8 +1055,7 @@ class queue:
def qapplied(self, repo, patch=None):
if patch and patch not in self.series:
- self.ui.warn("%s not in the series file\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("patch %s is not in series file") % patch)
if not patch:
end = len(self.applied)
else:
@@ -1117,8 +1101,8 @@ class queue:
def qimport(self, repo, files, patch=None, existing=None, force=None):
if len(files) > 1 and patch:
- self.ui.warn("-n option not valid when importing multiple files\n")
- sys.exit(1)
+ raise util.Abort(_('option "-n" not valid when importing multiple '
+ 'files'))
i = 0
added = []
for filename in files:
@@ -1126,24 +1110,21 @@ class queue:
if not patch:
patch = filename
if not os.path.isfile(os.path.join(self.path, patch)):
- self.ui.warn("patch %s does not exist\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("patch %s does not exist") % patch)
else:
try:
text = file(filename).read()
except IOError:
- self.ui.warn("Unable to read %s\n" % patch)
- sys.exit(1)
+ raise util.Abort(_("unable to read %s") % patch)
if not patch:
patch = os.path.split(filename)[1]
- if not force and os.path.isfile(os.path.join(self.path, patch)):
- self.ui.warn("patch %s already exists\n" % patch)
- sys.exit(1)
+ if not force and os.path.exists(os.path.join(self.path, patch)):
+ raise util.Abort(_('patch "%s" already exists') % patch)
patchf = self.opener(patch, "w")
patchf.write(text)
if patch in self.series:
- self.ui.warn("patch %s is already in the series file\n" % patch)
- sys.exit(1)
+ raise util.Abort(_('patch %s is already in the series file')
+ % patch)
index = self.full_series_end() + i
self.full_series[index:index] = [patch]
self.read_series(self.full_series)
@@ -1158,24 +1139,24 @@ class queue:
def delete(ui, repo, patch, **opts):
"""remove a patch from the series file"""
- q = repomap[repo]
+ q = repo.mq
q.delete(repo, patch)
q.save_dirty()
return 0
def applied(ui, repo, patch=None, **opts):
"""print the patches already applied"""
- repomap[repo].qapplied(repo, patch)
+ repo.mq.qapplied(repo, patch)
return 0
def unapplied(ui, repo, patch=None, **opts):
"""print the patches not yet applied"""
- repomap[repo].unapplied(repo, patch)
+ repo.mq.unapplied(repo, patch)
return 0
def qimport(ui, repo, *filename, **opts):
"""import a patch"""
- q = repomap[repo]
+ q = repo.mq
q.qimport(repo, filename, patch=opts['name'],
existing=opts['existing'], force=opts['force'])
q.save_dirty()
@@ -1183,7 +1164,7 @@ def qimport(ui, repo, *filename, **opts)
def init(ui, repo, **opts):
"""init a new queue repository"""
- q = repomap[repo]
+ q = repo.mq
r = q.init(repo, create=opts['create_repo'])
q.save_dirty()
if r:
@@ -1195,36 +1176,80 @@ def init(ui, repo, **opts):
r.add(['.hgignore', 'series'])
return 0
+def clone(ui, source, dest=None, **opts):
+ '''clone main and patch repository at same time
+
+ If source is local, destination will have no patches applied. If
+ source is remote, this command can not check if patches are
+ applied in source, so cannot guarantee that patches are not
+ applied in destination. If you clone remote repository, be sure
+ before that it has no patches applied.
+
+ Source patch repository is looked for in <src>/.hg/patches by
+ default. Use -p <url> to change.
+ '''
+ ui.setconfig_remoteopts(**opts)
+ if dest is None:
+ dest = hg.defaultdest(source)
+ sr = hg.repository(ui, ui.expandpath(source))
+ qbase, destrev = None, None
+ if sr.local():
+ reposetup(ui, sr)
+ if sr.mq.applied:
+ qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
+ if not hg.islocal(dest):
+ destrev = sr.parents(qbase)[0]
+ ui.note(_('cloning main repo\n'))
+ sr, dr = hg.clone(ui, sr, dest,
+ pull=opts['pull'],
+ rev=destrev,
+ update=False,
+ stream=opts['uncompressed'])
+ ui.note(_('cloning patch repo\n'))
+ spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
+ dr.url() + '/.hg/patches',
+ pull=opts['pull'],
+ update=not opts['noupdate'],
+ stream=opts['uncompressed'])
+ if dr.local():
+ if qbase:
+ ui.note(_('stripping applied patches from destination repo\n'))
+ reposetup(ui, dr)
+ dr.mq.strip(dr, qbase, update=False, backup=None)
+ if not opts['noupdate']:
+ ui.note(_('updating destination repo\n'))
+ dr.update(dr.changelog.tip())
+
def commit(ui, repo, *pats, **opts):
"""commit changes in the queue repository"""
- q = repomap[repo]
+ q = repo.mq
r = q.qrepo()
if not r: raise util.Abort('no queue repository')
commands.commit(r.ui, r, *pats, **opts)
def series(ui, repo, **opts):
"""print the entire series file"""
- repomap[repo].qseries(repo, missing=opts['missing'])
+ repo.mq.qseries(repo, missing=opts['missing'])
return 0
def top(ui, repo, **opts):
"""print the name of the current patch"""
- repomap[repo].top(repo)
+ repo.mq.top(repo)
return 0
def next(ui, repo, **opts):
"""print the name of the next patch"""
- repomap[repo].next(repo)
+ repo.mq.next(repo)
return 0
def prev(ui, repo, **opts):
"""print the name of the previous patch"""
- repomap[repo].prev(repo)
+ repo.mq.prev(repo)
return 0
def new(ui, repo, patch, **opts):
"""create a new patch"""
- q = repomap[repo]
+ q = repo.mq
message=commands.logmessage(**opts)
q.new(repo, patch, msg=message, force=opts['force'])
q.save_dirty()
@@ -1232,7 +1257,7 @@ def new(ui, repo, patch, **opts):
def refresh(ui, repo, **opts):
"""update the current patch"""
- q = repomap[repo]
+ q = repo.mq
message=commands.logmessage(**opts)
q.refresh(repo, msg=message, short=opts['short'])
q.save_dirty()
@@ -1241,7 +1266,7 @@ def refresh(ui, repo, **opts):
def diff(ui, repo, *files, **opts):
"""diff of the current patch"""
# deep in the dirstate code, the walkhelper method wants a list, not a tuple
- repomap[repo].diff(repo, list(files))
+ repo.mq.diff(repo, list(files))
return 0
def lastsavename(path):
@@ -1270,7 +1295,7 @@ def savename(path):
def push(ui, repo, patch=None, **opts):
"""push the next patch onto the stack"""
- q = repomap[repo]
+ q = repo.mq
mergeq = None
if opts['all']:
@@ -1298,7 +1323,7 @@ def pop(ui, repo, patch=None, **opts):
ui.warn('using patch queue: %s\n' % q.path)
localupdate = False
else:
- q = repomap[repo]
+ q = repo.mq
q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
q.save_dirty()
return 0
@@ -1306,7 +1331,7 @@ def pop(ui, repo, patch=None, **opts):
def restore(ui, repo, rev, **opts):
"""restore the queue state saved by a rev"""
rev = repo.lookup(rev)
- q = repomap[repo]
+ q = repo.mq
q.restore(repo, rev, delete=opts['delete'],
qupdate=opts['update'])
q.save_dirty()
@@ -1314,7 +1339,7 @@ def restore(ui, repo, rev, **opts):
def save(ui, repo, **opts):
"""save current queue state"""
- q = repomap[repo]
+ q = repo.mq
message=commands.logmessage(**opts)
ret = q.save(repo, msg=message)
if ret:
@@ -1326,13 +1351,11 @@ def save(ui, repo, **opts):
newpath = os.path.join(q.basepath, opts['name'])
if os.path.exists(newpath):
if not os.path.isdir(newpath):
- ui.warn("destination %s exists and is not a directory\n" %
- newpath)
- sys.exit(1)
+ raise util.Abort(_('destination %s exists and is not '
+ 'a directory') % newpath)
if not opts['force']:
- ui.warn("destination %s exists, use -f to force\n" %
- newpath)
- sys.exit(1)
+ raise util.Abort(_('destination %s exists, '
+ 'use -f to force') % newpath)
else:
newpath = savename(path)
ui.warn("copy %s to %s\n" % (path, newpath))
@@ -1352,7 +1375,7 @@ def strip(ui, repo, rev, **opts):
backup = 'strip'
elif opts['nobackup']:
backup = 'none'
- repomap[repo].strip(repo, rev, backup=backup)
+ repo.mq.strip(repo, rev, backup=backup)
return 0
def version(ui, q=None):
@@ -1361,34 +1384,43 @@ def version(ui, q=None):
return 0
def reposetup(ui, repo):
- repomap[repo] = queue(ui, repo.join(""))
- oldtags = repo.tags
+ class MqRepo(repo.__class__):
+ def tags(self):
+ if self.tagscache:
+ return self.tagscache
+
+ tagscache = super(self.__class__, self).tags()
- def qtags():
- if repo.tagscache:
- return repo.tagscache
+ q = self.mq
+ if not q.applied:
+ return tagscache
- tagscache = oldtags()
+ mqtags = [patch.split(':') for patch in q.applied]
+ mqtags.append((mqtags[-1][0], 'qtip'))
+ mqtags.append((mqtags[0][0], 'qbase'))
+ for patch in mqtags:
+ if patch[1] in tagscache:
+ self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
+ else:
+ tagscache[patch[1]] = revlog.bin(patch[0])
- q = repomap[repo]
- if len(q.applied) == 0:
return tagscache
- mqtags = [patch.split(':') for patch in q.applied]
- mqtags.append((mqtags[-1][0], 'qtip'))
- mqtags.append((mqtags[0][0], 'qbase'))
- for patch in mqtags:
- if patch[1] in tagscache:
- repo.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
- else:
- tagscache[patch[1]] = revlog.bin(patch[0])
-
- return tagscache
-
- repo.tags = qtags
+ repo.__class__ = MqRepo
+ repo.mq = queue(ui, repo.join(""))
cmdtable = {
"qapplied": (applied, [], 'hg qapplied [PATCH]'),
+ "qclone": (clone,
+ [('', 'pull', None, _('use pull protocol to copy metadata')),
+ ('U', 'noupdate', None, _('do not update the new working directories')),
+ ('', 'uncompressed', None,
+ _('use uncompressed transfer (fast over LAN)')),
+ ('e', 'ssh', '', _('specify ssh command to use')),
+ ('p', 'patches', '', _('location of source patch repo')),
+ ('', 'remotecmd', '',
+ _('specify hg command to run on the remote side'))],
+ 'hg qclone [OPTION]... SOURCE [DEST]'),
"qcommit|qci":
(commit,
commands.table["^commit|ci"][1],