##// END OF EJS Templates
histedit: fix unused variable warnings spotted by pyflakes
Patrick Mezard -
r17451:8e1fa8a3 default
parent child Browse files
Show More
@@ -1,661 +1,661 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 (combines N and N-1)
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 (combines N and N-1)
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 lock as lockmod
154 154 from mercurial import node
155 155 from mercurial import patch
156 156 from mercurial import repair
157 157 from mercurial import scmutil
158 158 from mercurial import util
159 159 from mercurial.i18n import _
160 160
161 161 cmdtable = {}
162 162 command = cmdutil.command(cmdtable)
163 163
164 164 testedwith = 'internal'
165 165
166 166 # i18n: command names and abbreviations must remain untranslated
167 167 editcomment = _("""# Edit history between %s and %s
168 168 #
169 169 # Commands:
170 170 # p, pick = use commit
171 171 # e, edit = use commit, but stop for amending
172 172 # f, fold = use commit, but fold into previous commit (combines N and N-1)
173 173 # d, drop = remove commit from history
174 174 # m, mess = edit message without changing commit content
175 175 #
176 176 """)
177 177
178 178 def foldchanges(ui, repo, node1, node2, opts):
179 179 """Produce a new changeset that represents the diff from node1 to node2."""
180 180 try:
181 181 fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
182 182 fp = os.fdopen(fd, 'w')
183 183 diffopts = patch.diffopts(ui, opts)
184 184 diffopts.git = True
185 185 diffopts.ignorews = False
186 186 diffopts.ignorewsamount = False
187 187 diffopts.ignoreblanklines = False
188 188 gen = patch.diff(repo, node1, node2, opts=diffopts)
189 189 for chunk in gen:
190 190 fp.write(chunk)
191 191 fp.close()
192 192 files = set()
193 193 patch.patch(ui, repo, patchfile, files=files, eolmode=None)
194 194 finally:
195 195 os.unlink(patchfile)
196 196 return files
197 197
198 198 def between(repo, old, new, keep):
199 199 revs = [old]
200 200 current = old
201 201 while current != new:
202 202 ctx = repo[current]
203 203 if not keep and len(ctx.children()) > 1:
204 204 raise util.Abort(_('cannot edit history that would orphan nodes'))
205 205 if len(ctx.parents()) != 1 and ctx.parents()[1] != node.nullid:
206 206 raise util.Abort(_("can't edit history with merges"))
207 207 if not ctx.children():
208 208 current = new
209 209 else:
210 210 current = ctx.children()[0].node()
211 211 revs.append(current)
212 212 if len(repo[current].children()) and not keep:
213 213 raise util.Abort(_('cannot edit history that would orphan nodes'))
214 214 return revs
215 215
216 216
217 217 def pick(ui, repo, ctx, ha, opts):
218 218 oldctx = repo[ha]
219 219 if oldctx.parents()[0] == ctx:
220 220 ui.debug('node %s unchanged\n' % ha)
221 221 return oldctx, [], [], []
222 222 hg.update(repo, ctx.node())
223 223 try:
224 224 files = foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
225 225 if not files:
226 226 ui.warn(_('%s: empty changeset')
227 227 % node.hex(ha))
228 228 return ctx, [], [], []
229 229 except Exception:
230 230 raise util.Abort(_('Fix up the change and run '
231 231 'hg histedit --continue'))
232 232 n = repo.commit(text=oldctx.description(), user=oldctx.user(),
233 233 date=oldctx.date(), extra=oldctx.extra())
234 234 return repo[n], [n], [oldctx.node()], []
235 235
236 236
237 237 def edit(ui, repo, ctx, ha, opts):
238 238 oldctx = repo[ha]
239 239 hg.update(repo, ctx.node())
240 240 try:
241 files = foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
241 foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
242 242 except Exception:
243 243 pass
244 244 raise util.Abort(_('Make changes as needed, you may commit or record as '
245 245 'needed now.\nWhen you are finished, run hg'
246 246 ' histedit --continue to resume.'))
247 247
248 248 def fold(ui, repo, ctx, ha, opts):
249 249 oldctx = repo[ha]
250 250 hg.update(repo, ctx.node())
251 251 try:
252 252 files = foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
253 253 if not files:
254 254 ui.warn(_('%s: empty changeset')
255 255 % node.hex(ha))
256 256 return ctx, [], [], []
257 257 except Exception:
258 258 raise util.Abort(_('Fix up the change and run '
259 259 'hg histedit --continue'))
260 260 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
261 261 date=oldctx.date(), extra=oldctx.extra())
262 262 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
263 263
264 264 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
265 265 parent = ctx.parents()[0].node()
266 266 hg.update(repo, parent)
267 files = foldchanges(ui, repo, parent, newnode, opts)
267 foldchanges(ui, repo, parent, newnode, opts)
268 268 newmessage = '\n***\n'.join(
269 269 [ctx.description()] +
270 270 [repo[r].description() for r in internalchanges] +
271 271 [oldctx.description()]) + '\n'
272 272 # If the changesets are from the same author, keep it.
273 273 if ctx.user() == oldctx.user():
274 274 username = ctx.user()
275 275 else:
276 276 username = ui.username()
277 277 newmessage = ui.edit(newmessage, username)
278 278 n = repo.commit(text=newmessage, user=username,
279 279 date=max(ctx.date(), oldctx.date()), extra=oldctx.extra())
280 280 return repo[n], [n], [oldctx.node(), ctx.node()], [newnode]
281 281
282 282 def drop(ui, repo, ctx, ha, opts):
283 283 return ctx, [], [repo[ha].node()], []
284 284
285 285
286 286 def message(ui, repo, ctx, ha, opts):
287 287 oldctx = repo[ha]
288 288 hg.update(repo, ctx.node())
289 289 try:
290 files = foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
290 foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
291 291 except Exception:
292 292 raise util.Abort(_('Fix up the change and run '
293 293 'hg histedit --continue'))
294 294 message = oldctx.description() + '\n'
295 295 message = ui.edit(message, ui.username())
296 296 new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(),
297 297 extra=oldctx.extra())
298 298 newctx = repo[new]
299 299 if oldctx.node() != newctx.node():
300 300 return newctx, [new], [oldctx.node()], []
301 301 # We didn't make an edit, so just indicate no replaced nodes
302 302 return newctx, [new], [], []
303 303
304 304
305 305 def makedesc(c):
306 306 summary = ''
307 307 if c.description():
308 308 summary = c.description().splitlines()[0]
309 309 line = 'pick %s %d %s' % (c.hex()[:12], c.rev(), summary)
310 310 return line[:80] # trim to 80 chars so it's not stupidly wide in my editor
311 311
312 312 actiontable = {'p': pick,
313 313 'pick': pick,
314 314 'e': edit,
315 315 'edit': edit,
316 316 'f': fold,
317 317 'fold': fold,
318 318 'd': drop,
319 319 'drop': drop,
320 320 'm': message,
321 321 'mess': message,
322 322 }
323 323
324 324 @command('histedit',
325 325 [('', 'commands', '',
326 326 _('Read history edits from the specified file.')),
327 327 ('c', 'continue', False, _('continue an edit already in progress')),
328 328 ('k', 'keep', False,
329 329 _("don't strip old nodes after edit is complete")),
330 330 ('', 'abort', False, _('abort an edit in progress')),
331 331 ('o', 'outgoing', False, _('changesets not found in destination')),
332 332 ('f', 'force', False,
333 333 _('force outgoing even for unrelated repositories')),
334 334 ('r', 'rev', [], _('first revision to be edited'))],
335 335 _("[PARENT]"))
336 336 def histedit(ui, repo, *parent, **opts):
337 337 """interactively edit changeset history
338 338 """
339 339 # TODO only abort if we try and histedit mq patches, not just
340 340 # blanket if mq patches are applied somewhere
341 341 mq = getattr(repo, 'mq', None)
342 342 if mq and mq.applied:
343 343 raise util.Abort(_('source has mq patches applied'))
344 344
345 345 parent = list(parent) + opts.get('rev', [])
346 346 if opts.get('outgoing'):
347 347 if len(parent) > 1:
348 348 raise util.Abort(
349 349 _('only one repo argument allowed with --outgoing'))
350 350 elif parent:
351 351 parent = parent[0]
352 352
353 353 dest = ui.expandpath(parent or 'default-push', parent or 'default')
354 354 dest, revs = hg.parseurl(dest, None)[:2]
355 355 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
356 356
357 357 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
358 358 other = hg.peer(repo, opts, dest)
359 359
360 360 if revs:
361 361 revs = [repo.lookup(rev) for rev in revs]
362 362
363 363 parent = discovery.findcommonoutgoing(
364 364 repo, other, [], force=opts.get('force')).missing[0:1]
365 365 else:
366 366 if opts.get('force'):
367 367 raise util.Abort(_('--force only allowed with --outgoing'))
368 368
369 369 if opts.get('continue', False):
370 370 if len(parent) != 0:
371 371 raise util.Abort(_('no arguments allowed with --continue'))
372 372 (parentctxnode, created, replaced,
373 373 tmpnodes, existing, rules, keep, tip, replacemap) = readstate(repo)
374 374 currentparent, wantnull = repo.dirstate.parents()
375 375 parentctx = repo[parentctxnode]
376 376 # existing is the list of revisions initially considered by
377 377 # histedit. Here we use it to list new changesets, descendants
378 378 # of parentctx without an 'existing' changeset in-between. We
379 379 # also have to exclude 'existing' changesets which were
380 380 # previously dropped.
381 381 descendants = set(c.node() for c in
382 382 repo.set('(%n::) - %n', parentctxnode, parentctxnode))
383 383 existing = set(existing)
384 384 notdropped = set(n for n in existing if n in descendants and
385 385 (n not in replacemap or replacemap[n] in descendants))
386 386 # Discover any nodes the user has added in the interim. We can
387 387 # miss changesets which were dropped and recreated the same.
388 388 newchildren = list(c.node() for c in repo.set(
389 389 'sort(%ln - (%ln or %ln::))', descendants, existing, notdropped))
390 390 action, currentnode = rules.pop(0)
391 391 if action in ('f', 'fold'):
392 392 tmpnodes.extend(newchildren)
393 393 else:
394 394 created.extend(newchildren)
395 395
396 396 m, a, r, d = repo.status()[:4]
397 397 oldctx = repo[currentnode]
398 398 message = oldctx.description() + '\n'
399 399 if action in ('e', 'edit', 'm', 'mess'):
400 400 message = ui.edit(message, ui.username())
401 401 elif action in ('f', 'fold'):
402 402 message = 'fold-temp-revision %s' % currentnode
403 403 new = None
404 404 if m or a or r or d:
405 405 new = repo.commit(text=message, user=oldctx.user(),
406 406 date=oldctx.date(), extra=oldctx.extra())
407 407
408 408 # If we're resuming a fold and we have new changes, mark the
409 409 # replacements and finish the fold. If not, it's more like a
410 410 # drop of the changesets that disappeared, and we can skip
411 411 # this step.
412 412 if action in ('f', 'fold') and (new or newchildren):
413 413 if new:
414 414 tmpnodes.append(new)
415 415 else:
416 416 new = newchildren[-1]
417 417 (parentctx, created_, replaced_, tmpnodes_) = finishfold(
418 418 ui, repo, parentctx, oldctx, new, opts, newchildren)
419 419 replaced.extend(replaced_)
420 420 created.extend(created_)
421 421 tmpnodes.extend(tmpnodes_)
422 422 elif action not in ('d', 'drop'):
423 423 if new != oldctx.node():
424 424 replaced.append(oldctx.node())
425 425 if new:
426 426 if new != oldctx.node():
427 427 created.append(new)
428 428 parentctx = repo[new]
429 429
430 430 elif opts.get('abort', False):
431 431 if len(parent) != 0:
432 432 raise util.Abort(_('no arguments allowed with --abort'))
433 433 (parentctxnode, created, replaced, tmpnodes,
434 434 existing, rules, keep, tip, replacemap) = readstate(repo)
435 435 ui.debug('restore wc to old tip %s\n' % node.hex(tip))
436 436 hg.clean(repo, tip)
437 437 ui.debug('should strip created nodes %s\n' %
438 438 ', '.join([node.hex(n)[:12] for n in created]))
439 439 ui.debug('should strip temp nodes %s\n' %
440 440 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
441 441 for nodes in (created, tmpnodes):
442 442 lock = None
443 443 try:
444 444 lock = repo.lock()
445 445 for n in reversed(nodes):
446 446 try:
447 447 repair.strip(ui, repo, n)
448 448 except error.LookupError:
449 449 pass
450 450 finally:
451 451 lockmod.release(lock)
452 452 os.unlink(os.path.join(repo.path, 'histedit-state'))
453 453 return
454 454 else:
455 455 cmdutil.bailifchanged(repo)
456 456 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
457 457 raise util.Abort(_('history edit already in progress, try '
458 458 '--continue or --abort'))
459 459
460 460 tip, empty = repo.dirstate.parents()
461 461
462 462
463 463 if len(parent) != 1:
464 464 raise util.Abort(_('histedit requires exactly one parent revision'))
465 465 parent = scmutil.revsingle(repo, parent[0]).node()
466 466
467 467 keep = opts.get('keep', False)
468 468 revs = between(repo, parent, tip, keep)
469 469
470 470 ctxs = [repo[r] for r in revs]
471 471 existing = [r.node() for r in ctxs]
472 472 rules = opts.get('commands', '')
473 473 if not rules:
474 474 rules = '\n'.join([makedesc(c) for c in ctxs])
475 475 rules += '\n\n'
476 476 rules += editcomment % (node.hex(parent)[:12], node.hex(tip)[:12])
477 477 rules = ui.edit(rules, ui.username())
478 478 # Save edit rules in .hg/histedit-last-edit.txt in case
479 479 # the user needs to ask for help after something
480 480 # surprising happens.
481 481 f = open(repo.join('histedit-last-edit.txt'), 'w')
482 482 f.write(rules)
483 483 f.close()
484 484 else:
485 485 f = open(rules)
486 486 rules = f.read()
487 487 f.close()
488 488 rules = [l for l in (r.strip() for r in rules.splitlines())
489 489 if l and not l[0] == '#']
490 490 rules = verifyrules(rules, repo, ctxs)
491 491
492 492 parentctx = repo[parent].parents()[0]
493 493 keep = opts.get('keep', False)
494 494 replaced = []
495 495 replacemap = {}
496 496 tmpnodes = []
497 497 created = []
498 498
499 499
500 500 while rules:
501 501 writestate(repo, parentctx.node(), created, replaced,
502 502 tmpnodes, existing, rules, keep, tip, replacemap)
503 503 action, ha = rules.pop(0)
504 504 (parentctx, created_, replaced_, tmpnodes_) = actiontable[action](
505 505 ui, repo, parentctx, ha, opts)
506 506
507 507 if replaced_:
508 508 clen, rlen = len(created_), len(replaced_)
509 509 if clen == rlen == 1:
510 510 ui.debug('histedit: exact replacement of %s with %s\n' % (
511 511 node.short(replaced_[0]), node.short(created_[0])))
512 512
513 513 replacemap[replaced_[0]] = created_[0]
514 514 elif clen > rlen:
515 515 assert rlen == 1, ('unexpected replacement of '
516 516 '%d changes with %d changes' % (rlen, clen))
517 517 # made more changesets than we're replacing
518 518 # TODO synthesize patch names for created patches
519 519 replacemap[replaced_[0]] = created_[-1]
520 520 ui.debug('histedit: created many, assuming %s replaced by %s' %
521 521 (node.short(replaced_[0]), node.short(created_[-1])))
522 522 elif rlen > clen:
523 523 if not created_:
524 524 # This must be a drop. Try and put our metadata on
525 525 # the parent change.
526 526 assert rlen == 1
527 527 r = replaced_[0]
528 528 ui.debug('histedit: %s seems replaced with nothing, '
529 529 'finding a parent\n' % (node.short(r)))
530 530 pctx = repo[r].parents()[0]
531 531 if pctx.node() in replacemap:
532 532 ui.debug('histedit: parent is already replaced\n')
533 533 replacemap[r] = replacemap[pctx.node()]
534 534 else:
535 535 replacemap[r] = pctx.node()
536 536 ui.debug('histedit: %s best replaced by %s\n' % (
537 537 node.short(r), node.short(replacemap[r])))
538 538 else:
539 539 assert len(created_) == 1
540 540 for r in replaced_:
541 541 ui.debug('histedit: %s replaced by %s\n' % (
542 542 node.short(r), node.short(created_[0])))
543 543 replacemap[r] = created_[0]
544 544 else:
545 545 assert False, (
546 546 'Unhandled case in replacement mapping! '
547 547 'replacing %d changes with %d changes' % (rlen, clen))
548 548 created.extend(created_)
549 549 replaced.extend(replaced_)
550 550 tmpnodes.extend(tmpnodes_)
551 551
552 552 hg.update(repo, parentctx.node())
553 553
554 554 if not keep:
555 555 if replacemap:
556 556 ui.note(_('histedit: Should update metadata for the following '
557 557 'changes:\n'))
558 558
559 559 def copybms(old, new):
560 560 if old in tmpnodes or old in created:
561 561 # can't have any metadata we'd want to update
562 562 return
563 563 while new in replacemap:
564 564 new = replacemap[new]
565 565 ui.note(_('histedit: %s to %s\n') % (node.short(old),
566 566 node.short(new)))
567 567 octx = repo[old]
568 568 marks = octx.bookmarks()
569 569 if marks:
570 570 ui.note(_('histedit: moving bookmarks %s\n') %
571 571 ', '.join(marks))
572 572 for mark in marks:
573 573 repo._bookmarks[mark] = new
574 574 bookmarks.write(repo)
575 575
576 576 # We assume that bookmarks on the tip should remain
577 577 # tipmost, but bookmarks on non-tip changesets should go
578 578 # to their most reasonable successor. As a result, find
579 579 # the old tip and new tip and copy those bookmarks first,
580 580 # then do the rest of the bookmark copies.
581 581 oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1]
582 582 newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1]
583 583 copybms(oldtip, newtip)
584 584
585 585 for old, new in sorted(replacemap.iteritems()):
586 586 copybms(old, new)
587 587 # TODO update mq state
588 588
589 589 ui.debug('should strip replaced nodes %s\n' %
590 590 ', '.join([node.hex(n)[:12] for n in replaced]))
591 591 lock = None
592 592 try:
593 593 lock = repo.lock()
594 594 for n in sorted(replaced, key=lambda x: repo[x].rev()):
595 595 try:
596 596 repair.strip(ui, repo, n)
597 597 except error.LookupError:
598 598 pass
599 599 finally:
600 600 lockmod.release(lock)
601 601
602 602 ui.debug('should strip temp nodes %s\n' %
603 603 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
604 604 lock = None
605 605 try:
606 606 lock = repo.lock()
607 607 for n in reversed(tmpnodes):
608 608 try:
609 609 repair.strip(ui, repo, n)
610 610 except error.LookupError:
611 611 pass
612 612 finally:
613 613 lockmod.release(lock)
614 614 os.unlink(os.path.join(repo.path, 'histedit-state'))
615 615 if os.path.exists(repo.sjoin('undo')):
616 616 os.unlink(repo.sjoin('undo'))
617 617
618 618
619 619 def writestate(repo, parentctxnode, created, replaced,
620 620 tmpnodes, existing, rules, keep, oldtip, replacemap):
621 621 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
622 622 pickle.dump((parentctxnode, created, replaced,
623 623 tmpnodes, existing, rules, keep, oldtip, replacemap),
624 624 fp)
625 625 fp.close()
626 626
627 627 def readstate(repo):
628 628 """Returns a tuple of (parentnode, created, replaced, tmp, existing, rules,
629 629 keep, oldtip, replacemap ).
630 630 """
631 631 fp = open(os.path.join(repo.path, 'histedit-state'))
632 632 return pickle.load(fp)
633 633
634 634
635 635 def verifyrules(rules, repo, ctxs):
636 636 """Verify that there exists exactly one edit rule per given changeset.
637 637
638 638 Will abort if there are to many or too few rules, a malformed rule,
639 639 or a rule on a changeset outside of the user-given range.
640 640 """
641 641 parsed = []
642 642 if len(rules) != len(ctxs):
643 643 raise util.Abort(_('must specify a rule for each changeset once'))
644 644 for r in rules:
645 645 if ' ' not in r:
646 646 raise util.Abort(_('malformed line "%s"') % r)
647 647 action, rest = r.split(' ', 1)
648 648 if ' ' in rest.strip():
649 649 ha, rest = rest.split(' ', 1)
650 650 else:
651 651 ha = r.strip()
652 652 try:
653 653 if repo[ha] not in ctxs:
654 654 raise util.Abort(
655 655 _('may not use changesets other than the ones listed'))
656 656 except error.RepoError:
657 657 raise util.Abort(_('unknown changeset %s listed') % ha)
658 658 if action not in actiontable:
659 659 raise util.Abort(_('unknown action "%s"') % action)
660 660 parsed.append([action, ha])
661 661 return parsed
General Comments 0
You need to be logged in to leave comments. Login now