prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  git.get_git_dir() appears to break with older versions of git
  When you install the current pre-commit (v1.11.0) on a system using an older git, eg 2.1.4, the `git rev-parse --git-common-dir` ultimately issued by `git.get_git_dir()` yields the result `--git-common-dir` instead of `.git` like you'd expect. This causes pre-commit to get installed in a directory literally named "--git-common-dir", and thus not work.
  
  Happened to encounter using a docker image for an older version of python, which used an older git. Examples to demo the issue:
  
  ```
  $ docker run python:2.7.9 sh -c "git init foo && cd foo && git rev-parse --git-common-dir && git --version"
  Initialized empty Git repository in /foo/.git/
  --git-common-dir
  git version 2.1.4
  $ docker run python:2.7.15 sh -c "git init foo && cd foo && git rev-parse --git-common-dir && git --version"
  Initialized empty Git repository in /foo/.git/
  .git
  git version 2.11.0
  ```
  
  In git.get_git_dir() as it stands, it appears that the older-git behavior is anticipated. It looks like older gits are expected to throw a `CalledProcessError` when `git rev-parse --git-common-dir` is called, and fall back to `git rev-parse --git-dir`. But evidently that's not working as intended?
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pre_commit/git.py`
  Content:
  ```
  1 from __future__ import unicode_literals
  2 
  3 import logging
  4 import os.path
  5 import sys
  6 
  7 from pre_commit.error_handler import FatalError
  8 from pre_commit.util import CalledProcessError
  9 from pre_commit.util import cmd_output
  10 
  11 
  12 logger = logging.getLogger('pre_commit')
  13 
  14 
  15 def zsplit(s):
  16     s = s.strip('\0')
  17     if s:
  18         return s.split('\0')
  19     else:
  20         return []
  21 
  22 
  23 def get_root():
  24     try:
  25         return cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()
  26     except CalledProcessError:
  27         raise FatalError(
  28             'git failed. Is it installed, and are you in a Git repository '
  29             'directory?',
  30         )
  31 
  32 
  33 def get_git_dir(git_root):
  34     def _git_dir(opt):
  35         return os.path.normpath(os.path.join(
  36             git_root,
  37             cmd_output('git', 'rev-parse', opt, cwd=git_root)[1].strip(),
  38         ))
  39 
  40     try:
  41         return _git_dir('--git-common-dir')
  42     except CalledProcessError:  # pragma: no cover (git < 2.5)
  43         return _git_dir('--git-dir')
  44 
  45 
  46 def get_remote_url(git_root):
  47     ret = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)[1]
  48     return ret.strip()
  49 
  50 
  51 def is_in_merge_conflict():
  52     git_dir = get_git_dir('.')
  53     return (
  54         os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and
  55         os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))
  56     )
  57 
  58 
  59 def parse_merge_msg_for_conflicts(merge_msg):
  60     # Conflicted files start with tabs
  61     return [
  62         line.lstrip(b'#').strip().decode('UTF-8')
  63         for line in merge_msg.splitlines()
  64         # '#\t' for git 2.4.1
  65         if line.startswith((b'\t', b'#\t'))
  66     ]
  67 
  68 
  69 def get_conflicted_files():
  70     logger.info('Checking merge-conflict files only.')
  71     # Need to get the conflicted files from the MERGE_MSG because they could
  72     # have resolved the conflict by choosing one side or the other
  73     with open(os.path.join(get_git_dir('.'), 'MERGE_MSG'), 'rb') as f:
  74         merge_msg = f.read()
  75     merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)
  76 
  77     # This will get the rest of the changes made after the merge.
  78     # If they resolved the merge conflict by choosing a mesh of both sides
  79     # this will also include the conflicted files
  80     tree_hash = cmd_output('git', 'write-tree')[1].strip()
  81     merge_diff_filenames = zsplit(cmd_output(
  82         'git', 'diff', '--name-only', '--no-ext-diff', '-z',
  83         '-m', tree_hash, 'HEAD', 'MERGE_HEAD',
  84     )[1])
  85     return set(merge_conflict_filenames) | set(merge_diff_filenames)
  86 
  87 
  88 def get_staged_files():
  89     return zsplit(cmd_output(
  90         'git', 'diff', '--staged', '--name-only', '--no-ext-diff', '-z',
  91         # Everything except for D
  92         '--diff-filter=ACMRTUXB',
  93     )[1])
  94 
  95 
  96 def get_all_files():
  97     return zsplit(cmd_output('git', 'ls-files', '-z')[1])
  98 
  99 
  100 def get_changed_files(new, old):
  101     return zsplit(cmd_output(
  102         'git', 'diff', '--name-only', '--no-ext-diff', '-z',
  103         '{}...{}'.format(old, new),
  104     )[1])
  105 
  106 
  107 def head_rev(remote):
  108     _, out, _ = cmd_output('git', 'ls-remote', '--exit-code', remote, 'HEAD')
  109     return out.split()[0]
  110 
  111 
  112 def check_for_cygwin_mismatch():
  113     """See https://github.com/pre-commit/pre-commit/issues/354"""
  114     if sys.platform in ('cygwin', 'win32'):  # pragma: no cover (windows)
  115         is_cygwin_python = sys.platform == 'cygwin'
  116         toplevel = cmd_output('git', 'rev-parse', '--show-toplevel')[1]
  117         is_cygwin_git = toplevel.startswith('/')
  118 
  119         if is_cygwin_python ^ is_cygwin_git:
  120             exe_type = {True: '(cygwin)', False: '(windows)'}
  121             logger.warn(
  122                 'pre-commit has detected a mix of cygwin python / git\n'
  123                 'This combination is not supported, it is likely you will '
  124                 'receive an error later in the program.\n'
  125                 'Make sure to use cygwin git+python while using cygwin\n'
  126                 'These can be installed through the cygwin installer.\n'
  127                 ' - python {}\n'
  128                 ' - git {}\n'.format(
  129                     exe_type[is_cygwin_python], exe_type[is_cygwin_git],
  130                 ),
  131             )
  132
  ```
  --- 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/pre_commit/git.py b/pre_commit/git.py
  --- a/pre_commit/git.py
  +++ b/pre_commit/git.py
  @@ -31,16 +31,13 @@
   
   
   def get_git_dir(git_root):
  -    def _git_dir(opt):
  -        return os.path.normpath(os.path.join(
  -            git_root,
  -            cmd_output('git', 'rev-parse', opt, cwd=git_root)[1].strip(),
  -        ))
  -
  -    try:
  -        return _git_dir('--git-common-dir')
  -    except CalledProcessError:  # pragma: no cover (git < 2.5)
  -        return _git_dir('--git-dir')
  +    opts = ('--git-common-dir', '--git-dir')
  +    _, out, _ = cmd_output('git', 'rev-parse', *opts, cwd=git_root)
  +    for line, opt in zip(out.splitlines(), opts):
  +        if line != opt:  # pragma: no branch (git < 2.5)
  +            return os.path.normpath(os.path.join(git_root, line))
  +    else:
  +        raise AssertionError('unreachable: no git dir')
   
   
   def get_remote_url(git_root):
  
verification_info: {"golden_diff": "diff --git a/pre_commit/git.py b/pre_commit/git.py\n--- a/pre_commit/git.py\n+++ b/pre_commit/git.py\n@@ -31,16 +31,13 @@\n \n \n def get_git_dir(git_root):\n-    def _git_dir(opt):\n-        return os.path.normpath(os.path.join(\n-            git_root,\n-            cmd_output('git', 'rev-parse', opt, cwd=git_root)[1].strip(),\n-        ))\n-\n-    try:\n-        return _git_dir('--git-common-dir')\n-    except CalledProcessError:  # pragma: no cover (git < 2.5)\n-        return _git_dir('--git-dir')\n+    opts = ('--git-common-dir', '--git-dir')\n+    _, out, _ = cmd_output('git', 'rev-parse', *opts, cwd=git_root)\n+    for line, opt in zip(out.splitlines(), opts):\n+        if line != opt:  # pragma: no branch (git < 2.5)\n+            return os.path.normpath(os.path.join(git_root, line))\n+    else:\n+        raise AssertionError('unreachable: no git dir')\n \n \n def get_remote_url(git_root):\n", "issue": "git.get_git_dir() appears to break with older versions of git\nWhen you install the current pre-commit (v1.11.0) on a system using an older git, eg 2.1.4, the `git rev-parse --git-common-dir` ultimately issued by `git.get_git_dir()` yields the result `--git-common-dir` instead of `.git` like you'd expect. This causes pre-commit to get installed in a directory literally named \"--git-common-dir\", and thus not work.\r\n\r\nHappened to encounter using a docker image for an older version of python, which used an older git. Examples to demo the issue:\r\n\r\n```\r\n$ docker run python:2.7.9 sh -c \"git init foo && cd foo && git rev-parse --git-common-dir && git --version\"\r\nInitialized empty Git repository in /foo/.git/\r\n--git-common-dir\r\ngit version 2.1.4\r\n$ docker run python:2.7.15 sh -c \"git init foo && cd foo && git rev-parse --git-common-dir && git --version\"\r\nInitialized empty Git repository in /foo/.git/\r\n.git\r\ngit version 2.11.0\r\n```\r\n\r\nIn git.get_git_dir() as it stands, it appears that the older-git behavior is anticipated. It looks like older gits are expected to throw a `CalledProcessError` when `git rev-parse --git-common-dir` is called, and fall back to `git rev-parse --git-dir`. But evidently that's not working as intended?\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport os.path\nimport sys\n\nfrom pre_commit.error_handler import FatalError\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\n\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef zsplit(s):\n    s = s.strip('\\0')\n    if s:\n        return s.split('\\0')\n    else:\n        return []\n\n\ndef get_root():\n    try:\n        return cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()\n    except CalledProcessError:\n        raise FatalError(\n            'git failed. Is it installed, and are you in a Git repository '\n            'directory?',\n        )\n\n\ndef get_git_dir(git_root):\n    def _git_dir(opt):\n        return os.path.normpath(os.path.join(\n            git_root,\n            cmd_output('git', 'rev-parse', opt, cwd=git_root)[1].strip(),\n        ))\n\n    try:\n        return _git_dir('--git-common-dir')\n    except CalledProcessError:  # pragma: no cover (git < 2.5)\n        return _git_dir('--git-dir')\n\n\ndef get_remote_url(git_root):\n    ret = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)[1]\n    return ret.strip()\n\n\ndef is_in_merge_conflict():\n    git_dir = get_git_dir('.')\n    return (\n        os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and\n        os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))\n    )\n\n\ndef parse_merge_msg_for_conflicts(merge_msg):\n    # Conflicted files start with tabs\n    return [\n        line.lstrip(b'#').strip().decode('UTF-8')\n        for line in merge_msg.splitlines()\n        # '#\\t' for git 2.4.1\n        if line.startswith((b'\\t', b'#\\t'))\n    ]\n\n\ndef get_conflicted_files():\n    logger.info('Checking merge-conflict files only.')\n    # Need to get the conflicted files from the MERGE_MSG because they could\n    # have resolved the conflict by choosing one side or the other\n    with open(os.path.join(get_git_dir('.'), 'MERGE_MSG'), 'rb') as f:\n        merge_msg = f.read()\n    merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)\n\n    # This will get the rest of the changes made after the merge.\n    # If they resolved the merge conflict by choosing a mesh of both sides\n    # this will also include the conflicted files\n    tree_hash = cmd_output('git', 'write-tree')[1].strip()\n    merge_diff_filenames = zsplit(cmd_output(\n        'git', 'diff', '--name-only', '--no-ext-diff', '-z',\n        '-m', tree_hash, 'HEAD', 'MERGE_HEAD',\n    )[1])\n    return set(merge_conflict_filenames) | set(merge_diff_filenames)\n\n\ndef get_staged_files():\n    return zsplit(cmd_output(\n        'git', 'diff', '--staged', '--name-only', '--no-ext-diff', '-z',\n        # Everything except for D\n        '--diff-filter=ACMRTUXB',\n    )[1])\n\n\ndef get_all_files():\n    return zsplit(cmd_output('git', 'ls-files', '-z')[1])\n\n\ndef get_changed_files(new, old):\n    return zsplit(cmd_output(\n        'git', 'diff', '--name-only', '--no-ext-diff', '-z',\n        '{}...{}'.format(old, new),\n    )[1])\n\n\ndef head_rev(remote):\n    _, out, _ = cmd_output('git', 'ls-remote', '--exit-code', remote, 'HEAD')\n    return out.split()[0]\n\n\ndef check_for_cygwin_mismatch():\n    \"\"\"See https://github.com/pre-commit/pre-commit/issues/354\"\"\"\n    if sys.platform in ('cygwin', 'win32'):  # pragma: no cover (windows)\n        is_cygwin_python = sys.platform == 'cygwin'\n        toplevel = cmd_output('git', 'rev-parse', '--show-toplevel')[1]\n        is_cygwin_git = toplevel.startswith('/')\n\n        if is_cygwin_python ^ is_cygwin_git:\n            exe_type = {True: '(cygwin)', False: '(windows)'}\n            logger.warn(\n                'pre-commit has detected a mix of cygwin python / git\\n'\n                'This combination is not supported, it is likely you will '\n                'receive an error later in the program.\\n'\n                'Make sure to use cygwin git+python while using cygwin\\n'\n                'These can be installed through the cygwin installer.\\n'\n                ' - python {}\\n'\n                ' - git {}\\n'.format(\n                    exe_type[is_cygwin_python], exe_type[is_cygwin_git],\n                ),\n            )\n", "path": "pre_commit/git.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport os.path\nimport sys\n\nfrom pre_commit.error_handler import FatalError\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\n\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef zsplit(s):\n    s = s.strip('\\0')\n    if s:\n        return s.split('\\0')\n    else:\n        return []\n\n\ndef get_root():\n    try:\n        return cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()\n    except CalledProcessError:\n        raise FatalError(\n            'git failed. Is it installed, and are you in a Git repository '\n            'directory?',\n        )\n\n\ndef get_git_dir(git_root):\n    opts = ('--git-common-dir', '--git-dir')\n    _, out, _ = cmd_output('git', 'rev-parse', *opts, cwd=git_root)\n    for line, opt in zip(out.splitlines(), opts):\n        if line != opt:  # pragma: no branch (git < 2.5)\n            return os.path.normpath(os.path.join(git_root, line))\n    else:\n        raise AssertionError('unreachable: no git dir')\n\n\ndef get_remote_url(git_root):\n    ret = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)[1]\n    return ret.strip()\n\n\ndef is_in_merge_conflict():\n    git_dir = get_git_dir('.')\n    return (\n        os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and\n        os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))\n    )\n\n\ndef parse_merge_msg_for_conflicts(merge_msg):\n    # Conflicted files start with tabs\n    return [\n        line.lstrip(b'#').strip().decode('UTF-8')\n        for line in merge_msg.splitlines()\n        # '#\\t' for git 2.4.1\n        if line.startswith((b'\\t', b'#\\t'))\n    ]\n\n\ndef get_conflicted_files():\n    logger.info('Checking merge-conflict files only.')\n    # Need to get the conflicted files from the MERGE_MSG because they could\n    # have resolved the conflict by choosing one side or the other\n    with open(os.path.join(get_git_dir('.'), 'MERGE_MSG'), 'rb') as f:\n        merge_msg = f.read()\n    merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)\n\n    # This will get the rest of the changes made after the merge.\n    # If they resolved the merge conflict by choosing a mesh of both sides\n    # this will also include the conflicted files\n    tree_hash = cmd_output('git', 'write-tree')[1].strip()\n    merge_diff_filenames = zsplit(cmd_output(\n        'git', 'diff', '--name-only', '--no-ext-diff', '-z',\n        '-m', tree_hash, 'HEAD', 'MERGE_HEAD',\n    )[1])\n    return set(merge_conflict_filenames) | set(merge_diff_filenames)\n\n\ndef get_staged_files():\n    return zsplit(cmd_output(\n        'git', 'diff', '--staged', '--name-only', '--no-ext-diff', '-z',\n        # Everything except for D\n        '--diff-filter=ACMRTUXB',\n    )[1])\n\n\ndef get_all_files():\n    return zsplit(cmd_output('git', 'ls-files', '-z')[1])\n\n\ndef get_changed_files(new, old):\n    return zsplit(cmd_output(\n        'git', 'diff', '--name-only', '--no-ext-diff', '-z',\n        '{}...{}'.format(old, new),\n    )[1])\n\n\ndef head_rev(remote):\n    _, out, _ = cmd_output('git', 'ls-remote', '--exit-code', remote, 'HEAD')\n    return out.split()[0]\n\n\ndef check_for_cygwin_mismatch():\n    \"\"\"See https://github.com/pre-commit/pre-commit/issues/354\"\"\"\n    if sys.platform in ('cygwin', 'win32'):  # pragma: no cover (windows)\n        is_cygwin_python = sys.platform == 'cygwin'\n        toplevel = cmd_output('git', 'rev-parse', '--show-toplevel')[1]\n        is_cygwin_git = toplevel.startswith('/')\n\n        if is_cygwin_python ^ is_cygwin_git:\n            exe_type = {True: '(cygwin)', False: '(windows)'}\n            logger.warn(\n                'pre-commit has detected a mix of cygwin python / git\\n'\n                'This combination is not supported, it is likely you will '\n                'receive an error later in the program.\\n'\n                'Make sure to use cygwin git+python while using cygwin\\n'\n                'These can be installed through the cygwin installer.\\n'\n                ' - python {}\\n'\n                ' - git {}\\n'.format(\n                    exe_type[is_cygwin_python], exe_type[is_cygwin_git],\n                ),\n            )\n", "path": "pre_commit/git.py"}]}