comparison mercurial/debugcommands.py @ 44433:f82d2d4e71db

debugbackupbundle: introduce command to interact with strip backups This vendors backups extension from hg-experimental. Listing backups and having some utility to apply them is nice. I know we have obsmarkers now, but this will help a lot of end users who still uses strip until we get evolve out of experimental. Differential Revision: https://phab.mercurial-scm.org/D7932
author Pulkit Goyal <7895pulkit@gmail.com>
date Fri, 17 Jan 2020 21:22:23 +0300
parents acbfa31cfaf2
children 9d2b2df2c2ba
comparison
equal deleted inserted replaced
44427:4ce2330f2d0b 44433:f82d2d4e71db
9 9
10 import codecs 10 import codecs
11 import collections 11 import collections
12 import difflib 12 import difflib
13 import errno 13 import errno
14 import glob
14 import operator 15 import operator
15 import os 16 import os
16 import platform 17 import platform
17 import random 18 import random
18 import re 19 import re
36 getattr, 37 getattr,
37 open, 38 open,
38 ) 39 )
39 from . import ( 40 from . import (
40 bundle2, 41 bundle2,
42 bundlerepo,
41 changegroup, 43 changegroup,
42 cmdutil, 44 cmdutil,
43 color, 45 color,
44 context, 46 context,
45 copies, 47 copies,
3400 finally: 3402 finally:
3401 s.close() 3403 s.close()
3402 3404
3403 3405
3404 @command( 3406 @command(
3407 b"debugbackupbundle",
3408 [
3409 (
3410 b"",
3411 b"recover",
3412 b"",
3413 b"brings the specified changeset back into the repository",
3414 )
3415 ]
3416 + cmdutil.logopts,
3417 _(b"hg debugbackupbundle [--recover HASH]"),
3418 )
3419 def debugbackupbundle(ui, repo, *pats, **opts):
3420 """lists the changesets available in backup bundles
3421
3422 Without any arguments, this command prints a list of the changesets in each
3423 backup bundle.
3424
3425 --recover takes a changeset hash and unbundles the first bundle that
3426 contains that hash, which puts that changeset back in your repository.
3427
3428 --verbose will print the entire commit message and the bundle path for that
3429 backup.
3430 """
3431 backups = list(
3432 filter(
3433 os.path.isfile, glob.glob(repo.vfs.join(b"strip-backup") + b"/*.hg")
3434 )
3435 )
3436 backups.sort(key=lambda x: os.path.getmtime(x), reverse=True)
3437
3438 opts = pycompat.byteskwargs(opts)
3439 opts[b"bundle"] = b""
3440 opts[b"force"] = None
3441 limit = logcmdutil.getlimit(opts)
3442
3443 def display(other, chlist, displayer):
3444 if opts.get(b"newest_first"):
3445 chlist.reverse()
3446 count = 0
3447 for n in chlist:
3448 if limit is not None and count >= limit:
3449 break
3450 parents = [True for p in other.changelog.parents(n) if p != nullid]
3451 if opts.get(b"no_merges") and len(parents) == 2:
3452 continue
3453 count += 1
3454 displayer.show(other[n])
3455
3456 recovernode = opts.get(b"recover")
3457 if recovernode:
3458 if scmutil.isrevsymbol(repo, recovernode):
3459 ui.warn(_(b"%s already exists in the repo\n") % recovernode)
3460 return
3461 elif backups:
3462 msg = _(
3463 b"Recover changesets using: hg debugbackupbundle --recover "
3464 b"<changeset hash>\n\nAvailable backup changesets:"
3465 )
3466 ui.status(msg, label=b"status.removed")
3467 else:
3468 ui.status(_(b"no backup changesets found\n"))
3469 return
3470
3471 for backup in backups:
3472 # Much of this is copied from the hg incoming logic
3473 source = ui.expandpath(os.path.relpath(backup, encoding.getcwd()))
3474 source, branches = hg.parseurl(source, opts.get(b"branch"))
3475 try:
3476 other = hg.peer(repo, opts, source)
3477 except error.LookupError as ex:
3478 msg = _(b"\nwarning: unable to open bundle %s") % source
3479 hint = _(b"\n(missing parent rev %s)\n") % short(ex.name)
3480 ui.warn(msg, hint=hint)
3481 continue
3482 revs, checkout = hg.addbranchrevs(
3483 repo, other, branches, opts.get(b"rev")
3484 )
3485
3486 if revs:
3487 revs = [other.lookup(rev) for rev in revs]
3488
3489 quiet = ui.quiet
3490 try:
3491 ui.quiet = True
3492 other, chlist, cleanupfn = bundlerepo.getremotechanges(
3493 ui, repo, other, revs, opts[b"bundle"], opts[b"force"]
3494 )
3495 except error.LookupError:
3496 continue
3497 finally:
3498 ui.quiet = quiet
3499
3500 try:
3501 if not chlist:
3502 continue
3503 if recovernode:
3504 with repo.lock(), repo.transaction(b"unbundle") as tr:
3505 if scmutil.isrevsymbol(other, recovernode):
3506 ui.status(_(b"Unbundling %s\n") % (recovernode))
3507 f = hg.openpath(ui, source)
3508 gen = exchange.readbundle(ui, f, source)
3509 if isinstance(gen, bundle2.unbundle20):
3510 bundle2.applybundle(
3511 repo,
3512 gen,
3513 tr,
3514 source=b"unbundle",
3515 url=b"bundle:" + source,
3516 )
3517 else:
3518 gen.apply(repo, b"unbundle", b"bundle:" + source)
3519 break
3520 else:
3521 backupdate = encoding.strtolocal(
3522 time.strftime(
3523 "%a %H:%M, %Y-%m-%d",
3524 time.localtime(os.path.getmtime(source)),
3525 )
3526 )
3527 ui.status(b"\n%s\n" % (backupdate.ljust(50)))
3528 if ui.verbose:
3529 ui.status(b"%s%s\n" % (b"bundle:".ljust(13), source))
3530 else:
3531 opts[
3532 b"template"
3533 ] = b"{label('status.modified', node|short)} {desc|firstline}\n"
3534 displayer = logcmdutil.changesetdisplayer(
3535 ui, other, opts, False
3536 )
3537 display(other, chlist, displayer)
3538 displayer.close()
3539 finally:
3540 cleanupfn()
3541
3542
3543 @command(
3405 b'debugsub', 3544 b'debugsub',
3406 [(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))], 3545 [(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))],
3407 _(b'[-r REV] [REV]'), 3546 _(b'[-r REV] [REV]'),
3408 ) 3547 )
3409 def debugsub(ui, repo, rev=None): 3548 def debugsub(ui, repo, rev=None):