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

login
register
mail settings
Submitter phabricator
Date Dec. 21, 2018, 6:12 p.m.
Message ID <e163faf50c32a575f8add3717dd00a3f@localhost.localdomain>
Download mbox | patch
Permalink /patch/37306/
State Not Applicable
Headers show

Comments

phabricator - Dec. 21, 2018, 6:12 p.m.
martinvonz updated this revision to Diff 12944.

REPOSITORY
  rHG Mercurial

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

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

AFFECTED FILES
  hgext/narrow/TODO.rst
  hgext/narrow/narrowcommands.py
  mercurial/hg.py
  mercurial/localrepo.py
  mercurial/narrowspec.py
  tests/test-narrow-debugcommands.t
  tests/test-narrow-share.t
  tests/test-narrow-trackedcmd.t

CHANGE DETAILS




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

Patch

diff --git a/tests/test-narrow-trackedcmd.t b/tests/test-narrow-trackedcmd.t
--- a/tests/test-narrow-trackedcmd.t
+++ b/tests/test-narrow-trackedcmd.t
@@ -107,6 +107,8 @@ 
       --clear                      whether to replace the existing narrowspec
       --force-delete-local-changes forces deletion of local changes when
                                    narrowing
+      --update-working-copy        update working copy when the store has
+                                   changed
    -e --ssh CMD                    specify ssh command to use
       --remotecmd CMD              specify hg command to run on the remote side
       --insecure                   do not verify server certificate (ignoring
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
@@ -75,13 +75,20 @@ 
   deleting meta/d5/00manifest.i (tree !)
   $ hg -R main tracked
   I path:d7
+  $ hg -R main files
+  abort: working copy's narrowspec is stale
+  (run 'hg tracked --update-working-copy')
+  [255]
+  $ hg -R main tracked --update-working-copy
+  not deleting possibly dirty file d3/f
+  not deleting possibly dirty file d3/g
+  not deleting possibly dirty file d5/f
 # d1/f, d3/f, d3/g and d5/f should no longer be reported
   $ hg -R main files
   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
@@ -102,16 +109,20 @@ 
   I path:d1
   I path:d3
   I path:d7
+  $ hg -R main files
+  abort: working copy's narrowspec is stale
+  (run 'hg tracked --update-working-copy')
+  [255]
+  $ hg -R main tracked --update-working-copy
 # d1/f, d3/f 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
 
@@ -130,3 +141,30 @@ 
   checking files
   checked 11 changesets with 3 changes to 3 files
   $ cd ..
+
+Dirstate should be left alone when upgrading from version of hg that didn't support narrow+share
+
+  $ hg share main share-upgrade
+  updating working directory
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd share-upgrade
+  $ echo x >> d1/f
+  $ echo y >> d3/g
+  $ hg add d3/g
+  $ hg rm d7/f
+  $ hg st
+  M d1/f
+  A d3/g
+  R d7/f
+Make it look like a repo from before narrow+share was supported
+  $ rm .hg/narrowspec.dirstate
+  $ hg st
+  abort: working copy's narrowspec is stale
+  (run 'hg tracked --update-working-copy')
+  [255]
+  $ hg tracked --update-working-copy
+  $ hg st
+  M d1/f
+  A d3/g
+  R d7/f
+  $ cd ..
diff --git a/tests/test-narrow-debugcommands.t b/tests/test-narrow-debugcommands.t
--- a/tests/test-narrow-debugcommands.t
+++ b/tests/test-narrow-debugcommands.t
@@ -6,6 +6,7 @@ 
   > path:foo
   > [exclude]
   > EOF
+  $ cp .hg/store/narrowspec .hg/narrowspec.dirstate
   $ echo treemanifest >> .hg/requires
   $ echo narrowhg-experimental >> .hg/requires
   $ mkdir -p foo/bar
diff --git a/mercurial/narrowspec.py b/mercurial/narrowspec.py
--- a/mercurial/narrowspec.py
+++ b/mercurial/narrowspec.py
@@ -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,59 @@ 
     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):
+    with repo.wlock(), repo.lock(), repo.transaction('wc-narrowspec') as 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)
+        copytoworkingcopy(repo, tr)
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1227,6 +1227,7 @@ 
     def _narrowmatch(self):
         if repository.NARROW_REQUIREMENT not in self.requirements:
             return matchmod.always(self.root, '')
+        narrowspec.checkworkingcopynarrowspec(self)
         include, exclude = self.narrowpats
         return narrowspec.match(self.root, include=include, exclude=exclude)
 
@@ -1251,7 +1252,14 @@ 
 
     def setnarrowpats(self, newincludes, newexcludes):
         narrowspec.save(self, newincludes, newexcludes)
+        narrowspec.copytoworkingcopy(self, self.currenttransaction())
         self.invalidate(clearfilecache=True)
+        # So the next access won't be considered a conflict
+        # TODO: It seems like there should be a way of doing this that
+        # doesn't involve replacing these attributes.
+        self.narrowpats = newincludes, newexcludes
+        self._narrowmatch = narrowspec.match(self.root, include=newincludes,
+                                             exclude=newexcludes)
 
     def __getitem__(self, changeid):
         if changeid is None:
@@ -1833,6 +1841,7 @@ 
 
                 repo.invalidate(clearfilecache=True)
 
+
         tr = transaction.transaction(rp, self.svfs, vfsmap,
                                      "journal",
                                      "undo",
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -38,6 +38,7 @@ 
     narrowspec,
     node,
     phases,
+    repository as repositorymod,
     scmutil,
     sshpeer,
     statichttprepo,
@@ -331,6 +332,9 @@ 
         template = ('[paths]\n'
                     'default = %s\n')
         destrepo.vfs.write('hgrc', util.tonativeeol(template % default))
+    if repositorymod.NARROW_REQUIREMENT in sourcerepo.requirements:
+        with destrepo.wlock():
+            narrowspec.copytoworkingcopy(destrepo, None)
 
 def _postshareupdate(repo, update, checkout=None):
     """Maybe perform a working directory update after a shared repo is created.
@@ -731,7 +735,7 @@ 
             local = destpeer.local()
             if local:
                 if narrow:
-                    with local.lock():
+                    with local.wlock(), local.lock():
                         local.setnarrowpats(storeincludepats, storeexcludepats)
 
                 u = util.url(abspath)
diff --git a/hgext/narrow/narrowcommands.py b/hgext/narrow/narrowcommands.py
--- a/hgext/narrow/narrowcommands.py
+++ b/hgext/narrow/narrowcommands.py
@@ -339,6 +339,8 @@ 
      ('', 'clear', False, _('whether to replace the existing narrowspec')),
      ('', 'force-delete-local-changes', False,
        _('forces deletion of local changes when narrowing')),
+     ('', 'update-working-copy', False,
+      _('update working copy when the store has changed')),
     ] + commands.remoteopts,
     _('[OPTIONS]... [REMOTE]'),
     inferrepo=True)
@@ -398,8 +400,9 @@ 
     addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
     removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
 
+    update_working_copy = opts['update_working_copy']
     only_show = not (addedincludes or removedincludes or addedexcludes or
-                     removedexcludes or newrules)
+                     removedexcludes or newrules or update_working_copy)
 
     oldincludes, oldexcludes = repo.narrowpats
 
@@ -428,6 +431,10 @@ 
         fm.end()
         return 0
 
+    if update_working_copy:
+        narrowspec.updateworkingcopy(repo)
+        return 0
+
     if not widening and not narrowing:
         ui.status(_("nothing to widen or narrow\n"))
         return 0
diff --git a/hgext/narrow/TODO.rst b/hgext/narrow/TODO.rst
--- a/hgext/narrow/TODO.rst
+++ b/hgext/narrow/TODO.rst
@@ -1,6 +1,3 @@ 
-Integration with the share extension needs improvement. Right now
-we've seen some odd bugs.
-
 Address commentary in manifest.excludedmanifestrevlog.add -
 specifically we should improve the collaboration with core so that
 add() never gets called on an excluded directory and we can improve