diff --git a/extras/git.pre-commit b/extras/git.pre-commit
new file mode 100755
index 0000000000000000000000000000000000000000..5a627198950f191e4c2c64fe44d23cf170cfcf50
--- /dev/null
+++ b/extras/git.pre-commit
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+
+# Adapted from: http://tech.yipit.com/2011/11/16/183772396/
+# Changes by Wibowo Arindrarto
+# Changes:
+#   - Allow code modification by linters to be comitted
+#   - Updated CHECKS
+#   - Python 3 calls + code style updates
+#
+# Usage: save this file into your .git/hooks directory as `pre-commit`
+# and set it to executable
+
+import os
+import re
+import subprocess
+import sys
+
+modified = re.compile(r"^[MA]\s+(?P<name>.*)$")
+
+CHECKS = [
+    {
+        "exe": "scalariform",
+        "output": "Formatting code with scalariform ...",
+        # Remove lines without filenames
+        "command": "scalariform -s=2.11.1 -p=scalariformStyle.properties --quiet %s",
+        "match_files": [".*scala$"],
+        "print_filename": False,
+        "commit_changes": True,
+    },
+]
+
+
+def matches_file(file_name, match_files):
+    return any(re.compile(match_file).match(file_name) for match_file
+            in match_files)
+
+
+def check_files(files, check):
+    result = 0
+    print(check["output"])
+    for file_name in files:
+
+        if not "match_files" in check or \
+                matches_file(file_name, check["match_files"]):
+
+            if not "ignore_files" in check or \
+                    not matches_file(file_name, check["ignore_files"]):
+
+                process = subprocess.Popen(check["command"] % file_name,
+                        stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                        shell=True)
+
+                out, err = process.communicate()
+                if out or err:
+                    if check["print_filename"]:
+                        prefix = "\t%s:" % file_name
+                    else:
+                        prefix = "\t"
+                    output_lines = ["%s%s" % (prefix, line) for 
+                            line in out.splitlines()]
+                    print("\n".join(output_lines))
+                    if err:
+                        print(err)
+                    result = 1
+                elif check["commit_changes"]:
+                    p = subprocess.Popen(["git", "add", file_name],
+                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+                    p.communicate()
+    return result
+
+
+def main(all_files):
+    # Check that the required linters and code checkers are all present
+    for check in CHECKS:
+        p = subprocess.Popen(["which", check["exe"]], stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE)
+        out, err = p.communicate()
+        if not out:
+            print("Required commit hook executable '%s' not found." % check["exe"])
+            sys.exit(1)
+
+    # Stash any changes to the working tree that are not going to be committed
+    subprocess.call(["git", "stash", "-u", "--keep-index"], stdout=subprocess.PIPE)
+
+    files = []
+    if all_files:
+        for root, dirs, file_names in os.walk("."):
+            for file_name in file_names:
+                files.append(os.path.join(root, file_name))
+    else:
+        p = subprocess.Popen(["git", "status", "--porcelain"],
+                stdout=subprocess.PIPE)
+        out, err = p.communicate()
+        for line in out.splitlines():
+            match = modified.match(line)
+            if match:
+                files.append(match.group("name"))
+
+    result = 0
+    for check in CHECKS:
+        result = check_files(files, check) or result
+
+    # Strategy:
+    #   - Check if the linters made any changes
+    #   - If there are no changes, pop the stash and commit
+    #   - Otherwise:
+    #       - Stash the change
+    #       - Pop stash@{1}
+    #       - Checkout stash@{0}
+    #       - Drop stash@{0} (cannot pop directly since stash may conflict)
+    #       - Commit
+    # This is because the initial stash will conflict with any possible
+    # changes made by the linters
+    p = subprocess.Popen(["git", "status", "--porcelain"],
+            stdout=subprocess.PIPE)
+    out, err = p.communicate()
+    if not out.strip():
+        subprocess.call(["git", "stash", "pop"],
+                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    else:
+        subprocess.call(["git", "stash"],
+                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        subprocess.call(["git", "stash", "pop", "--quiet", "--index", "stash@{1}"],
+                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        subprocess.call(["git", "checkout", "stash", "--", "."],
+                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        subprocess.call(["git", "stash", "drop"],
+                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    sys.exit(result)
+
+
+if __name__ == "__main__":
+
+    all_files = False
+
+    if len(sys.argv) > 1 and sys.argv[1] == "--all-files":
+        all_files = True
+
+    main(all_files)