sapling/hgext/backups.py
Jun Wu 584656dff3 codemod: join the auto-formatter party
Summary:
Turned on the auto formatter. Ran `arc lint --apply-patches --take BLACK **/*.py`.
Then run `arc lint` again so some other autofixers like spellchecker etc. looked
at the code base. Manually accept the changes whenever they make sense, or use
a workaround (ex. changing "dict()" to "dict constructor") where autofix is false
positive. Disabled linters on files that are hard (i18n/polib.py) to fix, or less
interesting to fix (hgsubversion tests), or cannot be fixed without breaking
OSS build (FBPYTHON4).

Conflicted linters (test-check-module-imports.t, part of test-check-code.t,
test-check-pyflakes.t) are removed or disabled.

Duplicated linters (test-check-pyflakes.t, test-check-pylint.t) are removed.

An issue of the auto-formatter is lines are no longer guarnateed to be <= 80
chars. But that seems less important comparing with the benefit auto-formatter
provides.

As we're here, also remove test-check-py3-compat.t, as it is currently broken
if `PYTHON3=/bin/python3` is set.

Reviewed By: wez, phillco, simpkins, pkaush, singhsrb

Differential Revision: D8173629

fbshipit-source-id: 90e248ae0c5e6eaadbe25520a6ee42d32005621b
2018-05-25 22:17:29 -07:00

177 lines
6.2 KiB
Python

# backups.py
#
# Copyright 2013 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
"""display recently made backups to recover stripped changesets
This extension allows the listing and restoring of strip backups.
By default, this will display a warning if you have obsolescence marker creation
enabled (since most hg installations use either strip or markers, but not both).
To disable that warning, set ``backups.warnobsolescence`` to False.
"""
import glob
import os
import time
from mercurial import (
bundle2,
bundlerepo,
cmdutil,
commands,
error,
exchange,
hg,
lock as lockmod,
obsolete,
pycompat,
registrar,
)
from mercurial.i18n import _
from mercurial.node import nullid, short
from . import pager
pager.attended.append("backups")
cmdtable = {}
command = registrar.command(cmdtable)
testedwith = "ships-with-fb-hgext"
msgwithcreatermarkers = """Marker creation is enabled so no changeset should be
stripped unless you explicitly called hg strip. hg backups will show you the
stripped changesets. If you are trying to recover a changeset hidden from a
previous command, use hg journal to get its sha1 and you will be able to access
it directly without recovering a backup.\n\n"""
verbosetemplate = "{label('status.modified', node|short)} {desc|firstline}\n"
@command(
"^backups",
[("", "recover", "", "brings the specified changeset back into the repository")]
+ commands.logopts,
_("hg backups [--recover HASH]"),
)
def backups(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.
"""
supportsmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
if supportsmarkers and ui.configbool("backups", "warnobsolescence", True):
# Warn users of obsolescence markers that they probably don't want to
# use backups but reflog instead
ui.warn(msgwithcreatermarkers)
backuppath = repo.vfs.join("strip-backup")
backups = filter(os.path.isfile, glob.glob(backuppath + "/*.hg"))
backups.sort(key=lambda x: os.path.getmtime(x), reverse=True)
opts["bundle"] = ""
opts["force"] = None
def display(other, chlist, displayer):
limit = cmdutil.loglimit(opts)
if opts.get("newest_first"):
chlist.reverse()
count = 0
for n in chlist:
if limit is not None and count >= limit:
break
parents = [p 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 recovernode in repo:
ui.warn(_("%s already exists in the repo\n") % recovernode)
return
else:
msg = _(
"Recover changesets using: hg backups --recover "
"<changeset hash>\n\nAvailable backup changesets:"
)
ui.status(msg, label="status.removed")
for backup in backups:
# Much of this is copied from the hg incoming logic
source = os.path.relpath(backup, pycompat.getcwd())
source = ui.expandpath(source)
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)
ui.warn(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 chlist:
if recovernode:
tr = lock = None
try:
lock = repo.lock()
if recovernode in other:
ui.status(_("Unbundling %s\n") % (recovernode))
f = hg.openpath(ui, source)
gen = exchange.readbundle(ui, f, source)
tr = repo.transaction("unbundle")
if not isinstance(gen, bundle2.unbundle20):
gen.apply(repo, "unbundle", "bundle:" + source)
if isinstance(gen, bundle2.unbundle20):
bundle2.applybundle(
repo,
gen,
tr,
source="unbundle",
url="bundle:" + source,
)
tr.close()
break
finally:
lockmod.release(lock, tr)
else:
backupdate = os.path.getmtime(source)
backupdate = time.strftime(
"%a %H:%M, %Y-%m-%d", time.localtime(backupdate)
)
ui.status("\n%s\n" % (backupdate.ljust(50)))
if not ui.verbose:
opts["template"] = verbosetemplate
else:
ui.status("%s%s\n" % ("bundle:".ljust(13), source))
displayer = cmdutil.show_changeset(ui, other, opts, False)
display(other, chlist, displayer)
displayer.close()
finally:
cleanupfn()