import re
import shutil
import os

from coalib.bears.GlobalBear import GlobalBear
from coalib.misc.ContextManagers import change_directory
from coalib.misc.Shell import run_shell_command
from coalib.results.Result import Result


class GitCommitBear(GlobalBear):

    @classmethod
    def check_prerequisites(cls):
        if shutil.which("git") is None:
            return "git is not installed."
        else:
            return True

    def run(self,
            shortlog_length: int=50,
            body_line_length: int=73,
            force_body: bool=False,
            allow_empty_commit_message: bool=False,
            shortlog_regex: str="",
            shortlog_trailing_period: bool=None):
        """
        Checks the current git commit message at HEAD.

        This bear ensures that the shortlog and body do not exceed a given
        line-length and that a newline lies between them.

        :param shortlog_length:            The maximum length of the shortlog.
                                           The shortlog is the first line of
                                           the commit message. The newline
                                           character at end does not count to
                                           the length.
        :param body_line_length:           The maximum line-length of the body.
                                           The newline character at each line
                                           end does not count to the length.
        :param force_body:                 Whether a body shall exist or not.
        :param allow_empty_commit_message: Whether empty commit messages are
                                           allowed or not.
        :param shortlog_regex:             A regex to check the shortlog with.
                                           A full match of this regex is then
                                           required. Passing an empty string
                                           disable the regex-check.
        :param shortlog_trailing_period:   Whether a dot shall be enforced at
                                           the end of the shortlog line.
                                           Providing ``None`` means
                                           "doesn't care".
        """
        with change_directory(self.get_config_dir() or os.getcwd()):
            stdout, stderr = run_shell_command("git log -1 --pretty=%B")

        if stderr:
            self.err("git:", repr(stderr))
            return

        stdout = stdout.rstrip("\n").splitlines()

        if len(stdout) == 0:
            if not allow_empty_commit_message:
                yield Result(self, "HEAD commit has no message.")
            return

        yield from self.check_shortlog(shortlog_length,
                                       shortlog_regex,
                                       shortlog_trailing_period,
                                       stdout[0])
        yield from self.check_body(body_line_length, force_body, stdout[1:])

    def check_shortlog(self,
                       shortlog_length,
                       regex,
                       shortlog_trailing_period,
                       shortlog):
        """
        Checks the given shortlog.

        :param shortlog_length:          The maximum length of the shortlog.
                                         The newline character at end does not
                                         count to the length.
        :param regex:                    A regex to check the shortlog with.
        :param shortlog_trailing_period: Whether a dot shall be enforced at end
                                         end or not (or ``None`` for "don't
                                         care").
        :param shortlog:                 The shortlog message string.
        """
        diff = len(shortlog) - shortlog_length
        if diff > 0:
            yield Result(self,
                         "Shortlog of HEAD commit is {} character(s) longer "
                         "than the limit ({} > {}).".format(
                             diff, len(shortlog), shortlog_length))

        if (shortlog[-1] != ".") == shortlog_trailing_period:
            yield Result(self,
                         "Shortlog of HEAD commit contains no period at end."
                         if shortlog_trailing_period else
                         "Shortlog of HEAD commit contains a period at end.")

        if regex != "":
            match = re.match(regex, shortlog)
            # fullmatch() inside re-module exists sadly since 3.4, but we
            # support 3.3 so we need to check that the regex matched completely
            # ourselves.
            if not match or match.end() != len(shortlog):
                yield Result(
                    self,
                    "Shortlog of HEAD commit does not match given regex.")

    def check_body(self, body_line_length, force_body, body):
        """
        Checks the given commit body.

        :param body_line_length: The maximum line-length of the body. The
                                 newline character at each line end does not
                                 count to the length.
        :param force_body:       Whether a body shall exist or not.
        :param body:             The commit body splitted by lines.
        """
        if len(body) == 0:
            if force_body:
                yield Result(self, "No commit message body at HEAD.")
            return

        if body[0] != "":
            yield Result(self, "No newline between shortlog and body at HEAD.")
            return

        if any(len(line) > body_line_length for line in body[1:]):
            yield Result(self, "Body of HEAD commit contains too long lines.")

bears/vcs/git/GitCommitBear.py
import re
import shutil
import os

from coalib.bears.GlobalBear import GlobalBear
from coalib.misc.ContextManagers import change_directory
from coalib.misc.Shell import run_shell_command
from coalib.results.Result import Result


class GitCommitBear(GlobalBear):

    @classmethod
    def check_prerequisites(cls):
        if shutil.which("git") is None:
            return "git is not installed."
        else:
            return True

    def run(self,
            shortlog_length: int=50,
            body_line_length: int=72,
            force_body: bool=False,
            allow_empty_commit_message: bool=False,
            shortlog_regex: str="",
            shortlog_trailing_period: bool=None):
        """
        Checks the current git commit message at HEAD.

        This bear ensures that the shortlog and body do not exceed a given
        line-length and that a newline lies between them.

        :param shortlog_length:            The maximum length of the shortlog.
                                           The shortlog is the first line of
                                           the commit message. The newline
                                           character at end does not count to
                                           the length.
        :param body_line_length:           The maximum line-length of the body.
                                           The newline character at each line
                                           end does not count to the length.
        :param force_body:                 Whether a body shall exist or not.
        :param allow_empty_commit_message: Whether empty commit messages are
                                           allowed or not.
        :param shortlog_regex:             A regex to check the shortlog with.
                                           A full match of this regex is then
                                           required. Passing an empty string
                                           disable the regex-check.
        :param shortlog_trailing_period:   Whether a dot shall be enforced at
                                           the end of the shortlog line.
                                           Providing ``None`` means
                                           "doesn't care".
        """
        with change_directory(self.get_config_dir() or os.getcwd()):
            stdout, stderr = run_shell_command("git log -1 --pretty=%B")

        if stderr:
            self.err("git:", repr(stderr))
            return

        stdout = stdout.rstrip("\n").splitlines()

        if len(stdout) == 0:
            if not allow_empty_commit_message:
                yield Result(self, "HEAD commit has no message.")
            return

        yield from self.check_shortlog(shortlog_length,
                                       shortlog_regex,
                                       shortlog_trailing_period,
                                       stdout[0])
        yield from self.check_body(body_line_length, force_body, stdout[1:])

    def check_shortlog(self,
                       shortlog_length,
                       regex,
                       shortlog_trailing_period,
                       shortlog):
        """
        Checks the given shortlog.

        :param shortlog_length:          The maximum length of the shortlog.
                                         The newline character at end does not
                                         count to the length.
        :param regex:                    A regex to check the shortlog with.
        :param shortlog_trailing_period: Whether a dot shall be enforced at end
                                         end or not (or ``None`` for "don't
                                         care").
        :param shortlog:                 The shortlog message string.
        """
        diff = len(shortlog) - shortlog_length
        if diff > 0:
            yield Result(self,
                         "Shortlog of HEAD commit is {} character(s) longer "
                         "than the limit ({} > {}).".format(
                             diff, len(shortlog), shortlog_length))

        if (shortlog[-1] != ".") == shortlog_trailing_period:
            yield Result(self,
                         "Shortlog of HEAD commit contains no period at end."
                         if shortlog_trailing_period else
                         "Shortlog of HEAD commit contains a period at end.")

        if regex != "":
            match = re.match(regex, shortlog)
            # fullmatch() inside re-module exists sadly since 3.4, but we
            # support 3.3 so we need to check that the regex matched completely
            # ourselves.
            if not match or match.end() != len(shortlog):
                yield Result(
                    self,
                    "Shortlog of HEAD commit does not match given regex.")

    def check_body(self, body_line_length, force_body, body):
        """
        Checks the given commit body.

        :param body_line_length: The maximum line-length of the body. The
                                 newline character at each line end does not
                                 count to the length.
        :param force_body:       Whether a body shall exist or not.
        :param body:             The commit body splitted by lines.
        """
        if len(body) == 0:
            if force_body:
                yield Result(self, "No commit message body at HEAD.")
            return

        if body[0] != "":
            yield Result(self, "No newline between shortlog and body at HEAD.")
            return

        if any(len(line) > body_line_length for line in body[1:]):
            yield Result(self, "Body of HEAD commit contains too long lines.")

bears/vcs/git/GitCommitBear.py
diff --git a/README.rst b/README.rst
index 1e2d17b8e1..758a714d04 100644
--- a/README.rst
+++ b/README.rst
@@ -40,14 +40,37 @@ USAGE
 
 If you want to learn more about coala-bears, its functionality and its usage,
 please take a look at our
-`tutorial <http://coala.readthedocs.org/en/latest/Users/Tutorials/Writing_Bears.html#what-is-a-bear>`__.
+`tutorial <http://coala.readthedocs.org/en/latest/Users/Tutorials/Tutorial.html>`__.
 
 ABOUT
 =====
 
-coala-bears is a Python package containing all the bears that is used by coala.
+coala-bears is a Python package containing all the bears that are officially
+supported by coala. It features more than **65 bears** covering **32 languages**.
+You can see all of them `here <https://github.com/coala-analyzer/coala/wiki/Bears>`_.
 
-Read more at our `documentation <http://coala.readthedocs.org/en/latest/Users/Tutorials/Writing_Bears.html#guide-to-write-a-bear>`__.
+The list of supported languages includes such famous languages, as:
+
+- C++
+- Go
+- Haskell
+- HTML
+- Java
+- JavaScript
+- Natural Language (English)
+- Python
+- R
+- Ruby
+
+For the full list of supported languages, `visit our wiki <https://github.com/coala-analyzer/coala-bears/wiki/Supported-languages>`__.
+
+The number of bears grows every day! If you want to see any particular
+functionality be sure to submit an issue, but please read the `GETTING INVOLVED`_
+section before doing so.
+
+You can read more at our `documentation <http://coala.readthedocs.org/en/latest/Users/Tutorials/Writing_Bears.html#guide-to-write-a-bear>`__.
+There you can learn how to easily write bears yourself! Be sure to let us know
+if you do so, then we'll be able to include it here and spread the word about it.
 
 Authors
 -------
diff --git a/bears/vcs/git/GitCommitBear.py b/bears/vcs/git/GitCommitBear.py
index 35ae7ffb61..2fd96cc276 100644
--- a/bears/vcs/git/GitCommitBear.py
+++ b/bears/vcs/git/GitCommitBear.py
@@ -19,7 +19,7 @@ def check_prerequisites(cls):
 
     def run(self,
             shortlog_length: int=50,
-            body_line_length: int=73,
+            body_line_length: int=72,
             force_body: bool=False,
             allow_empty_commit_message: bool=False,
             shortlog_regex: str="",
diff --git a/tests/c_languages/codeclone_detection/ClangASTPrintBearTest.py b/tests/c_languages/codeclone_detection/ClangASTPrintBearTest.py
index 099d8367c9..dc079fc618 100644
--- a/tests/c_languages/codeclone_detection/ClangASTPrintBearTest.py
+++ b/tests/c_languages/codeclone_detection/ClangASTPrintBearTest.py
@@ -52,6 +52,6 @@ def test_ast(self):
         ast = "\n"
         # Only check beginning of AST
         for i in range(expected_ast.count("\n")-1):
-            ast += self.queue.get(timeout=0).message + "\n"
+            ast += self.queue.get(timeout=0.1).message + "\n"
 
         self.assertEqual(ast, expected_ast)
diff --git a/tests/js/ESLintBearTest.py b/tests/js/ESLintBearTest.py
index 8298276b1b..b88ebbe498 100644
--- a/tests/js/ESLintBearTest.py
+++ b/tests/js/ESLintBearTest.py
@@ -36,4 +36,4 @@
 
 ESLintBearWithoutConfig = verify_local_bear(ESLintBear,
                                             valid_files=(test_good, test_bad),
-                                            invalid_files=(),)
+                                            invalid_files=())
diff --git a/tests/vcs/git/GitCommitBearTest.py b/tests/vcs/git/GitCommitBearTest.py
index 3f8fc5e99e..497ba83960 100644
--- a/tests/vcs/git/GitCommitBearTest.py
+++ b/tests/vcs/git/GitCommitBearTest.py
@@ -170,7 +170,7 @@ def test_body_checks(self):
         self.git_commit(
             "Commits message with a body\n\n"
             "nearly exceeding the default length of a body, but not quite. "
-            "haaaaaaands")
+            "haaaaaands")
 
         self.assertEqual(self.run_uut(), [])
         self.assertTrue(self.msg_queue.empty())

GitCommitBear: body_line_length shall be 72 and not 73
The standard git commit guidelines suggest wrapping the body at 72 chars. So just replace the default of 73 with 72.

