hgext/mq.py
changeset 2830 49988d9f0758
parent 2822 30f59f4a327e
parent 2818 05316bb57d01
child 2832 582cbc4392cb
child 2852 046a8b03ea59
equal deleted inserted replaced
2829:4870f795f681 2830:49988d9f0758
    33 from mercurial.demandload import *
    33 from mercurial.demandload import *
    34 demandload(globals(), "os sys re struct traceback errno bz2")
    34 demandload(globals(), "os sys re struct traceback errno bz2")
    35 from mercurial.i18n import gettext as _
    35 from mercurial.i18n import gettext as _
    36 from mercurial import ui, hg, revlog, commands, util
    36 from mercurial import ui, hg, revlog, commands, util
    37 
    37 
    38 versionstr = "0.45"
       
    39 
       
    40 commands.norepo += " qclone qversion"
    38 commands.norepo += " qclone qversion"
    41 
    39 
    42 class StatusEntry:
    40 class statusentry:
    43     def __init__(self, rev, name=None):
    41     def __init__(self, rev, name=None):
    44         if not name:
    42         if not name:
    45             self.rev, self.name = rev.split(':')
    43             fields = rev.split(':')
       
    44             if len(fields) == 2:
       
    45                 self.rev, self.name = fields
       
    46             else:
       
    47                 self.rev, self.name = None, None
    46         else:
    48         else:
    47             self.rev, self.name = rev, name
    49             self.rev, self.name = rev, name
    48 
    50 
    49     def __str__(self):
    51     def __str__(self):
    50         return self.rev + ':' + self.name
    52         return self.rev + ':' + self.name
    51 
    53 
    52 class queue:
    54 class queue:
    53     def __init__(self, ui, path, patchdir=None):
    55     def __init__(self, ui, path, patchdir=None):
    54         self.basepath = path
    56         self.basepath = path
    55         if patchdir:
    57         self.path = patchdir or os.path.join(path, "patches")
    56             self.path = patchdir
       
    57         else:
       
    58             self.path = os.path.join(path, "patches")
       
    59         self.opener = util.opener(self.path)
    58         self.opener = util.opener(self.path)
    60         self.ui = ui
    59         self.ui = ui
    61         self.applied = []
    60         self.applied = []
    62         self.full_series = []
    61         self.full_series = []
    63         self.applied_dirty = 0
    62         self.applied_dirty = 0
    64         self.series_dirty = 0
    63         self.series_dirty = 0
    65         self.series_path = "series"
    64         self.series_path = "series"
    66         self.status_path = "status"
    65         self.status_path = "status"
    67 
    66         self.guards_path = "guards"
    68         if os.path.exists(os.path.join(self.path, self.series_path)):
    67         self.active_guards = None
       
    68         self.guards_dirty = False
       
    69 
       
    70         if os.path.exists(self.join(self.series_path)):
    69             self.full_series = self.opener(self.series_path).read().splitlines()
    71             self.full_series = self.opener(self.series_path).read().splitlines()
    70         self.parse_series()
    72         self.parse_series()
    71 
    73 
    72         if os.path.exists(os.path.join(self.path, self.status_path)):
    74         if os.path.exists(self.join(self.status_path)):
    73             self.applied = [StatusEntry(l)
    75             lines = self.opener(self.status_path).read().splitlines()
    74                             for l in self.opener(self.status_path).read().splitlines()]
    76             self.applied = [statusentry(l) for l in lines]
       
    77 
       
    78     def join(self, *p):
       
    79         return os.path.join(self.path, *p)
    75 
    80 
    76     def find_series(self, patch):
    81     def find_series(self, patch):
    77         pre = re.compile("(\s*)([^#]+)")
    82         pre = re.compile("(\s*)([^#]+)")
    78         index = 0
    83         index = 0
    79         for l in self.full_series:
    84         for l in self.full_series:
    84                 if s == patch:
    89                 if s == patch:
    85                     return index
    90                     return index
    86             index += 1
    91             index += 1
    87         return None
    92         return None
    88 
    93 
       
    94     guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
       
    95 
    89     def parse_series(self):
    96     def parse_series(self):
    90         self.series = []
    97         self.series = []
       
    98         self.series_guards = []
    91         for l in self.full_series:
    99         for l in self.full_series:
    92             s = l.split('#', 1)[0].strip()
   100             h = l.find('#')
    93             if s:
   101             if h == -1:
    94                 self.series.append(s)
   102                 patch = l
       
   103                 comment = ''
       
   104             elif h == 0:
       
   105                 continue
       
   106             else:
       
   107                 patch = l[:h]
       
   108                 comment = l[h:]
       
   109             patch = patch.strip()
       
   110             if patch:
       
   111                 self.series.append(patch)
       
   112                 self.series_guards.append(self.guard_re.findall(comment))
       
   113 
       
   114     def check_guard(self, guard):
       
   115         bad_chars = '# \t\r\n\f'
       
   116         first = guard[0]
       
   117         for c in '-+':
       
   118             if first == c:
       
   119                 return (_('guard %r starts with invalid character: %r') %
       
   120                         (guard, c))
       
   121         for c in bad_chars:
       
   122             if c in guard:
       
   123                 return _('invalid character in guard %r: %r') % (guard, c)
       
   124         
       
   125     def set_active(self, guards):
       
   126         for guard in guards:
       
   127             bad = self.check_guard(guard)
       
   128             if bad:
       
   129                 raise util.Abort(bad)
       
   130         guards = dict.fromkeys(guards).keys()
       
   131         guards.sort()
       
   132         self.ui.debug('active guards: %s\n' % ' '.join(guards))
       
   133         self.active_guards = guards
       
   134         self.guards_dirty = True
       
   135 
       
   136     def active(self):
       
   137         if self.active_guards is None:
       
   138             self.active_guards = []
       
   139             try:
       
   140                 guards = self.opener(self.guards_path).read().split()
       
   141             except IOError, err:
       
   142                 if err.errno != errno.ENOENT: raise
       
   143                 guards = []
       
   144             for i, guard in enumerate(guards):
       
   145                 bad = self.check_guard(guard)
       
   146                 if bad:
       
   147                     self.ui.warn('%s:%d: %s\n' %
       
   148                                  (self.join(self.guards_path), i + 1, bad))
       
   149                 else:
       
   150                     self.active_guards.append(guard)
       
   151         return self.active_guards
       
   152 
       
   153     def set_guards(self, idx, guards):
       
   154         for g in guards:
       
   155             if len(g) < 2:
       
   156                 raise util.Abort(_('guard %r too short') % g)
       
   157             if g[0] not in '-+':
       
   158                 raise util.Abort(_('guard %r starts with invalid char') % g)
       
   159             bad = self.check_guard(g[1:])
       
   160             if bad:
       
   161                 raise util.Abort(bad)
       
   162         drop = self.guard_re.sub('', self.full_series[idx])
       
   163         self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
       
   164         self.parse_series()
       
   165         self.series_dirty = True
       
   166         
       
   167     def pushable(self, idx):
       
   168         if isinstance(idx, str):
       
   169             idx = self.series.index(idx)
       
   170         patchguards = self.series_guards[idx]
       
   171         if not patchguards:
       
   172             return True, None
       
   173         default = False
       
   174         guards = self.active()
       
   175         exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
       
   176         if exactneg:
       
   177             return False, exactneg[0]
       
   178         pos = [g for g in patchguards if g[0] == '+']
       
   179         nonpos = [g for g in pos if g[1:] not in guards]
       
   180         if pos:
       
   181             if not nonpos:
       
   182                 return True, ''
       
   183             return False, nonpos
       
   184         return True, ''
       
   185 
       
   186     def explain_pushable(self, idx, all_patches=False):
       
   187         write = all_patches and self.ui.write or self.ui.warn
       
   188         if all_patches or self.ui.verbose:
       
   189             if isinstance(idx, str):
       
   190                 idx = self.series.index(idx)
       
   191             pushable, why = self.pushable(idx)
       
   192             if all_patches and pushable:
       
   193                 if why is None:
       
   194                     write(_('allowing %s - no guards in effect\n') %
       
   195                           self.series[idx])
       
   196                 else:
       
   197                     if not why:
       
   198                         write(_('allowing %s - no matching negative guards\n') %
       
   199                               self.series[idx])
       
   200                     else:
       
   201                         write(_('allowing %s - guarded by %r\n') %
       
   202                               (self.series[idx], why))
       
   203             if not pushable:
       
   204                 if why:
       
   205                     write(_('skipping %s - guarded by %r\n') %
       
   206                           (self.series[idx], ' '.join(why)))
       
   207                 else:
       
   208                     write(_('skipping %s - no matching guards\n') %
       
   209                           self.series[idx])
    95 
   210 
    96     def save_dirty(self):
   211     def save_dirty(self):
    97         def write_list(items, path):
   212         def write_list(items, path):
    98             fp = self.opener(path, 'w')
   213             fp = self.opener(path, 'w')
    99             for i in items:
   214             for i in items:
   100                 print >> fp, i
   215                 print >> fp, i
   101             fp.close()
   216             fp.close()
   102         if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
   217         if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
   103         if self.series_dirty: write_list(self.full_series, self.series_path)
   218         if self.series_dirty: write_list(self.full_series, self.series_path)
       
   219         if self.guards_dirty: write_list(self.active_guards, self.guards_path)
   104 
   220 
   105     def readheaders(self, patch):
   221     def readheaders(self, patch):
   106         def eatdiff(lines):
   222         def eatdiff(lines):
   107             while lines:
   223             while lines:
   108                 l = lines[-1]
   224                 l = lines[-1]
   118                 if re.match('\s*$', l):
   234                 if re.match('\s*$', l):
   119                     del lines[-1]
   235                     del lines[-1]
   120                 else:
   236                 else:
   121                     break
   237                     break
   122 
   238 
   123         pf = os.path.join(self.path, patch)
   239         pf = self.join(patch)
   124         message = []
   240         message = []
   125         comments = []
   241         comments = []
   126         user = None
   242         user = None
   127         date = None
   243         date = None
   128         format = None
   244         format = None
   241             # the first patch in the queue is never a merge patch
   357             # the first patch in the queue is never a merge patch
   242             #
   358             #
   243             pname = ".hg.patches.merge.marker"
   359             pname = ".hg.patches.merge.marker"
   244             n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
   360             n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
   245                             wlock=wlock)
   361                             wlock=wlock)
   246             self.applied.append(StatusEntry(revlog.hex(n), pname))
   362             self.applied.append(statusentry(revlog.hex(n), pname))
   247             self.applied_dirty = 1
   363             self.applied_dirty = 1
   248 
   364 
   249         head = self.qparents(repo)
   365         head = self.qparents(repo)
   250 
   366 
   251         for patch in series:
   367         for patch in series:
   252             patch = mergeq.lookup(patch, strict=True)
   368             patch = mergeq.lookup(patch, strict=True)
   253             if not patch:
   369             if not patch:
   254                 self.ui.warn("patch %s does not exist\n" % patch)
   370                 self.ui.warn("patch %s does not exist\n" % patch)
   255                 return (1, None)
   371                 return (1, None)
   256 
   372             pushable, reason = self.pushable(patch)
       
   373             if not pushable:
       
   374                 self.explain_pushable(patch, all_patches=True)
       
   375                 continue
   257             info = mergeq.isapplied(patch)
   376             info = mergeq.isapplied(patch)
   258             if not info:
   377             if not info:
   259                 self.ui.warn("patch %s is not applied\n" % patch)
   378                 self.ui.warn("patch %s is not applied\n" % patch)
   260                 return (1, None)
   379                 return (1, None)
   261             rev = revlog.bin(info[1])
   380             rev = revlog.bin(info[1])
   262             (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
   381             (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
   263             if head:
   382             if head:
   264                 self.applied.append(StatusEntry(revlog.hex(head), patch))
   383                 self.applied.append(statusentry(revlog.hex(head), patch))
   265                 self.applied_dirty = 1
   384                 self.applied_dirty = 1
   266             if err:
   385             if err:
   267                 return (err, head)
   386                 return (err, head)
   268         return (0, head)
   387         return (0, head)
   269 
   388 
   315             wlock = repo.wlock()
   434             wlock = repo.wlock()
   316         lock = repo.lock()
   435         lock = repo.lock()
   317         tr = repo.transaction()
   436         tr = repo.transaction()
   318         n = None
   437         n = None
   319         for patch in series:
   438         for patch in series:
       
   439             pushable, reason = self.pushable(patch)
       
   440             if not pushable:
       
   441                 self.explain_pushable(patch, all_patches=True)
       
   442                 continue
   320             self.ui.warn("applying %s\n" % patch)
   443             self.ui.warn("applying %s\n" % patch)
   321             pf = os.path.join(patchdir, patch)
   444             pf = os.path.join(patchdir, patch)
   322 
   445 
   323             try:
   446             try:
   324                 message, comments, user, date, patchfound = self.readheaders(patch)
   447                 message, comments, user, date, patchfound = self.readheaders(patch)
   354 
   477 
   355             if n == None:
   478             if n == None:
   356                 raise util.Abort(_("repo commit failed"))
   479                 raise util.Abort(_("repo commit failed"))
   357 
   480 
   358             if update_status:
   481             if update_status:
   359                 self.applied.append(StatusEntry(revlog.hex(n), patch))
   482                 self.applied.append(statusentry(revlog.hex(n), patch))
   360 
   483 
   361             if patcherr:
   484             if patcherr:
   362                 if not patchfound:
   485                 if not patchfound:
   363                     self.ui.warn("patch %s is empty\n" % patch)
   486                     self.ui.warn("patch %s is empty\n" % patch)
   364                     err = 0
   487                     err = 0
   384         if force:
   507         if force:
   385             r = self.qrepo()
   508             r = self.qrepo()
   386             if r:
   509             if r:
   387                 r.remove([patch], True)
   510                 r.remove([patch], True)
   388             else:
   511             else:
   389                 os.unlink(os.path.join(self.path, patch))
   512                 os.unlink(self.join(patch))
   390         i = self.find_series(patch)
   513         i = self.find_series(patch)
   391         del self.full_series[i]
   514         del self.full_series[i]
   392         self.parse_series()
   515         self.parse_series()
   393         self.series_dirty = 1
   516         self.series_dirty = 1
   394 
   517 
   403     def check_localchanges(self, repo):
   526     def check_localchanges(self, repo):
   404         (c, a, r, d, u) = repo.changes(None, None)
   527         (c, a, r, d, u) = repo.changes(None, None)
   405         if c or a or d or r:
   528         if c or a or d or r:
   406             raise util.Abort(_("local changes found, refresh first"))
   529             raise util.Abort(_("local changes found, refresh first"))
   407     def new(self, repo, patch, msg=None, force=None):
   530     def new(self, repo, patch, msg=None, force=None):
   408         if os.path.exists(os.path.join(self.path, patch)):
   531         if os.path.exists(self.join(patch)):
   409             raise util.Abort(_('patch "%s" already exists') % patch)
   532             raise util.Abort(_('patch "%s" already exists') % patch)
   410         commitfiles = []
   533         commitfiles = []
   411         (c, a, r, d, u) = repo.changes(None, None)
   534         (c, a, r, d, u) = repo.changes(None, None)
   412         if c or a or d or r:
   535         if c or a or d or r:
   413             if not force:
   536             if not force:
   423             n = repo.commit(commitfiles,
   546             n = repo.commit(commitfiles,
   424                             "New patch: %s" % patch, force=True, wlock=wlock)
   547                             "New patch: %s" % patch, force=True, wlock=wlock)
   425         if n == None:
   548         if n == None:
   426             raise util.Abort(_("repo commit failed"))
   549             raise util.Abort(_("repo commit failed"))
   427         self.full_series[insert:insert] = [patch]
   550         self.full_series[insert:insert] = [patch]
   428         self.applied.append(StatusEntry(revlog.hex(n), patch))
   551         self.applied.append(statusentry(revlog.hex(n), patch))
   429         self.parse_series()
   552         self.parse_series()
   430         self.series_dirty = 1
   553         self.series_dirty = 1
   431         self.applied_dirty = 1
   554         self.applied_dirty = 1
   432         p = self.opener(patch, "w")
   555         p = self.opener(patch, "w")
   433         if msg:
   556         if msg:
   626         # sure the file name passed in does not exist (checked below)
   749         # sure the file name passed in does not exist (checked below)
   627         res = partial_name(patch)
   750         res = partial_name(patch)
   628         if res and res == patch:
   751         if res and res == patch:
   629             return res
   752             return res
   630 
   753 
   631         if not os.path.isfile(os.path.join(self.path, patch)):
   754         if not os.path.isfile(self.join(patch)):
   632             try:
   755             try:
   633                 sno = int(patch)
   756                 sno = int(patch)
   634             except(ValueError, OverflowError):
   757             except(ValueError, OverflowError):
   635                 pass
   758                 pass
   636             else:
   759             else:
   637                 if sno < len(self.series):
   760                 if sno < len(self.series):
   638                     patch = self.series[sno]
   761                     return self.series[sno]
   639                     return patch
       
   640             if not strict:
   762             if not strict:
   641                 # return any partial match made above
   763                 # return any partial match made above
   642                 if res:
   764                 if res:
   643                     return res
   765                     return res
   644                 minus = patch.rsplit('-', 1)
   766                 minus = patch.rsplit('-', 1)
   898             else:
  1020             else:
   899                 message = msg
  1021                 message = msg
   900 
  1022 
   901             self.strip(repo, top, update=False, backup='strip', wlock=wlock)
  1023             self.strip(repo, top, update=False, backup='strip', wlock=wlock)
   902             n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
  1024             n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
   903             self.applied[-1] = StatusEntry(revlog.hex(n), patch)
  1025             self.applied[-1] = statusentry(revlog.hex(n), patch)
   904             self.applied_dirty = 1
  1026             self.applied_dirty = 1
   905         else:
  1027         else:
   906             commands.dodiff(patchf, self.ui, repo, patchparent, None)
  1028             commands.dodiff(patchf, self.ui, repo, patchparent, None)
   907             patchf.close()
  1029             patchf.close()
   908             self.pop(repo, force=True, wlock=wlock)
  1030             self.pop(repo, force=True, wlock=wlock)
   920             raise util.Abort(_("patch %s is not in series file") % patch)
  1042             raise util.Abort(_("patch %s is not in series file") % patch)
   921         if not patch:
  1043         if not patch:
   922             start = self.series_end()
  1044             start = self.series_end()
   923         else:
  1045         else:
   924             start = self.series.index(patch) + 1
  1046             start = self.series.index(patch) + 1
   925         return [(i, self.series[i]) for i in xrange(start, len(self.series))]
  1047         unapplied = []
       
  1048         for i in xrange(start, len(self.series)):
       
  1049             pushable, reason = self.pushable(i)
       
  1050             if pushable:
       
  1051                 unapplied.append((i, self.series[i]))
       
  1052             self.explain_pushable(i)
       
  1053         return unapplied
   926 
  1054 
   927     def qseries(self, repo, missing=None, summary=False):
  1055     def qseries(self, repo, missing=None, summary=False):
   928         start = self.series_end()
  1056         start = self.series_end(all_patches=True)
   929         if not missing:
  1057         if not missing:
   930             for i in range(len(self.series)):
  1058             for i in range(len(self.series)):
   931                 patch = self.series[i]
  1059                 patch = self.series[i]
   932                 if self.ui.verbose:
  1060                 if self.ui.verbose:
   933                     if i < start:
  1061                     if i < start:
   934                         status = 'A'
  1062                         status = 'A'
       
  1063                     elif self.pushable(i)[0]:
       
  1064                         status = 'U'
   935                     else:
  1065                     else:
   936                         status = 'U'
  1066                         status = 'G'
   937                     self.ui.write('%d %s ' % (i, status))
  1067                     self.ui.write('%d %s ' % (i, status))
   938                 if summary:
  1068                 if summary:
   939                     msg = self.readheaders(patch)[0]
  1069                     msg = self.readheaders(patch)[0]
   940                     msg = msg and ': ' + msg[0] or ': '
  1070                     msg = msg and ': ' + msg[0] or ': '
   941                 else:
  1071                 else:
   956                 if self.ui.verbose:
  1086                 if self.ui.verbose:
   957                     self.ui.write("D ")
  1087                     self.ui.write("D ")
   958                 self.ui.write("%s\n" % x)
  1088                 self.ui.write("%s\n" % x)
   959 
  1089 
   960     def issaveline(self, l):
  1090     def issaveline(self, l):
   961         name = l.split(':')[1]
  1091         if l.name == '.hg.patches.save.line':
   962         if name == '.hg.patches.save.line':
       
   963             return True
  1092             return True
   964 
  1093 
   965     def qrepo(self, create=False):
  1094     def qrepo(self, create=False):
   966         if create or os.path.isdir(os.path.join(self.path, ".hg")):
  1095         if create or os.path.isdir(self.join(".hg")):
   967             return hg.repository(self.ui, path=self.path, create=create)
  1096             return hg.repository(self.ui, path=self.path, create=create)
   968 
  1097 
   969     def restore(self, repo, rev, delete=None, qupdate=None):
  1098     def restore(self, repo, rev, delete=None, qupdate=None):
   970         c = repo.changelog.read(rev)
  1099         c = repo.changelog.read(rev)
   971         desc = c[4].strip()
  1100         desc = c[4].strip()
   982                 l = lines[i].rstrip()
  1111                 l = lines[i].rstrip()
   983                 l = l[10:].split(' ')
  1112                 l = l[10:].split(' ')
   984                 qpp = [ hg.bin(x) for x in l ]
  1113                 qpp = [ hg.bin(x) for x in l ]
   985             elif datastart != None:
  1114             elif datastart != None:
   986                 l = lines[i].rstrip()
  1115                 l = lines[i].rstrip()
   987                 se = StatusEntry(l)
  1116                 se = statusentry(l)
   988                 file_ = se.name
  1117                 file_ = se.name
   989                 if se.rev:
  1118                 if se.rev:
   990                     applied.append(se)
  1119                     applied.append(se)
   991                 series.append(file_)
  1120                 series.append(file_)
   992         if datastart == None:
  1121         if datastart == None:
  1037         r = self.qrepo()
  1166         r = self.qrepo()
  1038         if r:
  1167         if r:
  1039             pp = r.dirstate.parents()
  1168             pp = r.dirstate.parents()
  1040             msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
  1169             msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
  1041         msg += "\n\nPatch Data:\n"
  1170         msg += "\n\nPatch Data:\n"
  1042         text = msg + "\n".join(str(self.applied)) + '\n' + (ar and "\n".join(ar)
  1171         text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
  1043                                                        + '\n' or "")
  1172                    "\n".join(ar) + '\n' or "")
  1044         n = repo.commit(None, text, user=None, force=1)
  1173         n = repo.commit(None, text, user=None, force=1)
  1045         if not n:
  1174         if not n:
  1046             self.ui.warn("repo commit failed\n")
  1175             self.ui.warn("repo commit failed\n")
  1047             return 1
  1176             return 1
  1048         self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line'))
  1177         self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
  1049         self.applied_dirty = 1
  1178         self.applied_dirty = 1
  1050 
  1179 
  1051     def full_series_end(self):
  1180     def full_series_end(self):
  1052         if len(self.applied) > 0:
  1181         if len(self.applied) > 0:
  1053             p = self.applied[-1].name
  1182             p = self.applied[-1].name
  1055             if end == None:
  1184             if end == None:
  1056                 return len(self.full_series)
  1185                 return len(self.full_series)
  1057             return end + 1
  1186             return end + 1
  1058         return 0
  1187         return 0
  1059 
  1188 
  1060     def series_end(self):
  1189     def series_end(self, all_patches=False):
  1061         end = 0
  1190         end = 0
       
  1191         def next(start):
       
  1192             if all_patches:
       
  1193                 return start
       
  1194             i = start
       
  1195             while i < len(self.series):
       
  1196                 p, reason = self.pushable(i)
       
  1197                 if p:
       
  1198                     break
       
  1199                 self.explain_pushable(i)
       
  1200                 i += 1
       
  1201             return i
  1062         if len(self.applied) > 0:
  1202         if len(self.applied) > 0:
  1063             p = self.applied[-1].name
  1203             p = self.applied[-1].name
  1064             try:
  1204             try:
  1065                 end = self.series.index(p)
  1205                 end = self.series.index(p)
  1066             except ValueError:
  1206             except ValueError:
  1067                 return 0
  1207                 return 0
  1068             return end + 1
  1208             return next(end + 1)
  1069         return end
  1209         return next(end)
  1070 
  1210 
  1071     def qapplied(self, repo, patch=None):
  1211     def qapplied(self, repo, patch=None):
  1072         if patch and patch not in self.series:
  1212         if patch and patch not in self.series:
  1073             raise util.Abort(_("patch %s is not in series file") % patch)
  1213             raise util.Abort(_("patch %s is not in series file") % patch)
  1074         if not patch:
  1214         if not patch:
  1121         added = []
  1261         added = []
  1122         for filename in files:
  1262         for filename in files:
  1123             if existing:
  1263             if existing:
  1124                 if not patch:
  1264                 if not patch:
  1125                     patch = filename
  1265                     patch = filename
  1126                 if not os.path.isfile(os.path.join(self.path, patch)):
  1266                 if not os.path.isfile(self.join(patch)):
  1127                     raise util.Abort(_("patch %s does not exist") % patch)
  1267                     raise util.Abort(_("patch %s does not exist") % patch)
  1128             else:
  1268             else:
  1129                 try:
  1269                 try:
  1130                     text = file(filename).read()
  1270                     text = file(filename).read()
  1131                 except IOError:
  1271                 except IOError:
  1132                     raise util.Abort(_("unable to read %s") % patch)
  1272                     raise util.Abort(_("unable to read %s") % patch)
  1133                 if not patch:
  1273                 if not patch:
  1134                     patch = os.path.split(filename)[1]
  1274                     patch = os.path.split(filename)[1]
  1135                 if not force and os.path.exists(os.path.join(self.path, patch)):
  1275                 if not force and os.path.exists(self.join(patch)):
  1136                     raise util.Abort(_('patch "%s" already exists') % patch)
  1276                     raise util.Abort(_('patch "%s" already exists') % patch)
  1137                 patchf = self.opener(patch, "w")
  1277                 patchf = self.opener(patch, "w")
  1138                 patchf.write(text)
  1278                 patchf.write(text)
  1139             if patch in self.series:
  1279             if patch in self.series:
  1140                 raise util.Abort(_('patch %s is already in the series file')
  1280                 raise util.Abort(_('patch %s is already in the series file')
  1345         patches.append(patch)
  1485         patches.append(patch)
  1346 
  1486 
  1347     for patch in patches:
  1487     for patch in patches:
  1348         if not message:
  1488         if not message:
  1349             messages.append(q.readheaders(patch)[0])
  1489             messages.append(q.readheaders(patch)[0])
  1350         pf = os.path.join(q.path, patch)
  1490         pf = q.join(patch)
  1351         (patchsuccess, files, fuzz) = q.patch(repo, pf)
  1491         (patchsuccess, files, fuzz) = q.patch(repo, pf)
  1352         if not patchsuccess:
  1492         if not patchsuccess:
  1353             raise util.Abort(_('Error folding patch %s') % patch)
  1493             raise util.Abort(_('Error folding patch %s') % patch)
  1354 
  1494 
  1355     if not message:
  1495     if not message:
  1366 
  1506 
  1367     for patch in patches:
  1507     for patch in patches:
  1368         q.delete(repo, patch, force=opts['force'])
  1508         q.delete(repo, patch, force=opts['force'])
  1369 
  1509 
  1370     q.save_dirty()
  1510     q.save_dirty()
       
  1511 
       
  1512 def guard(ui, repo, *args, **opts):
       
  1513     '''set or print guards for a patch
       
  1514 
       
  1515     guards control whether a patch can be pushed.  a patch with no
       
  1516     guards is aways pushed.  a patch with posative guard ("+foo") is
       
  1517     pushed only if qselect command enables guard "foo".  a patch with
       
  1518     nagative guard ("-foo") is never pushed if qselect command enables
       
  1519     guard "foo".
       
  1520 
       
  1521     with no arguments, default is to print current active guards.
       
  1522     with arguments, set active guards for patch.
       
  1523 
       
  1524     to set nagative guard "-foo" on topmost patch ("--" is needed so
       
  1525     hg will not interpret "-foo" as argument):
       
  1526       hg qguard -- -foo
       
  1527 
       
  1528     to set guards on other patch:
       
  1529       hg qguard other.patch +2.6.17 -stable    
       
  1530     '''
       
  1531     def status(idx):
       
  1532         guards = q.series_guards[idx] or ['unguarded']
       
  1533         ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
       
  1534     q = repo.mq
       
  1535     patch = None
       
  1536     args = list(args)
       
  1537     if opts['list']:
       
  1538         if args or opts['none']:
       
  1539             raise util.Abort(_('cannot mix -l/--list with options or arguments'))
       
  1540         for i in xrange(len(q.series)):
       
  1541             status(i)
       
  1542         return
       
  1543     if not args or args[0][0:1] in '-+':
       
  1544         if not q.applied:
       
  1545             raise util.Abort(_('no patches applied'))
       
  1546         patch = q.applied[-1].name
       
  1547     if patch is None and args[0][0:1] not in '-+':
       
  1548         patch = args.pop(0)
       
  1549     if patch is None:
       
  1550         raise util.Abort(_('no patch to work with'))
       
  1551     if args or opts['none']:
       
  1552         q.set_guards(q.find_series(patch), args)
       
  1553         q.save_dirty()
       
  1554     else:
       
  1555         status(q.series.index(q.lookup(patch)))
  1371 
  1556 
  1372 def header(ui, repo, patch=None):
  1557 def header(ui, repo, patch=None):
  1373     """Print the header of the topmost or specified patch"""
  1558     """Print the header of the topmost or specified patch"""
  1374     q = repo.mq
  1559     q = repo.mq
  1375 
  1560 
  1456         patch = None
  1641         patch = None
  1457 
  1642 
  1458     if name in q.series:
  1643     if name in q.series:
  1459         raise util.Abort(_('A patch named %s already exists in the series file') % name)
  1644         raise util.Abort(_('A patch named %s already exists in the series file') % name)
  1460 
  1645 
  1461     absdest = os.path.join(q.path, name)
  1646     absdest = q.join(name)
  1462     if os.path.exists(absdest):
  1647     if os.path.exists(absdest):
  1463         raise util.Abort(_('%s already exists') % absdest)
  1648         raise util.Abort(_('%s already exists') % absdest)
  1464     
  1649     
  1465     if patch:
  1650     if patch:
  1466         patch = q.lookup(patch)
  1651         patch = q.lookup(patch)
  1477     q.parse_series()
  1662     q.parse_series()
  1478     q.series_dirty = 1
  1663     q.series_dirty = 1
  1479 
  1664 
  1480     info = q.isapplied(patch)
  1665     info = q.isapplied(patch)
  1481     if info:
  1666     if info:
  1482         q.applied[info[0]] = StatusEntry(info[1], name)
  1667         q.applied[info[0]] = statusentry(info[1], name)
  1483     q.applied_dirty = 1
  1668     q.applied_dirty = 1
  1484 
  1669 
  1485     util.rename(os.path.join(q.path, patch), absdest)
  1670     util.rename(q.join(patch), absdest)
  1486     r = q.qrepo()
  1671     r = q.qrepo()
  1487     if r:
  1672     if r:
  1488         wlock = r.wlock()
  1673         wlock = r.wlock()
  1489         if r.dirstate.state(name) == 'r':
  1674         if r.dirstate.state(name) == 'r':
  1490             r.undelete([name], wlock)
  1675             r.undelete([name], wlock)
  1525             newpath = savename(path)
  1710             newpath = savename(path)
  1526         ui.warn("copy %s to %s\n" % (path, newpath))
  1711         ui.warn("copy %s to %s\n" % (path, newpath))
  1527         util.copyfiles(path, newpath)
  1712         util.copyfiles(path, newpath)
  1528     if opts['empty']:
  1713     if opts['empty']:
  1529         try:
  1714         try:
  1530             os.unlink(os.path.join(q.path, q.status_path))
  1715             os.unlink(q.join(q.status_path))
  1531         except:
  1716         except:
  1532             pass
  1717             pass
  1533     return 0
  1718     return 0
  1534 
  1719 
  1535 def strip(ui, repo, rev, **opts):
  1720 def strip(ui, repo, rev, **opts):
  1541     elif opts['nobackup']:
  1726     elif opts['nobackup']:
  1542         backup = 'none'
  1727         backup = 'none'
  1543     repo.mq.strip(repo, rev, backup=backup)
  1728     repo.mq.strip(repo, rev, backup=backup)
  1544     return 0
  1729     return 0
  1545 
  1730 
  1546 def version(ui, q=None):
  1731 def select(ui, repo, *args, **opts):
  1547     """print the version number of the mq extension"""
  1732     '''set or print guarded patches to push
  1548     ui.write("mq version %s\n" % versionstr)
  1733 
  1549     return 0
  1734     use qguard command to set or print guards on patch.  then use
       
  1735     qselect to tell mq which guards to use.  example:
       
  1736 
       
  1737         qguard foo.patch -stable    (nagative guard)
       
  1738         qguard bar.patch +stable    (posative guard)
       
  1739         qselect stable
       
  1740 
       
  1741     this sets "stable" guard.  mq will skip foo.patch (because it has
       
  1742     nagative match) but push bar.patch (because it has posative
       
  1743     match).  patch is pushed only if all posative guards match and no
       
  1744     nagative guards match.
       
  1745 
       
  1746     with no arguments, default is to print current active guards.
       
  1747     with arguments, set active guards as given.
       
  1748     
       
  1749     use -n/--none to deactivate guards (no other arguments needed).
       
  1750     when no guards active, patches with posative guards are skipped,
       
  1751     patches with nagative guards are pushed.
       
  1752 
       
  1753     use -s/--series to print list of all guards in series file (no
       
  1754     other arguments needed).  use -v for more information.'''
       
  1755 
       
  1756     q = repo.mq
       
  1757     guards = q.active()
       
  1758     if args or opts['none']:
       
  1759         q.set_active(args)
       
  1760         q.save_dirty()
       
  1761         if not args:
       
  1762             ui.status(_('guards deactivated\n'))
       
  1763         if q.series:
       
  1764             ui.status(_('%d of %d unapplied patches active\n') %
       
  1765                       (len(q.unapplied(repo)), len(q.series)))
       
  1766     elif opts['series']:
       
  1767         guards = {}
       
  1768         noguards = 0
       
  1769         for gs in q.series_guards:
       
  1770             if not gs:
       
  1771                 noguards += 1
       
  1772             for g in gs:
       
  1773                 guards.setdefault(g, 0)
       
  1774                 guards[g] += 1
       
  1775         if ui.verbose:
       
  1776             guards['NONE'] = noguards
       
  1777         guards = guards.items()
       
  1778         guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
       
  1779         if guards:
       
  1780             ui.note(_('guards in series file:\n'))
       
  1781             for guard, count in guards:
       
  1782                 ui.note('%2d  ' % count)
       
  1783                 ui.write(guard, '\n')
       
  1784         else:
       
  1785             ui.note(_('no guards in series file\n'))
       
  1786     else:
       
  1787         if guards:
       
  1788             ui.note(_('active guards:\n'))
       
  1789             for g in guards:
       
  1790                 ui.write(g, '\n')
       
  1791         else:
       
  1792             ui.write(_('no active guards\n'))
  1550 
  1793 
  1551 def reposetup(ui, repo):
  1794 def reposetup(ui, repo):
  1552     class MqRepo(repo.__class__):
  1795     class mqrepo(repo.__class__):
  1553         def tags(self):
  1796         def tags(self):
  1554             if self.tagscache:
  1797             if self.tagscache:
  1555                 return self.tagscache
  1798                 return self.tagscache
  1556 
  1799 
  1557             tagscache = super(MqRepo, self).tags()
  1800             tagscache = super(mqrepo, self).tags()
  1558 
  1801 
  1559             q = self.mq
  1802             q = self.mq
  1560             if not q.applied:
  1803             if not q.applied:
  1561                 return tagscache
  1804                 return tagscache
  1562 
  1805 
  1569                 else:
  1812                 else:
  1570                     tagscache[patch[1]] = revlog.bin(patch[0])
  1813                     tagscache[patch[1]] = revlog.bin(patch[0])
  1571 
  1814 
  1572             return tagscache
  1815             return tagscache
  1573 
  1816 
  1574     repo.__class__ = MqRepo
  1817     repo.__class__ = mqrepo
  1575     repo.mq = queue(ui, repo.join(""))
  1818     repo.mq = queue(ui, repo.join(""))
  1576 
  1819 
  1577 cmdtable = {
  1820 cmdtable = {
  1578     "qapplied": (applied, [], 'hg qapplied [PATCH]'),
  1821     "qapplied": (applied, [], 'hg qapplied [PATCH]'),
  1579     "qclone": (clone,
  1822     "qclone": (clone,
  1600          [('e', 'edit', None, _('edit patch header')),
  1843          [('e', 'edit', None, _('edit patch header')),
  1601           ('f', 'force', None, _('delete folded patch files')),
  1844           ('f', 'force', None, _('delete folded patch files')),
  1602           ('m', 'message', '', _('set patch header to <text>')),
  1845           ('m', 'message', '', _('set patch header to <text>')),
  1603           ('l', 'logfile', '', _('set patch header to contents of <file>'))],
  1846           ('l', 'logfile', '', _('set patch header to contents of <file>'))],
  1604          'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
  1847          'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
       
  1848     'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
       
  1849                        ('n', 'none', None, _('drop all guards'))],
       
  1850                'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
  1605     'qheader': (header, [],
  1851     'qheader': (header, [],
  1606                 _('hg qheader [PATCH]')),
  1852                 _('hg qheader [PATCH]')),
  1607     "^qimport":
  1853     "^qimport":
  1608         (qimport,
  1854         (qimport,
  1609          [('e', 'existing', None, 'import file in patch dir'),
  1855          [('e', 'existing', None, 'import file in patch dir'),
  1657           ('c', 'copy', None, 'copy patch directory'),
  1903           ('c', 'copy', None, 'copy patch directory'),
  1658           ('n', 'name', '', 'copy directory name'),
  1904           ('n', 'name', '', 'copy directory name'),
  1659           ('e', 'empty', None, 'clear queue status file'),
  1905           ('e', 'empty', None, 'clear queue status file'),
  1660           ('f', 'force', None, 'force copy')],
  1906           ('f', 'force', None, 'force copy')],
  1661          'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
  1907          'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
       
  1908     "qselect": (select,
       
  1909                 [('n', 'none', None, _('disable all guards')),
       
  1910                  ('s', 'series', None, _('list all guards in series file'))],
       
  1911                 'hg qselect [GUARDS]'),
  1662     "qseries":
  1912     "qseries":
  1663         (series,
  1913         (series,
  1664          [('m', 'missing', None, 'print patches not in series'),
  1914          [('m', 'missing', None, 'print patches not in series'),
  1665           ('s', 'summary', None, _('print first line of patch header'))],
  1915           ('s', 'summary', None, _('print first line of patch header'))],
  1666          'hg qseries [-m]'),
  1916          'hg qseries [-m]'),
  1670           ('b', 'backup', None, 'bundle unrelated changesets'),
  1920           ('b', 'backup', None, 'bundle unrelated changesets'),
  1671           ('n', 'nobackup', None, 'no backups')],
  1921           ('n', 'nobackup', None, 'no backups')],
  1672          'hg strip [-f] [-b] [-n] REV'),
  1922          'hg strip [-f] [-b] [-n] REV'),
  1673     "qtop": (top, [], 'hg qtop'),
  1923     "qtop": (top, [], 'hg qtop'),
  1674     "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
  1924     "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
  1675     "qversion": (version, [], 'hg qversion')
       
  1676 }
  1925 }
  1677 
  1926