blob: 556183d8753e5e01aed21feea57b85922272d094 [file] [log] [blame]
[email protected]eebfb752012-03-19 19:30:551#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
[email protected]647798c2012-05-24 17:43:546"""Lists branches with closed and abandoned issues."""
[email protected]eebfb752012-03-19 19:30:557
[email protected]647798c2012-05-24 17:43:548import optparse
[email protected]eebfb752012-03-19 19:30:559import os
10import sys
11import urllib2
12
13BASE_DIR = os.path.dirname(os.path.abspath(__file__))
14DEPOT_TOOLS_DIR = os.path.dirname(BASE_DIR)
15sys.path.insert(0, DEPOT_TOOLS_DIR)
16
17import git_cl
18
19
20def get_branches():
21 """Get list of all local git branches."""
[email protected]c0cb4572013-04-24 22:29:0022 branches = [l.split() for l in git_cl.RunGit(
23 ["for-each-ref",
24 "--format=%(refname:short) %(upstream:short)",
25 "refs/heads"]).splitlines()]
26 return [Branch(*b) for b in branches]
27
28def get_change_count(start, end):
29 return int(git_cl.RunGit(["rev-list", "%s..%s" % (start, end), "--count" ]))
[email protected]eebfb752012-03-19 19:30:5530
31
32class Branch(git_cl.Changelist):
[email protected]a3800b22013-05-02 01:41:3533 def __init__(self, name, upstream=None):
[email protected]eebfb752012-03-19 19:30:5534 git_cl.Changelist.__init__(self, branchref=name)
[email protected]c0cb4572013-04-24 22:29:0035 self._upstream = upstream
36 self._distance = None
[email protected]eebfb752012-03-19 19:30:5537 self._issue_status = None
[email protected]647798c2012-05-24 17:43:5438
[email protected]eebfb752012-03-19 19:30:5539 def GetStatus(self):
40 if not self._issue_status:
41 if self.GetIssue():
[email protected]647798c2012-05-24 17:43:5442 try:
[email protected]eebfb752012-03-19 19:30:5543 issue_properties = self.RpcServer().get_issue_properties(
44 self.GetIssue(), None)
45 if issue_properties['closed']:
46 self._issue_status = 'closed'
47 else:
48 self._issue_status = 'pending'
49 except urllib2.HTTPError, e:
50 if e.code == 404:
51 self._issue_status = 'abandoned'
52 else:
53 self._issue_status = 'no-issue'
[email protected]c0cb4572013-04-24 22:29:0054 if (self._issue_status != 'pending'
[email protected]a3800b22013-05-02 01:41:3555 and self._upstream
[email protected]c0cb4572013-04-24 22:29:0056 and not self.GetDistance()[0]
57 and not self._upstream.startswith("origin/")):
58 self._issue_status = 'empty'
[email protected]eebfb752012-03-19 19:30:5559 return self._issue_status
[email protected]c0cb4572013-04-24 22:29:0060
61 def GetDistance(self):
[email protected]a3800b22013-05-02 01:41:3562 if self._upstream is None:
63 return None;
[email protected]c0cb4572013-04-24 22:29:0064 if not self._distance:
65 self._distance = [get_change_count(self._upstream, self.GetBranch()),
66 get_change_count(self.GetBranch(), self._upstream)]
67 return self._distance
[email protected]eebfb752012-03-19 19:30:5568
[email protected]c0cb4572013-04-24 22:29:0069 def GetDistanceInfo(self):
[email protected]a3800b22013-05-02 01:41:3570 if not self._upstream:
71 return "<No upstream branch>"
[email protected]c0cb4572013-04-24 22:29:0072 formatted_dist = ", ".join(["%s %d" % (x,y)
73 for (x,y) in zip(["ahead","behind"], self.GetDistance()) if y])
74 return "[%s%s]" % (
75 self._upstream, ": " + formatted_dist if formatted_dist else "")
[email protected]eebfb752012-03-19 19:30:5576
[email protected]a3800b22013-05-02 01:41:3577def print_branches(title, fmt, branches):
78 if branches:
79 print title
80 for branch in branches:
81 print fmt.format(branch=branch.GetBranch(),
82 issue=branch.GetIssue(),
83 distance=branch.GetDistanceInfo())
84
[email protected]647798c2012-05-24 17:43:5485def main():
86 parser = optparse.OptionParser(usage=sys.modules['__main__'].__doc__)
87 options, args = parser.parse_args()
88 if args:
89 parser.error('Unsupported arg: %s' % args)
90
[email protected]eebfb752012-03-19 19:30:5591 branches = get_branches()
92 filtered = { 'closed' : [],
[email protected]c0cb4572013-04-24 22:29:0093 'empty' : [],
[email protected]eebfb752012-03-19 19:30:5594 'pending' : [],
95 'abandoned' : [],
96 'no-issue' : []}
97
98 for branch in branches:
99 filtered[branch.GetStatus()].append(branch)
100
[email protected]a3800b22013-05-02 01:41:35101 print_branches("# Branches with closed issues",
102 "git branch -D {branch} # Issue {issue} is closed.",
103 filtered['closed'])
104 print_branches("\n# Empty branches",
105 "git branch -D {branch} # Empty.",
106 filtered['empty'])
107 print_branches("\n# Pending Branches",
108 "# Branch {branch} - Issue {issue} - {distance}",
109 filtered['pending']);
110 print_branches("\n# Branches with abandoned issues",
111 "# Branch {branch} - was issue {issue} - {distance}",
112 filtered['abandoned'])
[email protected]eebfb752012-03-19 19:30:55113
[email protected]a3800b22013-05-02 01:41:35114 print_branches("\n# Branches without associated issues",
115 "# Branch {branch} - {distance}",
116 filtered['no-issue'])
[email protected]eebfb752012-03-19 19:30:55117
118 return 0
119
120
121if __name__ == '__main__':
[email protected]647798c2012-05-24 17:43:54122 sys.exit(main())