summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Frysinger <vapier@google.com>2019-08-27 01:10:59 -0400
committerMike Frysinger <vapier@google.com>2019-08-28 03:54:11 +0000
commitae6cb08ae52d488a4cc6892f811c1c1acf8c3c12 (patch)
treec927415df288d9bf9076e758835db53cc633597d
parent3fc157285cb61d6a4faa55dc4f011fb94d598c20 (diff)
downloadgit-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.py10
-rwxr-xr-xmain.py1
-rw-r--r--subcmds/abandon.py10
-rw-r--r--subcmds/checkout.py3
-rw-r--r--subcmds/cherry_pick.py3
-rw-r--r--subcmds/diffmanifests.py5
-rw-r--r--subcmds/forall.py3
-rw-r--r--subcmds/init.py11
-rw-r--r--subcmds/list.py9
-rw-r--r--subcmds/manifest.py11
-rw-r--r--subcmds/start.py7
-rw-r--r--subcmds/sync.py34
12 files changed, 55 insertions, 52 deletions
diff --git a/command.py b/command.py
index 8c5e2461..f7d20a22 100644
--- a/command.py
+++ b/command.py
@@ -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 """
diff --git a/main.py b/main.py
index 889fc216..2ab79b57 100755
--- a/main.py
+++ b/main.py
@@ -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)