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:

Patch:
diff --git a/pre_commit/git.py b/pre_commit/git.py
index 4bf2823..6264529 100644
--- 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
index 51d5f8c..aa21880 100644
--- 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'),
     (