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