Show More
@@ -0,0 +1,39 b'' | |||||
|
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 b' 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 | |
@@ -38,6 +39,7 b' from .pycompat import (' | |||||
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, | |
@@ -3402,6 +3404,143 b' def debugssl(ui, repo, source=None, **op' | |||||
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]'), |
@@ -75,6 +75,7 b' Show debug commands if there are no othe' | |||||
75 | $ hg debugcomplete debug |
|
75 | $ hg debugcomplete debug | |
76 | debugancestor |
|
76 | debugancestor | |
77 | debugapplystreamclonebundle |
|
77 | debugapplystreamclonebundle | |
|
78 | debugbackupbundle | |||
78 | debugbuilddag |
|
79 | debugbuilddag | |
79 | debugbundle |
|
80 | debugbundle | |
80 | debugcapabilities |
|
81 | debugcapabilities | |
@@ -260,6 +261,7 b' Show all commands + options' | |||||
260 | copy: forget, after, at-rev, force, include, exclude, dry-run |
|
261 | copy: forget, after, at-rev, force, include, exclude, dry-run | |
261 | debugancestor: |
|
262 | debugancestor: | |
262 | debugapplystreamclonebundle: |
|
263 | debugapplystreamclonebundle: | |
|
264 | debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template | |||
263 | debugbuilddag: mergeable-file, overwritten-file, new-file |
|
265 | debugbuilddag: mergeable-file, overwritten-file, new-file | |
264 | debugbundle: all, part-type, spec |
|
266 | debugbundle: all, part-type, spec | |
265 | debugcapabilities: |
|
267 | debugcapabilities: |
@@ -973,6 +973,8 b' Test list of internal help commands' | |||||
973 | find the ancestor revision of two revisions in a given index |
|
973 | find the ancestor revision of two revisions in a given index | |
974 | debugapplystreamclonebundle |
|
974 | debugapplystreamclonebundle | |
975 | apply a stream clone bundle file |
|
975 | apply a stream clone bundle file | |
|
976 | debugbackupbundle | |||
|
977 | lists the changesets available in backup bundles | |||
976 | debugbuilddag |
|
978 | debugbuilddag | |
977 | builds a repo with a given DAG from scratch in the current |
|
979 | builds a repo with a given DAG from scratch in the current | |
978 | empty repo |
|
980 | empty repo |
General Comments 0
You need to be logged in to leave comments.
Login now