comparison hgext/mq.py @ 4700:a2e025fcd256

strip: pull core strip logic into its own function
author Matt Mackall <mpm@selenic.com>
date Sun, 24 Jun 2007 18:22:40 -0500
parents 3a645af7fb76
children d2da07fb5727
comparison
equal deleted inserted replaced
4699:a6b62584d0b2 4700:a2e025fcd256
36 commands.norepo += " qclone qversion" 36 commands.norepo += " qclone qversion"
37 37
38 # Patch names looks like unix-file names. 38 # Patch names looks like unix-file names.
39 # They must be joinable with queue directory and result in the patch path. 39 # They must be joinable with queue directory and result in the patch path.
40 normname = util.normpath 40 normname = util.normpath
41
42 def _strip(ui, repo, rev, backup="all"):
43 def limitheads(chlog, stop):
44 """return the list of all nodes that have no children"""
45 p = {}
46 h = []
47 stoprev = 0
48 if stop in chlog.nodemap:
49 stoprev = chlog.rev(stop)
50
51 for r in xrange(chlog.count() - 1, -1, -1):
52 n = chlog.node(r)
53 if n not in p:
54 h.append(n)
55 if n == stop:
56 break
57 if r < stoprev:
58 break
59 for pn in chlog.parents(n):
60 p[pn] = 1
61 return h
62
63 def bundle(cg):
64 backupdir = repo.join("strip-backup")
65 if not os.path.isdir(backupdir):
66 os.mkdir(backupdir)
67 name = os.path.join(backupdir, "%s" % revlog.short(rev))
68 name = savename(name)
69 ui.warn("saving bundle to %s\n" % name)
70 return changegroup.writebundle(cg, name, "HG10BZ")
71
72 def stripall(revnum):
73 mm = repo.changectx(rev).manifest()
74 seen = {}
75
76 for x in xrange(revnum, repo.changelog.count()):
77 for f in repo.changectx(x).files():
78 if f in seen:
79 continue
80 seen[f] = 1
81 if f in mm:
82 filerev = mm[f]
83 else:
84 filerev = 0
85 seen[f] = filerev
86 # we go in two steps here so the strip loop happens in a
87 # sensible order. When stripping many files, this helps keep
88 # our disk access patterns under control.
89 seen_list = seen.keys()
90 seen_list.sort()
91 for f in seen_list:
92 ff = repo.file(f)
93 filerev = seen[f]
94 if filerev != 0:
95 if filerev in ff.nodemap:
96 filerev = ff.rev(filerev)
97 else:
98 filerev = 0
99 ff.strip(filerev, revnum)
100
101 chlog = repo.changelog
102 # TODO delete the undo files, and handle undo of merge sets
103 pp = chlog.parents(rev)
104 revnum = chlog.rev(rev)
105
106 # save is a list of all the branches we are truncating away
107 # that we actually want to keep. changegroup will be used
108 # to preserve them and add them back after the truncate
109 saveheads = []
110 savebases = {}
111
112 heads = limitheads(chlog, rev)
113 seen = {}
114
115 # search through all the heads, finding those where the revision
116 # we want to strip away is an ancestor. Also look for merges
117 # that might be turned into new heads by the strip.
118 while heads:
119 h = heads.pop()
120 n = h
121 while True:
122 seen[n] = 1
123 pp = chlog.parents(n)
124 if pp[1] != revlog.nullid:
125 for p in pp:
126 if chlog.rev(p) > revnum and p not in seen:
127 heads.append(p)
128 if pp[0] == revlog.nullid:
129 break
130 if chlog.rev(pp[0]) < revnum:
131 break
132 n = pp[0]
133 if n == rev:
134 break
135 r = chlog.reachable(h, rev)
136 if rev not in r:
137 saveheads.append(h)
138 for x in r:
139 if chlog.rev(x) > revnum:
140 savebases[x] = 1
141
142 # create a changegroup for all the branches we need to keep
143 if backup == "all":
144 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
145 bundle(backupch)
146 if saveheads:
147 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
148 chgrpfile = bundle(backupch)
149
150 stripall(revnum)
151
152 change = chlog.read(rev)
153 chlog.strip(revnum, revnum)
154 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
155 if saveheads:
156 ui.status("adding branch\n")
157 commands.unbundle(ui, repo, "file:%s" % chgrpfile, update=False)
158 if backup != "strip":
159 os.unlink(chgrpfile)
41 160
42 class statusentry: 161 class statusentry:
43 def __init__(self, rev, name=None): 162 def __init__(self, rev, name=None):
44 if not name: 163 if not name:
45 fields = rev.split(':', 1) 164 fields = rev.split(':', 1)
627 if commitfiles: 746 if commitfiles:
628 self.refresh(repo, short=True) 747 self.refresh(repo, short=True)
629 self.removeundo(repo) 748 self.removeundo(repo)
630 749
631 def strip(self, repo, rev, update=True, backup="all", wlock=None): 750 def strip(self, repo, rev, update=True, backup="all", wlock=None):
632 def limitheads(chlog, stop):
633 """return the list of all nodes that have no children"""
634 p = {}
635 h = []
636 stoprev = 0
637 if stop in chlog.nodemap:
638 stoprev = chlog.rev(stop)
639
640 for r in xrange(chlog.count() - 1, -1, -1):
641 n = chlog.node(r)
642 if n not in p:
643 h.append(n)
644 if n == stop:
645 break
646 if r < stoprev:
647 break
648 for pn in chlog.parents(n):
649 p[pn] = 1
650 return h
651
652 def bundle(cg):
653 backupdir = repo.join("strip-backup")
654 if not os.path.isdir(backupdir):
655 os.mkdir(backupdir)
656 name = os.path.join(backupdir, "%s" % revlog.short(rev))
657 name = savename(name)
658 self.ui.warn("saving bundle to %s\n" % name)
659 return changegroup.writebundle(cg, name, "HG10BZ")
660
661 def stripall(revnum):
662 mm = repo.changectx(rev).manifest()
663 seen = {}
664
665 for x in xrange(revnum, repo.changelog.count()):
666 for f in repo.changectx(x).files():
667 if f in seen:
668 continue
669 seen[f] = 1
670 if f in mm:
671 filerev = mm[f]
672 else:
673 filerev = 0
674 seen[f] = filerev
675 # we go in two steps here so the strip loop happens in a
676 # sensible order. When stripping many files, this helps keep
677 # our disk access patterns under control.
678 seen_list = seen.keys()
679 seen_list.sort()
680 for f in seen_list:
681 ff = repo.file(f)
682 filerev = seen[f]
683 if filerev != 0:
684 if filerev in ff.nodemap:
685 filerev = ff.rev(filerev)
686 else:
687 filerev = 0
688 ff.strip(filerev, revnum)
689
690 if not wlock: 751 if not wlock:
691 wlock = repo.wlock() 752 wlock = repo.wlock()
692 lock = repo.lock() 753 lock = repo.lock()
693 chlog = repo.changelog
694 # TODO delete the undo files, and handle undo of merge sets
695 pp = chlog.parents(rev)
696 revnum = chlog.rev(rev)
697 754
698 if update: 755 if update:
699 self.check_localchanges(repo, refresh=False) 756 self.check_localchanges(repo, refresh=False)
700 urev = self.qparents(repo, rev) 757 urev = self.qparents(repo, rev)
701 hg.clean(repo, urev, wlock=wlock) 758 hg.clean(repo, urev, wlock=wlock)
702 repo.dirstate.write() 759 repo.dirstate.write()
703 760
704 # save is a list of all the branches we are truncating away
705 # that we actually want to keep. changegroup will be used
706 # to preserve them and add them back after the truncate
707 saveheads = []
708 savebases = {}
709
710 heads = limitheads(chlog, rev)
711 seen = {}
712
713 # search through all the heads, finding those where the revision
714 # we want to strip away is an ancestor. Also look for merges
715 # that might be turned into new heads by the strip.
716 while heads:
717 h = heads.pop()
718 n = h
719 while True:
720 seen[n] = 1
721 pp = chlog.parents(n)
722 if pp[1] != revlog.nullid:
723 for p in pp:
724 if chlog.rev(p) > revnum and p not in seen:
725 heads.append(p)
726 if pp[0] == revlog.nullid:
727 break
728 if chlog.rev(pp[0]) < revnum:
729 break
730 n = pp[0]
731 if n == rev:
732 break
733 r = chlog.reachable(h, rev)
734 if rev not in r:
735 saveheads.append(h)
736 for x in r:
737 if chlog.rev(x) > revnum:
738 savebases[x] = 1
739
740 # create a changegroup for all the branches we need to keep
741 if backup == "all":
742 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
743 bundle(backupch)
744 if saveheads:
745 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
746 chgrpfile = bundle(backupch)
747
748 stripall(revnum)
749
750 change = chlog.read(rev)
751 chlog.strip(revnum, revnum)
752 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
753 self.removeundo(repo) 761 self.removeundo(repo)
754 if saveheads: 762 _strip(self.ui, repo, rev, backup)
755 self.ui.status("adding branch\n")
756 commands.unbundle(self.ui, repo, "file:%s" % chgrpfile,
757 update=False)
758 if backup != "strip":
759 os.unlink(chgrpfile)
760 763
761 def isapplied(self, patch): 764 def isapplied(self, patch):
762 """returns (index, rev, patch)""" 765 """returns (index, rev, patch)"""
763 for i in xrange(len(self.applied)): 766 for i in xrange(len(self.applied)):
764 a = self.applied[i] 767 a = self.applied[i]