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