hgext/mq.py
changeset 2775 ee48e5ef8753
parent 2765 0327bd1c831c
child 2776 ae726521717c
equal deleted inserted replaced
2765:0327bd1c831c 2775:ee48e5ef8753
    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.read_series(self.full_series)
    70         self.read_series(self.full_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:
    93             if len(self.applied) > 0:
   104             if len(self.applied) > 0:
    94                 nl = "\n"
   105                 nl = "\n"
    95             else:
   106             else:
    96                 nl = ""
   107                 nl = ""
    97             f = self.opener(self.status_path, "w")
   108             f = self.opener(self.status_path, "w")
    98             f.write("\n".join(self.applied) + nl)
   109             f.write("\n".join([str(x) for x in self.applied]) + nl)
    99         if self.series_dirty:
   110         if self.series_dirty:
   100             if len(self.full_series) > 0:
   111             if len(self.full_series) > 0:
   101                 nl = "\n"
   112                 nl = "\n"
   102             else:
   113             else:
   103                 nl = ""
   114                 nl = ""
   220             (p1, p2) = repo.dirstate.parents()
   231             (p1, p2) = repo.dirstate.parents()
   221             if p2 == revlog.nullid:
   232             if p2 == revlog.nullid:
   222                 return p1
   233                 return p1
   223             if len(self.applied) == 0:
   234             if len(self.applied) == 0:
   224                 return None
   235                 return None
   225             (top, patch) = self.applied[-1].split(':')
   236             return revlog.bin(self.applied[-1].rev)
   226             top = revlog.bin(top)
       
   227             return top
       
   228         pp = repo.changelog.parents(rev)
   237         pp = repo.changelog.parents(rev)
   229         if pp[1] != revlog.nullid:
   238         if pp[1] != revlog.nullid:
   230             arevs = [ x.split(':')[0] for x in self.applied ]
   239             arevs = [ x.rev for x in self.applied ]
   231             p0 = revlog.hex(pp[0])
   240             p0 = revlog.hex(pp[0])
   232             p1 = revlog.hex(pp[1])
   241             p1 = revlog.hex(pp[1])
   233             if p0 in arevs:
   242             if p0 in arevs:
   234                 return pp[0]
   243                 return pp[0]
   235             if p1 in arevs:
   244             if p1 in arevs:
   245             # the first patch in the queue is never a merge patch
   254             # the first patch in the queue is never a merge patch
   246             #
   255             #
   247             pname = ".hg.patches.merge.marker"
   256             pname = ".hg.patches.merge.marker"
   248             n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
   257             n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
   249                             wlock=wlock)
   258                             wlock=wlock)
   250             self.applied.append(revlog.hex(n) + ":" + pname)
   259             self.applied.append(StatusEntry(revlog.hex(n), pname))
   251             self.applied_dirty = 1
   260             self.applied_dirty = 1
   252 
   261 
   253         head = self.qparents(repo)
   262         head = self.qparents(repo)
   254 
   263 
   255         for patch in series:
   264         for patch in series:
   263                 self.ui.warn("patch %s is not applied\n" % patch)
   272                 self.ui.warn("patch %s is not applied\n" % patch)
   264                 return (1, None)
   273                 return (1, None)
   265             rev = revlog.bin(info[1])
   274             rev = revlog.bin(info[1])
   266             (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
   275             (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
   267             if head:
   276             if head:
   268                 self.applied.append(revlog.hex(head) + ":" + patch)
   277                 self.applied.append(StatusEntry(revlog.hex(head), patch))
   269                 self.applied_dirty = 1
   278                 self.applied_dirty = 1
   270             if err:
   279             if err:
   271                 return (err, head)
   280                 return (err, head)
   272         return (0, head)
   281         return (0, head)
   273 
   282 
   362 
   371 
   363             if n == None:
   372             if n == None:
   364                 raise util.Abort(_("repo commit failed"))
   373                 raise util.Abort(_("repo commit failed"))
   365 
   374 
   366             if update_status:
   375             if update_status:
   367                 self.applied.append(revlog.hex(n) + ":" + patch)
   376                 self.applied.append(StatusEntry(revlog.hex(n), patch))
   368 
   377 
   369             if patcherr:
   378             if patcherr:
   370                 if not patchfound:
   379                 if not patchfound:
   371                     self.ui.warn("patch %s is empty\n" % patch)
   380                     self.ui.warn("patch %s is empty\n" % patch)
   372                     err = 0
   381                     err = 0
   400         self.read_series(self.full_series)
   409         self.read_series(self.full_series)
   401         self.series_dirty = 1
   410         self.series_dirty = 1
   402 
   411 
   403     def check_toppatch(self, repo):
   412     def check_toppatch(self, repo):
   404         if len(self.applied) > 0:
   413         if len(self.applied) > 0:
   405             (top, patch) = self.applied[-1].split(':')
   414             top = revlog.bin(self.applied[-1].rev)
   406             top = revlog.bin(top)
       
   407             pp = repo.dirstate.parents()
   415             pp = repo.dirstate.parents()
   408             if top not in pp:
   416             if top not in pp:
   409                 raise util.Abort(_("queue top not at same revision as working directory"))
   417                 raise util.Abort(_("queue top not at same revision as working directory"))
   410             return top
   418             return top
   411         return None
   419         return None
   432             n = repo.commit(commitfiles,
   440             n = repo.commit(commitfiles,
   433                             "New patch: %s" % patch, force=True, wlock=wlock)
   441                             "New patch: %s" % patch, force=True, wlock=wlock)
   434         if n == None:
   442         if n == None:
   435             raise util.Abort(_("repo commit failed"))
   443             raise util.Abort(_("repo commit failed"))
   436         self.full_series[insert:insert] = [patch]
   444         self.full_series[insert:insert] = [patch]
   437         self.applied.append(revlog.hex(n) + ":" + patch)
   445         self.applied.append(StatusEntry(revlog.hex(n), patch))
   438         self.read_series(self.full_series)
   446         self.read_series(self.full_series)
   439         self.series_dirty = 1
   447         self.series_dirty = 1
   440         self.applied_dirty = 1
   448         self.applied_dirty = 1
   441         p = self.opener(patch, "w")
   449         p = self.opener(patch, "w")
   442         if msg:
   450         if msg:
   598                 os.unlink(chgrpfile)
   606                 os.unlink(chgrpfile)
   599 
   607 
   600     def isapplied(self, patch):
   608     def isapplied(self, patch):
   601         """returns (index, rev, patch)"""
   609         """returns (index, rev, patch)"""
   602         for i in xrange(len(self.applied)):
   610         for i in xrange(len(self.applied)):
   603             p = self.applied[i]
   611             a = self.applied[i]
   604             a = p.split(':')
   612             if a.name == patch:
   605             if a[1] == patch:
   613                 return (i, a.rev, a.name)
   606                 return (i, a[0], a[1])
       
   607         return None
   614         return None
   608 
   615 
   609     # if the exact patch name does not exist, we try a few 
   616     # if the exact patch name does not exist, we try a few 
   610     # variations.  If strict is passed, we try only #1
   617     # variations.  If strict is passed, we try only #1
   611     #
   618     #
   704         s = self.series[start:end]
   711         s = self.series[start:end]
   705         if mergeq:
   712         if mergeq:
   706             ret = self.mergepatch(repo, mergeq, s, wlock)
   713             ret = self.mergepatch(repo, mergeq, s, wlock)
   707         else:
   714         else:
   708             ret = self.apply(repo, s, list, wlock=wlock)
   715             ret = self.apply(repo, s, list, wlock=wlock)
   709         top = self.applied[-1].split(':')[1]
   716         top = self.applied[-1].name
   710         if ret[0]:
   717         if ret[0]:
   711             self.ui.write("Errors during apply, please fix and refresh %s\n" %
   718             self.ui.write("Errors during apply, please fix and refresh %s\n" %
   712                           top)
   719                           top)
   713         else:
   720         else:
   714             self.ui.write("Now at: %s\n" % top)
   721             self.ui.write("Now at: %s\n" % top)
   741             self.ui.warn(_("no patches applied\n"))
   748             self.ui.warn(_("no patches applied\n"))
   742             sys.exit(1)
   749             sys.exit(1)
   743 
   750 
   744         if not update:
   751         if not update:
   745             parents = repo.dirstate.parents()
   752             parents = repo.dirstate.parents()
   746             rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
   753             rr = [ revlog.bin(x.rev) for x in self.applied ]
   747             for p in parents:
   754             for p in parents:
   748                 if p in rr:
   755                 if p in rr:
   749                     self.ui.warn("qpop: forcing dirstate update\n")
   756                     self.ui.warn("qpop: forcing dirstate update\n")
   750                     update = True
   757                     update = True
   751 
   758 
   762         else:
   769         else:
   763             popi = info[0] + 1
   770             popi = info[0] + 1
   764             if popi >= end:
   771             if popi >= end:
   765                 self.ui.warn("qpop: %s is already at the top\n" % patch)
   772                 self.ui.warn("qpop: %s is already at the top\n" % patch)
   766                 return
   773                 return
   767         info = [ popi ] + self.applied[popi].split(':')
   774         info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
   768 
   775 
   769         start = info[0]
   776         start = info[0]
   770         rev = revlog.bin(info[1])
   777         rev = revlog.bin(info[1])
   771 
   778 
   772         # we know there are no local changes, so we can make a simplified
   779         # we know there are no local changes, so we can make a simplified
   795                 repo.dirstate.forget(a)
   802                 repo.dirstate.forget(a)
   796             repo.dirstate.setparents(qp, revlog.nullid)
   803             repo.dirstate.setparents(qp, revlog.nullid)
   797         self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
   804         self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
   798         del self.applied[start:end]
   805         del self.applied[start:end]
   799         if len(self.applied):
   806         if len(self.applied):
   800             self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
   807             self.ui.write("Now at: %s\n" % self.applied[-1].name)
   801         else:
   808         else:
   802             self.ui.write("Patch queue now empty\n")
   809             self.ui.write("Patch queue now empty\n")
   803 
   810 
   804     def diff(self, repo, files):
   811     def diff(self, repo, files):
   805         top = self.check_toppatch(repo)
   812         top = self.check_toppatch(repo)
   814             self.ui.write("No patches applied\n")
   821             self.ui.write("No patches applied\n")
   815             return
   822             return
   816         wlock = repo.wlock()
   823         wlock = repo.wlock()
   817         self.check_toppatch(repo)
   824         self.check_toppatch(repo)
   818         qp = self.qparents(repo)
   825         qp = self.qparents(repo)
   819         (top, patch) = self.applied[-1].split(':')
   826         (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
   820         top = revlog.bin(top)
   827         top = revlog.bin(top)
   821         cparents = repo.changelog.parents(top)
   828         cparents = repo.changelog.parents(top)
   822         patchparent = self.qparents(repo, top)
   829         patchparent = self.qparents(repo, top)
   823         message, comments, user, date, patchfound = self.readheaders(patch)
   830         message, comments, user, date, patchfound = self.readheaders(patch)
   824 
   831 
   910             else:
   917             else:
   911                 message = msg
   918                 message = msg
   912 
   919 
   913             self.strip(repo, top, update=False, backup='strip', wlock=wlock)
   920             self.strip(repo, top, update=False, backup='strip', wlock=wlock)
   914             n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
   921             n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
   915             self.applied[-1] = revlog.hex(n) + ':' + patch
   922             self.applied[-1] = StatusEntry(revlog.hex(n), patch)
   916             self.applied_dirty = 1
   923             self.applied_dirty = 1
   917         else:
   924         else:
   918             commands.dodiff(patchf, self.ui, repo, patchparent, None)
   925             commands.dodiff(patchf, self.ui, repo, patchparent, None)
   919             patchf.close()
   926             patchf.close()
   920             self.pop(repo, force=True, wlock=wlock)
   927             self.pop(repo, force=True, wlock=wlock)
   998                 l = lines[i].rstrip()
  1005                 l = lines[i].rstrip()
   999                 l = l[10:].split(' ')
  1006                 l = l[10:].split(' ')
  1000                 qpp = [ hg.bin(x) for x in l ]
  1007                 qpp = [ hg.bin(x) for x in l ]
  1001             elif datastart != None:
  1008             elif datastart != None:
  1002                 l = lines[i].rstrip()
  1009                 l = lines[i].rstrip()
  1003                 index = l.index(':')
  1010                 se = StatusEntry(l)
  1004                 id = l[:index]
  1011                 id = se.rev
  1005                 file = l[index + 1:]
  1012                 file = se.name
  1006                 if id:
  1013                 if id:
  1007                     applied.append(l)
  1014                     applied.append(se)
  1008                 series.append(file)
  1015                 series.append(file)
  1009         if datastart == None:
  1016         if datastart == None:
  1010             self.ui.warn("No saved patch data found\n")
  1017             self.ui.warn("No saved patch data found\n")
  1011             return 1
  1018             return 1
  1012         self.ui.warn("restoring status: %s\n" % lines[0])
  1019         self.ui.warn("restoring status: %s\n" % lines[0])
  1054         r = self.qrepo()
  1061         r = self.qrepo()
  1055         if r:
  1062         if r:
  1056             pp = r.dirstate.parents()
  1063             pp = r.dirstate.parents()
  1057             msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
  1064             msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
  1058         msg += "\n\nPatch Data:\n"
  1065         msg += "\n\nPatch Data:\n"
  1059         text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
  1066         text = msg + "\n".join(str(self.applied)) + '\n' + (ar and "\n".join(ar)
  1060                                                        + '\n' or "")
  1067                                                        + '\n' or "")
  1061         n = repo.commit(None, text, user=None, force=1)
  1068         n = repo.commit(None, text, user=None, force=1)
  1062         if not n:
  1069         if not n:
  1063             self.ui.warn("repo commit failed\n")
  1070             self.ui.warn("repo commit failed\n")
  1064             return 1
  1071             return 1
  1065         self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
  1072         self.applied.append(StatusEntry(revlog.hex(n),'.hg.patches.save.line'))
  1066         self.applied_dirty = 1
  1073         self.applied_dirty = 1
  1067 
  1074 
  1068     def full_series_end(self):
  1075     def full_series_end(self):
  1069         if len(self.applied) > 0:
  1076         if len(self.applied) > 0:
  1070             (top, p) = self.applied[-1].split(':')
  1077             p = self.applied[-1].name
  1071             end = self.find_series(p)
  1078             end = self.find_series(p)
  1072             if end == None:
  1079             if end == None:
  1073                 return len(self.full_series)
  1080                 return len(self.full_series)
  1074             return end + 1
  1081             return end + 1
  1075         return 0
  1082         return 0
  1076 
  1083 
  1077     def series_end(self):
  1084     def series_end(self):
  1078         end = 0
  1085         end = 0
  1079         if len(self.applied) > 0:
  1086         if len(self.applied) > 0:
  1080             (top, p) = self.applied[-1].split(':')
  1087             p = self.applied[-1].name
  1081             try:
  1088             try:
  1082                 end = self.series.index(p)
  1089                 end = self.series.index(p)
  1083             except ValueError:
  1090             except ValueError:
  1084                 return 0
  1091                 return 0
  1085             return end + 1
  1092             return end + 1
  1095         for x in xrange(end):
  1102         for x in xrange(end):
  1096             p = self.appliedname(x)
  1103             p = self.appliedname(x)
  1097             self.ui.write("%s\n" % p)
  1104             self.ui.write("%s\n" % p)
  1098 
  1105 
  1099     def appliedname(self, index):
  1106     def appliedname(self, index):
  1100         p = self.applied[index]
  1107         pname = self.applied[index].name
  1101         pname = p.split(':')[1]
       
  1102         if not self.ui.verbose:
  1108         if not self.ui.verbose:
  1103             p = pname
  1109             p = pname
  1104         else:
  1110         else:
  1105             p = str(self.series.index(pname)) + " " + p
  1111             p = str(self.series.index(pname)) + " " + p
  1106         return p
  1112         return p
  1234     sr = hg.repository(ui, ui.expandpath(source))
  1240     sr = hg.repository(ui, ui.expandpath(source))
  1235     qbase, destrev = None, None
  1241     qbase, destrev = None, None
  1236     if sr.local():
  1242     if sr.local():
  1237         reposetup(ui, sr)
  1243         reposetup(ui, sr)
  1238         if sr.mq.applied:
  1244         if sr.mq.applied:
  1239             qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
  1245             qbase = revlog.bin(sr.mq.applied[0].rev)
  1240             if not hg.islocal(dest):
  1246             if not hg.islocal(dest):
  1241                 destrev = sr.parents(qbase)[0]
  1247                 destrev = sr.parents(qbase)[0]
  1242     ui.note(_('cloning main repo\n'))
  1248     ui.note(_('cloning main repo\n'))
  1243     sr, dr = hg.clone(ui, sr, dest,
  1249     sr, dr = hg.clone(ui, sr, dest,
  1244                       pull=opts['pull'],
  1250                       pull=opts['pull'],
  1312     q = repo.mq
  1318     q = repo.mq
  1313     message=commands.logmessage(**opts)
  1319     message=commands.logmessage(**opts)
  1314     if opts['edit']:
  1320     if opts['edit']:
  1315         if message:
  1321         if message:
  1316             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
  1322             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
  1317         patch = q.applied[-1].split(':')[1]
  1323         patch = q.applied[-1].name
  1318         (message, comment, user, date, hasdiff) = q.readheaders(patch)
  1324         (message, comment, user, date, hasdiff) = q.readheaders(patch)
  1319         message = ui.edit('\n'.join(message), user or ui.username())
  1325         message = ui.edit('\n'.join(message), user or ui.username())
  1320     q.refresh(repo, msg=message, short=opts['short'])
  1326     q.refresh(repo, msg=message, short=opts['short'])
  1321     q.save_dirty()
  1327     q.save_dirty()
  1322     return 0
  1328     return 0
  1490     q.read_series(q.full_series)
  1496     q.read_series(q.full_series)
  1491     q.series_dirty = 1
  1497     q.series_dirty = 1
  1492 
  1498 
  1493     info = q.isapplied(patch)
  1499     info = q.isapplied(patch)
  1494     if info:
  1500     if info:
  1495         q.applied[info[0]] = info[1] + ':' + name
  1501         q.applied[info[0]] = StatusEntry(info[1], name)
  1496     q.applied_dirty = 1
  1502     q.applied_dirty = 1
  1497 
  1503 
  1498     util.rename(os.path.join(q.path, patch), absdest)
  1504     util.rename(os.path.join(q.path, patch), absdest)
  1499     r = q.qrepo()
  1505     r = q.qrepo()
  1500     if r:
  1506     if r:
  1571 
  1577 
  1572             q = self.mq
  1578             q = self.mq
  1573             if not q.applied:
  1579             if not q.applied:
  1574                 return tagscache
  1580                 return tagscache
  1575 
  1581 
  1576             mqtags = [patch.split(':') for patch in q.applied]
  1582             mqtags = [(patch.rev, patch.name) for patch in q.applied]
  1577             mqtags.append((mqtags[-1][0], 'qtip'))
  1583             mqtags.append((mqtags[-1][0], 'qtip'))
  1578             mqtags.append((mqtags[0][0], 'qbase'))
  1584             mqtags.append((mqtags[0][0], 'qbase'))
  1579             for patch in mqtags:
  1585             for patch in mqtags:
  1580                 if patch[1] in tagscache:
  1586                 if patch[1] in tagscache:
  1581                     self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
  1587                     self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])