Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 | |
| 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
| 4 # Use of this source code is governed by a BSD-style license that can be | |
| 5 # found in the LICENSE file. | |
| 6 | |
| 7 """Helper script for printing differences between tags.""" | |
| 8 | |
| 9 import cgi | |
| 10 from datetime import datetime | |
| 11 import os | |
| 12 import re | |
| 13 import sys | |
| 14 | |
| 15 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../lib')) | |
| 16 from cros_build_lib import RunCommand | |
| 17 | |
| 18 DEFAULT_TRACKER = 'chromium-os' | |
| 19 | |
| 20 | |
| 21 def _GrabOutput(cmd): | |
| 22 """Returns output from specified command.""" | |
| 23 return RunCommand(cmd, shell=True, print_cmd=False, | |
| 24 redirect_stdout=True).output | |
| 25 | |
| 26 | |
| 27 def _GrabTags(): | |
| 28 """Returns list of tags from current git repository.""" | |
|
diandersAtChromium
2010/10/28 20:36:15
It would be a lot easier to understand / make chan
David James
2010/10/28 20:52:52
Good idea. Could you add a TODO for this? This cod
| |
| 29 cmd = ("git for-each-ref refs/tags | awk '{print $3}' | " | |
| 30 "sed 's,refs/tags/,,g' | sort -t. -k3,3rn -k4,4rn") | |
| 31 return _GrabOutput(cmd).split() | |
| 32 | |
| 33 | |
| 34 def _GrabDirs(): | |
| 35 """Returns list of directories managed by repo.""" | |
| 36 return _GrabOutput('repo forall -c "pwd"').split() | |
| 37 | |
| 38 | |
| 39 class Commit(object): | |
| 40 """Class for tracking git commits.""" | |
| 41 | |
| 42 def __init__(self, commit, projectname, commit_email, commit_date, subject, | |
| 43 body): | |
| 44 """Create commit logs.""" | |
|
diandersAtChromium
2010/10/28 20:36:15
Seems like parameters should be documented here?
David James
2010/10/28 20:52:52
+1
| |
| 45 self.commit = commit | |
| 46 self.projectname = projectname | |
| 47 self.commit_email = commit_email | |
| 48 fmt = '%a %b %d %H:%M:%S %Y' | |
| 49 self.commit_date = datetime.strptime(commit_date, fmt) | |
| 50 self.subject = subject | |
| 51 self.body = body | |
| 52 self.bug_ids = self._GetBugIDs() | |
| 53 | |
| 54 def _GetBugIDs(self): | |
| 55 """Get bug ID from commit logs.""" | |
|
diandersAtChromium
2010/10/28 20:36:15
Describe inputs and outputs for this function: inp
David James
2010/10/28 20:52:52
+1
| |
| 56 | |
|
diandersAtChromium
2010/10/28 20:36:15
Some comments about what this is doing would be ap
David James
2010/10/28 20:52:52
+1. We should also mention it's copied from bugdro
| |
| 57 entries = [] | |
| 58 for line in self.body.split('\n'): | |
| 59 match = re.match(r'^ *BUG *=(.*)', line) | |
| 60 if match: | |
| 61 for i in match.group(1).split(','): | |
| 62 entries.extend(filter(None, [x.strip() for x in i.split()])) | |
| 63 | |
| 64 bug_ids = [] | |
| 65 last_tracker = DEFAULT_TRACKER | |
| 66 regex = (r'http://code.google.com/p/(\S+)/issues/detail\?id=([0-9]+)' | |
| 67 r'|(\S+):([0-9]+)|(\b[0-9]+\b)') | |
| 68 | |
| 69 for new_item in entries: | |
| 70 bug_numbers = re.findall(regex, new_item) | |
| 71 for bug_tuple in bug_numbers: | |
| 72 if bug_tuple[0] and bug_tuple[1]: | |
| 73 bug_ids.append('%s:%s' % (bug_tuple[0], bug_tuple[1])) | |
| 74 last_tracker = bug_tuple[0] | |
| 75 elif bug_tuple[2] and bug_tuple[3]: | |
| 76 bug_ids.append('%s:%s' % (bug_tuple[2], bug_tuple[3])) | |
| 77 last_tracker = bug_tuple[2] | |
| 78 elif bug_tuple[4]: | |
| 79 bug_ids.append('%s:%s' % (last_tracker, bug_tuple[4])) | |
| 80 | |
| 81 bug_ids.sort(key=str.lower) | |
| 82 return bug_ids | |
| 83 | |
| 84 def AsHTMLTableRow(self): | |
| 85 """Returns HTML for this change, for printing as part of a table. | |
| 86 | |
| 87 Columns: Project, Date, Commit, Committer, Bugs, Subject. | |
| 88 """ | |
| 89 | |
| 90 bugs = [] | |
| 91 bug_url_fmt = 'http://code.google.com/p/%s/issues/detail?id=%s' | |
| 92 link_fmt = '<a href="%s">%s</a>' | |
| 93 for bug in self.bug_ids: | |
| 94 tracker, bug_id = bug.split(':') | |
| 95 | |
| 96 # Get bug URL. We use short URLs to make the URLs a bit more readable. | |
| 97 if tracker == 'chromium-os': | |
| 98 bug_url = 'http://crosbug.com/%s' % bug_id | |
| 99 elif tracker == 'chrome-os-partner': | |
| 100 bug_url = 'http://crosbug.com/p/%s' % bug_id | |
| 101 else: | |
| 102 bug_url = bug_url_fmt % (tracker, bug_id) | |
| 103 | |
| 104 bugs.append(link_fmt % (bug_url, bug)) | |
| 105 | |
| 106 url_fmt = 'http://chromiumos-git/git/?p=%s.git;a=commitdiff;h=%s' | |
|
David McMahon
2010/10/28 02:30:44
Probably want to use gitrw.chromium.org:9222.
diandersAtChromium
2010/10/28 20:36:15
I'm not sure I understand this comment. I think t
David James
2010/10/28 20:52:52
Yeah. That was a misunderstanding. djmm and I disc
| |
| 107 url = url_fmt % (self.projectname, self.commit) | |
| 108 commit_desc = link_fmt % (url, self.commit[:8]) | |
| 109 bug_str = '<br>'.join(bugs) | |
| 110 if not bug_str: | |
| 111 if (self.projectname == 'kernel-next' or | |
| 112 self.commit_email == 'chrome-bot@chromium.org'): | |
| 113 bug_str = 'not needed' | |
| 114 else: | |
| 115 bug_str = '<font color="red">none</font>' | |
| 116 | |
| 117 cols = [ | |
| 118 cgi.escape(self.projectname), | |
| 119 str(self.commit_date), | |
| 120 commit_desc, | |
| 121 cgi.escape(self.commit_email), | |
| 122 bug_str, | |
| 123 cgi.escape(self.subject[:100]), | |
| 124 ] | |
| 125 return '<tr><td>%s</td></tr>' % ('</td><td>'.join(cols)) | |
| 126 | |
| 127 def __cmp__(self, other): | |
| 128 """Compare two Commit objects first by project name, then by date.""" | |
| 129 return (cmp(self.projectname, other.projectname) or | |
| 130 cmp(self.commit_date, other.commit_date)) | |
| 131 | |
| 132 | |
| 133 def _GrabChanges(path, tag1, tag2): | |
| 134 """Return list of commits to path between tag1 and tag2.""" | |
|
diandersAtChromium
2010/10/28 20:36:15
Would be nice to see parameters / return values co
David James
2010/10/28 20:52:52
+1
| |
| 135 | |
| 136 cmd = 'cd %s && git config --get remote.cros.projectname' % path | |
| 137 projectname = _GrabOutput(cmd).strip() | |
| 138 log_fmt = '%x00%H\t%ce\t%cd\t%s\t%b' | |
| 139 cmd_fmt = 'cd %s && git log --format="%s" --date=local %s..%s' | |
| 140 cmd = cmd_fmt % (path, log_fmt, tag1, tag2) | |
| 141 output = _GrabOutput(cmd) | |
| 142 commits = [] | |
| 143 for log_data in output.split('\0')[1:]: | |
| 144 commit, commit_email, commit_date, subject, body = log_data.split('\t', 4) | |
| 145 change = Commit(commit, projectname, commit_email, commit_date, subject, | |
| 146 body) | |
| 147 commits.append(change) | |
| 148 return commits | |
| 149 | |
| 150 | |
| 151 def main(): | |
| 152 tags = _GrabTags() | |
| 153 tag1 = None | |
| 154 if len(sys.argv) == 3: | |
| 155 tag1 = sys.argv[1] | |
| 156 tag2 = sys.argv[2] | |
| 157 elif len(sys.argv) == 2: | |
| 158 tag2 = sys.argv[1] | |
| 159 else: | |
| 160 print >>sys.stderr, 'Usage: %s [tag1] tag2' % sys.argv[0] | |
| 161 print >>sys.stderr, 'If only one tag is specified, we view the differences' | |
| 162 print >>sys.stderr, 'between that tag and the previous tag. You can also' | |
| 163 print >>sys.stderr, 'specify cros/master to show differences with' | |
| 164 print >>sys.stderr, 'tip-of-tree.' | |
| 165 sys.exit(1) | |
| 166 if not tag2.startswith('cros/') and tag2 not in tags: | |
| 167 print >>sys.stderr, 'Unrecognized tag: %s' % tag2 | |
| 168 sys.exit(1) | |
| 169 if not tag1: | |
|
diandersAtChromium
2010/10/28 20:36:15
Should be "if tag1 is None" unless you really want
David James
2010/10/28 20:52:52
Empty string is bad too, so let's leave this as-is
| |
| 170 tag1 = tags[tags.index(tag2) + 1] | |
|
diandersAtChromium
2010/10/28 20:36:15
Potential out of bounds issue here if tag2 is the
David James
2010/10/28 20:52:52
+1, please fix
| |
| 171 if not tag1.startswith('cros/') and tag1 not in tags: | |
| 172 print >>sys.stderr, 'Unrecognized tag: %s' % tag1 | |
| 173 sys.exit(1) | |
| 174 | |
| 175 print >>sys.stderr, 'Finding differences between %s and %s' % (tag1, tag2) | |
| 176 paths = _GrabDirs() | |
| 177 changes = [] | |
| 178 for path in paths: | |
| 179 changes.extend(_GrabChanges(path, tag1, tag2)) | |
| 180 | |
| 181 title = 'Changelog for %s to %s' % (tag1, tag2) | |
| 182 print '<html>' | |
| 183 print '<head><title>%s</title></head>' % title | |
| 184 print '<h1>%s</h1>' % title | |
| 185 cols = ['Project', 'Date', 'Commit', 'Committer', 'Bugs', 'Subject'] | |
| 186 print '<table border="1" cellpadding="4">' | |
| 187 print '<tr><th>%s</th>' % ('</th><th>'.join(cols)) | |
| 188 for change in sorted(changes): | |
| 189 print change.AsHTMLTableRow() | |
| 190 print '</table>' | |
| 191 print '</html>' | |
| 192 | |
| 193 | |
| 194 if __name__ == '__main__': | |
| 195 main() | |
| OLD | NEW |