comparison hgext/mq.py @ 6042:2da5b19a6460

Merge with crew
author Bryan O'Sullivan <bos@serpentine.com>
date Wed, 06 Feb 2008 19:57:52 -0800
parents dd714452c26e 30d2fecaab76
children 6283316bcfd4
comparison
equal deleted inserted replaced
6041:dd714452c26e 6042:2da5b19a6460
32 from mercurial.i18n import _ 32 from mercurial.i18n import _
33 from mercurial import commands, cmdutil, hg, patch, revlog, util 33 from mercurial import commands, cmdutil, hg, patch, revlog, util
34 from mercurial import repair 34 from mercurial import repair
35 import os, sys, re, errno 35 import os, sys, re, errno
36 36
37 commands.norepo += " qclone qversion" 37 commands.norepo += " qclone"
38 38
39 # Patch names looks like unix-file names. 39 # Patch names looks like unix-file names.
40 # They must be joinable with queue directory and result in the patch path. 40 # They must be joinable with queue directory and result in the patch path.
41 normname = util.normpath 41 normname = util.normpath
42 42
222 222
223 def save_dirty(self): 223 def save_dirty(self):
224 def write_list(items, path): 224 def write_list(items, path):
225 fp = self.opener(path, 'w') 225 fp = self.opener(path, 'w')
226 for i in items: 226 for i in items:
227 print >> fp, i 227 fp.write("%s\n" % i)
228 fp.close() 228 fp.close()
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path) 229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path) 230 if self.series_dirty: write_list(self.full_series, self.series_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path) 231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
232 232
453 finally: 453 finally:
454 repo.invalidate() 454 repo.invalidate()
455 repo.dirstate.invalidate() 455 repo.dirstate.invalidate()
456 raise 456 raise
457 finally: 457 finally:
458 del lock, wlock, tr 458 del tr, lock, wlock
459 self.removeundo(repo)
459 460
460 def _apply(self, repo, series, list=False, update_status=True, 461 def _apply(self, repo, series, list=False, update_status=True,
461 strict=False, patchdir=None, merge=None, all_files={}): 462 strict=False, patchdir=None, merge=None, all_files={}):
462 # TODO unify with commands.py 463 # TODO unify with commands.py
463 if not patchdir: 464 if not patchdir:
525 526
526 if fuzz and strict: 527 if fuzz and strict:
527 self.ui.warn("fuzz found when applying patch, stopping\n") 528 self.ui.warn("fuzz found when applying patch, stopping\n")
528 err = 1 529 err = 1
529 break 530 break
530 self.removeundo(repo)
531 return (err, n) 531 return (err, n)
532 532
533 def delete(self, repo, patches, opts): 533 def delete(self, repo, patches, opts):
534 if not patches and not opts.get('rev'): 534 if not patches and not opts.get('rev'):
535 raise util.Abort(_('qdelete requires at least one revision or ' 535 raise util.Abort(_('qdelete requires at least one revision or '
585 def check_toppatch(self, repo): 585 def check_toppatch(self, repo):
586 if len(self.applied) > 0: 586 if len(self.applied) > 0:
587 top = revlog.bin(self.applied[-1].rev) 587 top = revlog.bin(self.applied[-1].rev)
588 pp = repo.dirstate.parents() 588 pp = repo.dirstate.parents()
589 if top not in pp: 589 if top not in pp:
590 raise util.Abort(_("queue top not at same revision as working directory")) 590 raise util.Abort(_("working directory revision is not qtip"))
591 return top 591 return top
592 return None 592 return None
593 def check_localchanges(self, repo, force=False, refresh=True): 593 def check_localchanges(self, repo, force=False, refresh=True):
594 m, a, r, d = repo.status()[:4] 594 m, a, r, d = repo.status()[:4]
595 if m or a or r or d: 595 if m or a or r or d:
598 raise util.Abort(_("local changes found, refresh first")) 598 raise util.Abort(_("local changes found, refresh first"))
599 else: 599 else:
600 raise util.Abort(_("local changes found")) 600 raise util.Abort(_("local changes found"))
601 return m, a, r, d 601 return m, a, r, d
602 602
603 _reserved = ('series', 'status', 'guards')
604 def check_reserved_name(self, name):
605 if (name in self._reserved or name.startswith('.hg')
606 or name.startswith('.mq')):
607 raise util.Abort(_('"%s" cannot be used as the name of a patch')
608 % name)
609
603 def new(self, repo, patch, *pats, **opts): 610 def new(self, repo, patch, *pats, **opts):
604 msg = opts.get('msg') 611 msg = opts.get('msg')
605 force = opts.get('force') 612 force = opts.get('force')
613 user = opts.get('user')
614 date = opts.get('date')
615 self.check_reserved_name(patch)
606 if os.path.exists(self.join(patch)): 616 if os.path.exists(self.join(patch)):
607 raise util.Abort(_('patch "%s" already exists') % patch) 617 raise util.Abort(_('patch "%s" already exists') % patch)
608 if opts.get('include') or opts.get('exclude') or pats: 618 if opts.get('include') or opts.get('exclude') or pats:
609 fns, match, anypats = cmdutil.matchpats(repo, pats, opts) 619 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
610 m, a, r, d = repo.status(files=fns, match=match)[:4] 620 m, a, r, d = repo.status(files=fns, match=match)[:4]
611 else: 621 else:
612 m, a, r, d = self.check_localchanges(repo, force) 622 m, a, r, d = self.check_localchanges(repo, force)
623 fns, match, anypats = cmdutil.matchpats(repo, m + a + r)
613 commitfiles = m + a + r 624 commitfiles = m + a + r
614 self.check_toppatch(repo) 625 self.check_toppatch(repo)
615 wlock = repo.wlock() 626 wlock = repo.wlock()
616 try: 627 try:
617 insert = self.full_series_end() 628 insert = self.full_series_end()
618 if msg: 629 commitmsg = msg and msg or ("[mq]: %s" % patch)
619 n = repo.commit(commitfiles, msg, force=True) 630 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
620 else:
621 n = repo.commit(commitfiles, "[mq]: %s" % patch, force=True)
622 if n == None: 631 if n == None:
623 raise util.Abort(_("repo commit failed")) 632 raise util.Abort(_("repo commit failed"))
624 self.full_series[insert:insert] = [patch] 633 self.full_series[insert:insert] = [patch]
625 self.applied.append(statusentry(revlog.hex(n), patch)) 634 self.applied.append(statusentry(revlog.hex(n), patch))
626 self.parse_series() 635 self.parse_series()
627 self.series_dirty = 1 636 self.series_dirty = 1
628 self.applied_dirty = 1 637 self.applied_dirty = 1
629 p = self.opener(patch, "w") 638 p = self.opener(patch, "w")
639 if date:
640 p.write("# HG changeset patch\n")
641 if user:
642 p.write("# User " + user + "\n")
643 p.write("# Date " + date + "\n")
644 p.write("\n")
645 elif user:
646 p.write("From: " + user + "\n")
647 p.write("\n")
630 if msg: 648 if msg:
631 msg = msg + "\n" 649 msg = msg + "\n"
632 p.write(msg) 650 p.write(msg)
633 p.close() 651 p.close()
634 wlock = None 652 wlock = None
635 r = self.qrepo() 653 r = self.qrepo()
636 if r: r.add([patch]) 654 if r: r.add([patch])
637 if commitfiles: 655 if commitfiles:
638 self.refresh(repo, short=True) 656 self.refresh(repo, short=True, git=opts.get('git'))
639 self.removeundo(repo) 657 self.removeundo(repo)
640 finally: 658 finally:
641 del wlock 659 del wlock
642 660
643 def strip(self, repo, rev, update=True, backup="all"): 661 def strip(self, repo, rev, update=True, backup="all"):
652 hg.clean(repo, urev) 670 hg.clean(repo, urev)
653 repo.dirstate.write() 671 repo.dirstate.write()
654 672
655 self.removeundo(repo) 673 self.removeundo(repo)
656 repair.strip(self.ui, repo, rev, backup) 674 repair.strip(self.ui, repo, rev, backup)
675 # strip may have unbundled a set of backed up revisions after
676 # the actual strip
677 self.removeundo(repo)
657 finally: 678 finally:
658 del lock, wlock 679 del lock, wlock
659 680
660 def isapplied(self, patch): 681 def isapplied(self, patch):
661 """returns (index, rev, patch)""" 682 """returns (index, rev, patch)"""
808 return ret[0] 829 return ret[0]
809 finally: 830 finally:
810 del wlock 831 del wlock
811 832
812 def pop(self, repo, patch=None, force=False, update=True, all=False): 833 def pop(self, repo, patch=None, force=False, update=True, all=False):
813 def getfile(f, rev): 834 def getfile(f, rev, flags):
814 t = repo.file(f).read(rev) 835 t = repo.file(f).read(rev)
815 repo.wfile(f, "w").write(t) 836 repo.wwrite(f, t, flags)
816 837
817 wlock = repo.wlock() 838 wlock = repo.wlock()
818 try: 839 try:
819 if patch: 840 if patch:
820 # index, rev, patch 841 # index, rev, patch
857 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name] 878 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
858 879
859 start = info[0] 880 start = info[0]
860 rev = revlog.bin(info[1]) 881 rev = revlog.bin(info[1])
861 882
883 if update:
884 top = self.check_toppatch(repo)
885
886 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
887 raise util.Abort("popping would remove a revision not "
888 "managed by this patch queue")
889
862 # we know there are no local changes, so we can make a simplified 890 # we know there are no local changes, so we can make a simplified
863 # form of hg.update. 891 # form of hg.update.
864 if update: 892 if update:
865 top = self.check_toppatch(repo)
866 qp = self.qparents(repo, rev) 893 qp = self.qparents(repo, rev)
867 changes = repo.changelog.read(qp) 894 changes = repo.changelog.read(qp)
868 mmap = repo.manifest.read(changes[0]) 895 mmap = repo.manifest.read(changes[0])
869 m, a, r, d, u = repo.status(qp, top)[:5] 896 m, a, r, d, u = repo.status(qp, top)[:5]
870 if d: 897 if d:
871 raise util.Abort("deletions found between repo revs") 898 raise util.Abort("deletions found between repo revs")
872 for f in m: 899 for f in m:
873 getfile(f, mmap[f]) 900 getfile(f, mmap[f], mmap.flags(f))
874 for f in r: 901 for f in r:
875 getfile(f, mmap[f]) 902 getfile(f, mmap[f], mmap.flags(f))
876 util.set_exec(repo.wjoin(f), mmap.execf(f))
877 for f in m + r: 903 for f in m + r:
878 repo.dirstate.normal(f) 904 repo.dirstate.normal(f)
879 for f in a: 905 for f in a:
880 try: 906 try:
881 os.unlink(repo.wjoin(f)) 907 os.unlink(repo.wjoin(f))
884 raise 910 raise
885 try: os.removedirs(os.path.dirname(repo.wjoin(f))) 911 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
886 except: pass 912 except: pass
887 repo.dirstate.forget(f) 913 repo.dirstate.forget(f)
888 repo.dirstate.setparents(qp, revlog.nullid) 914 repo.dirstate.setparents(qp, revlog.nullid)
915 del self.applied[start:end]
889 self.strip(repo, rev, update=False, backup='strip') 916 self.strip(repo, rev, update=False, backup='strip')
890 del self.applied[start:end]
891 if len(self.applied): 917 if len(self.applied):
892 self.ui.write("Now at: %s\n" % self.applied[-1].name) 918 self.ui.write("Now at: %s\n" % self.applied[-1].name)
893 else: 919 else:
894 self.ui.write("Patch queue now empty\n") 920 self.ui.write("Patch queue now empty\n")
895 finally: 921 finally:
912 wlock = repo.wlock() 938 wlock = repo.wlock()
913 try: 939 try:
914 self.check_toppatch(repo) 940 self.check_toppatch(repo)
915 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name) 941 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
916 top = revlog.bin(top) 942 top = revlog.bin(top)
943 if repo.changelog.heads(top) != [top]:
944 raise util.Abort("cannot refresh a revision with children")
917 cparents = repo.changelog.parents(top) 945 cparents = repo.changelog.parents(top)
918 patchparent = self.qparents(repo, top) 946 patchparent = self.qparents(repo, top)
919 message, comments, user, date, patchfound = self.readheaders(patchfn) 947 message, comments, user, date, patchfound = self.readheaders(patchfn)
920 948
921 patchf = self.opener(patchfn, 'r+') 949 patchf = self.opener(patchfn, 'r+')
923 # if the patch was a git patch, refresh it as a git patch 951 # if the patch was a git patch, refresh it as a git patch
924 for line in patchf: 952 for line in patchf:
925 if line.startswith('diff --git'): 953 if line.startswith('diff --git'):
926 self.diffopts().git = True 954 self.diffopts().git = True
927 break 955 break
956
957 msg = opts.get('msg', '').rstrip()
958 if msg and comments:
959 # Remove existing message, keeping the rest of the comments
960 # fields.
961 # If comments contains 'subject: ', message will prepend
962 # the field and a blank line.
963 if message:
964 subj = 'subject: ' + message[0].lower()
965 for i in xrange(len(comments)):
966 if subj == comments[i].lower():
967 del comments[i]
968 message = message[2:]
969 break
970 ci = 0
971 for mi in xrange(len(message)):
972 while message[mi] != comments[ci]:
973 ci += 1
974 del comments[ci]
975
976 def setheaderfield(comments, prefixes, new):
977 # Update all references to a field in the patch header.
978 # If none found, add it email style.
979 res = False
980 for prefix in prefixes:
981 for i in xrange(len(comments)):
982 if comments[i].startswith(prefix):
983 comments[i] = prefix + new
984 res = True
985 break
986 return res
987
988 newuser = opts.get('user')
989 if newuser:
990 if not setheaderfield(comments, ['From: ', '# User '], newuser):
991 try:
992 patchheaderat = comments.index('# HG changeset patch')
993 comments.insert(patchheaderat + 1,'# User ' + newuser)
994 except ValueError:
995 comments = ['From: ' + newuser, ''] + comments
996 user = newuser
997
998 newdate = opts.get('date')
999 if newdate:
1000 if setheaderfield(comments, ['# Date '], newdate):
1001 date = newdate
1002
1003 if msg:
1004 comments.append(msg)
1005
928 patchf.seek(0) 1006 patchf.seek(0)
929 patchf.truncate() 1007 patchf.truncate()
930 1008
931 msg = opts.get('msg', '').rstrip()
932 if msg:
933 if comments:
934 # Remove existing message.
935 ci = 0
936 subj = None
937 for mi in xrange(len(message)):
938 if comments[ci].lower().startswith('subject: '):
939 subj = comments[ci][9:]
940 while message[mi] != comments[ci] and message[mi] != subj:
941 ci += 1
942 del comments[ci]
943 comments.append(msg)
944 if comments: 1009 if comments:
945 comments = "\n".join(comments) + '\n\n' 1010 comments = "\n".join(comments) + '\n\n'
946 patchf.write(comments) 1011 patchf.write(comments)
947 1012
948 if opts.get('git'): 1013 if opts.get('git'):
1015 1080
1016 repo.dirstate.setparents(*cparents) 1081 repo.dirstate.setparents(*cparents)
1017 copies = {} 1082 copies = {}
1018 for dst in a: 1083 for dst in a:
1019 src = repo.dirstate.copied(dst) 1084 src = repo.dirstate.copied(dst)
1020 if src is None: 1085 if src is not None:
1021 continue 1086 copies.setdefault(src, []).append(dst)
1022 copies.setdefault(src, []).append(dst)
1023 repo.dirstate.add(dst) 1087 repo.dirstate.add(dst)
1024 # remember the copies between patchparent and tip 1088 # remember the copies between patchparent and tip
1025 # this may be slow, so don't do it if we're not tracking copies 1089 # this may be slow, so don't do it if we're not tracking copies
1026 if self.diffopts().git: 1090 if self.diffopts().git:
1027 for dst in aaa: 1091 for dst in aaa:
1047 mm.append(m[i]) 1111 mm.append(m[i])
1048 del m[i] 1112 del m[i]
1049 for f in m: 1113 for f in m:
1050 repo.dirstate.normal(f) 1114 repo.dirstate.normal(f)
1051 for f in mm: 1115 for f in mm:
1052 repo.dirstate.normaldirty(f) 1116 repo.dirstate.normallookup(f)
1053 for f in forget: 1117 for f in forget:
1054 repo.dirstate.forget(f) 1118 repo.dirstate.forget(f)
1055 1119
1056 if not msg: 1120 if not msg:
1057 if not message: 1121 if not message:
1059 else: 1123 else:
1060 message = "\n".join(message) 1124 message = "\n".join(message)
1061 else: 1125 else:
1062 message = msg 1126 message = msg
1063 1127
1128 if not user:
1129 user = changes[1]
1130
1131 self.applied.pop()
1132 self.applied_dirty = 1
1064 self.strip(repo, top, update=False, 1133 self.strip(repo, top, update=False,
1065 backup='strip') 1134 backup='strip')
1066 n = repo.commit(filelist, message, changes[1], match=matchfn, 1135 n = repo.commit(filelist, message, user, date, match=matchfn,
1067 force=1) 1136 force=1)
1068 self.applied[-1] = statusentry(revlog.hex(n), patchfn) 1137 self.applied.append(statusentry(revlog.hex(n), patchfn))
1069 self.applied_dirty = 1
1070 self.removeundo(repo) 1138 self.removeundo(repo)
1071 else: 1139 else:
1072 self.printdiff(repo, patchparent, fp=patchf) 1140 self.printdiff(repo, patchparent, fp=patchf)
1073 patchf.close() 1141 patchf.close()
1074 added = repo.status()[1] 1142 added = repo.status()[1]
1214 self.strip(repo, rev, update=update, backup='strip') 1282 self.strip(repo, rev, update=update, backup='strip')
1215 if qpp: 1283 if qpp:
1216 self.ui.warn("saved queue repository parents: %s %s\n" % 1284 self.ui.warn("saved queue repository parents: %s %s\n" %
1217 (hg.short(qpp[0]), hg.short(qpp[1]))) 1285 (hg.short(qpp[0]), hg.short(qpp[1])))
1218 if qupdate: 1286 if qupdate:
1219 print "queue directory updating" 1287 self.ui.status(_("queue directory updating\n"))
1220 r = self.qrepo() 1288 r = self.qrepo()
1221 if not r: 1289 if not r:
1222 self.ui.warn("Unable to load queue repository\n") 1290 self.ui.warn("Unable to load queue repository\n")
1223 return 1 1291 return 1
1224 hg.clean(r, qpp[0]) 1292 hg.clean(r, qpp[0])
1353 % (r, lastparent)) 1421 % (r, lastparent))
1354 lastparent = p1 1422 lastparent = p1
1355 1423
1356 if not patchname: 1424 if not patchname:
1357 patchname = normname('%d.diff' % r) 1425 patchname = normname('%d.diff' % r)
1426 self.check_reserved_name(patchname)
1358 checkseries(patchname) 1427 checkseries(patchname)
1359 checkfile(patchname) 1428 checkfile(patchname)
1360 self.full_series.insert(0, patchname) 1429 self.full_series.insert(0, patchname)
1361 1430
1362 patchf = self.opener(patchname, "w") 1431 patchf = self.opener(patchname, "w")
1375 if existing: 1444 if existing:
1376 if filename == '-': 1445 if filename == '-':
1377 raise util.Abort(_('-e is incompatible with import from -')) 1446 raise util.Abort(_('-e is incompatible with import from -'))
1378 if not patchname: 1447 if not patchname:
1379 patchname = normname(filename) 1448 patchname = normname(filename)
1449 self.check_reserved_name(patchname)
1380 if not os.path.isfile(self.join(patchname)): 1450 if not os.path.isfile(self.join(patchname)):
1381 raise util.Abort(_("patch %s does not exist") % patchname) 1451 raise util.Abort(_("patch %s does not exist") % patchname)
1382 else: 1452 else:
1383 try: 1453 try:
1384 if filename == '-': 1454 if filename == '-':
1389 text = file(filename).read() 1459 text = file(filename).read()
1390 except IOError: 1460 except IOError:
1391 raise util.Abort(_("unable to read %s") % patchname) 1461 raise util.Abort(_("unable to read %s") % patchname)
1392 if not patchname: 1462 if not patchname:
1393 patchname = normname(os.path.basename(filename)) 1463 patchname = normname(os.path.basename(filename))
1464 self.check_reserved_name(patchname)
1394 checkfile(patchname) 1465 checkfile(patchname)
1395 patchf = self.opener(patchname, "w") 1466 patchf = self.opener(patchname, "w")
1396 patchf.write(text) 1467 patchf.write(text)
1397 checkseries(patchname) 1468 checkseries(patchname)
1398 index = self.full_series_end() + i 1469 index = self.full_series_end() + i
1506 default. Use -p <url> to change. 1577 default. Use -p <url> to change.
1507 1578
1508 The patch directory must be a nested mercurial repository, as 1579 The patch directory must be a nested mercurial repository, as
1509 would be created by qinit -c. 1580 would be created by qinit -c.
1510 ''' 1581 '''
1582 def patchdir(repo):
1583 url = repo.url()
1584 if url.endswith('/'):
1585 url = url[:-1]
1586 return url + '/.hg/patches'
1511 cmdutil.setremoteconfig(ui, opts) 1587 cmdutil.setremoteconfig(ui, opts)
1512 if dest is None: 1588 if dest is None:
1513 dest = hg.defaultdest(source) 1589 dest = hg.defaultdest(source)
1514 sr = hg.repository(ui, ui.expandpath(source)) 1590 sr = hg.repository(ui, ui.expandpath(source))
1515 patchdir = opts['patches'] or (sr.url() + '/.hg/patches') 1591 patchespath = opts['patches'] or patchdir(sr)
1516 try: 1592 try:
1517 pr = hg.repository(ui, patchdir) 1593 pr = hg.repository(ui, patchespath)
1518 except hg.RepoError: 1594 except hg.RepoError:
1519 raise util.Abort(_('versioned patch repository not found' 1595 raise util.Abort(_('versioned patch repository not found'
1520 ' (see qinit -c)')) 1596 ' (see qinit -c)'))
1521 qbase, destrev = None, None 1597 qbase, destrev = None, None
1522 if sr.local(): 1598 if sr.local():
1533 pull=opts['pull'], 1609 pull=opts['pull'],
1534 rev=destrev, 1610 rev=destrev,
1535 update=False, 1611 update=False,
1536 stream=opts['uncompressed']) 1612 stream=opts['uncompressed'])
1537 ui.note(_('cloning patch repo\n')) 1613 ui.note(_('cloning patch repo\n'))
1538 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'), 1614 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1539 dr.url() + '/.hg/patches', 1615 pull=opts['pull'], update=not opts['noupdate'],
1540 pull=opts['pull'],
1541 update=not opts['noupdate'],
1542 stream=opts['uncompressed']) 1616 stream=opts['uncompressed'])
1543 if dr.local(): 1617 if dr.local():
1544 if qbase: 1618 if qbase:
1545 ui.note(_('stripping applied patches from destination repo\n')) 1619 ui.note(_('stripping applied patches from destination repo\n'))
1546 dr.mq.strip(dr, qbase, update=False, backup=None) 1620 dr.mq.strip(dr, qbase, update=False, backup=None)
1591 ui.write("No patches applied\n") 1665 ui.write("No patches applied\n")
1592 return 1 1666 return 1
1593 return q.qseries(repo, start=l-2, length=1, status='A', 1667 return q.qseries(repo, start=l-2, length=1, status='A',
1594 summary=opts.get('summary')) 1668 summary=opts.get('summary'))
1595 1669
1670 def setupheaderopts(ui, opts):
1671 def do(opt,val):
1672 if not opts[opt] and opts['current' + opt]:
1673 opts[opt] = val
1674 do('user', ui.username())
1675 do('date', "%d %d" % util.makedate())
1676
1596 def new(ui, repo, patch, *args, **opts): 1677 def new(ui, repo, patch, *args, **opts):
1597 """create a new patch 1678 """create a new patch
1598 1679
1599 qnew creates a new patch on top of the currently-applied patch 1680 qnew creates a new patch on top of the currently-applied patch
1600 (if any). It will refuse to run if there are any outstanding 1681 (if any). It will refuse to run if there are any outstanding
1609 q = repo.mq 1690 q = repo.mq
1610 message = cmdutil.logmessage(opts) 1691 message = cmdutil.logmessage(opts)
1611 if opts['edit']: 1692 if opts['edit']:
1612 message = ui.edit(message, ui.username()) 1693 message = ui.edit(message, ui.username())
1613 opts['msg'] = message 1694 opts['msg'] = message
1695 setupheaderopts(ui, opts)
1614 q.new(repo, patch, *args, **opts) 1696 q.new(repo, patch, *args, **opts)
1615 q.save_dirty() 1697 q.save_dirty()
1616 return 0 1698 return 0
1617 1699
1618 def refresh(ui, repo, *pats, **opts): 1700 def refresh(ui, repo, *pats, **opts):
1626 git-style patches (--git or [diff] git=1) to track copies and renames. 1708 git-style patches (--git or [diff] git=1) to track copies and renames.
1627 """ 1709 """
1628 q = repo.mq 1710 q = repo.mq
1629 message = cmdutil.logmessage(opts) 1711 message = cmdutil.logmessage(opts)
1630 if opts['edit']: 1712 if opts['edit']:
1713 if not q.applied:
1714 ui.write(_("No patches applied\n"))
1715 return 1
1631 if message: 1716 if message:
1632 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"')) 1717 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1633 patch = q.applied[-1].name 1718 patch = q.applied[-1].name
1634 (message, comment, user, date, hasdiff) = q.readheaders(patch) 1719 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1635 message = ui.edit('\n'.join(message), user or ui.username()) 1720 message = ui.edit('\n'.join(message), user or ui.username())
1721 setupheaderopts(ui, opts)
1636 ret = q.refresh(repo, pats, msg=message, **opts) 1722 ret = q.refresh(repo, pats, msg=message, **opts)
1637 q.save_dirty() 1723 q.save_dirty()
1638 return ret 1724 return ret
1639 1725
1640 def diff(ui, repo, *pats, **opts): 1726 def diff(ui, repo, *pats, **opts):
2079 q = self.mq 2165 q = self.mq
2080 if not q.applied: 2166 if not q.applied:
2081 return tagscache 2167 return tagscache
2082 2168
2083 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied] 2169 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2170
2171 if mqtags[-1][0] not in self.changelog.nodemap:
2172 self.ui.warn('mq status file refers to unknown node %s\n'
2173 % revlog.short(mqtags[-1][0]))
2174 return tagscache
2175
2084 mqtags.append((mqtags[-1][0], 'qtip')) 2176 mqtags.append((mqtags[-1][0], 'qtip'))
2085 mqtags.append((mqtags[0][0], 'qbase')) 2177 mqtags.append((mqtags[0][0], 'qbase'))
2086 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent')) 2178 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2087 for patch in mqtags: 2179 for patch in mqtags:
2088 if patch[1] in tagscache: 2180 if patch[1] in tagscache:
2095 def _branchtags(self): 2187 def _branchtags(self):
2096 q = self.mq 2188 q = self.mq
2097 if not q.applied: 2189 if not q.applied:
2098 return super(mqrepo, self)._branchtags() 2190 return super(mqrepo, self)._branchtags()
2099 2191
2192 cl = self.changelog
2193 qbasenode = revlog.bin(q.applied[0].rev)
2194 if qbasenode not in cl.nodemap:
2195 self.ui.warn('mq status file refers to unknown node %s\n'
2196 % revlog.short(qbasenode))
2197 return super(mqrepo, self)._branchtags()
2198
2100 self.branchcache = {} # avoid recursion in changectx 2199 self.branchcache = {} # avoid recursion in changectx
2101 cl = self.changelog
2102 partial, last, lrev = self._readbranchcache() 2200 partial, last, lrev = self._readbranchcache()
2103 2201
2104 qbase = cl.rev(revlog.bin(q.applied[0].rev)) 2202 qbase = cl.rev(qbasenode)
2105 start = lrev + 1 2203 start = lrev + 1
2106 if start < qbase: 2204 if start < qbase:
2107 # update the cache (excluding the patches) and save it 2205 # update the cache (excluding the patches) and save it
2108 self._updatebranchcache(partial, lrev+1, qbase) 2206 self._updatebranchcache(partial, lrev+1, qbase)
2109 self._writebranchcache(partial, cl.node(qbase-1), qbase-1) 2207 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2121 repo.__class__ = mqrepo 2219 repo.__class__ = mqrepo
2122 repo.mq = queue(ui, repo.join("")) 2220 repo.mq = queue(ui, repo.join(""))
2123 2221
2124 seriesopts = [('s', 'summary', None, _('print first line of patch header'))] 2222 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2125 2223
2224 headeropts = [
2225 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2226 ('u', 'user', '', _('add "From: <given user>" to patch')),
2227 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2228 ('d', 'date', '', _('add "Date: <given date>" to patch'))]
2229
2126 cmdtable = { 2230 cmdtable = {
2127 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')), 2231 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2128 "qclone": 2232 "qclone":
2129 (clone, 2233 (clone,
2130 [('', 'pull', None, _('use pull protocol to copy metadata')), 2234 [('', 'pull', None, _('use pull protocol to copy metadata')),
2131 ('U', 'noupdate', None, _('do not update the new working directories')), 2235 ('U', 'noupdate', None, _('do not update the new working directories')),
2132 ('', 'uncompressed', None, 2236 ('', 'uncompressed', None,
2133 _('use uncompressed transfer (fast over LAN)')), 2237 _('use uncompressed transfer (fast over LAN)')),
2134 ('e', 'ssh', '', _('specify ssh command to use')),
2135 ('p', 'patches', '', _('location of source patch repo')), 2238 ('p', 'patches', '', _('location of source patch repo')),
2136 ('', 'remotecmd', '', 2239 ] + commands.remoteopts,
2137 _('specify hg command to run on the remote side'))],
2138 _('hg qclone [OPTION]... SOURCE [DEST]')), 2240 _('hg qclone [OPTION]... SOURCE [DEST]')),
2139 "qcommit|qci": 2241 "qcommit|qci":
2140 (commit, 2242 (commit,
2141 commands.table["^commit|ci"][1], 2243 commands.table["^commit|ci"][1],
2142 _('hg qcommit [OPTION]... [FILE]...')), 2244 _('hg qcommit [OPTION]... [FILE]...')),
2143 "^qdiff": 2245 "^qdiff":
2144 (diff, 2246 (diff,
2145 [('g', 'git', None, _('use git extended diff format')), 2247 [('g', 'git', None, _('use git extended diff format')),
2146 ('I', 'include', [], _('include names matching the given patterns')), 2248 ('U', 'unified', 3, _('number of lines of context to show')),
2147 ('X', 'exclude', [], _('exclude names matching the given patterns')), 2249 ] + commands.walkopts,
2148 ('U', 'unified', 3, _('number of lines of context to show'))],
2149 _('hg qdiff [-I] [-X] [-U NUM] [-g] [FILE]...')), 2250 _('hg qdiff [-I] [-X] [-U NUM] [-g] [FILE]...')),
2150 "qdelete|qremove|qrm": 2251 "qdelete|qremove|qrm":
2151 (delete, 2252 (delete,
2152 [('k', 'keep', None, _('keep patch file')), 2253 [('k', 'keep', None, _('keep patch file')),
2153 ('r', 'rev', [], _('stop managing a revision'))], 2254 ('r', 'rev', [], _('stop managing a revision'))],
2182 _('hg qinit [-c]')), 2283 _('hg qinit [-c]')),
2183 "qnew": 2284 "qnew":
2184 (new, 2285 (new,
2185 [('e', 'edit', None, _('edit commit message')), 2286 [('e', 'edit', None, _('edit commit message')),
2186 ('f', 'force', None, _('import uncommitted changes into patch')), 2287 ('f', 'force', None, _('import uncommitted changes into patch')),
2187 ('I', 'include', [], _('include names matching the given patterns')), 2288 ('g', 'git', None, _('use git extended diff format')),
2188 ('X', 'exclude', [], _('exclude names matching the given patterns')), 2289 ] + commands.walkopts + commands.commitopts + headeropts,
2189 ] + commands.commitopts,
2190 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')), 2290 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2191 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')), 2291 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2192 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')), 2292 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2193 "^qpop": 2293 "^qpop":
2194 (pop, 2294 (pop,
2207 "^qrefresh": 2307 "^qrefresh":
2208 (refresh, 2308 (refresh,
2209 [('e', 'edit', None, _('edit commit message')), 2309 [('e', 'edit', None, _('edit commit message')),
2210 ('g', 'git', None, _('use git extended diff format')), 2310 ('g', 'git', None, _('use git extended diff format')),
2211 ('s', 'short', None, _('refresh only files already in the patch')), 2311 ('s', 'short', None, _('refresh only files already in the patch')),
2212 ('I', 'include', [], _('include names matching the given patterns')), 2312 ] + commands.walkopts + commands.commitopts + headeropts,
2213 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2214 ] + commands.commitopts,
2215 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')), 2313 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2216 'qrename|qmv': 2314 'qrename|qmv':
2217 (rename, [], _('hg qrename PATCH1 [PATCH2]')), 2315 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2218 "qrestore": 2316 "qrestore":
2219 (restore, 2317 (restore,