Change-Id: I94d943f707b4e3cfcb7db577fc95e489b2c47cc1
This commit is contained in:
Raman Tenneti 2021-07-28 14:01:11 -07:00
commit 6edbd1ab2d
12 changed files with 200 additions and 71 deletions

View File

@ -14,6 +14,9 @@
# Programmable bash completion. https://github.com/scop/bash-completion
# TODO: Handle interspersed options. We handle `repo h<tab>`, but not
# `repo --time h<tab>`.
# Complete the list of repo subcommands.
__complete_repo_list_commands() {
local repo=${COMP_WORDS[0]}
@ -37,6 +40,7 @@ __complete_repo_list_branches() {
__complete_repo_list_projects() {
local repo=${COMP_WORDS[0]}
"${repo}" list -n 2>/dev/null
"${repo}" list -p --relative-to=. 2>/dev/null
}
# Complete the repo <command> argument.
@ -66,6 +70,48 @@ __complete_repo_command_projects() {
COMPREPLY=($(compgen -W "$(__complete_repo_list_projects)" -- "${current}"))
}
# Complete `repo help`.
__complete_repo_command_help() {
local current=$1
# CWORD=1 is "start".
# CWORD=2 is the <subcommand> which we complete here.
if [[ ${COMP_CWORD} -eq 2 ]]; then
COMPREPLY=(
$(compgen -W "$(__complete_repo_list_commands)" -- "${current}")
)
fi
}
# Complete `repo forall`.
__complete_repo_command_forall() {
local current=$1
# CWORD=1 is "forall".
# CWORD=2+ are <projects> *until* we hit the -c option.
local i
for (( i = 0; i < COMP_CWORD; ++i )); do
if [[ "${COMP_WORDS[i]}" == "-c" ]]; then
return 0
fi
done
COMPREPLY=(
$(compgen -W "$(__complete_repo_list_projects)" -- "${current}")
)
}
# Complete `repo start`.
__complete_repo_command_start() {
local current=$1
# CWORD=1 is "start".
# CWORD=2 is the <branch> which we don't complete.
# CWORD=3+ are <projects> which we complete here.
if [[ ${COMP_CWORD} -gt 2 ]]; then
COMPREPLY=(
$(compgen -W "$(__complete_repo_list_projects)" -- "${current}")
)
fi
}
# Complete the repo subcommand arguments.
__complete_repo_arg() {
if [[ ${COMP_CWORD} -le 1 ]]; then
@ -86,21 +132,8 @@ __complete_repo_arg() {
return 0
;;
help)
if [[ ${COMP_CWORD} -eq 2 ]]; then
COMPREPLY=(
$(compgen -W "$(__complete_repo_list_commands)" -- "${current}")
)
fi
return 0
;;
start)
if [[ ${COMP_CWORD} -gt 2 ]]; then
COMPREPLY=(
$(compgen -W "$(__complete_repo_list_projects)" -- "${current}")
)
fi
help|start|forall)
__complete_repo_command_${command} "${current}"
return 0
;;
@ -118,4 +151,6 @@ __complete_repo() {
return 0
}
complete -F __complete_repo repo
# Fallback to the default complete methods if we aren't able to provide anything
# useful. This will allow e.g. local paths to be used when it makes sense.
complete -F __complete_repo -o bashdefault -o default repo

View File

@ -31,7 +31,8 @@ from repo_trace import Trace
from git_command import GitCommand
from git_refs import R_CHANGES, R_HEADS, R_TAGS
# Prefix Add all the data of SyncAnalysisState object.
# Prefix that is prepended to all the keys of SyncAnalysisState's data
# that is saved in the config.
SYNC_STATE_PREFIX = 'syncstate.'
ID_RE = re.compile(r'^[0-9a-f]{40}$')
@ -271,10 +272,7 @@ class GitConfig(object):
return {k: v for k, v in self.DumpConfigDict().items() if k.startswith(SYNC_STATE_PREFIX)}
def UpdateSyncAnalysisState(self, options, superproject_logging_data):
"""Update Config's SyncAnalysisState with the latest sync data.
Creates SyncAnalysisState object with |options| and |superproject_logging_data|
which in turn persists the data into the |self| object.
"""Update Config's SYNC_STATE_PREFIX* data with the latest sync data.
Args:
options: Options passed to sync returned from optparse. See _Options().
@ -743,7 +741,7 @@ class Branch(object):
class SyncAnalysisState():
"""Configuration options related to logging of Sync state for analysis.
"""Configuration options related to logging of sync state for analysis.
This object is versioned.
"""
@ -786,7 +784,7 @@ class SyncAnalysisState():
def _Set(self, key, value):
"""Set the |value| for a |key| in the |_config| member.
Every key is stored by prepending the value of SYNC_STATE_PREFIX constant to it.
|key| is prepended with the value of SYNC_STATE_PREFIX constant.
Args:
key: Name of the key.

View File

@ -106,6 +106,11 @@ class Superproject(object):
"""Returns a dictionary of projects and their commit ids."""
return self._project_commit_ids
@property
def manifest_path(self):
"""Returns the manifest path if the path exists or None."""
return self._manifest_path if os.path.exists(self._manifest_path) else None
def _GetBranch(self):
"""Returns the branch name for getting the approved manifest."""
p = self._manifest.manifestProject

73
main.py
View File

@ -95,6 +95,8 @@ global_options = optparse.OptionParser(
add_help_option=False)
global_options.add_option('-h', '--help', action='store_true',
help='show this help message and exit')
global_options.add_option('--help-all', action='store_true',
help='show this help message with all subcommands and exit')
global_options.add_option('-p', '--paginate',
dest='pager', action='store_true',
help='display command output in the pager')
@ -116,6 +118,10 @@ global_options.add_option('--time',
global_options.add_option('--version',
dest='show_version', action='store_true',
help='display this version of repo')
global_options.add_option('--show-toplevel',
action='store_true',
help='display the path of the top-level directory of '
'the repo client checkout')
global_options.add_option('--event-log',
dest='event_log', action='store',
help='filename of event log to append timeline to')
@ -128,34 +134,40 @@ class _Repo(object):
self.repodir = repodir
self.commands = all_commands
def _PrintHelp(self, short: bool = False, all_commands: bool = False):
"""Show --help screen."""
global_options.print_help()
print()
if short:
commands = ' '.join(sorted(self.commands))
wrapped_commands = textwrap.wrap(commands, width=77)
print('Available commands:\n %s' % ('\n '.join(wrapped_commands),))
print('\nRun `repo help <command>` for command-specific details.')
print('Bug reports:', Wrapper().BUG_URL)
else:
cmd = self.commands['help']()
if all_commands:
cmd.PrintAllCommandsBody()
else:
cmd.PrintCommonCommandsBody()
def _ParseArgs(self, argv):
"""Parse the main `repo` command line options."""
name = None
glob = []
for i in range(len(argv)):
if not argv[i].startswith('-'):
name = argv[i]
if i > 0:
glob = argv[:i]
for i, arg in enumerate(argv):
if not arg.startswith('-'):
name = arg
glob = argv[:i]
argv = argv[i + 1:]
break
if not name:
else:
name = None
glob = argv
name = 'help'
argv = []
gopts, _gargs = global_options.parse_args(glob)
name, alias_args = self._ExpandAlias(name)
argv = alias_args + argv
if gopts.help:
global_options.print_help()
commands = ' '.join(sorted(self.commands))
wrapped_commands = textwrap.wrap(commands, width=77)
print('\nAvailable commands:\n %s' % ('\n '.join(wrapped_commands),))
print('\nRun `repo help <command>` for command-specific details.')
global_options.exit()
if name:
name, alias_args = self._ExpandAlias(name)
argv = alias_args + argv
return (name, gopts, argv)
@ -186,12 +198,21 @@ class _Repo(object):
if gopts.trace:
SetTrace()
if gopts.show_version:
if name == 'help':
name = 'version'
else:
print('fatal: invalid usage of --version', file=sys.stderr)
return 1
# Handle options that terminate quickly first.
if gopts.help or gopts.help_all:
self._PrintHelp(short=False, all_commands=gopts.help_all)
return 0
elif gopts.show_version:
# Always allow global --version regardless of subcommand validity.
name = 'version'
elif gopts.show_toplevel:
print(os.path.dirname(self.repodir))
return 0
elif not name:
# No subcommand specified, so show the help/subcommand.
self._PrintHelp(short=True)
return 1
SetDefaultColoring(gopts.color)

View File

@ -27,15 +27,19 @@ project is in
\fB\-a\fR, \fB\-\-all\fR
show projects regardless of checkout state
.TP
\fB\-f\fR, \fB\-\-fullpath\fR
display the full work tree path instead of the
relative path
.TP
\fB\-n\fR, \fB\-\-name\-only\fR
display only the name of the repository
.TP
\fB\-p\fR, \fB\-\-path\-only\fR
display only the path of the repository
.TP
\fB\-f\fR, \fB\-\-fullpath\fR
display the full work tree path instead of the
relative path
.TP
\fB\-\-relative\-to\fR=\fI\,PATH\/\fR
display paths relative to this one (default: top of
repo client checkout)
.SS Logging options:
.TP
\fB\-v\fR, \fB\-\-verbose\fR

View File

@ -36,6 +36,9 @@ output manifest in JSON format (experimental)
\fB\-\-pretty\fR
format output for humans to read
.TP
\fB\-\-no\-local\-manifests\fR
ignore local manifests
.TP
\fB\-o\fR \-|NAME.xml, \fB\-\-output\-file\fR=\fI\,\-\/\fR|NAME.xml
file to save the manifest to
.SS Logging options:
@ -95,7 +98,7 @@ include*)>
.IP
<!ELEMENT notice (#PCDATA)>
.IP
<!ELEMENT remote EMPTY>
<!ELEMENT remote (annotation*)>
<!ATTLIST remote name ID #REQUIRED>
<!ATTLIST remote alias CDATA #IMPLIED>
<!ATTLIST remote fetch CDATA #REQUIRED>
@ -393,13 +396,13 @@ Same syntax as the corresponding element of `project`.
.PP
Element annotation
.PP
Zero or more annotation elements may be specified as children of a project
element. Each element describes a name\-value pair that will be exported into
each project's environment during a 'forall' command, prefixed with REPO__. In
addition, there is an optional attribute "keep" which accepts the case
insensitive values "true" (default) or "false". This attribute determines
whether or not the annotation will be kept when exported with the manifest
subcommand.
Zero or more annotation elements may be specified as children of a project or
remote element. Each element describes a name\-value pair. For projects, this
name\-value pair will be exported into each project's environment during a
\&'forall' command, prefixed with `REPO__`. In addition, there is an optional
attribute "keep" which accepts the case insensitive values "true" (default) or
"false". This attribute determines whether or not the annotation will be kept
when exported with the manifest subcommand.
.PP
Element copyfile
.PP

View File

@ -2,9 +2,48 @@
.TH REPO "1" "July 2021" "repo" "Repo Manual"
.SH NAME
repo \- repository management tool built on top of git
.SH DESCRIPTION
usage: repo COMMAND [ARGS]
The complete list of recognized repo commands are:
.SH SYNOPSIS
.B repo
[\fI\,-p|--paginate|--no-pager\/\fR] \fI\,COMMAND \/\fR[\fI\,ARGS\/\fR]
.SH OPTIONS
.TP
\fB\-h\fR, \fB\-\-help\fR
show this help message and exit
.TP
\fB\-\-help\-all\fR
show this help message with all subcommands and exit
.TP
\fB\-p\fR, \fB\-\-paginate\fR
display command output in the pager
.TP
\fB\-\-no\-pager\fR
disable the pager
.TP
\fB\-\-color\fR=\fI\,COLOR\/\fR
control color usage: auto, always, never
.TP
\fB\-\-trace\fR
trace git command execution (REPO_TRACE=1)
.TP
\fB\-\-trace\-python\fR
trace python command execution
.TP
\fB\-\-time\fR
time repo command execution
.TP
\fB\-\-version\fR
display this version of repo
.TP
\fB\-\-show\-toplevel\fR
display the path of the top\-level directory of the
repo client checkout
.TP
\fB\-\-event\-log\fR=\fI\,EVENT_LOG\/\fR
filename of event log to append timeline to
.TP
\fB\-\-git\-trace2\-event\-log\fR=\fI\,GIT_TRACE2_EVENT_LOG\/\fR
directory to write git trace2 event log to
.SS "The complete list of recognized repo commands are:"
.TP
abandon
Permanently abandon a development branch
@ -91,3 +130,4 @@ version
Display the version of repo
.PP
See 'repo help <command>' for more information on a specific command.
Bug reports: https://bugs.chromium.org/p/gerrit/issues/entry?template=Repo+tool+issue

View File

@ -59,7 +59,7 @@ def main(argv):
cmdlist.append(['help2man', '-N', '-n', 'repository management tool built on top of git',
'-S', 'repo', '-m', 'Repo Manual', f'--version-string={version}',
'-o', MANDIR.joinpath('repo.1'), TOPDIR.joinpath('repo'),
'-h', 'help --all'])
'-h', '--help-all'])
with tempfile.TemporaryDirectory() as tempdir:
repo_dir = Path(tempdir) / '.repo'

View File

@ -50,14 +50,21 @@ Displays detailed usage information about a command.
def _PrintAllCommands(self):
print('usage: repo COMMAND [ARGS]')
self.PrintAllCommandsBody()
def PrintAllCommandsBody(self):
print('The complete list of recognized repo commands are:')
commandNames = list(sorted(all_commands))
self._PrintCommands(commandNames)
print("See 'repo help <command>' for more information on a "
'specific command.')
print('Bug reports:', Wrapper().BUG_URL)
def _PrintCommonCommands(self):
print('usage: repo COMMAND [ARGS]')
self.PrintCommonCommandsBody()
def PrintCommonCommandsBody(self):
print('The most commonly used repo commands are:')
def gitc_supported(cmd):

View File

@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from command import Command, MirrorSafeCommand
@ -43,20 +45,26 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'.
p.add_option('-a', '--all',
action='store_true',
help='show projects regardless of checkout state')
p.add_option('-f', '--fullpath',
dest='fullpath', action='store_true',
help='display the full work tree path instead of the relative path')
p.add_option('-n', '--name-only',
dest='name_only', action='store_true',
help='display only the name of the repository')
p.add_option('-p', '--path-only',
dest='path_only', action='store_true',
help='display only the path of the repository')
p.add_option('-f', '--fullpath',
dest='fullpath', action='store_true',
help='display the full work tree path instead of the relative path')
p.add_option('--relative-to', metavar='PATH',
help='display paths relative to this one (default: top of repo client checkout)')
def ValidateOptions(self, opt, args):
if opt.fullpath and opt.name_only:
self.OptionParser.error('cannot combine -f and -n')
# Resolve any symlinks so the output is stable.
if opt.relative_to:
opt.relative_to = os.path.realpath(opt.relative_to)
def Execute(self, opt, args):
"""List all projects and the associated directories.
@ -76,6 +84,8 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'.
def _getpath(x):
if opt.fullpath:
return x.worktree
if opt.relative_to:
return os.path.relpath(x.worktree, opt.relative_to)
return x.relpath
lines = []

View File

@ -302,6 +302,12 @@ later is required to fix a server side protocol bug.
self.repodir,
self.git_event_log,
quiet=opt.quiet)
if opt.local_only:
manifest_path = superproject.manifest_path
if manifest_path:
self._ReloadManifest(manifest_path, load_local_manifests)
return manifest_path
all_projects = self.GetProjects(args,
missing_ok=True,
submodules_ok=opt.fetch_submodules)
@ -1080,11 +1086,11 @@ later is required to fix a server side protocol bug.
file=sys.stderr)
sys.exit(1)
# Log the previous sync state from the config.
# Log the previous sync analysis state from the config.
self.git_event_log.LogConfigEvents(mp.config.GetSyncAnalysisStateData(),
'previous_sync_state')
# Update and log with the new sync state.
# Update and log with the new sync analysis state.
mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
self.git_event_log.LogConfigEvents(mp.config.GetSyncAnalysisStateData(),
'current_sync_state')

View File

@ -12,7 +12,7 @@
intm = 10m
intg = 10g
[syncstate "main"]
synctime = 2021-07-28T19:42:03.866355Z
synctime = 2021-07-28T21:16:10.873226Z
version = 1
[syncstate "sys"]
argv = ['/usr/bin/pytest-3']