hgext/mq.py
changeset 8653 aa011d123f71
parent 8632 9e055cfdd620
child 8654 f6cc3638f468
equal deleted inserted replaced
8652:64614c7e4bd9 8653:aa011d123f71
    55 
    55 
    56     def __str__(self):
    56     def __str__(self):
    57         return self.rev + ':' + self.name
    57         return self.rev + ':' + self.name
    58 
    58 
    59 class patchheader(object):
    59 class patchheader(object):
    60     def __init__(self, message, comments, user, date, haspatch):
    60     def __init__(self, pf):
    61         self.message = message
       
    62         self.comments = comments
       
    63         self.user = user
       
    64         self.date = date
       
    65         self.haspatch = haspatch
       
    66 
       
    67     def setuser(self, user):
       
    68         if not self.setheader(['From: ', '# User '], user):
       
    69             try:
       
    70                 patchheaderat = self.comments.index('# HG changeset patch')
       
    71                 self.comments.insert(patchheaderat + 1,'# User ' + user)
       
    72             except ValueError:
       
    73                 self.comments = ['From: ' + user, ''] + self.comments
       
    74         self.user = user
       
    75 
       
    76     def setdate(self, date):
       
    77         if self.setheader(['# Date '], date):
       
    78             self.date = date
       
    79 
       
    80     def setmessage(self, message):
       
    81         if self.comments:
       
    82             self._delmsg()
       
    83         self.message = [message]
       
    84         self.comments += self.message
       
    85 
       
    86     def setheader(self, prefixes, new):
       
    87         '''Update all references to a field in the patch header.
       
    88         If none found, add it email style.'''
       
    89         res = False
       
    90         for prefix in prefixes:
       
    91             for i in xrange(len(self.comments)):
       
    92                 if self.comments[i].startswith(prefix):
       
    93                     self.comments[i] = prefix + new
       
    94                     res = True
       
    95                     break
       
    96         return res
       
    97 
       
    98     def __str__(self):
       
    99         if not self.comments:
       
   100             return ''
       
   101         return '\n'.join(self.comments) + '\n\n'
       
   102 
       
   103     def _delmsg(self):
       
   104         '''Remove existing message, keeping the rest of the comments fields.
       
   105         If comments contains 'subject: ', message will prepend
       
   106         the field and a blank line.'''
       
   107         if self.message:
       
   108             subj = 'subject: ' + self.message[0].lower()
       
   109             for i in xrange(len(self.comments)):
       
   110                 if subj == self.comments[i].lower():
       
   111                     del self.comments[i]
       
   112                     self.message = self.message[2:]
       
   113                     break
       
   114         ci = 0
       
   115         for mi in self.message:
       
   116             while mi != self.comments[ci]:
       
   117                 ci += 1
       
   118             del self.comments[ci]
       
   119 
       
   120 class queue:
       
   121     def __init__(self, ui, path, patchdir=None):
       
   122         self.basepath = path
       
   123         self.path = patchdir or os.path.join(path, "patches")
       
   124         self.opener = util.opener(self.path)
       
   125         self.ui = ui
       
   126         self.applied_dirty = 0
       
   127         self.series_dirty = 0
       
   128         self.series_path = "series"
       
   129         self.status_path = "status"
       
   130         self.guards_path = "guards"
       
   131         self.active_guards = None
       
   132         self.guards_dirty = False
       
   133         self._diffopts = None
       
   134 
       
   135     @util.propertycache
       
   136     def applied(self):
       
   137         if os.path.exists(self.join(self.status_path)):
       
   138             lines = self.opener(self.status_path).read().splitlines()
       
   139             return [statusentry(l) for l in lines]
       
   140         return []
       
   141 
       
   142     @util.propertycache
       
   143     def full_series(self):
       
   144         if os.path.exists(self.join(self.series_path)):
       
   145             return self.opener(self.series_path).read().splitlines()
       
   146         return []
       
   147 
       
   148     @util.propertycache
       
   149     def series(self):
       
   150         self.parse_series()
       
   151         return self.series
       
   152 
       
   153     @util.propertycache
       
   154     def series_guards(self):
       
   155         self.parse_series()
       
   156         return self.series_guards
       
   157 
       
   158     def invalidate(self):
       
   159         for a in 'applied full_series series series_guards'.split():
       
   160             if a in self.__dict__:
       
   161                 delattr(self, a)
       
   162         self.applied_dirty = 0
       
   163         self.series_dirty = 0
       
   164         self.guards_dirty = False
       
   165         self.active_guards = None
       
   166 
       
   167     def diffopts(self):
       
   168         if self._diffopts is None:
       
   169             self._diffopts = patch.diffopts(self.ui)
       
   170         return self._diffopts
       
   171 
       
   172     def join(self, *p):
       
   173         return os.path.join(self.path, *p)
       
   174 
       
   175     def find_series(self, patch):
       
   176         pre = re.compile("(\s*)([^#]+)")
       
   177         index = 0
       
   178         for l in self.full_series:
       
   179             m = pre.match(l)
       
   180             if m:
       
   181                 s = m.group(2)
       
   182                 s = s.rstrip()
       
   183                 if s == patch:
       
   184                     return index
       
   185             index += 1
       
   186         return None
       
   187 
       
   188     guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
       
   189 
       
   190     def parse_series(self):
       
   191         self.series = []
       
   192         self.series_guards = []
       
   193         for l in self.full_series:
       
   194             h = l.find('#')
       
   195             if h == -1:
       
   196                 patch = l
       
   197                 comment = ''
       
   198             elif h == 0:
       
   199                 continue
       
   200             else:
       
   201                 patch = l[:h]
       
   202                 comment = l[h:]
       
   203             patch = patch.strip()
       
   204             if patch:
       
   205                 if patch in self.series:
       
   206                     raise util.Abort(_('%s appears more than once in %s') %
       
   207                                      (patch, self.join(self.series_path)))
       
   208                 self.series.append(patch)
       
   209                 self.series_guards.append(self.guard_re.findall(comment))
       
   210 
       
   211     def check_guard(self, guard):
       
   212         if not guard:
       
   213             return _('guard cannot be an empty string')
       
   214         bad_chars = '# \t\r\n\f'
       
   215         first = guard[0]
       
   216         if first in '-+':
       
   217             return (_('guard %r starts with invalid character: %r') %
       
   218                       (guard, first))
       
   219         for c in bad_chars:
       
   220             if c in guard:
       
   221                 return _('invalid character in guard %r: %r') % (guard, c)
       
   222 
       
   223     def set_active(self, guards):
       
   224         for guard in guards:
       
   225             bad = self.check_guard(guard)
       
   226             if bad:
       
   227                 raise util.Abort(bad)
       
   228         guards = sorted(set(guards))
       
   229         self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
       
   230         self.active_guards = guards
       
   231         self.guards_dirty = True
       
   232 
       
   233     def active(self):
       
   234         if self.active_guards is None:
       
   235             self.active_guards = []
       
   236             try:
       
   237                 guards = self.opener(self.guards_path).read().split()
       
   238             except IOError, err:
       
   239                 if err.errno != errno.ENOENT: raise
       
   240                 guards = []
       
   241             for i, guard in enumerate(guards):
       
   242                 bad = self.check_guard(guard)
       
   243                 if bad:
       
   244                     self.ui.warn('%s:%d: %s\n' %
       
   245                                  (self.join(self.guards_path), i + 1, bad))
       
   246                 else:
       
   247                     self.active_guards.append(guard)
       
   248         return self.active_guards
       
   249 
       
   250     def set_guards(self, idx, guards):
       
   251         for g in guards:
       
   252             if len(g) < 2:
       
   253                 raise util.Abort(_('guard %r too short') % g)
       
   254             if g[0] not in '-+':
       
   255                 raise util.Abort(_('guard %r starts with invalid char') % g)
       
   256             bad = self.check_guard(g[1:])
       
   257             if bad:
       
   258                 raise util.Abort(bad)
       
   259         drop = self.guard_re.sub('', self.full_series[idx])
       
   260         self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
       
   261         self.parse_series()
       
   262         self.series_dirty = True
       
   263 
       
   264     def pushable(self, idx):
       
   265         if isinstance(idx, str):
       
   266             idx = self.series.index(idx)
       
   267         patchguards = self.series_guards[idx]
       
   268         if not patchguards:
       
   269             return True, None
       
   270         guards = self.active()
       
   271         exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
       
   272         if exactneg:
       
   273             return False, exactneg[0]
       
   274         pos = [g for g in patchguards if g[0] == '+']
       
   275         exactpos = [g for g in pos if g[1:] in guards]
       
   276         if pos:
       
   277             if exactpos:
       
   278                 return True, exactpos[0]
       
   279             return False, pos
       
   280         return True, ''
       
   281 
       
   282     def explain_pushable(self, idx, all_patches=False):
       
   283         write = all_patches and self.ui.write or self.ui.warn
       
   284         if all_patches or self.ui.verbose:
       
   285             if isinstance(idx, str):
       
   286                 idx = self.series.index(idx)
       
   287             pushable, why = self.pushable(idx)
       
   288             if all_patches and pushable:
       
   289                 if why is None:
       
   290                     write(_('allowing %s - no guards in effect\n') %
       
   291                           self.series[idx])
       
   292                 else:
       
   293                     if not why:
       
   294                         write(_('allowing %s - no matching negative guards\n') %
       
   295                               self.series[idx])
       
   296                     else:
       
   297                         write(_('allowing %s - guarded by %r\n') %
       
   298                               (self.series[idx], why))
       
   299             if not pushable:
       
   300                 if why:
       
   301                     write(_('skipping %s - guarded by %r\n') %
       
   302                           (self.series[idx], why))
       
   303                 else:
       
   304                     write(_('skipping %s - no matching guards\n') %
       
   305                           self.series[idx])
       
   306 
       
   307     def save_dirty(self):
       
   308         def write_list(items, path):
       
   309             fp = self.opener(path, 'w')
       
   310             for i in items:
       
   311                 fp.write("%s\n" % i)
       
   312             fp.close()
       
   313         if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
       
   314         if self.series_dirty: write_list(self.full_series, self.series_path)
       
   315         if self.guards_dirty: write_list(self.active_guards, self.guards_path)
       
   316 
       
   317     def readheaders(self, patch):
       
   318         def eatdiff(lines):
    61         def eatdiff(lines):
   319             while lines:
    62             while lines:
   320                 l = lines[-1]
    63                 l = lines[-1]
   321                 if (l.startswith("diff -") or
    64                 if (l.startswith("diff -") or
   322                     l.startswith("Index:") or
    65                     l.startswith("Index:") or
   330                 if re.match('\s*$', l):
    73                 if re.match('\s*$', l):
   331                     del lines[-1]
    74                     del lines[-1]
   332                 else:
    75                 else:
   333                     break
    76                     break
   334 
    77 
   335         pf = self.join(patch)
       
   336         message = []
    78         message = []
   337         comments = []
    79         comments = []
   338         user = None
    80         user = None
   339         date = None
    81         date = None
   340         format = None
    82         format = None
   387 
   129 
   388         # make sure message isn't empty
   130         # make sure message isn't empty
   389         if format and format.startswith("tag") and subject:
   131         if format and format.startswith("tag") and subject:
   390             message.insert(0, "")
   132             message.insert(0, "")
   391             message.insert(0, subject)
   133             message.insert(0, subject)
   392         return patchheader(message, comments, user, date, diffstart > 1)
   134 
       
   135         self.message = message
       
   136         self.comments = comments
       
   137         self.user = user
       
   138         self.date = date
       
   139         self.haspatch = diffstart > 1
       
   140 
       
   141     def setuser(self, user):
       
   142         if not self.setheader(['From: ', '# User '], user):
       
   143             try:
       
   144                 patchheaderat = self.comments.index('# HG changeset patch')
       
   145                 self.comments.insert(patchheaderat + 1,'# User ' + user)
       
   146             except ValueError:
       
   147                 self.comments = ['From: ' + user, ''] + self.comments
       
   148         self.user = user
       
   149 
       
   150     def setdate(self, date):
       
   151         if self.setheader(['# Date '], date):
       
   152             self.date = date
       
   153 
       
   154     def setmessage(self, message):
       
   155         if self.comments:
       
   156             self._delmsg()
       
   157         self.message = [message]
       
   158         self.comments += self.message
       
   159 
       
   160     def setheader(self, prefixes, new):
       
   161         '''Update all references to a field in the patch header.
       
   162         If none found, add it email style.'''
       
   163         res = False
       
   164         for prefix in prefixes:
       
   165             for i in xrange(len(self.comments)):
       
   166                 if self.comments[i].startswith(prefix):
       
   167                     self.comments[i] = prefix + new
       
   168                     res = True
       
   169                     break
       
   170         return res
       
   171 
       
   172     def __str__(self):
       
   173         if not self.comments:
       
   174             return ''
       
   175         return '\n'.join(self.comments) + '\n\n'
       
   176 
       
   177     def _delmsg(self):
       
   178         '''Remove existing message, keeping the rest of the comments fields.
       
   179         If comments contains 'subject: ', message will prepend
       
   180         the field and a blank line.'''
       
   181         if self.message:
       
   182             subj = 'subject: ' + self.message[0].lower()
       
   183             for i in xrange(len(self.comments)):
       
   184                 if subj == self.comments[i].lower():
       
   185                     del self.comments[i]
       
   186                     self.message = self.message[2:]
       
   187                     break
       
   188         ci = 0
       
   189         for mi in self.message:
       
   190             while mi != self.comments[ci]:
       
   191                 ci += 1
       
   192             del self.comments[ci]
       
   193 
       
   194 class queue:
       
   195     def __init__(self, ui, path, patchdir=None):
       
   196         self.basepath = path
       
   197         self.path = patchdir or os.path.join(path, "patches")
       
   198         self.opener = util.opener(self.path)
       
   199         self.ui = ui
       
   200         self.applied_dirty = 0
       
   201         self.series_dirty = 0
       
   202         self.series_path = "series"
       
   203         self.status_path = "status"
       
   204         self.guards_path = "guards"
       
   205         self.active_guards = None
       
   206         self.guards_dirty = False
       
   207         self._diffopts = None
       
   208 
       
   209     @util.propertycache
       
   210     def applied(self):
       
   211         if os.path.exists(self.join(self.status_path)):
       
   212             lines = self.opener(self.status_path).read().splitlines()
       
   213             return [statusentry(l) for l in lines]
       
   214         return []
       
   215 
       
   216     @util.propertycache
       
   217     def full_series(self):
       
   218         if os.path.exists(self.join(self.series_path)):
       
   219             return self.opener(self.series_path).read().splitlines()
       
   220         return []
       
   221 
       
   222     @util.propertycache
       
   223     def series(self):
       
   224         self.parse_series()
       
   225         return self.series
       
   226 
       
   227     @util.propertycache
       
   228     def series_guards(self):
       
   229         self.parse_series()
       
   230         return self.series_guards
       
   231 
       
   232     def invalidate(self):
       
   233         for a in 'applied full_series series series_guards'.split():
       
   234             if a in self.__dict__:
       
   235                 delattr(self, a)
       
   236         self.applied_dirty = 0
       
   237         self.series_dirty = 0
       
   238         self.guards_dirty = False
       
   239         self.active_guards = None
       
   240 
       
   241     def diffopts(self):
       
   242         if self._diffopts is None:
       
   243             self._diffopts = patch.diffopts(self.ui)
       
   244         return self._diffopts
       
   245 
       
   246     def join(self, *p):
       
   247         return os.path.join(self.path, *p)
       
   248 
       
   249     def find_series(self, patch):
       
   250         pre = re.compile("(\s*)([^#]+)")
       
   251         index = 0
       
   252         for l in self.full_series:
       
   253             m = pre.match(l)
       
   254             if m:
       
   255                 s = m.group(2)
       
   256                 s = s.rstrip()
       
   257                 if s == patch:
       
   258                     return index
       
   259             index += 1
       
   260         return None
       
   261 
       
   262     guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
       
   263 
       
   264     def parse_series(self):
       
   265         self.series = []
       
   266         self.series_guards = []
       
   267         for l in self.full_series:
       
   268             h = l.find('#')
       
   269             if h == -1:
       
   270                 patch = l
       
   271                 comment = ''
       
   272             elif h == 0:
       
   273                 continue
       
   274             else:
       
   275                 patch = l[:h]
       
   276                 comment = l[h:]
       
   277             patch = patch.strip()
       
   278             if patch:
       
   279                 if patch in self.series:
       
   280                     raise util.Abort(_('%s appears more than once in %s') %
       
   281                                      (patch, self.join(self.series_path)))
       
   282                 self.series.append(patch)
       
   283                 self.series_guards.append(self.guard_re.findall(comment))
       
   284 
       
   285     def check_guard(self, guard):
       
   286         if not guard:
       
   287             return _('guard cannot be an empty string')
       
   288         bad_chars = '# \t\r\n\f'
       
   289         first = guard[0]
       
   290         if first in '-+':
       
   291             return (_('guard %r starts with invalid character: %r') %
       
   292                       (guard, first))
       
   293         for c in bad_chars:
       
   294             if c in guard:
       
   295                 return _('invalid character in guard %r: %r') % (guard, c)
       
   296 
       
   297     def set_active(self, guards):
       
   298         for guard in guards:
       
   299             bad = self.check_guard(guard)
       
   300             if bad:
       
   301                 raise util.Abort(bad)
       
   302         guards = sorted(set(guards))
       
   303         self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
       
   304         self.active_guards = guards
       
   305         self.guards_dirty = True
       
   306 
       
   307     def active(self):
       
   308         if self.active_guards is None:
       
   309             self.active_guards = []
       
   310             try:
       
   311                 guards = self.opener(self.guards_path).read().split()
       
   312             except IOError, err:
       
   313                 if err.errno != errno.ENOENT: raise
       
   314                 guards = []
       
   315             for i, guard in enumerate(guards):
       
   316                 bad = self.check_guard(guard)
       
   317                 if bad:
       
   318                     self.ui.warn('%s:%d: %s\n' %
       
   319                                  (self.join(self.guards_path), i + 1, bad))
       
   320                 else:
       
   321                     self.active_guards.append(guard)
       
   322         return self.active_guards
       
   323 
       
   324     def set_guards(self, idx, guards):
       
   325         for g in guards:
       
   326             if len(g) < 2:
       
   327                 raise util.Abort(_('guard %r too short') % g)
       
   328             if g[0] not in '-+':
       
   329                 raise util.Abort(_('guard %r starts with invalid char') % g)
       
   330             bad = self.check_guard(g[1:])
       
   331             if bad:
       
   332                 raise util.Abort(bad)
       
   333         drop = self.guard_re.sub('', self.full_series[idx])
       
   334         self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
       
   335         self.parse_series()
       
   336         self.series_dirty = True
       
   337 
       
   338     def pushable(self, idx):
       
   339         if isinstance(idx, str):
       
   340             idx = self.series.index(idx)
       
   341         patchguards = self.series_guards[idx]
       
   342         if not patchguards:
       
   343             return True, None
       
   344         guards = self.active()
       
   345         exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
       
   346         if exactneg:
       
   347             return False, exactneg[0]
       
   348         pos = [g for g in patchguards if g[0] == '+']
       
   349         exactpos = [g for g in pos if g[1:] in guards]
       
   350         if pos:
       
   351             if exactpos:
       
   352                 return True, exactpos[0]
       
   353             return False, pos
       
   354         return True, ''
       
   355 
       
   356     def explain_pushable(self, idx, all_patches=False):
       
   357         write = all_patches and self.ui.write or self.ui.warn
       
   358         if all_patches or self.ui.verbose:
       
   359             if isinstance(idx, str):
       
   360                 idx = self.series.index(idx)
       
   361             pushable, why = self.pushable(idx)
       
   362             if all_patches and pushable:
       
   363                 if why is None:
       
   364                     write(_('allowing %s - no guards in effect\n') %
       
   365                           self.series[idx])
       
   366                 else:
       
   367                     if not why:
       
   368                         write(_('allowing %s - no matching negative guards\n') %
       
   369                               self.series[idx])
       
   370                     else:
       
   371                         write(_('allowing %s - guarded by %r\n') %
       
   372                               (self.series[idx], why))
       
   373             if not pushable:
       
   374                 if why:
       
   375                     write(_('skipping %s - guarded by %r\n') %
       
   376                           (self.series[idx], why))
       
   377                 else:
       
   378                     write(_('skipping %s - no matching guards\n') %
       
   379                           self.series[idx])
       
   380 
       
   381     def save_dirty(self):
       
   382         def write_list(items, path):
       
   383             fp = self.opener(path, 'w')
       
   384             for i in items:
       
   385                 fp.write("%s\n" % i)
       
   386             fp.close()
       
   387         if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
       
   388         if self.series_dirty: write_list(self.full_series, self.series_path)
       
   389         if self.guards_dirty: write_list(self.active_guards, self.guards_path)
   393 
   390 
   394     def removeundo(self, repo):
   391     def removeundo(self, repo):
   395         undo = repo.sjoin('undo')
   392         undo = repo.sjoin('undo')
   396         if not os.path.exists(undo):
   393         if not os.path.exists(undo):
   397             return
   394             return
   431             raise util.Abort(_("update returned %d") % ret)
   428             raise util.Abort(_("update returned %d") % ret)
   432         n = repo.commit(None, ctx.description(), ctx.user(), force=1)
   429         n = repo.commit(None, ctx.description(), ctx.user(), force=1)
   433         if n is None:
   430         if n is None:
   434             raise util.Abort(_("repo commit failed"))
   431             raise util.Abort(_("repo commit failed"))
   435         try:
   432         try:
   436             ph = mergeq.readheaders(patch)
   433             ph = patchheader(mergeq.join(patch))
   437         except:
   434         except:
   438             raise util.Abort(_("unable to read %s") % patch)
   435             raise util.Abort(_("unable to read %s") % patch)
   439 
   436 
   440         patchf = self.opener(patch, "w")
   437         patchf = self.opener(patch, "w")
   441         comments = str(ph)
   438         comments = str(ph)
   558                 continue
   555                 continue
   559             self.ui.warn(_("applying %s\n") % patchname)
   556             self.ui.warn(_("applying %s\n") % patchname)
   560             pf = os.path.join(patchdir, patchname)
   557             pf = os.path.join(patchdir, patchname)
   561 
   558 
   562             try:
   559             try:
   563                 ph = self.readheaders(patchname)
   560                 ph = patchheader(self.join(patchname))
   564             except:
   561             except:
   565                 self.ui.warn(_("Unable to read %s\n") % patchname)
   562                 self.ui.warn(_("Unable to read %s\n") % patchname)
   566                 err = 1
   563                 err = 1
   567                 break
   564                 break
   568 
   565 
  1118             top = bin(top)
  1115             top = bin(top)
  1119             if repo.changelog.heads(top) != [top]:
  1116             if repo.changelog.heads(top) != [top]:
  1120                 raise util.Abort(_("cannot refresh a revision with children"))
  1117                 raise util.Abort(_("cannot refresh a revision with children"))
  1121             cparents = repo.changelog.parents(top)
  1118             cparents = repo.changelog.parents(top)
  1122             patchparent = self.qparents(repo, top)
  1119             patchparent = self.qparents(repo, top)
  1123             ph = self.readheaders(patchfn)
  1120             ph = patchheader(self.join(patchfn))
  1124 
  1121 
  1125             patchf = self.opener(patchfn, 'r')
  1122             patchf = self.opener(patchfn, 'r')
  1126 
  1123 
  1127             # if the patch was a git patch, refresh it as a git patch
  1124             # if the patch was a git patch, refresh it as a git patch
  1128             for line in patchf:
  1125             for line in patchf:
  1347 
  1344 
  1348     def qseries(self, repo, missing=None, start=0, length=None, status=None,
  1345     def qseries(self, repo, missing=None, start=0, length=None, status=None,
  1349                 summary=False):
  1346                 summary=False):
  1350         def displayname(patchname):
  1347         def displayname(patchname):
  1351             if summary:
  1348             if summary:
  1352                 ph = self.readheaders(patchname)
  1349                 ph = patchheader(self.join(patchname))
  1353                 msg = ph.message
  1350                 msg = ph.message
  1354                 msg = msg and ': ' + msg[0] or ': '
  1351                 msg = msg and ': ' + msg[0] or ': '
  1355             else:
  1352             else:
  1356                 msg = ''
  1353                 msg = ''
  1357             return '%s%s' % (patchname, msg)
  1354             return '%s%s' % (patchname, msg)
  1920             ui.write(_("no patches applied\n"))
  1917             ui.write(_("no patches applied\n"))
  1921             return 1
  1918             return 1
  1922         if message:
  1919         if message:
  1923             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
  1920             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
  1924         patch = q.applied[-1].name
  1921         patch = q.applied[-1].name
  1925         ph = q.readheaders(patch)
  1922         ph = patchheader(q.join(patch))
  1926         message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
  1923         message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
  1927     setupheaderopts(ui, opts)
  1924     setupheaderopts(ui, opts)
  1928     ret = q.refresh(repo, pats, msg=message, **opts)
  1925     ret = q.refresh(repo, pats, msg=message, **opts)
  1929     q.save_dirty()
  1926     q.save_dirty()
  1930     return ret
  1927     return ret
  1982             raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
  1979             raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
  1983         patches.append(p)
  1980         patches.append(p)
  1984 
  1981 
  1985     for p in patches:
  1982     for p in patches:
  1986         if not message:
  1983         if not message:
  1987             ph = q.readheaders(p)
  1984             ph = patchheader(q.join(p))
  1988             if ph.message:
  1985             if ph.message:
  1989                 messages.append(ph.message)
  1986                 messages.append(ph.message)
  1990         pf = q.join(p)
  1987         pf = q.join(p)
  1991         (patchsuccess, files, fuzz) = q.patch(repo, pf)
  1988         (patchsuccess, files, fuzz) = q.patch(repo, pf)
  1992         if not patchsuccess:
  1989         if not patchsuccess:
  1993             raise util.Abort(_('Error folding patch %s') % p)
  1990             raise util.Abort(_('Error folding patch %s') % p)
  1994         patch.updatedir(ui, repo, files)
  1991         patch.updatedir(ui, repo, files)
  1995 
  1992 
  1996     if not message:
  1993     if not message:
  1997         ph = q.readheaders(parent)
  1994         ph = patchheader(q.join(parent))
  1998         message, user = ph.message, ph.user
  1995         message, user = ph.message, ph.user
  1999         for msg in messages:
  1996         for msg in messages:
  2000             message.append('* * *')
  1997             message.append('* * *')
  2001             message.extend(msg)
  1998             message.extend(msg)
  2002         message = '\n'.join(message)
  1999         message = '\n'.join(message)
  2073     else:
  2070     else:
  2074         if not q.applied:
  2071         if not q.applied:
  2075             ui.write('no patches applied\n')
  2072             ui.write('no patches applied\n')
  2076             return 1
  2073             return 1
  2077         patch = q.lookup('qtip')
  2074         patch = q.lookup('qtip')
  2078     ph = repo.mq.readheaders(patch)
  2075     ph = patchheader(repo.mq.join(patch))
  2079 
  2076 
  2080     ui.write('\n'.join(ph.message) + '\n')
  2077     ui.write('\n'.join(ph.message) + '\n')
  2081 
  2078 
  2082 def lastsavename(path):
  2079 def lastsavename(path):
  2083     (directory, base) = os.path.split(path)
  2080     (directory, base) = os.path.split(path)