##// END OF EJS Templates
dirstate-item: use `maybe_clean` instead of `state` in `strip`...
marmoute -
r48908:97c5d1c2 default
parent child Browse files
Show More
@@ -1,280 +1,282 b''
1 1 from __future__ import absolute_import
2 2
3 3 from .i18n import _
4 4 from .pycompat import getattr
5 5 from . import (
6 6 bookmarks as bookmarksmod,
7 7 cmdutil,
8 8 error,
9 9 hg,
10 10 lock as lockmod,
11 11 mergestate as mergestatemod,
12 12 pycompat,
13 13 registrar,
14 14 repair,
15 15 scmutil,
16 16 util,
17 17 )
18 18
19 19 release = lockmod.release
20 20
21 21 cmdtable = {}
22 22 command = registrar.command(cmdtable)
23 23
24 24
25 25 def checklocalchanges(repo, force=False):
26 26 s = repo.status()
27 27 if not force:
28 28 cmdutil.checkunfinished(repo)
29 29 cmdutil.bailifchanged(repo)
30 30 else:
31 31 cmdutil.checkunfinished(repo, skipmerge=True)
32 32 return s
33 33
34 34
35 35 def _findupdatetarget(repo, nodes):
36 36 unode, p2 = repo.changelog.parents(nodes[0])
37 37 currentbranch = repo[None].branch()
38 38
39 39 if (
40 40 util.safehasattr(repo, b'mq')
41 41 and p2 != repo.nullid
42 42 and p2 in [x.node for x in repo.mq.applied]
43 43 ):
44 44 unode = p2
45 45 elif currentbranch != repo[unode].branch():
46 46 pwdir = b'parents(wdir())'
47 47 revset = b'max(((parents(%ln::%r) + %r) - %ln::%r) and branch(%s))'
48 48 branchtarget = repo.revs(
49 49 revset, nodes, pwdir, pwdir, nodes, pwdir, currentbranch
50 50 )
51 51 if branchtarget:
52 52 cl = repo.changelog
53 53 unode = cl.node(branchtarget.first())
54 54
55 55 return unode
56 56
57 57
58 58 def strip(
59 59 ui,
60 60 repo,
61 61 revs,
62 62 update=True,
63 63 backup=True,
64 64 force=None,
65 65 bookmarks=None,
66 66 soft=False,
67 67 ):
68 68 with repo.wlock(), repo.lock():
69 69
70 70 if update:
71 71 checklocalchanges(repo, force=force)
72 72 urev = _findupdatetarget(repo, revs)
73 73 hg.clean(repo, urev)
74 74 repo.dirstate.write(repo.currenttransaction())
75 75
76 76 if soft:
77 77 repair.softstrip(ui, repo, revs, backup)
78 78 else:
79 79 repair.strip(ui, repo, revs, backup)
80 80
81 81 repomarks = repo._bookmarks
82 82 if bookmarks:
83 83 with repo.transaction(b'strip') as tr:
84 84 if repo._activebookmark in bookmarks:
85 85 bookmarksmod.deactivate(repo)
86 86 repomarks.applychanges(repo, tr, [(b, None) for b in bookmarks])
87 87 for bookmark in sorted(bookmarks):
88 88 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
89 89
90 90
91 91 @command(
92 92 b"debugstrip",
93 93 [
94 94 (
95 95 b'r',
96 96 b'rev',
97 97 [],
98 98 _(
99 99 b'strip specified revision (optional, '
100 100 b'can specify revisions without this '
101 101 b'option)'
102 102 ),
103 103 _(b'REV'),
104 104 ),
105 105 (
106 106 b'f',
107 107 b'force',
108 108 None,
109 109 _(
110 110 b'force removal of changesets, discard '
111 111 b'uncommitted changes (no backup)'
112 112 ),
113 113 ),
114 114 (b'', b'no-backup', None, _(b'do not save backup bundle')),
115 115 (
116 116 b'',
117 117 b'nobackup',
118 118 None,
119 119 _(b'do not save backup bundle (DEPRECATED)'),
120 120 ),
121 121 (b'n', b'', None, _(b'ignored (DEPRECATED)')),
122 122 (
123 123 b'k',
124 124 b'keep',
125 125 None,
126 126 _(b"do not modify working directory during strip"),
127 127 ),
128 128 (
129 129 b'B',
130 130 b'bookmark',
131 131 [],
132 132 _(b"remove revs only reachable from given bookmark"),
133 133 _(b'BOOKMARK'),
134 134 ),
135 135 (
136 136 b'',
137 137 b'soft',
138 138 None,
139 139 _(b"simply drop changesets from visible history (EXPERIMENTAL)"),
140 140 ),
141 141 ],
142 142 _(b'hg debugstrip [-k] [-f] [-B bookmark] [-r] REV...'),
143 143 helpcategory=command.CATEGORY_MAINTENANCE,
144 144 )
145 145 def debugstrip(ui, repo, *revs, **opts):
146 146 """strip changesets and all their descendants from the repository
147 147
148 148 The strip command removes the specified changesets and all their
149 149 descendants. If the working directory has uncommitted changes, the
150 150 operation is aborted unless the --force flag is supplied, in which
151 151 case changes will be discarded.
152 152
153 153 If a parent of the working directory is stripped, then the working
154 154 directory will automatically be updated to the most recent
155 155 available ancestor of the stripped parent after the operation
156 156 completes.
157 157
158 158 Any stripped changesets are stored in ``.hg/strip-backup`` as a
159 159 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
160 160 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
161 161 where BUNDLE is the bundle file created by the strip. Note that
162 162 the local revision numbers will in general be different after the
163 163 restore.
164 164
165 165 Use the --no-backup option to discard the backup bundle once the
166 166 operation completes.
167 167
168 168 Strip is not a history-rewriting operation and can be used on
169 169 changesets in the public phase. But if the stripped changesets have
170 170 been pushed to a remote repository you will likely pull them again.
171 171
172 172 Return 0 on success.
173 173 """
174 174 opts = pycompat.byteskwargs(opts)
175 175 backup = True
176 176 if opts.get(b'no_backup') or opts.get(b'nobackup'):
177 177 backup = False
178 178
179 179 cl = repo.changelog
180 180 revs = list(revs) + opts.get(b'rev')
181 181 revs = set(scmutil.revrange(repo, revs))
182 182
183 183 with repo.wlock():
184 184 bookmarks = set(opts.get(b'bookmark'))
185 185 if bookmarks:
186 186 repomarks = repo._bookmarks
187 187 if not bookmarks.issubset(repomarks):
188 188 raise error.Abort(
189 189 _(b"bookmark '%s' not found")
190 190 % b','.join(sorted(bookmarks - set(repomarks.keys())))
191 191 )
192 192
193 193 # If the requested bookmark is not the only one pointing to a
194 194 # a revision we have to only delete the bookmark and not strip
195 195 # anything. revsets cannot detect that case.
196 196 nodetobookmarks = {}
197 197 for mark, node in pycompat.iteritems(repomarks):
198 198 nodetobookmarks.setdefault(node, []).append(mark)
199 199 for marks in nodetobookmarks.values():
200 200 if bookmarks.issuperset(marks):
201 201 rsrevs = scmutil.bookmarkrevs(repo, marks[0])
202 202 revs.update(set(rsrevs))
203 203 if not revs:
204 204 with repo.lock(), repo.transaction(b'bookmark') as tr:
205 205 bmchanges = [(b, None) for b in bookmarks]
206 206 repomarks.applychanges(repo, tr, bmchanges)
207 207 for bookmark in sorted(bookmarks):
208 208 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
209 209
210 210 if not revs:
211 211 raise error.Abort(_(b'empty revision set'))
212 212
213 213 descendants = set(cl.descendants(revs))
214 214 strippedrevs = revs.union(descendants)
215 215 roots = revs.difference(descendants)
216 216
217 217 # if one of the wdir parent is stripped we'll need
218 218 # to update away to an earlier revision
219 219 update = any(
220 220 p != repo.nullid and cl.rev(p) in strippedrevs
221 221 for p in repo.dirstate.parents()
222 222 )
223 223
224 224 rootnodes = {cl.node(r) for r in roots}
225 225
226 226 q = getattr(repo, 'mq', None)
227 227 if q is not None and q.applied:
228 228 # refresh queue state if we're about to strip
229 229 # applied patches
230 230 if cl.rev(repo.lookup(b'qtip')) in strippedrevs:
231 231 q.applieddirty = True
232 232 start = 0
233 233 end = len(q.applied)
234 234 for i, statusentry in enumerate(q.applied):
235 235 if statusentry.node in rootnodes:
236 236 # if one of the stripped roots is an applied
237 237 # patch, only part of the queue is stripped
238 238 start = i
239 239 break
240 240 del q.applied[start:end]
241 241 q.savedirty()
242 242
243 243 revs = sorted(rootnodes)
244 244 if update and opts.get(b'keep'):
245 245 urev = _findupdatetarget(repo, revs)
246 246 uctx = repo[urev]
247 247
248 248 # only reset the dirstate for files that would actually change
249 249 # between the working context and uctx
250 250 descendantrevs = repo.revs(b"only(., %d)", uctx.rev())
251 251 changedfiles = []
252 252 for rev in descendantrevs:
253 253 # blindly reset the files, regardless of what actually changed
254 254 changedfiles.extend(repo[rev].files())
255 255
256 256 # reset files that only changed in the dirstate too
257 257 dirstate = repo.dirstate
258 dirchanges = [f for f in dirstate if dirstate[f] != b'n']
258 dirchanges = [
259 f for f in dirstate if not dirstate.get_entry(f).maybe_clean
260 ]
259 261 changedfiles.extend(dirchanges)
260 262
261 263 repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
262 264 repo.dirstate.write(repo.currenttransaction())
263 265
264 266 # clear resolve state
265 267 mergestatemod.mergestate.clean(repo)
266 268
267 269 update = False
268 270
269 271 strip(
270 272 ui,
271 273 repo,
272 274 revs,
273 275 backup=backup,
274 276 update=update,
275 277 force=opts.get(b'force'),
276 278 bookmarks=bookmarks,
277 279 soft=opts[b'soft'],
278 280 )
279 281
280 282 return 0
General Comments 0
You need to be logged in to leave comments. Login now