D5278: narrow: detect if narrowspec was changed in a different share

martinvonz (Martin von Zweigbergk) phabricator at mercurial-scm.org
Thu Nov 15 01:13:24 UTC 2018


martinvonz created this revision.
Herald added a reviewer: durin42.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  With this commit, `hg share` should be usable with narrow repos.
  
  Design explained on https://www.mercurial-scm.org/wiki/NarrowSharePlan

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D5278

AFFECTED FILES
  mercurial/localrepo.py
  mercurial/narrowspec.py
  tests/test-narrow-share.t

CHANGE DETAILS

diff --git a/tests/test-narrow-share.t b/tests/test-narrow-share.t
--- a/tests/test-narrow-share.t
+++ b/tests/test-narrow-share.t
@@ -77,11 +77,13 @@
   I path:d7
 # d1/f, d3/f, d3/g and d5/f should no longer be reported
   $ hg -R main files
+  not deleting possibly dirty file d3/f
+  not deleting possibly dirty file d3/g
+  not deleting possibly dirty file d5/f
   main/d7/f
 # d1/f should no longer be there, d3/f should be since it was dirty, d3/g should be there since
 # it was added, and d5/f should be since we couldn't be sure it was clean
   $ find main/d* -type f
-  main/d1/f
   main/d3/g
   main/d3/f
   main/d5/f
@@ -105,17 +107,44 @@
   $ hg -R main files
   main/d1/f
   main/d3/f
-  main/d3/g
   main/d7/f
 # d1/f, d3/f, d3/g should be back
   $ hg -R main files
   main/d1/f
   main/d3/f
-  main/d3/g
   main/d7/f
 # d3/f should be modified (not clobbered by the widening), and d3/g should be untracked
   $ hg -R main st --all
   M d3/f
-  A d3/g
+  ? d3/g
   C d1/f
   C d7/f
+
+Check that failure to get the lock is not a problem
+
+#if unix-permissions system-sh
+  $ cat >> sleep10 << EOF
+  > sleep 10
+  > EOF
+  $ chmod +x sleep10
+  $ hg -R main --config hooks.update="$TESTTMP/sleep10" up 9 -C > /dev/null 2>&1 &
+  $ echo $! > pid
+  $ hg -R share1 tracked --removeinclude d7 -q
+# d7/f is no longer reported
+  $ hg -R main files
+  main/d1/f
+  main/d3/f
+# but d7/f is still in the dirstate because we failed to update it earlier
+  $ hg -R main debugdirstate --no-dates
+  n 644          2 *               d1/f (glob)
+  n 644          2 *               d3/f (glob)
+  n 644          2 *               d7/f (glob)
+  $ kill $(cat pid)
+# Now the update should work
+  $ hg -R main st
+  ? d3/g
+  $ hg -R main debugdirstate --no-dates
+  n 644          2 *               d1/f (glob)
+  n 644          2 *               d3/f (glob)
+
+#endif
diff --git a/mercurial/narrowspec.py b/mercurial/narrowspec.py
--- a/mercurial/narrowspec.py
+++ b/mercurial/narrowspec.py
@@ -18,7 +18,10 @@
     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
@@ -226,3 +229,77 @@
     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):
+    for f in files:
+        if not repo.wvfs.exists(f):
+            repo.wvfs.write(f, pctx[f].data())
+
+def updatesharenarrowspec(repo):
+    # Check if we have already compared the working copy's narrowspec to the
+    # store's narrowspec
+    if getattr(repo, '_sharenarrowspecchecked', False):
+        return
+    repo._sharenarrowspecchecked = True
+
+    if repository.NARROW_REQUIREMENT not in repo.requirements:
+        return
+
+    oldspec = repo.vfs.tryread(DIRSTATE_FILENAME)
+    newspec = repo.svfs.tryread(FILENAME)
+    if not oldspec:
+        # There was no narrowspec for the working copy, only one for the
+        # store. That should only happen on repos created before we had
+        # support for narrow+share. Assume the dirstate already matches
+        # the store's narrowspec and write the store's narrowspec to
+        # the working copy narrowspec.
+        try:
+            with repo.wlock(wait=False):
+                repo.vfs.write(DIRSTATE_FILENAME, newspec)
+        except error.LockError:
+            pass
+        return
+    elif newspec == oldspec:
+        return
+
+    try:
+        wlock = repo.wlock(False)
+    except error.LockError:
+        # We cannot update the dirstate and .hg/narrowspec.dirstate this
+        # time. That's fine, we'll try again on the next hg invocation.
+        return
+    with wlock:
+        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)
+
+        pctx = repo['.']
+        newfiles = list(pctx.manifest().walk(addedmatch))
+        # TODO: should probably use the merge.update() code here
+        # so the file-writing is parallelized
+        for f in newfiles:
+            if f not in ds:
+                ds.normallookup(f)
+        _writeaddedfiles(repo, pctx, newfiles)
+
+        ds.write(repo.currenttransaction())
+        repo.vfs.write(DIRSTATE_FILENAME, newspec)
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1227,6 +1227,8 @@
         If `includeexact` is True, then any exact matches from `match` will
         be included even if they're outside the narrowspec.
         """
+        narrowspec.updatesharenarrowspec(self)
+
         if match:
             if includeexact and not self._narrowmatch.always():
                 # do not exclude explicitly-specified paths so that they can



To: martinvonz, durin42, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list