diff --git a/hgext/largefiles/lfutil.py b/hgext/largefiles/lfutil.py --- a/hgext/largefiles/lfutil.py +++ b/hgext/largefiles/lfutil.py @@ -162,6 +162,9 @@ class largefilesdirstate(dirstate.dirsta def __getitem__(self, key): return super(largefilesdirstate, self).__getitem__(unixpath(key)) + def set_tracked(self, f): + return super(largefilesdirstate, self).set_tracked(unixpath(f)) + def normal(self, f): return super(largefilesdirstate, self).normal(unixpath(f)) diff --git a/hgext/narrow/narrowdirstate.py b/hgext/narrow/narrowdirstate.py --- a/hgext/narrow/narrowdirstate.py +++ b/hgext/narrow/narrowdirstate.py @@ -38,6 +38,10 @@ def wrapdirstate(repo, dirstate): return super(narrowdirstate, self).normal(*args, **kwargs) @_editfunc + def set_tracked(self, *args): + return super(narrowdirstate, self).set_tracked(*args) + + @_editfunc def add(self, *args): return super(narrowdirstate, self).add(*args) diff --git a/hgext/sparse.py b/hgext/sparse.py --- a/hgext/sparse.py +++ b/hgext/sparse.py @@ -256,6 +256,7 @@ def _setupdirstate(ui): # Prevent adding files that are outside the sparse checkout editfuncs = [ b'normal', + b'set_tracked', b'add', b'normallookup', b'copy', diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -83,6 +83,17 @@ def requires_parents_change(func): return wrap +def requires_no_parents_change(func): + def wrap(self, *args, **kwargs): + if not self.pendingparentchange(): + msg = 'calling `%s` inside of a parentchange context' + msg %= func.__name__ + raise error.ProgrammingError(msg) + return func(self, *args, **kwargs) + + return wrap + + @interfaceutil.implementer(intdirstate.idirstate) class dirstate(object): def __init__( @@ -451,6 +462,24 @@ class dirstate(object): def copies(self): return self._map.copymap + @requires_no_parents_change + def set_tracked(self, filename): + """a "public" method for generic code to mark a file as tracked + + This function is to be called outside of "update/merge" case. For + example by a command like `hg add X`. + + return True the file was previously untracked, False otherwise. + """ + entry = self._map.get(filename) + if entry is None: + self._add(filename) + return True + elif not entry.tracked: + self.normallookup(filename) + return True + return False + @requires_parents_change def update_file_reference( self,