eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/mq.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # mq.py - patch queues for mercurial
       
     2 #
       
     3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 '''manage a stack of patches
       
     9 
       
    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).
       
    13 
       
    14 Known patches are represented as patch files in the .hg/patches
       
    15 directory. Applied patches are both patch files and changesets.
       
    16 
       
    17 Common tasks (use :hg:`help command` for more details)::
       
    18 
       
    19   create new patch                          qnew
       
    20   import existing patch                     qimport
       
    21 
       
    22   print patch series                        qseries
       
    23   print applied patches                     qapplied
       
    24 
       
    25   add known patch to applied stack          qpush
       
    26   remove patch from applied stack           qpop
       
    27   refresh contents of top applied patch     qrefresh
       
    28 
       
    29 By default, mq will automatically use git patches when required to
       
    30 avoid losing file mode changes, copy records, binary files or empty
       
    31 files creations or deletions. This behaviour can be configured with::
       
    32 
       
    33   [mq]
       
    34   git = auto/keep/yes/no
       
    35 
       
    36 If set to 'keep', mq will obey the [diff] section configuration while
       
    37 preserving existing git patches upon qrefresh. If set to 'yes' or
       
    38 'no', mq will override the [diff] section and always generate git or
       
    39 regular patches, possibly losing data in the second case.
       
    40 
       
    41 You will by default be managing a patch queue named "patches". You can
       
    42 create other, independent patch queues with the :hg:`qqueue` command.
       
    43 '''
       
    44 
       
    45 from mercurial.i18n import _
       
    46 from mercurial.node import bin, hex, short, nullid, nullrev
       
    47 from mercurial.lock import release
       
    48 from mercurial import commands, cmdutil, hg, patch, util
       
    49 from mercurial import repair, extensions, url, error
       
    50 import os, sys, re, errno, shutil
       
    51 
       
    52 commands.norepo += " qclone"
       
    53 
       
    54 # Patch names looks like unix-file names.
       
    55 # They must be joinable with queue directory and result in the patch path.
       
    56 normname = util.normpath
       
    57 
       
    58 class statusentry(object):
       
    59     def __init__(self, node, name):
       
    60         self.node, self.name = node, name
       
    61     def __repr__(self):
       
    62         return hex(self.node) + ':' + self.name
       
    63 
       
    64 class patchheader(object):
       
    65     def __init__(self, pf, plainmode=False):
       
    66         def eatdiff(lines):
       
    67             while lines:
       
    68                 l = lines[-1]
       
    69                 if (l.startswith("diff -") or
       
    70                     l.startswith("Index:") or
       
    71                     l.startswith("===========")):
       
    72                     del lines[-1]
       
    73                 else:
       
    74                     break
       
    75         def eatempty(lines):
       
    76             while lines:
       
    77                 if not lines[-1].strip():
       
    78                     del lines[-1]
       
    79                 else:
       
    80                     break
       
    81 
       
    82         message = []
       
    83         comments = []
       
    84         user = None
       
    85         date = None
       
    86         parent = None
       
    87         format = None
       
    88         subject = None
       
    89         diffstart = 0
       
    90 
       
    91         for line in file(pf):
       
    92             line = line.rstrip()
       
    93             if (line.startswith('diff --git')
       
    94                 or (diffstart and line.startswith('+++ '))):
       
    95                 diffstart = 2
       
    96                 break
       
    97             diffstart = 0 # reset
       
    98             if line.startswith("--- "):
       
    99                 diffstart = 1
       
   100                 continue
       
   101             elif format == "hgpatch":
       
   102                 # parse values when importing the result of an hg export
       
   103                 if line.startswith("# User "):
       
   104                     user = line[7:]
       
   105                 elif line.startswith("# Date "):
       
   106                     date = line[7:]
       
   107                 elif line.startswith("# Parent "):
       
   108                     parent = line[9:]
       
   109                 elif not line.startswith("# ") and line:
       
   110                     message.append(line)
       
   111                     format = None
       
   112             elif line == '# HG changeset patch':
       
   113                 message = []
       
   114                 format = "hgpatch"
       
   115             elif (format != "tagdone" and (line.startswith("Subject: ") or
       
   116                                            line.startswith("subject: "))):
       
   117                 subject = line[9:]
       
   118                 format = "tag"
       
   119             elif (format != "tagdone" and (line.startswith("From: ") or
       
   120                                            line.startswith("from: "))):
       
   121                 user = line[6:]
       
   122                 format = "tag"
       
   123             elif (format != "tagdone" and (line.startswith("Date: ") or
       
   124                                            line.startswith("date: "))):
       
   125                 date = line[6:]
       
   126                 format = "tag"
       
   127             elif format == "tag" and line == "":
       
   128                 # when looking for tags (subject: from: etc) they
       
   129                 # end once you find a blank line in the source
       
   130                 format = "tagdone"
       
   131             elif message or line:
       
   132                 message.append(line)
       
   133             comments.append(line)
       
   134 
       
   135         eatdiff(message)
       
   136         eatdiff(comments)
       
   137         eatempty(message)
       
   138         eatempty(comments)
       
   139 
       
   140         # make sure message isn't empty
       
   141         if format and format.startswith("tag") and subject:
       
   142             message.insert(0, "")
       
   143             message.insert(0, subject)
       
   144 
       
   145         self.message = message
       
   146         self.comments = comments
       
   147         self.user = user
       
   148         self.date = date
       
   149         self.parent = parent
       
   150         self.haspatch = diffstart > 1
       
   151         self.plainmode = plainmode
       
   152 
       
   153     def setuser(self, user):
       
   154         if not self.updateheader(['From: ', '# User '], user):
       
   155             try:
       
   156                 patchheaderat = self.comments.index('# HG changeset patch')
       
   157                 self.comments.insert(patchheaderat + 1, '# User ' + user)
       
   158             except ValueError:
       
   159                 if self.plainmode or self._hasheader(['Date: ']):
       
   160                     self.comments = ['From: ' + user] + self.comments
       
   161                 else:
       
   162                     tmp = ['# HG changeset patch', '# User ' + user, '']
       
   163                     self.comments = tmp + self.comments
       
   164         self.user = user
       
   165 
       
   166     def setdate(self, date):
       
   167         if not self.updateheader(['Date: ', '# Date '], date):
       
   168             try:
       
   169                 patchheaderat = self.comments.index('# HG changeset patch')
       
   170                 self.comments.insert(patchheaderat + 1, '# Date ' + date)
       
   171             except ValueError:
       
   172                 if self.plainmode or self._hasheader(['From: ']):
       
   173                     self.comments = ['Date: ' + date] + self.comments
       
   174                 else:
       
   175                     tmp = ['# HG changeset patch', '# Date ' + date, '']
       
   176                     self.comments = tmp + self.comments
       
   177         self.date = date
       
   178 
       
   179     def setparent(self, parent):
       
   180         if not self.updateheader(['# Parent '], parent):
       
   181             try:
       
   182                 patchheaderat = self.comments.index('# HG changeset patch')
       
   183                 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
       
   184             except ValueError:
       
   185                 pass
       
   186         self.parent = parent
       
   187 
       
   188     def setmessage(self, message):
       
   189         if self.comments:
       
   190             self._delmsg()
       
   191         self.message = [message]
       
   192         self.comments += self.message
       
   193 
       
   194     def updateheader(self, prefixes, new):
       
   195         '''Update all references to a field in the patch header.
       
   196         Return whether the field is present.'''
       
   197         res = False
       
   198         for prefix in prefixes:
       
   199             for i in xrange(len(self.comments)):
       
   200                 if self.comments[i].startswith(prefix):
       
   201                     self.comments[i] = prefix + new
       
   202                     res = True
       
   203                     break
       
   204         return res
       
   205 
       
   206     def _hasheader(self, prefixes):
       
   207         '''Check if a header starts with any of the given prefixes.'''
       
   208         for prefix in prefixes:
       
   209             for comment in self.comments:
       
   210                 if comment.startswith(prefix):
       
   211                     return True
       
   212         return False
       
   213 
       
   214     def __str__(self):
       
   215         if not self.comments:
       
   216             return ''
       
   217         return '\n'.join(self.comments) + '\n\n'
       
   218 
       
   219     def _delmsg(self):
       
   220         '''Remove existing message, keeping the rest of the comments fields.
       
   221         If comments contains 'subject: ', message will prepend
       
   222         the field and a blank line.'''
       
   223         if self.message:
       
   224             subj = 'subject: ' + self.message[0].lower()
       
   225             for i in xrange(len(self.comments)):
       
   226                 if subj == self.comments[i].lower():
       
   227                     del self.comments[i]
       
   228                     self.message = self.message[2:]
       
   229                     break
       
   230         ci = 0
       
   231         for mi in self.message:
       
   232             while mi != self.comments[ci]:
       
   233                 ci += 1
       
   234             del self.comments[ci]
       
   235 
       
   236 class queue(object):
       
   237     def __init__(self, ui, path, patchdir=None):
       
   238         self.basepath = path
       
   239         try:
       
   240             fh = open(os.path.join(path, 'patches.queue'))
       
   241             cur = fh.read().rstrip()
       
   242             if not cur:
       
   243                 curpath = os.path.join(path, 'patches')
       
   244             else:
       
   245                 curpath = os.path.join(path, 'patches-' + cur)
       
   246         except IOError:
       
   247             curpath = os.path.join(path, 'patches')
       
   248         self.path = patchdir or curpath
       
   249         self.opener = util.opener(self.path)
       
   250         self.ui = ui
       
   251         self.applied_dirty = 0
       
   252         self.series_dirty = 0
       
   253         self.added = []
       
   254         self.series_path = "series"
       
   255         self.status_path = "status"
       
   256         self.guards_path = "guards"
       
   257         self.active_guards = None
       
   258         self.guards_dirty = False
       
   259         # Handle mq.git as a bool with extended values
       
   260         try:
       
   261             gitmode = ui.configbool('mq', 'git', None)
       
   262             if gitmode is None:
       
   263                 raise error.ConfigError()
       
   264             self.gitmode = gitmode and 'yes' or 'no'
       
   265         except error.ConfigError:
       
   266             self.gitmode = ui.config('mq', 'git', 'auto').lower()
       
   267         self.plainmode = ui.configbool('mq', 'plain', False)
       
   268 
       
   269     @util.propertycache
       
   270     def applied(self):
       
   271         if os.path.exists(self.join(self.status_path)):
       
   272             def parse(l):
       
   273                 n, name = l.split(':', 1)
       
   274                 return statusentry(bin(n), name)
       
   275             lines = self.opener(self.status_path).read().splitlines()
       
   276             return [parse(l) for l in lines]
       
   277         return []
       
   278 
       
   279     @util.propertycache
       
   280     def full_series(self):
       
   281         if os.path.exists(self.join(self.series_path)):
       
   282             return self.opener(self.series_path).read().splitlines()
       
   283         return []
       
   284 
       
   285     @util.propertycache
       
   286     def series(self):
       
   287         self.parse_series()
       
   288         return self.series
       
   289 
       
   290     @util.propertycache
       
   291     def series_guards(self):
       
   292         self.parse_series()
       
   293         return self.series_guards
       
   294 
       
   295     def invalidate(self):
       
   296         for a in 'applied full_series series series_guards'.split():
       
   297             if a in self.__dict__:
       
   298                 delattr(self, a)
       
   299         self.applied_dirty = 0
       
   300         self.series_dirty = 0
       
   301         self.guards_dirty = False
       
   302         self.active_guards = None
       
   303 
       
   304     def diffopts(self, opts={}, patchfn=None):
       
   305         diffopts = patch.diffopts(self.ui, opts)
       
   306         if self.gitmode == 'auto':
       
   307             diffopts.upgrade = True
       
   308         elif self.gitmode == 'keep':
       
   309             pass
       
   310         elif self.gitmode in ('yes', 'no'):
       
   311             diffopts.git = self.gitmode == 'yes'
       
   312         else:
       
   313             raise util.Abort(_('mq.git option can be auto/keep/yes/no'
       
   314                                ' got %s') % self.gitmode)
       
   315         if patchfn:
       
   316             diffopts = self.patchopts(diffopts, patchfn)
       
   317         return diffopts
       
   318 
       
   319     def patchopts(self, diffopts, *patches):
       
   320         """Return a copy of input diff options with git set to true if
       
   321         referenced patch is a git patch and should be preserved as such.
       
   322         """
       
   323         diffopts = diffopts.copy()
       
   324         if not diffopts.git and self.gitmode == 'keep':
       
   325             for patchfn in patches:
       
   326                 patchf = self.opener(patchfn, 'r')
       
   327                 # if the patch was a git patch, refresh it as a git patch
       
   328                 for line in patchf:
       
   329                     if line.startswith('diff --git'):
       
   330                         diffopts.git = True
       
   331                         break
       
   332                 patchf.close()
       
   333         return diffopts
       
   334 
       
   335     def join(self, *p):
       
   336         return os.path.join(self.path, *p)
       
   337 
       
   338     def find_series(self, patch):
       
   339         def matchpatch(l):
       
   340             l = l.split('#', 1)[0]
       
   341             return l.strip() == patch
       
   342         for index, l in enumerate(self.full_series):
       
   343             if matchpatch(l):
       
   344                 return index
       
   345         return None
       
   346 
       
   347     guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
       
   348 
       
   349     def parse_series(self):
       
   350         self.series = []
       
   351         self.series_guards = []
       
   352         for l in self.full_series:
       
   353             h = l.find('#')
       
   354             if h == -1:
       
   355                 patch = l
       
   356                 comment = ''
       
   357             elif h == 0:
       
   358                 continue
       
   359             else:
       
   360                 patch = l[:h]
       
   361                 comment = l[h:]
       
   362             patch = patch.strip()
       
   363             if patch:
       
   364                 if patch in self.series:
       
   365                     raise util.Abort(_('%s appears more than once in %s') %
       
   366                                      (patch, self.join(self.series_path)))
       
   367                 self.series.append(patch)
       
   368                 self.series_guards.append(self.guard_re.findall(comment))
       
   369 
       
   370     def check_guard(self, guard):
       
   371         if not guard:
       
   372             return _('guard cannot be an empty string')
       
   373         bad_chars = '# \t\r\n\f'
       
   374         first = guard[0]
       
   375         if first in '-+':
       
   376             return (_('guard %r starts with invalid character: %r') %
       
   377                       (guard, first))
       
   378         for c in bad_chars:
       
   379             if c in guard:
       
   380                 return _('invalid character in guard %r: %r') % (guard, c)
       
   381 
       
   382     def set_active(self, guards):
       
   383         for guard in guards:
       
   384             bad = self.check_guard(guard)
       
   385             if bad:
       
   386                 raise util.Abort(bad)
       
   387         guards = sorted(set(guards))
       
   388         self.ui.debug('active guards: %s\n' % ' '.join(guards))
       
   389         self.active_guards = guards
       
   390         self.guards_dirty = True
       
   391 
       
   392     def active(self):
       
   393         if self.active_guards is None:
       
   394             self.active_guards = []
       
   395             try:
       
   396                 guards = self.opener(self.guards_path).read().split()
       
   397             except IOError, err:
       
   398                 if err.errno != errno.ENOENT:
       
   399                     raise
       
   400                 guards = []
       
   401             for i, guard in enumerate(guards):
       
   402                 bad = self.check_guard(guard)
       
   403                 if bad:
       
   404                     self.ui.warn('%s:%d: %s\n' %
       
   405                                  (self.join(self.guards_path), i + 1, bad))
       
   406                 else:
       
   407                     self.active_guards.append(guard)
       
   408         return self.active_guards
       
   409 
       
   410     def set_guards(self, idx, guards):
       
   411         for g in guards:
       
   412             if len(g) < 2:
       
   413                 raise util.Abort(_('guard %r too short') % g)
       
   414             if g[0] not in '-+':
       
   415                 raise util.Abort(_('guard %r starts with invalid char') % g)
       
   416             bad = self.check_guard(g[1:])
       
   417             if bad:
       
   418                 raise util.Abort(bad)
       
   419         drop = self.guard_re.sub('', self.full_series[idx])
       
   420         self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
       
   421         self.parse_series()
       
   422         self.series_dirty = True
       
   423 
       
   424     def pushable(self, idx):
       
   425         if isinstance(idx, str):
       
   426             idx = self.series.index(idx)
       
   427         patchguards = self.series_guards[idx]
       
   428         if not patchguards:
       
   429             return True, None
       
   430         guards = self.active()
       
   431         exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
       
   432         if exactneg:
       
   433             return False, exactneg[0]
       
   434         pos = [g for g in patchguards if g[0] == '+']
       
   435         exactpos = [g for g in pos if g[1:] in guards]
       
   436         if pos:
       
   437             if exactpos:
       
   438                 return True, exactpos[0]
       
   439             return False, pos
       
   440         return True, ''
       
   441 
       
   442     def explain_pushable(self, idx, all_patches=False):
       
   443         write = all_patches and self.ui.write or self.ui.warn
       
   444         if all_patches or self.ui.verbose:
       
   445             if isinstance(idx, str):
       
   446                 idx = self.series.index(idx)
       
   447             pushable, why = self.pushable(idx)
       
   448             if all_patches and pushable:
       
   449                 if why is None:
       
   450                     write(_('allowing %s - no guards in effect\n') %
       
   451                           self.series[idx])
       
   452                 else:
       
   453                     if not why:
       
   454                         write(_('allowing %s - no matching negative guards\n') %
       
   455                               self.series[idx])
       
   456                     else:
       
   457                         write(_('allowing %s - guarded by %r\n') %
       
   458                               (self.series[idx], why))
       
   459             if not pushable:
       
   460                 if why:
       
   461                     write(_('skipping %s - guarded by %r\n') %
       
   462                           (self.series[idx], why))
       
   463                 else:
       
   464                     write(_('skipping %s - no matching guards\n') %
       
   465                           self.series[idx])
       
   466 
       
   467     def save_dirty(self):
       
   468         def write_list(items, path):
       
   469             fp = self.opener(path, 'w')
       
   470             for i in items:
       
   471                 fp.write("%s\n" % i)
       
   472             fp.close()
       
   473         if self.applied_dirty:
       
   474             write_list(map(str, self.applied), self.status_path)
       
   475         if self.series_dirty:
       
   476             write_list(self.full_series, self.series_path)
       
   477         if self.guards_dirty:
       
   478             write_list(self.active_guards, self.guards_path)
       
   479         if self.added:
       
   480             qrepo = self.qrepo()
       
   481             if qrepo:
       
   482                 qrepo[None].add(f for f in self.added if f not in qrepo[None])
       
   483             self.added = []
       
   484 
       
   485     def removeundo(self, repo):
       
   486         undo = repo.sjoin('undo')
       
   487         if not os.path.exists(undo):
       
   488             return
       
   489         try:
       
   490             os.unlink(undo)
       
   491         except OSError, inst:
       
   492             self.ui.warn(_('error removing undo: %s\n') % str(inst))
       
   493 
       
   494     def printdiff(self, repo, diffopts, node1, node2=None, files=None,
       
   495                   fp=None, changes=None, opts={}):
       
   496         stat = opts.get('stat')
       
   497         m = cmdutil.match(repo, files, opts)
       
   498         cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2,  m,
       
   499                                changes, stat, fp)
       
   500 
       
   501     def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
       
   502         # first try just applying the patch
       
   503         (err, n) = self.apply(repo, [patch], update_status=False,
       
   504                               strict=True, merge=rev)
       
   505 
       
   506         if err == 0:
       
   507             return (err, n)
       
   508 
       
   509         if n is None:
       
   510             raise util.Abort(_("apply failed for patch %s") % patch)
       
   511 
       
   512         self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
       
   513 
       
   514         # apply failed, strip away that rev and merge.
       
   515         hg.clean(repo, head)
       
   516         self.strip(repo, [n], update=False, backup='strip')
       
   517 
       
   518         ctx = repo[rev]
       
   519         ret = hg.merge(repo, rev)
       
   520         if ret:
       
   521             raise util.Abort(_("update returned %d") % ret)
       
   522         n = repo.commit(ctx.description(), ctx.user(), force=True)
       
   523         if n is None:
       
   524             raise util.Abort(_("repo commit failed"))
       
   525         try:
       
   526             ph = patchheader(mergeq.join(patch), self.plainmode)
       
   527         except:
       
   528             raise util.Abort(_("unable to read %s") % patch)
       
   529 
       
   530         diffopts = self.patchopts(diffopts, patch)
       
   531         patchf = self.opener(patch, "w")
       
   532         comments = str(ph)
       
   533         if comments:
       
   534             patchf.write(comments)
       
   535         self.printdiff(repo, diffopts, head, n, fp=patchf)
       
   536         patchf.close()
       
   537         self.removeundo(repo)
       
   538         return (0, n)
       
   539 
       
   540     def qparents(self, repo, rev=None):
       
   541         if rev is None:
       
   542             (p1, p2) = repo.dirstate.parents()
       
   543             if p2 == nullid:
       
   544                 return p1
       
   545             if not self.applied:
       
   546                 return None
       
   547             return self.applied[-1].node
       
   548         p1, p2 = repo.changelog.parents(rev)
       
   549         if p2 != nullid and p2 in [x.node for x in self.applied]:
       
   550             return p2
       
   551         return p1
       
   552 
       
   553     def mergepatch(self, repo, mergeq, series, diffopts):
       
   554         if not self.applied:
       
   555             # each of the patches merged in will have two parents.  This
       
   556             # can confuse the qrefresh, qdiff, and strip code because it
       
   557             # needs to know which parent is actually in the patch queue.
       
   558             # so, we insert a merge marker with only one parent.  This way
       
   559             # the first patch in the queue is never a merge patch
       
   560             #
       
   561             pname = ".hg.patches.merge.marker"
       
   562             n = repo.commit('[mq]: merge marker', force=True)
       
   563             self.removeundo(repo)
       
   564             self.applied.append(statusentry(n, pname))
       
   565             self.applied_dirty = 1
       
   566 
       
   567         head = self.qparents(repo)
       
   568 
       
   569         for patch in series:
       
   570             patch = mergeq.lookup(patch, strict=True)
       
   571             if not patch:
       
   572                 self.ui.warn(_("patch %s does not exist\n") % patch)
       
   573                 return (1, None)
       
   574             pushable, reason = self.pushable(patch)
       
   575             if not pushable:
       
   576                 self.explain_pushable(patch, all_patches=True)
       
   577                 continue
       
   578             info = mergeq.isapplied(patch)
       
   579             if not info:
       
   580                 self.ui.warn(_("patch %s is not applied\n") % patch)
       
   581                 return (1, None)
       
   582             rev = info[1]
       
   583             err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
       
   584             if head:
       
   585                 self.applied.append(statusentry(head, patch))
       
   586                 self.applied_dirty = 1
       
   587             if err:
       
   588                 return (err, head)
       
   589         self.save_dirty()
       
   590         return (0, head)
       
   591 
       
   592     def patch(self, repo, patchfile):
       
   593         '''Apply patchfile  to the working directory.
       
   594         patchfile: name of patch file'''
       
   595         files = {}
       
   596         try:
       
   597             fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
       
   598                                files=files, eolmode=None)
       
   599         except Exception, inst:
       
   600             self.ui.note(str(inst) + '\n')
       
   601             if not self.ui.verbose:
       
   602                 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
       
   603             return (False, files, False)
       
   604 
       
   605         return (True, files, fuzz)
       
   606 
       
   607     def apply(self, repo, series, list=False, update_status=True,
       
   608               strict=False, patchdir=None, merge=None, all_files=None):
       
   609         wlock = lock = tr = None
       
   610         try:
       
   611             wlock = repo.wlock()
       
   612             lock = repo.lock()
       
   613             tr = repo.transaction("qpush")
       
   614             try:
       
   615                 ret = self._apply(repo, series, list, update_status,
       
   616                                   strict, patchdir, merge, all_files=all_files)
       
   617                 tr.close()
       
   618                 self.save_dirty()
       
   619                 return ret
       
   620             except:
       
   621                 try:
       
   622                     tr.abort()
       
   623                 finally:
       
   624                     repo.invalidate()
       
   625                     repo.dirstate.invalidate()
       
   626                 raise
       
   627         finally:
       
   628             release(tr, lock, wlock)
       
   629             self.removeundo(repo)
       
   630 
       
   631     def _apply(self, repo, series, list=False, update_status=True,
       
   632                strict=False, patchdir=None, merge=None, all_files=None):
       
   633         '''returns (error, hash)
       
   634         error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
       
   635         # TODO unify with commands.py
       
   636         if not patchdir:
       
   637             patchdir = self.path
       
   638         err = 0
       
   639         n = None
       
   640         for patchname in series:
       
   641             pushable, reason = self.pushable(patchname)
       
   642             if not pushable:
       
   643                 self.explain_pushable(patchname, all_patches=True)
       
   644                 continue
       
   645             self.ui.status(_("applying %s\n") % patchname)
       
   646             pf = os.path.join(patchdir, patchname)
       
   647 
       
   648             try:
       
   649                 ph = patchheader(self.join(patchname), self.plainmode)
       
   650             except:
       
   651                 self.ui.warn(_("unable to read %s\n") % patchname)
       
   652                 err = 1
       
   653                 break
       
   654 
       
   655             message = ph.message
       
   656             if not message:
       
   657                 # The commit message should not be translated
       
   658                 message = "imported patch %s\n" % patchname
       
   659             else:
       
   660                 if list:
       
   661                     # The commit message should not be translated
       
   662                     message.append("\nimported patch %s" % patchname)
       
   663                 message = '\n'.join(message)
       
   664 
       
   665             if ph.haspatch:
       
   666                 (patcherr, files, fuzz) = self.patch(repo, pf)
       
   667                 if all_files is not None:
       
   668                     all_files.update(files)
       
   669                 patcherr = not patcherr
       
   670             else:
       
   671                 self.ui.warn(_("patch %s is empty\n") % patchname)
       
   672                 patcherr, files, fuzz = 0, [], 0
       
   673 
       
   674             if merge and files:
       
   675                 # Mark as removed/merged and update dirstate parent info
       
   676                 removed = []
       
   677                 merged = []
       
   678                 for f in files:
       
   679                     if os.path.lexists(repo.wjoin(f)):
       
   680                         merged.append(f)
       
   681                     else:
       
   682                         removed.append(f)
       
   683                 for f in removed:
       
   684                     repo.dirstate.remove(f)
       
   685                 for f in merged:
       
   686                     repo.dirstate.merge(f)
       
   687                 p1, p2 = repo.dirstate.parents()
       
   688                 repo.dirstate.setparents(p1, merge)
       
   689 
       
   690             files = cmdutil.updatedir(self.ui, repo, files)
       
   691             match = cmdutil.matchfiles(repo, files or [])
       
   692             n = repo.commit(message, ph.user, ph.date, match=match, force=True)
       
   693 
       
   694             if n is None:
       
   695                 raise util.Abort(_("repository commit failed"))
       
   696 
       
   697             if update_status:
       
   698                 self.applied.append(statusentry(n, patchname))
       
   699 
       
   700             if patcherr:
       
   701                 self.ui.warn(_("patch failed, rejects left in working dir\n"))
       
   702                 err = 2
       
   703                 break
       
   704 
       
   705             if fuzz and strict:
       
   706                 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
       
   707                 err = 3
       
   708                 break
       
   709         return (err, n)
       
   710 
       
   711     def _cleanup(self, patches, numrevs, keep=False):
       
   712         if not keep:
       
   713             r = self.qrepo()
       
   714             if r:
       
   715                 r[None].remove(patches, True)
       
   716             else:
       
   717                 for p in patches:
       
   718                     os.unlink(self.join(p))
       
   719 
       
   720         if numrevs:
       
   721             del self.applied[:numrevs]
       
   722             self.applied_dirty = 1
       
   723 
       
   724         for i in sorted([self.find_series(p) for p in patches], reverse=True):
       
   725             del self.full_series[i]
       
   726         self.parse_series()
       
   727         self.series_dirty = 1
       
   728 
       
   729     def _revpatches(self, repo, revs):
       
   730         firstrev = repo[self.applied[0].node].rev()
       
   731         patches = []
       
   732         for i, rev in enumerate(revs):
       
   733 
       
   734             if rev < firstrev:
       
   735                 raise util.Abort(_('revision %d is not managed') % rev)
       
   736 
       
   737             ctx = repo[rev]
       
   738             base = self.applied[i].node
       
   739             if ctx.node() != base:
       
   740                 msg = _('cannot delete revision %d above applied patches')
       
   741                 raise util.Abort(msg % rev)
       
   742 
       
   743             patch = self.applied[i].name
       
   744             for fmt in ('[mq]: %s', 'imported patch %s'):
       
   745                 if ctx.description() == fmt % patch:
       
   746                     msg = _('patch %s finalized without changeset message\n')
       
   747                     repo.ui.status(msg % patch)
       
   748                     break
       
   749 
       
   750             patches.append(patch)
       
   751         return patches
       
   752 
       
   753     def finish(self, repo, revs):
       
   754         patches = self._revpatches(repo, sorted(revs))
       
   755         self._cleanup(patches, len(patches))
       
   756 
       
   757     def delete(self, repo, patches, opts):
       
   758         if not patches and not opts.get('rev'):
       
   759             raise util.Abort(_('qdelete requires at least one revision or '
       
   760                                'patch name'))
       
   761 
       
   762         realpatches = []
       
   763         for patch in patches:
       
   764             patch = self.lookup(patch, strict=True)
       
   765             info = self.isapplied(patch)
       
   766             if info:
       
   767                 raise util.Abort(_("cannot delete applied patch %s") % patch)
       
   768             if patch not in self.series:
       
   769                 raise util.Abort(_("patch %s not in series file") % patch)
       
   770             if patch not in realpatches:
       
   771                 realpatches.append(patch)
       
   772 
       
   773         numrevs = 0
       
   774         if opts.get('rev'):
       
   775             if not self.applied:
       
   776                 raise util.Abort(_('no patches applied'))
       
   777             revs = cmdutil.revrange(repo, opts.get('rev'))
       
   778             if len(revs) > 1 and revs[0] > revs[1]:
       
   779                 revs.reverse()
       
   780             revpatches = self._revpatches(repo, revs)
       
   781             realpatches += revpatches
       
   782             numrevs = len(revpatches)
       
   783 
       
   784         self._cleanup(realpatches, numrevs, opts.get('keep'))
       
   785 
       
   786     def check_toppatch(self, repo):
       
   787         if self.applied:
       
   788             top = self.applied[-1].node
       
   789             patch = self.applied[-1].name
       
   790             pp = repo.dirstate.parents()
       
   791             if top not in pp:
       
   792                 raise util.Abort(_("working directory revision is not qtip"))
       
   793             return top, patch
       
   794         return None, None
       
   795 
       
   796     def check_localchanges(self, repo, force=False, refresh=True):
       
   797         m, a, r, d = repo.status()[:4]
       
   798         if (m or a or r or d) and not force:
       
   799             if refresh:
       
   800                 raise util.Abort(_("local changes found, refresh first"))
       
   801             else:
       
   802                 raise util.Abort(_("local changes found"))
       
   803         return m, a, r, d
       
   804 
       
   805     _reserved = ('series', 'status', 'guards')
       
   806     def check_reserved_name(self, name):
       
   807         if (name in self._reserved or name.startswith('.hg')
       
   808             or name.startswith('.mq') or '#' in name or ':' in name):
       
   809             raise util.Abort(_('"%s" cannot be used as the name of a patch')
       
   810                              % name)
       
   811 
       
   812     def new(self, repo, patchfn, *pats, **opts):
       
   813         """options:
       
   814            msg: a string or a no-argument function returning a string
       
   815         """
       
   816         msg = opts.get('msg')
       
   817         user = opts.get('user')
       
   818         date = opts.get('date')
       
   819         if date:
       
   820             date = util.parsedate(date)
       
   821         diffopts = self.diffopts({'git': opts.get('git')})
       
   822         self.check_reserved_name(patchfn)
       
   823         if os.path.exists(self.join(patchfn)):
       
   824             if os.path.isdir(self.join(patchfn)):
       
   825                 raise util.Abort(_('"%s" already exists as a directory')
       
   826                                  % patchfn)
       
   827             else:
       
   828                 raise util.Abort(_('patch "%s" already exists') % patchfn)
       
   829         if opts.get('include') or opts.get('exclude') or pats:
       
   830             match = cmdutil.match(repo, pats, opts)
       
   831             # detect missing files in pats
       
   832             def badfn(f, msg):
       
   833                 raise util.Abort('%s: %s' % (f, msg))
       
   834             match.bad = badfn
       
   835             m, a, r, d = repo.status(match=match)[:4]
       
   836         else:
       
   837             m, a, r, d = self.check_localchanges(repo, force=True)
       
   838             match = cmdutil.matchfiles(repo, m + a + r)
       
   839         if len(repo[None].parents()) > 1:
       
   840             raise util.Abort(_('cannot manage merge changesets'))
       
   841         commitfiles = m + a + r
       
   842         self.check_toppatch(repo)
       
   843         insert = self.full_series_end()
       
   844         wlock = repo.wlock()
       
   845         try:
       
   846             try:
       
   847                 # if patch file write fails, abort early
       
   848                 p = self.opener(patchfn, "w")
       
   849             except IOError, e:
       
   850                 raise util.Abort(_('cannot write patch "%s": %s')
       
   851                                  % (patchfn, e.strerror))
       
   852             try:
       
   853                 if self.plainmode:
       
   854                     if user:
       
   855                         p.write("From: " + user + "\n")
       
   856                         if not date:
       
   857                             p.write("\n")
       
   858                     if date:
       
   859                         p.write("Date: %d %d\n\n" % date)
       
   860                 else:
       
   861                     p.write("# HG changeset patch\n")
       
   862                     p.write("# Parent "
       
   863                             + hex(repo[None].parents()[0].node()) + "\n")
       
   864                     if user:
       
   865                         p.write("# User " + user + "\n")
       
   866                     if date:
       
   867                         p.write("# Date %s %s\n\n" % date)
       
   868                 if hasattr(msg, '__call__'):
       
   869                     msg = msg()
       
   870                 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
       
   871                 n = repo.commit(commitmsg, user, date, match=match, force=True)
       
   872                 if n is None:
       
   873                     raise util.Abort(_("repo commit failed"))
       
   874                 try:
       
   875                     self.full_series[insert:insert] = [patchfn]
       
   876                     self.applied.append(statusentry(n, patchfn))
       
   877                     self.parse_series()
       
   878                     self.series_dirty = 1
       
   879                     self.applied_dirty = 1
       
   880                     if msg:
       
   881                         msg = msg + "\n\n"
       
   882                         p.write(msg)
       
   883                     if commitfiles:
       
   884                         parent = self.qparents(repo, n)
       
   885                         chunks = patch.diff(repo, node1=parent, node2=n,
       
   886                                             match=match, opts=diffopts)
       
   887                         for chunk in chunks:
       
   888                             p.write(chunk)
       
   889                     p.close()
       
   890                     wlock.release()
       
   891                     wlock = None
       
   892                     r = self.qrepo()
       
   893                     if r:
       
   894                         r[None].add([patchfn])
       
   895                 except:
       
   896                     repo.rollback()
       
   897                     raise
       
   898             except Exception:
       
   899                 patchpath = self.join(patchfn)
       
   900                 try:
       
   901                     os.unlink(patchpath)
       
   902                 except:
       
   903                     self.ui.warn(_('error unlinking %s\n') % patchpath)
       
   904                 raise
       
   905             self.removeundo(repo)
       
   906         finally:
       
   907             release(wlock)
       
   908 
       
   909     def strip(self, repo, revs, update=True, backup="all", force=None):
       
   910         wlock = lock = None
       
   911         try:
       
   912             wlock = repo.wlock()
       
   913             lock = repo.lock()
       
   914 
       
   915             if update:
       
   916                 self.check_localchanges(repo, force=force, refresh=False)
       
   917                 urev = self.qparents(repo, revs[0])
       
   918                 hg.clean(repo, urev)
       
   919                 repo.dirstate.write()
       
   920 
       
   921             self.removeundo(repo)
       
   922             for rev in revs:
       
   923                 repair.strip(self.ui, repo, rev, backup)
       
   924             # strip may have unbundled a set of backed up revisions after
       
   925             # the actual strip
       
   926             self.removeundo(repo)
       
   927         finally:
       
   928             release(lock, wlock)
       
   929 
       
   930     def isapplied(self, patch):
       
   931         """returns (index, rev, patch)"""
       
   932         for i, a in enumerate(self.applied):
       
   933             if a.name == patch:
       
   934                 return (i, a.node, a.name)
       
   935         return None
       
   936 
       
   937     # if the exact patch name does not exist, we try a few
       
   938     # variations.  If strict is passed, we try only #1
       
   939     #
       
   940     # 1) a number to indicate an offset in the series file
       
   941     # 2) a unique substring of the patch name was given
       
   942     # 3) patchname[-+]num to indicate an offset in the series file
       
   943     def lookup(self, patch, strict=False):
       
   944         patch = patch and str(patch)
       
   945 
       
   946         def partial_name(s):
       
   947             if s in self.series:
       
   948                 return s
       
   949             matches = [x for x in self.series if s in x]
       
   950             if len(matches) > 1:
       
   951                 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
       
   952                 for m in matches:
       
   953                     self.ui.warn('  %s\n' % m)
       
   954                 return None
       
   955             if matches:
       
   956                 return matches[0]
       
   957             if self.series and self.applied:
       
   958                 if s == 'qtip':
       
   959                     return self.series[self.series_end(True)-1]
       
   960                 if s == 'qbase':
       
   961                     return self.series[0]
       
   962             return None
       
   963 
       
   964         if patch is None:
       
   965             return None
       
   966         if patch in self.series:
       
   967             return patch
       
   968 
       
   969         if not os.path.isfile(self.join(patch)):
       
   970             try:
       
   971                 sno = int(patch)
       
   972             except (ValueError, OverflowError):
       
   973                 pass
       
   974             else:
       
   975                 if -len(self.series) <= sno < len(self.series):
       
   976                     return self.series[sno]
       
   977 
       
   978             if not strict:
       
   979                 res = partial_name(patch)
       
   980                 if res:
       
   981                     return res
       
   982                 minus = patch.rfind('-')
       
   983                 if minus >= 0:
       
   984                     res = partial_name(patch[:minus])
       
   985                     if res:
       
   986                         i = self.series.index(res)
       
   987                         try:
       
   988                             off = int(patch[minus + 1:] or 1)
       
   989                         except (ValueError, OverflowError):
       
   990                             pass
       
   991                         else:
       
   992                             if i - off >= 0:
       
   993                                 return self.series[i - off]
       
   994                 plus = patch.rfind('+')
       
   995                 if plus >= 0:
       
   996                     res = partial_name(patch[:plus])
       
   997                     if res:
       
   998                         i = self.series.index(res)
       
   999                         try:
       
  1000                             off = int(patch[plus + 1:] or 1)
       
  1001                         except (ValueError, OverflowError):
       
  1002                             pass
       
  1003                         else:
       
  1004                             if i + off < len(self.series):
       
  1005                                 return self.series[i + off]
       
  1006         raise util.Abort(_("patch %s not in series") % patch)
       
  1007 
       
  1008     def push(self, repo, patch=None, force=False, list=False,
       
  1009              mergeq=None, all=False, move=False):
       
  1010         diffopts = self.diffopts()
       
  1011         wlock = repo.wlock()
       
  1012         try:
       
  1013             heads = []
       
  1014             for b, ls in repo.branchmap().iteritems():
       
  1015                 heads += ls
       
  1016             if not heads:
       
  1017                 heads = [nullid]
       
  1018             if repo.dirstate.parents()[0] not in heads:
       
  1019                 self.ui.status(_("(working directory not at a head)\n"))
       
  1020 
       
  1021             if not self.series:
       
  1022                 self.ui.warn(_('no patches in series\n'))
       
  1023                 return 0
       
  1024 
       
  1025             patch = self.lookup(patch)
       
  1026             # Suppose our series file is: A B C and the current 'top'
       
  1027             # patch is B. qpush C should be performed (moving forward)
       
  1028             # qpush B is a NOP (no change) qpush A is an error (can't
       
  1029             # go backwards with qpush)
       
  1030             if patch:
       
  1031                 info = self.isapplied(patch)
       
  1032                 if info:
       
  1033                     if info[0] < len(self.applied) - 1:
       
  1034                         raise util.Abort(
       
  1035                             _("cannot push to a previous patch: %s") % patch)
       
  1036                     self.ui.warn(
       
  1037                         _('qpush: %s is already at the top\n') % patch)
       
  1038                     return 0
       
  1039                 pushable, reason = self.pushable(patch)
       
  1040                 if not pushable:
       
  1041                     if reason:
       
  1042                         reason = _('guarded by %r') % reason
       
  1043                     else:
       
  1044                         reason = _('no matching guards')
       
  1045                     self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
       
  1046                     return 1
       
  1047             elif all:
       
  1048                 patch = self.series[-1]
       
  1049                 if self.isapplied(patch):
       
  1050                     self.ui.warn(_('all patches are currently applied\n'))
       
  1051                     return 0
       
  1052 
       
  1053             # Following the above example, starting at 'top' of B:
       
  1054             # qpush should be performed (pushes C), but a subsequent
       
  1055             # qpush without an argument is an error (nothing to
       
  1056             # apply). This allows a loop of "...while hg qpush..." to
       
  1057             # work as it detects an error when done
       
  1058             start = self.series_end()
       
  1059             if start == len(self.series):
       
  1060                 self.ui.warn(_('patch series already fully applied\n'))
       
  1061                 return 1
       
  1062             if not force:
       
  1063                 self.check_localchanges(repo)
       
  1064 
       
  1065             if move:
       
  1066                 if not patch:
       
  1067                     raise  util.Abort(_("please specify the patch to move"))
       
  1068                 for i, rpn in enumerate(self.full_series[start:]):
       
  1069                     # strip markers for patch guards
       
  1070                     if self.guard_re.split(rpn, 1)[0] == patch:
       
  1071                         break
       
  1072                 index = start + i
       
  1073                 assert index < len(self.full_series)
       
  1074                 fullpatch = self.full_series[index]
       
  1075                 del self.full_series[index]
       
  1076                 self.full_series.insert(start, fullpatch)
       
  1077                 self.parse_series()
       
  1078                 self.series_dirty = 1
       
  1079 
       
  1080             self.applied_dirty = 1
       
  1081             if start > 0:
       
  1082                 self.check_toppatch(repo)
       
  1083             if not patch:
       
  1084                 patch = self.series[start]
       
  1085                 end = start + 1
       
  1086             else:
       
  1087                 end = self.series.index(patch, start) + 1
       
  1088 
       
  1089             s = self.series[start:end]
       
  1090             all_files = set()
       
  1091             try:
       
  1092                 if mergeq:
       
  1093                     ret = self.mergepatch(repo, mergeq, s, diffopts)
       
  1094                 else:
       
  1095                     ret = self.apply(repo, s, list, all_files=all_files)
       
  1096             except:
       
  1097                 self.ui.warn(_('cleaning up working directory...'))
       
  1098                 node = repo.dirstate.parents()[0]
       
  1099                 hg.revert(repo, node, None)
       
  1100                 # only remove unknown files that we know we touched or
       
  1101                 # created while patching
       
  1102                 for f in all_files:
       
  1103                     if f not in repo.dirstate:
       
  1104                         try:
       
  1105                             util.unlink(repo.wjoin(f))
       
  1106                         except OSError, inst:
       
  1107                             if inst.errno != errno.ENOENT:
       
  1108                                 raise
       
  1109                 self.ui.warn(_('done\n'))
       
  1110                 raise
       
  1111 
       
  1112             if not self.applied:
       
  1113                 return ret[0]
       
  1114             top = self.applied[-1].name
       
  1115             if ret[0] and ret[0] > 1:
       
  1116                 msg = _("errors during apply, please fix and refresh %s\n")
       
  1117                 self.ui.write(msg % top)
       
  1118             else:
       
  1119                 self.ui.write(_("now at: %s\n") % top)
       
  1120             return ret[0]
       
  1121 
       
  1122         finally:
       
  1123             wlock.release()
       
  1124 
       
  1125     def pop(self, repo, patch=None, force=False, update=True, all=False):
       
  1126         wlock = repo.wlock()
       
  1127         try:
       
  1128             if patch:
       
  1129                 # index, rev, patch
       
  1130                 info = self.isapplied(patch)
       
  1131                 if not info:
       
  1132                     patch = self.lookup(patch)
       
  1133                 info = self.isapplied(patch)
       
  1134                 if not info:
       
  1135                     raise util.Abort(_("patch %s is not applied") % patch)
       
  1136 
       
  1137             if not self.applied:
       
  1138                 # Allow qpop -a to work repeatedly,
       
  1139                 # but not qpop without an argument
       
  1140                 self.ui.warn(_("no patches applied\n"))
       
  1141                 return not all
       
  1142 
       
  1143             if all:
       
  1144                 start = 0
       
  1145             elif patch:
       
  1146                 start = info[0] + 1
       
  1147             else:
       
  1148                 start = len(self.applied) - 1
       
  1149 
       
  1150             if start >= len(self.applied):
       
  1151                 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
       
  1152                 return
       
  1153 
       
  1154             if not update:
       
  1155                 parents = repo.dirstate.parents()
       
  1156                 rr = [x.node for x in self.applied]
       
  1157                 for p in parents:
       
  1158                     if p in rr:
       
  1159                         self.ui.warn(_("qpop: forcing dirstate update\n"))
       
  1160                         update = True
       
  1161             else:
       
  1162                 parents = [p.node() for p in repo[None].parents()]
       
  1163                 needupdate = False
       
  1164                 for entry in self.applied[start:]:
       
  1165                     if entry.node in parents:
       
  1166                         needupdate = True
       
  1167                         break
       
  1168                 update = needupdate
       
  1169 
       
  1170             if not force and update:
       
  1171                 self.check_localchanges(repo)
       
  1172 
       
  1173             self.applied_dirty = 1
       
  1174             end = len(self.applied)
       
  1175             rev = self.applied[start].node
       
  1176             if update:
       
  1177                 top = self.check_toppatch(repo)[0]
       
  1178 
       
  1179             try:
       
  1180                 heads = repo.changelog.heads(rev)
       
  1181             except error.LookupError:
       
  1182                 node = short(rev)
       
  1183                 raise util.Abort(_('trying to pop unknown node %s') % node)
       
  1184 
       
  1185             if heads != [self.applied[-1].node]:
       
  1186                 raise util.Abort(_("popping would remove a revision not "
       
  1187                                    "managed by this patch queue"))
       
  1188 
       
  1189             # we know there are no local changes, so we can make a simplified
       
  1190             # form of hg.update.
       
  1191             if update:
       
  1192                 qp = self.qparents(repo, rev)
       
  1193                 ctx = repo[qp]
       
  1194                 m, a, r, d = repo.status(qp, top)[:4]
       
  1195                 if d:
       
  1196                     raise util.Abort(_("deletions found between repo revs"))
       
  1197                 for f in a:
       
  1198                     try:
       
  1199                         util.unlink(repo.wjoin(f))
       
  1200                     except OSError, e:
       
  1201                         if e.errno != errno.ENOENT:
       
  1202                             raise
       
  1203                     repo.dirstate.forget(f)
       
  1204                 for f in m + r:
       
  1205                     fctx = ctx[f]
       
  1206                     repo.wwrite(f, fctx.data(), fctx.flags())
       
  1207                     repo.dirstate.normal(f)
       
  1208                 repo.dirstate.setparents(qp, nullid)
       
  1209             for patch in reversed(self.applied[start:end]):
       
  1210                 self.ui.status(_("popping %s\n") % patch.name)
       
  1211             del self.applied[start:end]
       
  1212             self.strip(repo, [rev], update=False, backup='strip')
       
  1213             if self.applied:
       
  1214                 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
       
  1215             else:
       
  1216                 self.ui.write(_("patch queue now empty\n"))
       
  1217         finally:
       
  1218             wlock.release()
       
  1219 
       
  1220     def diff(self, repo, pats, opts):
       
  1221         top, patch = self.check_toppatch(repo)
       
  1222         if not top:
       
  1223             self.ui.write(_("no patches applied\n"))
       
  1224             return
       
  1225         qp = self.qparents(repo, top)
       
  1226         if opts.get('reverse'):
       
  1227             node1, node2 = None, qp
       
  1228         else:
       
  1229             node1, node2 = qp, None
       
  1230         diffopts = self.diffopts(opts, patch)
       
  1231         self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
       
  1232 
       
  1233     def refresh(self, repo, pats=None, **opts):
       
  1234         if not self.applied:
       
  1235             self.ui.write(_("no patches applied\n"))
       
  1236             return 1
       
  1237         msg = opts.get('msg', '').rstrip()
       
  1238         newuser = opts.get('user')
       
  1239         newdate = opts.get('date')
       
  1240         if newdate:
       
  1241             newdate = '%d %d' % util.parsedate(newdate)
       
  1242         wlock = repo.wlock()
       
  1243 
       
  1244         try:
       
  1245             self.check_toppatch(repo)
       
  1246             (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
       
  1247             if repo.changelog.heads(top) != [top]:
       
  1248                 raise util.Abort(_("cannot refresh a revision with children"))
       
  1249 
       
  1250             cparents = repo.changelog.parents(top)
       
  1251             patchparent = self.qparents(repo, top)
       
  1252             ph = patchheader(self.join(patchfn), self.plainmode)
       
  1253             diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
       
  1254             if msg:
       
  1255                 ph.setmessage(msg)
       
  1256             if newuser:
       
  1257                 ph.setuser(newuser)
       
  1258             if newdate:
       
  1259                 ph.setdate(newdate)
       
  1260             ph.setparent(hex(patchparent))
       
  1261 
       
  1262             # only commit new patch when write is complete
       
  1263             patchf = self.opener(patchfn, 'w', atomictemp=True)
       
  1264 
       
  1265             comments = str(ph)
       
  1266             if comments:
       
  1267                 patchf.write(comments)
       
  1268 
       
  1269             # update the dirstate in place, strip off the qtip commit
       
  1270             # and then commit.
       
  1271             #
       
  1272             # this should really read:
       
  1273             #   mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
       
  1274             # but we do it backwards to take advantage of manifest/chlog
       
  1275             # caching against the next repo.status call
       
  1276             mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
       
  1277             changes = repo.changelog.read(top)
       
  1278             man = repo.manifest.read(changes[0])
       
  1279             aaa = aa[:]
       
  1280             matchfn = cmdutil.match(repo, pats, opts)
       
  1281             # in short mode, we only diff the files included in the
       
  1282             # patch already plus specified files
       
  1283             if opts.get('short'):
       
  1284                 # if amending a patch, we start with existing
       
  1285                 # files plus specified files - unfiltered
       
  1286                 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
       
  1287                 # filter with inc/exl options
       
  1288                 matchfn = cmdutil.match(repo, opts=opts)
       
  1289             else:
       
  1290                 match = cmdutil.matchall(repo)
       
  1291             m, a, r, d = repo.status(match=match)[:4]
       
  1292 
       
  1293             # we might end up with files that were added between
       
  1294             # qtip and the dirstate parent, but then changed in the
       
  1295             # local dirstate. in this case, we want them to only
       
  1296             # show up in the added section
       
  1297             for x in m:
       
  1298                 if x == '.hgsub' or x == '.hgsubstate':
       
  1299                     self.ui.warn(_('warning: not refreshing %s\n') % x)
       
  1300                     continue
       
  1301                 if x not in aa:
       
  1302                     mm.append(x)
       
  1303             # we might end up with files added by the local dirstate that
       
  1304             # were deleted by the patch.  In this case, they should only
       
  1305             # show up in the changed section.
       
  1306             for x in a:
       
  1307                 if x == '.hgsub' or x == '.hgsubstate':
       
  1308                     self.ui.warn(_('warning: not adding %s\n') % x)
       
  1309                     continue
       
  1310                 if x in dd:
       
  1311                     del dd[dd.index(x)]
       
  1312                     mm.append(x)
       
  1313                 else:
       
  1314                     aa.append(x)
       
  1315             # make sure any files deleted in the local dirstate
       
  1316             # are not in the add or change column of the patch
       
  1317             forget = []
       
  1318             for x in d + r:
       
  1319                 if x == '.hgsub' or x == '.hgsubstate':
       
  1320                     self.ui.warn(_('warning: not removing %s\n') % x)
       
  1321                     continue
       
  1322                 if x in aa:
       
  1323                     del aa[aa.index(x)]
       
  1324                     forget.append(x)
       
  1325                     continue
       
  1326                 elif x in mm:
       
  1327                     del mm[mm.index(x)]
       
  1328                 dd.append(x)
       
  1329 
       
  1330             m = list(set(mm))
       
  1331             r = list(set(dd))
       
  1332             a = list(set(aa))
       
  1333             c = [filter(matchfn, l) for l in (m, a, r)]
       
  1334             match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
       
  1335             chunks = patch.diff(repo, patchparent, match=match,
       
  1336                                 changes=c, opts=diffopts)
       
  1337             for chunk in chunks:
       
  1338                 patchf.write(chunk)
       
  1339 
       
  1340             try:
       
  1341                 if diffopts.git or diffopts.upgrade:
       
  1342                     copies = {}
       
  1343                     for dst in a:
       
  1344                         src = repo.dirstate.copied(dst)
       
  1345                         # during qfold, the source file for copies may
       
  1346                         # be removed. Treat this as a simple add.
       
  1347                         if src is not None and src in repo.dirstate:
       
  1348                             copies.setdefault(src, []).append(dst)
       
  1349                         repo.dirstate.add(dst)
       
  1350                     # remember the copies between patchparent and qtip
       
  1351                     for dst in aaa:
       
  1352                         f = repo.file(dst)
       
  1353                         src = f.renamed(man[dst])
       
  1354                         if src:
       
  1355                             copies.setdefault(src[0], []).extend(
       
  1356                                 copies.get(dst, []))
       
  1357                             if dst in a:
       
  1358                                 copies[src[0]].append(dst)
       
  1359                         # we can't copy a file created by the patch itself
       
  1360                         if dst in copies:
       
  1361                             del copies[dst]
       
  1362                     for src, dsts in copies.iteritems():
       
  1363                         for dst in dsts:
       
  1364                             repo.dirstate.copy(src, dst)
       
  1365                 else:
       
  1366                     for dst in a:
       
  1367                         repo.dirstate.add(dst)
       
  1368                     # Drop useless copy information
       
  1369                     for f in list(repo.dirstate.copies()):
       
  1370                         repo.dirstate.copy(None, f)
       
  1371                 for f in r:
       
  1372                     repo.dirstate.remove(f)
       
  1373                 # if the patch excludes a modified file, mark that
       
  1374                 # file with mtime=0 so status can see it.
       
  1375                 mm = []
       
  1376                 for i in xrange(len(m)-1, -1, -1):
       
  1377                     if not matchfn(m[i]):
       
  1378                         mm.append(m[i])
       
  1379                         del m[i]
       
  1380                 for f in m:
       
  1381                     repo.dirstate.normal(f)
       
  1382                 for f in mm:
       
  1383                     repo.dirstate.normallookup(f)
       
  1384                 for f in forget:
       
  1385                     repo.dirstate.forget(f)
       
  1386 
       
  1387                 if not msg:
       
  1388                     if not ph.message:
       
  1389                         message = "[mq]: %s\n" % patchfn
       
  1390                     else:
       
  1391                         message = "\n".join(ph.message)
       
  1392                 else:
       
  1393                     message = msg
       
  1394 
       
  1395                 user = ph.user or changes[1]
       
  1396 
       
  1397                 # assumes strip can roll itself back if interrupted
       
  1398                 repo.dirstate.setparents(*cparents)
       
  1399                 self.applied.pop()
       
  1400                 self.applied_dirty = 1
       
  1401                 self.strip(repo, [top], update=False,
       
  1402                            backup='strip')
       
  1403             except:
       
  1404                 repo.dirstate.invalidate()
       
  1405                 raise
       
  1406 
       
  1407             try:
       
  1408                 # might be nice to attempt to roll back strip after this
       
  1409                 patchf.rename()
       
  1410                 n = repo.commit(message, user, ph.date, match=match,
       
  1411                                 force=True)
       
  1412                 self.applied.append(statusentry(n, patchfn))
       
  1413             except:
       
  1414                 ctx = repo[cparents[0]]
       
  1415                 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
       
  1416                 self.save_dirty()
       
  1417                 self.ui.warn(_('refresh interrupted while patch was popped! '
       
  1418                                '(revert --all, qpush to recover)\n'))
       
  1419                 raise
       
  1420         finally:
       
  1421             wlock.release()
       
  1422             self.removeundo(repo)
       
  1423 
       
  1424     def init(self, repo, create=False):
       
  1425         if not create and os.path.isdir(self.path):
       
  1426             raise util.Abort(_("patch queue directory already exists"))
       
  1427         try:
       
  1428             os.mkdir(self.path)
       
  1429         except OSError, inst:
       
  1430             if inst.errno != errno.EEXIST or not create:
       
  1431                 raise
       
  1432         if create:
       
  1433             return self.qrepo(create=True)
       
  1434 
       
  1435     def unapplied(self, repo, patch=None):
       
  1436         if patch and patch not in self.series:
       
  1437             raise util.Abort(_("patch %s is not in series file") % patch)
       
  1438         if not patch:
       
  1439             start = self.series_end()
       
  1440         else:
       
  1441             start = self.series.index(patch) + 1
       
  1442         unapplied = []
       
  1443         for i in xrange(start, len(self.series)):
       
  1444             pushable, reason = self.pushable(i)
       
  1445             if pushable:
       
  1446                 unapplied.append((i, self.series[i]))
       
  1447             self.explain_pushable(i)
       
  1448         return unapplied
       
  1449 
       
  1450     def qseries(self, repo, missing=None, start=0, length=None, status=None,
       
  1451                 summary=False):
       
  1452         def displayname(pfx, patchname, state):
       
  1453             if pfx:
       
  1454                 self.ui.write(pfx)
       
  1455             if summary:
       
  1456                 ph = patchheader(self.join(patchname), self.plainmode)
       
  1457                 msg = ph.message and ph.message[0] or ''
       
  1458                 if self.ui.formatted():
       
  1459                     width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
       
  1460                     if width > 0:
       
  1461                         msg = util.ellipsis(msg, width)
       
  1462                     else:
       
  1463                         msg = ''
       
  1464                 self.ui.write(patchname, label='qseries.' + state)
       
  1465                 self.ui.write(': ')
       
  1466                 self.ui.write(msg, label='qseries.message.' + state)
       
  1467             else:
       
  1468                 self.ui.write(patchname, label='qseries.' + state)
       
  1469             self.ui.write('\n')
       
  1470 
       
  1471         applied = set([p.name for p in self.applied])
       
  1472         if length is None:
       
  1473             length = len(self.series) - start
       
  1474         if not missing:
       
  1475             if self.ui.verbose:
       
  1476                 idxwidth = len(str(start + length - 1))
       
  1477             for i in xrange(start, start + length):
       
  1478                 patch = self.series[i]
       
  1479                 if patch in applied:
       
  1480                     char, state = 'A', 'applied'
       
  1481                 elif self.pushable(i)[0]:
       
  1482                     char, state = 'U', 'unapplied'
       
  1483                 else:
       
  1484                     char, state = 'G', 'guarded'
       
  1485                 pfx = ''
       
  1486                 if self.ui.verbose:
       
  1487                     pfx = '%*d %s ' % (idxwidth, i, char)
       
  1488                 elif status and status != char:
       
  1489                     continue
       
  1490                 displayname(pfx, patch, state)
       
  1491         else:
       
  1492             msng_list = []
       
  1493             for root, dirs, files in os.walk(self.path):
       
  1494                 d = root[len(self.path) + 1:]
       
  1495                 for f in files:
       
  1496                     fl = os.path.join(d, f)
       
  1497                     if (fl not in self.series and
       
  1498                         fl not in (self.status_path, self.series_path,
       
  1499                                    self.guards_path)
       
  1500                         and not fl.startswith('.')):
       
  1501                         msng_list.append(fl)
       
  1502             for x in sorted(msng_list):
       
  1503                 pfx = self.ui.verbose and ('D ') or ''
       
  1504                 displayname(pfx, x, 'missing')
       
  1505 
       
  1506     def issaveline(self, l):
       
  1507         if l.name == '.hg.patches.save.line':
       
  1508             return True
       
  1509 
       
  1510     def qrepo(self, create=False):
       
  1511         ui = self.ui.copy()
       
  1512         ui.setconfig('paths', 'default', '', overlay=False)
       
  1513         ui.setconfig('paths', 'default-push', '', overlay=False)
       
  1514         if create or os.path.isdir(self.join(".hg")):
       
  1515             return hg.repository(ui, path=self.path, create=create)
       
  1516 
       
  1517     def restore(self, repo, rev, delete=None, qupdate=None):
       
  1518         desc = repo[rev].description().strip()
       
  1519         lines = desc.splitlines()
       
  1520         i = 0
       
  1521         datastart = None
       
  1522         series = []
       
  1523         applied = []
       
  1524         qpp = None
       
  1525         for i, line in enumerate(lines):
       
  1526             if line == 'Patch Data:':
       
  1527                 datastart = i + 1
       
  1528             elif line.startswith('Dirstate:'):
       
  1529                 l = line.rstrip()
       
  1530                 l = l[10:].split(' ')
       
  1531                 qpp = [bin(x) for x in l]
       
  1532             elif datastart != None:
       
  1533                 l = line.rstrip()
       
  1534                 n, name = l.split(':', 1)
       
  1535                 if n:
       
  1536                     applied.append(statusentry(bin(n), name))
       
  1537                 else:
       
  1538                     series.append(l)
       
  1539         if datastart is None:
       
  1540             self.ui.warn(_("No saved patch data found\n"))
       
  1541             return 1
       
  1542         self.ui.warn(_("restoring status: %s\n") % lines[0])
       
  1543         self.full_series = series
       
  1544         self.applied = applied
       
  1545         self.parse_series()
       
  1546         self.series_dirty = 1
       
  1547         self.applied_dirty = 1
       
  1548         heads = repo.changelog.heads()
       
  1549         if delete:
       
  1550             if rev not in heads:
       
  1551                 self.ui.warn(_("save entry has children, leaving it alone\n"))
       
  1552             else:
       
  1553                 self.ui.warn(_("removing save entry %s\n") % short(rev))
       
  1554                 pp = repo.dirstate.parents()
       
  1555                 if rev in pp:
       
  1556                     update = True
       
  1557                 else:
       
  1558                     update = False
       
  1559                 self.strip(repo, [rev], update=update, backup='strip')
       
  1560         if qpp:
       
  1561             self.ui.warn(_("saved queue repository parents: %s %s\n") %
       
  1562                          (short(qpp[0]), short(qpp[1])))
       
  1563             if qupdate:
       
  1564                 self.ui.status(_("updating queue directory\n"))
       
  1565                 r = self.qrepo()
       
  1566                 if not r:
       
  1567                     self.ui.warn(_("Unable to load queue repository\n"))
       
  1568                     return 1
       
  1569                 hg.clean(r, qpp[0])
       
  1570 
       
  1571     def save(self, repo, msg=None):
       
  1572         if not self.applied:
       
  1573             self.ui.warn(_("save: no patches applied, exiting\n"))
       
  1574             return 1
       
  1575         if self.issaveline(self.applied[-1]):
       
  1576             self.ui.warn(_("status is already saved\n"))
       
  1577             return 1
       
  1578 
       
  1579         if not msg:
       
  1580             msg = _("hg patches saved state")
       
  1581         else:
       
  1582             msg = "hg patches: " + msg.rstrip('\r\n')
       
  1583         r = self.qrepo()
       
  1584         if r:
       
  1585             pp = r.dirstate.parents()
       
  1586             msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
       
  1587         msg += "\n\nPatch Data:\n"
       
  1588         msg += ''.join('%s\n' % x for x in self.applied)
       
  1589         msg += ''.join(':%s\n' % x for x in self.full_series)
       
  1590         n = repo.commit(msg, force=True)
       
  1591         if not n:
       
  1592             self.ui.warn(_("repo commit failed\n"))
       
  1593             return 1
       
  1594         self.applied.append(statusentry(n, '.hg.patches.save.line'))
       
  1595         self.applied_dirty = 1
       
  1596         self.removeundo(repo)
       
  1597 
       
  1598     def full_series_end(self):
       
  1599         if self.applied:
       
  1600             p = self.applied[-1].name
       
  1601             end = self.find_series(p)
       
  1602             if end is None:
       
  1603                 return len(self.full_series)
       
  1604             return end + 1
       
  1605         return 0
       
  1606 
       
  1607     def series_end(self, all_patches=False):
       
  1608         """If all_patches is False, return the index of the next pushable patch
       
  1609         in the series, or the series length. If all_patches is True, return the
       
  1610         index of the first patch past the last applied one.
       
  1611         """
       
  1612         end = 0
       
  1613         def next(start):
       
  1614             if all_patches or start >= len(self.series):
       
  1615                 return start
       
  1616             for i in xrange(start, len(self.series)):
       
  1617                 p, reason = self.pushable(i)
       
  1618                 if p:
       
  1619                     break
       
  1620                 self.explain_pushable(i)
       
  1621             return i
       
  1622         if self.applied:
       
  1623             p = self.applied[-1].name
       
  1624             try:
       
  1625                 end = self.series.index(p)
       
  1626             except ValueError:
       
  1627                 return 0
       
  1628             return next(end + 1)
       
  1629         return next(end)
       
  1630 
       
  1631     def appliedname(self, index):
       
  1632         pname = self.applied[index].name
       
  1633         if not self.ui.verbose:
       
  1634             p = pname
       
  1635         else:
       
  1636             p = str(self.series.index(pname)) + " " + pname
       
  1637         return p
       
  1638 
       
  1639     def qimport(self, repo, files, patchname=None, rev=None, existing=None,
       
  1640                 force=None, git=False):
       
  1641         def checkseries(patchname):
       
  1642             if patchname in self.series:
       
  1643                 raise util.Abort(_('patch %s is already in the series file')
       
  1644                                  % patchname)
       
  1645         def checkfile(patchname):
       
  1646             if not force and os.path.exists(self.join(patchname)):
       
  1647                 raise util.Abort(_('patch "%s" already exists')
       
  1648                                  % patchname)
       
  1649 
       
  1650         if rev:
       
  1651             if files:
       
  1652                 raise util.Abort(_('option "-r" not valid when importing '
       
  1653                                    'files'))
       
  1654             rev = cmdutil.revrange(repo, rev)
       
  1655             rev.sort(reverse=True)
       
  1656         if (len(files) > 1 or len(rev) > 1) and patchname:
       
  1657             raise util.Abort(_('option "-n" not valid when importing multiple '
       
  1658                                'patches'))
       
  1659         if rev:
       
  1660             # If mq patches are applied, we can only import revisions
       
  1661             # that form a linear path to qbase.
       
  1662             # Otherwise, they should form a linear path to a head.
       
  1663             heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
       
  1664             if len(heads) > 1:
       
  1665                 raise util.Abort(_('revision %d is the root of more than one '
       
  1666                                    'branch') % rev[-1])
       
  1667             if self.applied:
       
  1668                 base = repo.changelog.node(rev[0])
       
  1669                 if base in [n.node for n in self.applied]:
       
  1670                     raise util.Abort(_('revision %d is already managed')
       
  1671                                      % rev[0])
       
  1672                 if heads != [self.applied[-1].node]:
       
  1673                     raise util.Abort(_('revision %d is not the parent of '
       
  1674                                        'the queue') % rev[0])
       
  1675                 base = repo.changelog.rev(self.applied[0].node)
       
  1676                 lastparent = repo.changelog.parentrevs(base)[0]
       
  1677             else:
       
  1678                 if heads != [repo.changelog.node(rev[0])]:
       
  1679                     raise util.Abort(_('revision %d has unmanaged children')
       
  1680                                      % rev[0])
       
  1681                 lastparent = None
       
  1682 
       
  1683             diffopts = self.diffopts({'git': git})
       
  1684             for r in rev:
       
  1685                 p1, p2 = repo.changelog.parentrevs(r)
       
  1686                 n = repo.changelog.node(r)
       
  1687                 if p2 != nullrev:
       
  1688                     raise util.Abort(_('cannot import merge revision %d') % r)
       
  1689                 if lastparent and lastparent != r:
       
  1690                     raise util.Abort(_('revision %d is not the parent of %d')
       
  1691                                      % (r, lastparent))
       
  1692                 lastparent = p1
       
  1693 
       
  1694                 if not patchname:
       
  1695                     patchname = normname('%d.diff' % r)
       
  1696                 self.check_reserved_name(patchname)
       
  1697                 checkseries(patchname)
       
  1698                 checkfile(patchname)
       
  1699                 self.full_series.insert(0, patchname)
       
  1700 
       
  1701                 patchf = self.opener(patchname, "w")
       
  1702                 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
       
  1703                 patchf.close()
       
  1704 
       
  1705                 se = statusentry(n, patchname)
       
  1706                 self.applied.insert(0, se)
       
  1707 
       
  1708                 self.added.append(patchname)
       
  1709                 patchname = None
       
  1710             self.parse_series()
       
  1711             self.applied_dirty = 1
       
  1712             self.series_dirty = True
       
  1713 
       
  1714         for i, filename in enumerate(files):
       
  1715             if existing:
       
  1716                 if filename == '-':
       
  1717                     raise util.Abort(_('-e is incompatible with import from -'))
       
  1718                 filename = normname(filename)
       
  1719                 self.check_reserved_name(filename)
       
  1720                 originpath = self.join(filename)
       
  1721                 if not os.path.isfile(originpath):
       
  1722                     raise util.Abort(_("patch %s does not exist") % filename)
       
  1723 
       
  1724                 if patchname:
       
  1725                     self.check_reserved_name(patchname)
       
  1726                     checkfile(patchname)
       
  1727 
       
  1728                     self.ui.write(_('renaming %s to %s\n')
       
  1729                                         % (filename, patchname))
       
  1730                     util.rename(originpath, self.join(patchname))
       
  1731                 else:
       
  1732                     patchname = filename
       
  1733 
       
  1734             else:
       
  1735                 try:
       
  1736                     if filename == '-':
       
  1737                         if not patchname:
       
  1738                             raise util.Abort(
       
  1739                                 _('need --name to import a patch from -'))
       
  1740                         text = sys.stdin.read()
       
  1741                     else:
       
  1742                         text = url.open(self.ui, filename).read()
       
  1743                 except (OSError, IOError):
       
  1744                     raise util.Abort(_("unable to read file %s") % filename)
       
  1745                 if not patchname:
       
  1746                     patchname = normname(os.path.basename(filename))
       
  1747                 self.check_reserved_name(patchname)
       
  1748                 checkfile(patchname)
       
  1749                 patchf = self.opener(patchname, "w")
       
  1750                 patchf.write(text)
       
  1751             if not force:
       
  1752                 checkseries(patchname)
       
  1753             if patchname not in self.series:
       
  1754                 index = self.full_series_end() + i
       
  1755                 self.full_series[index:index] = [patchname]
       
  1756             self.parse_series()
       
  1757             self.series_dirty = True
       
  1758             self.ui.warn(_("adding %s to series file\n") % patchname)
       
  1759             self.added.append(patchname)
       
  1760             patchname = None
       
  1761 
       
  1762 def delete(ui, repo, *patches, **opts):
       
  1763     """remove patches from queue
       
  1764 
       
  1765     The patches must not be applied, and at least one patch is required. With
       
  1766     -k/--keep, the patch files are preserved in the patch directory.
       
  1767 
       
  1768     To stop managing a patch and move it into permanent history,
       
  1769     use the :hg:`qfinish` command."""
       
  1770     q = repo.mq
       
  1771     q.delete(repo, patches, opts)
       
  1772     q.save_dirty()
       
  1773     return 0
       
  1774 
       
  1775 def applied(ui, repo, patch=None, **opts):
       
  1776     """print the patches already applied
       
  1777 
       
  1778     Returns 0 on success."""
       
  1779 
       
  1780     q = repo.mq
       
  1781 
       
  1782     if patch:
       
  1783         if patch not in q.series:
       
  1784             raise util.Abort(_("patch %s is not in series file") % patch)
       
  1785         end = q.series.index(patch) + 1
       
  1786     else:
       
  1787         end = q.series_end(True)
       
  1788 
       
  1789     if opts.get('last') and not end:
       
  1790         ui.write(_("no patches applied\n"))
       
  1791         return 1
       
  1792     elif opts.get('last') and end == 1:
       
  1793         ui.write(_("only one patch applied\n"))
       
  1794         return 1
       
  1795     elif opts.get('last'):
       
  1796         start = end - 2
       
  1797         end = 1
       
  1798     else:
       
  1799         start = 0
       
  1800 
       
  1801     q.qseries(repo, length=end, start=start, status='A',
       
  1802               summary=opts.get('summary'))
       
  1803 
       
  1804 
       
  1805 def unapplied(ui, repo, patch=None, **opts):
       
  1806     """print the patches not yet applied
       
  1807 
       
  1808     Returns 0 on success."""
       
  1809 
       
  1810     q = repo.mq
       
  1811     if patch:
       
  1812         if patch not in q.series:
       
  1813             raise util.Abort(_("patch %s is not in series file") % patch)
       
  1814         start = q.series.index(patch) + 1
       
  1815     else:
       
  1816         start = q.series_end(True)
       
  1817 
       
  1818     if start == len(q.series) and opts.get('first'):
       
  1819         ui.write(_("all patches applied\n"))
       
  1820         return 1
       
  1821 
       
  1822     length = opts.get('first') and 1 or None
       
  1823     q.qseries(repo, start=start, length=length, status='U',
       
  1824               summary=opts.get('summary'))
       
  1825 
       
  1826 def qimport(ui, repo, *filename, **opts):
       
  1827     """import a patch
       
  1828 
       
  1829     The patch is inserted into the series after the last applied
       
  1830     patch. If no patches have been applied, qimport prepends the patch
       
  1831     to the series.
       
  1832 
       
  1833     The patch will have the same name as its source file unless you
       
  1834     give it a new one with -n/--name.
       
  1835 
       
  1836     You can register an existing patch inside the patch directory with
       
  1837     the -e/--existing flag.
       
  1838 
       
  1839     With -f/--force, an existing patch of the same name will be
       
  1840     overwritten.
       
  1841 
       
  1842     An existing changeset may be placed under mq control with -r/--rev
       
  1843     (e.g. qimport --rev tip -n patch will place tip under mq control).
       
  1844     With -g/--git, patches imported with --rev will use the git diff
       
  1845     format. See the diffs help topic for information on why this is
       
  1846     important for preserving rename/copy information and permission
       
  1847     changes.
       
  1848 
       
  1849     To import a patch from standard input, pass - as the patch file.
       
  1850     When importing from standard input, a patch name must be specified
       
  1851     using the --name flag.
       
  1852 
       
  1853     To import an existing patch while renaming it::
       
  1854 
       
  1855       hg qimport -e existing-patch -n new-name
       
  1856 
       
  1857     Returns 0 if import succeeded.
       
  1858     """
       
  1859     q = repo.mq
       
  1860     try:
       
  1861         q.qimport(repo, filename, patchname=opts.get('name'),
       
  1862               existing=opts.get('existing'), force=opts.get('force'),
       
  1863               rev=opts.get('rev'), git=opts.get('git'))
       
  1864     finally:
       
  1865         q.save_dirty()
       
  1866 
       
  1867     if opts.get('push') and not opts.get('rev'):
       
  1868         return q.push(repo, None)
       
  1869     return 0
       
  1870 
       
  1871 def qinit(ui, repo, create):
       
  1872     """initialize a new queue repository
       
  1873 
       
  1874     This command also creates a series file for ordering patches, and
       
  1875     an mq-specific .hgignore file in the queue repository, to exclude
       
  1876     the status and guards files (these contain mostly transient state).
       
  1877 
       
  1878     Returns 0 if initialization succeeded."""
       
  1879     q = repo.mq
       
  1880     r = q.init(repo, create)
       
  1881     q.save_dirty()
       
  1882     if r:
       
  1883         if not os.path.exists(r.wjoin('.hgignore')):
       
  1884             fp = r.wopener('.hgignore', 'w')
       
  1885             fp.write('^\\.hg\n')
       
  1886             fp.write('^\\.mq\n')
       
  1887             fp.write('syntax: glob\n')
       
  1888             fp.write('status\n')
       
  1889             fp.write('guards\n')
       
  1890             fp.close()
       
  1891         if not os.path.exists(r.wjoin('series')):
       
  1892             r.wopener('series', 'w').close()
       
  1893         r[None].add(['.hgignore', 'series'])
       
  1894         commands.add(ui, r)
       
  1895     return 0
       
  1896 
       
  1897 def init(ui, repo, **opts):
       
  1898     """init a new queue repository (DEPRECATED)
       
  1899 
       
  1900     The queue repository is unversioned by default. If
       
  1901     -c/--create-repo is specified, qinit will create a separate nested
       
  1902     repository for patches (qinit -c may also be run later to convert
       
  1903     an unversioned patch repository into a versioned one). You can use
       
  1904     qcommit to commit changes to this queue repository.
       
  1905 
       
  1906     This command is deprecated. Without -c, it's implied by other relevant
       
  1907     commands. With -c, use :hg:`init --mq` instead."""
       
  1908     return qinit(ui, repo, create=opts.get('create_repo'))
       
  1909 
       
  1910 def clone(ui, source, dest=None, **opts):
       
  1911     '''clone main and patch repository at same time
       
  1912 
       
  1913     If source is local, destination will have no patches applied. If
       
  1914     source is remote, this command can not check if patches are
       
  1915     applied in source, so cannot guarantee that patches are not
       
  1916     applied in destination. If you clone remote repository, be sure
       
  1917     before that it has no patches applied.
       
  1918 
       
  1919     Source patch repository is looked for in <src>/.hg/patches by
       
  1920     default. Use -p <url> to change.
       
  1921 
       
  1922     The patch directory must be a nested Mercurial repository, as
       
  1923     would be created by :hg:`init --mq`.
       
  1924 
       
  1925     Return 0 on success.
       
  1926     '''
       
  1927     def patchdir(repo):
       
  1928         url = repo.url()
       
  1929         if url.endswith('/'):
       
  1930             url = url[:-1]
       
  1931         return url + '/.hg/patches'
       
  1932     if dest is None:
       
  1933         dest = hg.defaultdest(source)
       
  1934     sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
       
  1935     if opts.get('patches'):
       
  1936         patchespath = ui.expandpath(opts.get('patches'))
       
  1937     else:
       
  1938         patchespath = patchdir(sr)
       
  1939     try:
       
  1940         hg.repository(ui, patchespath)
       
  1941     except error.RepoError:
       
  1942         raise util.Abort(_('versioned patch repository not found'
       
  1943                            ' (see init --mq)'))
       
  1944     qbase, destrev = None, None
       
  1945     if sr.local():
       
  1946         if sr.mq.applied:
       
  1947             qbase = sr.mq.applied[0].node
       
  1948             if not hg.islocal(dest):
       
  1949                 heads = set(sr.heads())
       
  1950                 destrev = list(heads.difference(sr.heads(qbase)))
       
  1951                 destrev.append(sr.changelog.parents(qbase)[0])
       
  1952     elif sr.capable('lookup'):
       
  1953         try:
       
  1954             qbase = sr.lookup('qbase')
       
  1955         except error.RepoError:
       
  1956             pass
       
  1957     ui.note(_('cloning main repository\n'))
       
  1958     sr, dr = hg.clone(ui, sr.url(), dest,
       
  1959                       pull=opts.get('pull'),
       
  1960                       rev=destrev,
       
  1961                       update=False,
       
  1962                       stream=opts.get('uncompressed'))
       
  1963     ui.note(_('cloning patch repository\n'))
       
  1964     hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
       
  1965              pull=opts.get('pull'), update=not opts.get('noupdate'),
       
  1966              stream=opts.get('uncompressed'))
       
  1967     if dr.local():
       
  1968         if qbase:
       
  1969             ui.note(_('stripping applied patches from destination '
       
  1970                       'repository\n'))
       
  1971             dr.mq.strip(dr, [qbase], update=False, backup=None)
       
  1972         if not opts.get('noupdate'):
       
  1973             ui.note(_('updating destination repository\n'))
       
  1974             hg.update(dr, dr.changelog.tip())
       
  1975 
       
  1976 def commit(ui, repo, *pats, **opts):
       
  1977     """commit changes in the queue repository (DEPRECATED)
       
  1978 
       
  1979     This command is deprecated; use :hg:`commit --mq` instead."""
       
  1980     q = repo.mq
       
  1981     r = q.qrepo()
       
  1982     if not r:
       
  1983         raise util.Abort('no queue repository')
       
  1984     commands.commit(r.ui, r, *pats, **opts)
       
  1985 
       
  1986 def series(ui, repo, **opts):
       
  1987     """print the entire series file
       
  1988 
       
  1989     Returns 0 on success."""
       
  1990     repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
       
  1991     return 0
       
  1992 
       
  1993 def top(ui, repo, **opts):
       
  1994     """print the name of the current patch
       
  1995 
       
  1996     Returns 0 on success."""
       
  1997     q = repo.mq
       
  1998     t = q.applied and q.series_end(True) or 0
       
  1999     if t:
       
  2000         q.qseries(repo, start=t - 1, length=1, status='A',
       
  2001                   summary=opts.get('summary'))
       
  2002     else:
       
  2003         ui.write(_("no patches applied\n"))
       
  2004         return 1
       
  2005 
       
  2006 def next(ui, repo, **opts):
       
  2007     """print the name of the next patch
       
  2008 
       
  2009     Returns 0 on success."""
       
  2010     q = repo.mq
       
  2011     end = q.series_end()
       
  2012     if end == len(q.series):
       
  2013         ui.write(_("all patches applied\n"))
       
  2014         return 1
       
  2015     q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
       
  2016 
       
  2017 def prev(ui, repo, **opts):
       
  2018     """print the name of the previous patch
       
  2019 
       
  2020     Returns 0 on success."""
       
  2021     q = repo.mq
       
  2022     l = len(q.applied)
       
  2023     if l == 1:
       
  2024         ui.write(_("only one patch applied\n"))
       
  2025         return 1
       
  2026     if not l:
       
  2027         ui.write(_("no patches applied\n"))
       
  2028         return 1
       
  2029     q.qseries(repo, start=l - 2, length=1, status='A',
       
  2030               summary=opts.get('summary'))
       
  2031 
       
  2032 def setupheaderopts(ui, opts):
       
  2033     if not opts.get('user') and opts.get('currentuser'):
       
  2034         opts['user'] = ui.username()
       
  2035     if not opts.get('date') and opts.get('currentdate'):
       
  2036         opts['date'] = "%d %d" % util.makedate()
       
  2037 
       
  2038 def new(ui, repo, patch, *args, **opts):
       
  2039     """create a new patch
       
  2040 
       
  2041     qnew creates a new patch on top of the currently-applied patch (if
       
  2042     any). The patch will be initialized with any outstanding changes
       
  2043     in the working directory. You may also use -I/--include,
       
  2044     -X/--exclude, and/or a list of files after the patch name to add
       
  2045     only changes to matching files to the new patch, leaving the rest
       
  2046     as uncommitted modifications.
       
  2047 
       
  2048     -u/--user and -d/--date can be used to set the (given) user and
       
  2049     date, respectively. -U/--currentuser and -D/--currentdate set user
       
  2050     to current user and date to current date.
       
  2051 
       
  2052     -e/--edit, -m/--message or -l/--logfile set the patch header as
       
  2053     well as the commit message. If none is specified, the header is
       
  2054     empty and the commit message is '[mq]: PATCH'.
       
  2055 
       
  2056     Use the -g/--git option to keep the patch in the git extended diff
       
  2057     format. Read the diffs help topic for more information on why this
       
  2058     is important for preserving permission changes and copy/rename
       
  2059     information.
       
  2060 
       
  2061     Returns 0 on successful creation of a new patch.
       
  2062     """
       
  2063     msg = cmdutil.logmessage(opts)
       
  2064     def getmsg():
       
  2065         return ui.edit(msg, opts.get('user') or ui.username())
       
  2066     q = repo.mq
       
  2067     opts['msg'] = msg
       
  2068     if opts.get('edit'):
       
  2069         opts['msg'] = getmsg
       
  2070     else:
       
  2071         opts['msg'] = msg
       
  2072     setupheaderopts(ui, opts)
       
  2073     q.new(repo, patch, *args, **opts)
       
  2074     q.save_dirty()
       
  2075     return 0
       
  2076 
       
  2077 def refresh(ui, repo, *pats, **opts):
       
  2078     """update the current patch
       
  2079 
       
  2080     If any file patterns are provided, the refreshed patch will
       
  2081     contain only the modifications that match those patterns; the
       
  2082     remaining modifications will remain in the working directory.
       
  2083 
       
  2084     If -s/--short is specified, files currently included in the patch
       
  2085     will be refreshed just like matched files and remain in the patch.
       
  2086 
       
  2087     If -e/--edit is specified, Mercurial will start your configured editor for
       
  2088     you to enter a message. In case qrefresh fails, you will find a backup of
       
  2089     your message in ``.hg/last-message.txt``.
       
  2090 
       
  2091     hg add/remove/copy/rename work as usual, though you might want to
       
  2092     use git-style patches (-g/--git or [diff] git=1) to track copies
       
  2093     and renames. See the diffs help topic for more information on the
       
  2094     git diff format.
       
  2095 
       
  2096     Returns 0 on success.
       
  2097     """
       
  2098     q = repo.mq
       
  2099     message = cmdutil.logmessage(opts)
       
  2100     if opts.get('edit'):
       
  2101         if not q.applied:
       
  2102             ui.write(_("no patches applied\n"))
       
  2103             return 1
       
  2104         if message:
       
  2105             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
       
  2106         patch = q.applied[-1].name
       
  2107         ph = patchheader(q.join(patch), q.plainmode)
       
  2108         message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
       
  2109         # We don't want to lose the patch message if qrefresh fails (issue2062)
       
  2110         msgfile = repo.opener('last-message.txt', 'wb')
       
  2111         msgfile.write(message)
       
  2112         msgfile.close()
       
  2113     setupheaderopts(ui, opts)
       
  2114     ret = q.refresh(repo, pats, msg=message, **opts)
       
  2115     q.save_dirty()
       
  2116     return ret
       
  2117 
       
  2118 def diff(ui, repo, *pats, **opts):
       
  2119     """diff of the current patch and subsequent modifications
       
  2120 
       
  2121     Shows a diff which includes the current patch as well as any
       
  2122     changes which have been made in the working directory since the
       
  2123     last refresh (thus showing what the current patch would become
       
  2124     after a qrefresh).
       
  2125 
       
  2126     Use :hg:`diff` if you only want to see the changes made since the
       
  2127     last qrefresh, or :hg:`export qtip` if you want to see changes
       
  2128     made by the current patch without including changes made since the
       
  2129     qrefresh.
       
  2130 
       
  2131     Returns 0 on success.
       
  2132     """
       
  2133     repo.mq.diff(repo, pats, opts)
       
  2134     return 0
       
  2135 
       
  2136 def fold(ui, repo, *files, **opts):
       
  2137     """fold the named patches into the current patch
       
  2138 
       
  2139     Patches must not yet be applied. Each patch will be successively
       
  2140     applied to the current patch in the order given. If all the
       
  2141     patches apply successfully, the current patch will be refreshed
       
  2142     with the new cumulative patch, and the folded patches will be
       
  2143     deleted. With -k/--keep, the folded patch files will not be
       
  2144     removed afterwards.
       
  2145 
       
  2146     The header for each folded patch will be concatenated with the
       
  2147     current patch header, separated by a line of ``* * *``.
       
  2148 
       
  2149     Returns 0 on success."""
       
  2150 
       
  2151     q = repo.mq
       
  2152 
       
  2153     if not files:
       
  2154         raise util.Abort(_('qfold requires at least one patch name'))
       
  2155     if not q.check_toppatch(repo)[0]:
       
  2156         raise util.Abort(_('no patches applied'))
       
  2157     q.check_localchanges(repo)
       
  2158 
       
  2159     message = cmdutil.logmessage(opts)
       
  2160     if opts.get('edit'):
       
  2161         if message:
       
  2162             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
       
  2163 
       
  2164     parent = q.lookup('qtip')
       
  2165     patches = []
       
  2166     messages = []
       
  2167     for f in files:
       
  2168         p = q.lookup(f)
       
  2169         if p in patches or p == parent:
       
  2170             ui.warn(_('Skipping already folded patch %s\n') % p)
       
  2171         if q.isapplied(p):
       
  2172             raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
       
  2173         patches.append(p)
       
  2174 
       
  2175     for p in patches:
       
  2176         if not message:
       
  2177             ph = patchheader(q.join(p), q.plainmode)
       
  2178             if ph.message:
       
  2179                 messages.append(ph.message)
       
  2180         pf = q.join(p)
       
  2181         (patchsuccess, files, fuzz) = q.patch(repo, pf)
       
  2182         if not patchsuccess:
       
  2183             raise util.Abort(_('error folding patch %s') % p)
       
  2184         cmdutil.updatedir(ui, repo, files)
       
  2185 
       
  2186     if not message:
       
  2187         ph = patchheader(q.join(parent), q.plainmode)
       
  2188         message, user = ph.message, ph.user
       
  2189         for msg in messages:
       
  2190             message.append('* * *')
       
  2191             message.extend(msg)
       
  2192         message = '\n'.join(message)
       
  2193 
       
  2194     if opts.get('edit'):
       
  2195         message = ui.edit(message, user or ui.username())
       
  2196 
       
  2197     diffopts = q.patchopts(q.diffopts(), *patches)
       
  2198     q.refresh(repo, msg=message, git=diffopts.git)
       
  2199     q.delete(repo, patches, opts)
       
  2200     q.save_dirty()
       
  2201 
       
  2202 def goto(ui, repo, patch, **opts):
       
  2203     '''push or pop patches until named patch is at top of stack
       
  2204 
       
  2205     Returns 0 on success.'''
       
  2206     q = repo.mq
       
  2207     patch = q.lookup(patch)
       
  2208     if q.isapplied(patch):
       
  2209         ret = q.pop(repo, patch, force=opts.get('force'))
       
  2210     else:
       
  2211         ret = q.push(repo, patch, force=opts.get('force'))
       
  2212     q.save_dirty()
       
  2213     return ret
       
  2214 
       
  2215 def guard(ui, repo, *args, **opts):
       
  2216     '''set or print guards for a patch
       
  2217 
       
  2218     Guards control whether a patch can be pushed. A patch with no
       
  2219     guards is always pushed. A patch with a positive guard ("+foo") is
       
  2220     pushed only if the :hg:`qselect` command has activated it. A patch with
       
  2221     a negative guard ("-foo") is never pushed if the :hg:`qselect` command
       
  2222     has activated it.
       
  2223 
       
  2224     With no arguments, print the currently active guards.
       
  2225     With arguments, set guards for the named patch.
       
  2226 
       
  2227     .. note::
       
  2228        Specifying negative guards now requires '--'.
       
  2229 
       
  2230     To set guards on another patch::
       
  2231 
       
  2232       hg qguard other.patch -- +2.6.17 -stable
       
  2233 
       
  2234     Returns 0 on success.
       
  2235     '''
       
  2236     def status(idx):
       
  2237         guards = q.series_guards[idx] or ['unguarded']
       
  2238         if q.series[idx] in applied:
       
  2239             state = 'applied'
       
  2240         elif q.pushable(idx)[0]:
       
  2241             state = 'unapplied'
       
  2242         else:
       
  2243             state = 'guarded'
       
  2244         label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
       
  2245         ui.write('%s: ' % ui.label(q.series[idx], label))
       
  2246 
       
  2247         for i, guard in enumerate(guards):
       
  2248             if guard.startswith('+'):
       
  2249                 ui.write(guard, label='qguard.positive')
       
  2250             elif guard.startswith('-'):
       
  2251                 ui.write(guard, label='qguard.negative')
       
  2252             else:
       
  2253                 ui.write(guard, label='qguard.unguarded')
       
  2254             if i != len(guards) - 1:
       
  2255                 ui.write(' ')
       
  2256         ui.write('\n')
       
  2257     q = repo.mq
       
  2258     applied = set(p.name for p in q.applied)
       
  2259     patch = None
       
  2260     args = list(args)
       
  2261     if opts.get('list'):
       
  2262         if args or opts.get('none'):
       
  2263             raise util.Abort(_('cannot mix -l/--list with options or arguments'))
       
  2264         for i in xrange(len(q.series)):
       
  2265             status(i)
       
  2266         return
       
  2267     if not args or args[0][0:1] in '-+':
       
  2268         if not q.applied:
       
  2269             raise util.Abort(_('no patches applied'))
       
  2270         patch = q.applied[-1].name
       
  2271     if patch is None and args[0][0:1] not in '-+':
       
  2272         patch = args.pop(0)
       
  2273     if patch is None:
       
  2274         raise util.Abort(_('no patch to work with'))
       
  2275     if args or opts.get('none'):
       
  2276         idx = q.find_series(patch)
       
  2277         if idx is None:
       
  2278             raise util.Abort(_('no patch named %s') % patch)
       
  2279         q.set_guards(idx, args)
       
  2280         q.save_dirty()
       
  2281     else:
       
  2282         status(q.series.index(q.lookup(patch)))
       
  2283 
       
  2284 def header(ui, repo, patch=None):
       
  2285     """print the header of the topmost or specified patch
       
  2286 
       
  2287     Returns 0 on success."""
       
  2288     q = repo.mq
       
  2289 
       
  2290     if patch:
       
  2291         patch = q.lookup(patch)
       
  2292     else:
       
  2293         if not q.applied:
       
  2294             ui.write(_('no patches applied\n'))
       
  2295             return 1
       
  2296         patch = q.lookup('qtip')
       
  2297     ph = patchheader(q.join(patch), q.plainmode)
       
  2298 
       
  2299     ui.write('\n'.join(ph.message) + '\n')
       
  2300 
       
  2301 def lastsavename(path):
       
  2302     (directory, base) = os.path.split(path)
       
  2303     names = os.listdir(directory)
       
  2304     namere = re.compile("%s.([0-9]+)" % base)
       
  2305     maxindex = None
       
  2306     maxname = None
       
  2307     for f in names:
       
  2308         m = namere.match(f)
       
  2309         if m:
       
  2310             index = int(m.group(1))
       
  2311             if maxindex is None or index > maxindex:
       
  2312                 maxindex = index
       
  2313                 maxname = f
       
  2314     if maxname:
       
  2315         return (os.path.join(directory, maxname), maxindex)
       
  2316     return (None, None)
       
  2317 
       
  2318 def savename(path):
       
  2319     (last, index) = lastsavename(path)
       
  2320     if last is None:
       
  2321         index = 0
       
  2322     newpath = path + ".%d" % (index + 1)
       
  2323     return newpath
       
  2324 
       
  2325 def push(ui, repo, patch=None, **opts):
       
  2326     """push the next patch onto the stack
       
  2327 
       
  2328     When -f/--force is applied, all local changes in patched files
       
  2329     will be lost.
       
  2330 
       
  2331     Return 0 on succces.
       
  2332     """
       
  2333     q = repo.mq
       
  2334     mergeq = None
       
  2335 
       
  2336     if opts.get('merge'):
       
  2337         if opts.get('name'):
       
  2338             newpath = repo.join(opts.get('name'))
       
  2339         else:
       
  2340             newpath, i = lastsavename(q.path)
       
  2341         if not newpath:
       
  2342             ui.warn(_("no saved queues found, please use -n\n"))
       
  2343             return 1
       
  2344         mergeq = queue(ui, repo.join(""), newpath)
       
  2345         ui.warn(_("merging with queue at: %s\n") % mergeq.path)
       
  2346     ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
       
  2347                  mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
       
  2348     return ret
       
  2349 
       
  2350 def pop(ui, repo, patch=None, **opts):
       
  2351     """pop the current patch off the stack
       
  2352 
       
  2353     By default, pops off the top of the patch stack. If given a patch
       
  2354     name, keeps popping off patches until the named patch is at the
       
  2355     top of the stack.
       
  2356 
       
  2357     Return 0 on success.
       
  2358     """
       
  2359     localupdate = True
       
  2360     if opts.get('name'):
       
  2361         q = queue(ui, repo.join(""), repo.join(opts.get('name')))
       
  2362         ui.warn(_('using patch queue: %s\n') % q.path)
       
  2363         localupdate = False
       
  2364     else:
       
  2365         q = repo.mq
       
  2366     ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
       
  2367                 all=opts.get('all'))
       
  2368     q.save_dirty()
       
  2369     return ret
       
  2370 
       
  2371 def rename(ui, repo, patch, name=None, **opts):
       
  2372     """rename a patch
       
  2373 
       
  2374     With one argument, renames the current patch to PATCH1.
       
  2375     With two arguments, renames PATCH1 to PATCH2.
       
  2376 
       
  2377     Returns 0 on success."""
       
  2378 
       
  2379     q = repo.mq
       
  2380 
       
  2381     if not name:
       
  2382         name = patch
       
  2383         patch = None
       
  2384 
       
  2385     if patch:
       
  2386         patch = q.lookup(patch)
       
  2387     else:
       
  2388         if not q.applied:
       
  2389             ui.write(_('no patches applied\n'))
       
  2390             return
       
  2391         patch = q.lookup('qtip')
       
  2392     absdest = q.join(name)
       
  2393     if os.path.isdir(absdest):
       
  2394         name = normname(os.path.join(name, os.path.basename(patch)))
       
  2395         absdest = q.join(name)
       
  2396     if os.path.exists(absdest):
       
  2397         raise util.Abort(_('%s already exists') % absdest)
       
  2398 
       
  2399     if name in q.series:
       
  2400         raise util.Abort(
       
  2401             _('A patch named %s already exists in the series file') % name)
       
  2402 
       
  2403     ui.note(_('renaming %s to %s\n') % (patch, name))
       
  2404     i = q.find_series(patch)
       
  2405     guards = q.guard_re.findall(q.full_series[i])
       
  2406     q.full_series[i] = name + ''.join([' #' + g for g in guards])
       
  2407     q.parse_series()
       
  2408     q.series_dirty = 1
       
  2409 
       
  2410     info = q.isapplied(patch)
       
  2411     if info:
       
  2412         q.applied[info[0]] = statusentry(info[1], name)
       
  2413     q.applied_dirty = 1
       
  2414 
       
  2415     destdir = os.path.dirname(absdest)
       
  2416     if not os.path.isdir(destdir):
       
  2417         os.makedirs(destdir)
       
  2418     util.rename(q.join(patch), absdest)
       
  2419     r = q.qrepo()
       
  2420     if r and patch in r.dirstate:
       
  2421         wctx = r[None]
       
  2422         wlock = r.wlock()
       
  2423         try:
       
  2424             if r.dirstate[patch] == 'a':
       
  2425                 r.dirstate.forget(patch)
       
  2426                 r.dirstate.add(name)
       
  2427             else:
       
  2428                 if r.dirstate[name] == 'r':
       
  2429                     wctx.undelete([name])
       
  2430                 wctx.copy(patch, name)
       
  2431                 wctx.remove([patch], False)
       
  2432         finally:
       
  2433             wlock.release()
       
  2434 
       
  2435     q.save_dirty()
       
  2436 
       
  2437 def restore(ui, repo, rev, **opts):
       
  2438     """restore the queue state saved by a revision (DEPRECATED)
       
  2439 
       
  2440     This command is deprecated, use :hg:`rebase` instead."""
       
  2441     rev = repo.lookup(rev)
       
  2442     q = repo.mq
       
  2443     q.restore(repo, rev, delete=opts.get('delete'),
       
  2444               qupdate=opts.get('update'))
       
  2445     q.save_dirty()
       
  2446     return 0
       
  2447 
       
  2448 def save(ui, repo, **opts):
       
  2449     """save current queue state (DEPRECATED)
       
  2450 
       
  2451     This command is deprecated, use :hg:`rebase` instead."""
       
  2452     q = repo.mq
       
  2453     message = cmdutil.logmessage(opts)
       
  2454     ret = q.save(repo, msg=message)
       
  2455     if ret:
       
  2456         return ret
       
  2457     q.save_dirty()
       
  2458     if opts.get('copy'):
       
  2459         path = q.path
       
  2460         if opts.get('name'):
       
  2461             newpath = os.path.join(q.basepath, opts.get('name'))
       
  2462             if os.path.exists(newpath):
       
  2463                 if not os.path.isdir(newpath):
       
  2464                     raise util.Abort(_('destination %s exists and is not '
       
  2465                                        'a directory') % newpath)
       
  2466                 if not opts.get('force'):
       
  2467                     raise util.Abort(_('destination %s exists, '
       
  2468                                        'use -f to force') % newpath)
       
  2469         else:
       
  2470             newpath = savename(path)
       
  2471         ui.warn(_("copy %s to %s\n") % (path, newpath))
       
  2472         util.copyfiles(path, newpath)
       
  2473     if opts.get('empty'):
       
  2474         try:
       
  2475             os.unlink(q.join(q.status_path))
       
  2476         except:
       
  2477             pass
       
  2478     return 0
       
  2479 
       
  2480 def strip(ui, repo, *revs, **opts):
       
  2481     """strip changesets and all their descendants from the repository
       
  2482 
       
  2483     The strip command removes the specified changesets and all their
       
  2484     descendants. If the working directory has uncommitted changes,
       
  2485     the operation is aborted unless the --force flag is supplied.
       
  2486 
       
  2487     If a parent of the working directory is stripped, then the working
       
  2488     directory will automatically be updated to the most recent
       
  2489     available ancestor of the stripped parent after the operation
       
  2490     completes.
       
  2491 
       
  2492     Any stripped changesets are stored in ``.hg/strip-backup`` as a
       
  2493     bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
       
  2494     be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
       
  2495     where BUNDLE is the bundle file created by the strip. Note that
       
  2496     the local revision numbers will in general be different after the
       
  2497     restore.
       
  2498 
       
  2499     Use the --no-backup option to discard the backup bundle once the
       
  2500     operation completes.
       
  2501 
       
  2502     Return 0 on success.
       
  2503     """
       
  2504     backup = 'all'
       
  2505     if opts.get('backup'):
       
  2506         backup = 'strip'
       
  2507     elif opts.get('no_backup') or opts.get('nobackup'):
       
  2508         backup = 'none'
       
  2509 
       
  2510     cl = repo.changelog
       
  2511     revs = set(cmdutil.revrange(repo, revs))
       
  2512     if not revs:
       
  2513         raise util.Abort(_('empty revision set'))
       
  2514 
       
  2515     descendants = set(cl.descendants(*revs))
       
  2516     strippedrevs = revs.union(descendants)
       
  2517     roots = revs.difference(descendants)
       
  2518 
       
  2519     update = False
       
  2520     # if one of the wdir parent is stripped we'll need
       
  2521     # to update away to an earlier revision
       
  2522     for p in repo.dirstate.parents():
       
  2523         if p != nullid and cl.rev(p) in strippedrevs:
       
  2524             update = True
       
  2525             break
       
  2526 
       
  2527     rootnodes = set(cl.node(r) for r in roots)
       
  2528 
       
  2529     q = repo.mq
       
  2530     if q.applied:
       
  2531         # refresh queue state if we're about to strip
       
  2532         # applied patches
       
  2533         if cl.rev(repo.lookup('qtip')) in strippedrevs:
       
  2534             q.applied_dirty = True
       
  2535             start = 0
       
  2536             end = len(q.applied)
       
  2537             for i, statusentry in enumerate(q.applied):
       
  2538                 if statusentry.node in rootnodes:
       
  2539                     # if one of the stripped roots is an applied
       
  2540                     # patch, only part of the queue is stripped
       
  2541                     start = i
       
  2542                     break
       
  2543             del q.applied[start:end]
       
  2544             q.save_dirty()
       
  2545 
       
  2546     revs = list(rootnodes)
       
  2547     if update and opts.get('keep'):
       
  2548         wlock = repo.wlock()
       
  2549         try:
       
  2550             urev = repo.mq.qparents(repo, revs[0])
       
  2551             repo.dirstate.rebuild(urev, repo[urev].manifest())
       
  2552             repo.dirstate.write()
       
  2553             update = False
       
  2554         finally:
       
  2555             wlock.release()
       
  2556 
       
  2557     repo.mq.strip(repo, revs, backup=backup, update=update,
       
  2558                   force=opts.get('force'))
       
  2559     return 0
       
  2560 
       
  2561 def select(ui, repo, *args, **opts):
       
  2562     '''set or print guarded patches to push
       
  2563 
       
  2564     Use the :hg:`qguard` command to set or print guards on patch, then use
       
  2565     qselect to tell mq which guards to use. A patch will be pushed if
       
  2566     it has no guards or any positive guards match the currently
       
  2567     selected guard, but will not be pushed if any negative guards
       
  2568     match the current guard. For example::
       
  2569 
       
  2570         qguard foo.patch -stable    (negative guard)
       
  2571         qguard bar.patch +stable    (positive guard)
       
  2572         qselect stable
       
  2573 
       
  2574     This activates the "stable" guard. mq will skip foo.patch (because
       
  2575     it has a negative match) but push bar.patch (because it has a
       
  2576     positive match).
       
  2577 
       
  2578     With no arguments, prints the currently active guards.
       
  2579     With one argument, sets the active guard.
       
  2580 
       
  2581     Use -n/--none to deactivate guards (no other arguments needed).
       
  2582     When no guards are active, patches with positive guards are
       
  2583     skipped and patches with negative guards are pushed.
       
  2584 
       
  2585     qselect can change the guards on applied patches. It does not pop
       
  2586     guarded patches by default. Use --pop to pop back to the last
       
  2587     applied patch that is not guarded. Use --reapply (which implies
       
  2588     --pop) to push back to the current patch afterwards, but skip
       
  2589     guarded patches.
       
  2590 
       
  2591     Use -s/--series to print a list of all guards in the series file
       
  2592     (no other arguments needed). Use -v for more information.
       
  2593 
       
  2594     Returns 0 on success.'''
       
  2595 
       
  2596     q = repo.mq
       
  2597     guards = q.active()
       
  2598     if args or opts.get('none'):
       
  2599         old_unapplied = q.unapplied(repo)
       
  2600         old_guarded = [i for i in xrange(len(q.applied)) if
       
  2601                        not q.pushable(i)[0]]
       
  2602         q.set_active(args)
       
  2603         q.save_dirty()
       
  2604         if not args:
       
  2605             ui.status(_('guards deactivated\n'))
       
  2606         if not opts.get('pop') and not opts.get('reapply'):
       
  2607             unapplied = q.unapplied(repo)
       
  2608             guarded = [i for i in xrange(len(q.applied))
       
  2609                        if not q.pushable(i)[0]]
       
  2610             if len(unapplied) != len(old_unapplied):
       
  2611                 ui.status(_('number of unguarded, unapplied patches has '
       
  2612                             'changed from %d to %d\n') %
       
  2613                           (len(old_unapplied), len(unapplied)))
       
  2614             if len(guarded) != len(old_guarded):
       
  2615                 ui.status(_('number of guarded, applied patches has changed '
       
  2616                             'from %d to %d\n') %
       
  2617                           (len(old_guarded), len(guarded)))
       
  2618     elif opts.get('series'):
       
  2619         guards = {}
       
  2620         noguards = 0
       
  2621         for gs in q.series_guards:
       
  2622             if not gs:
       
  2623                 noguards += 1
       
  2624             for g in gs:
       
  2625                 guards.setdefault(g, 0)
       
  2626                 guards[g] += 1
       
  2627         if ui.verbose:
       
  2628             guards['NONE'] = noguards
       
  2629         guards = guards.items()
       
  2630         guards.sort(key=lambda x: x[0][1:])
       
  2631         if guards:
       
  2632             ui.note(_('guards in series file:\n'))
       
  2633             for guard, count in guards:
       
  2634                 ui.note('%2d  ' % count)
       
  2635                 ui.write(guard, '\n')
       
  2636         else:
       
  2637             ui.note(_('no guards in series file\n'))
       
  2638     else:
       
  2639         if guards:
       
  2640             ui.note(_('active guards:\n'))
       
  2641             for g in guards:
       
  2642                 ui.write(g, '\n')
       
  2643         else:
       
  2644             ui.write(_('no active guards\n'))
       
  2645     reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
       
  2646     popped = False
       
  2647     if opts.get('pop') or opts.get('reapply'):
       
  2648         for i in xrange(len(q.applied)):
       
  2649             pushable, reason = q.pushable(i)
       
  2650             if not pushable:
       
  2651                 ui.status(_('popping guarded patches\n'))
       
  2652                 popped = True
       
  2653                 if i == 0:
       
  2654                     q.pop(repo, all=True)
       
  2655                 else:
       
  2656                     q.pop(repo, i - 1)
       
  2657                 break
       
  2658     if popped:
       
  2659         try:
       
  2660             if reapply:
       
  2661                 ui.status(_('reapplying unguarded patches\n'))
       
  2662                 q.push(repo, reapply)
       
  2663         finally:
       
  2664             q.save_dirty()
       
  2665 
       
  2666 def finish(ui, repo, *revrange, **opts):
       
  2667     """move applied patches into repository history
       
  2668 
       
  2669     Finishes the specified revisions (corresponding to applied
       
  2670     patches) by moving them out of mq control into regular repository
       
  2671     history.
       
  2672 
       
  2673     Accepts a revision range or the -a/--applied option. If --applied
       
  2674     is specified, all applied mq revisions are removed from mq
       
  2675     control. Otherwise, the given revisions must be at the base of the
       
  2676     stack of applied patches.
       
  2677 
       
  2678     This can be especially useful if your changes have been applied to
       
  2679     an upstream repository, or if you are about to push your changes
       
  2680     to upstream.
       
  2681 
       
  2682     Returns 0 on success.
       
  2683     """
       
  2684     if not opts.get('applied') and not revrange:
       
  2685         raise util.Abort(_('no revisions specified'))
       
  2686     elif opts.get('applied'):
       
  2687         revrange = ('qbase::qtip',) + revrange
       
  2688 
       
  2689     q = repo.mq
       
  2690     if not q.applied:
       
  2691         ui.status(_('no patches applied\n'))
       
  2692         return 0
       
  2693 
       
  2694     revs = cmdutil.revrange(repo, revrange)
       
  2695     q.finish(repo, revs)
       
  2696     q.save_dirty()
       
  2697     return 0
       
  2698 
       
  2699 def qqueue(ui, repo, name=None, **opts):
       
  2700     '''manage multiple patch queues
       
  2701 
       
  2702     Supports switching between different patch queues, as well as creating
       
  2703     new patch queues and deleting existing ones.
       
  2704 
       
  2705     Omitting a queue name or specifying -l/--list will show you the registered
       
  2706     queues - by default the "normal" patches queue is registered. The currently
       
  2707     active queue will be marked with "(active)".
       
  2708 
       
  2709     To create a new queue, use -c/--create. The queue is automatically made
       
  2710     active, except in the case where there are applied patches from the
       
  2711     currently active queue in the repository. Then the queue will only be
       
  2712     created and switching will fail.
       
  2713 
       
  2714     To delete an existing queue, use --delete. You cannot delete the currently
       
  2715     active queue.
       
  2716 
       
  2717     Returns 0 on success.
       
  2718     '''
       
  2719 
       
  2720     q = repo.mq
       
  2721 
       
  2722     _defaultqueue = 'patches'
       
  2723     _allqueues = 'patches.queues'
       
  2724     _activequeue = 'patches.queue'
       
  2725 
       
  2726     def _getcurrent():
       
  2727         cur = os.path.basename(q.path)
       
  2728         if cur.startswith('patches-'):
       
  2729             cur = cur[8:]
       
  2730         return cur
       
  2731 
       
  2732     def _noqueues():
       
  2733         try:
       
  2734             fh = repo.opener(_allqueues, 'r')
       
  2735             fh.close()
       
  2736         except IOError:
       
  2737             return True
       
  2738 
       
  2739         return False
       
  2740 
       
  2741     def _getqueues():
       
  2742         current = _getcurrent()
       
  2743 
       
  2744         try:
       
  2745             fh = repo.opener(_allqueues, 'r')
       
  2746             queues = [queue.strip() for queue in fh if queue.strip()]
       
  2747             if current not in queues:
       
  2748                 queues.append(current)
       
  2749         except IOError:
       
  2750             queues = [_defaultqueue]
       
  2751 
       
  2752         return sorted(queues)
       
  2753 
       
  2754     def _setactive(name):
       
  2755         if q.applied:
       
  2756             raise util.Abort(_('patches applied - cannot set new queue active'))
       
  2757         _setactivenocheck(name)
       
  2758 
       
  2759     def _setactivenocheck(name):
       
  2760         fh = repo.opener(_activequeue, 'w')
       
  2761         if name != 'patches':
       
  2762             fh.write(name)
       
  2763         fh.close()
       
  2764 
       
  2765     def _addqueue(name):
       
  2766         fh = repo.opener(_allqueues, 'a')
       
  2767         fh.write('%s\n' % (name,))
       
  2768         fh.close()
       
  2769 
       
  2770     def _queuedir(name):
       
  2771         if name == 'patches':
       
  2772             return repo.join('patches')
       
  2773         else:
       
  2774             return repo.join('patches-' + name)
       
  2775 
       
  2776     def _validname(name):
       
  2777         for n in name:
       
  2778             if n in ':\\/.':
       
  2779                 return False
       
  2780         return True
       
  2781 
       
  2782     def _delete(name):
       
  2783         if name not in existing:
       
  2784             raise util.Abort(_('cannot delete queue that does not exist'))
       
  2785 
       
  2786         current = _getcurrent()
       
  2787 
       
  2788         if name == current:
       
  2789             raise util.Abort(_('cannot delete currently active queue'))
       
  2790 
       
  2791         fh = repo.opener('patches.queues.new', 'w')
       
  2792         for queue in existing:
       
  2793             if queue == name:
       
  2794                 continue
       
  2795             fh.write('%s\n' % (queue,))
       
  2796         fh.close()
       
  2797         util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
       
  2798 
       
  2799     if not name or opts.get('list'):
       
  2800         current = _getcurrent()
       
  2801         for queue in _getqueues():
       
  2802             ui.write('%s' % (queue,))
       
  2803             if queue == current and not ui.quiet:
       
  2804                 ui.write(_(' (active)\n'))
       
  2805             else:
       
  2806                 ui.write('\n')
       
  2807         return
       
  2808 
       
  2809     if not _validname(name):
       
  2810         raise util.Abort(
       
  2811                 _('invalid queue name, may not contain the characters ":\\/."'))
       
  2812 
       
  2813     existing = _getqueues()
       
  2814 
       
  2815     if opts.get('create'):
       
  2816         if name in existing:
       
  2817             raise util.Abort(_('queue "%s" already exists') % name)
       
  2818         if _noqueues():
       
  2819             _addqueue(_defaultqueue)
       
  2820         _addqueue(name)
       
  2821         _setactive(name)
       
  2822     elif opts.get('rename'):
       
  2823         current = _getcurrent()
       
  2824         if name == current:
       
  2825             raise util.Abort(_('can\'t rename "%s" to its current name') % name)
       
  2826         if name in existing:
       
  2827             raise util.Abort(_('queue "%s" already exists') % name)
       
  2828 
       
  2829         olddir = _queuedir(current)
       
  2830         newdir = _queuedir(name)
       
  2831 
       
  2832         if os.path.exists(newdir):
       
  2833             raise util.Abort(_('non-queue directory "%s" already exists') %
       
  2834                     newdir)
       
  2835 
       
  2836         fh = repo.opener('patches.queues.new', 'w')
       
  2837         for queue in existing:
       
  2838             if queue == current:
       
  2839                 fh.write('%s\n' % (name,))
       
  2840                 if os.path.exists(olddir):
       
  2841                     util.rename(olddir, newdir)
       
  2842             else:
       
  2843                 fh.write('%s\n' % (queue,))
       
  2844         fh.close()
       
  2845         util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
       
  2846         _setactivenocheck(name)
       
  2847     elif opts.get('delete'):
       
  2848         _delete(name)
       
  2849     elif opts.get('purge'):
       
  2850         if name in existing:
       
  2851             _delete(name)
       
  2852         qdir = _queuedir(name)
       
  2853         if os.path.exists(qdir):
       
  2854             shutil.rmtree(qdir)
       
  2855     else:
       
  2856         if name not in existing:
       
  2857             raise util.Abort(_('use --create to create a new queue'))
       
  2858         _setactive(name)
       
  2859 
       
  2860 def reposetup(ui, repo):
       
  2861     class mqrepo(repo.__class__):
       
  2862         @util.propertycache
       
  2863         def mq(self):
       
  2864             return queue(self.ui, self.join(""))
       
  2865 
       
  2866         def abort_if_wdir_patched(self, errmsg, force=False):
       
  2867             if self.mq.applied and not force:
       
  2868                 parent = self.dirstate.parents()[0]
       
  2869                 if parent in [s.node for s in self.mq.applied]:
       
  2870                     raise util.Abort(errmsg)
       
  2871 
       
  2872         def commit(self, text="", user=None, date=None, match=None,
       
  2873                    force=False, editor=False, extra={}):
       
  2874             self.abort_if_wdir_patched(
       
  2875                 _('cannot commit over an applied mq patch'),
       
  2876                 force)
       
  2877 
       
  2878             return super(mqrepo, self).commit(text, user, date, match, force,
       
  2879                                               editor, extra)
       
  2880 
       
  2881         def push(self, remote, force=False, revs=None, newbranch=False):
       
  2882             if self.mq.applied and not force:
       
  2883                 haspatches = True
       
  2884                 if revs:
       
  2885                     # Assume applied patches have no non-patch descendants
       
  2886                     # and are not on remote already. If they appear in the
       
  2887                     # set of resolved 'revs', bail out.
       
  2888                     applied = set(e.node for e in self.mq.applied)
       
  2889                     haspatches = bool([n for n in revs if n in applied])
       
  2890                 if haspatches:
       
  2891                     raise util.Abort(_('source has mq patches applied'))
       
  2892             return super(mqrepo, self).push(remote, force, revs, newbranch)
       
  2893 
       
  2894         def _findtags(self):
       
  2895             '''augment tags from base class with patch tags'''
       
  2896             result = super(mqrepo, self)._findtags()
       
  2897 
       
  2898             q = self.mq
       
  2899             if not q.applied:
       
  2900                 return result
       
  2901 
       
  2902             mqtags = [(patch.node, patch.name) for patch in q.applied]
       
  2903 
       
  2904             if mqtags[-1][0] not in self.changelog.nodemap:
       
  2905                 self.ui.warn(_('mq status file refers to unknown node %s\n')
       
  2906                              % short(mqtags[-1][0]))
       
  2907                 return result
       
  2908 
       
  2909             mqtags.append((mqtags[-1][0], 'qtip'))
       
  2910             mqtags.append((mqtags[0][0], 'qbase'))
       
  2911             mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
       
  2912             tags = result[0]
       
  2913             for patch in mqtags:
       
  2914                 if patch[1] in tags:
       
  2915                     self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
       
  2916                                  % patch[1])
       
  2917                 else:
       
  2918                     tags[patch[1]] = patch[0]
       
  2919 
       
  2920             return result
       
  2921 
       
  2922         def _branchtags(self, partial, lrev):
       
  2923             q = self.mq
       
  2924             if not q.applied:
       
  2925                 return super(mqrepo, self)._branchtags(partial, lrev)
       
  2926 
       
  2927             cl = self.changelog
       
  2928             qbasenode = q.applied[0].node
       
  2929             if qbasenode not in cl.nodemap:
       
  2930                 self.ui.warn(_('mq status file refers to unknown node %s\n')
       
  2931                              % short(qbasenode))
       
  2932                 return super(mqrepo, self)._branchtags(partial, lrev)
       
  2933 
       
  2934             qbase = cl.rev(qbasenode)
       
  2935             start = lrev + 1
       
  2936             if start < qbase:
       
  2937                 # update the cache (excluding the patches) and save it
       
  2938                 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
       
  2939                 self._updatebranchcache(partial, ctxgen)
       
  2940                 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
       
  2941                 start = qbase
       
  2942             # if start = qbase, the cache is as updated as it should be.
       
  2943             # if start > qbase, the cache includes (part of) the patches.
       
  2944             # we might as well use it, but we won't save it.
       
  2945 
       
  2946             # update the cache up to the tip
       
  2947             ctxgen = (self[r] for r in xrange(start, len(cl)))
       
  2948             self._updatebranchcache(partial, ctxgen)
       
  2949 
       
  2950             return partial
       
  2951 
       
  2952     if repo.local():
       
  2953         repo.__class__ = mqrepo
       
  2954 
       
  2955 def mqimport(orig, ui, repo, *args, **kwargs):
       
  2956     if (hasattr(repo, 'abort_if_wdir_patched')
       
  2957         and not kwargs.get('no_commit', False)):
       
  2958         repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
       
  2959                                    kwargs.get('force'))
       
  2960     return orig(ui, repo, *args, **kwargs)
       
  2961 
       
  2962 def mqinit(orig, ui, *args, **kwargs):
       
  2963     mq = kwargs.pop('mq', None)
       
  2964 
       
  2965     if not mq:
       
  2966         return orig(ui, *args, **kwargs)
       
  2967 
       
  2968     if args:
       
  2969         repopath = args[0]
       
  2970         if not hg.islocal(repopath):
       
  2971             raise util.Abort(_('only a local queue repository '
       
  2972                                'may be initialized'))
       
  2973     else:
       
  2974         repopath = cmdutil.findrepo(os.getcwd())
       
  2975         if not repopath:
       
  2976             raise util.Abort(_('there is no Mercurial repository here '
       
  2977                                '(.hg not found)'))
       
  2978     repo = hg.repository(ui, repopath)
       
  2979     return qinit(ui, repo, True)
       
  2980 
       
  2981 def mqcommand(orig, ui, repo, *args, **kwargs):
       
  2982     """Add --mq option to operate on patch repository instead of main"""
       
  2983 
       
  2984     # some commands do not like getting unknown options
       
  2985     mq = kwargs.pop('mq', None)
       
  2986 
       
  2987     if not mq:
       
  2988         return orig(ui, repo, *args, **kwargs)
       
  2989 
       
  2990     q = repo.mq
       
  2991     r = q.qrepo()
       
  2992     if not r:
       
  2993         raise util.Abort(_('no queue repository'))
       
  2994     return orig(r.ui, r, *args, **kwargs)
       
  2995 
       
  2996 def summary(orig, ui, repo, *args, **kwargs):
       
  2997     r = orig(ui, repo, *args, **kwargs)
       
  2998     q = repo.mq
       
  2999     m = []
       
  3000     a, u = len(q.applied), len(q.unapplied(repo))
       
  3001     if a:
       
  3002         m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
       
  3003     if u:
       
  3004         m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
       
  3005     if m:
       
  3006         ui.write("mq:     %s\n" % ', '.join(m))
       
  3007     else:
       
  3008         ui.note(_("mq:     (empty queue)\n"))
       
  3009     return r
       
  3010 
       
  3011 def uisetup(ui):
       
  3012     mqopt = [('', 'mq', None, _("operate on patch repository"))]
       
  3013 
       
  3014     extensions.wrapcommand(commands.table, 'import', mqimport)
       
  3015     extensions.wrapcommand(commands.table, 'summary', summary)
       
  3016 
       
  3017     entry = extensions.wrapcommand(commands.table, 'init', mqinit)
       
  3018     entry[1].extend(mqopt)
       
  3019 
       
  3020     nowrap = set(commands.norepo.split(" ") + ['qrecord'])
       
  3021 
       
  3022     def dotable(cmdtable):
       
  3023         for cmd in cmdtable.keys():
       
  3024             cmd = cmdutil.parsealiases(cmd)[0]
       
  3025             if cmd in nowrap:
       
  3026                 continue
       
  3027             entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
       
  3028             entry[1].extend(mqopt)
       
  3029 
       
  3030     dotable(commands.table)
       
  3031 
       
  3032     for extname, extmodule in extensions.extensions():
       
  3033         if extmodule.__file__ != __file__:
       
  3034             dotable(getattr(extmodule, 'cmdtable', {}))
       
  3035 
       
  3036 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
       
  3037 
       
  3038 cmdtable = {
       
  3039     "qapplied":
       
  3040         (applied,
       
  3041          [('1', 'last', None, _('show only the last patch'))] + seriesopts,
       
  3042          _('hg qapplied [-1] [-s] [PATCH]')),
       
  3043     "qclone":
       
  3044         (clone,
       
  3045          [('', 'pull', None, _('use pull protocol to copy metadata')),
       
  3046           ('U', 'noupdate', None, _('do not update the new working directories')),
       
  3047           ('', 'uncompressed', None,
       
  3048            _('use uncompressed transfer (fast over LAN)')),
       
  3049           ('p', 'patches', '',
       
  3050            _('location of source patch repository'), _('REPO')),
       
  3051          ] + commands.remoteopts,
       
  3052          _('hg qclone [OPTION]... SOURCE [DEST]')),
       
  3053     "qcommit|qci":
       
  3054         (commit,
       
  3055          commands.table["^commit|ci"][1],
       
  3056          _('hg qcommit [OPTION]... [FILE]...')),
       
  3057     "^qdiff":
       
  3058         (diff,
       
  3059          commands.diffopts + commands.diffopts2 + commands.walkopts,
       
  3060          _('hg qdiff [OPTION]... [FILE]...')),
       
  3061     "qdelete|qremove|qrm":
       
  3062         (delete,
       
  3063          [('k', 'keep', None, _('keep patch file')),
       
  3064           ('r', 'rev', [],
       
  3065            _('stop managing a revision (DEPRECATED)'), _('REV'))],
       
  3066          _('hg qdelete [-k] [PATCH]...')),
       
  3067     'qfold':
       
  3068         (fold,
       
  3069          [('e', 'edit', None, _('edit patch header')),
       
  3070           ('k', 'keep', None, _('keep folded patch files')),
       
  3071          ] + commands.commitopts,
       
  3072          _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
       
  3073     'qgoto':
       
  3074         (goto,
       
  3075          [('f', 'force', None, _('overwrite any local changes'))],
       
  3076          _('hg qgoto [OPTION]... PATCH')),
       
  3077     'qguard':
       
  3078         (guard,
       
  3079          [('l', 'list', None, _('list all patches and guards')),
       
  3080           ('n', 'none', None, _('drop all guards'))],
       
  3081          _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
       
  3082     'qheader': (header, [], _('hg qheader [PATCH]')),
       
  3083     "qimport":
       
  3084         (qimport,
       
  3085          [('e', 'existing', None, _('import file in patch directory')),
       
  3086           ('n', 'name', '',
       
  3087            _('name of patch file'), _('NAME')),
       
  3088           ('f', 'force', None, _('overwrite existing files')),
       
  3089           ('r', 'rev', [],
       
  3090            _('place existing revisions under mq control'), _('REV')),
       
  3091           ('g', 'git', None, _('use git extended diff format')),
       
  3092           ('P', 'push', None, _('qpush after importing'))],
       
  3093          _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
       
  3094     "^qinit":
       
  3095         (init,
       
  3096          [('c', 'create-repo', None, _('create queue repository'))],
       
  3097          _('hg qinit [-c]')),
       
  3098     "^qnew":
       
  3099         (new,
       
  3100          [('e', 'edit', None, _('edit commit message')),
       
  3101           ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
       
  3102           ('g', 'git', None, _('use git extended diff format')),
       
  3103           ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
       
  3104           ('u', 'user', '',
       
  3105            _('add "From: <USER>" to patch'), _('USER')),
       
  3106           ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
       
  3107           ('d', 'date', '',
       
  3108            _('add "Date: <DATE>" to patch'), _('DATE'))
       
  3109           ] + commands.walkopts + commands.commitopts,
       
  3110          _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
       
  3111     "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
       
  3112     "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
       
  3113     "^qpop":
       
  3114         (pop,
       
  3115          [('a', 'all', None, _('pop all patches')),
       
  3116           ('n', 'name', '',
       
  3117            _('queue name to pop (DEPRECATED)'), _('NAME')),
       
  3118           ('f', 'force', None, _('forget any local changes to patched files'))],
       
  3119          _('hg qpop [-a] [-f] [PATCH | INDEX]')),
       
  3120     "^qpush":
       
  3121         (push,
       
  3122          [('f', 'force', None, _('apply on top of local changes')),
       
  3123           ('l', 'list', None, _('list patch name in commit text')),
       
  3124           ('a', 'all', None, _('apply all patches')),
       
  3125           ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
       
  3126           ('n', 'name', '',
       
  3127            _('merge queue name (DEPRECATED)'), _('NAME')),
       
  3128           ('', 'move', None, _('reorder patch series and apply only the patch'))],
       
  3129          _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]')),
       
  3130     "^qrefresh":
       
  3131         (refresh,
       
  3132          [('e', 'edit', None, _('edit commit message')),
       
  3133           ('g', 'git', None, _('use git extended diff format')),
       
  3134           ('s', 'short', None,
       
  3135            _('refresh only files already in the patch and specified files')),
       
  3136           ('U', 'currentuser', None,
       
  3137            _('add/update author field in patch with current user')),
       
  3138           ('u', 'user', '',
       
  3139            _('add/update author field in patch with given user'), _('USER')),
       
  3140           ('D', 'currentdate', None,
       
  3141            _('add/update date field in patch with current date')),
       
  3142           ('d', 'date', '',
       
  3143            _('add/update date field in patch with given date'), _('DATE'))
       
  3144           ] + commands.walkopts + commands.commitopts,
       
  3145          _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
       
  3146     'qrename|qmv':
       
  3147         (rename, [], _('hg qrename PATCH1 [PATCH2]')),
       
  3148     "qrestore":
       
  3149         (restore,
       
  3150          [('d', 'delete', None, _('delete save entry')),
       
  3151           ('u', 'update', None, _('update queue working directory'))],
       
  3152          _('hg qrestore [-d] [-u] REV')),
       
  3153     "qsave":
       
  3154         (save,
       
  3155          [('c', 'copy', None, _('copy patch directory')),
       
  3156           ('n', 'name', '',
       
  3157            _('copy directory name'), _('NAME')),
       
  3158           ('e', 'empty', None, _('clear queue status file')),
       
  3159           ('f', 'force', None, _('force copy'))] + commands.commitopts,
       
  3160          _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
       
  3161     "qselect":
       
  3162         (select,
       
  3163          [('n', 'none', None, _('disable all guards')),
       
  3164           ('s', 'series', None, _('list all guards in series file')),
       
  3165           ('', 'pop', None, _('pop to before first guarded applied patch')),
       
  3166           ('', 'reapply', None, _('pop, then reapply patches'))],
       
  3167          _('hg qselect [OPTION]... [GUARD]...')),
       
  3168     "qseries":
       
  3169         (series,
       
  3170          [('m', 'missing', None, _('print patches not in series')),
       
  3171          ] + seriesopts,
       
  3172           _('hg qseries [-ms]')),
       
  3173      "strip":
       
  3174          (strip,
       
  3175          [('f', 'force', None, _('force removal of changesets even if the '
       
  3176                                  'working directory has uncommitted changes')),
       
  3177           ('b', 'backup', None, _('bundle only changesets with local revision'
       
  3178                                   ' number greater than REV which are not'
       
  3179                                   ' descendants of REV (DEPRECATED)')),
       
  3180           ('n', 'no-backup', None, _('no backups')),
       
  3181           ('', 'nobackup', None, _('no backups (DEPRECATED)')),
       
  3182           ('k', 'keep', None, _("do not modify working copy during strip"))],
       
  3183           _('hg strip [-k] [-f] [-n] REV...')),
       
  3184      "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
       
  3185     "qunapplied":
       
  3186         (unapplied,
       
  3187          [('1', 'first', None, _('show only the first patch'))] + seriesopts,
       
  3188          _('hg qunapplied [-1] [-s] [PATCH]')),
       
  3189     "qfinish":
       
  3190         (finish,
       
  3191          [('a', 'applied', None, _('finish all applied changesets'))],
       
  3192          _('hg qfinish [-a] [REV]...')),
       
  3193     'qqueue':
       
  3194         (qqueue,
       
  3195          [
       
  3196              ('l', 'list', False, _('list all available queues')),
       
  3197              ('c', 'create', False, _('create new queue')),
       
  3198              ('', 'rename', False, _('rename active queue')),
       
  3199              ('', 'delete', False, _('delete reference to queue')),
       
  3200              ('', 'purge', False, _('delete queue, and remove patch dir')),
       
  3201          ],
       
  3202          _('[OPTION] [QUEUE]')),
       
  3203 }
       
  3204 
       
  3205 colortable = {'qguard.negative': 'red',
       
  3206               'qguard.positive': 'yellow',
       
  3207               'qguard.unguarded': 'green',
       
  3208               'qseries.applied': 'blue bold underline',
       
  3209               'qseries.guarded': 'black bold',
       
  3210               'qseries.missing': 'red bold',
       
  3211               'qseries.unapplied': 'black bold'}