aboutsummaryrefslogtreecommitdiffhomepage
path: root/gbp/dch.py
diff options
context:
space:
mode:
authorRob Browning <rlb@defaultvalue.org>2010-12-06 08:30:22 +0100
committerGuido Günther <agx@sigxcpu.org>2010-12-06 09:17:04 +0100
commit3640569c90a3326aa8b882fa72d63ed881785de4 (patch)
treede2c96284fadad8e87b4c258202b68a526d7b905 /gbp/dch.py
parente8757040c20697c7cce1180127e257f9f17ab713 (diff)
Add git-dch --customizations FILE to allow changelog entry customization
Add support for git-dch --customizations FILE. FILE must be Python code, and for now, the only useful thing it can do is define a format_changelog_entry() function which will override gbp.dch.format_changelog_entry(). Add a new customization option group for --customizations. Create a gbp.dch module and move the changelog entry formatting functions there. Create separate procedures to handle extracting metadata from the git log, and use them in the default format_changelog_entry(). These functions are also available for use by custom formatters: extract_git_dch_cmds(), filter_ignore_rx_matches(), extract_bts_cmds(), extract_thanks_info(), etc. Add a GitRepository.get_commit_info() method, and use it in git-dch parse_commit(). Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Diffstat (limited to 'gbp/dch.py')
-rw-r--r--gbp/dch.py116
1 files changed, 116 insertions, 0 deletions
diff --git a/gbp/dch.py b/gbp/dch.py
new file mode 100644
index 00000000..8ce8f2bc
--- /dev/null
+++ b/gbp/dch.py
@@ -0,0 +1,116 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2010 Rob Browning <rlb@defaultvalue.org>
+"""provides git-dch helpers"""
+
+import re
+
+
+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']
+ (git_dch_cmds, body) = extract_git_dch_cmds(body, options)
+ if 'ignore' in git_dch_cmds:
+ return None
+ (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()])
+ for bts in bts_cmds:
+ print bts_cmds
+ entry[-1] += '(%s: %s) ' % (bts, ', '.join(bts_cmds[bts]))
+ if thanks:
+ # Last wins for now (match old behavior).
+ entry[-1] += '- thanks to %s' % thanks[-1]
+ if options.idlen:
+ entry[0] = '[%s] ' % commitid[0:options.idlen] + entry[0]
+ entry = terminate_first_line_if_needed(entry)
+ if not last_commit:
+ entry += ''
+ return entry