Initial commit.
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..418c1f1
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,19 @@
+[report]
+exclude_lines =
+    # Don't complain about defensive assertions
+    raise NotImplementedError
+    raise AssertionError
+
+    # Don't complain about non-runnable code
+    if __name__ == .__main__.:
+
+omit =
+    /usr/*
+    py_env/*
+    */__init__.py
+
+    # Ignore test coverage
+    tests/*
+
+    # Don't complain about our pre-commit file
+    pre-commit.py
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fe21114
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.pyc
+.pydevproject
+.project
+.coverage
+/py_env
+*.db
+.idea
+build
+dist
+*.egg-info
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6e98e9d
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+language: python
+
+python:
+    - 2.6
+    - 2.7
+
+install: pip install virtualenv
+script: make
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..62667e1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,40 @@
+
+TEST_TARGETS =
+ITEST_TARGETS = -m integration
+UTEST_TARGETS = -m "not(integration)"
+
+all: _tests
+
+integration:
+	$(eval TEST_TARGETS := $(ITEST_TARGETS))
+
+unit:
+	$(eval TEST_TARGETS := $(UTEST_TARGETS))
+
+utests: test
+utest: test
+tests: test
+test: unit _tests
+itests: itest
+itest: integration _tests
+
+_tests: py_env
+	bash -c 'source py_env/bin/activate && py.test tests $(TEST_TARGETS)'
+
+ucoverage: unit coverage
+icoverage: integration coverage
+
+coverage: py_env
+	bash -c 'source py_env/bin/activate && \
+		coverage erase && \
+		coverage run `which py.test` tests $(TEST_TARGETS) && \
+		coverage report -m'
+
+py_env: requirements.txt
+	rm -rf py_env
+	virtualenv py_env
+	bash -c 'source py_env/bin/activate && \
+		pip install -r requirements.txt'
+
+clean:
+	rm -rf py_env
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f6d50bb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+pre-commit-hooks
+==========
+
+Some out-of-the-box hooks for pre-commit.
+
+See also: https://github.com/asottile/pre-commit
diff --git a/pre-commit.py b/pre-commit.py
new file mode 100755
index 0000000..5fd8c73
--- /dev/null
+++ b/pre-commit.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python
+
+import collections
+import optparse
+import os
+import os.path
+import shutil
+import subprocess
+import sys
+
+def __backport_check_output():
+    def check_output(*popenargs, **kwargs):
+        r"""Run command with arguments and return its output as a byte string.
+
+        Backported from Python 2.7 as it's implemented as pure python on stdlib.
+
+        >>> check_output(['/usr/bin/python', '--version'])
+        Python 2.6.2
+        """
+        process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
+        output, unused_err = process.communicate()
+        retcode = process.poll()
+        if retcode:
+            cmd = kwargs.get("args")
+            if cmd is None:
+                cmd = popenargs[0]
+            error = subprocess.CalledProcessError(retcode, cmd)
+            error.output = output
+            raise error
+        return output
+
+    if not hasattr(subprocess, 'check_output'):
+        setattr(subprocess, 'check_output', check_output)
+
+__backport_check_output()
+del __backport_check_output
+
+
+FILE_LIST = 'git ls-files | egrep "\.%s$"'
+
+ALL_FILES = FILE_LIST % '(php|sql|py|js|htm|c|cpp|h|sh|css)'
+JS_FILES = FILE_LIST % 'js'
+PY_FILES = FILE_LIST % 'py'
+CPP_FILES = FILE_LIST % '(cc|cpp|h)'
+C_FILES = FILE_LIST % '(c|h)'
+C_LIKE_FILES = FILE_LIST % '(c|cc|cpp|h)'
+HEADER_FILES = FILE_LIST % 'h'
+
+RED = '\033[41m'
+GREEN = '\033[42m'
+NORMAL = '\033[0m'
+COLS = int(subprocess.check_output(['tput', 'cols']))
+
+Test = collections.namedtuple('Test', ['command', 'name', 'nonzero', 'config'])
+
+TESTS = [
+    Test(
+        "%s | xargs pyflakes" % PY_FILES,
+        'Py    - Pyflakes',
+        False, 'testpyflakes',
+    ),
+    Test(
+        "%s | xargs grep 'import\sipdb'" % PY_FILES,
+        'Py    - ipdb',
+        True, 'testipdb',
+    ),
+    Test(
+        "%s | grep 'tests' | grep -v '_test.py$' | grep -v '__init__.py' | grep -v '/conftest.py'" % PY_FILES,
+        'Py    - Test files should end in _test.py',
+        True, 'testtestnames',
+    ),
+    Test(
+        "%s | xargs egrep 'split\(.\\\\n.\)'" % PY_FILES,
+        'Py    - Use s.splitlines over s.split',
+        True, 'testsplitlines',
+    ),
+    Test(
+        "%s | xargs grep -H -n -P '\t'" % ALL_FILES,
+        "All   - No tabs",
+        True, 'testtabs',
+    ),
+    Test(
+        "make test",
+        "Py    - Tests",
+        False, 'testtests',
+    ),
+]
+
+def get_git_config(config_name):
+    config_result = ''
+    try:
+        config_result = subprocess.check_output([
+            'git', 'config', config_name
+        ])
+    except subprocess.CalledProcessError: pass
+
+    return config_result.strip()
+
+def get_pre_commit_path():
+    git_top = subprocess.check_output(
+        ['git', 'rev-parse', '--show-toplevel']
+    ).strip()
+    return os.path.join(git_top, '.git/hooks/pre-commit')
+
+class FixAllBase(object):
+    name = None
+    matching_files_command = None
+
+    def get_all_files(self):
+        try:
+            files = subprocess.check_output(
+                self.matching_files_command,
+                shell=True,
+            )
+            files_split = files.splitlines()
+            return [file.strip() for file in files_split]
+        except subprocess.CalledProcessError:
+            return []
+
+    def fix_file(self, file):
+        '''Implement to fix the file.'''
+        raise NotImplementedError
+
+    def run(self):
+        '''Runs the process to fix the files. Returns True if nothign to fix.'''
+        print '%s...' % self.name
+        all_files = self.get_all_files()
+        for file in all_files:
+            print 'Fixing %s' % file
+            self.fix_file(file)
+        return not all_files
+
+class FixTrailingWhitespace(FixAllBase):
+    name = 'Trimming trailing whitespace'
+    matching_files_command = '%s | xargs egrep -l "[[:space:]]$"' % ALL_FILES
+
+    def fix_file(self, file):
+        subprocess.check_call(['sed', '-i', '-e', 's/[[:space:]]*$//', file])
+
+class FixLineEndings(FixAllBase):
+    name = 'Fixing line endings'
+    matching_files_command = "%s | xargs egrep -l $'\\r'\\$" % ALL_FILES
+
+    def fix_file(self, file):
+        subprocess.check_call(['dos2unix', file])
+        subprocess.check_call(['mac2unix', file])
+
+FIXERS = [
+    FixTrailingWhitespace,
+    FixLineEndings,
+]
+
+def run_tests():
+    passed = True
+    for test in TESTS:
+        run_test = get_git_config('hooks.%s' % test.config)
+        if run_test == 'false':
+            print 'Skipping "%s" due to git config.' % test.name
+            continue
+
+        try:
+            retcode = 0
+            output = subprocess.check_output(
+                test.command, shell=True, stderr=subprocess.STDOUT
+            )
+        except subprocess.CalledProcessError as e:
+            retcode = e.returncode
+            output = e.output
+
+        pass_fail = '%sSuccess%s' % (GREEN, NORMAL)
+        failed_test = False
+        if (retcode and not test.nonzero) or (not retcode and test.nonzero):
+            pass_fail = '%sFailure(%s)%s' % (RED, retcode, NORMAL)
+            failed_test = True
+
+        dots = COLS - len(pass_fail) - len(test.name)
+        print '%s%s%s' % (test.name, '.' * dots, pass_fail)
+
+        if failed_test:
+            print
+            print output
+            passed = False
+
+    return passed
+
+if __name__ == '__main__':
+    parser = optparse.OptionParser()
+    parser.add_option(
+        '-u', '--uninstall',
+        action='store_true', dest='uninstall', default=False,
+        help='Uninstall pre-commit script.'
+    )
+    parser.add_option(
+        '-i', '--install',
+        action='store_true', dest='install', default=False,
+        help='Install pre-commit script.'
+    )
+    opts, args = parser.parse_args()
+
+    if opts.install:
+        pre_commit_path = get_pre_commit_path()
+        shutil.copyfile(__file__, pre_commit_path)
+        os.chmod(pre_commit_path, 0755)
+        print 'Installed pre commit to %s' % pre_commit_path
+        sys.exit(0)
+    elif opts.uninstall:
+        pre_commit_path = get_pre_commit_path()
+        if os.path.exists(pre_commit_path):
+            os.remove(pre_commit_path)
+        print 'Removed pre-commit scripts.'
+
+    passed = True
+    for fixer in FIXERS:
+        passed &= fixer().run()
+    passed &= run_tests()
+
+    if not passed:
+        print '%sFailures / Fixes detected.%s' % (RED, NORMAL)
+        print 'Please fix and commit again.'
+        print "You could also pass --no-verify, but you probably shouldn't."
+        print
+        print "Here's git status for convenience: "
+        print
+        os.system('git status')
+        sys.exit(-1)
diff --git a/pre_commit_hooks/__init__.py b/pre_commit_hooks/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pre_commit_hooks/__init__.py
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..c2671ad
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,10 @@
+argparse
+pyyaml
+simplejson
+
+# Testing requirements
+coverage
+ipdb
+mock
+pyflakes
+pytest
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..41dc28e
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,12 @@
+from setuptools import find_packages
+from setuptools import setup
+
+setup(
+    name='pre_commit_hooks',
+    version='0.0.0',
+    packages=find_packages('.', exclude=('tests*', 'testing*')),
+    install_requires=[
+        'argparse',
+        'simplejson',
+    ],
+)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py