mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 09:17:30 +03:00
b2a8809a8b
Summary: Fix a ValueError when reverting conflicted files during conflict resolution. Reviewed By: simpkins Differential Revision: D20254088 fbshipit-source-id: 0c121e8b4b7a3d97ce138a824a44e0d258019d0d
241 lines
8.7 KiB
Python
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.unfiltered()[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)
|