Repository: pre-commit/pre-commit

Issue:
Fails when force pushing unrelated histories
pre-commit looks to find difference between remote and local branches as pre-commit wants to know the files changed. But in case of force, git correctly fails to generate diff. 

Expected: pre-commit can run all the hooks that has pass-filename=false as they do not require diffs.

Thanks for the awesome stuff!


Discussion:
I can't reproduce, please provide an example as force pushing is working fine for me
The forced push is on unrelated heads and so git cannot get the difference. Below is my output...

```console
git.exe push -v --progress --force "github" master:main
Pushing to https://github.com/bogusfocused/ha_lan_smartthings.git
An unexpected error has occurred: CalledProcessError: command: ('C:\\Program Files\\Git\\mingw64\\libexec\\git-core
\\git.EXE', 'diff', '--name-only', '--no-ext-diff', '-z', 
'8e9e6537d911cbfa8a88aa608c3afa893a340f8c...ced7786cd5098f321b48e9a1027cce4e8938cbbf')
return code: 128
expected return code: 0
stdout: (none)
stderr:
fatal: 8e9e6537d911cbfa8a88aa608c3afa893a340f8c...ced7786cd5098f321b48e9a1027cce4e8938cbbf: no merge base

Check the log at C:\Users\Rohit\.cache\pre-commit\pre-commit.log
error: failed to push some refs to 'https://github.com/bogusfocused/ha_lan_smartthings.git'


git did not exit cleanly (exit code 1) (1672 ms @ 8/9/2021 12:48:56 PM)
```
ah yeah if you're force pushing unrelated histories then that'll happen -- I'm not sure there's much that could be done in that case since it's really "push everything"
hmmm actually I still can't reproduce -- what version of `git` are you using?

Resolving patch:
diff --git a/pre_commit/git.py b/pre_commit/git.py
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -155,12 +155,15 @@ def get_all_files() -> List[str]:
 
 
 def get_changed_files(old: str, new: str) -> List[str]:
-    return zsplit(
-        cmd_output(
-            'git', 'diff', '--name-only', '--no-ext-diff', '-z',
-            f'{old}...{new}',
-        )[1],
-    )
+    diff_cmd = ('git', 'diff', '--name-only', '--no-ext-diff', '-z')
+    try:
+        _, out, _ = cmd_output(*diff_cmd, f'{old}...{new}')
+    except CalledProcessError:  # pragma: no cover (new git)
+        # on newer git where old and new do not have a merge base git fails
+        # so we try a full diff (this is what old git did for us!)
+        _, out, _ = cmd_output(*diff_cmd, f'{old}..{new}')
+
+    return zsplit(out)
 
 
 def head_rev(remote: str) -> str:


Test patch:
diff --git a/tests/git_test.py b/tests/git_test.py
--- a/tests/git_test.py
+++ b/tests/git_test.py
@@ -139,6 +139,24 @@ def test_get_changed_files(in_git_dir):
     assert files == []
 
 
+def test_get_changed_files_disparate_histories(in_git_dir):
+    """in modern versions of git, `...` does not fall back to full diff"""
+    git_commit()
+    in_git_dir.join('a.txt').ensure()
+    cmd_output('git', 'add', '.')
+    git_commit()
+    cmd_output('git', 'branch', '-m', 'branch1')
+
+    cmd_output('git', 'checkout', '--orphan', 'branch2')
+    cmd_output('git', 'rm', '-rf', '.')
+    in_git_dir.join('a.txt').ensure()
+    in_git_dir.join('b.txt').ensure()
+    cmd_output('git', 'add', '.')
+    git_commit()
+
+    assert git.get_changed_files('branch1', 'branch2') == ['b.txt']
+
+
 @pytest.mark.parametrize(
     ('s', 'expected'),
     (