Mercurial > hg
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, |