aboutsummaryrefslogtreecommitdiffhomepage
path: root/gbp/dch.py
blob: 3dfa8cfcce7c20d5eb428d78b0bd768f4eec9fab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# vim: set fileencoding=utf-8 :
#
# (C) 2010 Rob Browning <rlb@defaultvalue.org>
"""provides git-dch helpers"""

import re

MAX_CHANGELOG_LINE_LENGTH = 76

def extract_git_dch_cmds(lines, options):
    """Return a dictionary of all Git-Dch: commands found in lines.
    The command keys will be lowercased, i.e. {'ignore' : True,
    'short': True}.  For now, all the options are binary.  Also return
    all of the lines that do not contain Git-Dch: commands."""
    commands = {}
    other_lines = []
    for line in lines:
        if line.startswith('Git-Dch: '):
            cmd = line.split(' ', 1)[1].strip().lower()
            commands[cmd] = True
        else:
            other_lines.append(line)
    return (commands, other_lines)


def filter_ignore_rx_matches(lines, options):
    """Filter any lines that match options.ignore_regex
    (i.e. --ignore-regex)."""
    if options.ignore_regex:
        ignore_re = re.compile(options.ignore_regex)
        return [line for line in lines if not ignore_re.match(line)]
    else:
        return lines


_bug_r = r'(?:bug|issue)?\#?\s?\d+'
_bug_re = re.compile(_bug_r, re.I)

def extract_bts_cmds(lines, opts):
    """Return a dictionary of the bug tracking system commands
    contained in the the given lines.  i.e. {'closed' : [1], 'fixed':
    [3, 4]}.  Right now, this will only notice a single directive
    clause on a line.  Also return all of the lines that do not
    contain bug tracking system commands."""
    bts_rx = re.compile(r'(?P<bts>%s):\s+%s' % (opts.meta_closes, _bug_r), re.I)
    commands = {}
    other_lines = []
    for line in lines:
        m = bts_rx.match(line)
        if m:
            bug_nums = [ bug.strip() for bug in _bug_re.findall(line, re.I) ]
            try:
                commands[m.group('bts')] += bug_nums
            except KeyError:
                commands[m.group('bts')] = bug_nums
        else:
            other_lines.append(line)
    return (commands, other_lines)


def extract_thanks_info(lines, options):
    """Return a list of all of the Thanks: entries, and a list of all
    of the lines that do not contain Thanks: entries."""
    thanks = []
    other_lines = []
    for line in lines:
        if line.startswith('Thanks: '):
            thanks.append(line.split(' ', 1)[1].strip())
        else:
            other_lines.append(line)
    return (thanks, other_lines)


def _ispunct(ch):
    return not ch.isalnum() and not ch.isspace()


def terminate_first_line_if_needed(lines):
    """Terminate the first line of lines with a '.' if multi-line."""
    # Don't add a period to empty or one line commit messages.
    if len(lines) < 2:
        return lines
    if lines[0] and _ispunct(lines[0][-1]):
        return lines
    if lines[1] and (_ispunct(lines[1][0]) or lines[1][0].islower()):
        return lines
    return [lines[0] + "."] + lines[1:]


def format_changelog_entry(commit_info, options, last_commit=False):
    """Return a list of lines (without newlines) as the changelog
    entry for commit_info (generated by
    GitRepository.get_commit_info()).  If last_commit is not False,
    then this entry is the last one in the series."""
    entry = [commit_info['subject']]
    body = commit_info['body']
    commitid = commit_info['id']
    (git_dch_cmds, body) = extract_git_dch_cmds(body, options)

    if 'ignore' in git_dch_cmds:
        return None
    if options.idlen:
        entry[0] = '[%s] ' % commitid[0:options.idlen] + entry[0]

    (bts_cmds, body) = extract_bts_cmds(body, options)
    (thanks, body) = extract_thanks_info(body, options)
    body = filter_ignore_rx_matches(body, options)

    if options.full and not 'short' in git_dch_cmds:
        # Add all non-blank body lines.
        entry.extend([line for line in body if line.strip()])
    if thanks:
        # Last wins for now (match old behavior).
        thanks_msg = 'Thanks to %s' % thanks[-1]
        entry.extend([thanks_msg])
    for bts in bts_cmds:
        bts_msg = '(%s: %s)' % (bts, ', '.join(bts_cmds[bts]))
        if len(entry[-1]) + len(bts_msg) >= MAX_CHANGELOG_LINE_LENGTH:
            entry.extend([''])
        else:
            entry[-1] += " "
        entry[-1] += bts_msg

    entry = terminate_first_line_if_needed(entry)
    return entry