summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--git/config.py2
-rw-r--r--git/objects/commit.py22
-rw-r--r--test/test_commit.py15
-rw-r--r--test/test_util.py7
5 files changed, 46 insertions, 1 deletions
diff --git a/AUTHORS b/AUTHORS
index ef414e31..97e14789 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -48,4 +48,5 @@ Contributors are:
-Hiroki Tokunaga <tokusan441 _at_ gmail.com>
-Julien Mauroy <pro.julien.mauroy _at_ gmail.com>
-Patrick Gerard
+-Luke Twist <itsluketwist@gmail.com>
Portions derived from other open source works and are clearly marked.
diff --git a/git/config.py b/git/config.py
index 5f07cb00..71d7ea68 100644
--- a/git/config.py
+++ b/git/config.py
@@ -84,7 +84,7 @@ CONFIG_LEVELS: ConfigLevels_Tup = ("system", "user", "global", "repository")
CONDITIONAL_INCLUDE_REGEXP = re.compile(r"(?<=includeIf )\"(gitdir|gitdir/i|onbranch):(.+)\"")
-class MetaParserBuilder(abc.ABCMeta):
+class MetaParserBuilder(abc.ABCMeta): # noqa: B024
"""Utility class wrapping base-class methods into decorators that assure read-only properties"""
def __new__(cls, name: str, bases: Tuple, clsdict: Dict[str, Any]) -> "MetaParserBuilder":
diff --git a/git/objects/commit.py b/git/objects/commit.py
index 66cb9191..cf7d9aaa 100644
--- a/git/objects/commit.py
+++ b/git/objects/commit.py
@@ -4,6 +4,7 @@
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
import datetime
+import re
from subprocess import Popen, PIPE
from gitdb import IStream
from git.util import hex_to_bin, Actor, Stats, finalize_process
@@ -738,3 +739,24 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
return self
# } END serializable implementation
+
+ @property
+ def co_authors(self) -> List[Actor]:
+ """
+ Search the commit message for any co-authors of this commit.
+ Details on co-authors: https://github.blog/2018-01-29-commit-together-with-co-authors/
+
+ :return: List of co-authors for this commit (as Actor objects).
+ """
+ co_authors = []
+
+ if self.message:
+ results = re.findall(
+ r"^Co-authored-by: (.*) <(.*?)>$",
+ self.message,
+ re.MULTILINE,
+ )
+ for author in results:
+ co_authors.append(Actor(*author))
+
+ return co_authors
diff --git a/test/test_commit.py b/test/test_commit.py
index 82126987..c5a43c94 100644
--- a/test/test_commit.py
+++ b/test/test_commit.py
@@ -509,3 +509,18 @@ JzJMZDRLQLFvnzqZuCjE
assert KEY_1 not in commit.trailers.keys()
assert KEY_2 in commit.trailers.keys()
assert commit.trailers[KEY_2] == VALUE_2
+
+ def test_commit_co_authors(self):
+ commit = copy.copy(self.rorepo.commit("4251bd5"))
+ commit.message = """Commit message
+
+Co-authored-by: Test User 1 <602352+test@users.noreply.github.com>
+Co-authored-by: test_user_2 <another_user-email@github.com>
+Co_authored_by: test_user_x <test@github.com>
+Co-authored-by: test_user_y <test@github.com> text
+Co-authored-by: test_user_3 <test_user_3@github.com>"""
+ assert commit.co_authors == [
+ Actor("Test User 1", "602352+test@users.noreply.github.com"),
+ Actor("test_user_2", "another_user-email@github.com"),
+ Actor("test_user_3", "test_user_3@github.com"),
+ ]
diff --git a/test/test_util.py b/test/test_util.py
index eb016189..90dd89a9 100644
--- a/test/test_util.py
+++ b/test/test_util.py
@@ -6,11 +6,13 @@
import os
import pickle
+import sys
import tempfile
import time
from unittest import mock, skipIf
from datetime import datetime
+import pytest
import ddt
from git.cmd import dashify
@@ -154,6 +156,11 @@ class TestUtils(TestBase):
lock_file._obtain_lock_or_raise()
lock_file._release_lock()
+ @pytest.mark.xfail(
+ sys.platform == "cygwin",
+ reason="Cygwin fails here for some reason, always",
+ raises=AssertionError
+ )
def test_blocking_lock_file(self):
my_file = tempfile.mktemp()
lock_file = BlockingLockFile(my_file)