comparison hgext/mq.py @ 4702:18e91c9def0c

strip: move strip code to a new repair module
author Matt Mackall <mpm@selenic.com>
date Mon, 25 Jun 2007 01:26:44 -0500
parents d2da07fb5727
children f49fcbb325bc
comparison
equal deleted inserted replaced
4701:d2da07fb5727 4702:18e91c9def0c
28 remove patch from applied stack qpop 28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh 29 refresh contents of top applied patch qrefresh
30 ''' 30 '''
31 31
32 from mercurial.i18n import _ 32 from mercurial.i18n import _
33 from mercurial import commands, cmdutil, hg, patch, revlog, util, changegroup 33 from mercurial import commands, cmdutil, hg, patch, revlog, util
34 from mercurial import repair
34 import os, sys, re, errno 35 import os, sys, re, errno
35 36
36 commands.norepo += " qclone qversion" 37 commands.norepo += " qclone qversion"
37 38
38 # Patch names looks like unix-file names. 39 # Patch names looks like unix-file names.
39 # 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.
40 normname = util.normpath 41 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(repo, bases, heads, rev, suffix):
64 cg = repo.changegroupsubset(bases, heads, 'strip')
65 backupdir = repo.join("strip-backup")
66 if not os.path.isdir(backupdir):
67 os.mkdir(backupdir)
68 name = os.path.join(backupdir, "%s-%s" % (revlog.short(rev), suffix))
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 bundle(repo, [rev], chlog.heads(), rev, 'backup')
145 if saveheads:
146 chgrpfile = bundle(repo, savebases.keys(), saveheads, rev, 'temp')
147
148 stripall(revnum)
149
150 change = chlog.read(rev)
151 chlog.strip(revnum, revnum)
152 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
153 if saveheads:
154 ui.status("adding branch\n")
155 commands.unbundle(ui, repo, "file:%s" % chgrpfile, update=False)
156 if backup != "strip":
157 os.unlink(chgrpfile)
158 42
159 class statusentry: 43 class statusentry:
160 def __init__(self, rev, name=None): 44 def __init__(self, rev, name=None):
161 if not name: 45 if not name:
162 fields = rev.split(':', 1) 46 fields = rev.split(':', 1)
755 urev = self.qparents(repo, rev) 639 urev = self.qparents(repo, rev)
756 hg.clean(repo, urev, wlock=wlock) 640 hg.clean(repo, urev, wlock=wlock)
757 repo.dirstate.write() 641 repo.dirstate.write()
758 642
759 self.removeundo(repo) 643 self.removeundo(repo)
760 _strip(self.ui, repo, rev, backup) 644 repair.strip(self.ui, repo, rev, backup)
761 645
762 def isapplied(self, patch): 646 def isapplied(self, patch):
763 """returns (index, rev, patch)""" 647 """returns (index, rev, patch)"""
764 for i in xrange(len(self.applied)): 648 for i in xrange(len(self.applied)):
765 a = self.applied[i] 649 a = self.applied[i]