mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 01:07:15 +03:00
25d0314a5b
Summary: backups: make hg backups evolve aware With this patch: hg backups urges evolve user to look at hg reflog to recover hidden commits. This is because evolve's commands are using obsolescence markers to hide commits instead of stripping them. Test Plan: Cross repo dependency, not sure how to make an automated test Reviewers: durham, pyd Differential Revision: https://phabricator.fb.com/D2088486 Tasks: 6534705
131 lines
5.0 KiB
Python
131 lines
5.0 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 commits"""
|
|
|
|
from mercurial import extensions, util, cmdutil, commands, error, bundlerepo
|
|
from mercurial import hg, changegroup, exchange
|
|
from mercurial.extensions import wrapfunction
|
|
from hgext import pager
|
|
from mercurial.node import hex, nullrev, nullid, short
|
|
from mercurial.i18n import _
|
|
import errno, os, re, glob, time
|
|
|
|
pager.attended.append('backups')
|
|
|
|
cmdtable = {}
|
|
command = cmdutil.command(cmdtable)
|
|
testedwith = 'internal'
|
|
msgwithevolve = """Evolve is enabled so no commit should be stripped unless you
|
|
explicitely called hg strip. hg backups will show you the stripped commits.
|
|
If you are trying to recover a commit hidden from a previous command, use hg
|
|
reflog to get its sha1 and you will be able to access it directly without
|
|
recovering a backup."""
|
|
|
|
@command('^backups', [
|
|
('', 'recover', '', 'brings the specified commit back into the repository')
|
|
] + commands.logopts, _('hg backups [--recover HASH]'))
|
|
def backups(ui, repo, *pats, **opts):
|
|
'''lists the commits available in backup bundles
|
|
|
|
Without any arguments, this command prints a list of the commits in each
|
|
backup bundle.
|
|
|
|
--recover takes a commit hash and unbundles the first bundle that contains
|
|
that commit hash, which puts that commit back in your repository.
|
|
|
|
--verbose will print the entire commit message and the bundle path for that
|
|
backup.
|
|
'''
|
|
try:
|
|
if extensions.find('evolve'):
|
|
# Warn evolve users that they probably don't want to use backups
|
|
# but reflog instead
|
|
ui.warn(msgwithevolve)
|
|
except KeyError:
|
|
pass
|
|
backuppath = repo.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:
|
|
ui.status("Recover commits using: hg backups --recover <commit hash>\n", label="status.removed")
|
|
|
|
for backup in backups:
|
|
# Much of this is copied from the hg incoming logic
|
|
source = os.path.relpath(backup, os.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:
|
|
ui.status("\nwarning: unable to open bundle %s - missing parent rev %s\n" %
|
|
(source, short(ex.name)))
|
|
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:
|
|
lock = repo.lock()
|
|
try:
|
|
if recovernode in other:
|
|
ui.status("Unbundling %s\n" % (recovernode))
|
|
f = hg.openpath(ui, source)
|
|
gen = exchange.readbundle(ui, f, source)
|
|
modheads = changegroup.addchangegroup(repo, gen, 'unbundle', 'bundle:' + source)
|
|
break
|
|
finally:
|
|
lock.release()
|
|
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'] = "{label('status.modified', node|short)} {desc|firstline}\n"
|
|
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()
|