##// END OF EJS Templates
uncommit: fix unaligned indentation...
Martin von Zweigbergk -
r36965:3d0178bf default
parent child Browse files
Show More
@@ -1,256 +1,256 b''
1 1 # uncommit - undo the actions of a commit
2 2 #
3 3 # Copyright 2011 Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
4 4 # Logilab SA <contact@logilab.fr>
5 5 # Pierre-Yves David <pierre-yves.david@ens-lyon.org>
6 6 # Patrick Mezard <patrick@mezard.eu>
7 7 # Copyright 2016 Facebook, Inc.
8 8 #
9 9 # This software may be used and distributed according to the terms of the
10 10 # GNU General Public License version 2 or any later version.
11 11
12 12 """uncommit part or all of a local changeset (EXPERIMENTAL)
13 13
14 14 This command undoes the effect of a local commit, returning the affected
15 15 files to their uncommitted state. This means that files modified, added or
16 16 removed in the changeset will be left unchanged, and so will remain modified,
17 17 added and removed in the working directory.
18 18 """
19 19
20 20 from __future__ import absolute_import
21 21
22 22 from mercurial.i18n import _
23 23
24 24 from mercurial import (
25 25 cmdutil,
26 26 commands,
27 27 context,
28 28 copies,
29 29 error,
30 30 node,
31 31 obsutil,
32 32 pycompat,
33 33 registrar,
34 34 rewriteutil,
35 35 scmutil,
36 36 )
37 37
38 38 cmdtable = {}
39 39 command = registrar.command(cmdtable)
40 40
41 41 configtable = {}
42 42 configitem = registrar.configitem(configtable)
43 43
44 44 configitem('experimental', 'uncommitondirtywdir',
45 45 default=False,
46 46 )
47 47
48 48 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
49 49 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
50 50 # be specifying the version(s) of Mercurial they are tested with, or
51 51 # leave the attribute unspecified.
52 52 testedwith = 'ships-with-hg-core'
53 53
54 54 def _commitfiltered(repo, ctx, match, allowempty):
55 55 """Recommit ctx with changed files not in match. Return the new
56 56 node identifier, or None if nothing changed.
57 57 """
58 58 base = ctx.p1()
59 59 # ctx
60 60 initialfiles = set(ctx.files())
61 61 exclude = set(f for f in initialfiles if match(f))
62 62
63 63 # No files matched commit, so nothing excluded
64 64 if not exclude:
65 65 return None
66 66
67 67 files = (initialfiles - exclude)
68 68 # return the p1 so that we don't create an obsmarker later
69 69 if not files and not allowempty:
70 70 return ctx.parents()[0].node()
71 71
72 72 # Filter copies
73 73 copied = copies.pathcopies(base, ctx)
74 74 copied = dict((dst, src) for dst, src in copied.iteritems()
75 75 if dst in files)
76 76 def filectxfn(repo, memctx, path, contentctx=ctx, redirect=()):
77 77 if path not in contentctx:
78 78 return None
79 79 fctx = contentctx[path]
80 80 mctx = context.memfilectx(repo, memctx, fctx.path(), fctx.data(),
81 81 fctx.islink(),
82 82 fctx.isexec(),
83 83 copied=copied.get(path))
84 84 return mctx
85 85
86 86 new = context.memctx(repo,
87 87 parents=[base.node(), node.nullid],
88 88 text=ctx.description(),
89 89 files=files,
90 90 filectxfn=filectxfn,
91 91 user=ctx.user(),
92 92 date=ctx.date(),
93 93 extra=ctx.extra())
94 94 # phase handling
95 95 commitphase = ctx.phase()
96 96 overrides = {('phases', 'new-commit'): commitphase}
97 97 with repo.ui.configoverride(overrides, 'uncommit'):
98 98 newid = repo.commitctx(new)
99 99 return newid
100 100
101 101 def _fixdirstate(repo, oldctx, newctx, status):
102 102 """ fix the dirstate after switching the working directory from oldctx to
103 103 newctx which can be result of either unamend or uncommit.
104 104 """
105 105 ds = repo.dirstate
106 106 copies = dict(ds.copies())
107 107 s = status
108 108 for f in s.modified:
109 109 if ds[f] == 'r':
110 110 # modified + removed -> removed
111 111 continue
112 112 ds.normallookup(f)
113 113
114 114 for f in s.added:
115 115 if ds[f] == 'r':
116 116 # added + removed -> unknown
117 117 ds.drop(f)
118 118 elif ds[f] != 'a':
119 119 ds.add(f)
120 120
121 121 for f in s.removed:
122 122 if ds[f] == 'a':
123 123 # removed + added -> normal
124 124 ds.normallookup(f)
125 125 elif ds[f] != 'r':
126 126 ds.remove(f)
127 127
128 128 # Merge old parent and old working dir copies
129 129 oldcopies = {}
130 130 for f in (s.modified + s.added):
131 131 src = oldctx[f].renamed()
132 132 if src:
133 133 oldcopies[f] = src[0]
134 134 oldcopies.update(copies)
135 135 copies = dict((dst, oldcopies.get(src, src))
136 136 for dst, src in oldcopies.iteritems())
137 137 # Adjust the dirstate copies
138 138 for dst, src in copies.iteritems():
139 139 if (src not in newctx or dst in newctx or ds[dst] != 'a'):
140 140 src = None
141 141 ds.copy(src, dst)
142 142
143 143 @command('uncommit',
144 144 [('', 'keep', False, _('allow an empty commit after uncommiting')),
145 145 ] + commands.walkopts,
146 146 _('[OPTION]... [FILE]...'))
147 147 def uncommit(ui, repo, *pats, **opts):
148 148 """uncommit part or all of a local changeset
149 149
150 150 This command undoes the effect of a local commit, returning the affected
151 151 files to their uncommitted state. This means that files modified or
152 152 deleted in the changeset will be left unchanged, and so will remain
153 153 modified in the working directory.
154 154 """
155 155 opts = pycompat.byteskwargs(opts)
156 156
157 157 with repo.wlock(), repo.lock():
158 158
159 159 if not pats and not repo.ui.configbool('experimental',
160 'uncommitondirtywdir'):
160 'uncommitondirtywdir'):
161 161 cmdutil.bailifchanged(repo)
162 162 old = repo['.']
163 163 rewriteutil.precheck(repo, [old.rev()], 'uncommit')
164 164 if len(old.parents()) > 1:
165 165 raise error.Abort(_("cannot uncommit merge changeset"))
166 166
167 167 with repo.transaction('uncommit'):
168 168 match = scmutil.match(old, pats, opts)
169 169 newid = _commitfiltered(repo, old, match, opts.get('keep'))
170 170 if newid is None:
171 171 ui.status(_("nothing to uncommit\n"))
172 172 return 1
173 173
174 174 mapping = {}
175 175 if newid != old.p1().node():
176 176 # Move local changes on filtered changeset
177 177 mapping[old.node()] = (newid,)
178 178 else:
179 179 # Fully removed the old commit
180 180 mapping[old.node()] = ()
181 181
182 182 scmutil.cleanupnodes(repo, mapping, 'uncommit')
183 183
184 184 with repo.dirstate.parentchange():
185 185 repo.dirstate.setparents(newid, node.nullid)
186 186 s = repo.status(old.p1(), old, match=match)
187 187 _fixdirstate(repo, old, repo[newid], s)
188 188
189 189 def predecessormarkers(ctx):
190 190 """yields the obsolete markers marking the given changeset as a successor"""
191 191 for data in ctx.repo().obsstore.predecessors.get(ctx.node(), ()):
192 192 yield obsutil.marker(ctx.repo(), data)
193 193
194 194 @command('^unamend', [])
195 195 def unamend(ui, repo, **opts):
196 196 """undo the most recent amend operation on a current changeset
197 197
198 198 This command will roll back to the previous version of a changeset,
199 199 leaving working directory in state in which it was before running
200 200 `hg amend` (e.g. files modified as part of an amend will be
201 201 marked as modified `hg status`)
202 202 """
203 203
204 204 unfi = repo.unfiltered()
205 205 with repo.wlock(), repo.lock(), repo.transaction('unamend'):
206 206
207 207 # identify the commit from which to unamend
208 208 curctx = repo['.']
209 209
210 210 rewriteutil.precheck(repo, [curctx.rev()], 'unamend')
211 211
212 212 # identify the commit to which to unamend
213 213 markers = list(predecessormarkers(curctx))
214 214 if len(markers) != 1:
215 215 e = _("changeset must have one predecessor, found %i predecessors")
216 216 raise error.Abort(e % len(markers))
217 217
218 218 prednode = markers[0].prednode()
219 219 predctx = unfi[prednode]
220 220
221 221 # add an extra so that we get a new hash
222 222 # note: allowing unamend to undo an unamend is an intentional feature
223 223 extras = predctx.extra()
224 224 extras['unamend_source'] = curctx.hex()
225 225
226 226 def filectxfn(repo, ctx_, path):
227 227 try:
228 228 return predctx.filectx(path)
229 229 except KeyError:
230 230 return None
231 231
232 232 # Make a new commit same as predctx
233 233 newctx = context.memctx(repo,
234 234 parents=(predctx.p1(), predctx.p2()),
235 235 text=predctx.description(),
236 236 files=predctx.files(),
237 237 filectxfn=filectxfn,
238 238 user=predctx.user(),
239 239 date=predctx.date(),
240 240 extra=extras)
241 241 # phase handling
242 242 commitphase = curctx.phase()
243 243 overrides = {('phases', 'new-commit'): commitphase}
244 244 with repo.ui.configoverride(overrides, 'uncommit'):
245 245 newprednode = repo.commitctx(newctx)
246 246
247 247 newpredctx = repo[newprednode]
248 248 dirstate = repo.dirstate
249 249
250 250 with dirstate.parentchange():
251 251 dirstate.setparents(newprednode, node.nullid)
252 252 s = repo.status(predctx, curctx)
253 253 _fixdirstate(repo, curctx, newpredctx, s)
254 254
255 255 mapping = {curctx.node(): (newprednode,)}
256 256 scmutil.cleanupnodes(repo, mapping, 'unamend')
General Comments 0
You need to be logged in to leave comments. Login now