##// END OF EJS Templates
with: use context manager for wlock in shelve stripcmd
Bryan O'Sullivan -
r27839:7ec3cb24 default
parent child Browse files
Show More
@@ -1,238 +1,235 b''
1 1 """strip changesets and their descendants from history
2 2
3 3 This extension allows you to strip changesets and all their descendants from the
4 4 repository. See the command help for details.
5 5 """
6 6 from mercurial.i18n import _
7 7 from mercurial.node import nullid
8 8 from mercurial.lock import release
9 9 from mercurial import cmdutil, hg, scmutil, util, error
10 10 from mercurial import repair, bookmarks as bookmarksmod , merge
11 11
12 12 cmdtable = {}
13 13 command = cmdutil.command(cmdtable)
14 14 # Note for extension authors: ONLY specify testedwith = 'internal' for
15 15 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
16 16 # be specifying the version(s) of Mercurial they are tested with, or
17 17 # leave the attribute unspecified.
18 18 testedwith = 'internal'
19 19
20 20 def checksubstate(repo, baserev=None):
21 21 '''return list of subrepos at a different revision than substate.
22 22 Abort if any subrepos have uncommitted changes.'''
23 23 inclsubs = []
24 24 wctx = repo[None]
25 25 if baserev:
26 26 bctx = repo[baserev]
27 27 else:
28 28 bctx = wctx.parents()[0]
29 29 for s in sorted(wctx.substate):
30 30 wctx.sub(s).bailifchanged(True)
31 31 if s not in bctx.substate or bctx.sub(s).dirty():
32 32 inclsubs.append(s)
33 33 return inclsubs
34 34
35 35 def checklocalchanges(repo, force=False, excsuffix=''):
36 36 cmdutil.checkunfinished(repo)
37 37 s = repo.status()
38 38 if not force:
39 39 if s.modified or s.added or s.removed or s.deleted:
40 40 _("local changes found") # i18n tool detection
41 41 raise error.Abort(_("local changes found" + excsuffix))
42 42 if checksubstate(repo):
43 43 _("local changed subrepos found") # i18n tool detection
44 44 raise error.Abort(_("local changed subrepos found" + excsuffix))
45 45 return s
46 46
47 47 def strip(ui, repo, revs, update=True, backup=True, force=None, bookmarks=None):
48 48 wlock = lock = None
49 49 try:
50 50 wlock = repo.wlock()
51 51 lock = repo.lock()
52 52
53 53 if update:
54 54 checklocalchanges(repo, force=force)
55 55 urev, p2 = repo.changelog.parents(revs[0])
56 56 if (util.safehasattr(repo, 'mq') and
57 57 p2 != nullid
58 58 and p2 in [x.node for x in repo.mq.applied]):
59 59 urev = p2
60 60 hg.clean(repo, urev)
61 61 repo.dirstate.write(repo.currenttransaction())
62 62
63 63 repair.strip(ui, repo, revs, backup)
64 64
65 65 repomarks = repo._bookmarks
66 66 if bookmarks:
67 67 tr = None
68 68 try:
69 69 tr = repo.transaction('strip')
70 70 if repo._activebookmark in bookmarks:
71 71 bookmarksmod.deactivate(repo)
72 72 for bookmark in bookmarks:
73 73 del repomarks[bookmark]
74 74 repomarks.recordchange(tr)
75 75 tr.close()
76 76 for bookmark in sorted(bookmarks):
77 77 ui.write(_("bookmark '%s' deleted\n") % bookmark)
78 78 finally:
79 79 release(tr)
80 80 finally:
81 81 release(lock, wlock)
82 82
83 83
84 84 @command("strip",
85 85 [
86 86 ('r', 'rev', [], _('strip specified revision (optional, '
87 87 'can specify revisions without this '
88 88 'option)'), _('REV')),
89 89 ('f', 'force', None, _('force removal of changesets, discard '
90 90 'uncommitted changes (no backup)')),
91 91 ('', 'no-backup', None, _('no backups')),
92 92 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
93 93 ('n', '', None, _('ignored (DEPRECATED)')),
94 94 ('k', 'keep', None, _("do not modify working directory during "
95 95 "strip")),
96 96 ('B', 'bookmark', [], _("remove revs only reachable from given"
97 97 " bookmark"))],
98 98 _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...'))
99 99 def stripcmd(ui, repo, *revs, **opts):
100 100 """strip changesets and all their descendants from the repository
101 101
102 102 The strip command removes the specified changesets and all their
103 103 descendants. If the working directory has uncommitted changes, the
104 104 operation is aborted unless the --force flag is supplied, in which
105 105 case changes will be discarded.
106 106
107 107 If a parent of the working directory is stripped, then the working
108 108 directory will automatically be updated to the most recent
109 109 available ancestor of the stripped parent after the operation
110 110 completes.
111 111
112 112 Any stripped changesets are stored in ``.hg/strip-backup`` as a
113 113 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
114 114 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
115 115 where BUNDLE is the bundle file created by the strip. Note that
116 116 the local revision numbers will in general be different after the
117 117 restore.
118 118
119 119 Use the --no-backup option to discard the backup bundle once the
120 120 operation completes.
121 121
122 122 Strip is not a history-rewriting operation and can be used on
123 123 changesets in the public phase. But if the stripped changesets have
124 124 been pushed to a remote repository you will likely pull them again.
125 125
126 126 Return 0 on success.
127 127 """
128 128 backup = True
129 129 if opts.get('no_backup') or opts.get('nobackup'):
130 130 backup = False
131 131
132 132 cl = repo.changelog
133 133 revs = list(revs) + opts.get('rev')
134 134 revs = set(scmutil.revrange(repo, revs))
135 135
136 wlock = repo.wlock()
137 try:
136 with repo.wlock():
138 137 bookmarks = set(opts.get('bookmark'))
139 138 if bookmarks:
140 139 repomarks = repo._bookmarks
141 140 if not bookmarks.issubset(repomarks):
142 141 raise error.Abort(_("bookmark '%s' not found") %
143 142 ','.join(sorted(bookmarks - set(repomarks.keys()))))
144 143
145 144 # If the requested bookmark is not the only one pointing to a
146 145 # a revision we have to only delete the bookmark and not strip
147 146 # anything. revsets cannot detect that case.
148 147 nodetobookmarks = {}
149 148 for mark, node in repomarks.iteritems():
150 149 nodetobookmarks.setdefault(node, []).append(mark)
151 150 for marks in nodetobookmarks.values():
152 151 if bookmarks.issuperset(marks):
153 152 rsrevs = repair.stripbmrevset(repo, marks[0])
154 153 revs.update(set(rsrevs))
155 154 if not revs:
156 155 lock = tr = None
157 156 try:
158 157 lock = repo.lock()
159 158 tr = repo.transaction('bookmark')
160 159 for bookmark in bookmarks:
161 160 del repomarks[bookmark]
162 161 repomarks.recordchange(tr)
163 162 tr.close()
164 163 for bookmark in sorted(bookmarks):
165 164 ui.write(_("bookmark '%s' deleted\n") % bookmark)
166 165 finally:
167 166 release(lock, tr)
168 167
169 168 if not revs:
170 169 raise error.Abort(_('empty revision set'))
171 170
172 171 descendants = set(cl.descendants(revs))
173 172 strippedrevs = revs.union(descendants)
174 173 roots = revs.difference(descendants)
175 174
176 175 update = False
177 176 # if one of the wdir parent is stripped we'll need
178 177 # to update away to an earlier revision
179 178 for p in repo.dirstate.parents():
180 179 if p != nullid and cl.rev(p) in strippedrevs:
181 180 update = True
182 181 break
183 182
184 183 rootnodes = set(cl.node(r) for r in roots)
185 184
186 185 q = getattr(repo, 'mq', None)
187 186 if q is not None and q.applied:
188 187 # refresh queue state if we're about to strip
189 188 # applied patches
190 189 if cl.rev(repo.lookup('qtip')) in strippedrevs:
191 190 q.applieddirty = True
192 191 start = 0
193 192 end = len(q.applied)
194 193 for i, statusentry in enumerate(q.applied):
195 194 if statusentry.node in rootnodes:
196 195 # if one of the stripped roots is an applied
197 196 # patch, only part of the queue is stripped
198 197 start = i
199 198 break
200 199 del q.applied[start:end]
201 200 q.savedirty()
202 201
203 202 revs = sorted(rootnodes)
204 203 if update and opts.get('keep'):
205 204 urev, p2 = repo.changelog.parents(revs[0])
206 205 if (util.safehasattr(repo, 'mq') and p2 != nullid
207 206 and p2 in [x.node for x in repo.mq.applied]):
208 207 urev = p2
209 208 uctx = repo[urev]
210 209
211 210 # only reset the dirstate for files that would actually change
212 211 # between the working context and uctx
213 212 descendantrevs = repo.revs("%s::." % uctx.rev())
214 213 changedfiles = []
215 214 for rev in descendantrevs:
216 215 # blindly reset the files, regardless of what actually changed
217 216 changedfiles.extend(repo[rev].files())
218 217
219 218 # reset files that only changed in the dirstate too
220 219 dirstate = repo.dirstate
221 220 dirchanges = [f for f in dirstate if dirstate[f] != 'n']
222 221 changedfiles.extend(dirchanges)
223 222
224 223 repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
225 224 repo.dirstate.write(repo.currenttransaction())
226 225
227 226 # clear resolve state
228 227 merge.mergestate.clean(repo, repo['.'].node())
229 228
230 229 update = False
231 230
232 231
233 232 strip(ui, repo, revs, backup=backup, update=update,
234 233 force=opts.get('force'), bookmarks=bookmarks)
235 finally:
236 wlock.release()
237 234
238 235 return 0
General Comments 0
You need to be logged in to leave comments. Login now