Repository: pre-commit/pre-commit

Issue:
[FR][bug?] pre-commit hook repo self-test
Given a repo with `.pre-commit-hooks.yaml` defined (like https://github.com/ansible/ansible-lint), I want to integrate testing of the hooks declared in it.

I can do `pre-commit try-repo https://github.com/ansible/ansible-lint.git` but this hits the remote which I want to avoid. I know that Git itself can work with local fs paths (like `/path/to/.git`) perfectly fine.

So I tried:
<details>
<summary>
  <code>$ <kbd>pre-commit try-repo .git -vvv</kbd></code>
</summary>

```console
➜ pre-commit try-repo .git -vvv
[WARNING] Creating temporary repo with uncommitted changes...
An unexpected error has occurred: CalledProcessError: Command: ('/usr/bin/git', 'add', '-u')
Return code: 128
Expected return code: 0
Output: (none)
Errors: 
    fatal: this operation must be run in a work tree
    

Check the log at ~/.cache/pre-commit/pre-commit.log
```
</details>

The log doesn't reveal anything more than the fact that the Git command failed.
<details>
<summary>
  <code>$ <kbd>cat ~/.cache/pre-commit/pre-commit.log</kbd></code>
</summary>

```console
An unexpected error has occurred: CalledProcessError: Command: ('/usr/bin/git', 'add', '-u')
Return code: 128
Expected return code: 0
Output: (none)
Errors: 
    fatal: this operation must be run in a work tree
    

Traceback (most recent call last):
  File "~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/pre_commit/error_handler.py", line 46, in error_handler
    yield
  File "~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/pre_commit/main.py", line 296, in main
    return try_repo(args)
  File "~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/pre_commit/commands/try_repo.py", line 55, in try_repo
    repo, ref = _repo_ref(tempdir, args.repo, args.ref)
  File "~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/pre_commit/commands/try_repo.py", line 45, in _repo_ref
    cmd_output('git', 'add', '-u', cwd=repo, env=env)
  File "~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/pre_commit/util.py", line 153, in cmd_output
    returncode, cmd, retcode, output=(stdout, stderr),
pre_commit.util.CalledProcessError: Command: ('/usr/bin/git', 'add', '-u')
Return code: 128
Expected return code: 0
Output: (none)
Errors: 
    fatal: this operation must be run in a work tree
```
</details>

It must be pretty easy to fix.


Discussion:
I believe you want `pre-commit try-repo .`
the docs go a little bit into this:
- [developing hooks interactively](https://pre-commit.com/#developing-hooks-interactively)
- [pre-commit try-repo](https://pre-commit.com/#pre-commit-try-repo)
This is what stopped me. I guess I forgot to check it this time because I saw some issues in the past...
```console
Errors: 
    Directory '.' is not installable. File 'setup.py' not found.
    You are using pip version 10.0.1, however version 19.3.1 is available.
    You should consider upgrading via the 'pip install --upgrade pip' command.
```

Trying now...


Anyway, it may be a good idea to emit some hint if somebody tries repo==.git
> * [developing hooks interactively](https://pre-commit.com/#developing-hooks-interactively)

It says:
> Since the `repo` property of `.pre-commit-config.yaml` can refer to anything that `git clone ...` understands

which breaks with `.git` meaning that the statement is not completely true in all cases...

Looks like this issue deserves a bug label after all.
Anyway, thanks for the docs! Using `.` works locally and I wanted this specifically for a smoke test in CI so I'm good. Feel free to close this issue if you don't intend to commit any fixes.
that setup error probably means you have an old version of `virtualenv` which did not understand `pyproject.toml`-based installations (therefore the old version of `pip`)

I'm still not sure how you got to trying `.git` -- none of the docs show anything like that :S
> I'm still not sure how you got to trying `.git`

I explained it above: I *know* that `git` command understands it (any path to a bare Git repo) so I gave it a shot :)



> that setup error probably means you have an old version of `virtualenv`

yep, already fixed by creating a new one.


I think I was confused because I hit multiple unrelated errors and that started clouding my judgments...	
oddly enough, the statement about `repo` appears correct, but `try-repo` has different semantics for local directories:

```console
$ pre-commit  run --all-files
[INFO] Installing environment for /home/asottile/workspace/pre-commit/.git.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
Validate Pre-Commit Manifest.........................(no files to check)Skipped
```
Maybe if you convert that to abspath in try-repo, it'd fix the issue.
this is returning `True` for bare repos, probably because `git diff` makes no sense there: https://github.com/pre-commit/pre-commit/blob/3a72fb39cbe94e945a070c984dc4e3de7f88a4c3/pre_commit/commands/try_repo.py#L30

Resolving patch:
diff --git a/pre_commit/git.py b/pre_commit/git.py
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -141,7 +141,7 @@ def has_diff(*args, **kwargs):
     repo = kwargs.pop('repo', '.')
     assert not kwargs, kwargs
     cmd = ('git', 'diff', '--quiet', '--no-ext-diff') + args
-    return cmd_output_b(*cmd, cwd=repo, retcode=None)[0]
+    return cmd_output_b(*cmd, cwd=repo, retcode=None)[0] == 1
 
 
 def has_core_hookpaths_set():


Test patch:
diff --git a/tests/commands/try_repo_test.py b/tests/commands/try_repo_test.py
--- a/tests/commands/try_repo_test.py
+++ b/tests/commands/try_repo_test.py
@@ -98,6 +98,15 @@ def test_try_repo_relative_path(cap_out, tempdir_factory):
         assert not try_repo(try_repo_opts(relative_repo, hook='bash_hook'))
 
 
+def test_try_repo_bare_repo(cap_out, tempdir_factory):
+    repo = make_repo(tempdir_factory, 'modified_file_returns_zero_repo')
+    with cwd(git_dir(tempdir_factory)):
+        _add_test_file()
+        bare_repo = os.path.join(repo, '.git')
+        # previously crashed attempting modification changes
+        assert not try_repo(try_repo_opts(bare_repo, hook='bash_hook'))
+
+
 def test_try_repo_specific_revision(cap_out, tempdir_factory):
     repo = make_repo(tempdir_factory, 'script_hooks_repo')
     ref = git.head_rev(repo)