nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: pre-commit/pre-commit
Pull request #1669 (2020-10-29 05:56:29)

allow default language_version of system when homedir is /


Patch:
diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py
index 69e1278..29138fd 100644
--- a/pre_commit/languages/helpers.py
+++ b/pre_commit/languages/helpers.py
@@ -37,8 +37,14 @@ def exe_exists(exe: str) -> bool:
         common = None
 
     return (
-        not SHIMS_RE.search(found) and  # it is not in a /shims/ directory
-        common != homedir  # it is not in the home directory
+        # it is not in a /shims/ directory
+        not SHIMS_RE.search(found) and
+        (
+            # the homedir is / (docker, service user, etc.)
+            os.path.dirname(homedir) == homedir or
+            # the exe is not contained in the home directory
+            common != homedir
+        )
     )
 
 


Test patch:
diff --git a/tests/languages/helpers_test.py b/tests/languages/helpers_test.py
index 2e8277e..669cd33 100644
--- a/tests/languages/helpers_test.py
+++ b/tests/languages/helpers_test.py
@@ -55,6 +55,12 @@ def test_exe_exists_commonpath_raises_ValueError(find_exe_mck, homedir_mck):
         assert helpers.exe_exists('ruby') is True
 
 
+def test_exe_exists_true_when_homedir_is_slash(find_exe_mck):
+    find_exe_mck.return_value = os.path.normpath('/usr/bin/ruby')
+    with mock.patch.object(os.path, 'expanduser', return_value=os.sep):
+        assert helpers.exe_exists('ruby') is True
+
+
 def test_basic_get_default_version():
     assert helpers.basic_get_default_version() == C.DEFAULT
Repository: pre-commit/pre-commit
Pull request #1735 (2020-12-17 04:25:57)

allow configuration for pre-commit.ci


Patch:
diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py
index 20d4492..916c5ff 100644
--- a/pre_commit/clientlib.py
+++ b/pre_commit/clientlib.py
@@ -297,9 +297,13 @@ CONFIG_SCHEMA = cfgv.Map(
             'exclude',
             'fail_fast',
             'minimum_pre_commit_version',
+            'ci',
         ),
         warn_unknown_keys_root,
     ),
+
+    # do not warn about configuration for pre-commit.ci
+    cfgv.OptionalNoDefault('ci', cfgv.check_type(dict)),
 )
 
 


Test patch:
diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py
index bfb754b..ba60236 100644
--- a/tests/clientlib_test.py
+++ b/tests/clientlib_test.py
@@ -166,6 +166,20 @@ def test_validate_warn_on_unknown_keys_at_top_level(tmpdir, caplog):
     ]
 
 
+def test_ci_map_key_allowed_at_top_level(caplog):
+    cfg = {
+        'ci': {'skip': ['foo']},
+        'repos': [{'repo': 'meta', 'hooks': [{'id': 'identity'}]}],
+    }
+    cfgv.validate(cfg, CONFIG_SCHEMA)
+    assert not caplog.record_tuples
+
+
+def test_ci_key_must_be_map():
+    with pytest.raises(cfgv.ValidationError):
+        cfgv.validate({'ci': 'invalid', 'repos': []}, CONFIG_SCHEMA)
+
+
 def test_validate_optional_sensible_regex(caplog):
     config_obj = {
         'id': 'flake8',
Repository: pre-commit/pre-commit
Pull request #1809 (2021-02-20 22:41:02)

Instruct users how to prevent a warning.
Instruct users how to prevent a mutable rev in repo warning.

JamMarHer: Don't mind my 'noobness' running tests locally now!
JamMarHer: Failed by:
```
           stderr:
               
               CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs/main/linux-64/repodata.json>
               Elapsed: -
               
               An HTTP error occurred when trying to retrieve this URL.
               HTTP errors are often intermittent, and a simple retry will get you on your way.
               
               If your current network has https://www.anaconda.com blocked, please file
               a support request with your network engineering team.
               
               'https://repo.anaconda.com/pkgs/main/linux-64'

```



Patch:
diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py
index 8f35057..962c7fa 100644
--- a/pre_commit/clientlib.py
+++ b/pre_commit/clientlib.py
@@ -128,7 +128,8 @@ class WarnMutableRev(cfgv.ConditionalOptional):
                     f'(moving tag / branch).  Mutable references are never '
                     f'updated after first install and are not supported.  '
                     f'See https://pre-commit.com/#using-the-latest-version-for-a-repository '  # noqa: E501
-                    f'for more details.',
+                    f'for more details.  '
+                    f'Hint: `pre-commit autoupdate` often fixes this.',
                 )
 
 


Test patch:
diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py
index 6bdb0d6..ff3cce3 100644
--- a/tests/clientlib_test.py
+++ b/tests/clientlib_test.py
@@ -228,7 +228,8 @@ def test_warn_mutable_rev_invalid(caplog, rev):
             'Mutable references are never updated after first install and are '
             'not supported.  '
             'See https://pre-commit.com/#using-the-latest-version-for-a-repository '  # noqa: E501
-            'for more details.',
+            'for more details.  '
+            'Hint: `pre-commit autoupdate` often fixes this.',
         ),
     ]
Repository: pre-commit/pre-commit
Pull request #1875 (2021-04-10 07:38:31)

skip installation for SKIP'd hooks


Patch:
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 58dee74..34ace23 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -10,7 +10,7 @@ resources:
       type: github
       endpoint: github
       name: asottile/azure-pipeline-templates
-      ref: refs/tags/v2.1.0
+      ref: refs/tags/v2.0.0
 
 jobs:
 - template: job--python-tox.yml@asottile
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 05c3268..0fef50d 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -271,11 +271,11 @@ def _get_diff() -> bytes:
 def _run_hooks(
         config: Dict[str, Any],
         hooks: Sequence[Hook],
+        skips: Set[str],
         args: argparse.Namespace,
         environ: MutableMapping[str, str],
 ) -> int:
     """Actually run the hooks."""
-    skips = _get_skips(environ)
     cols = _compute_cols(hooks)
     classifier = Classifier.from_config(
         _all_filenames(args), config['files'], config['exclude'],
@@ -403,9 +403,11 @@ def run(
             )
             return 1
 
-        install_hook_envs(hooks, store)
+        skips = _get_skips(environ)
+        to_install = [hook for hook in hooks if hook.id not in skips]
+        install_hook_envs(to_install, store)
 
-        return _run_hooks(config, hooks, args, environ)
+        return _run_hooks(config, hooks, skips, args, environ)
 
     # https://github.com/python/mypy/issues/7726
     raise AssertionError('unreachable')


Test patch:
diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py
index 4cd70fd..8dcb579 100644
--- a/tests/commands/run_test.py
+++ b/tests/commands/run_test.py
@@ -600,6 +600,29 @@ def test_skip_aliased_hook(cap_out, store, aliased_repo):
         assert printed.count(msg) == 1
 
 
+def test_skip_bypasses_installation(cap_out, store, repo_with_passing_hook):
+    config = {
+        'repo': 'local',
+        'hooks': [
+            {
+                'id': 'skipme',
+                'name': 'skipme',
+                'entry': 'skipme',
+                'language': 'python',
+                'additional_dependencies': ['/pre-commit-does-not-exist'],
+            },
+        ],
+    }
+    add_config_to_repo(repo_with_passing_hook, config)
+
+    ret, printed = _do_run(
+        cap_out, store, repo_with_passing_hook,
+        run_opts(all_files=True),
+        {'SKIP': 'skipme'},
+    )
+    assert ret == 0
+
+
 def test_hook_id_not_in_non_verbose_output(
         cap_out, store, repo_with_passing_hook,
 ):
Repository: pre-commit/pre-commit
Pull request #2026 (2021-08-28 17:44:39)

make `repo: meta` only apply to top level configuration
originally reported in https://github.com/pre-commit-ci/issues/issues/84


Patch:
diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py
index 962c7fa..bc7274a 100644
--- a/pre_commit/clientlib.py
+++ b/pre_commit/clientlib.py
@@ -216,14 +216,14 @@ _meta = (
     (
         'check-hooks-apply', (
             ('name', 'Check hooks apply to the repository'),
-            ('files', C.CONFIG_FILE),
+            ('files', f'^{re.escape(C.CONFIG_FILE)}$'),
             ('entry', _entry('check_hooks_apply')),
         ),
     ),
     (
         'check-useless-excludes', (
             ('name', 'Check for useless excludes'),
-            ('files', C.CONFIG_FILE),
+            ('files', f'^{re.escape(C.CONFIG_FILE)}$'),
             ('entry', _entry('check_useless_excludes')),
         ),
     ),


Test patch:
diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py
index 09bdb3e..da794e6 100644
--- a/tests/clientlib_test.py
+++ b/tests/clientlib_test.py
@@ -1,4 +1,5 @@
 import logging
+import re
 
 import cfgv
 import pytest
@@ -10,6 +11,7 @@ from pre_commit.clientlib import CONFIG_REPO_DICT
 from pre_commit.clientlib import CONFIG_SCHEMA
 from pre_commit.clientlib import DEFAULT_LANGUAGE_VERSION
 from pre_commit.clientlib import MANIFEST_SCHEMA
+from pre_commit.clientlib import META_HOOK_DICT
 from pre_commit.clientlib import MigrateShaToRev
 from pre_commit.clientlib import validate_config_main
 from pre_commit.clientlib import validate_manifest_main
@@ -392,6 +394,15 @@ def test_meta_hook_invalid(config_repo):
         cfgv.validate(config_repo, CONFIG_REPO_DICT)
 
 
+def test_meta_check_hooks_apply_only_at_top_level():
+    cfg = {'id': 'check-hooks-apply'}
+    cfg = cfgv.apply_defaults(cfg, META_HOOK_DICT)
+
+    files_re = re.compile(cfg['files'])
+    assert files_re.search('.pre-commit-config.yaml')
+    assert not files_re.search('foo/.pre-commit-config.yaml')
+
+
 @pytest.mark.parametrize(
     'mapping',
     (
Repository: pre-commit/pre-commit
Pull request #2136 (2021-11-22 22:04:41)

support gitconfig from env
Add exceptions to the git env so externally configured gitconfig
values set via `GIT_CONFIG_KEY_<n>`, `GIT_CONFIG_VALUE_<n>` and
`GIT_CONFIG_COUNT` get passed through. This is e.g. useful when using
the env to inject insteadOf configurations to force use of proxy servers
in a CI environment.

asottile: can you link a reference?
emzeat: > can you link a reference?

Sure, see http://git-scm.com/docs/git-config#Documentation/git-config.txt-GITCONFIGCOUNT

Patch:
diff --git a/pre_commit/git.py b/pre_commit/git.py
index 883723e..e9ec601 100644
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -41,9 +41,10 @@ def no_git_env(
     return {
         k: v for k, v in _env.items()
         if not k.startswith('GIT_') or
+        k.startswith(('GIT_CONFIG_KEY_', 'GIT_CONFIG_VALUE_')) or
         k in {
             'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',
-            'GIT_SSL_NO_VERIFY',
+            'GIT_SSL_NO_VERIFY', 'GIT_CONFIG_COUNT',
         }
     }
 


Test patch:
diff --git a/tests/git_test.py b/tests/git_test.py
index aa21880..bcb3fd1 100644
--- a/tests/git_test.py
+++ b/tests/git_test.py
@@ -227,6 +227,11 @@ def test_no_git_env():
         'GIT_SSH': '/usr/bin/ssh',
         'GIT_SSH_COMMAND': 'ssh -o',
         'GIT_DIR': '/none/shall/pass',
+        'GIT_CONFIG_KEY_0': 'user.name',
+        'GIT_CONFIG_VALUE_0': 'anthony',
+        'GIT_CONFIG_KEY_1': 'user.email',
+        'GIT_CONFIG_VALUE_1': 'asottile@example.com',
+        'GIT_CONFIG_COUNT': '2',
     }
     no_git_env = git.no_git_env(env)
     assert no_git_env == {
@@ -234,6 +239,11 @@ def test_no_git_env():
         'GIT_EXEC_PATH': '/some/git/exec/path',
         'GIT_SSH': '/usr/bin/ssh',
         'GIT_SSH_COMMAND': 'ssh -o',
+        'GIT_CONFIG_KEY_0': 'user.name',
+        'GIT_CONFIG_VALUE_0': 'anthony',
+        'GIT_CONFIG_KEY_1': 'user.email',
+        'GIT_CONFIG_VALUE_1': 'asottile@example.com',
+        'GIT_CONFIG_COUNT': '2',
     }
Repository: pre-commit/pre-commit
Pull request #2181 (2022-01-01 01:31:42)

forbid overriding `entry` for meta hooks
would have avoided some confusion in #2180


Patch:
diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py
index b8f2368..47ebd54 100644
--- a/pre_commit/clientlib.py
+++ b/pre_commit/clientlib.py
@@ -251,12 +251,21 @@ _meta = (
     ),
 )
 
+
+class NotAllowed(cfgv.OptionalNoDefault):
+    def check(self, dct: Dict[str, Any]) -> None:
+        if self.key in dct:
+            raise cfgv.ValidationError(f'{self.key!r} cannot be overridden')
+
+
 META_HOOK_DICT = cfgv.Map(
     'Hook', 'id',
     cfgv.Required('id', cfgv.check_string),
     cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))),
     # language must be system
     cfgv.Optional('language', cfgv.check_one_of({'system'}), 'system'),
+    # entry cannot be overridden
+    NotAllowed('entry', cfgv.check_any),
     *(
         # default to the hook definition for the meta hooks
         cfgv.ConditionalOptional(key, cfgv.check_any, value, 'id', hook_id)


Test patch:
diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py
index a2be51b..39a3716 100644
--- a/tests/clientlib_test.py
+++ b/tests/clientlib_test.py
@@ -423,6 +423,13 @@ def test_migrate_to_sha_ok():
         {'repo': 'meta', 'hooks': [{'id': 'identity', 'language': 'python'}]},
         # name override must be string
         {'repo': 'meta', 'hooks': [{'id': 'identity', 'name': False}]},
+        pytest.param(
+            {
+                'repo': 'meta',
+                'hooks': [{'id': 'identity', 'entry': 'echo hi'}],
+            },
+            id='cannot override entry for meta hooks',
+        ),
     ),
 )
 def test_meta_hook_invalid(config_repo):
Repository: pre-commit/pre-commit
Pull request #2210 (2022-01-16 15:21:47)

add git version to error output


Patch:
diff --git a/pre_commit/error_handler.py b/pre_commit/error_handler.py
index 023dd35..7e74b95 100644
--- a/pre_commit/error_handler.py
+++ b/pre_commit/error_handler.py
@@ -9,6 +9,7 @@ import pre_commit.constants as C
 from pre_commit import output
 from pre_commit.errors import FatalError
 from pre_commit.store import Store
+from pre_commit.util import cmd_output_b
 from pre_commit.util import force_bytes
 
 
@@ -21,6 +22,9 @@ def _log_and_exit(
     error_msg = f'{msg}: {type(exc).__name__}: '.encode() + force_bytes(exc)
     output.write_line_b(error_msg)
 
+    _, git_version_b, _ = cmd_output_b('git', '--version', retcode=None)
+    git_version = git_version_b.decode(errors='backslashreplace').rstrip()
+
     storedir = Store().directory
     log_path = os.path.join(storedir, 'pre-commit.log')
     with contextlib.ExitStack() as ctx:
@@ -38,6 +42,7 @@ def _log_and_exit(
         _log_line()
         _log_line('```')
         _log_line(f'pre-commit version: {C.VERSION}')
+        _log_line(f'git --version: {git_version}')
         _log_line('sys.version:')
         for line in sys.version.splitlines():
             _log_line(f'    {line}')


Test patch:
diff --git a/tests/error_handler_test.py b/tests/error_handler_test.py
index 6b0bb86..cb76dcf 100644
--- a/tests/error_handler_test.py
+++ b/tests/error_handler_test.py
@@ -122,6 +122,7 @@ def test_log_and_exit(cap_out, mock_store_dir):
             r'\n'
             r'```\n'
             r'pre-commit version: \d+\.\d+\.\d+\n'
+            r'git --version: git version .+\n'
             r'sys.version:\n'
             r'(    .*\n)*'
             r'sys.executable: .*\n'
Repository: pre-commit/pre-commit-hooks
Pull request #1044 (2024-04-16 18:43:56)

support more illegal Windows filenames
See comments in #1031


Patch:
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index 116392d..44a8648 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -44,7 +44,7 @@
     name: check illegal windows names
     entry: Illegal windows filenames detected
     language: fail
-    files: '(?i)(^|/)(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.|/|$)'
+    files: '(?i)((^|/)(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.|/|$)|:)'
 -   id: check-json
     name: check json
     description: checks json files for parseable syntax.


Test patch:
diff --git a/tests/check_illegal_windows_names_test.py b/tests/check_illegal_windows_names_test.py
index 4dce4f6..35a5be3 100644
--- a/tests/check_illegal_windows_names_test.py
+++ b/tests/check_illegal_windows_names_test.py
@@ -28,6 +28,8 @@ def hook_re():
         pytest.param('aux', id='without ext'),
         pytest.param('AuX.tXt', id='capitals'),
         pytest.param('com7.dat', id='com with digit'),
+        pytest.param(':', id='bare colon'),
+        pytest.param('file:Zone.Identifier', id='mid colon'),
     ),
 )
 def test_check_illegal_windows_names_matches(hook_re, s):
Repository: pre-commit/pre-commit
Pull request #2484 (2022-08-15 17:46:33)

allow `pre-commit run --files ...` against unmerged files


Patch:
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 8d11882..37f78f7 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -333,7 +333,7 @@ def run(
     stash = not args.all_files and not args.files
 
     # Check if we have unresolved merge conflict files and fail fast.
-    if _has_unmerged_paths():
+    if stash and _has_unmerged_paths():
         logger.error('Unmerged files.  Resolve before committing.')
         return 1
     if bool(args.from_ref) != bool(args.to_ref):


Test patch:
diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py
index 3ae3b53..ef86533 100644
--- a/tests/commands/run_test.py
+++ b/tests/commands/run_test.py
@@ -536,6 +536,13 @@ def test_merge_conflict(cap_out, store, in_merge_conflict):
     assert b'Unmerged files.  Resolve before committing.' in printed
 
 
+def test_files_during_merge_conflict(cap_out, store, in_merge_conflict):
+    opts = run_opts(files=['placeholder'])
+    ret, printed = _do_run(cap_out, store, in_merge_conflict, opts)
+    assert ret == 0
+    assert b'Bash hook' in printed
+
+
 def test_merge_conflict_modified(cap_out, store, in_merge_conflict):
     # Touch another file so we have unstaged non-conflicting things
     assert os.path.exists('placeholder')
Repository: pre-commit/pre-commit
Pull request #2775 (2023-02-21 17:43:14)

fix trailing whitespace in CalledProcessError output


Patch:
diff --git a/pre_commit/util.py b/pre_commit/util.py
index 3d448e3..ea0d4f5 100644
--- a/pre_commit/util.py
+++ b/pre_commit/util.py
@@ -62,7 +62,7 @@ class CalledProcessError(RuntimeError):
     def __bytes__(self) -> bytes:
         def _indent_or_none(part: bytes | None) -> bytes:
             if part:
-                return b'\n    ' + part.replace(b'\n', b'\n    ')
+                return b'\n    ' + part.replace(b'\n', b'\n    ').rstrip()
             else:
                 return b' (none)'
 


Test patch:
diff --git a/tests/staged_files_only_test.py b/tests/staged_files_only_test.py
index 50f146b..58dbe5a 100644
--- a/tests/staged_files_only_test.py
+++ b/tests/staged_files_only_test.py
@@ -425,9 +425,7 @@ def test_failed_diff_does_not_discard_changes(in_git_dir, patch_dir):
         r'stdout: \(none\)\n'
         r'stderr:\n'
         r'    error: open\("1"\): Permission denied\n'
-        r'    fatal: cannot hash 1\n'
-        # TODO: not sure why there's weird whitespace here
-        r'    $',
+        r'    fatal: cannot hash 1$',
     ).assert_matches(msg)
 
     # even though it errored, the unstaged changes should still be present
diff --git a/tests/util_test.py b/tests/util_test.py
index 310f8f5..5b26211 100644
--- a/tests/util_test.py
+++ b/tests/util_test.py
@@ -16,7 +16,7 @@ from pre_commit.util import rmtree
 
 
 def test_CalledProcessError_str():
-    error = CalledProcessError(1, ('exe',), b'output', b'errors')
+    error = CalledProcessError(1, ('exe',), b'output\n', b'errors\n')
     assert str(error) == (
         "command: ('exe',)\n"
         'return code: 1\n'
Repository: pre-commit/pre-commit
Pull request #2776 (2023-02-23 01:31:29)

remove sorting for repo key for additional_deps
in other languages this order can matter (such as ruby)


Patch:
diff --git a/pre_commit/repository.py b/pre_commit/repository.py
index 5183df4..040f238 100644
--- a/pre_commit/repository.py
+++ b/pre_commit/repository.py
@@ -33,7 +33,7 @@ def _state_filename_v2(venv: str) -> str:
 
 
 def _state(additional_deps: Sequence[str]) -> object:
-    return {'additional_dependencies': sorted(additional_deps)}
+    return {'additional_dependencies': additional_deps}
 
 
 def _read_state(venv: str) -> object | None:
diff --git a/pre_commit/store.py b/pre_commit/store.py
index 6ddc7c4..487e3e7 100644
--- a/pre_commit/store.py
+++ b/pre_commit/store.py
@@ -125,7 +125,7 @@ class Store:
     @classmethod
     def db_repo_name(cls, repo: str, deps: Sequence[str]) -> str:
         if deps:
-            return f'{repo}:{",".join(sorted(deps))}'
+            return f'{repo}:{",".join(deps)}'
         else:
             return repo
 


Test patch:
diff --git a/tests/store_test.py b/tests/store_test.py
index 146eac4..eaab940 100644
--- a/tests/store_test.py
+++ b/tests/store_test.py
@@ -180,7 +180,7 @@ def test_create_when_store_already_exists(store):
 
 def test_db_repo_name(store):
     assert store.db_repo_name('repo', ()) == 'repo'
-    assert store.db_repo_name('repo', ('b', 'a', 'c')) == 'repo:a,b,c'
+    assert store.db_repo_name('repo', ('b', 'a', 'c')) == 'repo:b,a,c'
 
 
 def test_local_resources_reflects_reality():
Repository: pre-commit/pre-commit
Pull request #3126 (2024-02-10 19:02:18)

staged_files_only can handle a crlf-only diff
reported in https://github.com/pre-commit/pre-commit/issues/2839#issuecomment-1910833889


Patch:
diff --git a/pre_commit/staged_files_only.py b/pre_commit/staged_files_only.py
index fd28e1c..e1f81ba 100644
--- a/pre_commit/staged_files_only.py
+++ b/pre_commit/staged_files_only.py
@@ -59,6 +59,11 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
         # There weren't any staged files so we don't need to do anything
         # special
         yield
+    elif retcode == 1 and not diff_stdout.strip():
+        # due to behaviour (probably a bug?) in git with crlf endings and
+        # autocrlf set to either `true` or `input` sometimes git will refuse
+        # to show a crlf-only diff to us :(
+        yield
     elif retcode == 1 and diff_stdout.strip():
         patch_filename = f'patch{int(time.time())}-{os.getpid()}'
         patch_filename = os.path.join(patch_dir, patch_filename)


Test patch:
diff --git a/tests/staged_files_only_test.py b/tests/staged_files_only_test.py
index 58dbe5a..cd2f638 100644
--- a/tests/staged_files_only_test.py
+++ b/tests/staged_files_only_test.py
@@ -358,6 +358,21 @@ def test_crlf(in_git_dir, patch_dir, crlf_before, crlf_after, autocrlf):
         assert_no_diff()
 
 
+@pytest.mark.parametrize('autocrlf', ('true', 'input'))
+def test_crlf_diff_only(in_git_dir, patch_dir, autocrlf):
+    # due to a quirk (?) in git -- a diff only in crlf does not show but
+    # still results in an exit code of `1`
+    # we treat this as "no diff" -- though ideally it would discard the diff
+    # while committing
+    cmd_output('git', 'config', '--local', 'core.autocrlf', autocrlf)
+
+    _write(b'1\r\n2\r\n3\r\n')
+    cmd_output('git', 'add', 'foo')
+    _write(b'1\n2\n3\n')
+    with staged_files_only(patch_dir):
+        pass
+
+
 def test_whitespace_errors(in_git_dir, patch_dir):
     cmd_output('git', 'config', '--local', 'apply.whitespace', 'error')
     test_crlf(in_git_dir, patch_dir, True, True, 'true')