Teach check-large-files-added about git-lfs. Reslves #82.
diff --git a/.coveragerc b/.coveragerc
index 97e9277..714aaa1 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -7,6 +7,7 @@
/usr/*
*/tmp*
setup.py
+ get-git-lfs.py
[report]
exclude_lines =
diff --git a/.travis.yml b/.travis.yml
index e870620..6a0aaa0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,6 +12,8 @@
before_install:
- git config --global user.name "Travis CI"
- git config --global user.email "user@example.com"
+ # Install git-lfs for a test
+ - './get-git-lfs.py && export PATH="/tmp/git-lfs:$PATH"'
after_success:
- coveralls
sudo: false
@@ -19,3 +21,4 @@
directories:
- $HOME/.cache/pip
- $HOME/.pre-commit
+ - /tmp/git-lfs
diff --git a/get-git-lfs.py b/get-git-lfs.py
new file mode 100755
index 0000000..f71b5e2
--- /dev/null
+++ b/get-git-lfs.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3.4
+"""This is a script to install git-lfs to a tempdir for use in tests"""
+import io
+import os.path
+import shutil
+import tarfile
+from urllib.request import urlopen
+
+DOWNLOAD_PATH = (
+ 'https://github.com/github/git-lfs/releases/download/'
+ 'v1.1.0/git-lfs-linux-amd64-1.1.0.tar.gz'
+)
+PATH_IN_TAR = 'git-lfs-1.1.0/git-lfs'
+DEST_PATH = '/tmp/git-lfs/git-lfs'
+DEST_DIR = os.path.dirname(DEST_PATH)
+
+
+def main():
+ if (
+ os.path.exists(DEST_PATH) and
+ os.path.isfile(DEST_PATH) and
+ os.access(DEST_PATH, os.X_OK)
+ ):
+ print('Already installed!')
+ return 0
+
+ shutil.rmtree(DEST_DIR, ignore_errors=True)
+ os.makedirs(DEST_DIR, exist_ok=True)
+
+ contents = io.BytesIO(urlopen(DOWNLOAD_PATH).read())
+ with tarfile.open(fileobj=contents) as tar:
+ with tar.extractfile(PATH_IN_TAR) as src_file:
+ with open(DEST_PATH, 'wb') as dest_file:
+ shutil.copyfileobj(src_file, dest_file)
+ os.chmod(DEST_PATH, 0o755)
+
+if __name__ == '__main__':
+ exit(main())
diff --git a/pre_commit_hooks/check_added_large_files.py b/pre_commit_hooks/check_added_large_files.py
index 973b958..3a53fe3 100644
--- a/pre_commit_hooks/check_added_large_files.py
+++ b/pre_commit_hooks/check_added_large_files.py
@@ -9,12 +9,34 @@
import sys
from pre_commit_hooks.util import added_files
+from pre_commit_hooks.util import CalledProcessError
+from pre_commit_hooks.util import cmd_output
+
+
+def lfs_files():
+ try: # pragma: no cover (no git-lfs)
+ lines = cmd_output('git', 'lfs', 'status', '--porcelain').splitlines()
+ except CalledProcessError:
+ lines = []
+
+ modes_and_fileparts = [
+ (line[:3].strip(), line[3:].rpartition(' ')[0]) for line in lines
+ ]
+
+ def to_file_part(mode, filepart):
+ assert mode in ('A', 'R')
+ return filepart if mode == 'A' else filepart.split(' -> ')[1]
+
+ return set(
+ to_file_part(mode, filepart) for mode, filepart in modes_and_fileparts
+ if mode in ('A', 'R')
+ )
def find_large_added_files(filenames, maxkb):
# Find all added files that are also in the list of files pre-commit tells
# us about
- filenames = added_files() & set(filenames)
+ filenames = (added_files() & set(filenames)) - lfs_files()
retv = 0
for filename in filenames:
diff --git a/pre_commit_hooks/util.py b/pre_commit_hooks/util.py
index bacb242..269b553 100644
--- a/pre_commit_hooks/util.py
+++ b/pre_commit_hooks/util.py
@@ -21,7 +21,9 @@
popen_kwargs.update(kwargs)
proc = subprocess.Popen(cmd, **popen_kwargs)
stdout, stderr = proc.communicate()
- stdout, stderr = stdout.decode('UTF-8'), stderr.decode('UTF-8')
+ stdout = stdout.decode('UTF-8')
+ if stderr is not None:
+ stderr = stderr.decode('UTF-8')
if retcode is not None and proc.returncode != retcode:
raise CalledProcessError(cmd, retcode, proc.returncode, stdout, stderr)
return stdout
diff --git a/tests/check_added_large_files_test.py b/tests/check_added_large_files_test.py
index b49f118..caf3e68 100644
--- a/tests/check_added_large_files_test.py
+++ b/tests/check_added_large_files_test.py
@@ -1,6 +1,10 @@
from __future__ import absolute_import
from __future__ import unicode_literals
+import subprocess
+
+import pytest
+
from pre_commit_hooks.check_added_large_files import find_large_added_files
from pre_commit_hooks.check_added_large_files import main
from pre_commit_hooks.util import cmd_output
@@ -62,3 +66,40 @@
# Should fail with --maxkb
assert main(argv=['--maxkb', '9', 'f.py']) == 1
+
+
+def has_gitlfs():
+ output = cmd_output('git', 'lfs', retcode=None, stderr=subprocess.STDOUT)
+ return 'git lfs status' in output
+
+
+xfailif_no_gitlfs = pytest.mark.xfail(
+ not has_gitlfs(), reason='This test requires git-lfs',
+)
+
+
+@xfailif_no_gitlfs
+def test_allows_gitlfs(temp_git_dir): # pragma: no cover
+ with cwd(temp_git_dir):
+ # Work around https://github.com/github/git-lfs/issues/913
+ cmd_output('git', 'commit', '--allow-empty', '-m', 'foo')
+ cmd_output('git', 'lfs', 'install')
+ write_file('f.py', 'a' * 10000)
+ cmd_output('git', 'lfs', 'track', 'f.py')
+ cmd_output('git', 'add', '.')
+ # Should succeed
+ assert main(('--maxkb', '9', 'f.py')) == 0
+
+
+@xfailif_no_gitlfs
+def test_moves_with_gitlfs(temp_git_dir): # pragma: no cover
+ with cwd(temp_git_dir):
+ cmd_output('git', 'lfs', 'install')
+ cmd_output('git', 'lfs', 'track', 'a.bin', 'b.bin')
+ # First add the file we're going to move
+ write_file('a.bin', 'a' * 10000)
+ cmd_output('git', 'add', '.')
+ cmd_output('git', 'commit', '-am', 'foo')
+ # Now move it and make sure the hook still succeeds
+ cmd_output('git', 'mv', 'a.bin', 'b.bin')
+ assert main(('--maxkb', '9', 'b.bin')) == 0