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