1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2011 The Chromium OS Authors.
3#
4
5import collections
6import re
7
8# Separates a tag: at the beginning of the subject from the rest of it
9re_subject_tag = re.compile('([^:\s]*):\s*(.*)')
10
11class Commit:
12    """Holds information about a single commit/patch in the series.
13
14    Args:
15        hash: Commit hash (as a string)
16
17    Variables:
18        hash: Commit hash
19        subject: Subject line
20        tags: List of maintainer tag strings
21        changes: Dict containing a list of changes (single line strings).
22            The dict is indexed by change version (an integer)
23        cc_list: List of people to aliases/emails to cc on this commit
24        notes: List of lines in the commit (not series) notes
25        change_id: the Change-Id: tag that was stripped from this commit
26            and can be used to generate the Message-Id.
27        rtags: Response tags (e.g. Reviewed-by) collected by the commit, dict:
28            key: rtag type (e.g. 'Reviewed-by')
29            value: Set of people who gave that rtag, each a name/email string
30        warn: List of warnings for this commit, each a str
31    """
32    def __init__(self, hash):
33        self.hash = hash
34        self.subject = None
35        self.tags = []
36        self.changes = {}
37        self.cc_list = []
38        self.signoff_set = set()
39        self.notes = []
40        self.change_id = None
41        self.rtags = collections.defaultdict(set)
42        self.warn = []
43
44    def __str__(self):
45        return self.subject
46
47    def AddChange(self, version, info):
48        """Add a new change line to the change list for a version.
49
50        Args:
51            version: Patch set version (integer: 1, 2, 3)
52            info: Description of change in this version
53        """
54        if not self.changes.get(version):
55            self.changes[version] = []
56        self.changes[version].append(info)
57
58    def CheckTags(self):
59        """Create a list of subject tags in the commit
60
61        Subject tags look like this:
62
63            propounder: fort: Change the widget to propound correctly
64
65        Here the tags are propounder and fort. Multiple tags are supported.
66        The list is updated in self.tag.
67
68        Returns:
69            None if ok, else the name of a tag with no email alias
70        """
71        str = self.subject
72        m = True
73        while m:
74            m = re_subject_tag.match(str)
75            if m:
76                tag = m.group(1)
77                self.tags.append(tag)
78                str = m.group(2)
79        return None
80
81    def AddCc(self, cc_list):
82        """Add a list of people to Cc when we send this patch.
83
84        Args:
85            cc_list:    List of aliases or email addresses
86        """
87        self.cc_list += cc_list
88
89    def CheckDuplicateSignoff(self, signoff):
90        """Check a list of signoffs we have send for this patch
91
92        Args:
93            signoff:    Signoff line
94        Returns:
95            True if this signoff is new, False if we have already seen it.
96        """
97        if signoff in self.signoff_set:
98          return False
99        self.signoff_set.add(signoff)
100        return True
101
102    def AddRtag(self, rtag_type, who):
103        """Add a response tag to a commit
104
105        Args:
106            key: rtag type (e.g. 'Reviewed-by')
107            who: Person who gave that rtag, e.g. 'Fred Bloggs <fred@bloggs.org>'
108        """
109        self.rtags[rtag_type].add(who)
110