##// END OF EJS Templates
histedit: make comment part of the file describing rules as translatable...
FUJIWARA Katsunori -
r17315:f320d7ed stable
parent child Browse files
Show More
@@ -1,700 +1,699 b''
1 1 # histedit.py - interactive history editing for mercurial
2 2 #
3 3 # Copyright 2009 Augie Fackler <raf@durin42.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 """interactive history editing
8 8
9 9 With this extension installed, Mercurial gains one new command: histedit. Usage
10 10 is as follows, assuming the following history::
11 11
12 12 @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
13 13 | Add delta
14 14 |
15 15 o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
16 16 | Add gamma
17 17 |
18 18 o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
19 19 | Add beta
20 20 |
21 21 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
22 22 Add alpha
23 23
24 24 If you were to run ``hg histedit c561b4e977df``, you would see the following
25 25 file open in your editor::
26 26
27 27 pick c561b4e977df Add beta
28 28 pick 030b686bedc4 Add gamma
29 29 pick 7c2fd3b9020c Add delta
30 30
31 31 # Edit history between 633536316234 and 7c2fd3b9020c
32 32 #
33 33 # Commands:
34 34 # p, pick = use commit
35 35 # e, edit = use commit, but stop for amending
36 36 # f, fold = use commit, but fold into previous commit
37 37 # d, drop = remove commit from history
38 38 # m, mess = edit message without changing commit content
39 39 #
40 40
41 41 In this file, lines beginning with ``#`` are ignored. You must specify a rule
42 42 for each revision in your history. For example, if you had meant to add gamma
43 43 before beta, and then wanted to add delta in the same revision as beta, you
44 44 would reorganize the file to look like this::
45 45
46 46 pick 030b686bedc4 Add gamma
47 47 pick c561b4e977df Add beta
48 48 fold 7c2fd3b9020c Add delta
49 49
50 50 # Edit history between 633536316234 and 7c2fd3b9020c
51 51 #
52 52 # Commands:
53 53 # p, pick = use commit
54 54 # e, edit = use commit, but stop for amending
55 55 # f, fold = use commit, but fold into previous commit
56 56 # d, drop = remove commit from history
57 57 # m, mess = edit message without changing commit content
58 58 #
59 59
60 60 At which point you close the editor and ``histedit`` starts working. When you
61 61 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
62 62 those revisions together, offering you a chance to clean up the commit message::
63 63
64 64 Add beta
65 65 ***
66 66 Add delta
67 67
68 68 Edit the commit message to your liking, then close the editor. For
69 69 this example, let's assume that the commit message was changed to
70 70 ``Add beta and delta.`` After histedit has run and had a chance to
71 71 remove any old or temporary revisions it needed, the history looks
72 72 like this::
73 73
74 74 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
75 75 | Add beta and delta.
76 76 |
77 77 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
78 78 | Add gamma
79 79 |
80 80 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
81 81 Add alpha
82 82
83 83 Note that ``histedit`` does *not* remove any revisions (even its own temporary
84 84 ones) until after it has completed all the editing operations, so it will
85 85 probably perform several strip operations when it's done. For the above example,
86 86 it had to run strip twice. Strip can be slow depending on a variety of factors,
87 87 so you might need to be a little patient. You can choose to keep the original
88 88 revisions by passing the ``--keep`` flag.
89 89
90 90 The ``edit`` operation will drop you back to a command prompt,
91 91 allowing you to edit files freely, or even use ``hg record`` to commit
92 92 some changes as a separate commit. When you're done, any remaining
93 93 uncommitted changes will be committed as well. When done, run ``hg
94 94 histedit --continue`` to finish this step. You'll be prompted for a
95 95 new commit message, but the default commit message will be the
96 96 original message for the ``edit`` ed revision.
97 97
98 98 The ``message`` operation will give you a chance to revise a commit
99 99 message without changing the contents. It's a shortcut for doing
100 100 ``edit`` immediately followed by `hg histedit --continue``.
101 101
102 102 If ``histedit`` encounters a conflict when moving a revision (while
103 103 handling ``pick`` or ``fold``), it'll stop in a similar manner to
104 104 ``edit`` with the difference that it won't prompt you for a commit
105 105 message when done. If you decide at this point that you don't like how
106 106 much work it will be to rearrange history, or that you made a mistake,
107 107 you can use ``hg histedit --abort`` to abandon the new changes you
108 108 have made and return to the state before you attempted to edit your
109 109 history.
110 110
111 111 If we clone the example repository above and add three more changes, such that
112 112 we have the following history::
113 113
114 114 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
115 115 | Add theta
116 116 |
117 117 o 5 140988835471 2009-04-27 18:04 -0500 stefan
118 118 | Add eta
119 119 |
120 120 o 4 122930637314 2009-04-27 18:04 -0500 stefan
121 121 | Add zeta
122 122 |
123 123 o 3 836302820282 2009-04-27 18:04 -0500 stefan
124 124 | Add epsilon
125 125 |
126 126 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
127 127 | Add beta and delta.
128 128 |
129 129 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
130 130 | Add gamma
131 131 |
132 132 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
133 133 Add alpha
134 134
135 135 If you run ``hg histedit --outgoing`` on the clone then it is the same
136 136 as running ``hg histedit 836302820282``. If you need plan to push to a
137 137 repository that Mercurial does not detect to be related to the source
138 138 repo, you can add a ``--force`` option.
139 139 """
140 140
141 141 try:
142 142 import cPickle as pickle
143 143 except ImportError:
144 144 import pickle
145 145 import tempfile
146 146 import os
147 147
148 148 from mercurial import bookmarks
149 149 from mercurial import cmdutil
150 150 from mercurial import discovery
151 151 from mercurial import error
152 152 from mercurial import hg
153 153 from mercurial import node
154 154 from mercurial import patch
155 155 from mercurial import repair
156 156 from mercurial import scmutil
157 157 from mercurial import util
158 158 from mercurial.i18n import _
159 159
160 160 cmdtable = {}
161 161 command = cmdutil.command(cmdtable)
162 162
163 163 testedwith = 'internal'
164 164
165 editcomment = """
166
167 # Edit history between %s and %s
165 editcomment = _("""# Edit history between %s and %s
168 166 #
169 167 # Commands:
170 168 # p, pick = use commit
171 169 # e, edit = use commit, but stop for amending
172 170 # f, fold = use commit, but fold into previous commit (combines N and N-1)
173 171 # d, drop = remove commit from history
174 172 # m, mess = edit message without changing commit content
175 173 #
176 """
174 """)
177 175
178 176 def between(repo, old, new, keep):
179 177 revs = [old]
180 178 current = old
181 179 while current != new:
182 180 ctx = repo[current]
183 181 if not keep and len(ctx.children()) > 1:
184 182 raise util.Abort(_('cannot edit history that would orphan nodes'))
185 183 if len(ctx.parents()) != 1 and ctx.parents()[1] != node.nullid:
186 184 raise util.Abort(_("can't edit history with merges"))
187 185 if not ctx.children():
188 186 current = new
189 187 else:
190 188 current = ctx.children()[0].node()
191 189 revs.append(current)
192 190 if len(repo[current].children()) and not keep:
193 191 raise util.Abort(_('cannot edit history that would orphan nodes'))
194 192 return revs
195 193
196 194
197 195 def pick(ui, repo, ctx, ha, opts):
198 196 oldctx = repo[ha]
199 197 if oldctx.parents()[0] == ctx:
200 198 ui.debug('node %s unchanged\n' % ha)
201 199 return oldctx, [], [], []
202 200 hg.update(repo, ctx.node())
203 201 fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
204 202 fp = os.fdopen(fd, 'w')
205 203 diffopts = patch.diffopts(ui, opts)
206 204 diffopts.git = True
207 205 diffopts.ignorews = False
208 206 diffopts.ignorewsamount = False
209 207 diffopts.ignoreblanklines = False
210 208 gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
211 209 for chunk in gen:
212 210 fp.write(chunk)
213 211 fp.close()
214 212 try:
215 213 files = set()
216 214 try:
217 215 patch.patch(ui, repo, patchfile, files=files, eolmode=None)
218 216 if not files:
219 217 ui.warn(_('%s: empty changeset')
220 218 % node.hex(ha))
221 219 return ctx, [], [], []
222 220 finally:
223 221 os.unlink(patchfile)
224 222 except Exception:
225 223 raise util.Abort(_('Fix up the change and run '
226 224 'hg histedit --continue'))
227 225 n = repo.commit(text=oldctx.description(), user=oldctx.user(),
228 226 date=oldctx.date(), extra=oldctx.extra())
229 227 return repo[n], [n], [oldctx.node()], []
230 228
231 229
232 230 def edit(ui, repo, ctx, ha, opts):
233 231 oldctx = repo[ha]
234 232 hg.update(repo, ctx.node())
235 233 fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
236 234 fp = os.fdopen(fd, 'w')
237 235 diffopts = patch.diffopts(ui, opts)
238 236 diffopts.git = True
239 237 diffopts.ignorews = False
240 238 diffopts.ignorewsamount = False
241 239 diffopts.ignoreblanklines = False
242 240 gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
243 241 for chunk in gen:
244 242 fp.write(chunk)
245 243 fp.close()
246 244 try:
247 245 files = set()
248 246 try:
249 247 patch.patch(ui, repo, patchfile, files=files, eolmode=None)
250 248 finally:
251 249 os.unlink(patchfile)
252 250 except Exception:
253 251 pass
254 252 raise util.Abort(_('Make changes as needed, you may commit or record as '
255 253 'needed now.\nWhen you are finished, run hg'
256 254 ' histedit --continue to resume.'))
257 255
258 256 def fold(ui, repo, ctx, ha, opts):
259 257 oldctx = repo[ha]
260 258 hg.update(repo, ctx.node())
261 259 fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
262 260 fp = os.fdopen(fd, 'w')
263 261 diffopts = patch.diffopts(ui, opts)
264 262 diffopts.git = True
265 263 diffopts.ignorews = False
266 264 diffopts.ignorewsamount = False
267 265 diffopts.ignoreblanklines = False
268 266 gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
269 267 for chunk in gen:
270 268 fp.write(chunk)
271 269 fp.close()
272 270 try:
273 271 files = set()
274 272 try:
275 273 patch.patch(ui, repo, patchfile, files=files, eolmode=None)
276 274 if not files:
277 275 ui.warn(_('%s: empty changeset')
278 276 % node.hex(ha))
279 277 return ctx, [], [], []
280 278 finally:
281 279 os.unlink(patchfile)
282 280 except Exception:
283 281 raise util.Abort(_('Fix up the change and run '
284 282 'hg histedit --continue'))
285 283 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
286 284 date=oldctx.date(), extra=oldctx.extra())
287 285 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
288 286
289 287 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
290 288 parent = ctx.parents()[0].node()
291 289 hg.update(repo, parent)
292 290 fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
293 291 fp = os.fdopen(fd, 'w')
294 292 diffopts = patch.diffopts(ui, opts)
295 293 diffopts.git = True
296 294 diffopts.ignorews = False
297 295 diffopts.ignorewsamount = False
298 296 diffopts.ignoreblanklines = False
299 297 gen = patch.diff(repo, parent, newnode, opts=diffopts)
300 298 for chunk in gen:
301 299 fp.write(chunk)
302 300 fp.close()
303 301 files = set()
304 302 try:
305 303 patch.patch(ui, repo, patchfile, files=files, eolmode=None)
306 304 finally:
307 305 os.unlink(patchfile)
308 306 newmessage = '\n***\n'.join(
309 307 [ctx.description()] +
310 308 [repo[r].description() for r in internalchanges] +
311 309 [oldctx.description()]) + '\n'
312 310 # If the changesets are from the same author, keep it.
313 311 if ctx.user() == oldctx.user():
314 312 username = ctx.user()
315 313 else:
316 314 username = ui.username()
317 315 newmessage = ui.edit(newmessage, username)
318 316 n = repo.commit(text=newmessage, user=username,
319 317 date=max(ctx.date(), oldctx.date()), extra=oldctx.extra())
320 318 return repo[n], [n], [oldctx.node(), ctx.node()], [newnode]
321 319
322 320 def drop(ui, repo, ctx, ha, opts):
323 321 return ctx, [], [repo[ha].node()], []
324 322
325 323
326 324 def message(ui, repo, ctx, ha, opts):
327 325 oldctx = repo[ha]
328 326 hg.update(repo, ctx.node())
329 327 fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
330 328 fp = os.fdopen(fd, 'w')
331 329 diffopts = patch.diffopts(ui, opts)
332 330 diffopts.git = True
333 331 diffopts.ignorews = False
334 332 diffopts.ignorewsamount = False
335 333 diffopts.ignoreblanklines = False
336 334 gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
337 335 for chunk in gen:
338 336 fp.write(chunk)
339 337 fp.close()
340 338 try:
341 339 files = set()
342 340 try:
343 341 patch.patch(ui, repo, patchfile, files=files, eolmode=None)
344 342 finally:
345 343 os.unlink(patchfile)
346 344 except Exception:
347 345 raise util.Abort(_('Fix up the change and run '
348 346 'hg histedit --continue'))
349 347 message = oldctx.description() + '\n'
350 348 message = ui.edit(message, ui.username())
351 349 new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(),
352 350 extra=oldctx.extra())
353 351 newctx = repo[new]
354 352 if oldctx.node() != newctx.node():
355 353 return newctx, [new], [oldctx.node()], []
356 354 # We didn't make an edit, so just indicate no replaced nodes
357 355 return newctx, [new], [], []
358 356
359 357
360 358 def makedesc(c):
361 359 summary = ''
362 360 if c.description():
363 361 summary = c.description().splitlines()[0]
364 362 line = 'pick %s %d %s' % (c.hex()[:12], c.rev(), summary)
365 363 return line[:80] # trim to 80 chars so it's not stupidly wide in my editor
366 364
367 365 actiontable = {'p': pick,
368 366 'pick': pick,
369 367 'e': edit,
370 368 'edit': edit,
371 369 'f': fold,
372 370 'fold': fold,
373 371 'd': drop,
374 372 'drop': drop,
375 373 'm': message,
376 374 'mess': message,
377 375 }
378 376
379 377 @command('histedit',
380 378 [('', 'commands', '',
381 379 _('Read history edits from the specified file.')),
382 380 ('c', 'continue', False, _('continue an edit already in progress')),
383 381 ('k', 'keep', False,
384 382 _("don't strip old nodes after edit is complete")),
385 383 ('', 'abort', False, _('abort an edit in progress')),
386 384 ('o', 'outgoing', False, _('changesets not found in destination')),
387 385 ('f', 'force', False,
388 386 _('force outgoing even for unrelated repositories')),
389 387 ('r', 'rev', [], _('first revision to be edited'))],
390 388 _("[PARENT]"))
391 389 def histedit(ui, repo, *parent, **opts):
392 390 """interactively edit changeset history
393 391 """
394 392 # TODO only abort if we try and histedit mq patches, not just
395 393 # blanket if mq patches are applied somewhere
396 394 mq = getattr(repo, 'mq', None)
397 395 if mq and mq.applied:
398 396 raise util.Abort(_('source has mq patches applied'))
399 397
400 398 parent = list(parent) + opts.get('rev', [])
401 399 if opts.get('outgoing'):
402 400 if len(parent) > 1:
403 401 raise util.Abort(
404 402 _('only one repo argument allowed with --outgoing'))
405 403 elif parent:
406 404 parent = parent[0]
407 405
408 406 dest = ui.expandpath(parent or 'default-push', parent or 'default')
409 407 dest, revs = hg.parseurl(dest, None)[:2]
410 408 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
411 409
412 410 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
413 411 other = hg.peer(repo, opts, dest)
414 412
415 413 if revs:
416 414 revs = [repo.lookup(rev) for rev in revs]
417 415
418 416 parent = discovery.findcommonoutgoing(
419 417 repo, other, [], force=opts.get('force')).missing[0:1]
420 418 else:
421 419 if opts.get('force'):
422 420 raise util.Abort(_('--force only allowed with --outgoing'))
423 421
424 422 if opts.get('continue', False):
425 423 if len(parent) != 0:
426 424 raise util.Abort(_('no arguments allowed with --continue'))
427 425 (parentctxnode, created, replaced,
428 426 tmpnodes, existing, rules, keep, tip, replacemap) = readstate(repo)
429 427 currentparent, wantnull = repo.dirstate.parents()
430 428 parentctx = repo[parentctxnode]
431 429 # existing is the list of revisions initially considered by
432 430 # histedit. Here we use it to list new changesets, descendants
433 431 # of parentctx without an 'existing' changeset in-between. We
434 432 # also have to exclude 'existing' changesets which were
435 433 # previously dropped.
436 434 descendants = set(c.node() for c in
437 435 repo.set('(%n::) - %n', parentctxnode, parentctxnode))
438 436 existing = set(existing)
439 437 notdropped = set(n for n in existing if n in descendants and
440 438 (n not in replacemap or replacemap[n] in descendants))
441 439 # Discover any nodes the user has added in the interim. We can
442 440 # miss changesets which were dropped and recreated the same.
443 441 newchildren = list(c.node() for c in repo.set(
444 442 'sort(%ln - (%ln or %ln::))', descendants, existing, notdropped))
445 443 action, currentnode = rules.pop(0)
446 444 if action in ('f', 'fold'):
447 445 tmpnodes.extend(newchildren)
448 446 else:
449 447 created.extend(newchildren)
450 448
451 449 m, a, r, d = repo.status()[:4]
452 450 oldctx = repo[currentnode]
453 451 message = oldctx.description() + '\n'
454 452 if action in ('e', 'edit', 'm', 'mess'):
455 453 message = ui.edit(message, ui.username())
456 454 elif action in ('f', 'fold'):
457 455 message = 'fold-temp-revision %s' % currentnode
458 456 new = None
459 457 if m or a or r or d:
460 458 new = repo.commit(text=message, user=oldctx.user(),
461 459 date=oldctx.date(), extra=oldctx.extra())
462 460
463 461 # If we're resuming a fold and we have new changes, mark the
464 462 # replacements and finish the fold. If not, it's more like a
465 463 # drop of the changesets that disappeared, and we can skip
466 464 # this step.
467 465 if action in ('f', 'fold') and (new or newchildren):
468 466 if new:
469 467 tmpnodes.append(new)
470 468 else:
471 469 new = newchildren[-1]
472 470 (parentctx, created_, replaced_, tmpnodes_) = finishfold(
473 471 ui, repo, parentctx, oldctx, new, opts, newchildren)
474 472 replaced.extend(replaced_)
475 473 created.extend(created_)
476 474 tmpnodes.extend(tmpnodes_)
477 475 elif action not in ('d', 'drop'):
478 476 if new != oldctx.node():
479 477 replaced.append(oldctx.node())
480 478 if new:
481 479 if new != oldctx.node():
482 480 created.append(new)
483 481 parentctx = repo[new]
484 482
485 483 elif opts.get('abort', False):
486 484 if len(parent) != 0:
487 485 raise util.Abort(_('no arguments allowed with --abort'))
488 486 (parentctxnode, created, replaced, tmpnodes,
489 487 existing, rules, keep, tip, replacemap) = readstate(repo)
490 488 ui.debug('restore wc to old tip %s\n' % node.hex(tip))
491 489 hg.clean(repo, tip)
492 490 ui.debug('should strip created nodes %s\n' %
493 491 ', '.join([node.hex(n)[:12] for n in created]))
494 492 ui.debug('should strip temp nodes %s\n' %
495 493 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
496 494 for nodes in (created, tmpnodes):
497 495 for n in reversed(nodes):
498 496 try:
499 497 repair.strip(ui, repo, n)
500 498 except error.LookupError:
501 499 pass
502 500 os.unlink(os.path.join(repo.path, 'histedit-state'))
503 501 return
504 502 else:
505 503 cmdutil.bailifchanged(repo)
506 504 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
507 505 raise util.Abort(_('history edit already in progress, try '
508 506 '--continue or --abort'))
509 507
510 508 tip, empty = repo.dirstate.parents()
511 509
512 510
513 511 if len(parent) != 1:
514 512 raise util.Abort(_('histedit requires exactly one parent revision'))
515 513 parent = scmutil.revsingle(repo, parent[0]).node()
516 514
517 515 keep = opts.get('keep', False)
518 516 revs = between(repo, parent, tip, keep)
519 517
520 518 ctxs = [repo[r] for r in revs]
521 519 existing = [r.node() for r in ctxs]
522 520 rules = opts.get('commands', '')
523 521 if not rules:
524 522 rules = '\n'.join([makedesc(c) for c in ctxs])
523 rules += '\n\n'
525 524 rules += editcomment % (node.hex(parent)[:12], node.hex(tip)[:12])
526 525 rules = ui.edit(rules, ui.username())
527 526 # Save edit rules in .hg/histedit-last-edit.txt in case
528 527 # the user needs to ask for help after something
529 528 # surprising happens.
530 529 f = open(repo.join('histedit-last-edit.txt'), 'w')
531 530 f.write(rules)
532 531 f.close()
533 532 else:
534 533 f = open(rules)
535 534 rules = f.read()
536 535 f.close()
537 536 rules = [l for l in (r.strip() for r in rules.splitlines())
538 537 if l and not l[0] == '#']
539 538 rules = verifyrules(rules, repo, ctxs)
540 539
541 540 parentctx = repo[parent].parents()[0]
542 541 keep = opts.get('keep', False)
543 542 replaced = []
544 543 replacemap = {}
545 544 tmpnodes = []
546 545 created = []
547 546
548 547
549 548 while rules:
550 549 writestate(repo, parentctx.node(), created, replaced,
551 550 tmpnodes, existing, rules, keep, tip, replacemap)
552 551 action, ha = rules.pop(0)
553 552 (parentctx, created_, replaced_, tmpnodes_) = actiontable[action](
554 553 ui, repo, parentctx, ha, opts)
555 554
556 555 if replaced_:
557 556 clen, rlen = len(created_), len(replaced_)
558 557 if clen == rlen == 1:
559 558 ui.debug('histedit: exact replacement of %s with %s\n' % (
560 559 node.short(replaced_[0]), node.short(created_[0])))
561 560
562 561 replacemap[replaced_[0]] = created_[0]
563 562 elif clen > rlen:
564 563 assert rlen == 1, ('unexpected replacement of '
565 564 '%d changes with %d changes' % (rlen, clen))
566 565 # made more changesets than we're replacing
567 566 # TODO synthesize patch names for created patches
568 567 replacemap[replaced_[0]] = created_[-1]
569 568 ui.debug('histedit: created many, assuming %s replaced by %s' %
570 569 (node.short(replaced_[0]), node.short(created_[-1])))
571 570 elif rlen > clen:
572 571 if not created_:
573 572 # This must be a drop. Try and put our metadata on
574 573 # the parent change.
575 574 assert rlen == 1
576 575 r = replaced_[0]
577 576 ui.debug('histedit: %s seems replaced with nothing, '
578 577 'finding a parent\n' % (node.short(r)))
579 578 pctx = repo[r].parents()[0]
580 579 if pctx.node() in replacemap:
581 580 ui.debug('histedit: parent is already replaced\n')
582 581 replacemap[r] = replacemap[pctx.node()]
583 582 else:
584 583 replacemap[r] = pctx.node()
585 584 ui.debug('histedit: %s best replaced by %s\n' % (
586 585 node.short(r), node.short(replacemap[r])))
587 586 else:
588 587 assert len(created_) == 1
589 588 for r in replaced_:
590 589 ui.debug('histedit: %s replaced by %s\n' % (
591 590 node.short(r), node.short(created_[0])))
592 591 replacemap[r] = created_[0]
593 592 else:
594 593 assert False, (
595 594 'Unhandled case in replacement mapping! '
596 595 'replacing %d changes with %d changes' % (rlen, clen))
597 596 created.extend(created_)
598 597 replaced.extend(replaced_)
599 598 tmpnodes.extend(tmpnodes_)
600 599
601 600 hg.update(repo, parentctx.node())
602 601
603 602 if not keep:
604 603 if replacemap:
605 604 ui.note(_('histedit: Should update metadata for the following '
606 605 'changes:\n'))
607 606
608 607 def copybms(old, new):
609 608 if old in tmpnodes or old in created:
610 609 # can't have any metadata we'd want to update
611 610 return
612 611 while new in replacemap:
613 612 new = replacemap[new]
614 613 ui.note(_('histedit: %s to %s\n') % (node.short(old),
615 614 node.short(new)))
616 615 octx = repo[old]
617 616 marks = octx.bookmarks()
618 617 if marks:
619 618 ui.note(_('histedit: moving bookmarks %s\n') %
620 619 ', '.join(marks))
621 620 for mark in marks:
622 621 repo._bookmarks[mark] = new
623 622 bookmarks.write(repo)
624 623
625 624 # We assume that bookmarks on the tip should remain
626 625 # tipmost, but bookmarks on non-tip changesets should go
627 626 # to their most reasonable successor. As a result, find
628 627 # the old tip and new tip and copy those bookmarks first,
629 628 # then do the rest of the bookmark copies.
630 629 oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1]
631 630 newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1]
632 631 copybms(oldtip, newtip)
633 632
634 633 for old, new in sorted(replacemap.iteritems()):
635 634 copybms(old, new)
636 635 # TODO update mq state
637 636
638 637 ui.debug('should strip replaced nodes %s\n' %
639 638 ', '.join([node.hex(n)[:12] for n in replaced]))
640 639 for n in sorted(replaced, key=lambda x: repo[x].rev()):
641 640 try:
642 641 repair.strip(ui, repo, n)
643 642 except error.LookupError:
644 643 pass
645 644
646 645 ui.debug('should strip temp nodes %s\n' %
647 646 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
648 647 for n in reversed(tmpnodes):
649 648 try:
650 649 repair.strip(ui, repo, n)
651 650 except error.LookupError:
652 651 pass
653 652 os.unlink(os.path.join(repo.path, 'histedit-state'))
654 653 if os.path.exists(repo.sjoin('undo')):
655 654 os.unlink(repo.sjoin('undo'))
656 655
657 656
658 657 def writestate(repo, parentctxnode, created, replaced,
659 658 tmpnodes, existing, rules, keep, oldtip, replacemap):
660 659 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
661 660 pickle.dump((parentctxnode, created, replaced,
662 661 tmpnodes, existing, rules, keep, oldtip, replacemap),
663 662 fp)
664 663 fp.close()
665 664
666 665 def readstate(repo):
667 666 """Returns a tuple of (parentnode, created, replaced, tmp, existing, rules,
668 667 keep, oldtip, replacemap ).
669 668 """
670 669 fp = open(os.path.join(repo.path, 'histedit-state'))
671 670 return pickle.load(fp)
672 671
673 672
674 673 def verifyrules(rules, repo, ctxs):
675 674 """Verify that there exists exactly one edit rule per given changeset.
676 675
677 676 Will abort if there are to many or too few rules, a malformed rule,
678 677 or a rule on a changeset outside of the user-given range.
679 678 """
680 679 parsed = []
681 680 if len(rules) != len(ctxs):
682 681 raise util.Abort(_('must specify a rule for each changeset once'))
683 682 for r in rules:
684 683 if ' ' not in r:
685 684 raise util.Abort(_('malformed line "%s"') % r)
686 685 action, rest = r.split(' ', 1)
687 686 if ' ' in rest.strip():
688 687 ha, rest = rest.split(' ', 1)
689 688 else:
690 689 ha = r.strip()
691 690 try:
692 691 if repo[ha] not in ctxs:
693 692 raise util.Abort(
694 693 _('may not use changesets other than the ones listed'))
695 694 except error.RepoError:
696 695 raise util.Abort(_('unknown changeset %s listed') % ha)
697 696 if action not in actiontable:
698 697 raise util.Abort(_('unknown action "%s"') % action)
699 698 parsed.append([action, ha])
700 699 return parsed
General Comments 0
You need to be logged in to leave comments. Login now