sapling/eden/scm/edenscm/mercurial/eden_dirstate.py
Jun Wu dabce28285 repoview: further remove repoview references
Summary:
Since repoview is removed, those concetps are useless. Therefore remove them.

This includes:
- repo.unfiltered(), repo.filtered(), repo.filtername
- changelog.filteredrevs
- error.FilteredIndexError, error.FilteredLookupError,
  error.FilteredRepoLookupError
- repo.unfilteredpropertycache, repo.filteredpropertycache,
  repo.unfilteredmethod
- index.headsrevsfiltered

Reviewed By: DurhamG

Differential Revision: D22367600

fbshipit-source-id: d133b8aaa136176b4c9f7f4b0c52ee60ac888531
2020-07-06 14:04:27 -07:00

241 lines
8.7 KiB
Python

# Copyright (c) Facebook, Inc. and its affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
import os
import stat
from eden.dirstate import MERGE_STATE_BOTH_PARENTS, MERGE_STATE_OTHER_PARENT
from . import (
EdenThriftClient as thrift,
dirstate,
eden_dirstate_fs,
eden_dirstate_map,
encoding,
localrepo,
match as matchmod,
perftrace,
pycompat,
scmutil,
ui as ui_mod,
util,
)
from .EdenThriftClient import ScmFileStatus
from .i18n import _
from .node import nullid
propertycache = util.propertycache
class eden_dirstate(dirstate.dirstate):
def __init__(self, repo, ui, root):
# type: (localrepo.localrepository, ui_mod.ui, str) -> None
self.eden_client = thrift.EdenThriftClient(repo)
# We should override any logic in dirstate that uses self._validate.
validate = repo._dirstatevalidate
try:
opener = repo.localvfs
except AttributeError:
opener = repo.vfs
super(eden_dirstate, self).__init__(opener, ui, root, validate, repo)
def create_eden_dirstate(ui, opener, root):
return eden_dirstate_map.eden_dirstate_map(
ui, opener, root, self.eden_client, repo
)
self._mapcls = create_eden_dirstate
self._fs = eden_dirstate_fs.eden_filesystem(self._root, self)
def __iter__(self):
# FIXME: This appears to be called by `hg reset`, so we provide a dummy
# response here, but really, we should outright prohibit this.
# Most likely, we will have to replace the implementation of `hg reset`.
return
yield
def iteritems(self): # override
# This seems like the type of O(repo) operation that should not be
# allowed. Or if it is, it should be through a separate, explicit
# codepath.
#
# We do provide pycompat.iteritems(ede) for users to iterate through only the
# files explicitly tracked in the eden dirstate.
raise NotImplementedError("pycompat.iteritems(eden_dirstate)")
def dirs(self): # override
raise NotImplementedError("eden_dirstate.dirs()")
def edeniteritems(self):
"""
Walk over all items tracked in the eden dirstate.
This includes non-normal files (e.g., files marked for addition or
removal), as well as normal files that have merge state information.
"""
return pycompat.iteritems(self._map._map)
def _p1_ctx(self):
"""Return the context object for the first parent commit."""
return self._map._repo[self.p1()]
def _call_match_callbacks(self, match, results1, results2):
"""
Process all explicit patterns in the match, and call match.bad()
if necessary
Returns a dictionary of (path -> mode) for all explicit matches that
are not already present in the results. The mode will be None if the
path does not exist on disk.
"""
# TODO: We do not currently invoke match.traversedir
# This is currently only used by `hg purge`, which uses it to remove
# empty directories.
# We probably should just build our own Eden-specific version of purge.
explicit_matches = {}
for path in sorted(match.files()):
try:
if path in results1 or path in results2:
continue
mode = os.lstat(os.path.join(self._root, path)).st_mode
if stat.S_ISDIR(mode):
pass
elif stat.S_ISREG(mode) or stat.S_ISLNK(mode):
explicit_matches[path] = mode
except OSError as ex:
# Check to see if this refers to a removed file or directory.
# Call match.bad() otherwise
if self._ismissing(path):
explicit_matches[path] = None
else:
match.bad(path, encoding.strtolocal(ex.strerror))
return explicit_matches
def _ismissing(self, path):
"""
Check to see if this path refers to a deleted file that mercurial
knows about but that no longer exists on disk.
"""
# Check to see if the parent commit knows about this path
parent_mf = self._p1_ctx().manifest()
if parent_mf.hasdir(path):
return True
# Check to see if the non-normal files list knows about this path
# or any child of this path as a directory name.
# (This handles the case where an untracked file was added with
# 'hg add' but then deleted from disk.)
if path in self._map._map:
return True
dirpath = path + "/"
for entry in self._map._map:
if entry.startswith(dirpath):
return True
return False
def _parent_commit_matches(self, match):
# Wrap match.bad()
# We don't want to complain about paths that do not exist in the parent
# commit but do exist in our non-normal files.
#
# However, the default mercurial dirstate.matches() code never invokes
# bad() at all, so lets just ignore all bad() calls entirely.
def bad(fn, msg):
return
m = matchmod.badmatch(match, bad)
return self._p1_ctx().matches(m)
def matches(self, match): # override
# Call matches() on the current working directory parent commit
results = set(self._parent_commit_matches(match))
# Augument the results with anything modified in the dirstate,
# to take care of added/removed files.
for path in self._map._map.keys():
if match(path):
results.add(path)
return results
def non_removed_matches(self, match): # override
"""
Behaves like matches(), but excludes files that have been removed from
the dirstate.
"""
results = set(self._parent_commit_matches(match))
# Augument the results with anything modified in the dirstate,
# to take care of added/removed files.
for path, state in self._map._map.items():
if match(path):
if state[0] == "r":
results.discard(path)
else:
results.add(path)
return results
def rebuild(self, parent, allfiles, changedfiles=None, exact=False):
# Ignore the input allfiles parameter, and always rebuild with
# an empty allfiles list.
#
# edenfs itself will track the file changes correctly.
# We only track merge state and added/removed status in the python
# dirstate code.
super(eden_dirstate, self).rebuild(
parent, allfiles=[], changedfiles=changedfiles, exact=exact
)
def normallookup(self, f): # override
"""Mark a file normal, but possibly dirty."""
if self._pl[1] != nullid:
# if there is a merge going on and the file was either
# in state 'm' (-1) or coming from other parent (-2) before
# being removed, restore that state.
#
# Note that we intentionally use self._map._map.get() here
# rather than self._map.get() to avoid making a thrift call to Eden
# if this file is already normal.
entry = self._map._map.get(f)
if entry is not None:
status, mode, merge_state, _dummy_mtime = entry
if status == "r" and merge_state in (
MERGE_STATE_BOTH_PARENTS,
MERGE_STATE_OTHER_PARENT,
):
source = self._map.copymap.get(f)
if merge_state == MERGE_STATE_BOTH_PARENTS:
self.merge(f)
elif merge_state == MERGE_STATE_OTHER_PARENT:
self.otherparent(f)
if source:
self.copy(source, f)
return
if status == "m":
return
if status == "n" and merge_state == MERGE_STATE_OTHER_PARENT:
return
# TODO: Just invoke self.normal() here for now.
# Our self.status() function always returns an empty list for the first
# entry of the returned tuple. (This is the list of files that we're
# unsure about and need to check on disk.) Therefore the
# workingctx._dirstatestatus() code never fixes up entries with the
# mtime set to -1.
#
# Ideally we should replace self.normal() too; we should be able to
# avoid the filesystem stat call in self.normal() anyway.
self.normal(f)