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"""