##// END OF EJS Templates
releasenotes: stop using the `pycompat.open()` shim
releasenotes: stop using the `pycompat.open()` shim

File last commit:

r52822:93d872a0 default
r53282:7a5b026c default
Show More
dirstate.py
429 lines | 12.5 KiB | text/x-python | PythonLexer
Matt Harbison
typing: add `from __future__ import annotations` to most files...
r52756 from __future__ import annotations
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 import contextlib
import os
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 from typing import (
Any,
Dict,
Iterable,
Iterator,
List,
Optional,
Tuple,
)
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 from mercurial.node import sha1nodeconstants
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 from mercurial import (
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968 dirstatemap,
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 error,
extensions,
match as matchmod,
pycompat,
scmutil,
util,
)
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968 from mercurial.dirstateutils import (
timestamp,
)
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 from mercurial.interfaces import (
dirstate as intdirstate,
)
from . import gitutil
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968
DirstateItem = dirstatemap.DirstateItem
propertycache = util.propertycache
Martin von Zweigbergk
git: don't fail import when pygit2 is not install...
r44968 pygit2 = gitutil.get_pygit2()
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
def readpatternfile(orig, filepath, warn, sourceinfo=False):
if not (b'info/exclude' in filepath or filepath.endswith(b'.gitignore')):
return orig(filepath, warn, sourceinfo=False)
result = []
warnings = []
Matt Harbison
git: un-byteify the `mode` argument for the builtin `open()`...
r49969 with open(filepath, 'rb') as fp:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 for l in fp:
l = l.strip()
if not l or l.startswith(b'#'):
continue
if l.startswith(b'!'):
warnings.append(b'unsupported ignore pattern %s' % l)
continue
if l.startswith(b'/'):
result.append(b'rootglob:' + l[1:])
else:
result.append(b'relglob:' + l)
return result, warnings
wrapfunction: use sysstr instead of bytes as argument in the "git" extension...
r51672 extensions.wrapfunction(matchmod, 'readpatternfile', readpatternfile)
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
Martin von Zweigbergk
git: don't fail import when pygit2 is not install...
r44968 _STATUS_MAP = {}
if pygit2:
_STATUS_MAP = {
pygit2.GIT_STATUS_CONFLICTED: b'm',
pygit2.GIT_STATUS_CURRENT: b'n',
pygit2.GIT_STATUS_IGNORED: b'?',
pygit2.GIT_STATUS_INDEX_DELETED: b'r',
pygit2.GIT_STATUS_INDEX_MODIFIED: b'n',
pygit2.GIT_STATUS_INDEX_NEW: b'a',
pygit2.GIT_STATUS_INDEX_RENAMED: b'a',
pygit2.GIT_STATUS_INDEX_TYPECHANGE: b'n',
pygit2.GIT_STATUS_WT_DELETED: b'r',
pygit2.GIT_STATUS_WT_MODIFIED: b'n',
pygit2.GIT_STATUS_WT_NEW: b'?',
pygit2.GIT_STATUS_WT_RENAMED: b'a',
pygit2.GIT_STATUS_WT_TYPECHANGE: b'n',
pygit2.GIT_STATUS_WT_UNREADABLE: b'?',
Matt Harbison
git: ensure all dirstate state values are bytes...
r47828 pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_WT_MODIFIED: b'm',
Martin von Zweigbergk
git: don't fail import when pygit2 is not install...
r44968 }
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
Matt Harbison
dirstate: subclass the new dirstate Protocol class...
r52818 class gitdirstate(intdirstate.idirstate):
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968 def __init__(self, ui, vfs, gitrepo, use_dirstate_v2):
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 self._ui = ui
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968 self._root = os.path.dirname(vfs.base)
self._opener = vfs
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 self.git = gitrepo
self._plchangecallbacks = {}
Augie Fackler
git: restore basic functionality (issue6545)...
r48571 # TODO: context.poststatusfixup is bad and uses this attribute
self._dirty = False
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968 self._mapcls = dirstatemap.dirstatemap
self._use_dirstate_v2 = use_dirstate_v2
@propertycache
def _map(self):
"""Return the dirstate contents (see documentation for dirstatemap)."""
self._map = self._mapcls(
self._ui,
self._opener,
self._root,
sha1nodeconstants,
self._use_dirstate_v2,
)
return self._map
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def p1(self) -> bytes:
Augie Fackler
git: correctly handle p1() on dirstate when underlying git repo is empty...
r44976 try:
return self.git.head.peel().id.raw
except pygit2.GitError:
# Typically happens when peeling HEAD fails, as in an
# empty repository.
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 return sha1nodeconstants.nullid
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def p2(self) -> bytes:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO: MERGE_HEAD? something like that, right?
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 return sha1nodeconstants.nullid
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def setparents(self, p1: bytes, p2: Optional[bytes] = None):
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if p2 is None:
p2 = sha1nodeconstants.nullid
assert p2 == sha1nodeconstants.nullid, b'TODO merging support'
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 self.git.head.set_target(gitutil.togitnode(p1))
@util.propertycache
def identity(self):
return util.filestat.frompath(
os.path.join(self._root, b'.git', b'index')
)
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def branch(self) -> bytes:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 return b'default'
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def parents(self) -> List[bytes]:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO how on earth do we find p2 if a merge is in flight?
Matt Harbison
git: make `dirstate.parents()` return a list like the core class...
r52820 return [self.p1(), sha1nodeconstants.nullid]
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def __iter__(self) -> Iterator[bytes]:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 return (pycompat.fsencode(f.path) for f in self.git.index)
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def items(self) -> Iterator[Tuple[bytes, intdirstate.DirstateItemT]]:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 for ie in self.git.index:
dirstate-item: rename the class to DirstateItem...
r48328 yield ie.path, None # value should be a DirstateItem
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
# py2,3 compat forward
iteritems = items
def __getitem__(self, filename):
try:
gs = self.git.status_file(filename)
except KeyError:
return b'?'
return _STATUS_MAP[gs]
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def __contains__(self, filename: Any) -> bool:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 try:
gs = self.git.status_file(filename)
return _STATUS_MAP[gs] != b'?'
except KeyError:
return False
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def status(
self,
match: matchmod.basematcher,
subrepos: bool,
ignored: bool,
clean: bool,
unknown: bool,
) -> intdirstate.StatusReturnT:
Pulkit Goyal
git: remove unrequired assignment of listignored and listunknown...
r46001 listclean = clean
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO handling of clean files - can we get that from git.status()?
modified, added, removed, deleted, unknown, ignored, clean = (
[],
[],
[],
[],
[],
[],
[],
)
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968
try:
mtime_boundary = timestamp.get_fs_now(self._opener)
except OSError:
# In largefiles or readonly context
mtime_boundary = None
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 gstatus = self.git.status()
for path, status in gstatus.items():
path = pycompat.fsencode(path)
Augie Fackler
git: make dirstate status() respect matcher...
r45990 if not match(path):
continue
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 if status == pygit2.GIT_STATUS_IGNORED:
if path.endswith(b'/'):
continue
ignored.append(path)
elif status in (
pygit2.GIT_STATUS_WT_MODIFIED,
pygit2.GIT_STATUS_INDEX_MODIFIED,
pygit2.GIT_STATUS_WT_MODIFIED
| pygit2.GIT_STATUS_INDEX_MODIFIED,
):
modified.append(path)
elif status == pygit2.GIT_STATUS_INDEX_NEW:
added.append(path)
elif status == pygit2.GIT_STATUS_WT_NEW:
unknown.append(path)
elif status == pygit2.GIT_STATUS_WT_DELETED:
deleted.append(path)
elif status == pygit2.GIT_STATUS_INDEX_DELETED:
removed.append(path)
else:
raise error.Abort(
b'unhandled case: status for %r is %r' % (path, status)
)
Augie Fackler
git: make dirstate actually support listclean parameter...
r45991 if listclean:
observed = set(
modified + added + removed + deleted + unknown + ignored
)
index = self.git.index
index.read()
for entry in index:
path = pycompat.fsencode(entry.path)
if not match(path):
continue
if path in observed:
continue # already in some other set
if path[-1] == b'/':
continue # directory
clean.append(path)
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO are we really always sure of status here?
return (
False,
scmutil.status(
modified, added, removed, deleted, unknown, ignored, clean
),
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968 mtime_boundary,
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 )
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def flagfunc(
self, buildfallback: intdirstate.FlagFuncFallbackT
) -> intdirstate.FlagFuncReturnT:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO we can do better
return buildfallback()
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def getcwd(self) -> bytes:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO is this a good way to do this?
return os.path.dirname(
os.path.dirname(pycompat.fsencode(self.git.path))
)
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def get_entry(self, path: bytes) -> intdirstate.DirstateItemT:
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968 """return a DirstateItem for the associated path"""
entry = self._map.get(path)
if entry is None:
return DirstateItem()
return entry
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def normalize(
self, path: bytes, isknown: bool = False, ignoremissing: bool = False
) -> bytes:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 normed = util.normcase(path)
assert normed == path, b"TODO handling of case folding: %s != %s" % (
normed,
path,
)
return path
@property
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def _checklink(self) -> bool:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 return util.checklink(os.path.dirname(pycompat.fsencode(self.git.path)))
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def copies(self) -> Dict[bytes, bytes]:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO support copies?
return {}
# # TODO what the heck is this
_filecache = set()
Matt Harbison
git: correct some signature mismatches between dirstate and the Protocol class...
r52817 @property
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def is_changing_parents(self) -> bool:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO: we need to implement the context manager bits and
# correctly stage/revert index edits.
return False
Matt Harbison
git: correct some signature mismatches between dirstate and the Protocol class...
r52817 @property
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def is_changing_any(self) -> bool:
dirstate: introduce a `is_changing_any` property...
r50918 # TODO: we need to implement the context manager bits and
# correctly stage/revert index edits.
return False
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def write(self, tr: Optional[intdirstate.TransactionT]) -> None:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO: call parent change callbacks
if tr:
def writeinner(category):
self.git.index.write()
tr.addpending(b'gitdirstate', writeinner)
else:
self.git.index.write()
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def pathto(self, f: bytes, cwd: Optional[bytes] = None) -> bytes:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 if cwd is None:
cwd = self.getcwd()
# TODO core dirstate does something about slashes here
assert isinstance(f, bytes)
r = util.pathto(self._root, cwd, f)
return r
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def matches(self, match: matchmod.basematcher) -> Iterable[bytes]:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 for x in self.git.index:
p = pycompat.fsencode(x.path)
if match(p):
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 yield p # TODO: return list instead of yielding?
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
dirstate: make it mandatory to provide parentfiledata in `set_clean`...
r49208 def set_clean(self, f, parentfiledata):
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 """Mark a file normal and clean."""
# TODO: for now we just let libgit2 re-stat the file. We can
# clearly do better.
Augie Fackler
git: restore basic functionality (issue6545)...
r48571 def set_possibly_dirty(self, f):
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 """Mark a file normal, but possibly dirty."""
# TODO: for now we just let libgit2 re-stat the file. We can
# clearly do better.
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def walk(
self,
match: matchmod.basematcher,
subrepos: Any,
unknown: bool,
ignored: bool,
full: bool = True,
) -> intdirstate.WalkReturnT:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO: we need to use .status() and not iterate the index,
# because the index doesn't force a re-walk and so `hg add` of
# a new file without an intervening call to status will
# silently do nothing.
r = {}
cwd = self.getcwd()
for path, status in self.git.status().items():
if path.startswith('.hg/'):
continue
path = pycompat.fsencode(path)
if not match(path):
continue
# TODO construct the stat info from the status object?
try:
s = os.stat(os.path.join(cwd, path))
Manuel Jacob
py3: catch FileNotFoundError instead of checking errno == ENOENT
r50201 except FileNotFoundError:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 continue
r[path] = s
return r
av6
git: add a missing reset_copy keyword argument to dirstate.set_tracked()...
r50280 def set_tracked(self, f, reset_copy=False):
# TODO: support copies and reset_copy=True
Augie Fackler
git: restore basic functionality (issue6545)...
r48571 uf = pycompat.fsdecode(f)
if uf in self.git.index:
return False
index = self.git.index
index.read()
index.add(uf)
index.write()
return True
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 def add(self, f):
Augie Fackler
git: fix up dirstate use of index...
r45989 index = self.git.index
index.read()
index.add(pycompat.fsdecode(f))
index.write()
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
def drop(self, f):
Augie Fackler
git: fix up dirstate use of index...
r45989 index = self.git.index
index.read()
Augie Fackler
git: fix index handling of removed files during commit (issue6398)...
r45992 fs = pycompat.fsdecode(f)
if fs in index:
index.remove(fs)
index.write()
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
Augie Fackler
git: restore basic functionality (issue6545)...
r48571 def set_untracked(self, f):
index = self.git.index
index.read()
fs = pycompat.fsdecode(f)
if fs in index:
index.remove(fs)
index.write()
return True
return False
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 def remove(self, f):
Augie Fackler
git: fix up dirstate use of index...
r45989 index = self.git.index
index.read()
index.remove(pycompat.fsdecode(f))
index.write()
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def copied(self, file: bytes) -> Optional[bytes]:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO: track copies?
return None
Josef 'Jeff' Sipek
git: implement stub prefetch_parents dirstate method...
r45446 def prefetch_parents(self):
# TODO
pass
Augie Fackler
git: restore basic functionality (issue6545)...
r48571 def update_file(self, *args, **kwargs):
# TODO
pass
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 @contextlib.contextmanager
dirstate: rename parentchange to changing_parents...
r50855 def changing_parents(self, repo):
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO: track this maybe?
yield
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def addparentchangecallback(
self, category: bytes, callback: intdirstate.AddParentChangeCallbackT
) -> None:
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 # TODO: should this be added to the dirstate interface?
self._plchangecallbacks[category] = callback
Matt Harbison
typing: add type annotations to the dirstate classes...
r52822 def setbranch(
self, branch: bytes, transaction: Optional[intdirstate.TransactionT]
) -> None:
Josef 'Jeff' Sipek
git: abort when attempting to set a branch...
r45112 raise error.Abort(
b'git repos do not support branches. try using bookmarks'
)