Patchwork D7932: debugbackupbundle: introduce command to interact with strip backups

login
register
mail settings
Submitter phabricator
Date Feb. 24, 2020, 6:11 p.m.
Message ID <0ef9ad30614bc5dc3cf00259e0154023@localhost.localdomain>
Download mbox | patch
Permalink /patch/45304/
State Not Applicable
Headers show

Comments

phabricator - Feb. 24, 2020, 6:11 p.m.
pulkit retitled this revision from "debugstripbackups: introduce command to interact with strip backups" to "debugbackupbundle: introduce command to interact with strip backups".
pulkit updated this revision to Diff 20288.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7932?vs=19868&id=20288

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7932/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7932

AFFECTED FILES
  mercurial/debugcommands.py
  tests/debugbackupbundle.t

CHANGE DETAILS




To: pulkit, #hg-reviewers, durin42
Cc: marmoute, durin42, mercurial-devel

Patch

diff --git a/tests/debugbackupbundle.t b/tests/debugbackupbundle.t
new file mode 100644
--- /dev/null
+++ b/tests/debugbackupbundle.t
@@ -0,0 +1,39 @@ 
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > strip=
+  > EOF
+
+Setup repo
+
+  $ hg init repo
+  $ cd repo
+
+Test backups list and recover
+
+  $ hg debugbackupbundle
+  no backup changesets found
+
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -l $1
+  > }
+  $ mkcommit a
+  $ mkcommit b
+  $ hg strip .
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/d2ae7f538514-2953539b-backup.hg (glob)
+  $ hg debugbackupbundle
+  Recover changesets using: hg debugbackupbundle --recover <changeset hash>
+  
+  Available backup changesets:
+  * (glob)
+  d2ae7f538514 b
+
+  $ hg debugbackupbundle --recover d2ae7f538514
+  Unbundling d2ae7f538514
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets d2ae7f538514 (1 drafts)
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -11,6 +11,7 @@ 
 import collections
 import difflib
 import errno
+import glob
 import operator
 import os
 import platform
@@ -39,6 +40,7 @@ 
 )
 from . import (
     bundle2,
+    bundlerepo,
     changegroup,
     cmdutil,
     color,
@@ -3423,6 +3425,138 @@ 
 
 
 @command(
+    "debugbackupbundle",
+    [
+        (
+            "",
+            "recover",
+            "",
+            "brings the specified changeset back into the repository",
+        )
+    ]
+    + cmdutil.logopts,
+    _("hg debugbackupbundle [--recover HASH]"),
+)
+def debugbackupbundle(ui, repo, *pats, **opts):
+    """lists the changesets available in backup bundles
+
+    Without any arguments, this command prints a list of the changesets in each
+    backup bundle.
+
+    --recover takes a changeset hash and unbundles the first bundle that
+    contains that hash, which puts that changeset back in your repository.
+
+    --verbose will print the entire commit message and the bundle path for that
+    backup.
+    """
+    backups = filter(
+        os.path.isfile, glob.glob(repo.vfs.join("strip-backup") + "/*.hg")
+    )
+    backups.sort(key=lambda x: os.path.getmtime(x), reverse=True)
+
+    opts["bundle"] = ""
+    opts["force"] = None
+    limit = logcmdutil.getlimit(opts)
+
+    def display(other, chlist, displayer):
+        if opts.get("newest_first"):
+            chlist.reverse()
+        count = 0
+        for n in chlist:
+            if limit is not None and count >= limit:
+                break
+            parents = [True for p in other.changelog.parents(n) if p != nullid]
+            if opts.get("no_merges") and len(parents) == 2:
+                continue
+            count += 1
+            displayer.show(other[n])
+
+    recovernode = opts.get("recover")
+    if recovernode:
+        if scmutil.isrevsymbol(repo, recovernode):
+            ui.warn(_("%s already exists in the repo\n") % recovernode)
+            return
+    elif backups:
+        msg = _(
+            "Recover changesets using: hg debugbackupbundle --recover "
+            "<changeset hash>\n\nAvailable backup changesets:"
+        )
+        ui.status(msg, label="status.removed")
+    else:
+        ui.status(_("no backup changesets found\n"))
+        return
+
+    for backup in backups:
+        # Much of this is copied from the hg incoming logic
+        source = ui.expandpath(os.path.relpath(backup, encoding.getcwd()))
+        source, branches = hg.parseurl(source, opts.get("branch"))
+        try:
+            other = hg.peer(repo, opts, source)
+        except error.LookupError as ex:
+            msg = _("\nwarning: unable to open bundle %s") % source
+            hint = _("\n(missing parent rev %s)\n") % short(ex.name)
+            ui.warn(msg, hint=hint)
+            continue
+        revs, checkout = hg.addbranchrevs(
+            repo, other, branches, opts.get("rev")
+        )
+
+        if revs:
+            revs = [other.lookup(rev) for rev in revs]
+
+        quiet = ui.quiet
+        try:
+            ui.quiet = True
+            other, chlist, cleanupfn = bundlerepo.getremotechanges(
+                ui, repo, other, revs, opts["bundle"], opts["force"]
+            )
+        except error.LookupError:
+            continue
+        finally:
+            ui.quiet = quiet
+
+        try:
+            if not chlist:
+                continue
+            if recovernode:
+                with repo.lock(), repo.transaction("unbundle") as tr:
+                    if scmutil.isrevsymbol(other, recovernode):
+                        ui.status(_("Unbundling %s\n") % (recovernode))
+                        f = hg.openpath(ui, source)
+                        gen = exchange.readbundle(ui, f, source)
+                        if isinstance(gen, bundle2.unbundle20):
+                            bundle2.applybundle(
+                                repo,
+                                gen,
+                                tr,
+                                source="unbundle",
+                                url="bundle:" + source,
+                            )
+                        else:
+                            gen.apply(repo, "unbundle", "bundle:" + source)
+                        break
+            else:
+                backupdate = time.strftime(
+                    "%a %H:%M, %Y-%m-%d",
+                    time.localtime(os.path.getmtime(source)),
+                )
+                ui.status("\n%s\n" % (backupdate.ljust(50)))
+                if ui.verbose:
+                    ui.status("%s%s\n" % ("bundle:".ljust(13), source))
+                else:
+                    opts[
+                        "template"
+                    ] = "{label('status.modified', node|short)} {desc|firstline}\n"
+                displayer = logcmdutil.changesetdisplayer(
+                    ui, other, opts, False
+                )
+                display(other, chlist, displayer)
+                displayer.close()
+        finally:
+            cleanupfn()
+
+
+@command(
     b'debugsub',
     [(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))],
     _(b'[-r REV] [REV]'),