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

login
register
mail settings
Submitter phabricator
Date Nov. 15, 2018, 5:32 a.m.
Message ID <011d1833d988edb14c7bd9c5ec902a4a@localhost.localdomain>
Download mbox | patch
Permalink /patch/36590/
State Not Applicable
Headers show

Comments

phabricator - Nov. 15, 2018, 5:32 a.m.
martinvonz updated this revision to Diff 12548.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D5278?vs=12547&id=12548

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

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

CHANGE DETAILS




To: martinvonz, durin42, #hg-reviewers
Cc: mercurial-devel
Yuya Nishihara - Nov. 17, 2018, 7:24 a.m.
>  # 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

I don't think it's good idea that any read-only commands can update working-copy
files. And I suspect the narrowing/widening process could get complicated if
merge/rebase were underway.

Instead, can't we take the repository as dirty/unfinished state if local
narrowspec differs from store's? The user will have to run some command to
narrow/widen the working directory. Maybe he can also restore narrowspec of
the store to finish ongoing merge/rebase.
phabricator - Nov. 17, 2018, 7:25 a.m.
yuja added a comment.


  > 1. 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
  
  I don't think it's good idea that any read-only commands can update working-copy
  files. And I suspect the narrowing/widening process could get complicated if
  merge/rebase were underway.
  
  Instead, can't we take the repository as dirty/unfinished state if local
  narrowspec differs from store's? The user will have to run some command to
  narrow/widen the working directory. Maybe he can also restore narrowspec of
  the store to finish ongoing merge/rebase.

REPOSITORY
  rHG Mercurial

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

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

Patch

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 $! > $DAEMON_PIDS
+  $ 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)
+  $ killdaemons.py
+# 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,75 @@ 
     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. This prevents infinite recursion with manifestlog
+    # creation.
+    if getattr(repo, '_sharenarrowspecchecked', False):
+        return
+    repo._sharenarrowspecchecked = True
+
+    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
@@ -1215,6 +1215,7 @@ 
     def _narrowmatch(self):
         if repository.NARROW_REQUIREMENT not in self.requirements:
             return matchmod.always(self.root, '')
+        narrowspec.updatesharenarrowspec(self)
         include, exclude = self.narrowpats
         return narrowspec.match(self.root, include=include, exclude=exclude)