##// END OF EJS Templates
graft: move "dry_run" and "base" in statedate...
marmoute -
r53229:8d702921 default
parent child Browse files
Show More
@@ -1,318 +1,321
1 1 # graft.py - implementation of the graft command
2 2
3 3 from ..i18n import _
4 4
5 5 from .. import cmdutil, error, logcmdutil, merge as mergemod, state as statemod
6 6
7 7
8 8 def cmd_graft(ui, repo, *revs, **opts):
9 9 """implement the graft command as defined in mercuria/commands.py"""
10 10 ret = _process_args(ui, repo, *revs, **opts)
11 11 if ret is None:
12 12 return -1
13 13 action, args = ret
14 14 if action == "ABORT":
15 15 return cmdutil.abortgraft(ui, repo, *args)
16 16 elif action == "STOP":
17 17 return _stopgraft(ui, repo, *args)
18 18 elif action == "GRAFT":
19 19 return _graft_revisions(ui, repo, *args)
20 20 else:
21 21 raise error.ProgrammingError(b'unknown action: %s' % action)
22 22 return 0
23 23
24 24
25 25 def _process_args(ui, repo, *revs, **opts):
26 26 """process the graft command argument to figure out what to do
27 27
28 28 This also filter the selected revision to skip the one that cannot be graft
29 29 or were alredy grafted.
30 30 """
31 31 if revs and opts.get('rev'):
32 32 ui.warn(
33 33 _(
34 34 b'warning: inconsistent use of --rev might give unexpected '
35 35 b'revision ordering!\n'
36 36 )
37 37 )
38 38
39 39 revs = list(revs)
40 40 revs.extend(opts.get('rev'))
41 41 # a dict of data to be stored in state file
42 42 statedata = {}
43 43 # list of new nodes created by ongoing graft
44 44 statedata[b'newnodes'] = []
45 45
46 46 cmdutil.resolve_commit_options(ui, opts)
47 47
48 48 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
49 49
50 50 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
51 51
52 52 cont = False
53 53 if opts.get('no_commit'):
54 54 cmdutil.check_incompatible_arguments(
55 55 opts,
56 56 'no_commit',
57 57 ['edit', 'currentuser', 'currentdate', 'log'],
58 58 )
59 59
60 60 graftstate = statemod.cmdstate(repo, b'graftstate')
61 61
62 62 if opts.get('stop'):
63 63 cmdutil.check_incompatible_arguments(
64 64 opts,
65 65 'stop',
66 66 [
67 67 'edit',
68 68 'log',
69 69 'user',
70 70 'date',
71 71 'currentdate',
72 72 'currentuser',
73 73 'rev',
74 74 ],
75 75 )
76 76 return "STOP", [graftstate]
77 77 elif opts.get('abort'):
78 78 cmdutil.check_incompatible_arguments(
79 79 opts,
80 80 'abort',
81 81 [
82 82 'edit',
83 83 'log',
84 84 'user',
85 85 'date',
86 86 'currentdate',
87 87 'currentuser',
88 88 'rev',
89 89 ],
90 90 )
91 91 return "ABORT", [graftstate]
92 92 elif opts.get('continue'):
93 93 cont = True
94 94 if revs:
95 95 raise error.InputError(_(b"can't specify --continue and revisions"))
96 96 # read in unfinished revisions
97 97 if graftstate.exists():
98 98 statedata = cmdutil.readgraftstate(repo, graftstate)
99 99 if statedata.get(b'no_commit'):
100 100 opts['no_commit'] = statedata.get(b'no_commit')
101 101 if statedata.get(b'base'):
102 102 opts['base'] = statedata.get(b'base')
103 103 nodes = statedata[b'nodes']
104 104 revs = [repo[node].rev() for node in nodes]
105 105 else:
106 106 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
107 107 elif not revs:
108 108 raise error.InputError(_(b'no revisions specified'))
109 109 else:
110 110 cmdutil.checkunfinished(repo)
111 111 cmdutil.bailifchanged(repo)
112 112 revs = logcmdutil.revrange(repo, revs)
113 113
114 114 for o in (
115 115 b'date',
116 116 b'user',
117 117 b'log',
118 118 b'no_commit',
119 b'dry_run',
119 120 ):
120 121 v = opts.get(o.decode('ascii'))
121 122 # if statedata is already set, it comes from --continue and test says
122 123 # we should honor them above the options (which seems weird).
123 124 if v and o not in statedata:
124 125 statedata[o] = v
125 126
126 127 skipped = set()
127 128 basectx = None
128 129 if opts.get('base'):
129 130 basectx = logcmdutil.revsingle(repo, opts['base'], None)
131 statedata[b'base'] = basectx.hex()
130 132 if basectx is None:
131 133 # check for merges
132 134 for rev in repo.revs(b'%ld and merge()', revs):
133 135 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
134 136 skipped.add(rev)
135 137 revs = [r for r in revs if r not in skipped]
136 138 if not revs:
137 139 return None
138 140 if basectx is not None and len(revs) != 1:
139 141 raise error.InputError(_(b'only one revision allowed with --base '))
140 142
141 143 # Don't check in the --continue case, in effect retaining --force across
142 144 # --continues. That's because without --force, any revisions we decided to
143 145 # skip would have been filtered out here, so they wouldn't have made their
144 146 # way to the graftstate. With --force, any revisions we would have otherwise
145 147 # skipped would not have been filtered out, and if they hadn't been applied
146 148 # already, they'd have been in the graftstate.
147 149 if not (cont or opts.get('force')) and basectx is None:
148 150 # check for ancestors of dest branch
149 151 ancestors = repo.revs(b'%ld & (::.)', revs)
150 152 for rev in ancestors:
151 153 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
152 154
153 155 revs = [r for r in revs if r not in ancestors]
154 156
155 157 if not revs:
156 158 return None
157 159
158 160 # analyze revs for earlier grafts
159 161 ids = {}
160 162 for ctx in repo.set(b"%ld", revs):
161 163 ids[ctx.hex()] = ctx.rev()
162 164 n = ctx.extra().get(b'source')
163 165 if n:
164 166 ids[n] = ctx.rev()
165 167
166 168 # check ancestors for earlier grafts
167 169 ui.debug(b'scanning for duplicate grafts\n')
168 170
169 171 # The only changesets we can be sure doesn't contain grafts of any
170 172 # revs, are the ones that are common ancestors of *all* revs:
171 173 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
172 174 ctx = repo[rev]
173 175 n = ctx.extra().get(b'source')
174 176 if n in ids:
175 177 try:
176 178 r = repo[n].rev()
177 179 except error.RepoLookupError:
178 180 r = None
179 181 if r in revs:
180 182 ui.warn(
181 183 _(
182 184 b'skipping revision %d:%s '
183 185 b'(already grafted to %d:%s)\n'
184 186 )
185 187 % (r, repo[r], rev, ctx)
186 188 )
187 189 revs.remove(r)
188 190 elif ids[n] in revs:
189 191 if r is None:
190 192 ui.warn(
191 193 _(
192 194 b'skipping already grafted revision %d:%s '
193 195 b'(%d:%s also has unknown origin %s)\n'
194 196 )
195 197 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
196 198 )
197 199 else:
198 200 ui.warn(
199 201 _(
200 202 b'skipping already grafted revision %d:%s '
201 203 b'(%d:%s also has origin %d:%s)\n'
202 204 )
203 205 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
204 206 )
205 207 revs.remove(ids[n])
206 208 elif ctx.hex() in ids:
207 209 r = ids[ctx.hex()]
208 210 if r in revs:
209 211 ui.warn(
210 212 _(
211 213 b'skipping already grafted revision %d:%s '
212 214 b'(was grafted from %d:%s)\n'
213 215 )
214 216 % (r, repo[r], rev, ctx)
215 217 )
216 218 revs.remove(r)
217 219 if not revs:
218 220 return None
219 221
220 if opts.get('base'):
221 statedata[b'base'] = opts['base']
222
223 return "GRAFT", [graftstate, statedata, revs, editor, basectx, cont, opts]
222 dry_run = bool(opts.get("dry_run"))
223 return "GRAFT", [graftstate, statedata, revs, editor, cont, dry_run, opts]
224 224
225 225
226 226 def _graft_revisions(
227 227 ui,
228 228 repo,
229 229 graftstate,
230 230 statedata,
231 231 revs,
232 232 editor,
233 basectx,
234 233 cont=False,
234 dry_run=False,
235 235 opts,
236 236 ):
237 237 """actually graft some revisions"""
238 238 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
239 239 desc = b'%d:%s "%s"' % (
240 240 ctx.rev(),
241 241 ctx,
242 242 ctx.description().split(b'\n', 1)[0],
243 243 )
244 244 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
245 245 if names:
246 246 desc += b' (%s)' % b' '.join(names)
247 247 ui.status(_(b'grafting %s\n') % desc)
248 if opts.get('dry_run'):
248 if dry_run:
249 249 continue
250 250
251 251 source = ctx.extra().get(b'source')
252 252 extra = {}
253 253 if source:
254 254 extra[b'source'] = source
255 255 extra[b'intermediate-source'] = ctx.hex()
256 256 else:
257 257 extra[b'source'] = ctx.hex()
258 258 user = statedata.get(b'user', ctx.user())
259 259 date = statedata.get(b'date', ctx.date())
260 260 message = ctx.description()
261 261 if statedata.get(b'log'):
262 262 message += b'\n(grafted from %s)' % ctx.hex()
263 263
264 264 # we don't merge the first commit when continuing
265 265 if not cont:
266 266 # perform the graft merge with p1(rev) as 'ancestor'
267 267 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
268 base = ctx.p1() if basectx is None else basectx
268 if b'base' in statedata:
269 base = repo[statedata[b'base']]
270 else:
271 base = ctx.p1()
269 272 with ui.configoverride(overrides, b'graft'):
270 273 stats = mergemod.graft(
271 274 repo, ctx, base, [b'local', b'graft', b'parent of graft']
272 275 )
273 276 # report any conflicts
274 277 if stats.unresolvedcount > 0:
275 278 # write out state for --continue
276 279 nodes = [repo[rev].hex() for rev in revs[pos:]]
277 280 statedata[b'nodes'] = nodes
278 281 stateversion = 1
279 282 graftstate.save(stateversion, statedata)
280 283 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
281 284 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
282 285 return 1
283 286 else:
284 287 cont = False
285 288
286 289 # commit if --no-commit is false
287 290 if not statedata.get(b'no_commit'):
288 291 node = repo.commit(
289 292 text=message, user=user, date=date, extra=extra, editor=editor
290 293 )
291 294 if node is None:
292 295 ui.warn(
293 296 _(b'note: graft of %d:%s created no changes to commit\n')
294 297 % (ctx.rev(), ctx)
295 298 )
296 299 # checking that newnodes exist because old state files won't have it
297 300 elif statedata.get(b'newnodes') is not None:
298 301 nn = statedata[b'newnodes']
299 302 assert isinstance(nn, list) # list of bytes
300 303 nn.append(node)
301 304
302 305 # remove state when we complete successfully
303 if not opts.get('dry_run'):
306 if not dry_run:
304 307 graftstate.delete()
305 308
306 309 return 0
307 310
308 311
309 312 def _stopgraft(ui, repo, graftstate):
310 313 """stop the interrupted graft"""
311 314 if not graftstate.exists():
312 315 raise error.StateError(_(b"no interrupted graft found"))
313 316 pctx = repo[b'.']
314 317 mergemod.clean_update(pctx)
315 318 graftstate.delete()
316 319 ui.status(_(b"stopped the interrupted graft\n"))
317 320 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
318 321 return 0
General Comments 0
You need to be logged in to leave comments. Login now