blob: c1a08e95e7794eff5093a6caf0df0e4713a99373 [file] [log] [blame]
Anton Staaf60f4fd72011-02-01 13:20:54 -08001#!/usr/bin/env python
2# Copyright (c) 2011 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
6"""Display active git branches and code changes in a ChromiumOS workspace."""
7
Anton Staaf9a7288c2011-02-09 13:03:31 -08008import optparse
Anton Staaf60f4fd72011-02-01 13:20:54 -08009import os
10import re
11import subprocess
12
13def RunCommand(path, command):
14 """Run a command in a given directory, return stdout."""
15
16 return subprocess.Popen(command,
17 cwd=path,
18 stdout=subprocess.PIPE).communicate()[0].rstrip()
19
20#
21# Taken with slight modification from gclient_utils.py in the depot_tools
22# project.
23#
24def FindFileUpwards(filename, path):
25 """Search upwards from the a directory to find a file."""
26
27 path = os.path.realpath(path)
28 while True:
29 file_path = os.path.join(path, filename)
30 if os.path.exists(file_path):
31 return file_path
32 (new_path, _) = os.path.split(path)
33 if new_path == path:
34 return None
35 path = new_path
36
37
38def ShowName(relative_name, color):
39 """Display the directory name."""
40
41 if color:
42 print('\033[44m\033[37m%s\033[0m' % relative_name)
43 else:
44 print relative_name
45
46
Anton Staaf1037c442011-02-08 14:39:53 -080047def GetBranches(full_name, relative_name, color):
48 """Return a list of branch descriptions."""
Anton Staaf60f4fd72011-02-01 13:20:54 -080049
Anton Staaf60f4fd72011-02-01 13:20:54 -080050 command = ['git', 'branch', '-vv']
51
52 if color:
53 command.append('--color')
54
Anton Staaf1037c442011-02-08 14:39:53 -080055 branches = RunCommand(full_name, command).splitlines()
Anton Staaf60f4fd72011-02-01 13:20:54 -080056
Anton Staaf1037c442011-02-08 14:39:53 -080057 if re.search(r"\(no branch\)", branches[0]):
58 return []
Anton Staaf60f4fd72011-02-01 13:20:54 -080059
Anton Staaf1037c442011-02-08 14:39:53 -080060 return branches
Anton Staaf60f4fd72011-02-01 13:20:54 -080061
Anton Staaf1037c442011-02-08 14:39:53 -080062def GetStatus(full_name, relative_name, color):
63 """Return a list of files that have modifications."""
Anton Staaf60f4fd72011-02-01 13:20:54 -080064
Anton Staaf1037c442011-02-08 14:39:53 -080065 command = ['git', 'status', '-s']
66
67 return RunCommand(full_name, command).splitlines()
68
69
Anton Staaf9a7288c2011-02-09 13:03:31 -080070def GetHistory(full_name, relative_name, color, author, days):
71 """Return a list of oneline log messages.
72
73 The messages are for the author going back a specified number of days.
74 """
75
76 command = ['git', 'log',
77 '--author=' + author,
78 '--after=' + '-' + str(days) + 'days',
79 '--pretty=oneline',
80 'm/master']
81
82 return RunCommand(full_name, command).splitlines()
83
84
85def ShowDir(full_name, relative_name, color, logs, author, days):
Anton Staaf1037c442011-02-08 14:39:53 -080086 """Display active work in a single git repository."""
87
88 branches = GetBranches(full_name, relative_name, color)
89 status = GetStatus(full_name, relative_name, color)
90
Anton Staaf9a7288c2011-02-09 13:03:31 -080091 if logs:
92 history = GetHistory(full_name, relative_name, color, author, days)
93 else:
94 history = []
95
96 if branches or status or history:
Anton Staaf1037c442011-02-08 14:39:53 -080097 ShowName(relative_name, color)
98
99 if branches: print '\n'.join(branches)
100 if status: print '\n'.join(status)
Anton Staaf9a7288c2011-02-09 13:03:31 -0800101 if history: print '\n'.join(history)
Anton Staaf1037c442011-02-08 14:39:53 -0800102
Anton Staaf9a7288c2011-02-09 13:03:31 -0800103 if branches or status or history:
Anton Staaf60f4fd72011-02-01 13:20:54 -0800104 print ""
105
106
107def FindRoot():
108 """Returns the repo root."""
109
110 repo_file = '.repo'
111 repo_path = FindFileUpwards(repo_file, os.getcwd())
112
113 if repo_path is None:
114 raise Exception('Failed to find %s.' % repo_file)
115
116 return os.path.dirname(repo_path)
117
118
119def main():
Anton Staaf9a7288c2011-02-09 13:03:31 -0800120 parser = optparse.OptionParser(usage = 'usage: %prog [options]\n')
121
122 parser.add_option('-l', '--logs', default=False,
123 help='Show the last few days of your commits in short '
124 'form.',
125 action='store_true',
126 dest='logs')
127
128 parser.add_option('-d', '--days', default=8,
129 help='Set the number of days of history to show.',
130 type='int',
131 dest='days')
132
133 parser.add_option('-a', '--author', default=os.environ['USER'],
134 help='Set the author to filter for.',
135 type='string',
136 dest='author')
137
138 options, arguments = parser.parse_args()
139
140 if arguments:
141 parser.print_usage()
142 return 1
Anton Staaf60f4fd72011-02-01 13:20:54 -0800143
144 color = os.isatty(1)
Anton Staaf60f4fd72011-02-01 13:20:54 -0800145 root = FindRoot()
146 repos = RunCommand(root, ['repo', 'forall', '-c', 'pwd']).splitlines()
147
148 # We want to use the full path for testing, but we want to use the relative
149 # path for display.
Anton Staaf82340d72011-02-08 14:39:19 -0800150 reldirs = [re.sub('^' + re.escape(root) + '/', '', p) for p in repos]
Anton Staaf60f4fd72011-02-01 13:20:54 -0800151
Anton Staaf82340d72011-02-08 14:39:19 -0800152 for full, relative in zip(repos, reldirs):
Anton Staaf9a7288c2011-02-09 13:03:31 -0800153 ShowDir(full, relative, color, options.logs, options.author, options.days)
Anton Staaf60f4fd72011-02-01 13:20:54 -0800154
155
156if __name__ == '__main__':
157 main()