hgext/mq.py
changeset 2803 987c31e2a08c
parent 2775 b550cd82f92a
parent 2797 a3c6e7888abf
child 2804 4b20daa25f15
equal deleted inserted replaced
2778:fdc232d8a193 2803:987c31e2a08c
    36 from mercurial import ui, hg, revlog, commands, util
    36 from mercurial import ui, hg, revlog, commands, util
    37 
    37 
    38 versionstr = "0.45"
    38 versionstr = "0.45"
    39 
    39 
    40 commands.norepo += " qclone qversion"
    40 commands.norepo += " qclone qversion"
       
    41 
       
    42 class StatusEntry:
       
    43     def __init__(self, rev, name=None):
       
    44         if not name:
       
    45             self.rev, self.name = rev.split(':')
       
    46         else:
       
    47             self.rev, self.name = rev, name
       
    48 
       
    49     def __str__(self):
       
    50         return self.rev + ':' + self.name
    41 
    51 
    42 class queue:
    52 class queue:
    43     def __init__(self, ui, path, patchdir=None):
    53     def __init__(self, ui, path, patchdir=None):
    44         self.basepath = path
    54         self.basepath = path
    45         if patchdir:
    55         if patchdir:
    58         if os.path.exists(os.path.join(self.path, self.series_path)):
    68         if os.path.exists(os.path.join(self.path, self.series_path)):
    59             self.full_series = self.opener(self.series_path).read().splitlines()
    69             self.full_series = self.opener(self.series_path).read().splitlines()
    60         self.parse_series()
    70         self.parse_series()
    61 
    71 
    62         if os.path.exists(os.path.join(self.path, self.status_path)):
    72         if os.path.exists(os.path.join(self.path, self.status_path)):
    63             self.applied = self.opener(self.status_path).read().splitlines()
    73             self.applied = [StatusEntry(l)
       
    74                             for l in self.opener(self.status_path).read().splitlines()]
    64 
    75 
    65     def find_series(self, patch):
    76     def find_series(self, patch):
    66         pre = re.compile("(\s*)([^#]+)")
    77         pre = re.compile("(\s*)([^#]+)")
    67         index = 0
    78         index = 0
    68         for l in self.full_series:
    79         for l in self.full_series:
    86         def write_list(items, path):
    97         def write_list(items, path):
    87             fp = self.opener(path, 'w')
    98             fp = self.opener(path, 'w')
    88             for i in items:
    99             for i in items:
    89                 print >> fp, i
   100                 print >> fp, i
    90             fp.close()
   101             fp.close()
    91         if self.applied_dirty: write_list(self.applied, self.status_path)
   102         if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
    92         if self.series_dirty: write_list(self.full_series, self.series_path)
   103         if self.series_dirty: write_list(self.full_series, self.series_path)
    93 
   104 
    94     def readheaders(self, patch):
   105     def readheaders(self, patch):
    95         def eatdiff(lines):
   106         def eatdiff(lines):
    96             while lines:
   107             while lines:
   207             (p1, p2) = repo.dirstate.parents()
   218             (p1, p2) = repo.dirstate.parents()
   208             if p2 == revlog.nullid:
   219             if p2 == revlog.nullid:
   209                 return p1
   220                 return p1
   210             if len(self.applied) == 0:
   221             if len(self.applied) == 0:
   211                 return None
   222                 return None
   212             (top, patch) = self.applied[-1].split(':')
   223             return revlog.bin(self.applied[-1].rev)
   213             top = revlog.bin(top)
       
   214             return top
       
   215         pp = repo.changelog.parents(rev)
   224         pp = repo.changelog.parents(rev)
   216         if pp[1] != revlog.nullid:
   225         if pp[1] != revlog.nullid:
   217             arevs = [ x.split(':')[0] for x in self.applied ]
   226             arevs = [ x.rev for x in self.applied ]
   218             p0 = revlog.hex(pp[0])
   227             p0 = revlog.hex(pp[0])
   219             p1 = revlog.hex(pp[1])
   228             p1 = revlog.hex(pp[1])
   220             if p0 in arevs:
   229             if p0 in arevs:
   221                 return pp[0]
   230                 return pp[0]
   222             if p1 in arevs:
   231             if p1 in arevs:
   232             # the first patch in the queue is never a merge patch
   241             # the first patch in the queue is never a merge patch
   233             #
   242             #
   234             pname = ".hg.patches.merge.marker"
   243             pname = ".hg.patches.merge.marker"
   235             n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
   244             n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
   236                             wlock=wlock)
   245                             wlock=wlock)
   237             self.applied.append(revlog.hex(n) + ":" + pname)
   246             self.applied.append(StatusEntry(revlog.hex(n), pname))
   238             self.applied_dirty = 1
   247             self.applied_dirty = 1
   239 
   248 
   240         head = self.qparents(repo)
   249         head = self.qparents(repo)
   241 
   250 
   242         for patch in series:
   251         for patch in series:
   250                 self.ui.warn("patch %s is not applied\n" % patch)
   259                 self.ui.warn("patch %s is not applied\n" % patch)
   251                 return (1, None)
   260                 return (1, None)
   252             rev = revlog.bin(info[1])
   261             rev = revlog.bin(info[1])
   253             (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
   262             (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
   254             if head:
   263             if head:
   255                 self.applied.append(revlog.hex(head) + ":" + patch)
   264                 self.applied.append(StatusEntry(revlog.hex(head), patch))
   256                 self.applied_dirty = 1
   265                 self.applied_dirty = 1
   257             if err:
   266             if err:
   258                 return (err, head)
   267                 return (err, head)
   259         return (0, head)
   268         return (0, head)
   260 
   269 
   261     def patch(self, repo, patchfile):
   270     def patch(self, repo, patchfile):
   262         '''Apply patchfile  to the working directory.
   271         '''Apply patchfile  to the working directory.
   263         patchfile: file name of patch'''
   272         patchfile: file name of patch'''
   264         try:
   273         try:
   265             pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
   274             pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
   266             f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
   275             f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" %
   267                          (pp, repo.root, patchfile))
   276                          (pp, util.shellquote(repo.root), util.shellquote(patchfile)))
   268         except:
   277         except:
   269             self.ui.warn("patch failed, unable to continue (try -v)\n")
   278             self.ui.warn("patch failed, unable to continue (try -v)\n")
   270             return (None, [], False)
   279             return (None, [], False)
   271         files = []
   280         files = []
   272         fuzz = False
   281         fuzz = False
   273         for l in f:
   282         for l in f:
   274             l = l.rstrip('\r\n');
   283             l = l.rstrip('\r\n');
   275             if self.ui.verbose:
   284             if self.ui.verbose:
   276                 self.ui.warn(l + "\n")
   285                 self.ui.warn(l + "\n")
   277             if l[:14] == 'patching file ':
   286             if l[:14] == 'patching file ':
   278                 pf = os.path.normpath(l[14:])
   287                 pf = os.path.normpath(util.parse_patch_output(l))
   279                 # when patch finds a space in the file name, it puts
       
   280                 # single quotes around the filename.  strip them off
       
   281                 if pf[0] == "'" and pf[-1] == "'":
       
   282                     pf = pf[1:-1]
       
   283                 if pf not in files:
   288                 if pf not in files:
   284                     files.append(pf)
   289                     files.append(pf)
   285                 printed_file = False
   290                 printed_file = False
   286                 file_str = l
   291                 file_str = l
   287             elif l.find('with fuzz') >= 0:
   292             elif l.find('with fuzz') >= 0:
   349 
   354 
   350             if n == None:
   355             if n == None:
   351                 raise util.Abort(_("repo commit failed"))
   356                 raise util.Abort(_("repo commit failed"))
   352 
   357 
   353             if update_status:
   358             if update_status:
   354                 self.applied.append(revlog.hex(n) + ":" + patch)
   359                 self.applied.append(StatusEntry(revlog.hex(n), patch))
   355 
   360 
   356             if patcherr:
   361             if patcherr:
   357                 if not patchfound:
   362                 if not patchfound:
   358                     self.ui.warn("patch %s is empty\n" % patch)
   363                     self.ui.warn("patch %s is empty\n" % patch)
   359                     err = 0
   364                     err = 0
   387         self.parse_series()
   392         self.parse_series()
   388         self.series_dirty = 1
   393         self.series_dirty = 1
   389 
   394 
   390     def check_toppatch(self, repo):
   395     def check_toppatch(self, repo):
   391         if len(self.applied) > 0:
   396         if len(self.applied) > 0:
   392             (top, patch) = self.applied[-1].split(':')
   397             top = revlog.bin(self.applied[-1].rev)
   393             top = revlog.bin(top)
       
   394             pp = repo.dirstate.parents()
   398             pp = repo.dirstate.parents()
   395             if top not in pp:
   399             if top not in pp:
   396                 raise util.Abort(_("queue top not at same revision as working directory"))
   400                 raise util.Abort(_("queue top not at same revision as working directory"))
   397             return top
   401             return top
   398         return None
   402         return None
   419             n = repo.commit(commitfiles,
   423             n = repo.commit(commitfiles,
   420                             "New patch: %s" % patch, force=True, wlock=wlock)
   424                             "New patch: %s" % patch, force=True, wlock=wlock)
   421         if n == None:
   425         if n == None:
   422             raise util.Abort(_("repo commit failed"))
   426             raise util.Abort(_("repo commit failed"))
   423         self.full_series[insert:insert] = [patch]
   427         self.full_series[insert:insert] = [patch]
   424         self.applied.append(revlog.hex(n) + ":" + patch)
   428         self.applied.append(StatusEntry(revlog.hex(n), patch))
   425         self.parse_series()
   429         self.parse_series()
   426         self.series_dirty = 1
   430         self.series_dirty = 1
   427         self.applied_dirty = 1
   431         self.applied_dirty = 1
   428         p = self.opener(patch, "w")
   432         p = self.opener(patch, "w")
   429         if msg:
   433         if msg:
   499                         filerev = 0
   503                         filerev = 0
   500                     seen[f] = filerev
   504                     seen[f] = filerev
   501             # we go in two steps here so the strip loop happens in a
   505             # we go in two steps here so the strip loop happens in a
   502             # sensible order.  When stripping many files, this helps keep
   506             # sensible order.  When stripping many files, this helps keep
   503             # our disk access patterns under control.
   507             # our disk access patterns under control.
   504             list = seen.keys()
   508             seen_list = seen.keys()
   505             list.sort()
   509             seen_list.sort()
   506             for f in list:
   510             for f in seen_list:
   507                 ff = repo.file(f)
   511                 ff = repo.file(f)
   508                 filerev = seen[f]
   512                 filerev = seen[f]
   509                 if filerev != 0:
   513                 if filerev != 0:
   510                     if filerev in ff.nodemap:
   514                     if filerev in ff.nodemap:
   511                         filerev = ff.rev(filerev)
   515                         filerev = ff.rev(filerev)
   533         # that we actually want to keep.  changegroup will be used
   537         # that we actually want to keep.  changegroup will be used
   534         # to preserve them and add them back after the truncate
   538         # to preserve them and add them back after the truncate
   535         saveheads = []
   539         saveheads = []
   536         savebases = {}
   540         savebases = {}
   537 
   541 
   538         tip = chlog.tip()
       
   539         heads = limitheads(chlog, rev)
   542         heads = limitheads(chlog, rev)
   540         seen = {}
   543         seen = {}
   541 
   544 
   542         # search through all the heads, finding those where the revision
   545         # search through all the heads, finding those where the revision
   543         # we want to strip away is an ancestor.  Also look for merges
   546         # we want to strip away is an ancestor.  Also look for merges
   564                 for x in r:
   567                 for x in r:
   565                     if chlog.rev(x) > revnum:
   568                     if chlog.rev(x) > revnum:
   566                         savebases[x] = 1
   569                         savebases[x] = 1
   567 
   570 
   568         # create a changegroup for all the branches we need to keep
   571         # create a changegroup for all the branches we need to keep
   569         if backup is "all":
   572         if backup == "all":
   570             backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
   573             backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
   571             bundle(backupch)
   574             bundle(backupch)
   572         if saveheads:
   575         if saveheads:
   573             backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
   576             backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
   574             chgrpfile = bundle(backupch)
   577             chgrpfile = bundle(backupch)
   579         repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
   582         repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
   580         chlog.strip(revnum, revnum)
   583         chlog.strip(revnum, revnum)
   581         if saveheads:
   584         if saveheads:
   582             self.ui.status("adding branch\n")
   585             self.ui.status("adding branch\n")
   583             commands.unbundle(self.ui, repo, chgrpfile, update=False)
   586             commands.unbundle(self.ui, repo, chgrpfile, update=False)
   584             if backup is not "strip":
   587             if backup != "strip":
   585                 os.unlink(chgrpfile)
   588                 os.unlink(chgrpfile)
   586 
   589 
   587     def isapplied(self, patch):
   590     def isapplied(self, patch):
   588         """returns (index, rev, patch)"""
   591         """returns (index, rev, patch)"""
   589         for i in xrange(len(self.applied)):
   592         for i in xrange(len(self.applied)):
   590             p = self.applied[i]
   593             a = self.applied[i]
   591             a = p.split(':')
   594             if a.name == patch:
   592             if a[1] == patch:
   595                 return (i, a.rev, a.name)
   593                 return (i, a[0], a[1])
       
   594         return None
   596         return None
   595 
   597 
   596     # if the exact patch name does not exist, we try a few 
   598     # if the exact patch name does not exist, we try a few 
   597     # variations.  If strict is passed, we try only #1
   599     # variations.  If strict is passed, we try only #1
   598     #
   600     #
   691         s = self.series[start:end]
   693         s = self.series[start:end]
   692         if mergeq:
   694         if mergeq:
   693             ret = self.mergepatch(repo, mergeq, s, wlock)
   695             ret = self.mergepatch(repo, mergeq, s, wlock)
   694         else:
   696         else:
   695             ret = self.apply(repo, s, list, wlock=wlock)
   697             ret = self.apply(repo, s, list, wlock=wlock)
   696         top = self.applied[-1].split(':')[1]
   698         top = self.applied[-1].name
   697         if ret[0]:
   699         if ret[0]:
   698             self.ui.write("Errors during apply, please fix and refresh %s\n" %
   700             self.ui.write("Errors during apply, please fix and refresh %s\n" %
   699                           top)
   701                           top)
   700         else:
   702         else:
   701             self.ui.write("Now at: %s\n" % top)
   703             self.ui.write("Now at: %s\n" % top)
   728             self.ui.warn(_("no patches applied\n"))
   730             self.ui.warn(_("no patches applied\n"))
   729             sys.exit(1)
   731             sys.exit(1)
   730 
   732 
   731         if not update:
   733         if not update:
   732             parents = repo.dirstate.parents()
   734             parents = repo.dirstate.parents()
   733             rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
   735             rr = [ revlog.bin(x.rev) for x in self.applied ]
   734             for p in parents:
   736             for p in parents:
   735                 if p in rr:
   737                 if p in rr:
   736                     self.ui.warn("qpop: forcing dirstate update\n")
   738                     self.ui.warn("qpop: forcing dirstate update\n")
   737                     update = True
   739                     update = True
   738 
   740 
   749         else:
   751         else:
   750             popi = info[0] + 1
   752             popi = info[0] + 1
   751             if popi >= end:
   753             if popi >= end:
   752                 self.ui.warn("qpop: %s is already at the top\n" % patch)
   754                 self.ui.warn("qpop: %s is already at the top\n" % patch)
   753                 return
   755                 return
   754         info = [ popi ] + self.applied[popi].split(':')
   756         info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
   755 
   757 
   756         start = info[0]
   758         start = info[0]
   757         rev = revlog.bin(info[1])
   759         rev = revlog.bin(info[1])
   758 
   760 
   759         # we know there are no local changes, so we can make a simplified
   761         # we know there are no local changes, so we can make a simplified
   782                 repo.dirstate.forget(a)
   784                 repo.dirstate.forget(a)
   783             repo.dirstate.setparents(qp, revlog.nullid)
   785             repo.dirstate.setparents(qp, revlog.nullid)
   784         self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
   786         self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
   785         del self.applied[start:end]
   787         del self.applied[start:end]
   786         if len(self.applied):
   788         if len(self.applied):
   787             self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
   789             self.ui.write("Now at: %s\n" % self.applied[-1].name)
   788         else:
   790         else:
   789             self.ui.write("Patch queue now empty\n")
   791             self.ui.write("Patch queue now empty\n")
   790 
   792 
   791     def diff(self, repo, files):
   793     def diff(self, repo, files):
   792         top = self.check_toppatch(repo)
   794         top = self.check_toppatch(repo)
   800         if len(self.applied) == 0:
   802         if len(self.applied) == 0:
   801             self.ui.write("No patches applied\n")
   803             self.ui.write("No patches applied\n")
   802             return
   804             return
   803         wlock = repo.wlock()
   805         wlock = repo.wlock()
   804         self.check_toppatch(repo)
   806         self.check_toppatch(repo)
   805         qp = self.qparents(repo)
   807         (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
   806         (top, patch) = self.applied[-1].split(':')
       
   807         top = revlog.bin(top)
   808         top = revlog.bin(top)
   808         cparents = repo.changelog.parents(top)
   809         cparents = repo.changelog.parents(top)
   809         patchparent = self.qparents(repo, top)
   810         patchparent = self.qparents(repo, top)
   810         message, comments, user, date, patchfound = self.readheaders(patch)
   811         message, comments, user, date, patchfound = self.readheaders(patch)
   811 
   812 
   897             else:
   898             else:
   898                 message = msg
   899                 message = msg
   899 
   900 
   900             self.strip(repo, top, update=False, backup='strip', wlock=wlock)
   901             self.strip(repo, top, update=False, backup='strip', wlock=wlock)
   901             n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
   902             n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
   902             self.applied[-1] = revlog.hex(n) + ':' + patch
   903             self.applied[-1] = StatusEntry(revlog.hex(n), patch)
   903             self.applied_dirty = 1
   904             self.applied_dirty = 1
   904         else:
   905         else:
   905             commands.dodiff(patchf, self.ui, repo, patchparent, None)
   906             commands.dodiff(patchf, self.ui, repo, patchparent, None)
   906             patchf.close()
   907             patchf.close()
   907             self.pop(repo, force=True, wlock=wlock)
   908             self.pop(repo, force=True, wlock=wlock)
   919             raise util.Abort(_("patch %s is not in series file") % patch)
   920             raise util.Abort(_("patch %s is not in series file") % patch)
   920         if not patch:
   921         if not patch:
   921             start = self.series_end()
   922             start = self.series_end()
   922         else:
   923         else:
   923             start = self.series.index(patch) + 1
   924             start = self.series.index(patch) + 1
   924         for p in self.series[start:]:
   925         return [(i, self.series[i]) for i in xrange(start, len(self.series))]
   925             if self.ui.verbose:
       
   926                 self.ui.write("%d " % self.series.index(p))
       
   927             self.ui.write("%s\n" % p)
       
   928 
   926 
   929     def qseries(self, repo, missing=None, summary=False):
   927     def qseries(self, repo, missing=None, summary=False):
   930         start = self.series_end()
   928         start = self.series_end()
   931         if not missing:
   929         if not missing:
   932             for i in range(len(self.series)):
   930             for i in range(len(self.series)):
   942                     msg = msg and ': ' + msg[0] or ': '
   940                     msg = msg and ': ' + msg[0] or ': '
   943                 else:
   941                 else:
   944                     msg = ''
   942                     msg = ''
   945                 self.ui.write('%s%s\n' % (patch, msg))
   943                 self.ui.write('%s%s\n' % (patch, msg))
   946         else:
   944         else:
   947             list = []
   945             msng_list = []
   948             for root, dirs, files in os.walk(self.path):
   946             for root, dirs, files in os.walk(self.path):
   949                 d = root[len(self.path) + 1:]
   947                 d = root[len(self.path) + 1:]
   950                 for f in files:
   948                 for f in files:
   951                     fl = os.path.join(d, f)
   949                     fl = os.path.join(d, f)
   952                     if (fl not in self.series and
   950                     if (fl not in self.series and
   953                         fl not in (self.status_path, self.series_path)
   951                         fl not in (self.status_path, self.series_path)
   954                         and not fl.startswith('.')):
   952                         and not fl.startswith('.')):
   955                         list.append(fl)
   953                         msng_list.append(fl)
   956             list.sort()
   954             msng_list.sort()
   957             if list:
   955             for x in msng_list:
   958                 for x in list:
   956                 if self.ui.verbose:
   959                     if self.ui.verbose:
   957                     self.ui.write("D ")
   960                         self.ui.write("D ")
   958                 self.ui.write("%s\n" % x)
   961                     self.ui.write("%s\n" % x)
       
   962 
   959 
   963     def issaveline(self, l):
   960     def issaveline(self, l):
   964         name = l.split(':')[1]
   961         name = l.split(':')[1]
   965         if name == '.hg.patches.save.line':
   962         if name == '.hg.patches.save.line':
   966             return True
   963             return True
   985                 l = lines[i].rstrip()
   982                 l = lines[i].rstrip()
   986                 l = l[10:].split(' ')
   983                 l = l[10:].split(' ')
   987                 qpp = [ hg.bin(x) for x in l ]
   984                 qpp = [ hg.bin(x) for x in l ]
   988             elif datastart != None:
   985             elif datastart != None:
   989                 l = lines[i].rstrip()
   986                 l = lines[i].rstrip()
   990                 index = l.index(':')
   987                 se = StatusEntry(l)
   991                 id = l[:index]
   988                 file_ = se.name
   992                 file = l[index + 1:]
   989                 if se.rev:
   993                 if id:
   990                     applied.append(se)
   994                     applied.append(l)
   991                 series.append(file_)
   995                 series.append(file)
       
   996         if datastart == None:
   992         if datastart == None:
   997             self.ui.warn("No saved patch data found\n")
   993             self.ui.warn("No saved patch data found\n")
   998             return 1
   994             return 1
   999         self.ui.warn("restoring status: %s\n" % lines[0])
   995         self.ui.warn("restoring status: %s\n" % lines[0])
  1000         self.full_series = series
   996         self.full_series = series
  1041         r = self.qrepo()
  1037         r = self.qrepo()
  1042         if r:
  1038         if r:
  1043             pp = r.dirstate.parents()
  1039             pp = r.dirstate.parents()
  1044             msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
  1040             msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
  1045         msg += "\n\nPatch Data:\n"
  1041         msg += "\n\nPatch Data:\n"
  1046         text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
  1042         text = msg + "\n".join(str(self.applied)) + '\n' + (ar and "\n".join(ar)
  1047                                                        + '\n' or "")
  1043                                                        + '\n' or "")
  1048         n = repo.commit(None, text, user=None, force=1)
  1044         n = repo.commit(None, text, user=None, force=1)
  1049         if not n:
  1045         if not n:
  1050             self.ui.warn("repo commit failed\n")
  1046             self.ui.warn("repo commit failed\n")
  1051             return 1
  1047             return 1
  1052         self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
  1048         self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line'))
  1053         self.applied_dirty = 1
  1049         self.applied_dirty = 1
  1054 
  1050 
  1055     def full_series_end(self):
  1051     def full_series_end(self):
  1056         if len(self.applied) > 0:
  1052         if len(self.applied) > 0:
  1057             (top, p) = self.applied[-1].split(':')
  1053             p = self.applied[-1].name
  1058             end = self.find_series(p)
  1054             end = self.find_series(p)
  1059             if end == None:
  1055             if end == None:
  1060                 return len(self.full_series)
  1056                 return len(self.full_series)
  1061             return end + 1
  1057             return end + 1
  1062         return 0
  1058         return 0
  1063 
  1059 
  1064     def series_end(self):
  1060     def series_end(self):
  1065         end = 0
  1061         end = 0
  1066         if len(self.applied) > 0:
  1062         if len(self.applied) > 0:
  1067             (top, p) = self.applied[-1].split(':')
  1063             p = self.applied[-1].name
  1068             try:
  1064             try:
  1069                 end = self.series.index(p)
  1065                 end = self.series.index(p)
  1070             except ValueError:
  1066             except ValueError:
  1071                 return 0
  1067                 return 0
  1072             return end + 1
  1068             return end + 1
  1082         for x in xrange(end):
  1078         for x in xrange(end):
  1083             p = self.appliedname(x)
  1079             p = self.appliedname(x)
  1084             self.ui.write("%s\n" % p)
  1080             self.ui.write("%s\n" % p)
  1085 
  1081 
  1086     def appliedname(self, index):
  1082     def appliedname(self, index):
  1087         p = self.applied[index]
  1083         pname = self.applied[index].name
  1088         pname = p.split(':')[1]
       
  1089         if not self.ui.verbose:
  1084         if not self.ui.verbose:
  1090             p = pname
  1085             p = pname
  1091         else:
  1086         else:
  1092             p = str(self.series.index(pname)) + " " + p
  1087             p = str(self.series.index(pname)) + " " + p
  1093         return p
  1088         return p
  1171     repo.mq.qapplied(repo, patch)
  1166     repo.mq.qapplied(repo, patch)
  1172     return 0
  1167     return 0
  1173 
  1168 
  1174 def unapplied(ui, repo, patch=None, **opts):
  1169 def unapplied(ui, repo, patch=None, **opts):
  1175     """print the patches not yet applied"""
  1170     """print the patches not yet applied"""
  1176     repo.mq.unapplied(repo, patch)
  1171     for i, p in repo.mq.unapplied(repo, patch):
  1177     return 0
  1172         if ui.verbose:
       
  1173             ui.write("%d " % i)
       
  1174         ui.write("%s\n" % p)
  1178 
  1175 
  1179 def qimport(ui, repo, *filename, **opts):
  1176 def qimport(ui, repo, *filename, **opts):
  1180     """import a patch"""
  1177     """import a patch"""
  1181     q = repo.mq
  1178     q = repo.mq
  1182     q.qimport(repo, filename, patch=opts['name'],
  1179     q.qimport(repo, filename, patch=opts['name'],
  1221     sr = hg.repository(ui, ui.expandpath(source))
  1218     sr = hg.repository(ui, ui.expandpath(source))
  1222     qbase, destrev = None, None
  1219     qbase, destrev = None, None
  1223     if sr.local():
  1220     if sr.local():
  1224         reposetup(ui, sr)
  1221         reposetup(ui, sr)
  1225         if sr.mq.applied:
  1222         if sr.mq.applied:
  1226             qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
  1223             qbase = revlog.bin(sr.mq.applied[0].rev)
  1227             if not hg.islocal(dest):
  1224             if not hg.islocal(dest):
  1228                 destrev = sr.parents(qbase)[0]
  1225                 destrev = sr.parents(qbase)[0]
  1229     ui.note(_('cloning main repo\n'))
  1226     ui.note(_('cloning main repo\n'))
  1230     sr, dr = hg.clone(ui, sr, dest,
  1227     sr, dr = hg.clone(ui, sr, dest,
  1231                       pull=opts['pull'],
  1228                       pull=opts['pull'],
  1284 
  1281 
  1285     -m or -l set the patch header as well as the commit message.
  1282     -m or -l set the patch header as well as the commit message.
  1286     If neither is specified, the patch header is empty and the
  1283     If neither is specified, the patch header is empty and the
  1287     commit message is 'New patch: PATCH'"""
  1284     commit message is 'New patch: PATCH'"""
  1288     q = repo.mq
  1285     q = repo.mq
  1289     message=commands.logmessage(**opts)
  1286     message = commands.logmessage(**opts)
  1290     q.new(repo, patch, msg=message, force=opts['force'])
  1287     q.new(repo, patch, msg=message, force=opts['force'])
  1291     q.save_dirty()
  1288     q.save_dirty()
  1292     return 0
  1289     return 0
  1293 
  1290 
  1294 def refresh(ui, repo, **opts):
  1291 def refresh(ui, repo, **opts):
  1295     """update the current patch"""
  1292     """update the current patch"""
  1296     q = repo.mq
  1293     q = repo.mq
  1297     message=commands.logmessage(**opts)
  1294     message = commands.logmessage(**opts)
  1298     if opts['edit']:
  1295     if opts['edit']:
  1299         if message:
  1296         if message:
  1300             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
  1297             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
  1301         patch = q.applied[-1].split(':')[1]
  1298         patch = q.applied[-1].name
  1302         (message, comment, user, date, hasdiff) = q.readheaders(patch)
  1299         (message, comment, user, date, hasdiff) = q.readheaders(patch)
  1303         message = ui.edit('\n'.join(message), user or ui.username())
  1300         message = ui.edit('\n'.join(message), user or ui.username())
  1304     q.refresh(repo, msg=message, short=opts['short'])
  1301     q.refresh(repo, msg=message, short=opts['short'])
  1305     q.save_dirty()
  1302     q.save_dirty()
  1306     return 0
  1303     return 0
  1329     if not files:
  1326     if not files:
  1330         raise util.Abort(_('qfold requires at least one patch name'))
  1327         raise util.Abort(_('qfold requires at least one patch name'))
  1331     if not q.check_toppatch(repo):
  1328     if not q.check_toppatch(repo):
  1332         raise util.Abort(_('No patches applied\n'))
  1329         raise util.Abort(_('No patches applied\n'))
  1333 
  1330 
  1334     message=commands.logmessage(**opts)
  1331     message = commands.logmessage(**opts)
  1335     if opts['edit']:
  1332     if opts['edit']:
  1336         if message:
  1333         if message:
  1337             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
  1334             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
  1338 
  1335 
  1339     parent = q.lookup('qtip')
  1336     parent = q.lookup('qtip')
  1340     patches = []
  1337     patches = []
  1341     messages = []
  1338     messages = []
  1342     for f in files:
  1339     for f in files:
  1343         patch = q.lookup(f)
  1340         patch = q.lookup(f)
  1344         if patch in patches or patch == parent:
  1341         if patch in patches or patch == parent:
  1345             self.ui.warn(_('Skipping already folded patch %s') % patch)
  1342             ui.warn(_('Skipping already folded patch %s') % patch)
  1346         if q.isapplied(patch):
  1343         if q.isapplied(patch):
  1347             raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
  1344             raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
  1348         patches.append(patch)
  1345         patches.append(patch)
  1349 
  1346 
  1350     for patch in patches:
  1347     for patch in patches:
  1386     message = repo.mq.readheaders(patch)[0]
  1383     message = repo.mq.readheaders(patch)[0]
  1387 
  1384 
  1388     ui.write('\n'.join(message) + '\n')
  1385     ui.write('\n'.join(message) + '\n')
  1389 
  1386 
  1390 def lastsavename(path):
  1387 def lastsavename(path):
  1391     (dir, base) = os.path.split(path)
  1388     (directory, base) = os.path.split(path)
  1392     names = os.listdir(dir)
  1389     names = os.listdir(directory)
  1393     namere = re.compile("%s.([0-9]+)" % base)
  1390     namere = re.compile("%s.([0-9]+)" % base)
  1394     max = None
  1391     maxindex = None
  1395     maxname = None
  1392     maxname = None
  1396     for f in names:
  1393     for f in names:
  1397         m = namere.match(f)
  1394         m = namere.match(f)
  1398         if m:
  1395         if m:
  1399             index = int(m.group(1))
  1396             index = int(m.group(1))
  1400             if max == None or index > max:
  1397             if maxindex == None or index > maxindex:
  1401                 max = index
  1398                 maxindex = index
  1402                 maxname = f
  1399                 maxname = f
  1403     if maxname:
  1400     if maxname:
  1404         return (os.path.join(dir, maxname), max)
  1401         return (os.path.join(directory, maxname), maxindex)
  1405     return (None, None)
  1402     return (None, None)
  1406 
  1403 
  1407 def savename(path):
  1404 def savename(path):
  1408     (last, index) = lastsavename(path)
  1405     (last, index) = lastsavename(path)
  1409     if last is None:
  1406     if last is None:
  1480     q.parse_series()
  1477     q.parse_series()
  1481     q.series_dirty = 1
  1478     q.series_dirty = 1
  1482 
  1479 
  1483     info = q.isapplied(patch)
  1480     info = q.isapplied(patch)
  1484     if info:
  1481     if info:
  1485         q.applied[info[0]] = info[1] + ':' + name
  1482         q.applied[info[0]] = StatusEntry(info[1], name)
  1486     q.applied_dirty = 1
  1483     q.applied_dirty = 1
  1487 
  1484 
  1488     util.rename(os.path.join(q.path, patch), absdest)
  1485     util.rename(os.path.join(q.path, patch), absdest)
  1489     r = q.qrepo()
  1486     r = q.qrepo()
  1490     if r:
  1487     if r:
  1506     return 0
  1503     return 0
  1507 
  1504 
  1508 def save(ui, repo, **opts):
  1505 def save(ui, repo, **opts):
  1509     """save current queue state"""
  1506     """save current queue state"""
  1510     q = repo.mq
  1507     q = repo.mq
  1511     message=commands.logmessage(**opts)
  1508     message = commands.logmessage(**opts)
  1512     ret = q.save(repo, msg=message)
  1509     ret = q.save(repo, msg=message)
  1513     if ret:
  1510     if ret:
  1514         return ret
  1511         return ret
  1515     q.save_dirty()
  1512     q.save_dirty()
  1516     if opts['copy']:
  1513     if opts['copy']:
  1561 
  1558 
  1562             q = self.mq
  1559             q = self.mq
  1563             if not q.applied:
  1560             if not q.applied:
  1564                 return tagscache
  1561                 return tagscache
  1565 
  1562 
  1566             mqtags = [patch.split(':') for patch in q.applied]
  1563             mqtags = [(patch.rev, patch.name) for patch in q.applied]
  1567             mqtags.append((mqtags[-1][0], 'qtip'))
  1564             mqtags.append((mqtags[-1][0], 'qtip'))
  1568             mqtags.append((mqtags[0][0], 'qbase'))
  1565             mqtags.append((mqtags[0][0], 'qbase'))
  1569             for patch in mqtags:
  1566             for patch in mqtags:
  1570                 if patch[1] in tagscache:
  1567                 if patch[1] in tagscache:
  1571                     self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
  1568                     self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])