##// END OF EJS Templates
py3: handle keyword arguments in hgext/uncommit.py...
Pulkit Goyal -
r35005:3ebae3ec default
parent child Browse files
Show More
@@ -1,194 +1,196
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 obsolete,
32 pycompat,
32 33 registrar,
33 34 scmutil,
34 35 )
35 36
36 37 cmdtable = {}
37 38 command = registrar.command(cmdtable)
38 39
39 40 configtable = {}
40 41 configitem = registrar.configitem(configtable)
41 42
42 43 configitem('experimental', 'uncommitondirtywdir',
43 44 default=False,
44 45 )
45 46
46 47 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
47 48 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
48 49 # be specifying the version(s) of Mercurial they are tested with, or
49 50 # leave the attribute unspecified.
50 51 testedwith = 'ships-with-hg-core'
51 52
52 53 def _commitfiltered(repo, ctx, match, allowempty):
53 54 """Recommit ctx with changed files not in match. Return the new
54 55 node identifier, or None if nothing changed.
55 56 """
56 57 base = ctx.p1()
57 58 # ctx
58 59 initialfiles = set(ctx.files())
59 60 exclude = set(f for f in initialfiles if match(f))
60 61
61 62 # No files matched commit, so nothing excluded
62 63 if not exclude:
63 64 return None
64 65
65 66 files = (initialfiles - exclude)
66 67 # return the p1 so that we don't create an obsmarker later
67 68 if not files and not allowempty:
68 69 return ctx.parents()[0].node()
69 70
70 71 # Filter copies
71 72 copied = copies.pathcopies(base, ctx)
72 73 copied = dict((dst, src) for dst, src in copied.iteritems()
73 74 if dst in files)
74 75 def filectxfn(repo, memctx, path, contentctx=ctx, redirect=()):
75 76 if path not in contentctx:
76 77 return None
77 78 fctx = contentctx[path]
78 79 mctx = context.memfilectx(repo, fctx.path(), fctx.data(),
79 80 fctx.islink(),
80 81 fctx.isexec(),
81 82 copied=copied.get(path))
82 83 return mctx
83 84
84 85 new = context.memctx(repo,
85 86 parents=[base.node(), node.nullid],
86 87 text=ctx.description(),
87 88 files=files,
88 89 filectxfn=filectxfn,
89 90 user=ctx.user(),
90 91 date=ctx.date(),
91 92 extra=ctx.extra())
92 93 # phase handling
93 94 commitphase = ctx.phase()
94 95 overrides = {('phases', 'new-commit'): commitphase}
95 96 with repo.ui.configoverride(overrides, 'uncommit'):
96 97 newid = repo.commitctx(new)
97 98 return newid
98 99
99 100 def _uncommitdirstate(repo, oldctx, match):
100 101 """Fix the dirstate after switching the working directory from
101 102 oldctx to a copy of oldctx not containing changed files matched by
102 103 match.
103 104 """
104 105 ctx = repo['.']
105 106 ds = repo.dirstate
106 107 copies = dict(ds.copies())
107 108 s = repo.status(oldctx.p1(), oldctx, match=match)
108 109 for f in s.modified:
109 110 if ds[f] == 'r':
110 111 # modified + removed -> removed
111 112 continue
112 113 ds.normallookup(f)
113 114
114 115 for f in s.added:
115 116 if ds[f] == 'r':
116 117 # added + removed -> unknown
117 118 ds.drop(f)
118 119 elif ds[f] != 'a':
119 120 ds.add(f)
120 121
121 122 for f in s.removed:
122 123 if ds[f] == 'a':
123 124 # removed + added -> normal
124 125 ds.normallookup(f)
125 126 elif ds[f] != 'r':
126 127 ds.remove(f)
127 128
128 129 # Merge old parent and old working dir copies
129 130 oldcopies = {}
130 131 for f in (s.modified + s.added):
131 132 src = oldctx[f].renamed()
132 133 if src:
133 134 oldcopies[f] = src[0]
134 135 oldcopies.update(copies)
135 136 copies = dict((dst, oldcopies.get(src, src))
136 137 for dst, src in oldcopies.iteritems())
137 138 # Adjust the dirstate copies
138 139 for dst, src in copies.iteritems():
139 140 if (src not in ctx or dst in ctx or ds[dst] != 'a'):
140 141 src = None
141 142 ds.copy(src, dst)
142 143
143 144 @command('uncommit',
144 145 [('', 'keep', False, _('allow an empty commit after uncommiting')),
145 146 ] + commands.walkopts,
146 147 _('[OPTION]... [FILE]...'))
147 148 def uncommit(ui, repo, *pats, **opts):
148 149 """uncommit part or all of a local changeset
149 150
150 151 This command undoes the effect of a local commit, returning the affected
151 152 files to their uncommitted state. This means that files modified or
152 153 deleted in the changeset will be left unchanged, and so will remain
153 154 modified in the working directory.
154 155 """
156 opts = pycompat.byteskwargs(opts)
155 157
156 158 with repo.wlock(), repo.lock():
157 159 wctx = repo[None]
158 160
159 161 if not pats and not repo.ui.configbool('experimental',
160 162 'uncommitondirtywdir'):
161 163 cmdutil.bailifchanged(repo)
162 164 if wctx.parents()[0].node() == node.nullid:
163 165 raise error.Abort(_("cannot uncommit null changeset"))
164 166 if len(wctx.parents()) > 1:
165 167 raise error.Abort(_("cannot uncommit while merging"))
166 168 old = repo['.']
167 169 if not old.mutable():
168 170 raise error.Abort(_('cannot uncommit public changesets'))
169 171 if len(old.parents()) > 1:
170 172 raise error.Abort(_("cannot uncommit merge changeset"))
171 173 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
172 174 if not allowunstable and old.children():
173 175 raise error.Abort(_('cannot uncommit changeset with children'))
174 176
175 177 with repo.transaction('uncommit'):
176 178 match = scmutil.match(old, pats, opts)
177 179 newid = _commitfiltered(repo, old, match, opts.get('keep'))
178 180 if newid is None:
179 181 ui.status(_("nothing to uncommit\n"))
180 182 return 1
181 183
182 184 mapping = {}
183 185 if newid != old.p1().node():
184 186 # Move local changes on filtered changeset
185 187 mapping[old.node()] = (newid,)
186 188 else:
187 189 # Fully removed the old commit
188 190 mapping[old.node()] = ()
189 191
190 192 scmutil.cleanupnodes(repo, mapping, 'uncommit')
191 193
192 194 with repo.dirstate.parentchange():
193 195 repo.dirstate.setparents(newid, node.nullid)
194 196 _uncommitdirstate(repo, old, match)
General Comments 0
You need to be logged in to leave comments. Login now