sapling/hgext/amend/common.py
Saurabh Singh 6317ba0ca2 amend: replace with the fbamend extension
Summary:
The functionality we care about is provided by the `fbamend`
extension. Therefore, lets replace the `amend` extension with the `fbamend`
extension.

Reviewed By: farnz

Differential Revision: D10320739

fbshipit-source-id: 5700d39f488777fcc4033f60ce0a51cda15ef2ad
2018-10-11 06:59:23 -07:00

258 lines
8.4 KiB
Python

# common.py - common utilities for building commands
#
# 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.
from __future__ import absolute_import
from collections import defaultdict
from hgext import rebase
from mercurial import cmdutil, context, copies, error, extensions, lock as lockmod
from mercurial.i18n import _
from mercurial.node import nullrev
def getchildrelationships(repo, revs):
"""Build a defaultdict of child relationships between all descendants of
revs. This information will prevent us from having to repeatedly
perform children that reconstruct these relationships each time.
"""
cl = repo.changelog
children = defaultdict(set)
for rev in repo.revs("(%ld)::", revs):
for parent in cl.parentrevs(rev):
if parent != nullrev:
children[parent].add(rev)
return children
def restackonce(
ui,
repo,
rev,
rebaseopts=None,
childrenonly=False,
noconflict=None,
noconflictmsg=None,
):
"""Rebase all descendants of precursors of rev onto rev, thereby
stabilzing any non-obsolete descendants of those precursors.
Takes in an optional dict of options for the rebase command.
If childrenonly is True, only rebases direct children of precursors
of rev rather than all descendants of those precursors.
NOTE(phillco): This function shouldn't be used; prefer restack.restack
or a custom rebase using `-d _destrestack(SRC)`.
"""
# Get visible descendants of precusors of rev.
allpredecessors = repo.revs("predecessors(%d) - (%d)", rev, rev)
fmt = "%s(%%ld) - %%ld" % ("children" if childrenonly else "descendants")
descendants = repo.revs(fmt, allpredecessors, allpredecessors)
# Nothing to do if there are no descendants.
if not descendants:
return
# Overwrite source and destination, leave all other options.
if rebaseopts is None:
rebaseopts = {}
rebaseopts["rev"] = descendants
rebaseopts["dest"] = rev
rebaseopts["noconflict"] = noconflict
# We need to ensure that the 'operation' field in the obsmarker metadata
# is always set to 'rebase', regardless of the current command so that
# the restacked commits will appear as 'rebased' in smartlog.
overrides = {}
try:
tweakdefaults = extensions.find("tweakdefaults")
except KeyError:
# No tweakdefaults extension -- skip this since there is no wrapper
# to set the metadata.
pass
else:
overrides[
(tweakdefaults.globaldata, tweakdefaults.createmarkersoperation)
] = "rebase"
if noconflictmsg:
overrides[("rebase", "noconflictmsg")] = noconflictmsg
# Perform rebase.
with repo.ui.configoverride(overrides, "restack"):
rebase.rebase(ui, repo, **rebaseopts)
def latest(repo, rev):
"""Find the "latest version" of the given revision -- either the
latest visible successor, or the revision itself if it has no
visible successors.
"""
latest = repo.revs("successors(%d)", rev).last()
return latest if latest is not None else rev
def bookmarksupdater(repo, oldids, tr):
"""Return a callable update(newid) updating the current bookmark
and bookmarks bound to oldid to newid.
"""
if type(oldids) is bytes:
oldids = [oldids]
def updatebookmarks(newid):
dirty = False
for oldid in oldids:
changes = []
oldbookmarks = repo.nodebookmarks(oldid)
if oldbookmarks:
for b in oldbookmarks:
changes.append((b, newid))
dirty = True
if dirty:
repo._bookmarks.applychanges(repo, tr, changes)
return updatebookmarks
def rewrite(repo, old, updates, head, newbases, commitopts):
"""Return (nodeid, created) where nodeid is the identifier of the
changeset generated by the rewrite process, and created is True if
nodeid was actually created. If created is False, nodeid
references a changeset existing before the rewrite call.
"""
wlock = lock = tr = None
try:
wlock = repo.wlock()
lock = repo.lock()
tr = repo.transaction("rewrite")
if len(old.parents()) > 1: # XXX remove this unnecessary limitation.
raise error.Abort(_("cannot amend merge changesets"))
base = old.p1()
updatebookmarks = bookmarksupdater(
repo, [old.node()] + [u.node() for u in updates], tr
)
# commit a new version of the old changeset, including the update
# collect all files which might be affected
files = set(old.files())
for u in updates:
files.update(u.files())
# Recompute copies (avoid recording a -> b -> a)
copied = copies.pathcopies(base, head)
# prune files which were reverted by the updates
def samefile(f):
if f in head.manifest():
a = head.filectx(f)
if f in base.manifest():
b = base.filectx(f)
return a.data() == b.data() and a.flags() == b.flags()
else:
return False
else:
return f not in base.manifest()
files = [f for f in files if not samefile(f)]
# commit version of these files as defined by head
headmf = head.manifest()
def filectxfn(repo, ctx, path):
if path in headmf:
fctx = head[path]
flags = fctx.flags()
mctx = context.memfilectx(
repo,
ctx,
fctx.path(),
fctx.data(),
islink="l" in flags,
isexec="x" in flags,
copied=copied.get(path),
)
return mctx
return None
message = cmdutil.logmessage(repo.ui, commitopts)
if not message:
message = old.description()
user = commitopts.get("user") or old.user()
# TODO: In case not date is given, we should take the old commit date
# if we are working one one changeset or mimic the fold behavior about
# date
date = commitopts.get("date") or None
extra = dict(commitopts.get("extra", old.extra()))
extra["branch"] = head.branch()
new = context.memctx(
repo,
parents=newbases,
text=message,
files=files,
filectxfn=filectxfn,
user=user,
date=date,
extra=extra,
)
if commitopts.get("edit"):
new._text = cmdutil.commitforceeditor(repo, new, [])
revcount = len(repo)
newid = repo.commitctx(new)
new = repo[newid]
created = len(repo) != revcount
updatebookmarks(newid)
tr.close()
return newid, created
finally:
lockmod.release(tr, lock, wlock)
def metarewrite(repo, old, newbases, commitopts):
"""Return (nodeid, created) where nodeid is the identifier of the
changeset generated by the rewrite process, and created is True if
nodeid was actually created. If created is False, nodeid
references a changeset existing before the rewrite call.
"""
wlock = lock = tr = None
try:
wlock = repo.wlock()
lock = repo.lock()
tr = repo.transaction("rewrite")
updatebookmarks = bookmarksupdater(repo, old.node(), tr)
message = cmdutil.logmessage(repo.ui, commitopts)
if not message:
message = old.description()
user = commitopts.get("user") or old.user()
date = commitopts.get("date") or None # old.date()
extra = dict(commitopts.get("extra", old.extra()))
extra["branch"] = old.branch()
new = context.metadataonlyctx(
repo, old, parents=newbases, text=message, user=user, date=date, extra=extra
)
if commitopts.get("edit"):
new._text = cmdutil.commitforceeditor(repo, new, [])
revcount = len(repo)
newid = repo.commitctx(new)
new = repo[newid]
created = len(repo) != revcount
updatebookmarks(newid)
tr.close()
return newid, created
finally:
lockmod.release(tr, lock, wlock)
def newunstable(repo, revs):
return repo.revs("(%ld::) - %ld", revs, revs)