mercurial/narrowspec.py
changeset 41043 ce0bc2952e2a
parent 41041 1e8d9f472ea1
child 41176 4475322b7533
--- a/mercurial/narrowspec.py	Fri Jul 13 11:26:46 2018 -0700
+++ b/mercurial/narrowspec.py	Fri Dec 21 10:13:49 2018 -0800
@@ -13,12 +13,16 @@
 from . import (
     error,
     match as matchmod,
+    merge,
     repository,
     sparse,
     util,
 )
 
+# The file in .hg/store/ that indicates which paths exit in the store
 FILENAME = 'narrowspec'
+# The file in .hg/ that indicates which paths exit in the dirstate
+DIRSTATE_FILENAME = 'narrowspec.dirstate'
 
 # Pattern prefixes that are allowed in narrow patterns. This list MUST
 # only contain patterns that are fast and safe to evaluate. Keep in mind
@@ -157,6 +161,18 @@
     spec = format(includepats, excludepats)
     repo.svfs.write(FILENAME, spec)
 
+def copytoworkingcopy(repo, tr):
+    if tr:
+        def write(file):
+            spec = repo.svfs.read(FILENAME)
+            file.write(spec)
+            file.close()
+        tr.addfilegenerator('narrowspec', (DIRSTATE_FILENAME,), write,
+                            location='plain')
+    else:
+        spec = repo.svfs.read(FILENAME)
+        repo.vfs.write(DIRSTATE_FILENAME, spec)
+
 def savebackup(repo, backupname):
     if repository.NARROW_REQUIREMENT not in repo.requirements:
         return
@@ -226,3 +242,57 @@
     else:
         res_includes = set(req_includes)
     return res_includes, res_excludes, invalid_includes
+
+# These two are extracted for extensions (specifically for Google's CitC file
+# system)
+def _deletecleanfiles(repo, files):
+    for f in files:
+        repo.wvfs.unlinkpath(f)
+
+def _writeaddedfiles(repo, pctx, files):
+    actions = merge.emptyactions()
+    addgaction = actions['g'].append
+    mf = repo['.'].manifest()
+    for f in files:
+        if not repo.wvfs.exists(f):
+            addgaction((f, (mf.flags(f), False), "narrowspec updated"))
+    merge.applyupdates(repo, actions, wctx=repo[None],
+                       mctx=repo['.'], overwrite=False)
+
+def checkworkingcopynarrowspec(repo):
+    storespec = repo.svfs.tryread(FILENAME)
+    wcspec = repo.vfs.tryread(DIRSTATE_FILENAME)
+    if wcspec != storespec:
+        raise error.Abort(_("working copy's narrowspec is stale"),
+                          hint=_("run 'hg tracked --update-working-copy'"))
+
+def updateworkingcopy(repo, tr):
+    oldspec = repo.vfs.tryread(DIRSTATE_FILENAME)
+    newspec = repo.svfs.tryread(FILENAME)
+
+    oldincludes, oldexcludes = parseconfig(repo.ui, oldspec)
+    newincludes, newexcludes = parseconfig(repo.ui, newspec)
+    oldmatch = match(repo.root, include=oldincludes, exclude=oldexcludes)
+    newmatch = match(repo.root, include=newincludes, exclude=newexcludes)
+    addedmatch = matchmod.differencematcher(newmatch, oldmatch)
+    removedmatch = matchmod.differencematcher(oldmatch, newmatch)
+
+    ds = repo.dirstate
+    lookup, status = ds.status(removedmatch, subrepos=[], ignored=False,
+                               clean=True, unknown=False)
+    _deletecleanfiles(repo, status.clean)
+    trackeddirty = lookup + status.modified + status.added
+    for f in sorted(trackeddirty):
+        repo.ui.status(_('not deleting possibly dirty file %s\n') % f)
+    for f in status.clean + trackeddirty:
+        ds.drop(f)
+
+    repo.narrowpats = newincludes, newexcludes
+    repo._narrowmatch = newmatch
+    pctx = repo['.']
+    newfiles = [f for f in pctx.manifest().walk(addedmatch) if f not in ds]
+    for f in newfiles:
+        ds.normallookup(f)
+    _writeaddedfiles(repo, pctx, newfiles)
+
+    ds.write(tr)