prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  .dockerignore does not work with patterns begin with slash
  docker version:
  
  ```
  docker -v
  Docker version 17.03.1-ce, build c6d412e
  ```
  reproduce:
  
  ```
  mkdir app
  cd app
  mkdir foo
  touch foo/bar
  echo '/foo/bar' > .dockerignore
  printf 'FROM alpine:3.1\nWORKDIR /app\nCOPY . .\n' > Dockerfile
  docker build -t app .
  docker run --rm app find foo
  ```
  
  output:
  
  ```
  foo
  foo/bar
  ```
  
  It seems the statement  from [the official document](https://docs.docker.com/engine/reference/builder/#dockerignore-file) below is not correct:
  
  > For example, the patterns `/foo/bar` and `foo/bar` both exclude a file or directory named `bar` in the `foo` subdirectory of `PATH` or in the root of the git repository located at `URL`.
  
  We should either amend the document or fix the bug.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `docker/utils/build.py`
  Content:
  ```
  1 import os
  2 
  3 from ..constants import IS_WINDOWS_PLATFORM
  4 from .fnmatch import fnmatch
  5 from .utils import create_archive
  6 
  7 
  8 def tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False):
  9     root = os.path.abspath(path)
  10     exclude = exclude or []
  11 
  12     return create_archive(
  13         files=sorted(exclude_paths(root, exclude, dockerfile=dockerfile)),
  14         root=root, fileobj=fileobj, gzip=gzip
  15     )
  16 
  17 
  18 def exclude_paths(root, patterns, dockerfile=None):
  19     """
  20     Given a root directory path and a list of .dockerignore patterns, return
  21     an iterator of all paths (both regular files and directories) in the root
  22     directory that do *not* match any of the patterns.
  23 
  24     All paths returned are relative to the root.
  25     """
  26     if dockerfile is None:
  27         dockerfile = 'Dockerfile'
  28 
  29     exceptions = [p for p in patterns if p.startswith('!')]
  30 
  31     include_patterns = [p[1:] for p in exceptions]
  32     include_patterns += [dockerfile, '.dockerignore']
  33 
  34     exclude_patterns = list(set(patterns) - set(exceptions))
  35 
  36     paths = get_paths(root, exclude_patterns, include_patterns,
  37                       has_exceptions=len(exceptions) > 0)
  38 
  39     return set(paths).union(
  40         # If the Dockerfile is in a subdirectory that is excluded, get_paths
  41         # will not descend into it and the file will be skipped. This ensures
  42         # it doesn't happen.
  43         set([dockerfile.replace('/', os.path.sep)])
  44         if os.path.exists(os.path.join(root, dockerfile)) else set()
  45     )
  46 
  47 
  48 def should_include(path, exclude_patterns, include_patterns):
  49     """
  50     Given a path, a list of exclude patterns, and a list of inclusion patterns:
  51 
  52     1. Returns True if the path doesn't match any exclusion pattern
  53     2. Returns False if the path matches an exclusion pattern and doesn't match
  54        an inclusion pattern
  55     3. Returns true if the path matches an exclusion pattern and matches an
  56        inclusion pattern
  57     """
  58     for pattern in exclude_patterns:
  59         if match_path(path, pattern):
  60             for pattern in include_patterns:
  61                 if match_path(path, pattern):
  62                     return True
  63             return False
  64     return True
  65 
  66 
  67 def should_check_directory(directory_path, exclude_patterns, include_patterns):
  68     """
  69     Given a directory path, a list of exclude patterns, and a list of inclusion
  70     patterns:
  71 
  72     1. Returns True if the directory path should be included according to
  73        should_include.
  74     2. Returns True if the directory path is the prefix for an inclusion
  75        pattern
  76     3. Returns False otherwise
  77     """
  78 
  79     # To account for exception rules, check directories if their path is a
  80     # a prefix to an inclusion pattern. This logic conforms with the current
  81     # docker logic (2016-10-27):
  82     # https://github.com/docker/docker/blob/bc52939b0455116ab8e0da67869ec81c1a1c3e2c/pkg/archive/archive.go#L640-L671
  83 
  84     def normalize_path(path):
  85         return path.replace(os.path.sep, '/')
  86 
  87     path_with_slash = normalize_path(directory_path) + '/'
  88     possible_child_patterns = [
  89         pattern for pattern in map(normalize_path, include_patterns)
  90         if (pattern + '/').startswith(path_with_slash)
  91     ]
  92     directory_included = should_include(
  93         directory_path, exclude_patterns, include_patterns
  94     )
  95     return directory_included or len(possible_child_patterns) > 0
  96 
  97 
  98 def get_paths(root, exclude_patterns, include_patterns, has_exceptions=False):
  99     paths = []
  100 
  101     for parent, dirs, files in os.walk(root, topdown=True, followlinks=False):
  102         parent = os.path.relpath(parent, root)
  103         if parent == '.':
  104             parent = ''
  105 
  106         # Remove excluded patterns from the list of directories to traverse
  107         # by mutating the dirs we're iterating over.
  108         # This looks strange, but is considered the correct way to skip
  109         # traversal. See https://docs.python.org/2/library/os.html#os.walk
  110         dirs[:] = [
  111             d for d in dirs if should_check_directory(
  112                 os.path.join(parent, d), exclude_patterns, include_patterns
  113             )
  114         ]
  115 
  116         for path in dirs:
  117             if should_include(os.path.join(parent, path),
  118                               exclude_patterns, include_patterns):
  119                 paths.append(os.path.join(parent, path))
  120 
  121         for path in files:
  122             if should_include(os.path.join(parent, path),
  123                               exclude_patterns, include_patterns):
  124                 paths.append(os.path.join(parent, path))
  125 
  126     return paths
  127 
  128 
  129 def match_path(path, pattern):
  130     pattern = pattern.rstrip('/' + os.path.sep)
  131     if pattern:
  132         pattern = os.path.relpath(pattern)
  133 
  134     pattern_components = pattern.split(os.path.sep)
  135     if len(pattern_components) == 1 and IS_WINDOWS_PLATFORM:
  136         pattern_components = pattern.split('/')
  137 
  138     if '**' not in pattern:
  139         path_components = path.split(os.path.sep)[:len(pattern_components)]
  140     else:
  141         path_components = path.split(os.path.sep)
  142     return fnmatch('/'.join(path_components), '/'.join(pattern_components))
  143
  ```
  --- 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/docker/utils/build.py b/docker/utils/build.py
  --- a/docker/utils/build.py
  +++ b/docker/utils/build.py
  @@ -26,6 +26,7 @@
       if dockerfile is None:
           dockerfile = 'Dockerfile'
   
  +    patterns = [p.lstrip('/') for p in patterns]
       exceptions = [p for p in patterns if p.startswith('!')]
   
       include_patterns = [p[1:] for p in exceptions]
  
verification_info: {"golden_diff": "diff --git a/docker/utils/build.py b/docker/utils/build.py\n--- a/docker/utils/build.py\n+++ b/docker/utils/build.py\n@@ -26,6 +26,7 @@\n     if dockerfile is None:\n         dockerfile = 'Dockerfile'\n \n+    patterns = [p.lstrip('/') for p in patterns]\n     exceptions = [p for p in patterns if p.startswith('!')]\n \n     include_patterns = [p[1:] for p in exceptions]\n", "issue": ".dockerignore does not work with patterns begin with slash\ndocker version:\r\n\r\n```\r\ndocker -v\r\nDocker version 17.03.1-ce, build c6d412e\r\n```\r\nreproduce:\r\n\r\n```\r\nmkdir app\r\ncd app\r\nmkdir foo\r\ntouch foo/bar\r\necho '/foo/bar' > .dockerignore\r\nprintf 'FROM alpine:3.1\\nWORKDIR /app\\nCOPY . .\\n' > Dockerfile\r\ndocker build -t app .\r\ndocker run --rm app find foo\r\n```\r\n\r\noutput:\r\n\r\n```\r\nfoo\r\nfoo/bar\r\n```\r\n\r\nIt seems the statement  from [the official document](https://docs.docker.com/engine/reference/builder/#dockerignore-file) below is not correct:\r\n\r\n> For example, the patterns `/foo/bar` and `foo/bar` both exclude a file or directory named `bar` in the `foo` subdirectory of `PATH` or in the root of the git repository located at `URL`.\r\n\r\nWe should either amend the document or fix the bug.\r\n\n", "before_files": [{"content": "import os\n\nfrom ..constants import IS_WINDOWS_PLATFORM\nfrom .fnmatch import fnmatch\nfrom .utils import create_archive\n\n\ndef tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False):\n    root = os.path.abspath(path)\n    exclude = exclude or []\n\n    return create_archive(\n        files=sorted(exclude_paths(root, exclude, dockerfile=dockerfile)),\n        root=root, fileobj=fileobj, gzip=gzip\n    )\n\n\ndef exclude_paths(root, patterns, dockerfile=None):\n    \"\"\"\n    Given a root directory path and a list of .dockerignore patterns, return\n    an iterator of all paths (both regular files and directories) in the root\n    directory that do *not* match any of the patterns.\n\n    All paths returned are relative to the root.\n    \"\"\"\n    if dockerfile is None:\n        dockerfile = 'Dockerfile'\n\n    exceptions = [p for p in patterns if p.startswith('!')]\n\n    include_patterns = [p[1:] for p in exceptions]\n    include_patterns += [dockerfile, '.dockerignore']\n\n    exclude_patterns = list(set(patterns) - set(exceptions))\n\n    paths = get_paths(root, exclude_patterns, include_patterns,\n                      has_exceptions=len(exceptions) > 0)\n\n    return set(paths).union(\n        # If the Dockerfile is in a subdirectory that is excluded, get_paths\n        # will not descend into it and the file will be skipped. This ensures\n        # it doesn't happen.\n        set([dockerfile.replace('/', os.path.sep)])\n        if os.path.exists(os.path.join(root, dockerfile)) else set()\n    )\n\n\ndef should_include(path, exclude_patterns, include_patterns):\n    \"\"\"\n    Given a path, a list of exclude patterns, and a list of inclusion patterns:\n\n    1. Returns True if the path doesn't match any exclusion pattern\n    2. Returns False if the path matches an exclusion pattern and doesn't match\n       an inclusion pattern\n    3. Returns true if the path matches an exclusion pattern and matches an\n       inclusion pattern\n    \"\"\"\n    for pattern in exclude_patterns:\n        if match_path(path, pattern):\n            for pattern in include_patterns:\n                if match_path(path, pattern):\n                    return True\n            return False\n    return True\n\n\ndef should_check_directory(directory_path, exclude_patterns, include_patterns):\n    \"\"\"\n    Given a directory path, a list of exclude patterns, and a list of inclusion\n    patterns:\n\n    1. Returns True if the directory path should be included according to\n       should_include.\n    2. Returns True if the directory path is the prefix for an inclusion\n       pattern\n    3. Returns False otherwise\n    \"\"\"\n\n    # To account for exception rules, check directories if their path is a\n    # a prefix to an inclusion pattern. This logic conforms with the current\n    # docker logic (2016-10-27):\n    # https://github.com/docker/docker/blob/bc52939b0455116ab8e0da67869ec81c1a1c3e2c/pkg/archive/archive.go#L640-L671\n\n    def normalize_path(path):\n        return path.replace(os.path.sep, '/')\n\n    path_with_slash = normalize_path(directory_path) + '/'\n    possible_child_patterns = [\n        pattern for pattern in map(normalize_path, include_patterns)\n        if (pattern + '/').startswith(path_with_slash)\n    ]\n    directory_included = should_include(\n        directory_path, exclude_patterns, include_patterns\n    )\n    return directory_included or len(possible_child_patterns) > 0\n\n\ndef get_paths(root, exclude_patterns, include_patterns, has_exceptions=False):\n    paths = []\n\n    for parent, dirs, files in os.walk(root, topdown=True, followlinks=False):\n        parent = os.path.relpath(parent, root)\n        if parent == '.':\n            parent = ''\n\n        # Remove excluded patterns from the list of directories to traverse\n        # by mutating the dirs we're iterating over.\n        # This looks strange, but is considered the correct way to skip\n        # traversal. See https://docs.python.org/2/library/os.html#os.walk\n        dirs[:] = [\n            d for d in dirs if should_check_directory(\n                os.path.join(parent, d), exclude_patterns, include_patterns\n            )\n        ]\n\n        for path in dirs:\n            if should_include(os.path.join(parent, path),\n                              exclude_patterns, include_patterns):\n                paths.append(os.path.join(parent, path))\n\n        for path in files:\n            if should_include(os.path.join(parent, path),\n                              exclude_patterns, include_patterns):\n                paths.append(os.path.join(parent, path))\n\n    return paths\n\n\ndef match_path(path, pattern):\n    pattern = pattern.rstrip('/' + os.path.sep)\n    if pattern:\n        pattern = os.path.relpath(pattern)\n\n    pattern_components = pattern.split(os.path.sep)\n    if len(pattern_components) == 1 and IS_WINDOWS_PLATFORM:\n        pattern_components = pattern.split('/')\n\n    if '**' not in pattern:\n        path_components = path.split(os.path.sep)[:len(pattern_components)]\n    else:\n        path_components = path.split(os.path.sep)\n    return fnmatch('/'.join(path_components), '/'.join(pattern_components))\n", "path": "docker/utils/build.py"}], "after_files": [{"content": "import os\n\nfrom ..constants import IS_WINDOWS_PLATFORM\nfrom .fnmatch import fnmatch\nfrom .utils import create_archive\n\n\ndef tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False):\n    root = os.path.abspath(path)\n    exclude = exclude or []\n\n    return create_archive(\n        files=sorted(exclude_paths(root, exclude, dockerfile=dockerfile)),\n        root=root, fileobj=fileobj, gzip=gzip\n    )\n\n\ndef exclude_paths(root, patterns, dockerfile=None):\n    \"\"\"\n    Given a root directory path and a list of .dockerignore patterns, return\n    an iterator of all paths (both regular files and directories) in the root\n    directory that do *not* match any of the patterns.\n\n    All paths returned are relative to the root.\n    \"\"\"\n    if dockerfile is None:\n        dockerfile = 'Dockerfile'\n\n    patterns = [p.lstrip('/') for p in patterns]\n    exceptions = [p for p in patterns if p.startswith('!')]\n\n    include_patterns = [p[1:] for p in exceptions]\n    include_patterns += [dockerfile, '.dockerignore']\n\n    exclude_patterns = list(set(patterns) - set(exceptions))\n\n    paths = get_paths(root, exclude_patterns, include_patterns,\n                      has_exceptions=len(exceptions) > 0)\n\n    return set(paths).union(\n        # If the Dockerfile is in a subdirectory that is excluded, get_paths\n        # will not descend into it and the file will be skipped. This ensures\n        # it doesn't happen.\n        set([dockerfile.replace('/', os.path.sep)])\n        if os.path.exists(os.path.join(root, dockerfile)) else set()\n    )\n\n\ndef should_include(path, exclude_patterns, include_patterns):\n    \"\"\"\n    Given a path, a list of exclude patterns, and a list of inclusion patterns:\n\n    1. Returns True if the path doesn't match any exclusion pattern\n    2. Returns False if the path matches an exclusion pattern and doesn't match\n       an inclusion pattern\n    3. Returns true if the path matches an exclusion pattern and matches an\n       inclusion pattern\n    \"\"\"\n    for pattern in exclude_patterns:\n        if match_path(path, pattern):\n            for pattern in include_patterns:\n                if match_path(path, pattern):\n                    return True\n            return False\n    return True\n\n\ndef should_check_directory(directory_path, exclude_patterns, include_patterns):\n    \"\"\"\n    Given a directory path, a list of exclude patterns, and a list of inclusion\n    patterns:\n\n    1. Returns True if the directory path should be included according to\n       should_include.\n    2. Returns True if the directory path is the prefix for an inclusion\n       pattern\n    3. Returns False otherwise\n    \"\"\"\n\n    # To account for exception rules, check directories if their path is a\n    # a prefix to an inclusion pattern. This logic conforms with the current\n    # docker logic (2016-10-27):\n    # https://github.com/docker/docker/blob/bc52939b0455116ab8e0da67869ec81c1a1c3e2c/pkg/archive/archive.go#L640-L671\n\n    def normalize_path(path):\n        return path.replace(os.path.sep, '/')\n\n    path_with_slash = normalize_path(directory_path) + '/'\n    possible_child_patterns = [\n        pattern for pattern in map(normalize_path, include_patterns)\n        if (pattern + '/').startswith(path_with_slash)\n    ]\n    directory_included = should_include(\n        directory_path, exclude_patterns, include_patterns\n    )\n    return directory_included or len(possible_child_patterns) > 0\n\n\ndef get_paths(root, exclude_patterns, include_patterns, has_exceptions=False):\n    paths = []\n\n    for parent, dirs, files in os.walk(root, topdown=True, followlinks=False):\n        parent = os.path.relpath(parent, root)\n        if parent == '.':\n            parent = ''\n\n        # Remove excluded patterns from the list of directories to traverse\n        # by mutating the dirs we're iterating over.\n        # This looks strange, but is considered the correct way to skip\n        # traversal. See https://docs.python.org/2/library/os.html#os.walk\n        dirs[:] = [\n            d for d in dirs if should_check_directory(\n                os.path.join(parent, d), exclude_patterns, include_patterns\n            )\n        ]\n\n        for path in dirs:\n            if should_include(os.path.join(parent, path),\n                              exclude_patterns, include_patterns):\n                paths.append(os.path.join(parent, path))\n\n        for path in files:\n            if should_include(os.path.join(parent, path),\n                              exclude_patterns, include_patterns):\n                paths.append(os.path.join(parent, path))\n\n    return paths\n\n\ndef match_path(path, pattern):\n    pattern = pattern.rstrip('/' + os.path.sep)\n    if pattern:\n        pattern = os.path.relpath(pattern)\n\n    pattern_components = pattern.split(os.path.sep)\n    if len(pattern_components) == 1 and IS_WINDOWS_PLATFORM:\n        pattern_components = pattern.split('/')\n\n    if '**' not in pattern:\n        path_components = path.split(os.path.sep)[:len(pattern_components)]\n    else:\n        path_components = path.split(os.path.sep)\n    return fnmatch('/'.join(path_components), '/'.join(pattern_components))\n", "path": "docker/utils/build.py"}]}