diff --git a/completion.zsh b/completion.zsh new file mode 100644 index 000000000..507ed2bb2 --- /dev/null +++ b/completion.zsh @@ -0,0 +1,449 @@ +#compdef _repo repo + +# Copyright (C) 2026 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +_repo() { + local context state line + typeset -A opt_args + + local -a subcommands + subcommands=( + 'abandon:Abandon a development branch' + 'branches:View current topic branches' + 'checkout:Checkout a branch' + 'cherry-pick:Cherry-pick a change' + 'diff:Show changes between commit and working tree' + 'diffmanifests:Show differences between two manifests' + 'download:Download and find a change' + 'forall:Run a shell command in each project' + 'gc:Garbage collect' + 'grep:Print lines matching a pattern' + 'help:Display help about a command' + 'info:Get info about the manifest' + 'init:Initialize a repo client' + 'list:List projects and their names' + 'manifest:Manifest management' + 'overview:Display overview of topic branches' + 'prune:Prune topic branches' + 'rebase:Rebase local branches' + 'selfupdate:Update repo' + 'smartsync:Update working tree to latest known good revision' + 'stage:Stage file(s) for commit' + 'start:Start a new branch for development' + 'status:Show the working tree status' + 'sync:Update working tree' + 'upload:Upload changes to the review server' + 'version:Display version' + 'wipe:Wipe uncommitted changes' + ) + + local -a global_opts + global_opts=( + '(-h --help)'{-h,--help}'[Show help message]' + '--help-all[Show help message for all commands]' + '(-p --paginate)'{-p,--paginate}'[Pipe all output into less]' + '--no-pager[Do not pipe output into a pager]' + '--color=[Control color output]:color:(always never auto)' + '--trace[Trace git command execution]' + '--trace-to-stderr[Trace git command execution to stderr]' + '--trace-python[Trace python execution]' + '--time[Time execute of command]' + '--version[Show version]' + '--show-toplevel[Show top level of workspace]' + '--event-log=[File to log events to]:file:_files' + '--git-trace2-event-log=[File to log git trace2 events to]:file:_files' + '--submanifest-path=[Path to submanifest]:path:_files' + ) + + _arguments -C \ + ${global_opts} \ + '1: :->cmds' \ + '*:: :->subcmds' + + case ${state} in + cmds) + _describe -t commands 'repo command' subcommands + ;; + subcmds) + local cmd=${words[1]} + curcontext="${curcontext%:*}-${cmd}:" + + local -a common_opts + common_opts=( + '(-h --help)'{-h,--help}'[Show help message]' + '(-v --verbose)'{-v,--verbose}'[Show verbose messages]' + '(-q --quiet)'{-q,--quiet}'[Show only errors]' + '(-j --jobs)'{-j,--jobs=}'[Number of jobs to run in parallel]:jobs:' + '--outer-manifest[Use outer manifest]' + '--no-outer-manifest[Do not use outer manifest]' + '--this-manifest-only[Only use this manifest]' + '--no-this-manifest-only[Do not only use this manifest]' + '--all-manifests[Use all manifests]' + ) + + case ${cmd} in + abandon) + _arguments \ + ${common_opts} \ + '--all[Abandon all branches]' \ + '1: :->branch' \ + '*: :->project' + ;; + branches) + _arguments \ + ${common_opts} \ + '*: :->project' + ;; + checkout) + _arguments \ + ${common_opts} \ + '1: :->branch' \ + '*: :->project' + ;; + cherry-pick) + _arguments \ + ${common_opts} \ + '1: :_message "sha1"' + ;; + diff) + _arguments \ + ${common_opts} \ + '(-u --absolute)'{-u,--absolute}'[Show absolute paths]' \ + '*: :->project' + ;; + diffmanifests) + _arguments \ + '--raw[Show raw diff]' \ + '--no-color[Do not show color]' \ + '--pretty-format=[Pretty format]:format:' \ + '1: :_files -g "*.xml"' \ + '2: :_files -g "*.xml"' + ;; + download) + _arguments \ + ${common_opts} \ + '(-b --branch)'{-b,--branch=}'[One or more branches to check]:branch:' \ + '(-c --cherry-pick)'{-c,--cherry-pick}'[Cherry-pick the change]' \ + '(-x --record-origin)'{-x,--record-origin}'[Record origin of cherry-pick]' \ + '(-r --revert)'{-r,--revert}'[Revert the change]' \ + '(-f --ff-only)'{-f,--ff-only}'[Force fast-forward]' \ + '*: :_message "change[/patchset]"' + ;; + forall) + _arguments \ + ${common_opts} \ + '(-r --regex)'{-r,--regex}'[Execute command only on projects matching regex]' \ + '(-i --inverse-regex)'{-i,--inverse-regex}'[Execute command only on projects not matching regex]' \ + '(-g --groups)'{-g,--groups=}'[Execute command only on projects matching groups]:groups:' \ + '(-c --command)'{-c,--command}'[Command to execute]' \ + '(-e --abort-on-errors)'{-e,--abort-on-errors}'[Abort on errors]' \ + '--ignore-missing[Ignore missing projects]' \ + '--interactive[Run interactively]' \ + '-p[Show project headers]' \ + '*: :->project' + ;; + gc) + _arguments \ + ${common_opts} \ + '(-n --dry-run)'{-n,--dry-run}'[Dry run]' \ + '(-y --yes)'{-y,--yes}'[Answer yes to all prompts]' \ + '--repack[Repack objects]' + ;; + grep) + _arguments \ + ${common_opts} \ + '--cached[Search cached files]' \ + '(-r --revision)'{-r,--revision=}'[Search in revision]:revision:' \ + '-e[Pattern]' \ + '(-i --ignore-case)'{-i,--ignore-case}'[Ignore case]' \ + '(-a --text)'{-a,--text}'[Treat all files as text]' \ + '-I[Do not match binary files]' \ + '(-w --word-regexp)'{-w,--word-regexp}'[Match word boundaries]' \ + '(-v --invert-match)'{-v,--invert-match}'[Invert match]' \ + '(-G --basic-regexp)'{-G,--basic-regexp}'[Basic regexp]' \ + '(-E --extended-regexp)'{-E,--extended-regexp}'[Extended regexp]' \ + '(-F --fixed-strings)'{-F,--fixed-strings}'[Fixed strings]' \ + '--all-match[All match]' \ + '--and[And]' \ + '--or[Or]' \ + '--not[Not]' \ + '-([Open paren]' \ + '-)[Close paren]' \ + '-n[Line number]' \ + '-C[Context]:lines:' \ + '-B[Before context]:lines:' \ + '-A[After context]:lines:' \ + '-l[Name only]' \ + '--name-only[Name only]' \ + '--files-with-matches[Files with matches]' \ + '-L[Files without match]' \ + '--files-without-match[Files without match]' \ + '1: :->pattern' \ + '*: :->project' + ;; + help) + _arguments \ + '(-a --all)'{-a,--all}'[Show all commands]' \ + '--help-all[Show help message for all commands]' \ + '1: :->help_cmds' + ;; + info) + _arguments \ + ${common_opts} \ + '(-d --diff)'{-d,--diff}'[Show diff]' \ + '(-o --overview)'{-o,--overview}'[Show overview]' \ + '(-c --current-branch)'{-c,--current-branch}'[Show current branch]' \ + '--no-current-branch[Do not show current branch]' \ + '(-l --local-only)'{-l,--local-only}'[Local only]' \ + '*: :->project' + ;; + init) + _arguments \ + '(-u --manifest-url)'{-u,--manifest-url=}'[Manifest URL]:url:' \ + '(-b --manifest-branch)'{-b,--manifest-branch=}'[Manifest branch]:branch:' \ + '--manifest-upstream-branch=[Manifest upstream branch]:branch:' \ + '(-m --manifest-name)'{-m,--manifest-name=}'[Manifest name]:file:_files -g "*.xml"' \ + '(-g --groups)'{-g,--groups=}'[Restrict manifest projects to groups]:groups:' \ + '(-p --platform)'{-p,--platform=}'[Restrict manifest projects to platform]:platform:' \ + '--submodules[Sync submodules]' \ + '--standalone-manifest[Standalone manifest]' \ + '--manifest-depth=[Manifest depth]:depth:' \ + '(-c --current-branch)'{-c,--current-branch}'[Sync current branch only]' \ + '--no-current-branch[Do not sync current branch only]' \ + '--tags[Sync tags]' \ + '--no-tags[Do not sync tags]' \ + '--mirror[Mirror]' \ + '--archive[Archive]' \ + '--worktree[Worktree]' \ + '--reference=[Reference repository]:repository:_files -/' \ + '--dissociate[Dissociate from reference]' \ + '--depth=[Depth]:depth:' \ + '--partial-clone[Partial clone]' \ + '--no-partial-clone[Do not partial clone]' \ + '--partial-clone-exclude=[Exclude from partial clone]:projects:' \ + '--clone-filter=[Clone filter]:filter:' \ + '--use-superproject[Use superproject]' \ + '--no-use-superproject[Do not use superproject]' \ + '--clone-bundle[Use clone bundle]' \ + '--no-clone-bundle[Do not use clone bundle]' \ + '--git-lfs[Use git lfs]' \ + '--no-git-lfs[Do not use git lfs]' \ + '--repo-url=[Repo URL]:url:' \ + '--repo-rev=[Repo revision]:revision:' \ + '--no-repo-verify[Do not verify repo]' \ + '--config-name[Use config name]' + ;; + list) + _arguments \ + ${common_opts} \ + '(-r --regex)'{-r,--regex}'[Filter by regex]' \ + '(-g --groups)'{-g,--groups=}'[Filter by groups]:groups:' \ + '(-a --all)'{-a,--all}'[Show all projects]' \ + '(-n --name-only)'{-n,--name-only}'[Show only names]' \ + '(-p --path-only)'{-p,--path-only}'[Show only paths]' \ + '(-f --fullpath)'{-f,--fullpath}'[Show full paths]' \ + '--relative-to=[Show paths relative to]:path:_files -/' \ + '*: :->project' + ;; + manifest) + _arguments \ + '(-r --revision-as-HEAD)'{-r,--revision-as-HEAD}'[Save revisions as current HEAD]' \ + '(-m --manifest-name)'{-m,--manifest-name=}'[Manifest name]:file:_files -g "*.xml"' \ + '--suppress-upstream-revision[Suppress upstream revision]' \ + '--suppress-dest-branch[Suppress dest branch]' \ + '--format=[Output format]:format:(xml json)' \ + '--pretty[Pretty print]' \ + '--no-local-manifests[Ignore local manifests]' \ + '(-o --output-file)'{-o,--output-file=}'[Output file]:file:_files' + ;; + overview) + _arguments \ + ${common_opts} \ + '(-c --current-branch)'{-c,--current-branch}'[Show current branch]' \ + '--no-current-branch[Do not show current branch]' \ + '*: :->project' + ;; + prune) + _arguments \ + ${common_opts} \ + '*: :->project' + ;; + rebase) + _arguments \ + ${common_opts} \ + '--fail-fast[Fail fast]' \ + '(-f --force-rebase)'{-f,--force-rebase}'[Force rebase]' \ + '--no-ff[No fast forward]' \ + '--autosquash[Autosquash]' \ + '--whitespace=[Whitespace option]:option:' \ + '--auto-stash[Auto stash]' \ + '(-m --onto-manifest)'{-m,--onto-manifest}'[Rebase onto manifest]' \ + '(-i --interactive)'{-i,--interactive}'[Interactive rebase]' \ + '*: :->project' + ;; + selfupdate) + _arguments \ + '--no-repo-verify[Do not verify repo]' + ;; + smartsync | sync) + _arguments \ + ${common_opts} \ + '--jobs-network=[Number of network jobs]:jobs:' \ + '--jobs-checkout=[Number of checkout jobs]:jobs:' \ + '(-f --force-broken)'{-f,--force-broken}'[Continue sync even if a project fails]' \ + '--fail-fast[Fail fast]' \ + '--force-sync[Overwrite existing git directories]' \ + '--force-checkout[Force checkout]' \ + '--force-remove-dirty[Force remove dirty]' \ + '--rebase[Rebase local branches]' \ + '(-l --local-only)'{-l,--local-only}'[Only use local files]' \ + '--no-manifest-update[Do not update manifest]' \ + '--nmu[Do not update manifest]' \ + '--interleaved[Interleave output]' \ + '--no-interleaved[Do not interleave output]' \ + '(-n --network-only)'{-n,--network-only}'[Only fetch]' \ + '(-d --detach)'{-d,--detach}'[Detach HEAD]' \ + '(-c --current-branch)'{-c,--current-branch}'[Fetch only current branch]' \ + '--no-current-branch[Do not fetch only current branch]' \ + '(-m --manifest-name)'{-m,--manifest-name=}'[Manifest name]:file:_files -g "*.xml"' \ + '--clone-bundle[Use clone bundle]' \ + '--no-clone-bundle[Do not use clone bundle]' \ + '(-u --manifest-server-username)'{-u,--manifest-server-username=}'[Username for manifest server]:username:' \ + '(-p --manifest-server-password)'{-p,--manifest-server-password=}'[Password for manifest server]:password:' \ + '--fetch-submodules[Fetch submodules]' \ + '--use-superproject[Use superproject]' \ + '--no-use-superproject[Do not use superproject]' \ + '--tags[Sync tags]' \ + '--no-tags[Do not sync tags]' \ + '--optimized-fetch[Optimized fetch]' \ + '--retry-fetches=[Retry fetches]:count:' \ + '--prune[Prune]' \ + '--no-prune[Do not prune]' \ + '--auto-gc[Run auto gc]' \ + '--no-auto-gc[Do not run auto gc]' \ + '(-s --smart-sync)'{-s,--smart-sync}'[Smart sync]' \ + '(-t --smart-tag)'{-t,--smart-tag=}'[Smart tag]:tag:' \ + '--no-repo-verify[Do not verify repo]' \ + '--no-verify[Do not verify]' \ + '--verify[Verify]' \ + '--ignore-hooks[Ignore hooks]' \ + '*: :->project' + ;; + stage) + _arguments \ + ${common_opts} \ + '(-i --interactive)'{-i,--interactive}'[Interactive staging]' \ + '*: :->project' + ;; + start) + _arguments \ + ${common_opts} \ + '--all[Start branch in all projects]' \ + '(-r --rev --revision)'{-r,--rev=,--revision=}'[Revision]:revision:' \ + '--head[Head]' \ + '--HEAD[Head]' \ + '1: :->newbranch' \ + '*: :->project' + ;; + status) + _arguments \ + ${common_opts} \ + '(-o --orphans)'{-o,--orphans}'[Show orphans]' \ + '*: :->project' + ;; + upload) + _arguments \ + ${common_opts} \ + '(-t --topic-branch)'{-t,--topic-branch}'[Topic branch]' \ + '--topic=[Topic]:topic:' \ + '--hashtag=[Hashtag]:hashtag:' \ + '--ht=[Hashtag]:hashtag:' \ + '--hashtag-branch[Hashtag branch]' \ + '--htb[Hashtag branch]' \ + '(-l --label)'{-l,--label=}'[Label]:label:' \ + '--pd=[Patchset description]:description:' \ + '--patchset-description=[Patchset description]:description:' \ + '--re=[Reviewers]:reviewers:' \ + '--reviewers=[Reviewers]:reviewers:' \ + '--cc=[CC]:cc:' \ + '--br=[Branch]:branch:' \ + '--branch=[Branch]:branch:' \ + '(-c --current-branch)'{-c,--current-branch}'[Upload current branch]' \ + '--no-current-branch[Do not upload current branch]' \ + '--ne[No emails]' \ + '--no-emails[No emails]' \ + '(-p --private)'{-p,--private}'[Private]' \ + '(-w --wip)'{-w,--wip}'[Work in progress]' \ + '(-r --ready)'{-r,--ready}'[Ready]' \ + '(-o --push-option)'{-o,--push-option=}'[Push option]:option:' \ + '(-D --destination --dest)'{-D,--destination=,--dest=}'[Destination]:destination:' \ + '(-n --dry-run)'{-n,--dry-run}'[Dry run]' \ + '(-y --yes)'{-y,--yes}'[Answer yes to all prompts]' \ + '--ignore-untracked-files[Ignore untracked files]' \ + '--no-ignore-untracked-files[Do not ignore untracked files]' \ + '--no-cert-checks[Do not check certificates]' \ + '--no-verify[Do not verify]' \ + '--verify[Verify]' \ + '--ignore-hooks[Ignore hooks]' \ + '*: :->project' + ;; + version) + _arguments ${common_opts} + ;; + wipe) + _arguments \ + ${common_opts} \ + '(-f --force)'{-f,--force}'[Force wipe]' \ + '--force-uncommitted[Force uncommitted]' \ + '--force-shared[Force shared]' \ + '*: :->project' + ;; + esac + + # Handle states for positional arguments. + case ${state} in + branch) + [[ ${PREFIX} != -* ]] && _repo_branches + ;; + project) + [[ ${PREFIX} != -* ]] && _repo_projects + ;; + pattern) + _message 'pattern' + ;; + newbranch) + _message 'new branch name' + ;; + help_cmds) + _describe -t commands 'repo command' subcommands + ;; + esac + ;; + esac +} + +_repo_branches() { + local -a branches + branches=(${(f)"$(_call_program branches repo branches 2>/dev/null | sed -E 's/^.*[[:space:]]([^[:space:]]+)[[:space:]]+\|.*$/\1/' | awk '{print $1}')"}) + _describe -t branches 'branch' branches +} + +_repo_projects() { + local -a projects + projects=(${(f)"$(_call_program projects repo list -n 2>/dev/null)"}) + _describe -t projects 'project' projects +} + +_repo "$@"