Mercurial > hg > mercurial-crew-with-dirclash
comparison hgext/mq.py @ 4959:97b734fb9c6f
Use try/finally pattern to cleanup locks and transactions
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Sat, 21 Jul 2007 16:02:10 -0500 |
parents | 30847b8af7ca |
children | 126f527b3ba3 |
comparison
equal
deleted
inserted
replaced
4958:9a2a73ea6135 | 4959:97b734fb9c6f |
---|---|
437 return (True, files, fuzz) | 437 return (True, files, fuzz) |
438 | 438 |
439 def apply(self, repo, series, list=False, update_status=True, | 439 def apply(self, repo, series, list=False, update_status=True, |
440 strict=False, patchdir=None, merge=None, wlock=None, | 440 strict=False, patchdir=None, merge=None, wlock=None, |
441 all_files={}): | 441 all_files={}): |
442 if not wlock: | 442 lock = tr = None |
443 wlock = repo.wlock() | |
444 lock = repo.lock() | |
445 tr = repo.transaction() | |
446 try: | 443 try: |
447 ret = self._apply(tr, repo, series, list, update_status, | 444 if not wlock: |
448 strict, patchdir, merge, wlock, | 445 wlock = repo.wlock() |
449 lock=lock, all_files=all_files) | 446 lock = repo.lock() |
450 tr.close() | 447 tr = repo.transaction() |
451 self.save_dirty() | |
452 return ret | |
453 except: | |
454 try: | 448 try: |
455 tr.abort() | 449 ret = self._apply(tr, repo, series, list, update_status, |
456 finally: | 450 strict, patchdir, merge, wlock, |
457 repo.invalidate() | 451 lock=lock, all_files=all_files) |
458 repo.dirstate.invalidate() | 452 tr.close() |
459 raise | 453 self.save_dirty() |
454 return ret | |
455 except: | |
456 try: | |
457 tr.abort() | |
458 finally: | |
459 repo.invalidate() | |
460 repo.dirstate.invalidate() | |
461 raise | |
462 finally: | |
463 del lock, wlock, tr | |
460 | 464 |
461 def _apply(self, tr, repo, series, list=False, update_status=True, | 465 def _apply(self, tr, repo, series, list=False, update_status=True, |
462 strict=False, patchdir=None, merge=None, wlock=None, | 466 strict=False, patchdir=None, merge=None, wlock=None, |
463 lock=None, all_files={}): | 467 lock=None, all_files={}): |
464 # TODO unify with commands.py | 468 # TODO unify with commands.py |
614 else: | 618 else: |
615 m, a, r, d = self.check_localchanges(repo, force) | 619 m, a, r, d = self.check_localchanges(repo, force) |
616 commitfiles = m + a + r | 620 commitfiles = m + a + r |
617 self.check_toppatch(repo) | 621 self.check_toppatch(repo) |
618 wlock = repo.wlock() | 622 wlock = repo.wlock() |
619 insert = self.full_series_end() | 623 try: |
620 if msg: | 624 insert = self.full_series_end() |
621 n = repo.commit(commitfiles, msg, force=True, wlock=wlock) | 625 if msg: |
622 else: | 626 n = repo.commit(commitfiles, msg, force=True, wlock=wlock) |
623 n = repo.commit(commitfiles, | 627 else: |
624 "[mq]: %s" % patch, force=True, wlock=wlock) | 628 n = repo.commit(commitfiles, |
625 if n == None: | 629 "[mq]: %s" % patch, force=True, wlock=wlock) |
626 raise util.Abort(_("repo commit failed")) | 630 if n == None: |
627 self.full_series[insert:insert] = [patch] | 631 raise util.Abort(_("repo commit failed")) |
628 self.applied.append(statusentry(revlog.hex(n), patch)) | 632 self.full_series[insert:insert] = [patch] |
629 self.parse_series() | 633 self.applied.append(statusentry(revlog.hex(n), patch)) |
630 self.series_dirty = 1 | 634 self.parse_series() |
631 self.applied_dirty = 1 | 635 self.series_dirty = 1 |
632 p = self.opener(patch, "w") | 636 self.applied_dirty = 1 |
633 if msg: | 637 p = self.opener(patch, "w") |
634 msg = msg + "\n" | 638 if msg: |
635 p.write(msg) | 639 msg = msg + "\n" |
636 p.close() | 640 p.write(msg) |
637 wlock = None | 641 p.close() |
638 r = self.qrepo() | 642 wlock = None |
639 if r: r.add([patch]) | 643 r = self.qrepo() |
640 if commitfiles: | 644 if r: r.add([patch]) |
641 self.refresh(repo, short=True) | 645 if commitfiles: |
642 self.removeundo(repo) | 646 self.refresh(repo, short=True) |
647 self.removeundo(repo) | |
648 finally: | |
649 del wlock | |
643 | 650 |
644 def strip(self, repo, rev, update=True, backup="all", wlock=None): | 651 def strip(self, repo, rev, update=True, backup="all", wlock=None): |
645 if not wlock: | 652 lock = None |
646 wlock = repo.wlock() | 653 try: |
647 lock = repo.lock() | 654 if not wlock: |
648 | 655 wlock = repo.wlock() |
649 if update: | 656 lock = repo.lock() |
650 self.check_localchanges(repo, refresh=False) | 657 |
651 urev = self.qparents(repo, rev) | 658 if update: |
652 hg.clean(repo, urev, wlock=wlock) | 659 self.check_localchanges(repo, refresh=False) |
653 repo.dirstate.write() | 660 urev = self.qparents(repo, rev) |
654 | 661 hg.clean(repo, urev, wlock=wlock) |
655 self.removeundo(repo) | 662 repo.dirstate.write() |
656 repair.strip(self.ui, repo, rev, backup) | 663 |
664 self.removeundo(repo) | |
665 repair.strip(self.ui, repo, rev, backup) | |
666 finally: | |
667 del lock, wlock | |
657 | 668 |
658 def isapplied(self, patch): | 669 def isapplied(self, patch): |
659 """returns (index, rev, patch)""" | 670 """returns (index, rev, patch)""" |
660 for i in xrange(len(self.applied)): | 671 for i in xrange(len(self.applied)): |
661 a = self.applied[i] | 672 a = self.applied[i] |
738 | 749 |
739 def push(self, repo, patch=None, force=False, list=False, | 750 def push(self, repo, patch=None, force=False, list=False, |
740 mergeq=None, wlock=None): | 751 mergeq=None, wlock=None): |
741 if not wlock: | 752 if not wlock: |
742 wlock = repo.wlock() | 753 wlock = repo.wlock() |
743 patch = self.lookup(patch) | 754 try: |
744 # Suppose our series file is: A B C and the current 'top' patch is B. | 755 patch = self.lookup(patch) |
745 # qpush C should be performed (moving forward) | 756 # Suppose our series file is: A B C and the current 'top' |
746 # qpush B is a NOP (no change) | 757 # patch is B. qpush C should be performed (moving forward) |
747 # qpush A is an error (can't go backwards with qpush) | 758 # qpush B is a NOP (no change) qpush A is an error (can't |
748 if patch: | 759 # go backwards with qpush) |
749 info = self.isapplied(patch) | 760 if patch: |
750 if info: | 761 info = self.isapplied(patch) |
751 if info[0] < len(self.applied) - 1: | 762 if info: |
752 raise util.Abort(_("cannot push to a previous patch: %s") % | 763 if info[0] < len(self.applied) - 1: |
753 patch) | 764 raise util.Abort( |
754 if info[0] < len(self.series) - 1: | 765 _("cannot push to a previous patch: %s") % patch) |
755 self.ui.warn(_('qpush: %s is already at the top\n') % patch) | 766 if info[0] < len(self.series) - 1: |
767 self.ui.warn( | |
768 _('qpush: %s is already at the top\n') % patch) | |
769 else: | |
770 self.ui.warn(_('all patches are currently applied\n')) | |
771 return | |
772 | |
773 # Following the above example, starting at 'top' of B: | |
774 # qpush should be performed (pushes C), but a subsequent | |
775 # qpush without an argument is an error (nothing to | |
776 # apply). This allows a loop of "...while hg qpush..." to | |
777 # work as it detects an error when done | |
778 if self.series_end() == len(self.series): | |
779 self.ui.warn(_('patch series already fully applied\n')) | |
780 return 1 | |
781 if not force: | |
782 self.check_localchanges(repo) | |
783 | |
784 self.applied_dirty = 1; | |
785 start = self.series_end() | |
786 if start > 0: | |
787 self.check_toppatch(repo) | |
788 if not patch: | |
789 patch = self.series[start] | |
790 end = start + 1 | |
791 else: | |
792 end = self.series.index(patch, start) + 1 | |
793 s = self.series[start:end] | |
794 all_files = {} | |
795 try: | |
796 if mergeq: | |
797 ret = self.mergepatch(repo, mergeq, s, wlock) | |
756 else: | 798 else: |
757 self.ui.warn(_('all patches are currently applied\n')) | 799 ret = self.apply(repo, s, list, wlock=wlock, |
758 return | 800 all_files=all_files) |
759 | 801 except: |
760 # Following the above example, starting at 'top' of B: | 802 self.ui.warn(_('cleaning up working directory...')) |
761 # qpush should be performed (pushes C), but a subsequent qpush without | 803 node = repo.dirstate.parents()[0] |
762 # an argument is an error (nothing to apply). This allows a loop | 804 hg.revert(repo, node, None, wlock) |
763 # of "...while hg qpush..." to work as it detects an error when done | 805 unknown = repo.status(wlock=wlock)[4] |
764 if self.series_end() == len(self.series): | 806 # only remove unknown files that we know we touched or |
765 self.ui.warn(_('patch series already fully applied\n')) | 807 # created while patching |
766 return 1 | 808 for f in unknown: |
767 if not force: | 809 if f in all_files: |
768 self.check_localchanges(repo) | 810 util.unlink(repo.wjoin(f)) |
769 | 811 self.ui.warn(_('done\n')) |
770 self.applied_dirty = 1; | 812 raise |
771 start = self.series_end() | 813 top = self.applied[-1].name |
772 if start > 0: | 814 if ret[0]: |
773 self.check_toppatch(repo) | 815 self.ui.write( |
774 if not patch: | 816 "Errors during apply, please fix and refresh %s\n" % top) |
775 patch = self.series[start] | |
776 end = start + 1 | |
777 else: | |
778 end = self.series.index(patch, start) + 1 | |
779 s = self.series[start:end] | |
780 all_files = {} | |
781 try: | |
782 if mergeq: | |
783 ret = self.mergepatch(repo, mergeq, s, wlock) | |
784 else: | 817 else: |
785 ret = self.apply(repo, s, list, wlock=wlock, | 818 self.ui.write("Now at: %s\n" % top) |
786 all_files=all_files) | 819 return ret[0] |
787 except: | 820 finally: |
788 self.ui.warn(_('cleaning up working directory...')) | 821 del wlock |
789 node = repo.dirstate.parents()[0] | |
790 hg.revert(repo, node, None, wlock) | |
791 unknown = repo.status(wlock=wlock)[4] | |
792 # only remove unknown files that we know we touched or | |
793 # created while patching | |
794 for f in unknown: | |
795 if f in all_files: | |
796 util.unlink(repo.wjoin(f)) | |
797 self.ui.warn(_('done\n')) | |
798 raise | |
799 top = self.applied[-1].name | |
800 if ret[0]: | |
801 self.ui.write("Errors during apply, please fix and refresh %s\n" % | |
802 top) | |
803 else: | |
804 self.ui.write("Now at: %s\n" % top) | |
805 return ret[0] | |
806 | 822 |
807 def pop(self, repo, patch=None, force=False, update=True, all=False, | 823 def pop(self, repo, patch=None, force=False, update=True, all=False, |
808 wlock=None): | 824 wlock=None): |
809 def getfile(f, rev): | 825 def getfile(f, rev): |
810 t = repo.file(f).read(rev) | 826 t = repo.file(f).read(rev) |
811 repo.wfile(f, "w").write(t) | 827 repo.wfile(f, "w").write(t) |
812 | 828 |
813 if not wlock: | 829 if not wlock: |
814 wlock = repo.wlock() | 830 wlock = repo.wlock() |
815 if patch: | 831 try: |
816 # index, rev, patch | 832 if patch: |
817 info = self.isapplied(patch) | 833 # index, rev, patch |
818 if not info: | 834 info = self.isapplied(patch) |
819 patch = self.lookup(patch) | 835 if not info: |
820 info = self.isapplied(patch) | 836 patch = self.lookup(patch) |
821 if not info: | 837 info = self.isapplied(patch) |
822 raise util.Abort(_("patch %s is not applied") % patch) | 838 if not info: |
823 | 839 raise util.Abort(_("patch %s is not applied") % patch) |
824 if len(self.applied) == 0: | 840 |
825 # Allow qpop -a to work repeatedly, | 841 if len(self.applied) == 0: |
826 # but not qpop without an argument | 842 # Allow qpop -a to work repeatedly, |
827 self.ui.warn(_("no patches applied\n")) | 843 # but not qpop without an argument |
828 return not all | 844 self.ui.warn(_("no patches applied\n")) |
829 | 845 return not all |
830 if not update: | 846 |
831 parents = repo.dirstate.parents() | 847 if not update: |
832 rr = [ revlog.bin(x.rev) for x in self.applied ] | 848 parents = repo.dirstate.parents() |
833 for p in parents: | 849 rr = [ revlog.bin(x.rev) for x in self.applied ] |
834 if p in rr: | 850 for p in parents: |
835 self.ui.warn("qpop: forcing dirstate update\n") | 851 if p in rr: |
836 update = True | 852 self.ui.warn("qpop: forcing dirstate update\n") |
837 | 853 update = True |
838 if not force and update: | 854 |
839 self.check_localchanges(repo) | 855 if not force and update: |
840 | 856 self.check_localchanges(repo) |
841 self.applied_dirty = 1; | 857 |
842 end = len(self.applied) | 858 self.applied_dirty = 1; |
843 if not patch: | 859 end = len(self.applied) |
844 if all: | 860 if not patch: |
845 popi = 0 | 861 if all: |
862 popi = 0 | |
863 else: | |
864 popi = len(self.applied) - 1 | |
846 else: | 865 else: |
847 popi = len(self.applied) - 1 | 866 popi = info[0] + 1 |
848 else: | 867 if popi >= end: |
849 popi = info[0] + 1 | 868 self.ui.warn("qpop: %s is already at the top\n" % patch) |
850 if popi >= end: | 869 return |
851 self.ui.warn("qpop: %s is already at the top\n" % patch) | 870 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name] |
852 return | 871 |
853 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name] | 872 start = info[0] |
854 | 873 rev = revlog.bin(info[1]) |
855 start = info[0] | 874 |
856 rev = revlog.bin(info[1]) | 875 # we know there are no local changes, so we can make a simplified |
857 | 876 # form of hg.update. |
858 # we know there are no local changes, so we can make a simplified | 877 if update: |
859 # form of hg.update. | 878 top = self.check_toppatch(repo) |
860 if update: | 879 qp = self.qparents(repo, rev) |
861 top = self.check_toppatch(repo) | 880 changes = repo.changelog.read(qp) |
862 qp = self.qparents(repo, rev) | 881 mmap = repo.manifest.read(changes[0]) |
863 changes = repo.changelog.read(qp) | 882 m, a, r, d, u = repo.status(qp, top)[:5] |
864 mmap = repo.manifest.read(changes[0]) | 883 if d: |
865 m, a, r, d, u = repo.status(qp, top)[:5] | 884 raise util.Abort("deletions found between repo revs") |
866 if d: | 885 for f in m: |
867 raise util.Abort("deletions found between repo revs") | 886 getfile(f, mmap[f]) |
868 for f in m: | 887 for f in r: |
869 getfile(f, mmap[f]) | 888 getfile(f, mmap[f]) |
870 for f in r: | 889 util.set_exec(repo.wjoin(f), mmap.execf(f)) |
871 getfile(f, mmap[f]) | 890 for f in m + r: |
872 util.set_exec(repo.wjoin(f), mmap.execf(f)) | 891 repo.dirstate.normal(f) |
873 for f in m + r: | 892 for f in a: |
874 repo.dirstate.normal(f) | 893 try: |
875 for f in a: | 894 os.unlink(repo.wjoin(f)) |
876 try: | 895 except OSError, e: |
877 os.unlink(repo.wjoin(f)) | 896 if e.errno != errno.ENOENT: |
878 except OSError, e: | 897 raise |
879 if e.errno != errno.ENOENT: | 898 try: os.removedirs(os.path.dirname(repo.wjoin(f))) |
880 raise | 899 except: pass |
881 try: os.removedirs(os.path.dirname(repo.wjoin(f))) | 900 repo.dirstate.forget(f) |
882 except: pass | 901 repo.dirstate.setparents(qp, revlog.nullid) |
883 repo.dirstate.forget(f) | 902 self.strip(repo, rev, update=False, backup='strip', wlock=wlock) |
884 repo.dirstate.setparents(qp, revlog.nullid) | 903 del self.applied[start:end] |
885 self.strip(repo, rev, update=False, backup='strip', wlock=wlock) | 904 if len(self.applied): |
886 del self.applied[start:end] | 905 self.ui.write("Now at: %s\n" % self.applied[-1].name) |
887 if len(self.applied): | 906 else: |
888 self.ui.write("Now at: %s\n" % self.applied[-1].name) | 907 self.ui.write("Patch queue now empty\n") |
889 else: | 908 finally: |
890 self.ui.write("Patch queue now empty\n") | 909 del wlock |
891 | 910 |
892 def diff(self, repo, pats, opts): | 911 def diff(self, repo, pats, opts): |
893 top = self.check_toppatch(repo) | 912 top = self.check_toppatch(repo) |
894 if not top: | 913 if not top: |
895 self.ui.write("No patches applied\n") | 914 self.ui.write("No patches applied\n") |
902 def refresh(self, repo, pats=None, **opts): | 921 def refresh(self, repo, pats=None, **opts): |
903 if len(self.applied) == 0: | 922 if len(self.applied) == 0: |
904 self.ui.write("No patches applied\n") | 923 self.ui.write("No patches applied\n") |
905 return 1 | 924 return 1 |
906 wlock = repo.wlock() | 925 wlock = repo.wlock() |
907 self.check_toppatch(repo) | 926 try: |
908 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name) | 927 self.check_toppatch(repo) |
909 top = revlog.bin(top) | 928 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name) |
910 cparents = repo.changelog.parents(top) | 929 top = revlog.bin(top) |
911 patchparent = self.qparents(repo, top) | 930 cparents = repo.changelog.parents(top) |
912 message, comments, user, date, patchfound = self.readheaders(patchfn) | 931 patchparent = self.qparents(repo, top) |
913 | 932 message, comments, user, date, patchfound = self.readheaders(patchfn) |
914 patchf = self.opener(patchfn, 'r+') | 933 |
915 | 934 patchf = self.opener(patchfn, 'r+') |
916 # if the patch was a git patch, refresh it as a git patch | 935 |
917 for line in patchf: | 936 # if the patch was a git patch, refresh it as a git patch |
918 if line.startswith('diff --git'): | 937 for line in patchf: |
938 if line.startswith('diff --git'): | |
939 self.diffopts().git = True | |
940 break | |
941 patchf.seek(0) | |
942 patchf.truncate() | |
943 | |
944 msg = opts.get('msg', '').rstrip() | |
945 if msg: | |
946 if comments: | |
947 # Remove existing message. | |
948 ci = 0 | |
949 subj = None | |
950 for mi in xrange(len(message)): | |
951 if comments[ci].lower().startswith('subject: '): | |
952 subj = comments[ci][9:] | |
953 while message[mi] != comments[ci] and message[mi] != subj: | |
954 ci += 1 | |
955 del comments[ci] | |
956 comments.append(msg) | |
957 if comments: | |
958 comments = "\n".join(comments) + '\n\n' | |
959 patchf.write(comments) | |
960 | |
961 if opts.get('git'): | |
919 self.diffopts().git = True | 962 self.diffopts().git = True |
920 break | 963 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) |
921 patchf.seek(0) | 964 tip = repo.changelog.tip() |
922 patchf.truncate() | 965 if top == tip: |
923 | 966 # if the top of our patch queue is also the tip, there is an |
924 msg = opts.get('msg', '').rstrip() | 967 # optimization here. We update the dirstate in place and strip |
925 if msg: | 968 # off the tip commit. Then just commit the current directory |
926 if comments: | 969 # tree. We can also send repo.commit the list of files |
927 # Remove existing message. | 970 # changed to speed up the diff |
928 ci = 0 | 971 # |
929 subj = None | 972 # in short mode, we only diff the files included in the |
930 for mi in xrange(len(message)): | 973 # patch already |
931 if comments[ci].lower().startswith('subject: '): | 974 # |
932 subj = comments[ci][9:] | 975 # this should really read: |
933 while message[mi] != comments[ci] and message[mi] != subj: | 976 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5] |
934 ci += 1 | 977 # but we do it backwards to take advantage of manifest/chlog |
935 del comments[ci] | 978 # caching against the next repo.status call |
936 comments.append(msg) | 979 # |
937 if comments: | 980 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5] |
938 comments = "\n".join(comments) + '\n\n' | 981 changes = repo.changelog.read(tip) |
939 patchf.write(comments) | 982 man = repo.manifest.read(changes[0]) |
940 | 983 aaa = aa[:] |
941 if opts.get('git'): | 984 if opts.get('short'): |
942 self.diffopts().git = True | 985 filelist = mm + aa + dd |
943 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) | 986 match = dict.fromkeys(filelist).__contains__ |
944 tip = repo.changelog.tip() | 987 else: |
945 if top == tip: | 988 filelist = None |
946 # if the top of our patch queue is also the tip, there is an | 989 match = util.always |
947 # optimization here. We update the dirstate in place and strip | 990 m, a, r, d, u = repo.status(files=filelist, match=match)[:5] |
948 # off the tip commit. Then just commit the current directory | 991 |
949 # tree. We can also send repo.commit the list of files | 992 # we might end up with files that were added between |
950 # changed to speed up the diff | 993 # tip and the dirstate parent, but then changed in the |
951 # | 994 # local dirstate. in this case, we want them to only |
952 # in short mode, we only diff the files included in the | 995 # show up in the added section |
953 # patch already | 996 for x in m: |
954 # | 997 if x not in aa: |
955 # this should really read: | 998 mm.append(x) |
956 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5] | 999 # we might end up with files added by the local dirstate that |
957 # but we do it backwards to take advantage of manifest/chlog | 1000 # were deleted by the patch. In this case, they should only |
958 # caching against the next repo.status call | 1001 # show up in the changed section. |
959 # | 1002 for x in a: |
960 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5] | 1003 if x in dd: |
961 changes = repo.changelog.read(tip) | 1004 del dd[dd.index(x)] |
962 man = repo.manifest.read(changes[0]) | 1005 mm.append(x) |
963 aaa = aa[:] | 1006 else: |
964 if opts.get('short'): | 1007 aa.append(x) |
965 filelist = mm + aa + dd | 1008 # make sure any files deleted in the local dirstate |
966 match = dict.fromkeys(filelist).__contains__ | 1009 # are not in the add or change column of the patch |
1010 forget = [] | |
1011 for x in d + r: | |
1012 if x in aa: | |
1013 del aa[aa.index(x)] | |
1014 forget.append(x) | |
1015 continue | |
1016 elif x in mm: | |
1017 del mm[mm.index(x)] | |
1018 dd.append(x) | |
1019 | |
1020 m = util.unique(mm) | |
1021 r = util.unique(dd) | |
1022 a = util.unique(aa) | |
1023 c = [filter(matchfn, l) for l in (m, a, r, [], u)] | |
1024 filelist = util.unique(c[0] + c[1] + c[2]) | |
1025 patch.diff(repo, patchparent, files=filelist, match=matchfn, | |
1026 fp=patchf, changes=c, opts=self.diffopts()) | |
1027 patchf.close() | |
1028 | |
1029 repo.dirstate.setparents(*cparents) | |
1030 copies = {} | |
1031 for dst in a: | |
1032 src = repo.dirstate.copied(dst) | |
1033 if src is None: | |
1034 continue | |
1035 copies.setdefault(src, []).append(dst) | |
1036 repo.dirstate.add(dst) | |
1037 # remember the copies between patchparent and tip | |
1038 # this may be slow, so don't do it if we're not tracking copies | |
1039 if self.diffopts().git: | |
1040 for dst in aaa: | |
1041 f = repo.file(dst) | |
1042 src = f.renamed(man[dst]) | |
1043 if src: | |
1044 copies[src[0]] = copies.get(dst, []) | |
1045 if dst in a: | |
1046 copies[src[0]].append(dst) | |
1047 # we can't copy a file created by the patch itself | |
1048 if dst in copies: | |
1049 del copies[dst] | |
1050 for src, dsts in copies.iteritems(): | |
1051 for dst in dsts: | |
1052 repo.dirstate.copy(src, dst) | |
1053 for f in r: | |
1054 repo.dirstate.remove(f) | |
1055 # if the patch excludes a modified file, mark that | |
1056 # file with mtime=0 so status can see it. | |
1057 mm = [] | |
1058 for i in xrange(len(m)-1, -1, -1): | |
1059 if not matchfn(m[i]): | |
1060 mm.append(m[i]) | |
1061 del m[i] | |
1062 for f in m: | |
1063 repo.dirstate.normal(f) | |
1064 for f in mm: | |
1065 repo.dirstate.normaldirty(f) | |
1066 for f in forget: | |
1067 repo.dirstate.forget(f) | |
1068 | |
1069 if not msg: | |
1070 if not message: | |
1071 message = "[mq]: %s\n" % patchfn | |
1072 else: | |
1073 message = "\n".join(message) | |
1074 else: | |
1075 message = msg | |
1076 | |
1077 self.strip(repo, top, update=False, | |
1078 backup='strip', wlock=wlock) | |
1079 n = repo.commit(filelist, message, changes[1], match=matchfn, | |
1080 force=1, wlock=wlock) | |
1081 self.applied[-1] = statusentry(revlog.hex(n), patchfn) | |
1082 self.applied_dirty = 1 | |
1083 self.removeundo(repo) | |
967 else: | 1084 else: |
968 filelist = None | 1085 self.printdiff(repo, patchparent, fp=patchf) |
969 match = util.always | 1086 patchf.close() |
970 m, a, r, d, u = repo.status(files=filelist, match=match)[:5] | 1087 added = repo.status()[1] |
971 | 1088 for a in added: |
972 # we might end up with files that were added between tip and | 1089 f = repo.wjoin(a) |
973 # the dirstate parent, but then changed in the local dirstate. | 1090 try: |
974 # in this case, we want them to only show up in the added section | 1091 os.unlink(f) |
975 for x in m: | 1092 except OSError, e: |
976 if x not in aa: | 1093 if e.errno != errno.ENOENT: |
977 mm.append(x) | 1094 raise |
978 # we might end up with files added by the local dirstate that | 1095 try: os.removedirs(os.path.dirname(f)) |
979 # were deleted by the patch. In this case, they should only | 1096 except: pass |
980 # show up in the changed section. | 1097 # forget the file copies in the dirstate |
981 for x in a: | 1098 # push should readd the files later on |
982 if x in dd: | 1099 repo.dirstate.forget(a) |
983 del dd[dd.index(x)] | 1100 self.pop(repo, force=True, wlock=wlock) |
984 mm.append(x) | 1101 self.push(repo, force=True, wlock=wlock) |
985 else: | 1102 finally: |
986 aa.append(x) | 1103 del wlock |
987 # make sure any files deleted in the local dirstate | |
988 # are not in the add or change column of the patch | |
989 forget = [] | |
990 for x in d + r: | |
991 if x in aa: | |
992 del aa[aa.index(x)] | |
993 forget.append(x) | |
994 continue | |
995 elif x in mm: | |
996 del mm[mm.index(x)] | |
997 dd.append(x) | |
998 | |
999 m = util.unique(mm) | |
1000 r = util.unique(dd) | |
1001 a = util.unique(aa) | |
1002 c = [filter(matchfn, l) for l in (m, a, r, [], u)] | |
1003 filelist = util.unique(c[0] + c[1] + c[2]) | |
1004 patch.diff(repo, patchparent, files=filelist, match=matchfn, | |
1005 fp=patchf, changes=c, opts=self.diffopts()) | |
1006 patchf.close() | |
1007 | |
1008 repo.dirstate.setparents(*cparents) | |
1009 copies = {} | |
1010 for dst in a: | |
1011 src = repo.dirstate.copied(dst) | |
1012 if src is None: | |
1013 continue | |
1014 copies.setdefault(src, []).append(dst) | |
1015 repo.dirstate.add(dst) | |
1016 # remember the copies between patchparent and tip | |
1017 # this may be slow, so don't do it if we're not tracking copies | |
1018 if self.diffopts().git: | |
1019 for dst in aaa: | |
1020 f = repo.file(dst) | |
1021 src = f.renamed(man[dst]) | |
1022 if src: | |
1023 copies[src[0]] = copies.get(dst, []) | |
1024 if dst in a: | |
1025 copies[src[0]].append(dst) | |
1026 # we can't copy a file created by the patch itself | |
1027 if dst in copies: | |
1028 del copies[dst] | |
1029 for src, dsts in copies.iteritems(): | |
1030 for dst in dsts: | |
1031 repo.dirstate.copy(src, dst) | |
1032 for f in r: | |
1033 repo.dirstate.remove(f) | |
1034 # if the patch excludes a modified file, mark that file with mtime=0 | |
1035 # so status can see it. | |
1036 mm = [] | |
1037 for i in xrange(len(m)-1, -1, -1): | |
1038 if not matchfn(m[i]): | |
1039 mm.append(m[i]) | |
1040 del m[i] | |
1041 for f in m: | |
1042 repo.dirstate.normal(f) | |
1043 for f in mm: | |
1044 repo.dirstate.normaldirty(f) | |
1045 for f in forget: | |
1046 repo.dirstate.forget(f) | |
1047 | |
1048 if not msg: | |
1049 if not message: | |
1050 message = "[mq]: %s\n" % patchfn | |
1051 else: | |
1052 message = "\n".join(message) | |
1053 else: | |
1054 message = msg | |
1055 | |
1056 self.strip(repo, top, update=False, backup='strip', wlock=wlock) | |
1057 n = repo.commit(filelist, message, changes[1], match=matchfn, | |
1058 force=1, wlock=wlock) | |
1059 self.applied[-1] = statusentry(revlog.hex(n), patchfn) | |
1060 self.applied_dirty = 1 | |
1061 self.removeundo(repo) | |
1062 else: | |
1063 self.printdiff(repo, patchparent, fp=patchf) | |
1064 patchf.close() | |
1065 added = repo.status()[1] | |
1066 for a in added: | |
1067 f = repo.wjoin(a) | |
1068 try: | |
1069 os.unlink(f) | |
1070 except OSError, e: | |
1071 if e.errno != errno.ENOENT: | |
1072 raise | |
1073 try: os.removedirs(os.path.dirname(f)) | |
1074 except: pass | |
1075 # forget the file copies in the dirstate | |
1076 # push should readd the files later on | |
1077 repo.dirstate.forget(a) | |
1078 self.pop(repo, force=True, wlock=wlock) | |
1079 self.push(repo, force=True, wlock=wlock) | |
1080 | 1104 |
1081 def init(self, repo, create=False): | 1105 def init(self, repo, create=False): |
1082 if not create and os.path.isdir(self.path): | 1106 if not create and os.path.isdir(self.path): |
1083 raise util.Abort(_("patch queue directory already exists")) | 1107 raise util.Abort(_("patch queue directory already exists")) |
1084 try: | 1108 try: |
1870 | 1894 |
1871 util.rename(q.join(patch), absdest) | 1895 util.rename(q.join(patch), absdest) |
1872 r = q.qrepo() | 1896 r = q.qrepo() |
1873 if r: | 1897 if r: |
1874 wlock = r.wlock() | 1898 wlock = r.wlock() |
1875 if r.dirstate[name] == 'r': | 1899 try: |
1876 r.undelete([name], wlock) | 1900 if r.dirstate[name] == 'r': |
1877 r.copy(patch, name, wlock) | 1901 r.undelete([name], wlock) |
1878 r.remove([patch], False, wlock) | 1902 r.copy(patch, name, wlock) |
1903 r.remove([patch], False, wlock) | |
1904 finally: | |
1905 del wlock | |
1879 | 1906 |
1880 q.save_dirty() | 1907 q.save_dirty() |
1881 | 1908 |
1882 def restore(ui, repo, rev, **opts): | 1909 def restore(ui, repo, rev, **opts): |
1883 """restore the queue state saved by a rev""" | 1910 """restore the queue state saved by a rev""" |