##// END OF EJS Templates
commit: move the addremove logic around to make the next changeset clearer...
commit: move the addremove logic around to make the next changeset clearer Lets do the noise now, without changing any thing. So the new changeset can focus on the actual semantic changes.

File last commit:

r50918:e1cff854 default
r50923:a46dfc2b default
Show More
dirstate.py
407 lines | 11.7 KiB | text/x-python | PythonLexer
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 import contextlib
import os
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,
util as interfaceutil,
)
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
extensions.wrapfunction(matchmod, b'readpatternfile', readpatternfile)
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
@interfaceutil.implementer(intdirstate.idirstate)
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class gitdirstate:
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
def p1(self):
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
def p2(self):
# 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
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 def setparents(self, p1, p2=None):
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')
)
def branch(self):
return b'default'
def parents(self):
# TODO how on earth do we find p2 if a merge is in flight?
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 return self.p1(), sha1nodeconstants.nullid
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961
def __iter__(self):
return (pycompat.fsencode(f.path) for f in self.git.index)
def items(self):
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]
def __contains__(self, filename):
try:
gs = self.git.status_file(filename)
return _STATUS_MAP[gs] != b'?'
except KeyError:
return False
def status(self, match, subrepos, ignored, clean, unknown):
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 )
def flagfunc(self, buildfallback):
# TODO we can do better
return buildfallback()
def getcwd(self):
# TODO is this a good way to do this?
return os.path.dirname(
os.path.dirname(pycompat.fsencode(self.git.path))
)
Matt Harbison
git: adapt to some recent dirstate API changes...
r49968 def get_entry(self, path):
"""return a DirstateItem for the associated path"""
entry = self._map.get(path)
if entry is None:
return DirstateItem()
return entry
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 def normalize(self, path):
normed = util.normcase(path)
assert normed == path, b"TODO handling of case folding: %s != %s" % (
normed,
path,
)
return path
@property
def _checklink(self):
return util.checklink(os.path.dirname(pycompat.fsencode(self.git.path)))
def copies(self):
# TODO support copies?
return {}
# # TODO what the heck is this
_filecache = set()
dirstate: rename `pendingparentchange` to `is_changing_parents`...
r50917 def is_changing_parents(self):
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
dirstate: introduce a `is_changing_any` property...
r50918 def is_changing_any(self):
# TODO: we need to implement the context manager bits and
# correctly stage/revert index edits.
return False
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 def write(self, tr):
# TODO: call parent change callbacks
if tr:
def writeinner(category):
self.git.index.write()
tr.addpending(b'gitdirstate', writeinner)
else:
self.git.index.write()
def pathto(self, f, cwd=None):
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
def matches(self, match):
for x in self.git.index:
p = pycompat.fsencode(x.path)
if match(p):
yield p
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.
def walk(self, match, subrepos, unknown, ignored, full=True):
# 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
def savebackup(self, tr, backupname):
# TODO: figure out a strategy for saving index backups.
pass
def restorebackup(self, tr, backupname):
# TODO: figure out a strategy for saving index backups.
pass
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
def copied(self, path):
# 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
def addparentchangecallback(self, category, callback):
# TODO: should this be added to the dirstate interface?
self._plchangecallbacks[category] = callback
def clearbackup(self, tr, backupname):
# TODO
pass
Josef 'Jeff' Sipek
git: abort when attempting to set a branch...
r45112
def setbranch(self, branch):
raise error.Abort(
b'git repos do not support branches. try using bookmarks'
)