| Index: gcl.py
|
| diff --git a/gcl.py b/gcl.py
|
| index 910a0d9139ce93abdc2558aa5c56fe0d639b8fad..272e8d64803fba3a346a1e7c875022ac8c65f5a2 100755
|
| --- a/gcl.py
|
| +++ b/gcl.py
|
| @@ -3,8 +3,10 @@
|
| # Use of this source code is governed by a BSD-style license that can be
|
| # found in the LICENSE file.
|
|
|
| -"""Wrapper script around Rietveld's upload.py that groups files into
|
| -changelists."""
|
| +"""\
|
| +Wrapper script around Rietveld's upload.py that simplifies working with groups
|
| +of files.
|
| +"""
|
|
|
| import getpass
|
| import os
|
| @@ -27,7 +29,7 @@ __pychecker__ = ''
|
| from scm import SVN
|
| import gclient_utils
|
|
|
| -__version__ = '1.1.4'
|
| +__version__ = '1.2'
|
|
|
|
|
| CODEREVIEW_SETTINGS = {
|
| @@ -82,14 +84,9 @@ def CheckHomeForFile(filename):
|
| return None
|
|
|
|
|
| -def UnknownFiles(extra_args):
|
| - """Runs svn status and returns unknown files.
|
| -
|
| - Any args in |extra_args| are passed to the tool to support giving alternate
|
| - code locations.
|
| - """
|
| - return [item[1] for item in SVN.CaptureStatus(extra_args)
|
| - if item[0][0] == '?']
|
| +def UnknownFiles():
|
| + """Runs svn status and returns unknown files."""
|
| + return [item[1] for item in SVN.CaptureStatus([]) if item[0][0] == '?']
|
|
|
|
|
| def GetRepositoryRoot():
|
| @@ -612,7 +609,7 @@ def ListFiles(show_unknown_files):
|
| for filename in files[cl_name]:
|
| print "".join(filename)
|
| if show_unknown_files:
|
| - unknown_files = UnknownFiles([])
|
| + unknown_files = UnknownFiles()
|
| if (files.get('') or (show_unknown_files and len(unknown_files))):
|
| print "\n--- Not in any changelist:"
|
| for item in files.get('', []):
|
| @@ -623,105 +620,6 @@ def ListFiles(show_unknown_files):
|
| return 0
|
|
|
|
|
| -def CMDopened(args):
|
| - """Lists modified files in the current directory down."""
|
| - if args:
|
| - ErrorExit("Doesn't support arguments")
|
| - return ListFiles(False)
|
| -
|
| -
|
| -def CMDstatus(args):
|
| - """Lists modified and unknown files in the current directory down."""
|
| - if args:
|
| - ErrorExit("Doesn't support arguments")
|
| - return ListFiles(True)
|
| -
|
| -
|
| -def CMDhelp(args):
|
| - """Prints this help or help for the given command."""
|
| - if args and len(args) > 2:
|
| - if args[2] == 'try':
|
| - return TryChange(None, ['--help'], swallow_exception=False)
|
| - if args[2] == 'upload':
|
| - upload.RealMain(['upload.py', '--help'])
|
| - return 0
|
| -
|
| - print (
|
| -"""GCL is a wrapper for Subversion that simplifies working with groups of files.
|
| -version """ + __version__ + """
|
| -
|
| -Basic commands:
|
| ------------------------------------------
|
| - gcl change change_name
|
| - Add/remove files to a changelist. Only scans the current directory and
|
| - subdirectories.
|
| -
|
| - gcl upload change_name [-r reviewer1@gmail.com,reviewer2@gmail.com,...]
|
| - [--send_mail] [--no_try] [--no_presubmit]
|
| - [--no_watchlists]
|
| - Uploads the changelist to the server for review.
|
| - (You can create the file '.gcl_upload_no_try' in your home dir to
|
| - skip the automatic tries.)
|
| -
|
| - gcl commit change_name [--no_presubmit]
|
| - Commits the changelist to the repository.
|
| -
|
| - gcl lint change_name
|
| - Check all the files in the changelist for possible style violations.
|
| -
|
| -Advanced commands:
|
| ------------------------------------------
|
| - gcl delete change_name
|
| - Deletes a changelist.
|
| -
|
| - gcl diff change_name
|
| - Diffs all files in the changelist.
|
| -
|
| - gcl presubmit change_name
|
| - Runs presubmit checks without uploading the changelist.
|
| -
|
| - gcl diff
|
| - Diffs all files in the current directory and subdirectories that aren't in
|
| - a changelist.
|
| -
|
| - gcl description
|
| - Prints the description of the specified change to stdout.
|
| -
|
| - gcl changes
|
| - Lists all the the changelists and the files in them.
|
| -
|
| - gcl rename <old-name> <new-name>
|
| - Renames an existing change.
|
| -
|
| - gcl nothave [optional directory]
|
| - Lists files unknown to Subversion.
|
| -
|
| - gcl opened
|
| - Lists modified files in the current directory and subdirectories.
|
| -
|
| - gcl settings
|
| - Print the code review settings for this directory.
|
| -
|
| - gcl status
|
| - Lists modified and unknown files in the current directory and
|
| - subdirectories.
|
| -
|
| - gcl try change_name
|
| - Sends the change to the tryserver so a trybot can do a test run on your
|
| - code. To send multiple changes as one path, use a comma-separated list
|
| - of changenames.
|
| - --> Use 'gcl help try' for more information!
|
| -
|
| - gcl deleteempties
|
| - Deletes all changelists that have no files associated with them. Careful,
|
| - you can lose your descriptions.
|
| -
|
| - gcl help [command]
|
| - Print this help menu, or help for the given command if it exists.
|
| -""")
|
| - return 0
|
| -
|
| -
|
| def GetEditor():
|
| editor = os.environ.get("SVN_EDITOR")
|
| if not editor:
|
| @@ -746,6 +644,13 @@ def OptionallyDoPresubmitChecks(change_info, committing, args):
|
| return DoPresubmitChecks(change_info, committing, True)
|
|
|
|
|
| +def defer_attributes(a, b):
|
| + """Copy attributes from an object (like a function) to another."""
|
| + for x in dir(a):
|
| + if not getattr(b, x, None):
|
| + setattr(b, x, getattr(a, x))
|
| +
|
| +
|
| def need_change(function):
|
| """Converts args -> change_info."""
|
| def hook(args):
|
| @@ -753,13 +658,63 @@ def need_change(function):
|
| ErrorExit("You need to pass a change list name")
|
| change_info = ChangeInfo.Load(args[0], GetRepositoryRoot(), True, True)
|
| return function(change_info)
|
| + defer_attributes(function, hook)
|
| + hook.need_change = True
|
| + hook.no_args = True
|
| return hook
|
|
|
|
|
| -def CMDupload(args):
|
| - if not args:
|
| - ErrorExit("You need to pass a change list name")
|
| - change_info = ChangeInfo.Load(args.pop(0), GetRepositoryRoot(), True, True)
|
| +def need_change_and_args(function):
|
| + """Converts args -> change_info."""
|
| + def hook(args):
|
| + change_info = ChangeInfo.Load(args.pop(0), GetRepositoryRoot(), True, True)
|
| + return function(change_info, args)
|
| + defer_attributes(function, hook)
|
| + hook.need_change = True
|
| + return hook
|
| +
|
| +
|
| +def no_args(function):
|
| + """Make sure no args are passed."""
|
| + def hook(args):
|
| + if args:
|
| + ErrorExit("Doesn't support arguments")
|
| + return function()
|
| + defer_attributes(function, hook)
|
| + hook.no_args = True
|
| + return hook
|
| +
|
| +
|
| +def attrs(**kwargs):
|
| + """Decorate a function with new attributes."""
|
| + def decorate(function):
|
| + for k in kwargs:
|
| + setattr(function, k, kwargs[k])
|
| + return function
|
| + return decorate
|
| +
|
| +
|
| +@no_args
|
| +def CMDopened():
|
| + """Lists modified files in the current directory down."""
|
| + return ListFiles(False)
|
| +
|
| +
|
| +@no_args
|
| +def CMDstatus():
|
| + """Lists modified and unknown files in the current directory down."""
|
| + return ListFiles(True)
|
| +
|
| +
|
| +@need_change_and_args
|
| +@attrs(usage='[--no_try] [--no_presubmit] [--clobber]\n'
|
| + ' [--no_watchlists]')
|
| +def CMDupload(change_info, args):
|
| + """Uploads the changelist to the server for review.
|
| +
|
| + (You can create the file '.gcl_upload_no_try' in your home dir to
|
| + skip the automatic tries.)
|
| + """
|
| if not change_info.GetFiles():
|
| print "Nothing to upload, changelist is empty."
|
| return 0
|
| @@ -768,11 +723,11 @@ def CMDupload(args):
|
| # Might want to support GetInfoDir()/GetRepositoryRoot() like
|
| # CheckHomeForFile() so the skip of tries can be per tree basis instead
|
| # of user global.
|
| - no_try = FilterFlag(args, "--no_try") or \
|
| - FilterFlag(args, "--no-try") or \
|
| - not (CheckHomeForFile(".gcl_upload_no_try") is None)
|
| - no_watchlists = FilterFlag(args, "--no_watchlists") or \
|
| - FilterFlag(args, "--no-watchlists")
|
| + no_try = (FilterFlag(args, "--no_try") or
|
| + FilterFlag(args, "--no-try") or
|
| + not (CheckHomeForFile(".gcl_upload_no_try") is None))
|
| + no_watchlists = (FilterFlag(args, "--no_watchlists") or
|
| + FilterFlag(args, "--no-watchlists"))
|
|
|
| # Map --send-mail to --send_mail
|
| if FilterFlag(args, "--send-mail"):
|
| @@ -877,7 +832,10 @@ def CMDupload(args):
|
|
|
| @need_change
|
| def CMDpresubmit(change_info):
|
| - """Runs presubmit checks on the change."""
|
| + """Runs presubmit checks on the change.
|
| +
|
| + The actual presubmit code is implemented in presubmit_support.py and looks
|
| + for PRESUBMIT.py files."""
|
| if not change_info.GetFiles():
|
| print "Nothing to presubmit check, changelist is empty."
|
| return 0
|
| @@ -917,10 +875,10 @@ def TryChange(change_info, args, swallow_exception):
|
| prog='gcl try')
|
|
|
|
|
| -def CMDcommit(args):
|
| - if not args:
|
| - ErrorExit("You need to pass a change list name")
|
| - change_info = ChangeInfo.Load(args.pop(0), GetRepositoryRoot(), True, True)
|
| +@need_change_and_args
|
| +@attrs(usage='[--no_presubmit]')
|
| +def CMDcommit(change_list, args):
|
| + """Commits the changelist to the repository."""
|
| if not change_info.GetFiles():
|
| print "Nothing to commit, changelist is empty."
|
| return 1
|
| @@ -978,7 +936,9 @@ def CMDcommit(args):
|
|
|
|
|
| def CMDchange(args):
|
| - """Creates/edits a changelist."""
|
| + """Creates or edits a changelist.
|
| +
|
| + Only scans the current directory and subdirectories."""
|
| if len(args) == 0:
|
| # Generate a random changelist name.
|
| changename = GenerateChangeName()
|
| @@ -1098,9 +1058,12 @@ def CMDchange(args):
|
| return 0
|
|
|
|
|
| -@need_change
|
| -def CMDlint(change_info):
|
| - """Runs cpplint.py on all the files in |change_info|"""
|
| +@need_change_and_args
|
| +def CMDlint(change_info, args):
|
| + """Runs cpplint.py on all the files in the change list.
|
| +
|
| + Checks all the files in the changelist for possible style violations.
|
| + """
|
| try:
|
| import cpplint
|
| except ImportError:
|
| @@ -1158,10 +1121,9 @@ def DoPresubmitChecks(change_info, committing, may_prompt):
|
| return result
|
|
|
|
|
| -def CMDchanges(args):
|
| +@no_args
|
| +def CMDchanges():
|
| """Lists all the changelists and their files."""
|
| - if args:
|
| - ErrorExit("Doesn't support arguments")
|
| for cl in GetCLs():
|
| change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True)
|
| print "\n--- Changelist " + change_info.name + ":"
|
| @@ -1170,10 +1132,9 @@ def CMDchanges(args):
|
| return 0
|
|
|
|
|
| -def CMDdeleteempties(args):
|
| +@no_args
|
| +def CMDdeleteempties():
|
| """Delete all changelists that have no files."""
|
| - if args:
|
| - ErrorExit("Doesn't support arguments")
|
| print "\n--- Deleting:"
|
| for cl in GetCLs():
|
| change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), True, True)
|
| @@ -1183,21 +1144,20 @@ def CMDdeleteempties(args):
|
| return 0
|
|
|
|
|
| -def CMDnothave(args):
|
| +@no_args
|
| +def CMDnothave():
|
| """Lists files unknown to Subversion."""
|
| - if args:
|
| - ErrorExit("Doesn't support arguments")
|
| - for filename in UnknownFiles(args):
|
| + for filename in UnknownFiles():
|
| print "? " + "".join(filename)
|
| return 0
|
|
|
|
|
| +@attrs(usage='<svn options>')
|
| def CMDdiff(args):
|
| - """Diffs all files in the changelist."""
|
| + """Diffs all files in the changelist or all files that aren't in a CL."""
|
| files = None
|
| if args:
|
| - change_info = ChangeInfo.Load(args[0], GetRepositoryRoot(), True, True)
|
| - args.pop(0)
|
| + change_info = ChangeInfo.Load(args.pop(0), GetRepositoryRoot(), True, True)
|
| files = change_info.GetFileNames()
|
| else:
|
| files = GetFilesNotInCL()
|
| @@ -1205,9 +1165,9 @@ def CMDdiff(args):
|
| print_output=True)[1]
|
|
|
|
|
| -def CMDsettings(args):
|
| - """Prints code review settings."""
|
| - __pychecker__ = 'unusednames=args'
|
| +@no_args
|
| +def CMDsettings():
|
| + """Prints code review settings for this checkout."""
|
| # Force load settings
|
| GetCodeReviewSetting("UNKNOWN");
|
| del CODEREVIEW_SETTINGS['__just_initialized']
|
| @@ -1231,8 +1191,7 @@ def CMDdelete(change_info):
|
|
|
|
|
| def CMDtry(args):
|
| - """Sends the change to the tryserver so a trybot can do a test run on your
|
| - code.
|
| + """Sends the change to the tryserver to do a test run on your code.
|
|
|
| To send multiple changes as one path, use a comma-separated list of
|
| changenames. Use 'gcl help try' for more information!"""
|
| @@ -1255,6 +1214,7 @@ def CMDtry(args):
|
| return TryChange(change_info, args, swallow_exception=False)
|
|
|
|
|
| +@attrs(usage='<old-name> <new-name>')
|
| def CMDrename(args):
|
| """Renames an existing change."""
|
| if len(args) != 2:
|
| @@ -1272,9 +1232,10 @@ def CMDrename(args):
|
|
|
|
|
| def CMDpassthru(args):
|
| - # Everything else that is passed into gcl we redirect to svn, after adding
|
| - # the files.
|
| - # args is guaranteed to be len(args) >= 1
|
| + """Everything else that is passed into gcl we redirect to svn.
|
| +
|
| + It assumes a change list name is passed and is converted with the files names.
|
| + """
|
| args = ["svn", args[0]]
|
| if len(args) > 1:
|
| root = GetRepositoryRoot()
|
| @@ -1283,61 +1244,73 @@ def CMDpassthru(args):
|
| return RunShellWithReturnCode(args, print_output=True)[1]
|
|
|
|
|
| +def Command(name):
|
| + return getattr(sys.modules[__name__], 'CMD' + name, None)
|
| +
|
| +
|
| +def GenUsage(command):
|
| + """Modify an OptParse object with the function's documentation."""
|
| + obj = Command(command)
|
| + display = command
|
| + more = getattr(obj, 'usage', '')
|
| + if command == 'help':
|
| + display = '<command>'
|
| + need_change = ''
|
| + if getattr(obj, 'need_change', None):
|
| + need_change = ' <change_list>'
|
| + options = ' [options]'
|
| + if getattr(obj, 'no_args', None):
|
| + options = ''
|
| + res = 'Usage: gcl %s%s%s %s\n\n' % (display, need_change, options, more)
|
| + res += re.sub('\n ', '\n', obj.__doc__)
|
| + return res
|
| +
|
| +
|
| +def CMDhelp(args):
|
| + """Prints this help or help for the given command."""
|
| + if args and 'CMD' + args[0] in dir(sys.modules[__name__]):
|
| + print GenUsage(args[0])
|
| +
|
| + # These commands defer to external tools so give this info too.
|
| + if args[0] == 'try':
|
| + TryChange(None, ['--help'], swallow_exception=False)
|
| + if args[0] == 'upload':
|
| + upload.RealMain(['upload.py', '--help'])
|
| + return 0
|
| +
|
| + print GenUsage('help')
|
| + print sys.modules[__name__].__doc__
|
| + print 'version ' + __version__ + '\n'
|
| +
|
| + print('Commands are:\n' + '\n'.join([
|
| + ' %-12s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip())
|
| + for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')]))
|
| + return 0
|
| +
|
| +
|
| def main(argv):
|
| - __pychecker__ = 'maxreturns=0'
|
| try:
|
| - # Create the directories where we store information about changelists if it
|
| - # doesn't exist.
|
| - if not os.path.exists(GetInfoDir()):
|
| - os.mkdir(GetInfoDir())
|
| - if not os.path.exists(GetChangesDir()):
|
| - os.mkdir(GetChangesDir())
|
| - if not os.path.exists(GetCacheDir()):
|
| - os.mkdir(GetCacheDir())
|
| + GetRepositoryRoot()
|
| except gclient_utils.Error:
|
| - # Will throw an exception if not run in a svn checkout.
|
| - pass
|
| + print('To use gcl, you need to be in a subversion checkout.')
|
| + return 1
|
| +
|
| + # Create the directories where we store information about changelists if it
|
| + # doesn't exist.
|
| + if not os.path.exists(GetInfoDir()):
|
| + os.mkdir(GetInfoDir())
|
| + if not os.path.exists(GetChangesDir()):
|
| + os.mkdir(GetChangesDir())
|
| + if not os.path.exists(GetCacheDir()):
|
| + os.mkdir(GetCacheDir())
|
|
|
| if not argv:
|
| argv = ['help']
|
| - # Commands that don't require an argument.
|
| - command = argv[0]
|
| - if command == "opened":
|
| - return CMDopened(argv[1:])
|
| - if command == "status":
|
| - return CMDstatus(argv[1:])
|
| - if command == "nothave":
|
| - return CMDnothave(argv[1:])
|
| - if command == "changes":
|
| - return CMDchanges(argv[1:])
|
| - if command == "help":
|
| - return CMDhelp(argv[1:])
|
| - if command == "diff":
|
| - return CMDdiff(argv[1:])
|
| - if command == "settings":
|
| - return CMDsettings(argv[1:])
|
| - if command == "deleteempties":
|
| - return CMDdeleteempties(argv[1:])
|
| - if command == "rename":
|
| - return CMDrename(argv[1:])
|
| - elif command == "change":
|
| - return CMDchange(argv[1:])
|
| - elif command == "description":
|
| - return CMDdescription(argv[1:])
|
| - elif command == "lint":
|
| - return CMDlint(argv[1:])
|
| - elif command == "upload":
|
| - return CMDupload(argv[1:])
|
| - elif command == "presubmit":
|
| - return CMDpresubmit(argv[1:])
|
| - elif command in ("commit", "submit"):
|
| - return CMDcommit(argv[1:])
|
| - elif command == "delete":
|
| - return CMDdelete(argv[1:])
|
| - elif command == "try":
|
| - return CMDtry(argv[1:])
|
| - else:
|
| - return CMDpassthru(argv)
|
| + command = Command(argv[0])
|
| + if command:
|
| + return command(argv[1:])
|
| + # Unknown command, try to pass that to svn
|
| + return CMDpassthru(argv)
|
|
|
|
|
| if __name__ == "__main__":
|
|
|