1 # mq.py - patch queues for mercurial
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
8 '''manage a stack of patches
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
17 Common tasks (use "hg help command" for more details)::
19 prepare repository to work with patches qinit
21 import existing patch qimport
23 print patch series qseries
24 print applied patches qapplied
25 print name of top applied patch qtop
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
32 from mercurial.i18n import _
33 from mercurial.node import bin, hex, short, nullid, nullrev
34 from mercurial.lock import release
35 from mercurial import commands, cmdutil, hg, patch, util
36 from mercurial import repair, extensions, url, error
37 import os, sys, re, errno
39 commands.norepo += " qclone"
41 # Patch names looks like unix-file names.
42 # They must be joinable with queue directory and result in the patch path.
43 normname = util.normpath
45 class statusentry(object):
46 def __init__(self, rev, name=None):
48 fields = rev.split(':', 1)
50 self.rev, self.name = fields
52 self.rev, self.name = None, None
54 self.rev, self.name = rev, name
57 return self.rev + ':' + self.name
59 class patchheader(object):
60 def __init__(self, pf):
64 if (l.startswith("diff -") or
65 l.startswith("Index:") or
66 l.startswith("===========")):
73 if re.match('\s*$', l):
88 if line.startswith('diff --git'):
92 if line.startswith('+++ '):
95 if line.startswith("--- "):
98 elif format == "hgpatch":
99 # parse values when importing the result of an hg export
100 if line.startswith("# User "):
102 elif line.startswith("# Date "):
104 elif not line.startswith("# ") and line:
107 elif line == '# HG changeset patch':
110 elif (format != "tagdone" and (line.startswith("Subject: ") or
111 line.startswith("subject: "))):
114 elif (format != "tagdone" and (line.startswith("From: ") or
115 line.startswith("from: "))):
118 elif format == "tag" and line == "":
119 # when looking for tags (subject: from: etc) they
120 # end once you find a blank line in the source
122 elif message or line:
124 comments.append(line)
131 # make sure message isn't empty
132 if format and format.startswith("tag") and subject:
133 message.insert(0, "")
134 message.insert(0, subject)
136 self.message = message
137 self.comments = comments
140 self.haspatch = diffstart > 1
142 def setuser(self, user):
143 if not self.updateheader(['From: ', '# User '], user):
145 patchheaderat = self.comments.index('# HG changeset patch')
146 self.comments.insert(patchheaderat + 1, '# User ' + user)
148 if self._hasheader(['Date: ']):
149 self.comments = ['From: ' + user] + self.comments
151 tmp = ['# HG changeset patch', '# User ' + user, '']
152 self.comments = tmp + self.comments
155 def setdate(self, date):
156 if not self.updateheader(['Date: ', '# Date '], date):
158 patchheaderat = self.comments.index('# HG changeset patch')
159 self.comments.insert(patchheaderat + 1, '# Date ' + date)
161 if self._hasheader(['From: ']):
162 self.comments = ['Date: ' + date] + self.comments
164 tmp = ['# HG changeset patch', '# Date ' + date, '']
165 self.comments = tmp + self.comments
168 def setmessage(self, message):
171 self.message = [message]
172 self.comments += self.message
174 def updateheader(self, prefixes, new):
175 '''Update all references to a field in the patch header.
176 Return whether the field is present.'''
178 for prefix in prefixes:
179 for i in xrange(len(self.comments)):
180 if self.comments[i].startswith(prefix):
181 self.comments[i] = prefix + new
186 def _hasheader(self, prefixes):
187 '''Check if a header starts with any of the given prefixes.'''
188 for prefix in prefixes:
189 for comment in self.comments:
190 if comment.startswith(prefix):
195 if not self.comments:
197 return '\n'.join(self.comments) + '\n\n'
200 '''Remove existing message, keeping the rest of the comments fields.
201 If comments contains 'subject: ', message will prepend
202 the field and a blank line.'''
204 subj = 'subject: ' + self.message[0].lower()
205 for i in xrange(len(self.comments)):
206 if subj == self.comments[i].lower():
208 self.message = self.message[2:]
211 for mi in self.message:
212 while mi != self.comments[ci]:
214 del self.comments[ci]
217 def __init__(self, ui, path, patchdir=None):
219 self.path = patchdir or os.path.join(path, "patches")
220 self.opener = util.opener(self.path)
222 self.applied_dirty = 0
223 self.series_dirty = 0
224 self.series_path = "series"
225 self.status_path = "status"
226 self.guards_path = "guards"
227 self.active_guards = None
228 self.guards_dirty = False
229 self._diffopts = None
233 if os.path.exists(self.join(self.status_path)):
234 lines = self.opener(self.status_path).read().splitlines()
235 return [statusentry(l) for l in lines]
239 def full_series(self):
240 if os.path.exists(self.join(self.series_path)):
241 return self.opener(self.series_path).read().splitlines()
250 def series_guards(self):
252 return self.series_guards
254 def invalidate(self):
255 for a in 'applied full_series series series_guards'.split():
256 if a in self.__dict__:
258 self.applied_dirty = 0
259 self.series_dirty = 0
260 self.guards_dirty = False
261 self.active_guards = None
264 if self._diffopts is None:
265 self._diffopts = patch.diffopts(self.ui)
266 return self._diffopts
269 return os.path.join(self.path, *p)
271 def find_series(self, patch):
272 pre = re.compile("(\s*)([^#]+)")
274 for l in self.full_series:
284 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
286 def parse_series(self):
288 self.series_guards = []
289 for l in self.full_series:
299 patch = patch.strip()
301 if patch in self.series:
302 raise util.Abort(_('%s appears more than once in %s') %
303 (patch, self.join(self.series_path)))
304 self.series.append(patch)
305 self.series_guards.append(self.guard_re.findall(comment))
307 def check_guard(self, guard):
309 return _('guard cannot be an empty string')
310 bad_chars = '# \t\r\n\f'
313 return (_('guard %r starts with invalid character: %r') %
317 return _('invalid character in guard %r: %r') % (guard, c)
319 def set_active(self, guards):
321 bad = self.check_guard(guard)
323 raise util.Abort(bad)
324 guards = sorted(set(guards))
325 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
326 self.active_guards = guards
327 self.guards_dirty = True
330 if self.active_guards is None:
331 self.active_guards = []
333 guards = self.opener(self.guards_path).read().split()
335 if err.errno != errno.ENOENT: raise
337 for i, guard in enumerate(guards):
338 bad = self.check_guard(guard)
340 self.ui.warn('%s:%d: %s\n' %
341 (self.join(self.guards_path), i + 1, bad))
343 self.active_guards.append(guard)
344 return self.active_guards
346 def set_guards(self, idx, guards):
349 raise util.Abort(_('guard %r too short') % g)
351 raise util.Abort(_('guard %r starts with invalid char') % g)
352 bad = self.check_guard(g[1:])
354 raise util.Abort(bad)
355 drop = self.guard_re.sub('', self.full_series[idx])
356 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
358 self.series_dirty = True
360 def pushable(self, idx):
361 if isinstance(idx, str):
362 idx = self.series.index(idx)
363 patchguards = self.series_guards[idx]
366 guards = self.active()
367 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
369 return False, exactneg[0]
370 pos = [g for g in patchguards if g[0] == '+']
371 exactpos = [g for g in pos if g[1:] in guards]
374 return True, exactpos[0]
378 def explain_pushable(self, idx, all_patches=False):
379 write = all_patches and self.ui.write or self.ui.warn
380 if all_patches or self.ui.verbose:
381 if isinstance(idx, str):
382 idx = self.series.index(idx)
383 pushable, why = self.pushable(idx)
384 if all_patches and pushable:
386 write(_('allowing %s - no guards in effect\n') %
390 write(_('allowing %s - no matching negative guards\n') %
393 write(_('allowing %s - guarded by %r\n') %
394 (self.series[idx], why))
397 write(_('skipping %s - guarded by %r\n') %
398 (self.series[idx], why))
400 write(_('skipping %s - no matching guards\n') %
403 def save_dirty(self):
404 def write_list(items, path):
405 fp = self.opener(path, 'w')
409 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
410 if self.series_dirty: write_list(self.full_series, self.series_path)
411 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
413 def removeundo(self, repo):
414 undo = repo.sjoin('undo')
415 if not os.path.exists(undo):
419 except OSError, inst:
420 self.ui.warn(_('error removing undo: %s\n') % str(inst))
422 def printdiff(self, repo, node1, node2=None, files=None,
423 fp=None, changes=None, opts={}):
424 m = cmdutil.match(repo, files, opts)
425 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
426 write = fp is None and repo.ui.write or fp.write
430 def mergeone(self, repo, mergeq, head, patch, rev):
431 # first try just applying the patch
432 (err, n) = self.apply(repo, [ patch ], update_status=False,
433 strict=True, merge=rev)
439 raise util.Abort(_("apply failed for patch %s") % patch)
441 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
443 # apply failed, strip away that rev and merge.
445 self.strip(repo, n, update=False, backup='strip')
448 ret = hg.merge(repo, rev)
450 raise util.Abort(_("update returned %d") % ret)
451 n = repo.commit(ctx.description(), ctx.user(), force=True)
453 raise util.Abort(_("repo commit failed"))
455 ph = patchheader(mergeq.join(patch))
457 raise util.Abort(_("unable to read %s") % patch)
459 patchf = self.opener(patch, "w")
462 patchf.write(comments)
463 self.printdiff(repo, head, n, fp=patchf)
465 self.removeundo(repo)
468 def qparents(self, repo, rev=None):
470 (p1, p2) = repo.dirstate.parents()
473 if len(self.applied) == 0:
475 return bin(self.applied[-1].rev)
476 pp = repo.changelog.parents(rev)
478 arevs = [ x.rev for x in self.applied ]
487 def mergepatch(self, repo, mergeq, series):
488 if len(self.applied) == 0:
489 # each of the patches merged in will have two parents. This
490 # can confuse the qrefresh, qdiff, and strip code because it
491 # needs to know which parent is actually in the patch queue.
492 # so, we insert a merge marker with only one parent. This way
493 # the first patch in the queue is never a merge patch
495 pname = ".hg.patches.merge.marker"
496 n = repo.commit('[mq]: merge marker', force=True)
497 self.removeundo(repo)
498 self.applied.append(statusentry(hex(n), pname))
499 self.applied_dirty = 1
501 head = self.qparents(repo)
504 patch = mergeq.lookup(patch, strict=True)
506 self.ui.warn(_("patch %s does not exist\n") % patch)
508 pushable, reason = self.pushable(patch)
510 self.explain_pushable(patch, all_patches=True)
512 info = mergeq.isapplied(patch)
514 self.ui.warn(_("patch %s is not applied\n") % patch)
517 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
519 self.applied.append(statusentry(hex(head), patch))
520 self.applied_dirty = 1
526 def patch(self, repo, patchfile):
527 '''Apply patchfile to the working directory.
528 patchfile: name of patch file'''
531 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
532 files=files, eolmode=None)
533 except Exception, inst:
534 self.ui.note(str(inst) + '\n')
535 if not self.ui.verbose:
536 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
537 return (False, files, False)
539 return (True, files, fuzz)
541 def apply(self, repo, series, list=False, update_status=True,
542 strict=False, patchdir=None, merge=None, all_files={}):
543 wlock = lock = tr = None
547 tr = repo.transaction()
549 ret = self._apply(repo, series, list, update_status,
550 strict, patchdir, merge, all_files=all_files)
559 repo.dirstate.invalidate()
564 self.removeundo(repo)
566 def _apply(self, repo, series, list=False, update_status=True,
567 strict=False, patchdir=None, merge=None, all_files={}):
568 '''returns (error, hash)
569 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
570 # TODO unify with commands.py
575 for patchname in series:
576 pushable, reason = self.pushable(patchname)
578 self.explain_pushable(patchname, all_patches=True)
580 self.ui.status(_("applying %s\n") % patchname)
581 pf = os.path.join(patchdir, patchname)
584 ph = patchheader(self.join(patchname))
586 self.ui.warn(_("unable to read %s\n") % patchname)
592 message = _("imported patch %s\n") % patchname
595 message.append(_("\nimported patch %s") % patchname)
596 message = '\n'.join(message)
599 (patcherr, files, fuzz) = self.patch(repo, pf)
600 all_files.update(files)
601 patcherr = not patcherr
603 self.ui.warn(_("patch %s is empty\n") % patchname)
604 patcherr, files, fuzz = 0, [], 0
607 # Mark as removed/merged and update dirstate parent info
611 if os.path.exists(repo.wjoin(f)):
616 repo.dirstate.remove(f)
618 repo.dirstate.merge(f)
619 p1, p2 = repo.dirstate.parents()
620 repo.dirstate.setparents(p1, merge)
622 files = patch.updatedir(self.ui, repo, files)
623 match = cmdutil.matchfiles(repo, files or [])
624 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
627 raise util.Abort(_("repo commit failed"))
630 self.applied.append(statusentry(hex(n), patchname))
633 self.ui.warn(_("patch failed, rejects left in working dir\n"))
638 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
643 def _cleanup(self, patches, numrevs, keep=False):
647 r.remove(patches, True)
650 os.unlink(self.join(p))
653 del self.applied[:numrevs]
654 self.applied_dirty = 1
656 for i in sorted([self.find_series(p) for p in patches], reverse=True):
657 del self.full_series[i]
659 self.series_dirty = 1
661 def _revpatches(self, repo, revs):
662 firstrev = repo[self.applied[0].rev].rev()
664 for i, rev in enumerate(revs):
667 raise util.Abort(_('revision %d is not managed') % rev)
670 base = bin(self.applied[i].rev)
671 if ctx.node() != base:
672 msg = _('cannot delete revision %d above applied patches')
673 raise util.Abort(msg % rev)
675 patch = self.applied[i].name
676 for fmt in ('[mq]: %s', 'imported patch %s'):
677 if ctx.description() == fmt % patch:
678 msg = _('patch %s finalized without changeset message\n')
679 repo.ui.status(msg % patch)
682 patches.append(patch)
685 def finish(self, repo, revs):
686 patches = self._revpatches(repo, sorted(revs))
687 self._cleanup(patches, len(patches))
689 def delete(self, repo, patches, opts):
690 if not patches and not opts.get('rev'):
691 raise util.Abort(_('qdelete requires at least one revision or '
695 for patch in patches:
696 patch = self.lookup(patch, strict=True)
697 info = self.isapplied(patch)
699 raise util.Abort(_("cannot delete applied patch %s") % patch)
700 if patch not in self.series:
701 raise util.Abort(_("patch %s not in series file") % patch)
702 realpatches.append(patch)
707 raise util.Abort(_('no patches applied'))
708 revs = cmdutil.revrange(repo, opts['rev'])
709 if len(revs) > 1 and revs[0] > revs[1]:
711 revpatches = self._revpatches(repo, revs)
712 realpatches += revpatches
713 numrevs = len(revpatches)
715 self._cleanup(realpatches, numrevs, opts.get('keep'))
717 def check_toppatch(self, repo):
718 if len(self.applied) > 0:
719 top = bin(self.applied[-1].rev)
720 pp = repo.dirstate.parents()
722 raise util.Abort(_("working directory revision is not qtip"))
725 def check_localchanges(self, repo, force=False, refresh=True):
726 m, a, r, d = repo.status()[:4]
730 raise util.Abort(_("local changes found, refresh first"))
732 raise util.Abort(_("local changes found"))
735 _reserved = ('series', 'status', 'guards')
736 def check_reserved_name(self, name):
737 if (name in self._reserved or name.startswith('.hg')
738 or name.startswith('.mq')):
739 raise util.Abort(_('"%s" cannot be used as the name of a patch')
742 def new(self, repo, patchfn, *pats, **opts):
744 msg: a string or a no-argument function returning a string
746 msg = opts.get('msg')
747 force = opts.get('force')
748 user = opts.get('user')
749 date = opts.get('date')
751 date = util.parsedate(date)
752 self.check_reserved_name(patchfn)
753 if os.path.exists(self.join(patchfn)):
754 raise util.Abort(_('patch "%s" already exists') % patchfn)
755 if opts.get('include') or opts.get('exclude') or pats:
756 match = cmdutil.match(repo, pats, opts)
757 # detect missing files in pats
759 raise util.Abort('%s: %s' % (f, msg))
761 m, a, r, d = repo.status(match=match)[:4]
763 m, a, r, d = self.check_localchanges(repo, force)
764 match = cmdutil.matchfiles(repo, m + a + r)
765 commitfiles = m + a + r
766 self.check_toppatch(repo)
767 insert = self.full_series_end()
770 # if patch file write fails, abort early
771 p = self.opener(patchfn, "w")
774 p.write("# HG changeset patch\n")
776 p.write("# User " + user + "\n")
777 p.write("# Date %d %d\n\n" % date)
779 p.write("From: " + user + "\n\n")
781 if hasattr(msg, '__call__'):
783 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
784 n = repo.commit(commitmsg, user, date, match=match, force=True)
786 raise util.Abort(_("repo commit failed"))
788 self.full_series[insert:insert] = [patchfn]
789 self.applied.append(statusentry(hex(n), patchfn))
791 self.series_dirty = 1
792 self.applied_dirty = 1
797 diffopts = self.diffopts()
798 if opts.get('git'): diffopts.git = True
799 parent = self.qparents(repo, n)
800 chunks = patch.diff(repo, node1=parent, node2=n,
801 match=match, opts=diffopts)
808 if r: r.add([patchfn])
813 patchpath = self.join(patchfn)
817 self.ui.warn(_('error unlinking %s\n') % patchpath)
819 self.removeundo(repo)
823 def strip(self, repo, rev, update=True, backup="all", force=None):
830 self.check_localchanges(repo, force=force, refresh=False)
831 urev = self.qparents(repo, rev)
833 repo.dirstate.write()
835 self.removeundo(repo)
836 repair.strip(self.ui, repo, rev, backup)
837 # strip may have unbundled a set of backed up revisions after
839 self.removeundo(repo)
843 def isapplied(self, patch):
844 """returns (index, rev, patch)"""
845 for i, a in enumerate(self.applied):
847 return (i, a.rev, a.name)
850 # if the exact patch name does not exist, we try a few
851 # variations. If strict is passed, we try only #1
853 # 1) a number to indicate an offset in the series file
854 # 2) a unique substring of the patch name was given
855 # 3) patchname[-+]num to indicate an offset in the series file
856 def lookup(self, patch, strict=False):
857 patch = patch and str(patch)
862 matches = [x for x in self.series if s in x]
864 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
866 self.ui.warn(' %s\n' % m)
870 if len(self.series) > 0 and len(self.applied) > 0:
872 return self.series[self.series_end(True)-1]
874 return self.series[0]
879 if patch in self.series:
882 if not os.path.isfile(self.join(patch)):
885 except(ValueError, OverflowError):
888 if -len(self.series) <= sno < len(self.series):
889 return self.series[sno]
892 res = partial_name(patch)
895 minus = patch.rfind('-')
897 res = partial_name(patch[:minus])
899 i = self.series.index(res)
901 off = int(patch[minus+1:] or 1)
902 except(ValueError, OverflowError):
906 return self.series[i - off]
907 plus = patch.rfind('+')
909 res = partial_name(patch[:plus])
911 i = self.series.index(res)
913 off = int(patch[plus+1:] or 1)
914 except(ValueError, OverflowError):
917 if i + off < len(self.series):
918 return self.series[i + off]
919 raise util.Abort(_("patch %s not in series") % patch)
921 def push(self, repo, patch=None, force=False, list=False,
922 mergeq=None, all=False):
925 if repo.dirstate.parents()[0] not in repo.heads():
926 self.ui.status(_("(working directory not at a head)\n"))
929 self.ui.warn(_('no patches in series\n'))
932 patch = self.lookup(patch)
933 # Suppose our series file is: A B C and the current 'top'
934 # patch is B. qpush C should be performed (moving forward)
935 # qpush B is a NOP (no change) qpush A is an error (can't
936 # go backwards with qpush)
938 info = self.isapplied(patch)
940 if info[0] < len(self.applied) - 1:
942 _("cannot push to a previous patch: %s") % patch)
944 _('qpush: %s is already at the top\n') % patch)
946 pushable, reason = self.pushable(patch)
949 reason = _('guarded by %r') % reason
951 reason = _('no matching guards')
952 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
955 patch = self.series[-1]
956 if self.isapplied(patch):
957 self.ui.warn(_('all patches are currently applied\n'))
960 # Following the above example, starting at 'top' of B:
961 # qpush should be performed (pushes C), but a subsequent
962 # qpush without an argument is an error (nothing to
963 # apply). This allows a loop of "...while hg qpush..." to
964 # work as it detects an error when done
965 start = self.series_end()
966 if start == len(self.series):
967 self.ui.warn(_('patch series already fully applied\n'))
970 self.check_localchanges(repo)
972 self.applied_dirty = 1
974 self.check_toppatch(repo)
976 patch = self.series[start]
979 end = self.series.index(patch, start) + 1
981 s = self.series[start:end]
985 ret = self.mergepatch(repo, mergeq, s)
987 ret = self.apply(repo, s, list, all_files=all_files)
989 self.ui.warn(_('cleaning up working directory...'))
990 node = repo.dirstate.parents()[0]
991 hg.revert(repo, node, None)
992 unknown = repo.status(unknown=True)[4]
993 # only remove unknown files that we know we touched or
994 # created while patching
997 util.unlink(repo.wjoin(f))
998 self.ui.warn(_('done\n'))
1001 top = self.applied[-1].name
1002 if ret[0] and ret[0] > 1:
1003 msg = _("errors during apply, please fix and refresh %s\n")
1004 self.ui.write(msg % top)
1006 self.ui.write(_("now at: %s\n") % top)
1012 def pop(self, repo, patch=None, force=False, update=True, all=False):
1013 def getfile(f, rev, flags):
1014 t = repo.file(f).read(rev)
1015 repo.wwrite(f, t, flags)
1017 wlock = repo.wlock()
1021 info = self.isapplied(patch)
1023 patch = self.lookup(patch)
1024 info = self.isapplied(patch)
1026 raise util.Abort(_("patch %s is not applied") % patch)
1028 if len(self.applied) == 0:
1029 # Allow qpop -a to work repeatedly,
1030 # but not qpop without an argument
1031 self.ui.warn(_("no patches applied\n"))
1039 start = len(self.applied) - 1
1041 if start >= len(self.applied):
1042 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1046 parents = repo.dirstate.parents()
1047 rr = [ bin(x.rev) for x in self.applied ]
1050 self.ui.warn(_("qpop: forcing dirstate update\n"))
1053 parents = [p.hex() for p in repo[None].parents()]
1055 for entry in self.applied[start:]:
1056 if entry.rev in parents:
1061 if not force and update:
1062 self.check_localchanges(repo)
1064 self.applied_dirty = 1
1065 end = len(self.applied)
1066 rev = bin(self.applied[start].rev)
1068 top = self.check_toppatch(repo)
1071 heads = repo.changelog.heads(rev)
1072 except error.LookupError:
1074 raise util.Abort(_('trying to pop unknown node %s') % node)
1076 if heads != [bin(self.applied[-1].rev)]:
1077 raise util.Abort(_("popping would remove a revision not "
1078 "managed by this patch queue"))
1080 # we know there are no local changes, so we can make a simplified
1081 # form of hg.update.
1083 qp = self.qparents(repo, rev)
1084 changes = repo.changelog.read(qp)
1085 mmap = repo.manifest.read(changes[0])
1086 m, a, r, d = repo.status(qp, top)[:4]
1088 raise util.Abort(_("deletions found between repo revs"))
1090 getfile(f, mmap[f], mmap.flags(f))
1092 getfile(f, mmap[f], mmap.flags(f))
1094 repo.dirstate.normal(f)
1097 os.unlink(repo.wjoin(f))
1099 if e.errno != errno.ENOENT:
1101 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1103 repo.dirstate.forget(f)
1104 repo.dirstate.setparents(qp, nullid)
1105 for patch in reversed(self.applied[start:end]):
1106 self.ui.status(_("popping %s\n") % patch.name)
1107 del self.applied[start:end]
1108 self.strip(repo, rev, update=False, backup='strip')
1109 if len(self.applied):
1110 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1112 self.ui.write(_("patch queue now empty\n"))
1116 def diff(self, repo, pats, opts):
1117 top = self.check_toppatch(repo)
1119 self.ui.write(_("no patches applied\n"))
1121 qp = self.qparents(repo, top)
1122 self._diffopts = patch.diffopts(self.ui, opts)
1123 self.printdiff(repo, qp, files=pats, opts=opts)
1125 def refresh(self, repo, pats=None, **opts):
1126 if len(self.applied) == 0:
1127 self.ui.write(_("no patches applied\n"))
1129 msg = opts.get('msg', '').rstrip()
1130 newuser = opts.get('user')
1131 newdate = opts.get('date')
1133 newdate = '%d %d' % util.parsedate(newdate)
1134 wlock = repo.wlock()
1136 self.check_toppatch(repo)
1137 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1139 if repo.changelog.heads(top) != [top]:
1140 raise util.Abort(_("cannot refresh a revision with children"))
1141 cparents = repo.changelog.parents(top)
1142 patchparent = self.qparents(repo, top)
1143 ph = patchheader(self.join(patchfn))
1145 patchf = self.opener(patchfn, 'r')
1147 # if the patch was a git patch, refresh it as a git patch
1149 if line.startswith('diff --git'):
1150 self.diffopts().git = True
1160 # only commit new patch when write is complete
1161 patchf = self.opener(patchfn, 'w', atomictemp=True)
1168 patchf.write(comments)
1171 self.diffopts().git = True
1172 tip = repo.changelog.tip()
1174 # if the top of our patch queue is also the tip, there is an
1175 # optimization here. We update the dirstate in place and strip
1176 # off the tip commit. Then just commit the current directory
1177 # tree. We can also send repo.commit the list of files
1178 # changed to speed up the diff
1180 # in short mode, we only diff the files included in the
1181 # patch already plus specified files
1183 # this should really read:
1184 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1185 # but we do it backwards to take advantage of manifest/chlog
1186 # caching against the next repo.status call
1188 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1189 changes = repo.changelog.read(tip)
1190 man = repo.manifest.read(changes[0])
1192 matchfn = cmdutil.match(repo, pats, opts)
1193 if opts.get('short'):
1194 # if amending a patch, we start with existing
1195 # files plus specified files - unfiltered
1196 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1197 # filter with inc/exl options
1198 matchfn = cmdutil.match(repo, opts=opts)
1200 match = cmdutil.matchall(repo)
1201 m, a, r, d = repo.status(match=match)[:4]
1203 # we might end up with files that were added between
1204 # tip and the dirstate parent, but then changed in the
1205 # local dirstate. in this case, we want them to only
1206 # show up in the added section
1210 # we might end up with files added by the local dirstate that
1211 # were deleted by the patch. In this case, they should only
1212 # show up in the changed section.
1219 # make sure any files deleted in the local dirstate
1220 # are not in the add or change column of the patch
1234 c = [filter(matchfn, l) for l in (m, a, r)]
1235 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1236 chunks = patch.diff(repo, patchparent, match=match,
1237 changes=c, opts=self.diffopts())
1238 for chunk in chunks:
1242 if self.diffopts().git:
1245 src = repo.dirstate.copied(dst)
1246 # during qfold, the source file for copies may
1247 # be removed. Treat this as a simple add.
1248 if src is not None and src in repo.dirstate:
1249 copies.setdefault(src, []).append(dst)
1250 repo.dirstate.add(dst)
1251 # remember the copies between patchparent and tip
1254 src = f.renamed(man[dst])
1256 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1258 copies[src[0]].append(dst)
1259 # we can't copy a file created by the patch itself
1262 for src, dsts in copies.iteritems():
1264 repo.dirstate.copy(src, dst)
1267 repo.dirstate.add(dst)
1268 # Drop useless copy information
1269 for f in list(repo.dirstate.copies()):
1270 repo.dirstate.copy(None, f)
1272 repo.dirstate.remove(f)
1273 # if the patch excludes a modified file, mark that
1274 # file with mtime=0 so status can see it.
1276 for i in xrange(len(m)-1, -1, -1):
1277 if not matchfn(m[i]):
1281 repo.dirstate.normal(f)
1283 repo.dirstate.normallookup(f)
1285 repo.dirstate.forget(f)
1289 message = "[mq]: %s\n" % patchfn
1291 message = "\n".join(ph.message)
1295 user = ph.user or changes[1]
1297 # assumes strip can roll itself back if interrupted
1298 repo.dirstate.setparents(*cparents)
1300 self.applied_dirty = 1
1301 self.strip(repo, top, update=False,
1304 repo.dirstate.invalidate()
1308 # might be nice to attempt to roll back strip after this
1310 n = repo.commit(message, user, ph.date, match=match,
1312 self.applied.append(statusentry(hex(n), patchfn))
1314 ctx = repo[cparents[0]]
1315 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1317 self.ui.warn(_('refresh interrupted while patch was popped! '
1318 '(revert --all, qpush to recover)\n'))
1321 self.printdiff(repo, patchparent, fp=patchf)
1323 added = repo.status()[1]
1329 if e.errno != errno.ENOENT:
1331 try: os.removedirs(os.path.dirname(f))
1333 # forget the file copies in the dirstate
1334 # push should readd the files later on
1335 repo.dirstate.forget(a)
1336 self.pop(repo, force=True)
1337 self.push(repo, force=True)
1340 self.removeundo(repo)
1342 def init(self, repo, create=False):
1343 if not create and os.path.isdir(self.path):
1344 raise util.Abort(_("patch queue directory already exists"))
1347 except OSError, inst:
1348 if inst.errno != errno.EEXIST or not create:
1351 return self.qrepo(create=True)
1353 def unapplied(self, repo, patch=None):
1354 if patch and patch not in self.series:
1355 raise util.Abort(_("patch %s is not in series file") % patch)
1357 start = self.series_end()
1359 start = self.series.index(patch) + 1
1361 for i in xrange(start, len(self.series)):
1362 pushable, reason = self.pushable(i)
1364 unapplied.append((i, self.series[i]))
1365 self.explain_pushable(i)
1368 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1370 def displayname(pfx, patchname):
1372 ph = patchheader(self.join(patchname))
1374 msg = msg and ': ' + msg[0] or ': '
1377 msg = "%s%s%s" % (pfx, patchname, msg)
1378 if self.ui.interactive():
1379 msg = util.ellipsis(msg, util.termwidth())
1380 self.ui.write(msg + '\n')
1382 applied = set([p.name for p in self.applied])
1384 length = len(self.series) - start
1387 idxwidth = len(str(start+length - 1))
1388 for i in xrange(start, start+length):
1389 patch = self.series[i]
1390 if patch in applied:
1392 elif self.pushable(i)[0]:
1398 pfx = '%*d %s ' % (idxwidth, i, stat)
1399 elif status and status != stat:
1401 displayname(pfx, patch)
1404 for root, dirs, files in os.walk(self.path):
1405 d = root[len(self.path) + 1:]
1407 fl = os.path.join(d, f)
1408 if (fl not in self.series and
1409 fl not in (self.status_path, self.series_path,
1411 and not fl.startswith('.')):
1412 msng_list.append(fl)
1413 for x in sorted(msng_list):
1414 pfx = self.ui.verbose and ('D ') or ''
1417 def issaveline(self, l):
1418 if l.name == '.hg.patches.save.line':
1421 def qrepo(self, create=False):
1422 if create or os.path.isdir(self.join(".hg")):
1423 return hg.repository(self.ui, path=self.path, create=create)
1425 def restore(self, repo, rev, delete=None, qupdate=None):
1426 c = repo.changelog.read(rev)
1428 lines = desc.splitlines()
1434 for i, line in enumerate(lines):
1435 if line == 'Patch Data:':
1437 elif line.startswith('Dirstate:'):
1439 l = l[10:].split(' ')
1440 qpp = [ bin(x) for x in l ]
1441 elif datastart != None:
1448 series.append(file_)
1449 if datastart is None:
1450 self.ui.warn(_("No saved patch data found\n"))
1452 self.ui.warn(_("restoring status: %s\n") % lines[0])
1453 self.full_series = series
1454 self.applied = applied
1456 self.series_dirty = 1
1457 self.applied_dirty = 1
1458 heads = repo.changelog.heads()
1460 if rev not in heads:
1461 self.ui.warn(_("save entry has children, leaving it alone\n"))
1463 self.ui.warn(_("removing save entry %s\n") % short(rev))
1464 pp = repo.dirstate.parents()
1469 self.strip(repo, rev, update=update, backup='strip')
1471 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1472 (short(qpp[0]), short(qpp[1])))
1474 self.ui.status(_("queue directory updating\n"))
1477 self.ui.warn(_("Unable to load queue repository\n"))
1481 def save(self, repo, msg=None):
1482 if len(self.applied) == 0:
1483 self.ui.warn(_("save: no patches applied, exiting\n"))
1485 if self.issaveline(self.applied[-1]):
1486 self.ui.warn(_("status is already saved\n"))
1489 ar = [ ':' + x for x in self.full_series ]
1491 msg = _("hg patches saved state")
1493 msg = "hg patches: " + msg.rstrip('\r\n')
1496 pp = r.dirstate.parents()
1497 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1498 msg += "\n\nPatch Data:\n"
1499 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1500 "\n".join(ar) + '\n' or "")
1501 n = repo.commit(text, force=True)
1503 self.ui.warn(_("repo commit failed\n"))
1505 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1506 self.applied_dirty = 1
1507 self.removeundo(repo)
1509 def full_series_end(self):
1510 if len(self.applied) > 0:
1511 p = self.applied[-1].name
1512 end = self.find_series(p)
1514 return len(self.full_series)
1518 def series_end(self, all_patches=False):
1519 """If all_patches is False, return the index of the next pushable patch
1520 in the series, or the series length. If all_patches is True, return the
1521 index of the first patch past the last applied one.
1528 while i < len(self.series):
1529 p, reason = self.pushable(i)
1532 self.explain_pushable(i)
1535 if len(self.applied) > 0:
1536 p = self.applied[-1].name
1538 end = self.series.index(p)
1541 return next(end + 1)
1544 def appliedname(self, index):
1545 pname = self.applied[index].name
1546 if not self.ui.verbose:
1549 p = str(self.series.index(pname)) + " " + pname
1552 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1553 force=None, git=False):
1554 def checkseries(patchname):
1555 if patchname in self.series:
1556 raise util.Abort(_('patch %s is already in the series file')
1558 def checkfile(patchname):
1559 if not force and os.path.exists(self.join(patchname)):
1560 raise util.Abort(_('patch "%s" already exists')
1565 raise util.Abort(_('option "-r" not valid when importing '
1567 rev = cmdutil.revrange(repo, rev)
1568 rev.sort(reverse=True)
1569 if (len(files) > 1 or len(rev) > 1) and patchname:
1570 raise util.Abort(_('option "-n" not valid when importing multiple '
1575 # If mq patches are applied, we can only import revisions
1576 # that form a linear path to qbase.
1577 # Otherwise, they should form a linear path to a head.
1578 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1580 raise util.Abort(_('revision %d is the root of more than one '
1581 'branch') % rev[-1])
1583 base = hex(repo.changelog.node(rev[0]))
1584 if base in [n.rev for n in self.applied]:
1585 raise util.Abort(_('revision %d is already managed')
1587 if heads != [bin(self.applied[-1].rev)]:
1588 raise util.Abort(_('revision %d is not the parent of '
1589 'the queue') % rev[0])
1590 base = repo.changelog.rev(bin(self.applied[0].rev))
1591 lastparent = repo.changelog.parentrevs(base)[0]
1593 if heads != [repo.changelog.node(rev[0])]:
1594 raise util.Abort(_('revision %d has unmanaged children')
1599 self.diffopts().git = True
1602 p1, p2 = repo.changelog.parentrevs(r)
1603 n = repo.changelog.node(r)
1605 raise util.Abort(_('cannot import merge revision %d') % r)
1606 if lastparent and lastparent != r:
1607 raise util.Abort(_('revision %d is not the parent of %d')
1612 patchname = normname('%d.diff' % r)
1613 self.check_reserved_name(patchname)
1614 checkseries(patchname)
1615 checkfile(patchname)
1616 self.full_series.insert(0, patchname)
1618 patchf = self.opener(patchname, "w")
1619 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1622 se = statusentry(hex(n), patchname)
1623 self.applied.insert(0, se)
1625 added.append(patchname)
1628 self.applied_dirty = 1
1630 for filename in files:
1633 raise util.Abort(_('-e is incompatible with import from -'))
1635 patchname = normname(filename)
1636 self.check_reserved_name(patchname)
1637 if not os.path.isfile(self.join(patchname)):
1638 raise util.Abort(_("patch %s does not exist") % patchname)
1643 raise util.Abort(_('need --name to import a patch from -'))
1644 text = sys.stdin.read()
1646 text = url.open(self.ui, filename).read()
1647 except (OSError, IOError):
1648 raise util.Abort(_("unable to read %s") % filename)
1650 patchname = normname(os.path.basename(filename))
1651 self.check_reserved_name(patchname)
1652 checkfile(patchname)
1653 patchf = self.opener(patchname, "w")
1656 checkseries(patchname)
1657 if patchname not in self.series:
1658 index = self.full_series_end() + i
1659 self.full_series[index:index] = [patchname]
1661 self.ui.warn(_("adding %s to series file\n") % patchname)
1663 added.append(patchname)
1665 self.series_dirty = 1
1666 qrepo = self.qrepo()
1670 def delete(ui, repo, *patches, **opts):
1671 """remove patches from queue
1673 The patches must not be applied, and at least one patch is required. With
1674 -k/--keep, the patch files are preserved in the patch directory.
1676 To stop managing a patch and move it into permanent history,
1677 use the qfinish command."""
1679 q.delete(repo, patches, opts)
1683 def applied(ui, repo, patch=None, **opts):
1684 """print the patches already applied"""
1687 if patch not in q.series:
1688 raise util.Abort(_("patch %s is not in series file") % patch)
1689 end = q.series.index(patch) + 1
1691 end = q.series_end(True)
1692 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1694 def unapplied(ui, repo, patch=None, **opts):
1695 """print the patches not yet applied"""
1698 if patch not in q.series:
1699 raise util.Abort(_("patch %s is not in series file") % patch)
1700 start = q.series.index(patch) + 1
1702 start = q.series_end(True)
1703 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1705 def qimport(ui, repo, *filename, **opts):
1708 The patch is inserted into the series after the last applied
1709 patch. If no patches have been applied, qimport prepends the patch
1712 The patch will have the same name as its source file unless you
1713 give it a new one with -n/--name.
1715 You can register an existing patch inside the patch directory with
1716 the -e/--existing flag.
1718 With -f/--force, an existing patch of the same name will be
1721 An existing changeset may be placed under mq control with -r/--rev
1722 (e.g. qimport --rev tip -n patch will place tip under mq control).
1723 With -g/--git, patches imported with --rev will use the git diff
1724 format. See the diffs help topic for information on why this is
1725 important for preserving rename/copy information and permission
1728 To import a patch from standard input, pass - as the patch file.
1729 When importing from standard input, a patch name must be specified
1730 using the --name flag.
1733 q.qimport(repo, filename, patchname=opts['name'],
1734 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1738 if opts.get('push') and not opts.get('rev'):
1739 return q.push(repo, None)
1742 def init(ui, repo, **opts):
1743 """init a new queue repository
1745 The queue repository is unversioned by default. If
1746 -c/--create-repo is specified, qinit will create a separate nested
1747 repository for patches (qinit -c may also be run later to convert
1748 an unversioned patch repository into a versioned one). You can use
1749 qcommit to commit changes to this queue repository."""
1751 r = q.init(repo, create=opts['create_repo'])
1754 if not os.path.exists(r.wjoin('.hgignore')):
1755 fp = r.wopener('.hgignore', 'w')
1756 fp.write('^\\.hg\n')
1757 fp.write('^\\.mq\n')
1758 fp.write('syntax: glob\n')
1759 fp.write('status\n')
1760 fp.write('guards\n')
1762 if not os.path.exists(r.wjoin('series')):
1763 r.wopener('series', 'w').close()
1764 r.add(['.hgignore', 'series'])
1768 def clone(ui, source, dest=None, **opts):
1769 '''clone main and patch repository at same time
1771 If source is local, destination will have no patches applied. If
1772 source is remote, this command can not check if patches are
1773 applied in source, so cannot guarantee that patches are not
1774 applied in destination. If you clone remote repository, be sure
1775 before that it has no patches applied.
1777 Source patch repository is looked for in <src>/.hg/patches by
1778 default. Use -p <url> to change.
1780 The patch directory must be a nested Mercurial repository, as
1781 would be created by qinit -c.
1785 if url.endswith('/'):
1787 return url + '/.hg/patches'
1789 dest = hg.defaultdest(source)
1790 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1792 patchespath = ui.expandpath(opts['patches'])
1794 patchespath = patchdir(sr)
1796 hg.repository(ui, patchespath)
1797 except error.RepoError:
1798 raise util.Abort(_('versioned patch repository not found'
1800 qbase, destrev = None, None
1803 qbase = bin(sr.mq.applied[0].rev)
1804 if not hg.islocal(dest):
1805 heads = set(sr.heads())
1806 destrev = list(heads.difference(sr.heads(qbase)))
1807 destrev.append(sr.changelog.parents(qbase)[0])
1808 elif sr.capable('lookup'):
1810 qbase = sr.lookup('qbase')
1811 except error.RepoError:
1813 ui.note(_('cloning main repository\n'))
1814 sr, dr = hg.clone(ui, sr.url(), dest,
1818 stream=opts['uncompressed'])
1819 ui.note(_('cloning patch repository\n'))
1820 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1821 pull=opts['pull'], update=not opts['noupdate'],
1822 stream=opts['uncompressed'])
1825 ui.note(_('stripping applied patches from destination '
1827 dr.mq.strip(dr, qbase, update=False, backup=None)
1828 if not opts['noupdate']:
1829 ui.note(_('updating destination repository\n'))
1830 hg.update(dr, dr.changelog.tip())
1832 def commit(ui, repo, *pats, **opts):
1833 """commit changes in the queue repository"""
1836 if not r: raise util.Abort('no queue repository')
1837 commands.commit(r.ui, r, *pats, **opts)
1839 def series(ui, repo, **opts):
1840 """print the entire series file"""
1841 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1844 def top(ui, repo, **opts):
1845 """print the name of the current patch"""
1847 t = q.applied and q.series_end(True) or 0
1849 return q.qseries(repo, start=t-1, length=1, status='A',
1850 summary=opts.get('summary'))
1852 ui.write(_("no patches applied\n"))
1855 def next(ui, repo, **opts):
1856 """print the name of the next patch"""
1858 end = q.series_end()
1859 if end == len(q.series):
1860 ui.write(_("all patches applied\n"))
1862 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1864 def prev(ui, repo, **opts):
1865 """print the name of the previous patch"""
1869 ui.write(_("only one patch applied\n"))
1872 ui.write(_("no patches applied\n"))
1874 return q.qseries(repo, start=l-2, length=1, status='A',
1875 summary=opts.get('summary'))
1877 def setupheaderopts(ui, opts):
1879 if not opts[opt] and opts['current' + opt]:
1881 do('user', ui.username())
1882 do('date', "%d %d" % util.makedate())
1884 def new(ui, repo, patch, *args, **opts):
1885 """create a new patch
1887 qnew creates a new patch on top of the currently-applied patch (if
1888 any). It will refuse to run if there are any outstanding changes
1889 unless -f/--force is specified, in which case the patch will be
1890 initialized with them. You may also use -I/--include,
1891 -X/--exclude, and/or a list of files after the patch name to add
1892 only changes to matching files to the new patch, leaving the rest
1893 as uncommitted modifications.
1895 -u/--user and -d/--date can be used to set the (given) user and
1896 date, respectively. -U/--currentuser and -D/--currentdate set user
1897 to current user and date to current date.
1899 -e/--edit, -m/--message or -l/--logfile set the patch header as
1900 well as the commit message. If none is specified, the header is
1901 empty and the commit message is '[mq]: PATCH'.
1903 Use the -g/--git option to keep the patch in the git extended diff
1904 format. Read the diffs help topic for more information on why this
1905 is important for preserving permission changes and copy/rename
1908 msg = cmdutil.logmessage(opts)
1909 def getmsg(): return ui.edit(msg, ui.username())
1912 if opts.get('edit'):
1913 opts['msg'] = getmsg
1916 setupheaderopts(ui, opts)
1917 q.new(repo, patch, *args, **opts)
1921 def refresh(ui, repo, *pats, **opts):
1922 """update the current patch
1924 If any file patterns are provided, the refreshed patch will
1925 contain only the modifications that match those patterns; the
1926 remaining modifications will remain in the working directory.
1928 If -s/--short is specified, files currently included in the patch
1929 will be refreshed just like matched files and remain in the patch.
1931 hg add/remove/copy/rename work as usual, though you might want to
1932 use git-style patches (-g/--git or [diff] git=1) to track copies
1933 and renames. See the diffs help topic for more information on the
1937 message = cmdutil.logmessage(opts)
1940 ui.write(_("no patches applied\n"))
1943 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1944 patch = q.applied[-1].name
1945 ph = patchheader(q.join(patch))
1946 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1947 setupheaderopts(ui, opts)
1948 ret = q.refresh(repo, pats, msg=message, **opts)
1952 def diff(ui, repo, *pats, **opts):
1953 """diff of the current patch and subsequent modifications
1955 Shows a diff which includes the current patch as well as any
1956 changes which have been made in the working directory since the
1957 last refresh (thus showing what the current patch would become
1960 Use 'hg diff' if you only want to see the changes made since the
1961 last qrefresh, or 'hg export qtip' if you want to see changes made
1962 by the current patch without including changes made since the
1965 repo.mq.diff(repo, pats, opts)
1968 def fold(ui, repo, *files, **opts):
1969 """fold the named patches into the current patch
1971 Patches must not yet be applied. Each patch will be successively
1972 applied to the current patch in the order given. If all the
1973 patches apply successfully, the current patch will be refreshed
1974 with the new cumulative patch, and the folded patches will be
1975 deleted. With -k/--keep, the folded patch files will not be
1978 The header for each folded patch will be concatenated with the
1979 current patch header, separated by a line of '* * *'."""
1984 raise util.Abort(_('qfold requires at least one patch name'))
1985 if not q.check_toppatch(repo):
1986 raise util.Abort(_('No patches applied'))
1987 q.check_localchanges(repo)
1989 message = cmdutil.logmessage(opts)
1992 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1994 parent = q.lookup('qtip')
1999 if p in patches or p == parent:
2000 ui.warn(_('Skipping already folded patch %s') % p)
2002 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2007 ph = patchheader(q.join(p))
2009 messages.append(ph.message)
2011 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2012 if not patchsuccess:
2013 raise util.Abort(_('Error folding patch %s') % p)
2014 patch.updatedir(ui, repo, files)
2017 ph = patchheader(q.join(parent))
2018 message, user = ph.message, ph.user
2019 for msg in messages:
2020 message.append('* * *')
2022 message = '\n'.join(message)
2025 message = ui.edit(message, user or ui.username())
2027 q.refresh(repo, msg=message)
2028 q.delete(repo, patches, opts)
2031 def goto(ui, repo, patch, **opts):
2032 '''push or pop patches until named patch is at top of stack'''
2034 patch = q.lookup(patch)
2035 if q.isapplied(patch):
2036 ret = q.pop(repo, patch, force=opts['force'])
2038 ret = q.push(repo, patch, force=opts['force'])
2042 def guard(ui, repo, *args, **opts):
2043 '''set or print guards for a patch
2045 Guards control whether a patch can be pushed. A patch with no
2046 guards is always pushed. A patch with a positive guard ("+foo") is
2047 pushed only if the qselect command has activated it. A patch with
2048 a negative guard ("-foo") is never pushed if the qselect command
2051 With no arguments, print the currently active guards.
2052 With arguments, set guards for the named patch.
2053 NOTE: Specifying negative guards now requires '--'.
2055 To set guards on another patch:
2056 hg qguard -- other.patch +2.6.17 -stable
2059 guards = q.series_guards[idx] or ['unguarded']
2060 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2065 if args or opts['none']:
2066 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2067 for i in xrange(len(q.series)):
2070 if not args or args[0][0:1] in '-+':
2072 raise util.Abort(_('no patches applied'))
2073 patch = q.applied[-1].name
2074 if patch is None and args[0][0:1] not in '-+':
2077 raise util.Abort(_('no patch to work with'))
2078 if args or opts['none']:
2079 idx = q.find_series(patch)
2081 raise util.Abort(_('no patch named %s') % patch)
2082 q.set_guards(idx, args)
2085 status(q.series.index(q.lookup(patch)))
2087 def header(ui, repo, patch=None):
2088 """print the header of the topmost or specified patch"""
2092 patch = q.lookup(patch)
2095 ui.write('no patches applied\n')
2097 patch = q.lookup('qtip')
2098 ph = patchheader(repo.mq.join(patch))
2100 ui.write('\n'.join(ph.message) + '\n')
2102 def lastsavename(path):
2103 (directory, base) = os.path.split(path)
2104 names = os.listdir(directory)
2105 namere = re.compile("%s.([0-9]+)" % base)
2111 index = int(m.group(1))
2112 if maxindex is None or index > maxindex:
2116 return (os.path.join(directory, maxname), maxindex)
2120 (last, index) = lastsavename(path)
2123 newpath = path + ".%d" % (index + 1)
2126 def push(ui, repo, patch=None, **opts):
2127 """push the next patch onto the stack
2129 When -f/--force is applied, all local changes in patched files
2137 newpath = repo.join(opts['name'])
2139 newpath, i = lastsavename(q.path)
2141 ui.warn(_("no saved queues found, please use -n\n"))
2143 mergeq = queue(ui, repo.join(""), newpath)
2144 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2145 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2146 mergeq=mergeq, all=opts.get('all'))
2149 def pop(ui, repo, patch=None, **opts):
2150 """pop the current patch off the stack
2152 By default, pops off the top of the patch stack. If given a patch
2153 name, keeps popping off patches until the named patch is at the
2158 q = queue(ui, repo.join(""), repo.join(opts['name']))
2159 ui.warn(_('using patch queue: %s\n') % q.path)
2163 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2168 def rename(ui, repo, patch, name=None, **opts):
2171 With one argument, renames the current patch to PATCH1.
2172 With two arguments, renames PATCH1 to PATCH2."""
2181 patch = q.lookup(patch)
2184 ui.write(_('no patches applied\n'))
2186 patch = q.lookup('qtip')
2187 absdest = q.join(name)
2188 if os.path.isdir(absdest):
2189 name = normname(os.path.join(name, os.path.basename(patch)))
2190 absdest = q.join(name)
2191 if os.path.exists(absdest):
2192 raise util.Abort(_('%s already exists') % absdest)
2194 if name in q.series:
2195 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2198 ui.write('renaming %s to %s\n' % (patch, name))
2199 i = q.find_series(patch)
2200 guards = q.guard_re.findall(q.full_series[i])
2201 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2205 info = q.isapplied(patch)
2207 q.applied[info[0]] = statusentry(info[1], name)
2210 util.rename(q.join(patch), absdest)
2215 if r.dirstate[patch] == 'a':
2216 r.dirstate.forget(patch)
2217 r.dirstate.add(name)
2219 if r.dirstate[name] == 'r':
2222 r.remove([patch], False)
2228 def restore(ui, repo, rev, **opts):
2229 """restore the queue state saved by a revision"""
2230 rev = repo.lookup(rev)
2232 q.restore(repo, rev, delete=opts['delete'],
2233 qupdate=opts['update'])
2237 def save(ui, repo, **opts):
2238 """save current queue state"""
2240 message = cmdutil.logmessage(opts)
2241 ret = q.save(repo, msg=message)
2248 newpath = os.path.join(q.basepath, opts['name'])
2249 if os.path.exists(newpath):
2250 if not os.path.isdir(newpath):
2251 raise util.Abort(_('destination %s exists and is not '
2252 'a directory') % newpath)
2253 if not opts['force']:
2254 raise util.Abort(_('destination %s exists, '
2255 'use -f to force') % newpath)
2257 newpath = savename(path)
2258 ui.warn(_("copy %s to %s\n") % (path, newpath))
2259 util.copyfiles(path, newpath)
2262 os.unlink(q.join(q.status_path))
2267 def strip(ui, repo, rev, **opts):
2268 """strip a revision and all its descendants from the repository
2270 If one of the working directory's parent revisions is stripped, the
2271 working directory will be updated to the parent of the stripped
2277 elif opts['nobackup']:
2280 rev = repo.lookup(rev)
2281 p = repo.dirstate.parents()
2286 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2288 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2291 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2294 def select(ui, repo, *args, **opts):
2295 '''set or print guarded patches to push
2297 Use the qguard command to set or print guards on patch, then use
2298 qselect to tell mq which guards to use. A patch will be pushed if
2299 it has no guards or any positive guards match the currently
2300 selected guard, but will not be pushed if any negative guards
2301 match the current guard. For example:
2303 qguard foo.patch -stable (negative guard)
2304 qguard bar.patch +stable (positive guard)
2307 This activates the "stable" guard. mq will skip foo.patch (because
2308 it has a negative match) but push bar.patch (because it has a
2311 With no arguments, prints the currently active guards.
2312 With one argument, sets the active guard.
2314 Use -n/--none to deactivate guards (no other arguments needed).
2315 When no guards are active, patches with positive guards are
2316 skipped and patches with negative guards are pushed.
2318 qselect can change the guards on applied patches. It does not pop
2319 guarded patches by default. Use --pop to pop back to the last
2320 applied patch that is not guarded. Use --reapply (which implies
2321 --pop) to push back to the current patch afterwards, but skip
2324 Use -s/--series to print a list of all guards in the series file
2325 (no other arguments needed). Use -v for more information.'''
2329 if args or opts['none']:
2330 old_unapplied = q.unapplied(repo)
2331 old_guarded = [i for i in xrange(len(q.applied)) if
2332 not q.pushable(i)[0]]
2336 ui.status(_('guards deactivated\n'))
2337 if not opts['pop'] and not opts['reapply']:
2338 unapplied = q.unapplied(repo)
2339 guarded = [i for i in xrange(len(q.applied))
2340 if not q.pushable(i)[0]]
2341 if len(unapplied) != len(old_unapplied):
2342 ui.status(_('number of unguarded, unapplied patches has '
2343 'changed from %d to %d\n') %
2344 (len(old_unapplied), len(unapplied)))
2345 if len(guarded) != len(old_guarded):
2346 ui.status(_('number of guarded, applied patches has changed '
2347 'from %d to %d\n') %
2348 (len(old_guarded), len(guarded)))
2349 elif opts['series']:
2352 for gs in q.series_guards:
2356 guards.setdefault(g, 0)
2359 guards['NONE'] = noguards
2360 guards = guards.items()
2361 guards.sort(key=lambda x: x[0][1:])
2363 ui.note(_('guards in series file:\n'))
2364 for guard, count in guards:
2365 ui.note('%2d ' % count)
2366 ui.write(guard, '\n')
2368 ui.note(_('no guards in series file\n'))
2371 ui.note(_('active guards:\n'))
2375 ui.write(_('no active guards\n'))
2376 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2378 if opts['pop'] or opts['reapply']:
2379 for i in xrange(len(q.applied)):
2380 pushable, reason = q.pushable(i)
2382 ui.status(_('popping guarded patches\n'))
2385 q.pop(repo, all=True)
2392 ui.status(_('reapplying unguarded patches\n'))
2393 q.push(repo, reapply)
2397 def finish(ui, repo, *revrange, **opts):
2398 """move applied patches into repository history
2400 Finishes the specified revisions (corresponding to applied
2401 patches) by moving them out of mq control into regular repository
2404 Accepts a revision range or the -a/--applied option. If --applied
2405 is specified, all applied mq revisions are removed from mq
2406 control. Otherwise, the given revisions must be at the base of the
2407 stack of applied patches.
2409 This can be especially useful if your changes have been applied to
2410 an upstream repository, or if you are about to push your changes
2413 if not opts['applied'] and not revrange:
2414 raise util.Abort(_('no revisions specified'))
2415 elif opts['applied']:
2416 revrange = ('qbase:qtip',) + revrange
2420 ui.status(_('no patches applied\n'))
2423 revs = cmdutil.revrange(repo, revrange)
2424 q.finish(repo, revs)
2428 def reposetup(ui, repo):
2429 class mqrepo(repo.__class__):
2432 return queue(self.ui, self.join(""))
2434 def abort_if_wdir_patched(self, errmsg, force=False):
2435 if self.mq.applied and not force:
2436 parent = hex(self.dirstate.parents()[0])
2437 if parent in [s.rev for s in self.mq.applied]:
2438 raise util.Abort(errmsg)
2440 def commit(self, text="", user=None, date=None, match=None,
2441 force=False, editor=False, extra={}):
2442 self.abort_if_wdir_patched(
2443 _('cannot commit over an applied mq patch'),
2446 return super(mqrepo, self).commit(text, user, date, match, force,
2449 def push(self, remote, force=False, revs=None):
2450 if self.mq.applied and not force and not revs:
2451 raise util.Abort(_('source has mq patches applied'))
2452 return super(mqrepo, self).push(remote, force, revs)
2454 def _findtags(self):
2455 '''augment tags from base class with patch tags'''
2456 result = super(mqrepo, self)._findtags()
2462 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2464 if mqtags[-1][0] not in self.changelog.nodemap:
2465 self.ui.warn(_('mq status file refers to unknown node %s\n')
2466 % short(mqtags[-1][0]))
2469 mqtags.append((mqtags[-1][0], 'qtip'))
2470 mqtags.append((mqtags[0][0], 'qbase'))
2471 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2473 for patch in mqtags:
2474 if patch[1] in tags:
2475 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2478 tags[patch[1]] = patch[0]
2482 def _branchtags(self, partial, lrev):
2485 return super(mqrepo, self)._branchtags(partial, lrev)
2488 qbasenode = bin(q.applied[0].rev)
2489 if qbasenode not in cl.nodemap:
2490 self.ui.warn(_('mq status file refers to unknown node %s\n')
2492 return super(mqrepo, self)._branchtags(partial, lrev)
2494 qbase = cl.rev(qbasenode)
2497 # update the cache (excluding the patches) and save it
2498 self._updatebranchcache(partial, lrev+1, qbase)
2499 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2501 # if start = qbase, the cache is as updated as it should be.
2502 # if start > qbase, the cache includes (part of) the patches.
2503 # we might as well use it, but we won't save it.
2505 # update the cache up to the tip
2506 self._updatebranchcache(partial, start, len(cl))
2511 repo.__class__ = mqrepo
2513 def mqimport(orig, ui, repo, *args, **kwargs):
2514 if hasattr(repo, 'abort_if_wdir_patched'):
2515 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2516 kwargs.get('force'))
2517 return orig(ui, repo, *args, **kwargs)
2520 extensions.wrapcommand(commands.table, 'import', mqimport)
2522 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2525 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2528 [('', 'pull', None, _('use pull protocol to copy metadata')),
2529 ('U', 'noupdate', None, _('do not update the new working directories')),
2530 ('', 'uncompressed', None,
2531 _('use uncompressed transfer (fast over LAN)')),
2532 ('p', 'patches', '', _('location of source patch repository')),
2533 ] + commands.remoteopts,
2534 _('hg qclone [OPTION]... SOURCE [DEST]')),
2537 commands.table["^commit|ci"][1],
2538 _('hg qcommit [OPTION]... [FILE]...')),
2541 commands.diffopts + commands.diffopts2 + commands.walkopts,
2542 _('hg qdiff [OPTION]... [FILE]...')),
2543 "qdelete|qremove|qrm":
2545 [('k', 'keep', None, _('keep patch file')),
2546 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2547 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2550 [('e', 'edit', None, _('edit patch header')),
2551 ('k', 'keep', None, _('keep folded patch files')),
2552 ] + commands.commitopts,
2553 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2556 [('f', 'force', None, _('overwrite any local changes'))],
2557 _('hg qgoto [OPTION]... PATCH')),
2560 [('l', 'list', None, _('list all patches and guards')),
2561 ('n', 'none', None, _('drop all guards'))],
2562 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2563 'qheader': (header, [], _('hg qheader [PATCH]')),
2566 [('e', 'existing', None, _('import file in patch directory')),
2567 ('n', 'name', '', _('name of patch file')),
2568 ('f', 'force', None, _('overwrite existing files')),
2569 ('r', 'rev', [], _('place existing revisions under mq control')),
2570 ('g', 'git', None, _('use git extended diff format')),
2571 ('P', 'push', None, _('qpush after importing'))],
2572 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2575 [('c', 'create-repo', None, _('create queue repository'))],
2576 _('hg qinit [-c]')),
2579 [('e', 'edit', None, _('edit commit message')),
2580 ('f', 'force', None, _('import uncommitted changes into patch')),
2581 ('g', 'git', None, _('use git extended diff format')),
2582 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2583 ('u', 'user', '', _('add "From: <given user>" to patch')),
2584 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2585 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2586 ] + commands.walkopts + commands.commitopts,
2587 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2588 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2589 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2592 [('a', 'all', None, _('pop all patches')),
2593 ('n', 'name', '', _('queue name to pop')),
2594 ('f', 'force', None, _('forget any local changes'))],
2595 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2598 [('f', 'force', None, _('apply if the patch has rejects')),
2599 ('l', 'list', None, _('list patch name in commit text')),
2600 ('a', 'all', None, _('apply all patches')),
2601 ('m', 'merge', None, _('merge from another queue')),
2602 ('n', 'name', '', _('merge queue name'))],
2603 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2606 [('e', 'edit', None, _('edit commit message')),
2607 ('g', 'git', None, _('use git extended diff format')),
2608 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2609 ('U', 'currentuser', None, _('add/update author field in patch with current user')),
2610 ('u', 'user', '', _('add/update author field in patch with given user')),
2611 ('D', 'currentdate', None, _('add/update date field in patch with current date')),
2612 ('d', 'date', '', _('add/update date field in patch with given date'))
2613 ] + commands.walkopts + commands.commitopts,
2614 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2616 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2619 [('d', 'delete', None, _('delete save entry')),
2620 ('u', 'update', None, _('update queue working directory'))],
2621 _('hg qrestore [-d] [-u] REV')),
2624 [('c', 'copy', None, _('copy patch directory')),
2625 ('n', 'name', '', _('copy directory name')),
2626 ('e', 'empty', None, _('clear queue status file')),
2627 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2628 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2631 [('n', 'none', None, _('disable all guards')),
2632 ('s', 'series', None, _('list all guards in series file')),
2633 ('', 'pop', None, _('pop to before first guarded applied patch')),
2634 ('', 'reapply', None, _('pop, then reapply patches'))],
2635 _('hg qselect [OPTION]... [GUARD]...')),
2638 [('m', 'missing', None, _('print patches not in series')),
2640 _('hg qseries [-ms]')),
2643 [('f', 'force', None, _('force removal with local changes')),
2644 ('b', 'backup', None, _('bundle unrelated changesets')),
2645 ('n', 'nobackup', None, _('no backups'))],
2646 _('hg strip [-f] [-b] [-n] REV')),
2647 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2648 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2651 [('a', 'applied', None, _('finish all applied changesets'))],
2652 _('hg qfinish [-a] [REV]...')),