comparison hgext/mq.py @ 2803:987c31e2a08c

Merge with crew
author Matt Mackall <mpm@selenic.com>
date Mon, 07 Aug 2006 16:47:06 -0500
parents b550cd82f92a a3c6e7888abf
children 4b20daa25f15
comparison
equal deleted inserted replaced
2802: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])