Show More
@@ -0,0 +1,39 | |||
|
1 | $ cat >> $HGRCPATH << EOF | |
|
2 | > [extensions] | |
|
3 | > strip= | |
|
4 | > EOF | |
|
5 | ||
|
6 | Setup repo | |
|
7 | ||
|
8 | $ hg init repo | |
|
9 | $ cd repo | |
|
10 | ||
|
11 | Test backups list and recover | |
|
12 | ||
|
13 | $ hg debugbackupbundle | |
|
14 | no backup changesets found | |
|
15 | ||
|
16 | $ mkcommit() { | |
|
17 | > echo "$1" > "$1" | |
|
18 | > hg add "$1" | |
|
19 | > hg ci -l $1 | |
|
20 | > } | |
|
21 | $ mkcommit a | |
|
22 | $ mkcommit b | |
|
23 | $ hg strip . | |
|
24 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |
|
25 | saved backup bundle to $TESTTMP/repo/.hg/strip-backup/d2ae7f538514-2953539b-backup.hg (glob) | |
|
26 | $ hg debugbackupbundle | |
|
27 | Recover changesets using: hg debugbackupbundle --recover <changeset hash> | |
|
28 | ||
|
29 | Available backup changesets: | |
|
30 | * (glob) | |
|
31 | d2ae7f538514 b | |
|
32 | ||
|
33 | $ hg debugbackupbundle --recover d2ae7f538514 | |
|
34 | Unbundling d2ae7f538514 | |
|
35 | adding changesets | |
|
36 | adding manifests | |
|
37 | adding file changes | |
|
38 | added 1 changesets with 1 changes to 1 files | |
|
39 | new changesets d2ae7f538514 (1 drafts) |
@@ -11,6 +11,7 import codecs | |||
|
11 | 11 | import collections |
|
12 | 12 | import difflib |
|
13 | 13 | import errno |
|
14 | import glob | |
|
14 | 15 | import operator |
|
15 | 16 | import os |
|
16 | 17 | import platform |
@@ -38,6 +39,7 from .pycompat import ( | |||
|
38 | 39 | ) |
|
39 | 40 | from . import ( |
|
40 | 41 | bundle2, |
|
42 | bundlerepo, | |
|
41 | 43 | changegroup, |
|
42 | 44 | cmdutil, |
|
43 | 45 | color, |
@@ -3402,6 +3404,143 def debugssl(ui, repo, source=None, **op | |||
|
3402 | 3404 | |
|
3403 | 3405 | |
|
3404 | 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 | 3544 | b'debugsub', |
|
3406 | 3545 | [(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))], |
|
3407 | 3546 | _(b'[-r REV] [REV]'), |
@@ -75,6 +75,7 Show debug commands if there are no othe | |||
|
75 | 75 | $ hg debugcomplete debug |
|
76 | 76 | debugancestor |
|
77 | 77 | debugapplystreamclonebundle |
|
78 | debugbackupbundle | |
|
78 | 79 | debugbuilddag |
|
79 | 80 | debugbundle |
|
80 | 81 | debugcapabilities |
@@ -260,6 +261,7 Show all commands + options | |||
|
260 | 261 | copy: forget, after, at-rev, force, include, exclude, dry-run |
|
261 | 262 | debugancestor: |
|
262 | 263 | debugapplystreamclonebundle: |
|
264 | debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template | |
|
263 | 265 | debugbuilddag: mergeable-file, overwritten-file, new-file |
|
264 | 266 | debugbundle: all, part-type, spec |
|
265 | 267 | debugcapabilities: |
@@ -973,6 +973,8 Test list of internal help commands | |||
|
973 | 973 | find the ancestor revision of two revisions in a given index |
|
974 | 974 | debugapplystreamclonebundle |
|
975 | 975 | apply a stream clone bundle file |
|
976 | debugbackupbundle | |
|
977 | lists the changesets available in backup bundles | |
|
976 | 978 | debugbuilddag |
|
977 | 979 | builds a repo with a given DAG from scratch in the current |
|
978 | 980 | empty repo |
General Comments 0
You need to be logged in to leave comments.
Login now