##// END OF EJS Templates
strip: move into core...
Valentin Gatien-Baron -
r46469:a46efd42 default draft
parent child Browse files
Show More
@@ -100,6 +100,7 b' from mercurial import ('
100 100 revsetlang,
101 101 scmutil,
102 102 smartset,
103 strip,
103 104 subrepoutil,
104 105 util,
105 106 vfs as vfsmod,
@@ -138,7 +139,7 b' configitem('
138 139
139 140 # force load strip extension formerly included in mq and import some utility
140 141 try:
141 stripext = extensions.find(b'strip')
142 extensions.find(b'strip')
142 143 except KeyError:
143 144 # note: load is lazy so we could avoid the try-except,
144 145 # but I (marmoute) prefer this explicit code.
@@ -149,9 +150,9 b' except KeyError:'
149 150 def log(self, event, msgfmt, *msgargs, **opts):
150 151 pass
151 152
152 stripext = extensions.load(dummyui(), b'strip', b'')
153
154 strip = stripext.strip
153 extensions.load(dummyui(), b'strip', b'')
154
155 strip = strip.strip
155 156
156 157
157 158 def checksubstate(repo, baserev=None):
@@ -1,287 +1,24 b''
1 """strip changesets and their descendants from history
1 """strip changesets and their descendants from history (DEPRECATED)
2
3 The functionality of this extension has been included in core Mercurial
4 since version 5.7. Please use :hg:`debugstrip ...` instead.
2 5
3 6 This extension allows you to strip changesets and all their descendants from the
4 7 repository. See the command help for details.
5 8 """
6 9 from __future__ import absolute_import
7 10
8 from mercurial.i18n import _
9 from mercurial.pycompat import getattr
10 11 from mercurial import (
11 bookmarks as bookmarksmod,
12 cmdutil,
13 error,
14 hg,
15 lock as lockmod,
16 mergestate as mergestatemod,
17 node as nodemod,
18 pycompat,
19 registrar,
20 repair,
21 scmutil,
22 util,
12 commands,
23 13 )
24 14
25 nullid = nodemod.nullid
26 release = lockmod.release
27
28 cmdtable = {}
29 command = registrar.command(cmdtable)
30 15 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
31 16 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
32 17 # be specifying the version(s) of Mercurial they are tested with, or
33 18 # leave the attribute unspecified.
34 19 testedwith = b'ships-with-hg-core'
35 20
36
37 def checklocalchanges(repo, force=False):
38 s = repo.status()
39 if not force:
40 cmdutil.checkunfinished(repo)
41 cmdutil.bailifchanged(repo)
42 else:
43 cmdutil.checkunfinished(repo, skipmerge=True)
44 return s
45
46
47 def _findupdatetarget(repo, nodes):
48 unode, p2 = repo.changelog.parents(nodes[0])
49 currentbranch = repo[None].branch()
50
51 if (
52 util.safehasattr(repo, b'mq')
53 and p2 != nullid
54 and p2 in [x.node for x in repo.mq.applied]
55 ):
56 unode = p2
57 elif currentbranch != repo[unode].branch():
58 pwdir = b'parents(wdir())'
59 revset = b'max(((parents(%ln::%r) + %r) - %ln::%r) and branch(%s))'
60 branchtarget = repo.revs(
61 revset, nodes, pwdir, pwdir, nodes, pwdir, currentbranch
62 )
63 if branchtarget:
64 cl = repo.changelog
65 unode = cl.node(branchtarget.first())
66
67 return unode
68
69
70 def strip(
71 ui,
72 repo,
73 revs,
74 update=True,
75 backup=True,
76 force=None,
77 bookmarks=None,
78 soft=False,
79 ):
80 with repo.wlock(), repo.lock():
81
82 if update:
83 checklocalchanges(repo, force=force)
84 urev = _findupdatetarget(repo, revs)
85 hg.clean(repo, urev)
86 repo.dirstate.write(repo.currenttransaction())
87
88 if soft:
89 repair.softstrip(ui, repo, revs, backup)
90 else:
91 repair.strip(ui, repo, revs, backup)
92
93 repomarks = repo._bookmarks
94 if bookmarks:
95 with repo.transaction(b'strip') as tr:
96 if repo._activebookmark in bookmarks:
97 bookmarksmod.deactivate(repo)
98 repomarks.applychanges(repo, tr, [(b, None) for b in bookmarks])
99 for bookmark in sorted(bookmarks):
100 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
101
102
103 @command(
104 b"strip",
105 [
106 (
107 b'r',
108 b'rev',
109 [],
110 _(
111 b'strip specified revision (optional, '
112 b'can specify revisions without this '
113 b'option)'
114 ),
115 _(b'REV'),
116 ),
117 (
118 b'f',
119 b'force',
120 None,
121 _(
122 b'force removal of changesets, discard '
123 b'uncommitted changes (no backup)'
124 ),
125 ),
126 (b'', b'no-backup', None, _(b'do not save backup bundle')),
127 (b'', b'nobackup', None, _(b'do not save backup bundle (DEPRECATED)'),),
128 (b'n', b'', None, _(b'ignored (DEPRECATED)')),
129 (
130 b'k',
131 b'keep',
132 None,
133 _(b"do not modify working directory during strip"),
134 ),
135 (
136 b'B',
137 b'bookmark',
138 [],
139 _(b"remove revs only reachable from given bookmark"),
140 _(b'BOOKMARK'),
141 ),
142 (
143 b'',
144 b'soft',
145 None,
146 _(b"simply drop changesets from visible history (EXPERIMENTAL)"),
147 ),
148 ],
149 _(b'hg strip [-k] [-f] [-B bookmark] [-r] REV...'),
150 helpcategory=command.CATEGORY_MAINTENANCE,
151 )
152 def stripcmd(ui, repo, *revs, **opts):
153 """strip changesets and all their descendants from the repository
154
155 The strip command removes the specified changesets and all their
156 descendants. If the working directory has uncommitted changes, the
157 operation is aborted unless the --force flag is supplied, in which
158 case changes will be discarded.
159
160 If a parent of the working directory is stripped, then the working
161 directory will automatically be updated to the most recent
162 available ancestor of the stripped parent after the operation
163 completes.
164
165 Any stripped changesets are stored in ``.hg/strip-backup`` as a
166 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
167 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
168 where BUNDLE is the bundle file created by the strip. Note that
169 the local revision numbers will in general be different after the
170 restore.
171
172 Use the --no-backup option to discard the backup bundle once the
173 operation completes.
174
175 Strip is not a history-rewriting operation and can be used on
176 changesets in the public phase. But if the stripped changesets have
177 been pushed to a remote repository you will likely pull them again.
178
179 Return 0 on success.
180 """
181 opts = pycompat.byteskwargs(opts)
182 backup = True
183 if opts.get(b'no_backup') or opts.get(b'nobackup'):
184 backup = False
185
186 cl = repo.changelog
187 revs = list(revs) + opts.get(b'rev')
188 revs = set(scmutil.revrange(repo, revs))
189
190 with repo.wlock():
191 bookmarks = set(opts.get(b'bookmark'))
192 if bookmarks:
193 repomarks = repo._bookmarks
194 if not bookmarks.issubset(repomarks):
195 raise error.Abort(
196 _(b"bookmark '%s' not found")
197 % b','.join(sorted(bookmarks - set(repomarks.keys())))
198 )
199
200 # If the requested bookmark is not the only one pointing to a
201 # a revision we have to only delete the bookmark and not strip
202 # anything. revsets cannot detect that case.
203 nodetobookmarks = {}
204 for mark, node in pycompat.iteritems(repomarks):
205 nodetobookmarks.setdefault(node, []).append(mark)
206 for marks in nodetobookmarks.values():
207 if bookmarks.issuperset(marks):
208 rsrevs = scmutil.bookmarkrevs(repo, marks[0])
209 revs.update(set(rsrevs))
210 if not revs:
211 with repo.lock(), repo.transaction(b'bookmark') as tr:
212 bmchanges = [(b, None) for b in bookmarks]
213 repomarks.applychanges(repo, tr, bmchanges)
214 for bookmark in sorted(bookmarks):
215 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
216
217 if not revs:
218 raise error.Abort(_(b'empty revision set'))
219
220 descendants = set(cl.descendants(revs))
221 strippedrevs = revs.union(descendants)
222 roots = revs.difference(descendants)
223
224 # if one of the wdir parent is stripped we'll need
225 # to update away to an earlier revision
226 update = any(
227 p != nullid and cl.rev(p) in strippedrevs
228 for p in repo.dirstate.parents()
229 )
230
231 rootnodes = {cl.node(r) for r in roots}
232
233 q = getattr(repo, 'mq', None)
234 if q is not None and q.applied:
235 # refresh queue state if we're about to strip
236 # applied patches
237 if cl.rev(repo.lookup(b'qtip')) in strippedrevs:
238 q.applieddirty = True
239 start = 0
240 end = len(q.applied)
241 for i, statusentry in enumerate(q.applied):
242 if statusentry.node in rootnodes:
243 # if one of the stripped roots is an applied
244 # patch, only part of the queue is stripped
245 start = i
246 break
247 del q.applied[start:end]
248 q.savedirty()
249
250 revs = sorted(rootnodes)
251 if update and opts.get(b'keep'):
252 urev = _findupdatetarget(repo, revs)
253 uctx = repo[urev]
254
255 # only reset the dirstate for files that would actually change
256 # between the working context and uctx
257 descendantrevs = repo.revs(b"only(., %d)", uctx.rev())
258 changedfiles = []
259 for rev in descendantrevs:
260 # blindly reset the files, regardless of what actually changed
261 changedfiles.extend(repo[rev].files())
262
263 # reset files that only changed in the dirstate too
264 dirstate = repo.dirstate
265 dirchanges = [f for f in dirstate if dirstate[f] != b'n']
266 changedfiles.extend(dirchanges)
267
268 repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
269 repo.dirstate.write(repo.currenttransaction())
270
271 # clear resolve state
272 mergestatemod.mergestate.clean(repo)
273
274 update = False
275
276 strip(
277 ui,
278 repo,
279 revs,
280 backup=backup,
281 update=update,
282 force=opts.get(b'force'),
283 bookmarks=bookmarks,
284 soft=opts[b'soft'],
285 )
286
287 return 0
21 # This is a bit ugly, but a uisetup function that defines strip as an
22 # alias for debugstrip would override any user alias for strip,
23 # including aliases like "strip = strip --no-backup".
24 commands.command.rename(old=b'debugstrip', new=b'debugstrip|strip')
@@ -78,6 +78,7 b' from . import ('
78 78 sshpeer,
79 79 sslutil,
80 80 streamclone,
81 strip,
81 82 tags as tagsmod,
82 83 templater,
83 84 treediscovery,
@@ -105,8 +106,9 b' from .revlogutils import ('
105 106
106 107 release = lockmod.release
107 108
108 command = registrar.command()
109
109 table = {}
110 table.update(strip.command._table)
111 command = registrar.command(table)
110 112
111 113 @command(b'debugancestor', [], _(b'[INDEX] REV1 REV2'), optionalrepo=True)
112 114 def debugancestor(ui, repo, *args):
@@ -234,6 +234,11 b' class command(_funcregistrarbase):'
234 234 self._table[name] = func, list(options)
235 235 return func
236 236
237 def rename(self, old, new):
238 """rename a command. Used to add aliases, debugstrip ->
239 debugstrip|strip
240 """
241 self._table[new] = self._table.pop(old)
237 242
238 243 INTENT_READONLY = b'readonly'
239 244
@@ -1,13 +1,8 b''
1 """strip changesets and their descendants from history
2
3 This extension allows you to strip changesets and all their descendants from the
4 repository. See the command help for details.
5 """
6 1 from __future__ import absolute_import
7 2
8 from mercurial.i18n import _
9 from mercurial.pycompat import getattr
10 from mercurial import (
3 from .i18n import _
4 from .pycompat import getattr
5 from . import (
11 6 bookmarks as bookmarksmod,
12 7 cmdutil,
13 8 error,
@@ -27,11 +22,6 b' release = lockmod.release'
27 22
28 23 cmdtable = {}
29 24 command = registrar.command(cmdtable)
30 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
31 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
32 # be specifying the version(s) of Mercurial they are tested with, or
33 # leave the attribute unspecified.
34 testedwith = b'ships-with-hg-core'
35 25
36 26
37 27 def checklocalchanges(repo, force=False):
@@ -101,7 +91,7 b' def strip('
101 91
102 92
103 93 @command(
104 b"strip",
94 b"debugstrip",
105 95 [
106 96 (
107 97 b'r',
@@ -146,10 +136,10 b' def strip('
146 136 _(b"simply drop changesets from visible history (EXPERIMENTAL)"),
147 137 ),
148 138 ],
149 _(b'hg strip [-k] [-f] [-B bookmark] [-r] REV...'),
139 _(b'hg debugstrip [-k] [-f] [-B bookmark] [-r] REV...'),
150 140 helpcategory=command.CATEGORY_MAINTENANCE,
151 141 )
152 def stripcmd(ui, repo, *revs, **opts):
142 def debugstrip(ui, repo, *revs, **opts):
153 143 """strip changesets and all their descendants from the repository
154 144
155 145 The strip command removes the specified changesets and all their
@@ -2,6 +2,9 b''
2 2
3 3
4 4
5 * `hg strip`, from the strip extension, is now a core command, `hg
6 debugstrip`. The extension remains for compatibility.
7
5 8 == New Experimental Features ==
6 9
7 10
@@ -131,6 +131,7 b' Show debug commands if there are no othe'
131 131 debugsetparents
132 132 debugsidedata
133 133 debugssl
134 debugstrip
134 135 debugsub
135 136 debugsuccessorssets
136 137 debugtagscache
@@ -319,6 +320,7 b' Show all commands + options'
319 320 debugsetparents:
320 321 debugsidedata: changelog, manifest, dir
321 322 debugssl:
323 debugstrip: rev, force, no-backup, nobackup, , keep, bookmark, soft
322 324 debugsub: rev
323 325 debugsuccessorssets: closest
324 326 debugtagscache:
@@ -380,7 +380,6 b' Test extension help:'
380 380 relink recreates hardlinks between repository clones
381 381 schemes extend schemes with shortcuts to repository swarms
382 382 share share a common history between several working directories
383 strip strip changesets and their descendants from history
384 383 transplant command to transplant changesets from another branch
385 384 win32mbcs allow the use of MBCS paths with problematic encodings
386 385 zeroconf discover and advertise repositories on the local network
@@ -1067,6 +1066,7 b' Test list of internal help commands'
1067 1066 debugsidedata
1068 1067 dump the side data for a cl/manifest/file revision
1069 1068 debugssl test a secure connection to a server
1069 debugstrip strip changesets and all their descendants from the repository
1070 1070 debugsub (no help text available)
1071 1071 debugsuccessorssets
1072 1072 show set of successors for revision
@@ -10,7 +10,7 b''
10 10 > hg up -C $1
11 11 > echo % before update $1, strip $2
12 12 > hg log -G -T '{rev}:{node}'
13 > hg --traceback strip $2
13 > hg --traceback debugstrip $2
14 14 > echo % after update $1, strip $2
15 15 > hg log -G -T '{rev}:{node}'
16 16 > restore
@@ -765,13 +765,11 b' test hg strip -B bookmark'
765 765 Make sure no one adds back a -b option:
766 766
767 767 $ hg strip -b tip
768 hg strip: option -b not recognized
769 hg strip [-k] [-f] [-B bookmark] [-r] REV...
768 hg debugstrip: option -b not recognized
769 hg debugstrip [-k] [-f] [-B bookmark] [-r] REV...
770 770
771 771 strip changesets and all their descendants from the repository
772 772
773 (use 'hg help -e strip' to show help for the strip extension)
774
775 773 options ([+] can be repeated):
776 774
777 775 -r --rev REV [+] strip specified revision (optional, can specify
@@ -783,7 +781,7 b' Make sure no one adds back a -b option:'
783 781 -B --bookmark BOOKMARK [+] remove revs only reachable from given bookmark
784 782 --mq operate on patch repository
785 783
786 (use 'hg strip -h' to show more help)
784 (use 'hg debugstrip -h' to show more help)
787 785 [255]
788 786
789 787 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now