mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 17:27:53 +03:00
584656dff3
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
202 lines
6.2 KiB
Python
202 lines
6.2 KiB
Python
# uncommit - undo the actions of a commit
|
|
#
|
|
# Copyright 2011 Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
|
|
# Logilab SA <contact@logilab.fr>
|
|
# Pierre-Yves David <pierre-yves.david@ens-lyon.org>
|
|
# Patrick Mezard <patrick@mezard.eu>
|
|
# Copyright 2016 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.
|
|
|
|
"""uncommit part or all of a local changeset (EXPERIMENTAL)
|
|
|
|
This command undoes the effect of a local commit, returning the affected
|
|
files to their uncommitted state. This means that files modified, added or
|
|
removed in the changeset will be left unchanged, and so will remain modified,
|
|
added and removed in the working directory.
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
|
|
from mercurial import (
|
|
cmdutil,
|
|
commands,
|
|
context,
|
|
copies,
|
|
error,
|
|
node,
|
|
obsutil,
|
|
pycompat,
|
|
registrar,
|
|
rewriteutil,
|
|
scmutil,
|
|
)
|
|
from mercurial.i18n import _
|
|
|
|
|
|
cmdtable = {}
|
|
command = registrar.command(cmdtable)
|
|
|
|
configtable = {}
|
|
configitem = registrar.configitem(configtable)
|
|
|
|
configitem("experimental", "uncommitondirtywdir", default=False)
|
|
|
|
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
|
|
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
|
|
# be specifying the version(s) of Mercurial they are tested with, or
|
|
# leave the attribute unspecified.
|
|
testedwith = "ships-with-hg-core"
|
|
|
|
|
|
def _commitfiltered(repo, ctx, match, allowempty):
|
|
"""Recommit ctx with changed files not in match. Return the new
|
|
node identifier, or None if nothing changed.
|
|
"""
|
|
base = ctx.p1()
|
|
# ctx
|
|
initialfiles = set(ctx.files())
|
|
exclude = set(f for f in initialfiles if match(f))
|
|
|
|
# No files matched commit, so nothing excluded
|
|
if not exclude:
|
|
return None
|
|
|
|
files = initialfiles - exclude
|
|
# return the p1 so that we don't create an obsmarker later
|
|
if not files and not allowempty:
|
|
return ctx.parents()[0].node()
|
|
|
|
# Filter copies
|
|
copied = copies.pathcopies(base, ctx)
|
|
copied = dict((dst, src) for dst, src in copied.iteritems() if dst in files)
|
|
|
|
def filectxfn(repo, memctx, path, contentctx=ctx, redirect=()):
|
|
if path not in contentctx:
|
|
return None
|
|
fctx = contentctx[path]
|
|
mctx = context.memfilectx(
|
|
repo,
|
|
memctx,
|
|
fctx.path(),
|
|
fctx.data(),
|
|
fctx.islink(),
|
|
fctx.isexec(),
|
|
copied=copied.get(path),
|
|
)
|
|
return mctx
|
|
|
|
new = context.memctx(
|
|
repo,
|
|
parents=[base.node(), node.nullid],
|
|
text=ctx.description(),
|
|
files=files,
|
|
filectxfn=filectxfn,
|
|
user=ctx.user(),
|
|
date=ctx.date(),
|
|
extra=ctx.extra(),
|
|
)
|
|
# phase handling
|
|
commitphase = ctx.phase()
|
|
overrides = {("phases", "new-commit"): commitphase}
|
|
with repo.ui.configoverride(overrides, "uncommit"):
|
|
newid = repo.commitctx(new)
|
|
return newid
|
|
|
|
|
|
def _fixdirstate(repo, oldctx, newctx, status):
|
|
""" fix the dirstate after switching the working directory from oldctx to
|
|
newctx which can be result of either unamend or uncommit.
|
|
"""
|
|
ds = repo.dirstate
|
|
copies = dict(ds.copies())
|
|
s = status
|
|
for f in s.modified:
|
|
if ds[f] == "r":
|
|
# modified + removed -> removed
|
|
continue
|
|
ds.normallookup(f)
|
|
|
|
for f in s.added:
|
|
if ds[f] == "r":
|
|
# added + removed -> unknown
|
|
ds.drop(f)
|
|
elif ds[f] != "a":
|
|
ds.add(f)
|
|
|
|
for f in s.removed:
|
|
if ds[f] == "a":
|
|
# removed + added -> normal
|
|
ds.normallookup(f)
|
|
elif ds[f] != "r":
|
|
ds.remove(f)
|
|
|
|
# Merge old parent and old working dir copies
|
|
oldcopies = {}
|
|
for f in s.modified + s.added:
|
|
src = oldctx[f].renamed()
|
|
if src:
|
|
oldcopies[f] = src[0]
|
|
oldcopies.update(copies)
|
|
copies = dict((dst, oldcopies.get(src, src)) for dst, src in oldcopies.iteritems())
|
|
# Adjust the dirstate copies
|
|
for dst, src in copies.iteritems():
|
|
if src not in newctx or dst in newctx or ds[dst] != "a":
|
|
src = None
|
|
ds.copy(src, dst)
|
|
|
|
|
|
@command(
|
|
"uncommit",
|
|
[("", "keep", False, _("allow an empty commit after uncommiting"))]
|
|
+ commands.walkopts,
|
|
_("[OPTION]... [FILE]..."),
|
|
)
|
|
def uncommit(ui, repo, *pats, **opts):
|
|
"""uncommit part or all of a local changeset
|
|
|
|
This command undoes the effect of a local commit, returning the affected
|
|
files to their uncommitted state. This means that files modified or
|
|
deleted in the changeset will be left unchanged, and so will remain
|
|
modified in the working directory.
|
|
"""
|
|
opts = pycompat.byteskwargs(opts)
|
|
|
|
with repo.wlock(), repo.lock():
|
|
|
|
if not pats and not repo.ui.configbool("experimental", "uncommitondirtywdir"):
|
|
cmdutil.bailifchanged(repo)
|
|
old = repo["."]
|
|
rewriteutil.precheck(repo, [old.rev()], "uncommit")
|
|
if len(old.parents()) > 1:
|
|
raise error.Abort(_("cannot uncommit merge changeset"))
|
|
|
|
with repo.transaction("uncommit"):
|
|
match = scmutil.match(old, pats, opts)
|
|
newid = _commitfiltered(repo, old, match, opts.get("keep"))
|
|
if newid is None:
|
|
ui.status(_("nothing to uncommit\n"))
|
|
return 1
|
|
|
|
mapping = {}
|
|
if newid != old.p1().node():
|
|
# Move local changes on filtered changeset
|
|
mapping[old.node()] = (newid,)
|
|
else:
|
|
# Fully removed the old commit
|
|
mapping[old.node()] = ()
|
|
|
|
scmutil.cleanupnodes(repo, mapping, "uncommit")
|
|
|
|
with repo.dirstate.parentchange():
|
|
repo.dirstate.setparents(newid, node.nullid)
|
|
s = repo.status(old.p1(), old, match=match)
|
|
_fixdirstate(repo, old, repo[newid], s)
|
|
|
|
|
|
def predecessormarkers(ctx):
|
|
"""yields the obsolete markers marking the given changeset as a successor"""
|
|
for data in ctx.repo().obsstore.predecessors.get(ctx.node(), ()):
|
|
yield obsutil.marker(ctx.repo(), data)
|