context: add memctx.mirror for making context mutable

Summary:
Not all context objects support the `__setitem__` API. Instead of adding supports
for all of them, let's add an API to create a `memctx` that mirrors the changes
from another `ctx` so one can them do mutations on the `memctx`. This will be
used by dirsync.

This might also be useful in other "amend" cases, because the `mirror` API takes
various kinds of context (workingctx, changectx, memctx), and is straihtforward
to use:

  In [2]: v=m.context.memctx.mirror(repo['.'])

  In [3]: v.commit()
  Out[3]: 'b\xdeF\xbb\xbb&\xe5\xdbvJ\x16\xf2\xc3\x92\x96Mr\xb7\x85\xbb'

  In [4]: repo['.'].node()
  Out[4]: 'b\xdeF\xbb\xbb&\xe5\xdbvJ\x16\xf2\xc3\x92\x96Mr\xb7\x85\xbb'

  In [5]: v['x']=v['.gitignore']

  In [6]: v.commit()
  Out[6]: 'b\xdeF\xbb\xbb&\xe5\xdbvJ\x16\xf2\xc3\x92\x96Mr\xb7\x85\xbb'

Reviewed By: DurhamG

Differential Revision: D26726480

fbshipit-source-id: 629cfc721f79d29ef395d4e1d6b3381f0095d573
This commit is contained in:
Jun Wu 2021-03-02 11:02:18 -08:00 committed by Facebook GitHub Bot
parent 691bc90d13
commit 2e52c0da0e

View File

@ -2559,6 +2559,70 @@ class memctx(committablectx):
self._text = editor(self._repo, self)
self._repo.savecommitmessage(self._text)
@classmethod
def mirror(
cls,
ctx,
user=None,
date=None,
text=None,
extra=None,
parentnodes=None,
mutinfo=None,
loginfo=None,
editor=False,
):
"""mirror another ctx, make it "mutable"
Note: This is different from overlayworkingctx in 2 ways:
1. memctx does not deep copy file contexts. So if the ctx has LFS
files that are lazily loaded, those files are still lazily loaded
with the new memctx.
2. memctx.mirror is for "amend", while overlayworkingctx.setbase is for
"commit on top of". memctx.mirror can also be used for "commit", if
passing in workingctx or another memctx. overlayworkingctx would
require deep copying for the "amend" use-case.
"""
repo = ctx.repo()
if parentnodes is None:
parentnodes = ([p.node() for p in ctx.parents()] + [nullid, nullid])[:2]
if text is None:
text = ctx.description()
if user is None:
user = ctx.user()
if date is None:
date = ctx.date()
if extra is None:
extra = ctx.extra()
def filectxfn(_repo, _ctx, path, ctx=ctx):
if path in ctx:
return ctx[path]
else:
# deleted file
return None
mctx = cls(
repo,
parents=parentnodes,
text=text,
files=ctx.files(),
filectxfn=filectxfn,
user=user,
date=date,
extra=extra,
loginfo=loginfo,
mutinfo=mutinfo,
)
if editor:
mctx._text = editor(repo, mctx)
repo.savecommitmessage(mctx._text)
return mctx
def filectx(self, path, filelog=None):
"""get a file context from the working directory