Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 2 | # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 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 Staaf | 9a7288c | 2011-02-09 13:03:31 -0800 | [diff] [blame] | 8 | import optparse |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 9 | import os |
| 10 | import re |
| 11 | import subprocess |
| 12 | |
| 13 | def 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 | # |
| 24 | def 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 | |
| 38 | def 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 | |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 47 | def GetBranches(full_name, color): |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 48 | """Return a list of branch descriptions.""" |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 49 | |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 50 | command = ['git', 'branch', '-vv'] |
| 51 | |
| 52 | if color: |
| 53 | command.append('--color') |
| 54 | |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 55 | branches = RunCommand(full_name, command).splitlines() |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 56 | |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 57 | if re.search(r"\(no branch\)", branches[0]): |
| 58 | return [] |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 59 | |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 60 | return branches |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 61 | |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 62 | def GetStatus(full_name, color): |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 63 | """Return a list of files that have modifications.""" |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 64 | |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 65 | command = ['git', 'status', '-s'] |
| 66 | |
| 67 | return RunCommand(full_name, command).splitlines() |
| 68 | |
| 69 | |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 70 | def GetHistory(full_name, color, author, days): |
Anton Staaf | 9a7288c | 2011-02-09 13:03:31 -0800 | [diff] [blame] | 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 | |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 85 | def ShowDir(full_name, color, logs, author, days): |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 86 | """Display active work in a single git repository.""" |
| 87 | |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 88 | branches = GetBranches(full_name, color) |
| 89 | status = GetStatus(full_name, color) |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 90 | |
Anton Staaf | 9a7288c | 2011-02-09 13:03:31 -0800 | [diff] [blame] | 91 | if logs: |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 92 | history = GetHistory(full_name, color, author, days) |
Anton Staaf | 9a7288c | 2011-02-09 13:03:31 -0800 | [diff] [blame] | 93 | else: |
| 94 | history = [] |
| 95 | |
| 96 | if branches or status or history: |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 97 | # We want to use the full path for testing, but we want to use the |
| 98 | # relative path for display. |
| 99 | ShowName(os.path.relpath(full_name), color) |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 100 | |
| 101 | if branches: print '\n'.join(branches) |
| 102 | if status: print '\n'.join(status) |
Anton Staaf | 9a7288c | 2011-02-09 13:03:31 -0800 | [diff] [blame] | 103 | if history: print '\n'.join(history) |
Anton Staaf | 1037c44 | 2011-02-08 14:39:53 -0800 | [diff] [blame] | 104 | |
Anton Staaf | 9a7288c | 2011-02-09 13:03:31 -0800 | [diff] [blame] | 105 | if branches or status or history: |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 106 | print "" |
| 107 | |
| 108 | |
| 109 | def FindRoot(): |
| 110 | """Returns the repo root.""" |
| 111 | |
| 112 | repo_file = '.repo' |
| 113 | repo_path = FindFileUpwards(repo_file, os.getcwd()) |
| 114 | |
| 115 | if repo_path is None: |
| 116 | raise Exception('Failed to find %s.' % repo_file) |
| 117 | |
| 118 | return os.path.dirname(repo_path) |
| 119 | |
| 120 | |
| 121 | def main(): |
Anton Staaf | 9a7288c | 2011-02-09 13:03:31 -0800 | [diff] [blame] | 122 | parser = optparse.OptionParser(usage = 'usage: %prog [options]\n') |
| 123 | |
| 124 | parser.add_option('-l', '--logs', default=False, |
| 125 | help='Show the last few days of your commits in short ' |
| 126 | 'form.', |
| 127 | action='store_true', |
| 128 | dest='logs') |
| 129 | |
| 130 | parser.add_option('-d', '--days', default=8, |
| 131 | help='Set the number of days of history to show.', |
| 132 | type='int', |
| 133 | dest='days') |
| 134 | |
| 135 | parser.add_option('-a', '--author', default=os.environ['USER'], |
| 136 | help='Set the author to filter for.', |
| 137 | type='string', |
| 138 | dest='author') |
| 139 | |
| 140 | options, arguments = parser.parse_args() |
| 141 | |
| 142 | if arguments: |
| 143 | parser.print_usage() |
| 144 | return 1 |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 145 | |
| 146 | color = os.isatty(1) |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 147 | root = FindRoot() |
| 148 | repos = RunCommand(root, ['repo', 'forall', '-c', 'pwd']).splitlines() |
| 149 | |
Vadim Bendebury | 2ed43ee | 2011-04-08 13:49:22 -0700 | [diff] [blame^] | 150 | for full in repos: |
| 151 | ShowDir(full, color, options.logs, options.author, options.days) |
Anton Staaf | 60f4fd7 | 2011-02-01 13:20:54 -0800 | [diff] [blame] | 152 | |
| 153 | |
| 154 | if __name__ == '__main__': |
| 155 | main() |