diff options
author | Mike Frysinger <vapier@google.com> | 2019-08-27 01:10:59 -0400 |
---|---|---|
committer | Mike Frysinger <vapier@google.com> | 2019-08-28 03:54:11 +0000 |
commit | ae6cb08ae52d488a4cc6892f811c1c1acf8c3c12 (patch) | |
tree | c927415df288d9bf9076e758835db53cc633597d | |
parent | 3fc157285cb61d6a4faa55dc4f011fb94d598c20 (diff) | |
download | git-repo-ae6cb08ae52d488a4cc6892f811c1c1acf8c3c12.tar.gz |
split out cli validation from executionv1.13.5
A common pattern in our subcommands is to verify the arguments &
options before executing things. For some subcommands, that check
stage is quite long which makes the execution function even bigger.
Lets split that logic out of the execute phase so it's easier to
manage these.
This is most noticeable in the sync subcommand whose Execute func
is quite large, and the option checking makes up ~15% of it.
The manifest command's Execute can be simplified significantly as
the optparse configuration always sets output_file to a string.
Change-Id: I7097847ff040e831345e63de6b467ee17609990e
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/234834
Reviewed-by: David Pursehouse <dpursehouse@collab.net>
Tested-by: Mike Frysinger <vapier@google.com>
-rw-r--r-- | command.py | 10 | ||||
-rwxr-xr-x | main.py | 1 | ||||
-rw-r--r-- | subcmds/abandon.py | 10 | ||||
-rw-r--r-- | subcmds/checkout.py | 3 | ||||
-rw-r--r-- | subcmds/cherry_pick.py | 3 | ||||
-rw-r--r-- | subcmds/diffmanifests.py | 5 | ||||
-rw-r--r-- | subcmds/forall.py | 3 | ||||
-rw-r--r-- | subcmds/init.py | 11 | ||||
-rw-r--r-- | subcmds/list.py | 9 | ||||
-rw-r--r-- | subcmds/manifest.py | 11 | ||||
-rw-r--r-- | subcmds/start.py | 7 | ||||
-rw-r--r-- | subcmds/sync.py | 34 |
12 files changed, 55 insertions, 52 deletions
@@ -98,6 +98,16 @@ class Command(object): | |||
98 | self.OptionParser.print_usage() | 98 | self.OptionParser.print_usage() |
99 | sys.exit(1) | 99 | sys.exit(1) |
100 | 100 | ||
101 | def ValidateOptions(self, opt, args): | ||
102 | """Validate the user options & arguments before executing. | ||
103 | |||
104 | This is meant to help break the code up into logical steps. Some tips: | ||
105 | * Use self.OptionParser.error to display CLI related errors. | ||
106 | * Adjust opt member defaults as makes sense. | ||
107 | * Adjust the args list, but do so inplace so the caller sees updates. | ||
108 | * Try to avoid updating self state. Leave that to Execute. | ||
109 | """ | ||
110 | |||
101 | def Execute(self, opt, args): | 111 | def Execute(self, opt, args): |
102 | """Perform the action, after option parsing is complete. | 112 | """Perform the action, after option parsing is complete. |
103 | """ | 113 | """ |
@@ -197,6 +197,7 @@ class _Repo(object): | |||
197 | cmd_event = cmd.event_log.Add(name, event_log.TASK_COMMAND, start) | 197 | cmd_event = cmd.event_log.Add(name, event_log.TASK_COMMAND, start) |
198 | cmd.event_log.SetParent(cmd_event) | 198 | cmd.event_log.SetParent(cmd_event) |
199 | try: | 199 | try: |
200 | cmd.ValidateOptions(copts, cargs) | ||
200 | result = cmd.Execute(copts, cargs) | 201 | result = cmd.Execute(copts, cargs) |
201 | except (DownloadError, ManifestInvalidRevisionError, | 202 | except (DownloadError, ManifestInvalidRevisionError, |
202 | NoManifestException) as e: | 203 | NoManifestException) as e: |
diff --git a/subcmds/abandon.py b/subcmds/abandon.py index 319262bd..cd1d0c40 100644 --- a/subcmds/abandon.py +++ b/subcmds/abandon.py | |||
@@ -37,19 +37,19 @@ It is equivalent to "git branch -D <branchname>". | |||
37 | dest='all', action='store_true', | 37 | dest='all', action='store_true', |
38 | help='delete all branches in all projects') | 38 | help='delete all branches in all projects') |
39 | 39 | ||
40 | def Execute(self, opt, args): | 40 | def ValidateOptions(self, opt, args): |
41 | if not opt.all and not args: | 41 | if not opt.all and not args: |
42 | self.Usage() | 42 | self.Usage() |
43 | 43 | ||
44 | if not opt.all: | 44 | if not opt.all: |
45 | nb = args[0] | 45 | nb = args[0] |
46 | if not git.check_ref_format('heads/%s' % nb): | 46 | if not git.check_ref_format('heads/%s' % nb): |
47 | print("error: '%s' is not a valid name" % nb, file=sys.stderr) | 47 | self.OptionParser.error("'%s' is not a valid branch name" % nb) |
48 | sys.exit(1) | ||
49 | else: | 48 | else: |
50 | args.insert(0,None) | 49 | args.insert(0, "'All local branches'") |
51 | nb = "'All local branches'" | ||
52 | 50 | ||
51 | def Execute(self, opt, args): | ||
52 | nb = args[0] | ||
53 | err = defaultdict(list) | 53 | err = defaultdict(list) |
54 | success = defaultdict(list) | 54 | success = defaultdict(list) |
55 | all_projects = self.GetProjects(args[1:]) | 55 | all_projects = self.GetProjects(args[1:]) |
diff --git a/subcmds/checkout.py b/subcmds/checkout.py index 51ac4833..c8a09a8e 100644 --- a/subcmds/checkout.py +++ b/subcmds/checkout.py | |||
@@ -34,10 +34,11 @@ The command is equivalent to: | |||
34 | repo forall [<project>...] -c git checkout <branchname> | 34 | repo forall [<project>...] -c git checkout <branchname> |
35 | """ | 35 | """ |
36 | 36 | ||
37 | def Execute(self, opt, args): | 37 | def ValidateOptions(self, opt, args): |
38 | if not args: | 38 | if not args: |
39 | self.Usage() | 39 | self.Usage() |
40 | 40 | ||
41 | def Execute(self, opt, args): | ||
41 | nb = args[0] | 42 | nb = args[0] |
42 | err = [] | 43 | err = [] |
43 | success = [] | 44 | success = [] |
diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py index 43215f91..a541a040 100644 --- a/subcmds/cherry_pick.py +++ b/subcmds/cherry_pick.py | |||
@@ -37,10 +37,11 @@ change id will be added. | |||
37 | def _Options(self, p): | 37 | def _Options(self, p): |
38 | pass | 38 | pass |
39 | 39 | ||
40 | def Execute(self, opt, args): | 40 | def ValidateOptions(self, opt, args): |
41 | if len(args) != 1: | 41 | if len(args) != 1: |
42 | self.Usage() | 42 | self.Usage() |
43 | 43 | ||
44 | def Execute(self, opt, args): | ||
44 | reference = args[0] | 45 | reference = args[0] |
45 | 46 | ||
46 | p = GitCommand(None, | 47 | p = GitCommand(None, |
diff --git a/subcmds/diffmanifests.py b/subcmds/diffmanifests.py index cae776a7..b999699e 100644 --- a/subcmds/diffmanifests.py +++ b/subcmds/diffmanifests.py | |||
@@ -176,10 +176,11 @@ synced and their revisions won't be found. | |||
176 | self.printText(log) | 176 | self.printText(log) |
177 | self.out.nl() | 177 | self.out.nl() |
178 | 178 | ||
179 | def Execute(self, opt, args): | 179 | def ValidateOptions(self, opt, args): |
180 | if not args or len(args) > 2: | 180 | if not args or len(args) > 2: |
181 | self.Usage() | 181 | self.OptionParser.error('missing manifests to diff') |
182 | 182 | ||
183 | def Execute(self, opt, args): | ||
183 | self.out = _Coloring(self.manifest.globalConfig) | 184 | self.out = _Coloring(self.manifest.globalConfig) |
184 | self.printText = self.out.nofmt_printer('text') | 185 | self.printText = self.out.nofmt_printer('text') |
185 | if opt.color: | 186 | if opt.color: |
diff --git a/subcmds/forall.py b/subcmds/forall.py index 3e6007ba..0be8d3bc 100644 --- a/subcmds/forall.py +++ b/subcmds/forall.py | |||
@@ -177,10 +177,11 @@ without iterating through the remaining projects. | |||
177 | 'worktree': project.worktree, | 177 | 'worktree': project.worktree, |
178 | } | 178 | } |
179 | 179 | ||
180 | def Execute(self, opt, args): | 180 | def ValidateOptions(self, opt, args): |
181 | if not opt.command: | 181 | if not opt.command: |
182 | self.Usage() | 182 | self.Usage() |
183 | 183 | ||
184 | def Execute(self, opt, args): | ||
184 | cmd = [opt.command[0]] | 185 | cmd = [opt.command[0]] |
185 | 186 | ||
186 | shell = True | 187 | shell = True |
diff --git a/subcmds/init.py b/subcmds/init.py index eaa6da50..32663a04 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -436,18 +436,17 @@ to update the working directory files. | |||
436 | print(' rm -r %s/.repo' % self.manifest.topdir) | 436 | print(' rm -r %s/.repo' % self.manifest.topdir) |
437 | print('and try again.') | 437 | print('and try again.') |
438 | 438 | ||
439 | def Execute(self, opt, args): | 439 | def ValidateOptions(self, opt, args): |
440 | git_require(MIN_GIT_VERSION, fail=True) | ||
441 | |||
442 | if opt.reference: | 440 | if opt.reference: |
443 | opt.reference = os.path.expanduser(opt.reference) | 441 | opt.reference = os.path.expanduser(opt.reference) |
444 | 442 | ||
445 | # Check this here, else manifest will be tagged "not new" and init won't be | 443 | # Check this here, else manifest will be tagged "not new" and init won't be |
446 | # possible anymore without removing the .repo/manifests directory. | 444 | # possible anymore without removing the .repo/manifests directory. |
447 | if opt.archive and opt.mirror: | 445 | if opt.archive and opt.mirror: |
448 | print('fatal: --mirror and --archive cannot be used together.', | 446 | self.OptionParser.error('--mirror and --archive cannot be used together.') |
449 | file=sys.stderr) | 447 | |
450 | sys.exit(1) | 448 | def Execute(self, opt, args): |
449 | git_require(MIN_GIT_VERSION, fail=True) | ||
451 | 450 | ||
452 | self._SyncManifest(opt) | 451 | self._SyncManifest(opt) |
453 | self._LinkManifest(opt.manifest_name) | 452 | self._LinkManifest(opt.manifest_name) |
diff --git a/subcmds/list.py b/subcmds/list.py index 961b1956..00172f0e 100644 --- a/subcmds/list.py +++ b/subcmds/list.py | |||
@@ -49,6 +49,10 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. | |||
49 | dest='path_only', action='store_true', | 49 | dest='path_only', action='store_true', |
50 | help="Display only the path of the repository") | 50 | help="Display only the path of the repository") |
51 | 51 | ||
52 | def ValidateOptions(self, opt, args): | ||
53 | if opt.fullpath and opt.name_only: | ||
54 | self.OptionParser.error('cannot combine -f and -n') | ||
55 | |||
52 | def Execute(self, opt, args): | 56 | def Execute(self, opt, args): |
53 | """List all projects and the associated directories. | 57 | """List all projects and the associated directories. |
54 | 58 | ||
@@ -60,11 +64,6 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. | |||
60 | opt: The options. | 64 | opt: The options. |
61 | args: Positional args. Can be a list of projects to list, or empty. | 65 | args: Positional args. Can be a list of projects to list, or empty. |
62 | """ | 66 | """ |
63 | |||
64 | if opt.fullpath and opt.name_only: | ||
65 | print('error: cannot combine -f and -n', file=sys.stderr) | ||
66 | sys.exit(1) | ||
67 | |||
68 | if not opt.regex: | 67 | if not opt.regex: |
69 | projects = self.GetProjects(args, groups=opt.groups) | 68 | projects = self.GetProjects(args, groups=opt.groups) |
70 | else: | 69 | else: |
diff --git a/subcmds/manifest.py b/subcmds/manifest.py index 07fa323a..768f072e 100644 --- a/subcmds/manifest.py +++ b/subcmds/manifest.py | |||
@@ -73,14 +73,9 @@ in a Git repository for use during future 'repo init' invocations. | |||
73 | if opt.output_file != '-': | 73 | if opt.output_file != '-': |
74 | print('Saved manifest to %s' % opt.output_file, file=sys.stderr) | 74 | print('Saved manifest to %s' % opt.output_file, file=sys.stderr) |
75 | 75 | ||
76 | def Execute(self, opt, args): | 76 | def ValidateOptions(self, opt, args): |
77 | if args: | 77 | if args: |
78 | self.Usage() | 78 | self.Usage() |
79 | 79 | ||
80 | if opt.output_file is not None: | 80 | def Execute(self, opt, args): |
81 | self._Output(opt) | 81 | self._Output(opt) |
82 | return | ||
83 | |||
84 | print('error: no operation to perform', file=sys.stderr) | ||
85 | print('error: see repo help manifest', file=sys.stderr) | ||
86 | sys.exit(1) | ||
diff --git a/subcmds/start.py b/subcmds/start.py index 0c60d78c..5d4c9c01 100644 --- a/subcmds/start.py +++ b/subcmds/start.py | |||
@@ -41,15 +41,16 @@ revision specified in the manifest. | |||
41 | dest='all', action='store_true', | 41 | dest='all', action='store_true', |
42 | help='begin branch in all projects') | 42 | help='begin branch in all projects') |
43 | 43 | ||
44 | def Execute(self, opt, args): | 44 | def ValidateOptions(self, opt, args): |
45 | if not args: | 45 | if not args: |
46 | self.Usage() | 46 | self.Usage() |
47 | 47 | ||
48 | nb = args[0] | 48 | nb = args[0] |
49 | if not git.check_ref_format('heads/%s' % nb): | 49 | if not git.check_ref_format('heads/%s' % nb): |
50 | print("error: '%s' is not a valid name" % nb, file=sys.stderr) | 50 | self.OptionParser.error("'%s' is not a valid name" % nb) |
51 | sys.exit(1) | ||
52 | 51 | ||
52 | def Execute(self, opt, args): | ||
53 | nb = args[0] | ||
53 | err = [] | 54 | err = [] |
54 | projects = [] | 55 | projects = [] |
55 | if not opt.all: | 56 | if not opt.all: |
diff --git a/subcmds/sync.py b/subcmds/sync.py index 3eab2fcf..5655a1e6 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -738,36 +738,30 @@ later is required to fix a server side protocol bug. | |||
738 | fd.close() | 738 | fd.close() |
739 | return 0 | 739 | return 0 |
740 | 740 | ||
741 | def Execute(self, opt, args): | 741 | def ValidateOptions(self, opt, args): |
742 | if opt.jobs: | ||
743 | self.jobs = opt.jobs | ||
744 | if self.jobs > 1: | ||
745 | soft_limit, _ = _rlimit_nofile() | ||
746 | self.jobs = min(self.jobs, (soft_limit - 5) // 3) | ||
747 | |||
748 | if opt.force_broken: | 742 | if opt.force_broken: |
749 | print('warning: -f/--force-broken is now the default behavior, and the ' | 743 | print('warning: -f/--force-broken is now the default behavior, and the ' |
750 | 'options are deprecated', file=sys.stderr) | 744 | 'options are deprecated', file=sys.stderr) |
751 | if opt.network_only and opt.detach_head: | 745 | if opt.network_only and opt.detach_head: |
752 | print('error: cannot combine -n and -d', file=sys.stderr) | 746 | self.OptionParser.error('cannot combine -n and -d') |
753 | sys.exit(1) | ||
754 | if opt.network_only and opt.local_only: | 747 | if opt.network_only and opt.local_only: |
755 | print('error: cannot combine -n and -l', file=sys.stderr) | 748 | self.OptionParser.error('cannot combine -n and -l') |
756 | sys.exit(1) | ||
757 | if opt.manifest_name and opt.smart_sync: | 749 | if opt.manifest_name and opt.smart_sync: |
758 | print('error: cannot combine -m and -s', file=sys.stderr) | 750 | self.OptionParser.error('cannot combine -m and -s') |
759 | sys.exit(1) | ||
760 | if opt.manifest_name and opt.smart_tag: | 751 | if opt.manifest_name and opt.smart_tag: |
761 | print('error: cannot combine -m and -t', file=sys.stderr) | 752 | self.OptionParser.error('cannot combine -m and -t') |
762 | sys.exit(1) | ||
763 | if opt.manifest_server_username or opt.manifest_server_password: | 753 | if opt.manifest_server_username or opt.manifest_server_password: |
764 | if not (opt.smart_sync or opt.smart_tag): | 754 | if not (opt.smart_sync or opt.smart_tag): |
765 | print('error: -u and -p may only be combined with -s or -t', | 755 | self.OptionParser.error('-u and -p may only be combined with -s or -t') |
766 | file=sys.stderr) | ||
767 | sys.exit(1) | ||
768 | if None in [opt.manifest_server_username, opt.manifest_server_password]: | 756 | if None in [opt.manifest_server_username, opt.manifest_server_password]: |
769 | print('error: both -u and -p must be given', file=sys.stderr) | 757 | self.OptionParser.error('both -u and -p must be given') |
770 | sys.exit(1) | 758 | |
759 | def Execute(self, opt, args): | ||
760 | if opt.jobs: | ||
761 | self.jobs = opt.jobs | ||
762 | if self.jobs > 1: | ||
763 | soft_limit, _ = _rlimit_nofile() | ||
764 | self.jobs = min(self.jobs, (soft_limit - 5) // 3) | ||
771 | 765 | ||
772 | if opt.manifest_name: | 766 | if opt.manifest_name: |
773 | self.manifest.Override(opt.manifest_name) | 767 | self.manifest.Override(opt.manifest_name) |