##// END OF EJS Templates
mq: apply patch is any posative guard matches...
mq: apply patch is any posative guard matches this is like guards command from quilt package.

File last commit:

r2850:851b07ec default
r2850:851b07ec default
Show More
mq.py
1990 lines | 69.8 KiB | text/x-python | PythonLexer
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748
mason@suse.com
Add mq extension
r1808 # queue.py - patch queues for mercurial
#
# Copyright 2005 Chris Mason <mason@suse.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Vadim Gelfer
help: add help to mq extension
r2554 '''patch management and development
This extension lets you work with a stack of patches in a Mercurial
repository. It manages two stacks of patches - all known patches, and
applied patches (subset of known patches).
Known patches are represented as patch files in the .hg/patches
directory. Applied patches are both patch files and changesets.
Common tasks (use "hg help command" for more details):
prepare repository to work with patches qinit
create new patch qnew
import existing patch qimport
print patch series qseries
print applied patches qapplied
print name of top applied patch qtop
add known patch to applied stack qpush
remove patch from applied stack qpop
refresh contents of top applied patch qrefresh
'''
mason@suse.com
Add mq extension
r1808 from mercurial.demandload import *
demandload(globals(), "os sys re struct traceback errno bz2")
from mercurial.i18n import gettext as _
from mercurial import ui, hg, revlog, commands, util
Vadim Gelfer
mq: add qclone command
r2720 commands.norepo += " qclone qversion"
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 class statusentry:
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 def __init__(self, rev, name=None):
if not name:
Brendan Cully
Update qsave to use StatusEntry; don't throw exception on bad status lines.
r2816 fields = rev.split(':')
if len(fields) == 2:
self.rev, self.name = fields
else:
self.rev, self.name = None, None
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 else:
self.rev, self.name = rev, name
def __str__(self):
return self.rev + ':' + self.name
mason@suse.com
Add mq extension
r1808 class queue:
def __init__(self, ui, path, patchdir=None):
self.basepath = path
Vadim Gelfer
mq: add join method
r2819 self.path = patchdir or os.path.join(path, "patches")
Thomas Arendsen Hein
Fix mq's usage of opener, which don't allow absolute paths now.
r1852 self.opener = util.opener(self.path)
mason@suse.com
Add mq extension
r1808 self.ui = ui
self.applied = []
self.full_series = []
self.applied_dirty = 0
self.series_dirty = 0
Thomas Arendsen Hein
Fix mq's usage of opener, which don't allow absolute paths now.
r1852 self.series_path = "series"
self.status_path = "status"
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 self.guards_path = "guards"
self.active_guards = None
self.guards_dirty = False
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
Vadim Gelfer
mq: add join method
r2819 if os.path.exists(self.join(self.series_path)):
Thomas Arendsen Hein
Fix mq's usage of opener, which don't allow absolute paths now.
r1852 self.full_series = self.opener(self.series_path).read().splitlines()
Vadim Gelfer
mq: rename read_series as parse_series, make simpler and faster
r2767 self.parse_series()
mason@suse.com
Add mq extension
r1808
Vadim Gelfer
mq: add join method
r2819 if os.path.exists(self.join(self.status_path)):
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 lines = self.opener(self.status_path).read().splitlines()
self.applied = [statusentry(l) for l in lines]
mason@suse.com
Add mq extension
r1808
Vadim Gelfer
mq: add join method
r2819 def join(self, *p):
return os.path.join(self.path, *p)
mason@suse.com
Add mq extension
r1808 def find_series(self, patch):
pre = re.compile("(\s*)([^#]+)")
index = 0
for l in self.full_series:
m = pre.match(l)
if m:
s = m.group(2)
s = s.rstrip()
if s == patch:
return index
index += 1
return None
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
Vadim Gelfer
mq: rename read_series as parse_series, make simpler and faster
r2767 def parse_series(self):
mason@suse.com
Add mq extension
r1808 self.series = []
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 self.series_guards = []
Vadim Gelfer
mq: rename read_series as parse_series, make simpler and faster
r2767 for l in self.full_series:
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 h = l.find('#')
if h == -1:
patch = l
comment = ''
elif h == 0:
continue
else:
patch = l[:h]
comment = l[h:]
patch = patch.strip()
if patch:
self.series.append(patch)
self.series_guards.append(self.guard_re.findall(comment))
def check_guard(self, guard):
bad_chars = '# \t\r\n\f'
first = guard[0]
for c in '-+':
if first == c:
return (_('guard %r starts with invalid character: %r') %
(guard, c))
for c in bad_chars:
if c in guard:
return _('invalid character in guard %r: %r') % (guard, c)
def set_active(self, guards):
for guard in guards:
bad = self.check_guard(guard)
if bad:
raise util.Abort(bad)
guards = dict.fromkeys(guards).keys()
guards.sort()
self.ui.debug('active guards: %s\n' % ' '.join(guards))
self.active_guards = guards
self.guards_dirty = True
def active(self):
if self.active_guards is None:
self.active_guards = []
try:
guards = self.opener(self.guards_path).read().split()
except IOError, err:
if err.errno != errno.ENOENT: raise
guards = []
for i, guard in enumerate(guards):
bad = self.check_guard(guard)
if bad:
self.ui.warn('%s:%d: %s\n' %
(self.join(self.guards_path), i + 1, bad))
else:
self.active_guards.append(guard)
return self.active_guards
def set_guards(self, idx, guards):
for g in guards:
if len(g) < 2:
raise util.Abort(_('guard %r too short') % g)
if g[0] not in '-+':
raise util.Abort(_('guard %r starts with invalid char') % g)
bad = self.check_guard(g[1:])
if bad:
raise util.Abort(bad)
drop = self.guard_re.sub('', self.full_series[idx])
self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
self.parse_series()
self.series_dirty = True
def pushable(self, idx):
if isinstance(idx, str):
idx = self.series.index(idx)
patchguards = self.series_guards[idx]
if not patchguards:
return True, None
default = False
guards = self.active()
exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
if exactneg:
return False, exactneg[0]
pos = [g for g in patchguards if g[0] == '+']
Vadim Gelfer
mq: apply patch is any posative guard matches...
r2850 exactpos = [g for g in pos if g[1:] in guards]
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 if pos:
Vadim Gelfer
mq: apply patch is any posative guard matches...
r2850 if exactpos:
return True, exactpos[0]
return False, pos
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 return True, ''
def explain_pushable(self, idx, all_patches=False):
write = all_patches and self.ui.write or self.ui.warn
if all_patches or self.ui.verbose:
if isinstance(idx, str):
idx = self.series.index(idx)
pushable, why = self.pushable(idx)
if all_patches and pushable:
if why is None:
write(_('allowing %s - no guards in effect\n') %
self.series[idx])
else:
if not why:
write(_('allowing %s - no matching negative guards\n') %
self.series[idx])
else:
write(_('allowing %s - guarded by %r\n') %
(self.series[idx], why))
if not pushable:
Vadim Gelfer
mq: make guards more strict, add tests
r2829 if why:
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 write(_('skipping %s - guarded by %r\n') %
Vadim Gelfer
mq: make guards more strict, add tests
r2829 (self.series[idx], ' '.join(why)))
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 else:
write(_('skipping %s - no matching guards\n') %
self.series[idx])
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 def save_dirty(self):
Vadim Gelfer
mq: simplify save_dirty
r2772 def write_list(items, path):
fp = self.opener(path, 'w')
for i in items:
print >> fp, i
fp.close()
Vadim Gelfer
merge with brendan.
r2781 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
Vadim Gelfer
mq: simplify save_dirty
r2772 if self.series_dirty: write_list(self.full_series, self.series_path)
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
mason@suse.com
Add mq extension
r1808
def readheaders(self, patch):
def eatdiff(lines):
while lines:
l = lines[-1]
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 if (l.startswith("diff -") or
l.startswith("Index:") or
l.startswith("===========")):
mason@suse.com
Add mq extension
r1808 del lines[-1]
else:
break
def eatempty(lines):
while lines:
l = lines[-1]
if re.match('\s*$', l):
del lines[-1]
else:
break
Vadim Gelfer
mq: add join method
r2819 pf = self.join(patch)
mason@suse.com
Add mq extension
r1808 message = []
comments = []
user = None
Danek Duvall
Add timestamp field to export format. Make import and mq use it.
r2299 date = None
mason@suse.com
Add mq extension
r1808 format = None
subject = None
diffstart = 0
for line in file(pf):
line = line.rstrip()
if diffstart:
if line.startswith('+++ '):
diffstart = 2
break
if line.startswith("--- "):
diffstart = 1
continue
elif format == "hgpatch":
# parse values when importing the result of an hg export
if line.startswith("# User "):
user = line[7:]
Thomas Arendsen Hein
Use "# Date" instead of "# Timestamp" for dated export/import of patches....
r2300 elif line.startswith("# Date "):
date = line[7:]
mason@suse.com
Add mq extension
r1808 elif not line.startswith("# ") and line:
message.append(line)
format = None
elif line == '# HG changeset patch':
format = "hgpatch"
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 elif (format != "tagdone" and (line.startswith("Subject: ") or
line.startswith("subject: "))):
mason@suse.com
Add mq extension
r1808 subject = line[9:]
format = "tag"
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 elif (format != "tagdone" and (line.startswith("From: ") or
line.startswith("from: "))):
mason@suse.com
Add mq extension
r1808 user = line[6:]
format = "tag"
elif format == "tag" and line == "":
# when looking for tags (subject: from: etc) they
# end once you find a blank line in the source
format = "tagdone"
Thomas Arendsen Hein
Strip empty lines and trailing spaces around commit messages....
r2301 elif message or line:
mason@suse.com
Add mq extension
r1808 message.append(line)
comments.append(line)
eatdiff(message)
eatdiff(comments)
eatempty(message)
eatempty(comments)
# make sure message isn't empty
if format and format.startswith("tag") and subject:
message.insert(0, "")
message.insert(0, subject)
Danek Duvall
Add timestamp field to export format. Make import and mq use it.
r2299 return (message, comments, user, date, diffstart > 1)
mason@suse.com
Add mq extension
r1808
def mergeone(self, repo, mergeq, head, patch, rev, wlock):
# first try just applying the patch
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 (err, n) = self.apply(repo, [ patch ], update_status=False,
mason@suse.com
Add mq extension
r1808 strict=True, merge=rev, wlock=wlock)
if err == 0:
return (err, n)
if n is None:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("apply failed for patch %s") % patch)
mason@suse.com
Add mq extension
r1808
self.ui.warn("patch didn't work out, merging %s\n" % patch)
# apply failed, strip away that rev and merge.
Matt Mackall
Introduce update helper functions: update, merge, clean, and revert
r2808 hg.clean(repo, head, wlock=wlock)
mason@suse.com
Add mq extension
r1808 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
c = repo.changelog.read(rev)
Matt Mackall
Introduce update helper functions: update, merge, clean, and revert
r2808 ret = hg.merge(repo, rev, wlock=wlock)
mason@suse.com
Add mq extension
r1808 if ret:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("update returned %d") % ret)
mason@suse.com
Add mq extension
r1808 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
if n == None:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("repo commit failed"))
mason@suse.com
Add mq extension
r1808 try:
Danek Duvall
Add timestamp field to export format. Make import and mq use it.
r2299 message, comments, user, date, patchfound = mergeq.readheaders(patch)
mason@suse.com
Add mq extension
r1808 except:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("unable to read %s") % patch)
mason@suse.com
Add mq extension
r1808
Thomas Arendsen Hein
Fix mq's usage of opener, which don't allow absolute paths now.
r1852 patchf = self.opener(patch, "w")
mason@suse.com
Add mq extension
r1808 if comments:
comments = "\n".join(comments) + '\n\n'
patchf.write(comments)
commands.dodiff(patchf, self.ui, repo, head, n)
patchf.close()
return (0, n)
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 def qparents(self, repo, rev=None):
if rev is None:
(p1, p2) = repo.dirstate.parents()
if p2 == revlog.nullid:
return p1
if len(self.applied) == 0:
return None
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 return revlog.bin(self.applied[-1].rev)
mason@suse.com
Add mq extension
r1808 pp = repo.changelog.parents(rev)
if pp[1] != revlog.nullid:
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 arevs = [ x.rev for x in self.applied ]
mason@suse.com
Add mq extension
r1808 p0 = revlog.hex(pp[0])
p1 = revlog.hex(pp[1])
if p0 in arevs:
return pp[0]
if p1 in arevs:
return pp[1]
return pp[0]
def mergepatch(self, repo, mergeq, series, wlock):
if len(self.applied) == 0:
# each of the patches merged in will have two parents. This
# can confuse the qrefresh, qdiff, and strip code because it
# needs to know which parent is actually in the patch queue.
# so, we insert a merge marker with only one parent. This way
# the first patch in the queue is never a merge patch
#
pname = ".hg.patches.merge.marker"
n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
wlock=wlock)
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 self.applied.append(statusentry(revlog.hex(n), pname))
mason@suse.com
Add mq extension
r1808 self.applied_dirty = 1
head = self.qparents(repo)
for patch in series:
Chris Mason
mq: patch naming shortcuts...
r2696 patch = mergeq.lookup(patch, strict=True)
mason@suse.com
Add mq extension
r1808 if not patch:
self.ui.warn("patch %s does not exist\n" % patch)
return (1, None)
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 pushable, reason = self.pushable(patch)
if not pushable:
self.explain_pushable(patch, all_patches=True)
continue
mason@suse.com
Add mq extension
r1808 info = mergeq.isapplied(patch)
if not info:
self.ui.warn("patch %s is not applied\n" % patch)
return (1, None)
rev = revlog.bin(info[1])
(err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
if head:
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 self.applied.append(statusentry(revlog.hex(head), patch))
mason@suse.com
Add mq extension
r1808 self.applied_dirty = 1
if err:
return (err, head)
return (0, head)
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 def patch(self, repo, patchfile):
'''Apply patchfile to the working directory.
patchfile: file name of patch'''
try:
pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
Brendan Cully
Add portable shell-quoting function; teach mq to use it.
r2791 f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" %
(pp, util.shellquote(repo.root), util.shellquote(patchfile)))
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 except:
self.ui.warn("patch failed, unable to continue (try -v)\n")
return (None, [], False)
files = []
fuzz = False
for l in f:
l = l.rstrip('\r\n');
if self.ui.verbose:
self.ui.warn(l + "\n")
if l[:14] == 'patching file ':
Brendan Cully
mq: use more portable util.parse_patch_output instead of handrolled version.
r2792 pf = os.path.normpath(util.parse_patch_output(l))
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 if pf not in files:
files.append(pf)
printed_file = False
file_str = l
elif l.find('with fuzz') >= 0:
if not printed_file:
self.ui.warn(file_str + '\n')
printed_file = True
self.ui.warn(l + '\n')
fuzz = True
elif l.find('saving rejects to file') >= 0:
self.ui.warn(l + '\n')
elif l.find('FAILED') >= 0:
if not printed_file:
self.ui.warn(file_str + '\n')
printed_file = True
self.ui.warn(l + '\n')
return (not f.close(), files, fuzz)
Benoit Boissinot
mq: codingstyle
r2796
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 def apply(self, repo, series, list=False, update_status=True,
mason@suse.com
Add mq extension
r1808 strict=False, patchdir=None, merge=None, wlock=None):
# TODO unify with commands.py
if not patchdir:
patchdir = self.path
err = 0
if not wlock:
wlock = repo.wlock()
lock = repo.lock()
tr = repo.transaction()
n = None
for patch in series:
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 pushable, reason = self.pushable(patch)
if not pushable:
self.explain_pushable(patch, all_patches=True)
continue
mason@suse.com
Add mq extension
r1808 self.ui.warn("applying %s\n" % patch)
pf = os.path.join(patchdir, patch)
try:
Danek Duvall
Add timestamp field to export format. Make import and mq use it.
r2299 message, comments, user, date, patchfound = self.readheaders(patch)
mason@suse.com
Add mq extension
r1808 except:
self.ui.warn("Unable to read %s\n" % pf)
err = 1
break
if not message:
message = "imported patch %s\n" % patch
else:
if list:
message.append("\nimported patch %s" % patch)
message = '\n'.join(message)
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 (patcherr, files, fuzz) = self.patch(repo, pf)
patcherr = not patcherr
mason@suse.com
Add mq extension
r1808
if merge and len(files) > 0:
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 # Mark as merged and update dirstate parent info
repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
p1, p2 = repo.dirstate.parents()
repo.dirstate.setparents(p1, merge)
mason@suse.com
Add mq extension
r1808 if len(files) > 0:
Vadim Gelfer
mq: allow to apply patches in subdir of repo again...
r2728 cwd = repo.getcwd()
cfiles = files
if cwd:
cfiles = [util.pathto(cwd, f) for f in files]
commands.addremove_lock(self.ui, repo, cfiles,
mason@suse.com
Add mq extension
r1808 opts={}, wlock=wlock)
Danek Duvall
Add timestamp field to export format. Make import and mq use it.
r2299 n = repo.commit(files, message, user, date, force=1, lock=lock,
mason@suse.com
Add mq extension
r1808 wlock=wlock)
if n == None:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("repo commit failed"))
mason@suse.com
Add mq extension
r1808
if update_status:
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 self.applied.append(statusentry(revlog.hex(n), patch))
mason@suse.com
Add mq extension
r1808
if patcherr:
if not patchfound:
self.ui.warn("patch %s is empty\n" % patch)
err = 0
else:
self.ui.warn("patch failed, rejects left in working dir\n")
err = 1
break
if fuzz and strict:
self.ui.warn("fuzz found when applying patch, stopping\n")
err = 1
break
tr.close()
return (err, n)
Brendan Cully
Add -f option to qdelete, to remove patch file.
r2752 def delete(self, repo, patch, force=False):
Chris Mason
mq: patch naming shortcuts...
r2696 patch = self.lookup(patch, strict=True)
mason@suse.com
Add mq extension
r1808 info = self.isapplied(patch)
if info:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("cannot delete applied patch %s") % patch)
mason@suse.com
Add mq extension
r1808 if patch not in self.series:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("patch %s not in series file") % patch)
Brendan Cully
Add -f option to qdelete, to remove patch file.
r2752 if force:
r = self.qrepo()
if r:
r.remove([patch], True)
else:
Vadim Gelfer
mq: add join method
r2819 os.unlink(self.join(patch))
mason@suse.com
Add mq extension
r1808 i = self.find_series(patch)
del self.full_series[i]
Vadim Gelfer
mq: rename read_series as parse_series, make simpler and faster
r2767 self.parse_series()
mason@suse.com
Add mq extension
r1808 self.series_dirty = 1
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 def check_toppatch(self, repo):
if len(self.applied) > 0:
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 top = revlog.bin(self.applied[-1].rev)
mason@suse.com
Add mq extension
r1808 pp = repo.dirstate.parents()
if top not in pp:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("queue top not at same revision as working directory"))
mason@suse.com
Add mq extension
r1808 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:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("local changes found, refresh first"))
mason@suse.com
Add mq extension
r1808 def new(self, repo, patch, msg=None, force=None):
Vadim Gelfer
mq: add join method
r2819 if os.path.exists(self.join(patch)):
Vadim Gelfer
mq: do not allow to qnew a patch twice
r2711 raise util.Abort(_('patch "%s" already exists') % patch)
Chris Mason
mq: hg qnew -f should refresh the new patch...
r2511 commitfiles = []
(c, a, r, d, u) = repo.changes(None, None)
if c or a or d or r:
if not force:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("local changes found, refresh first"))
commitfiles = c + a + r
mason@suse.com
Add mq extension
r1808 self.check_toppatch(repo)
wlock = repo.wlock()
Chris Mason
mq: fix qnew and qimport to deal with series file comments...
r2698 insert = self.full_series_end()
mason@suse.com
Add mq extension
r1808 if msg:
Chris Mason
mq: hg qnew -f should refresh the new patch...
r2511 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
wlock=wlock)
mason@suse.com
Add mq extension
r1808 else:
Chris Mason
mq: hg qnew -f should refresh the new patch...
r2511 n = repo.commit(commitfiles,
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "New patch: %s" % patch, force=True, wlock=wlock)
mason@suse.com
Add mq extension
r1808 if n == None:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("repo commit failed"))
mason@suse.com
Add mq extension
r1808 self.full_series[insert:insert] = [patch]
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 self.applied.append(statusentry(revlog.hex(n), patch))
Vadim Gelfer
mq: rename read_series as parse_series, make simpler and faster
r2767 self.parse_series()
mason@suse.com
Add mq extension
r1808 self.series_dirty = 1
self.applied_dirty = 1
Thomas Arendsen Hein
Fix mq's usage of opener, which don't allow absolute paths now.
r1852 p = self.opener(patch, "w")
mason@suse.com
Add mq extension
r1808 if msg:
msg = msg + "\n"
p.write(msg)
p.close()
wlock = None
r = self.qrepo()
if r: r.add([patch])
Chris Mason
mq: hg qnew -f should refresh the new patch...
r2511 if commitfiles:
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 self.refresh(repo, msg=None, short=True)
mason@suse.com
Add mq extension
r1808
def strip(self, repo, rev, update=True, backup="all", wlock=None):
def limitheads(chlog, stop):
"""return the list of all nodes that have no children"""
p = {}
h = []
stoprev = 0
if stop in chlog.nodemap:
stoprev = chlog.rev(stop)
for r in range(chlog.count() - 1, -1, -1):
n = chlog.node(r)
if n not in p:
h.append(n)
if n == stop:
break
if r < stoprev:
break
for pn in chlog.parents(n):
p[pn] = 1
return h
def bundle(cg):
backupdir = repo.join("strip-backup")
if not os.path.isdir(backupdir):
os.mkdir(backupdir)
name = os.path.join(backupdir, "%s" % revlog.short(rev))
name = savename(name)
self.ui.warn("saving bundle to %s\n" % name)
# TODO, exclusive open
f = open(name, "wb")
try:
f.write("HG10")
z = bz2.BZ2Compressor(9)
while 1:
chunk = cg.read(4096)
if not chunk:
break
f.write(z.compress(chunk))
f.write(z.flush())
except:
os.unlink(name)
raise
f.close()
return name
def stripall(rev, revnum):
cl = repo.changelog
c = cl.read(rev)
mm = repo.manifest.read(c[0])
seen = {}
for x in xrange(revnum, cl.count()):
c = cl.read(cl.node(x))
for f in c[3]:
if f in seen:
continue
seen[f] = 1
if f in mm:
filerev = mm[f]
else:
filerev = 0
seen[f] = filerev
# we go in two steps here so the strip loop happens in a
# sensible order. When stripping many files, this helps keep
# our disk access patterns under control.
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 seen_list = seen.keys()
seen_list.sort()
for f in seen_list:
mason@suse.com
Add mq extension
r1808 ff = repo.file(f)
filerev = seen[f]
if filerev != 0:
if filerev in ff.nodemap:
filerev = ff.rev(filerev)
else:
filerev = 0
ff.strip(filerev, revnum)
if not wlock:
wlock = repo.wlock()
lock = repo.lock()
chlog = repo.changelog
# TODO delete the undo files, and handle undo of merge sets
pp = chlog.parents(rev)
revnum = chlog.rev(rev)
if update:
Chris Mason
mq: strip should not blow away local changes...
r2699 (c, a, r, d, u) = repo.changes(None, None)
if c or a or d or r:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("local changes found"))
mason@suse.com
Add mq extension
r1808 urev = self.qparents(repo, rev)
Matt Mackall
Introduce update helper functions: update, merge, clean, and revert
r2808 hg.clean(repo, urev, wlock=wlock)
mason@suse.com
Add mq extension
r1808 repo.dirstate.write()
# save is a list of all the branches we are truncating away
# that we actually want to keep. changegroup will be used
# to preserve them and add them back after the truncate
saveheads = []
savebases = {}
heads = limitheads(chlog, rev)
seen = {}
# search through all the heads, finding those where the revision
# we want to strip away is an ancestor. Also look for merges
# that might be turned into new heads by the strip.
while heads:
h = heads.pop()
n = h
while True:
seen[n] = 1
pp = chlog.parents(n)
if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
if pp[1] not in seen:
heads.append(pp[1])
if pp[0] == revlog.nullid:
break
if chlog.rev(pp[0]) < revnum:
break
n = pp[0]
if n == rev:
break
r = chlog.reachable(h, rev)
if rev not in r:
saveheads.append(h)
for x in r:
if chlog.rev(x) > revnum:
savebases[x] = 1
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 # create a changegroup for all the branches we need to keep
Benoit Boissinot
mq: unused variables, improper usage of 'is [not]', undefined variable
r2797 if backup == "all":
mason@suse.com
Add mq extension
r1808 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
bundle(backupch)
if saveheads:
backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
chgrpfile = bundle(backupch)
stripall(rev, revnum)
change = chlog.read(rev)
repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
chlog.strip(revnum, revnum)
if saveheads:
self.ui.status("adding branch\n")
commands.unbundle(self.ui, repo, chgrpfile, update=False)
Benoit Boissinot
mq: unused variables, improper usage of 'is [not]', undefined variable
r2797 if backup != "strip":
mason@suse.com
Add mq extension
r1808 os.unlink(chgrpfile)
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 def isapplied(self, patch):
"""returns (index, rev, patch)"""
for i in xrange(len(self.applied)):
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 a = self.applied[i]
if a.name == patch:
return (i, a.rev, a.name)
mason@suse.com
Add mq extension
r1808 return None
Chris Mason
mq: patch naming shortcuts...
r2696 # if the exact patch name does not exist, we try a few
# variations. If strict is passed, we try only #1
#
# 1) a number to indicate an offset in the series file
# 2) a unique substring of the patch name was given
# 3) patchname[-+]num to indicate an offset in the series file
def lookup(self, patch, strict=False):
Vadim Gelfer
qselect: add --pop, --reapply options
r2844 patch = patch and str(patch)
Chris Mason
mq: patch naming shortcuts...
r2696 def partial_name(s):
if s in self.series:
return s
Vadim Gelfer
mq: print matches if patch name not unique
r2765 matches = [x for x in self.series if s in x]
if len(matches) > 1:
self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
for m in matches:
self.ui.warn(' %s\n' % m)
return None
if matches:
return matches[0]
Chris Mason
mq: patch naming shortcuts...
r2696 if len(self.series) > 0 and len(self.applied) > 0:
if s == 'qtip':
return self.series[self.series_end()-1]
if s == 'qbase':
return self.series[0]
return None
mason@suse.com
Add mq extension
r1808 if patch == None:
return None
Chris Mason
mq: patch naming shortcuts...
r2696
# we don't want to return a partial match until we make
# sure the file name passed in does not exist (checked below)
res = partial_name(patch)
if res and res == patch:
return res
Vadim Gelfer
mq: add join method
r2819 if not os.path.isfile(self.join(patch)):
mason@suse.com
Add mq extension
r1808 try:
sno = int(patch)
except(ValueError, OverflowError):
Chris Mason
mq: patch naming shortcuts...
r2696 pass
else:
if sno < len(self.series):
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 return self.series[sno]
Chris Mason
mq: patch naming shortcuts...
r2696 if not strict:
# return any partial match made above
if res:
return res
minus = patch.rsplit('-', 1)
if len(minus) > 1:
res = partial_name(minus[0])
if res:
i = self.series.index(res)
try:
off = int(minus[1] or 1)
except(ValueError, OverflowError):
pass
else:
if i - off >= 0:
return self.series[i - off]
plus = patch.rsplit('+', 1)
if len(plus) > 1:
res = partial_name(plus[0])
if res:
i = self.series.index(res)
try:
off = int(plus[1] or 1)
except(ValueError, OverflowError):
pass
else:
if i + off < len(self.series):
return self.series[i + off]
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("patch %s not in series") % patch)
mason@suse.com
Add mq extension
r1808
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 def push(self, repo, patch=None, force=False, list=False,
mason@suse.com
Add mq extension
r1808 mergeq=None, wlock=None):
if not wlock:
wlock = repo.wlock()
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 patch = self.lookup(patch)
mason@suse.com
Add mq extension
r1808 if patch and self.isapplied(patch):
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 self.ui.warn(_("patch %s is already applied\n") % patch)
mason@suse.com
Add mq extension
r1808 sys.exit(1)
if self.series_end() == len(self.series):
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 self.ui.warn(_("patch series fully applied\n"))
mason@suse.com
Add mq extension
r1808 sys.exit(1)
if not force:
self.check_localchanges(repo)
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 self.applied_dirty = 1;
start = self.series_end()
if start > 0:
self.check_toppatch(repo)
if not patch:
patch = self.series[start]
end = start + 1
else:
end = self.series.index(patch, start) + 1
s = self.series[start:end]
if mergeq:
ret = self.mergepatch(repo, mergeq, s, wlock)
else:
ret = self.apply(repo, s, list, wlock=wlock)
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 top = self.applied[-1].name
mason@suse.com
Add mq extension
r1808 if ret[0]:
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 self.ui.write("Errors during apply, please fix and refresh %s\n" %
mason@suse.com
Add mq extension
r1808 top)
else:
self.ui.write("Now at: %s\n" % top)
return ret[0]
Chris Mason
mq: qpop should act like quilt pop...
r2697 def pop(self, repo, patch=None, force=False, update=True, all=False,
wlock=None):
mason@suse.com
Add mq extension
r1808 def getfile(f, rev):
t = repo.file(f).read(rev)
try:
repo.wfile(f, "w").write(t)
except IOError:
Vadim Gelfer
mq: do not fail if directory to create exists
r2086 try:
os.makedirs(os.path.dirname(repo.wjoin(f)))
except OSError, err:
if err.errno != errno.EEXIST: raise
mason@suse.com
Add mq extension
r1808 repo.wfile(f, "w").write(t)
if not wlock:
wlock = repo.wlock()
if patch:
# index, rev, patch
info = self.isapplied(patch)
if not info:
patch = self.lookup(patch)
info = self.isapplied(patch)
if not info:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("patch %s is not applied") % patch)
mason@suse.com
Add mq extension
r1808 if len(self.applied) == 0:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 self.ui.warn(_("no patches applied\n"))
mason@suse.com
Add mq extension
r1808 sys.exit(1)
if not update:
parents = repo.dirstate.parents()
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 rr = [ revlog.bin(x.rev) for x in self.applied ]
mason@suse.com
Add mq extension
r1808 for p in parents:
if p in rr:
self.ui.warn("qpop: forcing dirstate update\n")
update = True
if not force and update:
self.check_localchanges(repo)
self.applied_dirty = 1;
end = len(self.applied)
if not patch:
Chris Mason
mq: qpop should act like quilt pop...
r2697 if all:
popi = 0
else:
popi = len(self.applied) - 1
else:
popi = info[0] + 1
if popi >= end:
self.ui.warn("qpop: %s is already at the top\n" % patch)
return
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
Chris Mason
mq: qpop should act like quilt pop...
r2697
mason@suse.com
Add mq extension
r1808 start = info[0]
rev = revlog.bin(info[1])
# we know there are no local changes, so we can make a simplified
# form of hg.update.
if update:
top = self.check_toppatch(repo)
qp = self.qparents(repo, rev)
changes = repo.changelog.read(qp)
mf1 = repo.manifest.readflags(changes[0])
mmap = repo.manifest.read(changes[0])
(c, a, r, d, u) = repo.changes(qp, top)
if d:
raise util.Abort("deletions found between repo revs")
for f in c:
getfile(f, mmap[f])
for f in r:
getfile(f, mmap[f])
util.set_exec(repo.wjoin(f), mf1[f])
repo.dirstate.update(c + r, 'n')
for f in a:
try: os.unlink(repo.wjoin(f))
except: raise
try: os.removedirs(os.path.dirname(repo.wjoin(f)))
except: pass
if a:
repo.dirstate.forget(a)
repo.dirstate.setparents(qp, revlog.nullid)
self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
del self.applied[start:end]
if len(self.applied):
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 self.ui.write("Now at: %s\n" % self.applied[-1].name)
mason@suse.com
Add mq extension
r1808 else:
self.ui.write("Patch queue now empty\n")
def diff(self, repo, files):
top = self.check_toppatch(repo)
if not top:
self.ui.write("No patches applied\n")
return
qp = self.qparents(repo, top)
commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 def refresh(self, repo, msg=None, short=False):
mason@suse.com
Add mq extension
r1808 if len(self.applied) == 0:
self.ui.write("No patches applied\n")
return
wlock = repo.wlock()
self.check_toppatch(repo)
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
mason@suse.com
Add mq extension
r1808 top = revlog.bin(top)
cparents = repo.changelog.parents(top)
patchparent = self.qparents(repo, top)
Danek Duvall
Add timestamp field to export format. Make import and mq use it.
r2299 message, comments, user, date, patchfound = self.readheaders(patch)
mason@suse.com
Add mq extension
r1808
Thomas Arendsen Hein
Fix mq's usage of opener, which don't allow absolute paths now.
r1852 patchf = self.opener(patch, "w")
Brendan Cully
Change patch header as well as commit message with qrefresh -m or -l.
r2745 msg = msg.rstrip()
if msg:
if comments:
# Remove existing message.
ci = 0
for mi in range(len(message)):
while message[mi] != comments[ci]:
ci += 1
del comments[ci]
comments.append(msg)
mason@suse.com
Add mq extension
r1808 if comments:
comments = "\n".join(comments) + '\n\n'
patchf.write(comments)
tip = repo.changelog.tip()
if top == tip:
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 # if the top of our patch queue is also the tip, there is an
mason@suse.com
Add mq extension
r1808 # optimization here. We update the dirstate in place and strip
# off the tip commit. Then just commit the current directory
# tree. We can also send repo.commit the list of files
# changed to speed up the diff
#
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 # in short mode, we only diff the files included in the
mason@suse.com
Add mq extension
r1808 # patch already
#
# this should really read:
#(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
# but we do it backwards to take advantage of manifest/chlog
# caching against the next repo.changes call
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 #
mason@suse.com
Add mq extension
r1808 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
if short:
filelist = cc + aa + dd
else:
filelist = None
(c, a, r, d, u) = repo.changes(None, None, filelist)
# we might end up with files that were added between tip and
# the dirstate parent, but then changed in the local dirstate.
# in this case, we want them to only show up in the added section
for x in c:
if x not in aa:
cc.append(x)
# we might end up with files added by the local dirstate that
# were deleted by the patch. In this case, they should only
# show up in the changed section.
for x in a:
if x in dd:
del dd[dd.index(x)]
cc.append(x)
else:
aa.append(x)
# make sure any files deleted in the local dirstate
# are not in the add or change column of the patch
forget = []
for x in d + r:
if x in aa:
del aa[aa.index(x)]
forget.append(x)
continue
elif x in cc:
del cc[cc.index(x)]
dd.append(x)
c = list(util.unique(cc))
r = list(util.unique(dd))
a = list(util.unique(aa))
filelist = list(util.unique(c + r + a ))
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 commands.dodiff(patchf, self.ui, repo, patchparent, None,
mason@suse.com
Add mq extension
r1808 filelist, changes=(c, a, r, [], u))
patchf.close()
changes = repo.changelog.read(tip)
repo.dirstate.setparents(*cparents)
repo.dirstate.update(a, 'a')
repo.dirstate.update(r, 'r')
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 repo.dirstate.update(c, 'n')
mason@suse.com
Add mq extension
r1808 repo.dirstate.forget(forget)
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 if not msg:
if not message:
message = "patch queue: %s\n" % patch
else:
message = "\n".join(message)
mason@suse.com
Add mq extension
r1808 else:
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 message = msg
mason@suse.com
Add mq extension
r1808 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 self.applied[-1] = statusentry(revlog.hex(n), patch)
mason@suse.com
Add mq extension
r1808 self.applied_dirty = 1
else:
commands.dodiff(patchf, self.ui, repo, patchparent, None)
patchf.close()
self.pop(repo, force=True, wlock=wlock)
self.push(repo, force=True, wlock=wlock)
def init(self, repo, create=False):
if os.path.isdir(self.path):
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("patch queue directory already exists"))
mason@suse.com
Add mq extension
r1808 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:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("patch %s is not in series file") % patch)
mason@suse.com
Add mq extension
r1808 if not patch:
start = self.series_end()
else:
start = self.series.index(patch) + 1
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 unapplied = []
for i in xrange(start, len(self.series)):
pushable, reason = self.pushable(i)
if pushable:
unapplied.append((i, self.series[i]))
self.explain_pushable(i)
return unapplied
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
Brendan Cully
Add -s option to qseries: display first line of patch header.
r2756 def qseries(self, repo, missing=None, summary=False):
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 start = self.series_end(all_patches=True)
mason@suse.com
Add mq extension
r1808 if not missing:
Brendan Cully
Add -s option to qseries: display first line of patch header.
r2756 for i in range(len(self.series)):
patch = self.series[i]
mason@suse.com
Add mq extension
r1808 if self.ui.verbose:
Brendan Cully
Add -s option to qseries: display first line of patch header.
r2756 if i < start:
status = 'A'
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 elif self.pushable(i)[0]:
status = 'U'
Brendan Cully
Add -s option to qseries: display first line of patch header.
r2756 else:
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 status = 'G'
Brendan Cully
Add -s option to qseries: display first line of patch header.
r2756 self.ui.write('%d %s ' % (i, status))
if summary:
msg = self.readheaders(patch)[0]
msg = msg and ': ' + msg[0] or ': '
else:
msg = ''
self.ui.write('%s%s\n' % (patch, msg))
mason@suse.com
Add mq extension
r1808 else:
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 msng_list = []
mason@suse.com
Add mq extension
r1808 for root, dirs, files in os.walk(self.path):
d = root[len(self.path) + 1:]
for f in files:
fl = os.path.join(d, f)
Thomas Arendsen Hein
Fix mq's usage of opener, which don't allow absolute paths now.
r1852 if (fl not in self.series and
fl not in (self.status_path, self.series_path)
and not fl.startswith('.')):
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 msng_list.append(fl)
msng_list.sort()
Benoit Boissinot
mq: remove unecessary test
r2795 for x in msng_list:
if self.ui.verbose:
self.ui.write("D ")
self.ui.write("%s\n" % x)
mason@suse.com
Add mq extension
r1808
def issaveline(self, l):
Brendan Cully
Update qsave to use StatusEntry; don't throw exception on bad status lines.
r2816 if l.name == '.hg.patches.save.line':
mason@suse.com
Add mq extension
r1808 return True
def qrepo(self, create=False):
Vadim Gelfer
mq: add join method
r2819 if create or os.path.isdir(self.join(".hg")):
Thomas Arendsen Hein
Create local ui object per repository, so .hg/hgrc don't get mixed....
r1839 return hg.repository(self.ui, path=self.path, create=create)
mason@suse.com
Add mq extension
r1808
def restore(self, repo, rev, delete=None, qupdate=None):
c = repo.changelog.read(rev)
desc = c[4].strip()
lines = desc.splitlines()
i = 0
datastart = None
series = []
applied = []
qpp = None
for i in xrange(0, len(lines)):
if lines[i] == 'Patch Data:':
datastart = i + 1
elif lines[i].startswith('Dirstate:'):
l = lines[i].rstrip()
l = l[10:].split(' ')
qpp = [ hg.bin(x) for x in l ]
elif datastart != None:
l = lines[i].rstrip()
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 se = statusentry(l)
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 file_ = se.name
if se.rev:
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 applied.append(se)
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 series.append(file_)
mason@suse.com
Add mq extension
r1808 if datastart == None:
self.ui.warn("No saved patch data found\n")
return 1
self.ui.warn("restoring status: %s\n" % lines[0])
self.full_series = series
self.applied = applied
Vadim Gelfer
mq: rename read_series as parse_series, make simpler and faster
r2767 self.parse_series()
mason@suse.com
Add mq extension
r1808 self.series_dirty = 1
self.applied_dirty = 1
heads = repo.changelog.heads()
if delete:
if rev not in heads:
self.ui.warn("save entry has children, leaving it alone\n")
else:
self.ui.warn("removing save entry %s\n" % hg.short(rev))
pp = repo.dirstate.parents()
if rev in pp:
update = True
else:
update = False
self.strip(repo, rev, update=update, backup='strip')
if qpp:
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 self.ui.warn("saved queue repository parents: %s %s\n" %
mason@suse.com
Add mq extension
r1808 (hg.short(qpp[0]), hg.short(qpp[1])))
if qupdate:
print "queue directory updating"
r = self.qrepo()
if not r:
self.ui.warn("Unable to load queue repository\n")
return 1
Matt Mackall
Introduce update helper functions: update, merge, clean, and revert
r2808 hg.clean(r, qpp[0])
mason@suse.com
Add mq extension
r1808
def save(self, repo, msg=None):
if len(self.applied) == 0:
self.ui.warn("save: no patches applied, exiting\n")
return 1
if self.issaveline(self.applied[-1]):
self.ui.warn("status is already saved\n")
return 1
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 ar = [ ':' + x for x in self.full_series ]
if not msg:
msg = "hg patches saved state"
else:
msg = "hg patches: " + msg.rstrip('\r\n')
r = self.qrepo()
if r:
pp = r.dirstate.parents()
msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
msg += "\n\nPatch Data:\n"
Brendan Cully
Update qsave to use StatusEntry; don't throw exception on bad status lines.
r2816 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
"\n".join(ar) + '\n' or "")
mason@suse.com
Add mq extension
r1808 n = repo.commit(None, text, user=None, force=1)
if not n:
self.ui.warn("repo commit failed\n")
return 1
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
mason@suse.com
Add mq extension
r1808 self.applied_dirty = 1
Chris Mason
mq: fix qnew and qimport to deal with series file comments...
r2698 def full_series_end(self):
if len(self.applied) > 0:
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 p = self.applied[-1].name
Chris Mason
mq: fix qnew and qimport to deal with series file comments...
r2698 end = self.find_series(p)
if end == None:
return len(self.full_series)
return end + 1
return 0
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 def series_end(self, all_patches=False):
mason@suse.com
Add mq extension
r1808 end = 0
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 def next(start):
if all_patches:
return start
i = start
while i < len(self.series):
p, reason = self.pushable(i)
if p:
break
self.explain_pushable(i)
i += 1
return i
mason@suse.com
Add mq extension
r1808 if len(self.applied) > 0:
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 p = self.applied[-1].name
mason@suse.com
Add mq extension
r1808 try:
end = self.series.index(p)
except ValueError:
return 0
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 return next(end + 1)
return next(end)
mason@suse.com
Add mq extension
r1808
def qapplied(self, repo, patch=None):
if patch and patch not in self.series:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("patch %s is not in series file") % patch)
mason@suse.com
Add mq extension
r1808 if not patch:
end = len(self.applied)
else:
end = self.series.index(patch) + 1
for x in xrange(end):
p = self.appliedname(x)
self.ui.write("%s\n" % p)
def appliedname(self, index):
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 pname = self.applied[index].name
mason@suse.com
Add mq extension
r1808 if not self.ui.verbose:
"Mathieu Clabaut "
mq: uniform verbose display of patche[s]....
r2677 p = pname
else:
p = str(self.series.index(pname)) + " " + p
mason@suse.com
Add mq extension
r1808 return p
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 def top(self, repo):
if len(self.applied):
p = self.appliedname(-1)
self.ui.write(p + '\n')
else:
self.ui.write("No patches applied\n")
def next(self, repo):
end = self.series_end()
if end == len(self.series):
self.ui.write("All patches applied\n")
else:
"Mathieu Clabaut "
mq: uniform verbose display of patche[s]....
r2677 p = self.series[end]
if self.ui.verbose:
self.ui.write("%d " % self.series.index(p))
self.ui.write(p + '\n')
mason@suse.com
Add mq extension
r1808
def prev(self, repo):
if len(self.applied) > 1:
p = self.appliedname(-2)
self.ui.write(p + '\n')
elif len(self.applied) == 1:
self.ui.write("Only one patch applied\n")
else:
self.ui.write("No patches applied\n")
def qimport(self, repo, files, patch=None, existing=None, force=None):
if len(files) > 1 and patch:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_('option "-n" not valid when importing multiple '
'files'))
mason@suse.com
Add mq extension
r1808 i = 0
Vadim Gelfer
mq: add qimported patches if patch dir is a repo
r2488 added = []
mason@suse.com
Add mq extension
r1808 for filename in files:
if existing:
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 if not patch:
mason@suse.com
Add mq extension
r1808 patch = filename
Vadim Gelfer
mq: add join method
r2819 if not os.path.isfile(self.join(patch)):
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("patch %s does not exist") % patch)
mason@suse.com
Add mq extension
r1808 else:
try:
text = file(filename).read()
except IOError:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_("unable to read %s") % patch)
mason@suse.com
Add mq extension
r1808 if not patch:
patch = os.path.split(filename)[1]
Vadim Gelfer
mq: add join method
r2819 if not force and os.path.exists(self.join(patch)):
Vadim Gelfer
mq: do not allow to qnew a patch twice
r2711 raise util.Abort(_('patch "%s" already exists') % patch)
Thomas Arendsen Hein
Fix mq's usage of opener, which don't allow absolute paths now.
r1852 patchf = self.opener(patch, "w")
mason@suse.com
Add mq extension
r1808 patchf.write(text)
if patch in self.series:
Vadim Gelfer
mq: do not allow to qnew a patch twice
r2711 raise util.Abort(_('patch %s is already in the series file')
% patch)
Chris Mason
mq: fix qnew and qimport to deal with series file comments...
r2698 index = self.full_series_end() + i
mason@suse.com
Add mq extension
r1808 self.full_series[index:index] = [patch]
Vadim Gelfer
mq: rename read_series as parse_series, make simpler and faster
r2767 self.parse_series()
mason@suse.com
Add mq extension
r1808 self.ui.warn("adding %s to series file\n" % patch)
i += 1
Vadim Gelfer
mq: add qimported patches if patch dir is a repo
r2488 added.append(patch)
mason@suse.com
Add mq extension
r1808 patch = None
self.series_dirty = 1
Vadim Gelfer
mq: add qimported patches if patch dir is a repo
r2488 qrepo = self.qrepo()
if qrepo:
qrepo.add(added)
mason@suse.com
Add mq extension
r1808
def delete(ui, repo, patch, **opts):
Brendan Cully
Add -f option to qdelete, to remove patch file.
r2752 """remove a patch from the series file
The patch must not be applied.
With -f, deletes the patch file as well as the series entry."""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
Brendan Cully
Add -f option to qdelete, to remove patch file.
r2752 q.delete(repo, patch, force=opts.get('force'))
mason@suse.com
Add mq extension
r1808 q.save_dirty()
return 0
def applied(ui, repo, patch=None, **opts):
"""print the patches already applied"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 repo.mq.qapplied(repo, patch)
mason@suse.com
Add mq extension
r1808 return 0
def unapplied(ui, repo, patch=None, **opts):
"""print the patches not yet applied"""
Vadim Gelfer
mq: make queue.unapplied useful as api
r2779 for i, p in repo.mq.unapplied(repo, patch):
if ui.verbose:
ui.write("%d " % i)
ui.write("%s\n" % p)
mason@suse.com
Add mq extension
r1808
def qimport(ui, repo, *filename, **opts):
"""import a patch"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 q.qimport(repo, filename, patch=opts['name'],
existing=opts['existing'], force=opts['force'])
mason@suse.com
Add mq extension
r1808 q.save_dirty()
return 0
def init(ui, repo, **opts):
Brendan Cully
Add more verbose help text to mq commands.
r2754 """init a new queue repository
The queue repository is unversioned by default. If -c is
specified, qinit will create a separate nested repository
for patches. Use qcommit to commit changes to this queue
repository."""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
mason@suse.com
Add mq extension
r1808 r = q.init(repo, create=opts['create_repo'])
q.save_dirty()
if r:
fp = r.wopener('.hgignore', 'w')
print >> fp, 'syntax: glob'
print >> fp, 'status'
fp.close()
r.wopener('series', 'w').close()
r.add(['.hgignore', 'series'])
return 0
Vadim Gelfer
mq: add qclone command
r2720 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.
'''
Bryan O'Sullivan
fix call to commands.setremoteconfig
r2766 commands.setremoteconfig(ui, opts)
Vadim Gelfer
mq: add qclone command
r2720 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)
Vadim Gelfer
mq: update to handle repomap not longer used
r2725 if sr.mq.applied:
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 qbase = revlog.bin(sr.mq.applied[0].rev)
Vadim Gelfer
mq: add qclone command
r2720 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)
Vadim Gelfer
mq: update to handle repomap not longer used
r2725 dr.mq.strip(dr, qbase, update=False, backup=None)
Vadim Gelfer
mq: add qclone command
r2720 if not opts['noupdate']:
ui.note(_('updating destination repo\n'))
Matt Mackall
Move merge code to its own module...
r2775 hg.update(dr, dr.changelog.tip())
Vadim Gelfer
mq: add qclone command
r2720
mason@suse.com
Add mq extension
r1808 def commit(ui, repo, *pats, **opts):
Thomas Arendsen Hein
mq: Added help for qcommit, consistently talk about queue repository.
r2526 """commit changes in the queue repository"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
mason@suse.com
Add mq extension
r1808 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"""
Brendan Cully
Add -s option to qseries: display first line of patch header.
r2756 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
mason@suse.com
Add mq extension
r1808 return 0
def top(ui, repo, **opts):
"""print the name of the current patch"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 repo.mq.top(repo)
mason@suse.com
Add mq extension
r1808 return 0
def next(ui, repo, **opts):
"""print the name of the next patch"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 repo.mq.next(repo)
mason@suse.com
Add mq extension
r1808 return 0
def prev(ui, repo, **opts):
"""print the name of the previous patch"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 repo.mq.prev(repo)
mason@suse.com
Add mq extension
r1808 return 0
def new(ui, repo, patch, **opts):
Brendan Cully
Add more verbose help text to mq commands.
r2754 """create a new patch
qnew creates a new patch on top of the currently-applied patch
(if any). It will refuse to run if there are any outstanding
changes unless -f is specified, in which case the patch will
be initialised with them.
-m or -l set the patch header as well as the commit message.
If neither is specified, the patch header is empty and the
Brendan Cully
Clean up qnew help text.
r2770 commit message is 'New patch: PATCH'"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
Brendan Cully
Update mq to use new logmessage arglist from 2794:bd8a9a94139f
r2804 message = commands.logmessage(opts)
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 q.new(repo, patch, msg=message, force=opts['force'])
mason@suse.com
Add mq extension
r1808 q.save_dirty()
return 0
def refresh(ui, repo, **opts):
"""update the current patch"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
Brendan Cully
Update mq to use new logmessage arglist from 2794:bd8a9a94139f
r2804 message = commands.logmessage(opts)
Brendan Cully
Add option -e/--edit to qrefresh, to edit the existing header.
r2746 if opts['edit']:
if message:
raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 patch = q.applied[-1].name
Brendan Cully
Add option -e/--edit to qrefresh, to edit the existing header.
r2746 (message, comment, user, date, hasdiff) = q.readheaders(patch)
message = ui.edit('\n'.join(message), user or ui.username())
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 q.refresh(repo, msg=message, short=opts['short'])
mason@suse.com
Add mq extension
r1808 q.save_dirty()
return 0
def diff(ui, repo, *files, **opts):
"""diff of the current patch"""
Alexis S. L. Carvalho
Fix hg qdiff <file>
r2097 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 repo.mq.diff(repo, list(files))
mason@suse.com
Add mq extension
r1808 return 0
Brendan Cully
Add -m, -l, -e options to qfold.
r2753 def fold(ui, repo, *files, **opts):
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 """fold the named patches into the current patch
Brendan Cully
Add -m, -l, -e options to qfold.
r2753
Brendan Cully
Add -f option to qfold; improve qfold documentation.
r2771 Patches must not yet be applied. Each patch will be successively
applied to the current patch in the order given. If all the
patches apply successfully, the current patch will be refreshed
with the new cumulative patch, and the folded patches will
be deleted. With -f/--force, the folded patch files will
be removed afterwards.
Brendan Cully
Add -m, -l, -e options to qfold.
r2753 The header for each folded patch will be concatenated with
the current patch header, separated by a line of '* * *'."""
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 q = repo.mq
if not files:
raise util.Abort(_('qfold requires at least one patch name'))
if not q.check_toppatch(repo):
raise util.Abort(_('No patches applied\n'))
Brendan Cully
Update mq to use new logmessage arglist from 2794:bd8a9a94139f
r2804 message = commands.logmessage(opts)
Brendan Cully
Add -m, -l, -e options to qfold.
r2753 if opts['edit']:
if message:
raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 parent = q.lookup('qtip')
patches = []
messages = []
for f in files:
patch = q.lookup(f)
if patch in patches or patch == parent:
Benoit Boissinot
mq: unused variables, improper usage of 'is [not]', undefined variable
r2797 ui.warn(_('Skipping already folded patch %s') % patch)
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 if q.isapplied(patch):
raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
patches.append(patch)
for patch in patches:
Brendan Cully
Add -m, -l, -e options to qfold.
r2753 if not message:
messages.append(q.readheaders(patch)[0])
Vadim Gelfer
mq: add join method
r2819 pf = q.join(patch)
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748 (patchsuccess, files, fuzz) = q.patch(repo, pf)
if not patchsuccess:
raise util.Abort(_('Error folding patch %s') % patch)
Brendan Cully
Add -m, -l, -e options to qfold.
r2753 if not message:
message, comments, user = q.readheaders(parent)[0:3]
for msg in messages:
message.append('* * *')
message.extend(msg)
message = '\n'.join(message)
if opts['edit']:
message = ui.edit(message, user or ui.username())
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748
q.refresh(repo, msg=message)
for patch in patches:
Brendan Cully
Add -f option to qfold; improve qfold documentation.
r2771 q.delete(repo, patch, force=opts['force'])
Brendan Cully
New mq command qfold: Merge patches into the current patch....
r2748
q.save_dirty()
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 def guard(ui, repo, *args, **opts):
'''set or print guards for a patch
guards control whether a patch can be pushed. a patch with no
guards is aways pushed. a patch with posative guard ("+foo") is
pushed only if qselect command enables guard "foo". a patch with
nagative guard ("-foo") is never pushed if qselect command enables
guard "foo".
with no arguments, default is to print current active guards.
with arguments, set active guards for patch.
to set nagative guard "-foo" on topmost patch ("--" is needed so
hg will not interpret "-foo" as argument):
hg qguard -- -foo
to set guards on other patch:
hg qguard other.patch +2.6.17 -stable
'''
def status(idx):
guards = q.series_guards[idx] or ['unguarded']
ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
q = repo.mq
patch = None
args = list(args)
if opts['list']:
if args or opts['none']:
raise util.Abort(_('cannot mix -l/--list with options or arguments'))
for i in xrange(len(q.series)):
status(i)
return
if not args or args[0][0:1] in '-+':
if not q.applied:
raise util.Abort(_('no patches applied'))
patch = q.applied[-1].name
if patch is None and args[0][0:1] not in '-+':
patch = args.pop(0)
if patch is None:
raise util.Abort(_('no patch to work with'))
if args or opts['none']:
q.set_guards(q.find_series(patch), args)
q.save_dirty()
else:
status(q.series.index(q.lookup(patch)))
Brendan Cully
Add command qheader to display the header of a given patch.
r2747 def header(ui, repo, patch=None):
"""Print the header of the topmost or specified patch"""
q = repo.mq
if patch:
patch = q.lookup(patch)
else:
if not q.applied:
ui.write('No patches applied\n')
return
patch = q.lookup('qtip')
message = repo.mq.readheaders(patch)[0]
ui.write('\n'.join(message) + '\n')
mason@suse.com
Add mq extension
r1808 def lastsavename(path):
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 (directory, base) = os.path.split(path)
names = os.listdir(directory)
mason@suse.com
Add mq extension
r1808 namere = re.compile("%s.([0-9]+)" % base)
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 maxindex = None
mason@suse.com
Add mq extension
r1808 maxname = None
for f in names:
m = namere.match(f)
if m:
index = int(m.group(1))
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 if maxindex == None or index > maxindex:
maxindex = index
mason@suse.com
Add mq extension
r1808 maxname = f
if maxname:
Benoit Boissinot
mq: fix variables shadowing builtin
r2794 return (os.path.join(directory, maxname), maxindex)
mason@suse.com
Add mq extension
r1808 return (None, None)
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 def savename(path):
(last, index) = lastsavename(path)
if last is None:
index = 0
newpath = path + ".%d" % (index + 1)
return newpath
def push(ui, repo, patch=None, **opts):
"""push the next patch onto the stack"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
mason@suse.com
Add mq extension
r1808 mergeq = None
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 if opts['all']:
patch = q.series[-1]
if opts['merge']:
if opts['name']:
newpath = opts['name']
else:
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 newpath, i = lastsavename(q.path)
mason@suse.com
Add mq extension
r1808 if not newpath:
ui.warn("no saved queues found, please use -n\n")
return 1
mergeq = queue(ui, repo.join(""), newpath)
ui.warn("merging with queue at: %s\n" % mergeq.path)
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
mason@suse.com
Add mq extension
r1808 mergeq=mergeq)
q.save_dirty()
return ret
def pop(ui, repo, patch=None, **opts):
"""pop the current patch off the stack"""
localupdate = True
if opts['name']:
q = queue(ui, repo.join(""), repo.join(opts['name']))
ui.warn('using patch queue: %s\n' % q.path)
localupdate = False
else:
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
Chris Mason
mq: qpop should act like quilt pop...
r2697 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
mason@suse.com
Add mq extension
r1808 q.save_dirty()
return 0
Brendan Cully
New self-explanatory command qrename.
r2750 def rename(ui, repo, patch, name=None, **opts):
"""rename a patch
With one argument, renames the current patch to PATCH1.
With two arguments, renames PATCH1 to PATCH2."""
q = repo.mq
if not name:
name = patch
patch = None
if name in q.series:
raise util.Abort(_('A patch named %s already exists in the series file') % name)
Vadim Gelfer
mq: add join method
r2819 absdest = q.join(name)
Brendan Cully
New self-explanatory command qrename.
r2750 if os.path.exists(absdest):
raise util.Abort(_('%s already exists') % absdest)
if patch:
patch = q.lookup(patch)
else:
if not q.applied:
ui.write(_('No patches applied\n'))
return
patch = q.lookup('qtip')
if ui.verbose:
ui.write('Renaming %s to %s\n' % (patch, name))
i = q.find_series(patch)
q.full_series[i] = name
Vadim Gelfer
mq: rename read_series as parse_series, make simpler and faster
r2767 q.parse_series()
Brendan Cully
New self-explanatory command qrename.
r2750 q.series_dirty = 1
info = q.isapplied(patch)
if info:
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 q.applied[info[0]] = statusentry(info[1], name)
Brendan Cully
New self-explanatory command qrename.
r2750 q.applied_dirty = 1
Vadim Gelfer
mq: add join method
r2819 util.rename(q.join(patch), absdest)
Brendan Cully
New self-explanatory command qrename.
r2750 r = q.qrepo()
if r:
wlock = r.wlock()
if r.dirstate.state(name) == 'r':
r.undelete([name], wlock)
r.copy(patch, name, wlock)
r.remove([patch], False, wlock)
q.save_dirty()
mason@suse.com
Add mq extension
r1808 def restore(ui, repo, rev, **opts):
"""restore the queue state saved by a rev"""
rev = repo.lookup(rev)
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
mason@suse.com
Add mq extension
r1808 q.restore(repo, rev, delete=opts['delete'],
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 qupdate=opts['update'])
mason@suse.com
Add mq extension
r1808 q.save_dirty()
return 0
def save(ui, repo, **opts):
"""save current queue state"""
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = repo.mq
Brendan Cully
Update mq to use new logmessage arglist from 2794:bd8a9a94139f
r2804 message = commands.logmessage(opts)
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 ret = q.save(repo, msg=message)
mason@suse.com
Add mq extension
r1808 if ret:
return ret
q.save_dirty()
if opts['copy']:
path = q.path
if opts['name']:
newpath = os.path.join(q.basepath, opts['name'])
if os.path.exists(newpath):
if not os.path.isdir(newpath):
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_('destination %s exists and is not '
'a directory') % newpath)
mason@suse.com
Add mq extension
r1808 if not opts['force']:
Vadim Gelfer
mq: move many error messages to util.Abort
r2712 raise util.Abort(_('destination %s exists, '
'use -f to force') % newpath)
mason@suse.com
Add mq extension
r1808 else:
newpath = savename(path)
ui.warn("copy %s to %s\n" % (path, newpath))
util.copyfiles(path, newpath)
if opts['empty']:
try:
Vadim Gelfer
mq: add join method
r2819 os.unlink(q.join(q.status_path))
mason@suse.com
Add mq extension
r1808 except:
pass
return 0
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810
mason@suse.com
Add mq extension
r1808 def strip(ui, repo, rev, **opts):
"""strip a revision and all later revs on the same branch"""
rev = repo.lookup(rev)
backup = 'all'
if opts['backup']:
backup = 'strip'
elif opts['nobackup']:
backup = 'none'
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 repo.mq.strip(repo, rev, backup=backup)
mason@suse.com
Add mq extension
r1808 return 0
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 def select(ui, repo, *args, **opts):
'''set or print guarded patches to push
use qguard command to set or print guards on patch. then use
qselect to tell mq which guards to use. example:
qguard foo.patch -stable (nagative guard)
qguard bar.patch +stable (posative guard)
qselect stable
this sets "stable" guard. mq will skip foo.patch (because it has
nagative match) but push bar.patch (because it has posative
Vadim Gelfer
mq: apply patch is any posative guard matches...
r2850 match). patch is pushed if any posative guards match and no
Vadim Gelfer
mq: make guards more strict, add tests
r2829 nagative guards match.
Vadim Gelfer
mq: new commands qselect, qguard...
r2821
with no arguments, default is to print current active guards.
with arguments, set active guards as given.
use -n/--none to deactivate guards (no other arguments needed).
when no guards active, patches with posative guards are skipped,
patches with nagative guards are pushed.
Vadim Gelfer
qselect: add --pop, --reapply options
r2844 qselect can change guards of applied patches. it does not pop
guarded patches by default. use --pop to pop back to last applied
patch that is not guarded. use --reapply (implies --pop) to push
back to current patch afterwards, but skip guarded patches.
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 use -s/--series to print list of all guards in series file (no
other arguments needed). use -v for more information.'''
q = repo.mq
guards = q.active()
if args or opts['none']:
Vadim Gelfer
qselect: add --pop, --reapply options
r2844 old_unapplied = q.unapplied(repo)
old_guarded = [i for i in xrange(len(q.applied)) if
not q.pushable(i)[0]]
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 q.set_active(args)
q.save_dirty()
if not args:
ui.status(_('guards deactivated\n'))
Vadim Gelfer
qselect: add --pop, --reapply options
r2844 if not opts['pop'] and not opts['reapply']:
unapplied = q.unapplied(repo)
guarded = [i for i in xrange(len(q.applied))
if not q.pushable(i)[0]]
if len(unapplied) != len(old_unapplied):
ui.status(_('number of unguarded, unapplied patches has '
'changed from %d to %d\n') %
(len(old_unapplied), len(unapplied)))
if len(guarded) != len(old_guarded):
ui.status(_('number of guarded, applied patches has changed '
'from %d to %d\n') %
(len(old_guarded), len(guarded)))
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 elif opts['series']:
guards = {}
noguards = 0
for gs in q.series_guards:
if not gs:
noguards += 1
for g in gs:
guards.setdefault(g, 0)
guards[g] += 1
if ui.verbose:
guards['NONE'] = noguards
guards = guards.items()
guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
if guards:
ui.note(_('guards in series file:\n'))
for guard, count in guards:
ui.note('%2d ' % count)
ui.write(guard, '\n')
else:
ui.note(_('no guards in series file\n'))
else:
if guards:
ui.note(_('active guards:\n'))
for g in guards:
ui.write(g, '\n')
else:
ui.write(_('no active guards\n'))
Vadim Gelfer
qselect: add --pop, --reapply options
r2844 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
popped = False
if opts['pop'] or opts['reapply']:
for i in xrange(len(q.applied)):
pushable, reason = q.pushable(i)
if not pushable:
ui.status(_('popping guarded patches\n'))
popped = True
if i == 0:
q.pop(repo, all=True)
else:
q.pop(repo, i-1)
break
if popped:
try:
if reapply:
ui.status(_('reapplying unguarded patches\n'))
q.push(repo, reapply)
finally:
q.save_dirty()
Vadim Gelfer
mq: new commands qselect, qguard...
r2821
mason@suse.com
Add mq extension
r1808 def reposetup(ui, repo):
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 class mqrepo(repo.__class__):
Vadim Gelfer
mq: do not allow to push from repo with patches applied
r2848 def abort_if_wdir_patched(self, errmsg, force=False):
if self.mq.applied and not force:
parent = revlog.hex(self.dirstate.parents()[0])
if parent in [s.rev for s in self.mq.applied]:
raise util.Abort(errmsg)
Brendan Cully
Disallow commit over an applied mq patch.
r2845 def commit(self, *args, **opts):
if len(args) >= 6:
force = args[5]
else:
force = opts.get('force')
Vadim Gelfer
mq: do not allow to push from repo with patches applied
r2848 self.abort_if_wdir_patched(
_('cannot commit over an applied mq patch'),
force)
Brendan Cully
Disallow commit over an applied mq patch.
r2845
return super(mqrepo, self).commit(*args, **opts)
Vadim Gelfer
mq: do not allow to push from repo with patches applied
r2848 def push(self, remote, force=False, revs=None):
if self.mq.applied and not force:
raise util.Abort(_('source has mq patches applied'))
return super(mqrepo, self).push(remote, force, revs)
Brendan Cully
mq: do not hold a reference to repo in tags override...
r2723 def tags(self):
if self.tagscache:
return self.tagscache
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 tagscache = super(mqrepo, self).tags()
Brendan Cully
Add mq patch names to tagscache instead of overriding lookup....
r2682
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 q = self.mq
Brendan Cully
mq: do not hold a reference to repo in tags override...
r2723 if not q.applied:
return tagscache
Brendan Cully
Mq: modify repo.lookup to resolve applied patches too.
r2663
Brendan Cully
Use StatusEntry class instead of repeated status line parsing....
r2780 mqtags = [(patch.rev, patch.name) for patch in q.applied]
Brendan Cully
mq: do not hold a reference to repo in tags override...
r2723 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])
Brendan Cully
Add mq patch names to tagscache instead of overriding lookup....
r2682
return tagscache
Brendan Cully
Add qtip and qbase to mq qlookup.
r2664
Brendan Cully
Make mq camelcase consistent with the rest of hg.
r2818 repo.__class__ = mqrepo
Brendan Cully
mq: replace module-wide repo hash with a repo attribute
r2724 repo.mq = queue(ui, repo.join(""))
mason@suse.com
Add mq extension
r1808
cmdtable = {
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
Vadim Gelfer
mq: add qclone command
r2720 "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]'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "qcommit|qci":
(commit,
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 commands.table["^commit|ci"][1],
'hg qcommit [OPTION]... [FILE]...'),
"^qdiff": (diff, [], 'hg qdiff [FILE]...'),
Brendan Cully
Add -f option to qdelete, to remove patch file.
r2752 "qdelete":
(delete,
[('f', 'force', None, _('delete patch file'))],
'hg qdelete [-f] PATCH'),
Brendan Cully
Add -m, -l, -e options to qfold.
r2753 'qfold':
(fold,
[('e', 'edit', None, _('edit patch header')),
Brendan Cully
Add -f option to qfold; improve qfold documentation.
r2771 ('f', 'force', None, _('delete folded patch files')),
Brendan Cully
Add -m, -l, -e options to qfold.
r2753 ('m', 'message', '', _('set patch header to <text>')),
('l', 'logfile', '', _('set patch header to contents of <file>'))],
'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
('n', 'none', None, _('drop all guards'))],
'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
Brendan Cully
Add command qheader to display the header of a given patch.
r2747 'qheader': (header, [],
_('hg qheader [PATCH]')),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "^qimport":
(qimport,
[('e', 'existing', None, 'import file in patch dir'),
('n', 'name', '', 'patch file name'),
('f', 'force', None, 'overwrite existing files')],
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 'hg qimport [-e] [-n NAME] [-f] FILE...'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "^qinit":
(init,
Thomas Arendsen Hein
mq: Added help for qcommit, consistently talk about queue repository.
r2526 [('c', 'create-repo', None, 'create queue repository')],
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 'hg qinit [-c]'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "qnew":
(new,
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 [('m', 'message', '', _('use <text> as commit message')),
('l', 'logfile', '', _('read the commit message from <file>')),
Brendan Cully
Add more verbose help text to mq commands.
r2754 ('f', 'force', None, _('import uncommitted changes into patch'))],
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "qnext": (next, [], 'hg qnext'),
"qprev": (prev, [], 'hg qprev'),
"^qpop":
(pop,
[('a', 'all', None, 'pop all patches'),
('n', 'name', '', 'queue name to pop'),
('f', 'force', None, 'forget any local changes')],
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "^qpush":
(push,
[('f', 'force', None, 'apply if the patch has rejects'),
('l', 'list', None, 'list patch name in commit text'),
('a', 'all', None, 'apply all patches'),
('m', 'merge', None, 'merge from another queue'),
('n', 'name', '', 'merge queue name')],
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "^qrefresh":
(refresh,
Brendan Cully
Add option -e/--edit to qrefresh, to edit the existing header.
r2746 [('e', 'edit', None, _('edit commit message')),
('m', 'message', '', _('change commit message with <text>')),
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 ('l', 'logfile', '', _('change commit message with <file> content')),
('s', 'short', None, 'short refresh')],
Brendan Cully
Add option -e/--edit to qrefresh, to edit the existing header.
r2746 'hg qrefresh [-e] [-m TEXT] [-l FILE] [-s]'),
Vadim Gelfer
mq: add qmv as alias for qrename
r2751 'qrename|qmv':
Brendan Cully
New self-explanatory command qrename.
r2750 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "qrestore":
(restore,
[('d', 'delete', None, 'delete save entry'),
('u', 'update', None, 'update queue working dir')],
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 'hg qrestore [-d] [-u] REV'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "qsave":
(save,
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 [('m', 'message', '', _('use <text> as commit message')),
('l', 'logfile', '', _('read the commit message from <file>')),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 ('c', 'copy', None, 'copy patch directory'),
('n', 'name', '', 'copy directory name'),
('e', 'empty', None, 'clear queue status file'),
('f', 'force', None, 'force copy')],
"Mathieu Clabaut "
MQ: uniformise message and logfile option....
r2694 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
Vadim Gelfer
mq: new commands qselect, qguard...
r2821 "qselect": (select,
[('n', 'none', None, _('disable all guards')),
Vadim Gelfer
qselect: add --pop, --reapply options
r2844 ('s', 'series', None, _('list all guards in series file')),
('', 'pop', None,
_('pop to before first guarded applied patch')),
('', 'reapply', None, _('pop, then reapply patches'))],
'hg qselect [OPTION...] [GUARD...]'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "qseries":
(series,
Brendan Cully
Add -s option to qseries: display first line of patch header.
r2756 [('m', 'missing', None, 'print patches not in series'),
('s', 'summary', None, _('print first line of patch header'))],
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 'hg qseries [-m]'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "^strip":
(strip,
[('f', 'force', None, 'force multi-head removal'),
('b', 'backup', None, 'bundle unrelated changesets'),
('n', 'nobackup', None, 'no backups')],
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 'hg strip [-f] [-b] [-n] REV'),
Thomas Arendsen Hein
Whitespace, tab and formatting cleanups, mainly in mq.py
r1810 "qtop": (top, [], 'hg qtop'),
Thomas Arendsen Hein
Better help for mq: Corrected synopses, get qcommit options from commands.py.
r2185 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
mason@suse.com
Add mq extension
r1808 }