prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  dvc: .dvcignore trouble with nfs mounted directory
  I have a large NFS mounted in a directory that I would like dvc to ignore. 
  
  Directory Structure:
  ```
  directory
  |___nfs
  |___...
  |___.dvc
  |___.dvcignore
  ```
  My *.dvcignore* has the following line:
  `/nfs/` (I've tried `nfs/` and `nfs/*`)
  
  The problem is that when I run `dvc status` or `dvc pull` the processes will just hang:
  ```
  DEBUG: PRAGMA user_version;
  DEBUG: fetched: [(3,)]
  DEBUG: CREATE TABLE IF NOT EXISTS state (inode INTEGER PRIMARY KEY, mtime TEXT NOT NULL, size TEXT NOT NULL, md5 TEXT NOT NULL, timestamp TEXT NOT NULL)
  DEBUG: CREATE TABLE IF NOT EXISTS state_info (count INTEGER)
  DEBUG: CREATE TABLE IF NOT EXISTS link_state (path TEXT PRIMARY KEY, inode INTEGER NOT NULL, mtime TEXT NOT NULL)
  DEBUG: INSERT OR IGNORE INTO state_info (count) SELECT 0 WHERE NOT EXISTS (SELECT * FROM state_info)
  DEBUG: PRAGMA user_version = 3; 
  ```
  
  Here is the traceback from `KeyboardInterrupt`:
  ```
     File "/home/ec2-user/app/proc/.env/lib/python3.7/site-packages/dvc/repo/__init__.py", line 499, in dvcignore
      return DvcIgnoreFilter(self.root_dir)
    File "/home/ec2-user/app/proc/.env/lib/python3.7/site-packages/dvc/ignore.py", line 67, in __init__
      for root, dirs, _ in os.walk(root_dir):
    File "/home/ec2-user/app/proc/.env/lib64/python3.7/os.py", line 410, in walk
      yield from walk(new_path, topdown, onerror, followlinks)
    File "/home/ec2-user/app/proc/.env/lib64/python3.7/os.py", line 368, in walk
      is_dir = entry.is_dir() 
  ```
  Which makes me feel like the directory is not being ignored.
  
  ***Additonal***
  I've unmounted the NFS directory and ran `dvc status` with no problem so I believe the issue stems from dvc trying to traverse it.
  
  System Information:
  ``` 
  DVC version: 0.66.6
  Python version: 3.7.4
  Platform: Linux 4.14.109-99.92.amzn2.x86_64
  Installation: pip
  ```
  
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `dvc/ignore.py`
  Content:
  ```
  1 from __future__ import unicode_literals
  2 
  3 import logging
  4 import os
  5 
  6 from pathspec import PathSpec
  7 from pathspec.patterns import GitWildMatchPattern
  8 
  9 from dvc.utils import relpath
  10 from dvc.utils.compat import open
  11 
  12 logger = logging.getLogger(__name__)
  13 
  14 
  15 class DvcIgnore(object):
  16     DVCIGNORE_FILE = ".dvcignore"
  17 
  18     def __call__(self, root, dirs, files):
  19         raise NotImplementedError
  20 
  21 
  22 class DvcIgnorePatterns(DvcIgnore):
  23     def __init__(self, ignore_file_path):
  24         assert os.path.isabs(ignore_file_path)
  25 
  26         self.ignore_file_path = ignore_file_path
  27         self.dirname = os.path.normpath(os.path.dirname(ignore_file_path))
  28 
  29         with open(ignore_file_path, encoding="utf-8") as fobj:
  30             self.ignore_spec = PathSpec.from_lines(GitWildMatchPattern, fobj)
  31 
  32     def __call__(self, root, dirs, files):
  33         files = [f for f in files if not self.matches(root, f)]
  34         dirs = [d for d in dirs if not self.matches(root, d)]
  35 
  36         return dirs, files
  37 
  38     def matches(self, dirname, basename):
  39         abs_path = os.path.join(dirname, basename)
  40         rel_path = relpath(abs_path, self.dirname)
  41 
  42         if os.pardir + os.sep in rel_path:
  43             return False
  44         return self.ignore_spec.match_file(rel_path)
  45 
  46     def __hash__(self):
  47         return hash(self.ignore_file_path)
  48 
  49     def __eq__(self, other):
  50         return self.ignore_file_path == other.ignore_file_path
  51 
  52 
  53 class DvcIgnoreDirs(DvcIgnore):
  54     def __init__(self, basenames):
  55         self.basenames = set(basenames)
  56 
  57     def __call__(self, root, dirs, files):
  58         dirs = [d for d in dirs if d not in self.basenames]
  59 
  60         return dirs, files
  61 
  62 
  63 class DvcIgnoreFilter(object):
  64     def __init__(self, root_dir):
  65         self.ignores = {DvcIgnoreDirs([".git", ".hg", ".dvc"])}
  66         self._update(root_dir)
  67         for root, dirs, _ in os.walk(root_dir):
  68             for d in dirs:
  69                 self._update(os.path.join(root, d))
  70 
  71     def _update(self, dirname):
  72         ignore_file_path = os.path.join(dirname, DvcIgnore.DVCIGNORE_FILE)
  73         if os.path.exists(ignore_file_path):
  74             self.ignores.add(DvcIgnorePatterns(ignore_file_path))
  75 
  76     def __call__(self, root, dirs, files):
  77         for ignore in self.ignores:
  78             dirs, files = ignore(root, dirs, files)
  79 
  80         return dirs, files
  81
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/dvc/ignore.py b/dvc/ignore.py
  --- a/dvc/ignore.py
  +++ b/dvc/ignore.py
  @@ -6,6 +6,7 @@
   from pathspec import PathSpec
   from pathspec.patterns import GitWildMatchPattern
   
  +from dvc.utils import dvc_walk
   from dvc.utils import relpath
   from dvc.utils.compat import open
   
  @@ -47,6 +48,9 @@
           return hash(self.ignore_file_path)
   
       def __eq__(self, other):
  +        if not isinstance(other, DvcIgnorePatterns):
  +            return NotImplemented
  +
           return self.ignore_file_path == other.ignore_file_path
   
   
  @@ -59,12 +63,21 @@
   
           return dirs, files
   
  +    def __hash__(self):
  +        return hash(tuple(self.basenames))
  +
  +    def __eq__(self, other):
  +        if not isinstance(other, DvcIgnoreDirs):
  +            return NotImplemented
  +
  +        return self.basenames == other.basenames
  +
   
   class DvcIgnoreFilter(object):
       def __init__(self, root_dir):
           self.ignores = {DvcIgnoreDirs([".git", ".hg", ".dvc"])}
           self._update(root_dir)
  -        for root, dirs, _ in os.walk(root_dir):
  +        for root, dirs, _ in dvc_walk(root_dir, self):
               for d in dirs:
                   self._update(os.path.join(root, d))
  
verification_info: {"golden_diff": "diff --git a/dvc/ignore.py b/dvc/ignore.py\n--- a/dvc/ignore.py\n+++ b/dvc/ignore.py\n@@ -6,6 +6,7 @@\n from pathspec import PathSpec\n from pathspec.patterns import GitWildMatchPattern\n \n+from dvc.utils import dvc_walk\n from dvc.utils import relpath\n from dvc.utils.compat import open\n \n@@ -47,6 +48,9 @@\n         return hash(self.ignore_file_path)\n \n     def __eq__(self, other):\n+        if not isinstance(other, DvcIgnorePatterns):\n+            return NotImplemented\n+\n         return self.ignore_file_path == other.ignore_file_path\n \n \n@@ -59,12 +63,21 @@\n \n         return dirs, files\n \n+    def __hash__(self):\n+        return hash(tuple(self.basenames))\n+\n+    def __eq__(self, other):\n+        if not isinstance(other, DvcIgnoreDirs):\n+            return NotImplemented\n+\n+        return self.basenames == other.basenames\n+\n \n class DvcIgnoreFilter(object):\n     def __init__(self, root_dir):\n         self.ignores = {DvcIgnoreDirs([\".git\", \".hg\", \".dvc\"])}\n         self._update(root_dir)\n-        for root, dirs, _ in os.walk(root_dir):\n+        for root, dirs, _ in dvc_walk(root_dir, self):\n             for d in dirs:\n                 self._update(os.path.join(root, d))\n", "issue": "dvc: .dvcignore trouble with nfs mounted directory\nI have a large NFS mounted in a directory that I would like dvc to ignore. \r\n\r\nDirectory Structure:\r\n```\r\ndirectory\r\n|___nfs\r\n|___...\r\n|___.dvc\r\n|___.dvcignore\r\n```\r\nMy *.dvcignore* has the following line:\r\n`/nfs/` (I've tried `nfs/` and `nfs/*`)\r\n\r\nThe problem is that when I run `dvc status` or `dvc pull` the processes will just hang:\r\n```\r\nDEBUG: PRAGMA user_version;\r\nDEBUG: fetched: [(3,)]\r\nDEBUG: CREATE TABLE IF NOT EXISTS state (inode INTEGER PRIMARY KEY, mtime TEXT NOT NULL, size TEXT NOT NULL, md5 TEXT NOT NULL, timestamp TEXT NOT NULL)\r\nDEBUG: CREATE TABLE IF NOT EXISTS state_info (count INTEGER)\r\nDEBUG: CREATE TABLE IF NOT EXISTS link_state (path TEXT PRIMARY KEY, inode INTEGER NOT NULL, mtime TEXT NOT NULL)\r\nDEBUG: INSERT OR IGNORE INTO state_info (count) SELECT 0 WHERE NOT EXISTS (SELECT * FROM state_info)\r\nDEBUG: PRAGMA user_version = 3; \r\n```\r\n\r\nHere is the traceback from `KeyboardInterrupt`:\r\n```\r\n   File \"/home/ec2-user/app/proc/.env/lib/python3.7/site-packages/dvc/repo/__init__.py\", line 499, in dvcignore\r\n    return DvcIgnoreFilter(self.root_dir)\r\n  File \"/home/ec2-user/app/proc/.env/lib/python3.7/site-packages/dvc/ignore.py\", line 67, in __init__\r\n    for root, dirs, _ in os.walk(root_dir):\r\n  File \"/home/ec2-user/app/proc/.env/lib64/python3.7/os.py\", line 410, in walk\r\n    yield from walk(new_path, topdown, onerror, followlinks)\r\n  File \"/home/ec2-user/app/proc/.env/lib64/python3.7/os.py\", line 368, in walk\r\n    is_dir = entry.is_dir() \r\n```\r\nWhich makes me feel like the directory is not being ignored.\r\n\r\n***Additonal***\r\nI've unmounted the NFS directory and ran `dvc status` with no problem so I believe the issue stems from dvc trying to traverse it.\r\n\r\nSystem Information:\r\n``` \r\nDVC version: 0.66.6\r\nPython version: 3.7.4\r\nPlatform: Linux 4.14.109-99.92.amzn2.x86_64\r\nInstallation: pip\r\n```\r\n\r\n\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport os\n\nfrom pathspec import PathSpec\nfrom pathspec.patterns import GitWildMatchPattern\n\nfrom dvc.utils import relpath\nfrom dvc.utils.compat import open\n\nlogger = logging.getLogger(__name__)\n\n\nclass DvcIgnore(object):\n    DVCIGNORE_FILE = \".dvcignore\"\n\n    def __call__(self, root, dirs, files):\n        raise NotImplementedError\n\n\nclass DvcIgnorePatterns(DvcIgnore):\n    def __init__(self, ignore_file_path):\n        assert os.path.isabs(ignore_file_path)\n\n        self.ignore_file_path = ignore_file_path\n        self.dirname = os.path.normpath(os.path.dirname(ignore_file_path))\n\n        with open(ignore_file_path, encoding=\"utf-8\") as fobj:\n            self.ignore_spec = PathSpec.from_lines(GitWildMatchPattern, fobj)\n\n    def __call__(self, root, dirs, files):\n        files = [f for f in files if not self.matches(root, f)]\n        dirs = [d for d in dirs if not self.matches(root, d)]\n\n        return dirs, files\n\n    def matches(self, dirname, basename):\n        abs_path = os.path.join(dirname, basename)\n        rel_path = relpath(abs_path, self.dirname)\n\n        if os.pardir + os.sep in rel_path:\n            return False\n        return self.ignore_spec.match_file(rel_path)\n\n    def __hash__(self):\n        return hash(self.ignore_file_path)\n\n    def __eq__(self, other):\n        return self.ignore_file_path == other.ignore_file_path\n\n\nclass DvcIgnoreDirs(DvcIgnore):\n    def __init__(self, basenames):\n        self.basenames = set(basenames)\n\n    def __call__(self, root, dirs, files):\n        dirs = [d for d in dirs if d not in self.basenames]\n\n        return dirs, files\n\n\nclass DvcIgnoreFilter(object):\n    def __init__(self, root_dir):\n        self.ignores = {DvcIgnoreDirs([\".git\", \".hg\", \".dvc\"])}\n        self._update(root_dir)\n        for root, dirs, _ in os.walk(root_dir):\n            for d in dirs:\n                self._update(os.path.join(root, d))\n\n    def _update(self, dirname):\n        ignore_file_path = os.path.join(dirname, DvcIgnore.DVCIGNORE_FILE)\n        if os.path.exists(ignore_file_path):\n            self.ignores.add(DvcIgnorePatterns(ignore_file_path))\n\n    def __call__(self, root, dirs, files):\n        for ignore in self.ignores:\n            dirs, files = ignore(root, dirs, files)\n\n        return dirs, files\n", "path": "dvc/ignore.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport os\n\nfrom pathspec import PathSpec\nfrom pathspec.patterns import GitWildMatchPattern\n\nfrom dvc.utils import dvc_walk\nfrom dvc.utils import relpath\nfrom dvc.utils.compat import open\n\nlogger = logging.getLogger(__name__)\n\n\nclass DvcIgnore(object):\n    DVCIGNORE_FILE = \".dvcignore\"\n\n    def __call__(self, root, dirs, files):\n        raise NotImplementedError\n\n\nclass DvcIgnorePatterns(DvcIgnore):\n    def __init__(self, ignore_file_path):\n        assert os.path.isabs(ignore_file_path)\n\n        self.ignore_file_path = ignore_file_path\n        self.dirname = os.path.normpath(os.path.dirname(ignore_file_path))\n\n        with open(ignore_file_path, encoding=\"utf-8\") as fobj:\n            self.ignore_spec = PathSpec.from_lines(GitWildMatchPattern, fobj)\n\n    def __call__(self, root, dirs, files):\n        files = [f for f in files if not self.matches(root, f)]\n        dirs = [d for d in dirs if not self.matches(root, d)]\n\n        return dirs, files\n\n    def matches(self, dirname, basename):\n        abs_path = os.path.join(dirname, basename)\n        rel_path = relpath(abs_path, self.dirname)\n\n        if os.pardir + os.sep in rel_path:\n            return False\n        return self.ignore_spec.match_file(rel_path)\n\n    def __hash__(self):\n        return hash(self.ignore_file_path)\n\n    def __eq__(self, other):\n        if not isinstance(other, DvcIgnorePatterns):\n            return NotImplemented\n\n        return self.ignore_file_path == other.ignore_file_path\n\n\nclass DvcIgnoreDirs(DvcIgnore):\n    def __init__(self, basenames):\n        self.basenames = set(basenames)\n\n    def __call__(self, root, dirs, files):\n        dirs = [d for d in dirs if d not in self.basenames]\n\n        return dirs, files\n\n    def __hash__(self):\n        return hash(tuple(self.basenames))\n\n    def __eq__(self, other):\n        if not isinstance(other, DvcIgnoreDirs):\n            return NotImplemented\n\n        return self.basenames == other.basenames\n\n\nclass DvcIgnoreFilter(object):\n    def __init__(self, root_dir):\n        self.ignores = {DvcIgnoreDirs([\".git\", \".hg\", \".dvc\"])}\n        self._update(root_dir)\n        for root, dirs, _ in dvc_walk(root_dir, self):\n            for d in dirs:\n                self._update(os.path.join(root, d))\n\n    def _update(self, dirname):\n        ignore_file_path = os.path.join(dirname, DvcIgnore.DVCIGNORE_FILE)\n        if os.path.exists(ignore_file_path):\n            self.ignores.add(DvcIgnorePatterns(ignore_file_path))\n\n    def __call__(self, root, dirs, files):\n        for ignore in self.ignores:\n            dirs, files = ignore(root, dirs, files)\n\n        return dirs, files\n", "path": "dvc/ignore.py"}]}