|  | #!/usr/bin/python | 
|  | from optparse import OptionParser | 
|  | import re | 
|  | import subprocess | 
|  | import sys | 
|  |  | 
|  | """ | 
|  | This script generates a release note from the output of git log | 
|  | between the specified tags. | 
|  |  | 
|  | Options: | 
|  | --issues          Show output the commits with issues associated with them. | 
|  | --issue-numbers   Show outputs issue numbers of the commits with issues | 
|  | associated with them | 
|  |  | 
|  | Arguments: | 
|  | since -- tag name | 
|  | until -- tag name | 
|  |  | 
|  | Example Input: | 
|  |  | 
|  | * <commit subject> | 
|  | + | 
|  | <commit message> | 
|  |  | 
|  | Bug: issue 123 | 
|  | Change-Id: <change id> | 
|  | Signed-off-by: <name> | 
|  |  | 
|  | Expected Output: | 
|  |  | 
|  | * issue 123 <commit subject> | 
|  | + | 
|  | <commit message> | 
|  | """ | 
|  |  | 
|  | parser = OptionParser(usage='usage: %prog [options] <since> <until>') | 
|  |  | 
|  | parser.add_option('-i', '--issues', action='store_true', | 
|  | dest='issues_only', default=False, | 
|  | help='only output the commits with issues association') | 
|  |  | 
|  | parser.add_option('-n', '--issue-numbers', action='store_true', | 
|  | dest='issue_numbers_only', default=False, | 
|  | help='only outputs issue numbers of the commits with \ | 
|  | issues association') | 
|  |  | 
|  | (options, args) = parser.parse_args() | 
|  |  | 
|  | if len(args) != 2: | 
|  | parser.error("wrong number of arguments") | 
|  |  | 
|  | issues_only = options.issues_only | 
|  | issue_numbers_only = options.issue_numbers_only | 
|  |  | 
|  | since_until = args[0] + '..' + args[1] | 
|  | proc = subprocess.Popen(['git', 'log', '--reverse', '--no-merges', | 
|  | since_until, "--format=* %s%n+%n%b"], | 
|  | stdout=subprocess.PIPE, | 
|  | stderr=subprocess.STDOUT,) | 
|  |  | 
|  | stdout_value = proc.communicate()[0] | 
|  |  | 
|  | subject = "" | 
|  | message = [] | 
|  | is_issue = False | 
|  |  | 
|  | # regex pattern to match following cases such as Bug: 123, Issue Bug: 123, | 
|  | # Bug: GERRIT-123, Bug: issue 123, Bug issue: 123, issue: 123, issue: bug 123 | 
|  | p = re.compile('bug: GERRIT-|bug(:? issue)?:? |issue(:? bug)?:? ', | 
|  | re.IGNORECASE) | 
|  |  | 
|  | if issue_numbers_only: | 
|  | for line in stdout_value.splitlines(True): | 
|  | if p.match(line): | 
|  | sys.stdout.write(p.sub('', line)) | 
|  | else: | 
|  | for line in stdout_value.splitlines(True): | 
|  | # Move issue number to subject line | 
|  | if p.match(line): | 
|  | line = p.sub('issue ', line).replace('\n',' ') | 
|  | subject = subject[:2] + line + subject[2:] | 
|  | is_issue = True | 
|  | elif line.startswith('* '): | 
|  | # Write change log for a commit | 
|  | if subject != "": | 
|  | if (not issues_only or is_issue): | 
|  | # Write subject | 
|  | sys.stdout.write(subject) | 
|  | # Write message lines | 
|  | if message != []: | 
|  | # Clear + from last line in commit message | 
|  | message[-1] = '\n' | 
|  | for m in message: | 
|  | sys.stdout.write(m) | 
|  | # Start new commit block | 
|  | message = [] | 
|  | subject = line | 
|  | is_issue = False | 
|  | # Remove commit footers | 
|  | elif re.match(r'((\w+-)+\w+:)', line): | 
|  | continue | 
|  | # Don't add extra blank line if last one is already blank | 
|  | elif line == '\n' and message and message[-1] != '+\n': | 
|  | message.append('+\n') | 
|  | elif line != '\n': | 
|  | message.append(line) |