Index: git_cl.py |
diff --git a/git_cl.py b/git_cl.py |
index 60c41f4a87b3d38a94fb8b193477a076ef3a6bd2..1dcb1fe8ca7bcc1bb0033997cd035a157f2b9e0c 100755 |
--- a/git_cl.py |
+++ b/git_cl.py |
@@ -1033,7 +1033,7 @@ def DownloadHooks(force): |
@usage('[repo root containing codereview.settings]') |
def CMDconfig(parser, args): |
- """edit configuration for this tree""" |
+ """Edits configuration for this tree.""" |
_, args = parser.parse_args(args) |
if len(args) == 0: |
@@ -1052,7 +1052,7 @@ def CMDconfig(parser, args): |
def CMDbaseurl(parser, args): |
- """get or set base-url for this branch""" |
+ """Gets or sets base-url for this branch.""" |
branchref = RunGit(['symbolic-ref', 'HEAD']).strip() |
branch = ShortBranchName(branchref) |
_, args = parser.parse_args(args) |
@@ -1067,7 +1067,17 @@ def CMDbaseurl(parser, args): |
def CMDstatus(parser, args): |
- """show status of changelists""" |
+ """Show status of changelists. |
+ |
+ Colors are used to tell the state of the CL unless --fast is used: |
+ - Green LGTM'ed |
+ - Blue waiting for review |
+ - Yellow waiting for you to reply to review |
+ - Red not sent for review or broken |
+ - Cyan was committed, branch can be deleted |
+ |
+ Also see 'git cl comments'. |
+ """ |
parser.add_option('--field', |
help='print only specific field (desc|id|patch|url)') |
parser.add_option('-f', '--fast', action='store_true', |
@@ -1109,18 +1119,37 @@ def CMDstatus(parser, args): |
if not options.fast: |
def fetch(b): |
+ """Fetches information for an issue and returns (branch, issue, color).""" |
c = Changelist(branchref=b) |
i = c.GetIssueURL() |
- try: |
- props = c.GetIssueProperties() |
- r = c.GetApprovingReviewers() if i else None |
- if not props.get('messages'): |
- r = None |
- except urllib2.HTTPError: |
- # The issue probably doesn't exist anymore. |
- i += ' (broken)' |
- r = None |
- output.put((b, i, r)) |
+ props = {} |
+ r = None |
+ if i: |
+ try: |
+ props = c.GetIssueProperties() |
+ r = c.GetApprovingReviewers() if i else None |
+ except urllib2.HTTPError: |
+ # The issue probably doesn't exist anymore. |
+ i += ' (broken)' |
+ |
+ msgs = props.get('messages') or [] |
+ |
+ if not i: |
+ color = Fore.WHITE |
+ elif props.get('closed'): |
+ # Issue is closed. |
+ color = Fore.CYAN |
+ elif r: |
+ # Was LGTM'ed. |
+ color = Fore.GREEN |
+ elif not msgs: |
+ # No message was sent. |
+ color = Fore.RED |
+ elif msgs[-1]['sender'] != props.get('owner_email'): |
+ color = Fore.YELLOW |
+ else: |
+ color = Fore.BLUE |
+ output.put((b, i, color)) |
threads = [threading.Thread(target=fetch, args=(b,)) for b in branches] |
for t in threads: |
@@ -1130,25 +1159,16 @@ def CMDstatus(parser, args): |
# Do not use GetApprovingReviewers(), since it requires an HTTP request. |
for b in branches: |
c = Changelist(branchref=b) |
- output.put((b, c.GetIssue(), None)) |
+ url = c.GetIssueURL() |
+ output.put((b, url, Fore.BLUE if url else Fore.WHITE)) |
tmp = {} |
alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) |
for branch in sorted(branches): |
while branch not in tmp: |
- b, i, r = output.get() |
- tmp[b] = (i, r) |
- issue, reviewers = tmp.pop(branch) |
- if not issue: |
- color = Fore.WHITE |
- elif reviewers: |
- # Was approved. |
- color = Fore.GREEN |
- elif reviewers is None: |
- # No message was sent. |
- color = Fore.RED |
- else: |
- color = Fore.BLUE |
+ b, i, color = output.get() |
+ tmp[b] = (i, color) |
+ issue, color = tmp.pop(branch) |
print ' %*s: %s%s%s' % ( |
alignment, ShortBranchName(branch), color, issue, Fore.RESET) |
@@ -1167,10 +1187,10 @@ def CMDstatus(parser, args): |
@usage('[issue_number]') |
def CMDissue(parser, args): |
- """Set or display the current code review issue number. |
+ """Sets or displays the current code review issue number. |
Pass issue number 0 to clear the current issue. |
-""" |
+ """ |
_, args = parser.parse_args(args) |
cl = Changelist() |
@@ -1186,7 +1206,7 @@ def CMDissue(parser, args): |
def CMDcomments(parser, args): |
- """show review comments of the current changelist""" |
+ """Shows review comments of the current changelist.""" |
(_, args) = parser.parse_args(args) |
if args: |
parser.error('Unsupported argument: %s' % args) |
@@ -1212,7 +1232,7 @@ def CMDcomments(parser, args): |
def CMDdescription(parser, args): |
- """brings up the editor for the current CL's description.""" |
+ """Brings up the editor for the current CL's description.""" |
cl = Changelist() |
if not cl.GetIssue(): |
DieWithError('This branch has no associated changelist.') |
@@ -1237,7 +1257,7 @@ def CreateDescriptionFromLog(args): |
def CMDpresubmit(parser, args): |
- """run presubmit tests on the current changelist""" |
+ """Runs presubmit tests on the current changelist.""" |
parser.add_option('-u', '--upload', action='store_true', |
help='Run upload hook instead of the push/dcommit hook') |
parser.add_option('-f', '--force', action='store_true', |
@@ -1424,7 +1444,7 @@ def cleanup_list(l): |
@usage('[args to "git diff"]') |
def CMDupload(parser, args): |
- """upload the current changelist to codereview""" |
+ """Uploads the current changelist to codereview.""" |
parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', |
help='bypass upload presubmit hook') |
parser.add_option('--bypass-watchlists', action='store_true', |
@@ -1747,7 +1767,7 @@ def SendUpstream(parser, args, cmd): |
@usage('[upstream branch to apply against]') |
def CMDdcommit(parser, args): |
- """commit the current changelist via git-svn""" |
+ """Commits the current changelist via git-svn.""" |
if not settings.GetIsGitSvn(): |
message = """This doesn't appear to be an SVN repository. |
If your project has a git mirror with an upstream SVN master, you probably need |
@@ -1763,7 +1783,7 @@ will instead be silently ignored.""" |
@usage('[upstream branch to apply against]') |
def CMDpush(parser, args): |
- """commit the current changelist via git""" |
+ """Commits the current changelist via git.""" |
if settings.GetIsGitSvn(): |
print('This appears to be an SVN repository.') |
print('Are you sure you didn\'t mean \'git cl dcommit\'?') |
@@ -1773,7 +1793,7 @@ def CMDpush(parser, args): |
@usage('<patch url or issue id>') |
def CMDpatch(parser, args): |
- """patch in a code review""" |
+ """Patchs in a code review.""" |
parser.add_option('-b', dest='newbranch', |
help='create a new branch off trunk for the patch') |
parser.add_option('-f', action='store_true', dest='force', |
@@ -1863,7 +1883,7 @@ def CMDpatch(parser, args): |
def CMDrebase(parser, args): |
- """rebase current branch on top of svn repo""" |
+ """Rebases current branch on top of svn repo.""" |
# Provide a wrapper for git svn rebase to help avoid accidental |
# git svn dcommit. |
# It's the only command that doesn't use parser at all since we just defer |
@@ -1901,7 +1921,7 @@ def GetTreeStatusReason(): |
def CMDtree(parser, args): |
- """show the status of the tree""" |
+ """Shows the status of the tree.""" |
_, args = parser.parse_args(args) |
status = GetTreeStatus() |
if 'unset' == status: |
@@ -2017,7 +2037,7 @@ def CMDtry(parser, args): |
@usage('[new upstream branch]') |
def CMDupstream(parser, args): |
- """prints or sets the name of the upstream branch, if any""" |
+ """Prints or sets the name of the upstream branch, if any.""" |
_, args = parser.parse_args(args) |
if len(args) > 1: |
parser.error('Unrecognized args: %s' % ' '.join(args)) |
@@ -2035,7 +2055,7 @@ def CMDupstream(parser, args): |
def CMDset_commit(parser, args): |
- """set the commit bit""" |
+ """Sets the commit bit to trigger the Commit Queue.""" |
_, args = parser.parse_args(args) |
if args: |
parser.error('Unrecognized args: %s' % ' '.join(args)) |
@@ -2045,7 +2065,7 @@ def CMDset_commit(parser, args): |
def CMDset_close(parser, args): |
- """close the issue""" |
+ """Closes the issue.""" |
_, args = parser.parse_args(args) |
if args: |
parser.error('Unrecognized args: %s' % ' '.join(args)) |
@@ -2057,7 +2077,7 @@ def CMDset_close(parser, args): |
def CMDformat(parser, args): |
- """run clang-format on the diff""" |
+ """Runs clang-format on the diff.""" |
CLANG_EXTS = ['.cc', '.cpp', '.h'] |
parser.add_option('--full', action='store_true', default=False) |
opts, args = parser.parse_args(args) |
@@ -2154,7 +2174,7 @@ def Command(name): |
def CMDhelp(parser, args): |
- """print list of commands or help for a specific command""" |
+ """Prints list of commands or help for a specific command.""" |
_, args = parser.parse_args(args) |
if len(args) == 1: |
return main(args + ['--help']) |
@@ -2172,11 +2192,32 @@ def GenUsage(parser, command): |
if command == 'help': |
command = '<command>' |
else: |
- # OptParser.description prefer nicely non-formatted strings. |
- parser.description = re.sub('[\r\n ]{2,}', ' ', obj.__doc__) |
+ parser.description = obj.__doc__ |
parser.set_usage('usage: %%prog %s [options] %s' % (command, more)) |
+class OptionParser(optparse.OptionParser): |
+ """Creates the option parse and add --verbose support.""" |
+ def __init__(self, *args, **kwargs): |
+ optparse.OptionParser.__init__(self, *args, **kwargs) |
+ self.add_option( |
+ '-v', '--verbose', action='count', default=0, |
+ help='Use 2 times for more debugging info') |
+ |
+ def parse_args(self, args=None, values=None): |
+ options, args = optparse.OptionParser.parse_args(self, args, values) |
+ levels = [logging.WARNING, logging.INFO, logging.DEBUG] |
+ logging.basicConfig(level=levels[min(options.verbose, len(levels) - 1)]) |
+ return options, args |
+ |
+ def format_description(self, _): |
+ """Disables automatic reformatting.""" |
+ lines = self.description.rstrip().splitlines() |
+ lines_fixed = [lines[0]] + [l[2:] if len(l) >= 2 else l for l in lines[1:]] |
+ description = ''.join(l + '\n' for l in lines_fixed) |
+ return description[0].upper() + description[1:] |
+ |
+ |
def main(argv): |
"""Doesn't parse the arguments here, just find the right subcommand to |
execute.""" |
@@ -2193,29 +2234,19 @@ def main(argv): |
# Do it late so all commands are listed. |
commands = Commands() |
length = max(len(c) for c in commands) |
+ |
+ def gen_summary(x): |
+ """Creates a oneline summary from the docstring.""" |
+ line = x.split('\n', 1)[0].rstrip('.') |
+ return line[0].lower() + line[1:] |
+ |
docs = sorted( |
- (name, handler.__doc__.split('\n')[0].strip()) |
+ (name, gen_summary(handler.__doc__).strip()) |
for name, handler in commands.iteritems()) |
CMDhelp.usage_more = ('\n\nCommands are:\n' + '\n'.join( |
' %-*s %s' % (length, name, doc) for name, doc in docs)) |
- # Create the option parse and add --verbose support. |
- parser = optparse.OptionParser() |
- parser.add_option( |
- '-v', '--verbose', action='count', default=0, |
- help='Use 2 times for more debugging info') |
- old_parser_args = parser.parse_args |
- def Parse(args): |
- options, args = old_parser_args(args) |
- if options.verbose >= 2: |
- logging.basicConfig(level=logging.DEBUG) |
- elif options.verbose: |
- logging.basicConfig(level=logging.INFO) |
- else: |
- logging.basicConfig(level=logging.WARNING) |
- return options, args |
- parser.parse_args = Parse |
- |
+ parser = OptionParser() |
if argv: |
command = Command(argv[0]) |
if command: |