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