Compare commits
1 Commits
master
...
inotify-ca
Author | SHA1 | Date |
---|---|---|
Elf M. Sternberg | 739075d2df |
|
@ -63,9 +63,3 @@ match = .json
|
||||||
print = False
|
print = False
|
||||||
condition = error
|
condition = error
|
||||||
|
|
||||||
[readability]
|
|
||||||
output = Running Text Style Check
|
|
||||||
match = .rst
|
|
||||||
command = git-lint-style --min=6 --max=12
|
|
||||||
print = False
|
|
||||||
condition = error
|
|
||||||
|
|
|
@ -10,4 +10,4 @@ Development Lead
|
||||||
Contributors
|
Contributors
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Tino de Bruijn <work@tino.io>
|
None yet. Why not be the first?
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
GitLint is a git command to automatically run a suite of pre-defined linters.
|
|
|
@ -8,6 +8,4 @@ recursive-include tests *
|
||||||
recursive-exclude * __pycache__
|
recursive-exclude * __pycache__
|
||||||
recursive-exclude * *.py[co]
|
recursive-exclude * *.py[co]
|
||||||
|
|
||||||
recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif *.1
|
recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif
|
||||||
include docs/_build/man/git-lint.1
|
|
||||||
include bin/pre-commit bin/git-lint-style
|
|
||||||
|
|
10
Makefile
10
Makefile
|
@ -12,7 +12,6 @@ export BROWSER_PYSCRIPT
|
||||||
BROWSER := python -c "$$BROWSER_PYSCRIPT"
|
BROWSER := python -c "$$BROWSER_PYSCRIPT"
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "all - local build including docs"
|
|
||||||
@echo "clean - remove all build, test, coverage and Python artifacts"
|
@echo "clean - remove all build, test, coverage and Python artifacts"
|
||||||
@echo "clean-build - remove build artifacts"
|
@echo "clean-build - remove build artifacts"
|
||||||
@echo "clean-pyc - remove Python file artifacts"
|
@echo "clean-pyc - remove Python file artifacts"
|
||||||
|
@ -22,14 +21,10 @@ help:
|
||||||
@echo "test-all - run tests on every Python version with tox"
|
@echo "test-all - run tests on every Python version with tox"
|
||||||
@echo "coverage - check code coverage quickly with the default Python"
|
@echo "coverage - check code coverage quickly with the default Python"
|
||||||
@echo "docs - generate Sphinx HTML documentation, including API docs"
|
@echo "docs - generate Sphinx HTML documentation, including API docs"
|
||||||
@echo "docsbrowse - generate Sphinx HTML documentation and start browser"
|
|
||||||
@echo "release - package and upload a release"
|
@echo "release - package and upload a release"
|
||||||
@echo "dist - package"
|
@echo "dist - package"
|
||||||
@echo "install - install the package to the active Python's site-packages"
|
@echo "install - install the package to the active Python's site-packages"
|
||||||
|
|
||||||
all: docs
|
|
||||||
python setup.py build
|
|
||||||
|
|
||||||
clean: clean-build clean-pyc clean-test
|
clean: clean-build clean-pyc clean-test
|
||||||
|
|
||||||
clean-build:
|
clean-build:
|
||||||
|
@ -72,9 +67,6 @@ docs:
|
||||||
sphinx-apidoc -o docs/ git_lint
|
sphinx-apidoc -o docs/ git_lint
|
||||||
$(MAKE) -C docs clean
|
$(MAKE) -C docs clean
|
||||||
$(MAKE) -C docs html
|
$(MAKE) -C docs html
|
||||||
$(MAKE) -C docs man
|
|
||||||
|
|
||||||
docbrowse: docs
|
|
||||||
$(BROWSER) docs/_build/html/index.html
|
$(BROWSER) docs/_build/html/index.html
|
||||||
|
|
||||||
servedocs: docs
|
servedocs: docs
|
||||||
|
@ -90,4 +82,4 @@ dist: clean
|
||||||
ls -l dist
|
ls -l dist
|
||||||
|
|
||||||
install: clean
|
install: clean
|
||||||
python setup.py install --prefix=/usr/local
|
python setup.py install
|
||||||
|
|
79
README.rst
79
README.rst
|
@ -1,65 +1,23 @@
|
||||||
===============================
|
===============================
|
||||||
Git Lint: README
|
Git Lint
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
A git command that automatically runs identifiable linters against
|
A git command that automatically runs identifiable linters against
|
||||||
changed files in your current git repository or staging area.
|
changed files in current repository or staging.
|
||||||
|
|
||||||
* Free software: MIT license
|
* Free software: MIT license
|
||||||
|
|
||||||
**Git Lint** runs a configurable set of syntax, style, and complexity
|
**Git Lint** runs a configurable set of syntax, style, and complexity
|
||||||
checkers against changed files in your current working directory or
|
checkers against changed files in your current working directory or
|
||||||
staging area. It can be configured to work with any `lint`-like
|
staging area. It can be configured to work with any `lint` command.
|
||||||
command. Some commands may require shell wrappers.
|
Some commands may require shell wrappers.
|
||||||
|
|
||||||
While it may be possible to create a custom lint command in your npm,
|
While it may be possible to create a custom lint command in your npm,
|
||||||
grunt, Make, CMake, or whatever, the fact is we all use a VCS, and most
|
grunt, Make, CMake, or whatever, the fact is we all use a VCS, and
|
||||||
of us use git. Having a centralized repository for what we want checked
|
most of us use git. Having a centralized repository for what we want
|
||||||
and how we want it checked, associated with git (and by extension, as a
|
checked and how we want it checked, associated with git (and by
|
||||||
pre-commit hook), that can be run at any time for any reason.
|
extension, as a pre-commit hook), that can be run at any time for any
|
||||||
|
reason.
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
To lint only what's changed recently in your current working directory:
|
|
||||||
`git lint`
|
|
||||||
|
|
||||||
To lint everything, changed or otherwise, from the current directory down:
|
|
||||||
`git lint -a`
|
|
||||||
|
|
||||||
To lint what's changed from the repo's base:
|
|
||||||
`git lint -b`
|
|
||||||
|
|
||||||
To lint what's in your staging directory:
|
|
||||||
`git lint -s`
|
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Complete documentation for the project is available in the docs directory, or at `Git
|
|
||||||
Linter Docs <https://elfsternberg.github.io/git-linter/index.html>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Install
|
|
||||||
-------
|
|
||||||
|
|
||||||
This *ought* to work:
|
|
||||||
|
|
||||||
`pip install git-linter`
|
|
||||||
|
|
||||||
You will need to copy the .git-lint configuration file to either your
|
|
||||||
home directory or the repo`s base directory. Edit the configuration
|
|
||||||
file as needed. You will also need any linters that you plan on
|
|
||||||
running.
|
|
||||||
|
|
||||||
As git-linter is still mostly alpha code, it might be better to install
|
|
||||||
from source:
|
|
||||||
|
|
||||||
``
|
|
||||||
git clone https://github.com/elfsternberg/git-linter
|
|
||||||
python setup.py install
|
|
||||||
``
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
@ -79,22 +37,11 @@ Features
|
||||||
restoration of your workspace, it ensures the timestamps are the
|
restoration of your workspace, it ensures the timestamps are the
|
||||||
same, so as not to confuse your build system or IDE.
|
same, so as not to confuse your build system or IDE.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
-------
|
||||||
|
|
||||||
Acknowledgements
|
The completion of this project was graciously sponsored by my employer,
|
||||||
----------------
|
Splunk <http://splunk.com>.
|
||||||
|
|
||||||
`Git lint` started life as a simple pre-commit hook. Most of the changes since were
|
|
||||||
inspired by Steve Pulec's `Why You Need a Git Pre-Commit Hook and Why Most Are Wrong <https://dzone.com/articles/why-your-need-git-pre-commit>`_, as well as just my own needs
|
|
||||||
as a software developer.
|
|
||||||
|
|
||||||
To do
|
|
||||||
-----
|
|
||||||
|
|
||||||
* The '-q' and '--quiet' arguments do not work.
|
|
||||||
* Ought to be able to silence the "no linter found" message.
|
|
||||||
* Ought to be able to configure '-q' and '--silence' commands in .git-lint/config file.
|
|
||||||
* Ought to be able to override config file for above.
|
|
||||||
|
|
||||||
|
|
||||||
Disclaimer
|
Disclaimer
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -46,10 +46,6 @@ def main(*args):
|
||||||
print(error)
|
print(error)
|
||||||
return returncode
|
return returncode
|
||||||
kincaid = re.search(r'Kincaid:\s*([\d\.]+)', values, re.MULTILINE)
|
kincaid = re.search(r'Kincaid:\s*([\d\.]+)', values, re.MULTILINE)
|
||||||
|
|
||||||
# Assumes an uncheckable issue, like an empty RST. Not unusual.
|
|
||||||
if not kincaid:
|
|
||||||
return 0
|
|
||||||
val = float(kincaid.group(1))
|
val = float(kincaid.group(1))
|
||||||
|
|
||||||
msg = ""
|
msg = ""
|
|
@ -1,6 +1,5 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from git_lint.git_lint import load_config, run_linters, git_base
|
from git_lint import load_config, run_linters, git_base
|
||||||
from git_lint.reporters import print_report
|
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
@ -21,14 +20,14 @@ def main(*args):
|
||||||
unlintable_filenames,
|
unlintable_filenames,
|
||||||
cant_lint_filenames,
|
cant_lint_filenames,
|
||||||
broken_linter_names,
|
broken_linter_names,
|
||||||
unfindable_filenames) = run_linters(pre_commit_options, config)
|
unfindable_filenames) = run_linters(options, config)
|
||||||
|
|
||||||
print_report(results,
|
print_report(results,
|
||||||
unlintable_filenames,
|
unlintable_filenames,
|
||||||
cant_lint_filenames,
|
cant_lint_filenames,
|
||||||
broken_linter_names,
|
broken_linter_names,
|
||||||
unfindable_filenames,
|
unfindable_filenames,
|
||||||
pre_commit_options)
|
options)
|
||||||
|
|
||||||
if not len(results):
|
if not len(results):
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -14,19 +14,12 @@
|
||||||
Scan the workspace [default]
|
Scan the workspace [default]
|
||||||
**-s, --staging**
|
**-s, --staging**
|
||||||
Scan the staging area (useful for pre-commit).
|
Scan the staging area (useful for pre-commit).
|
||||||
**-c <path>, --config=<path>**
|
**-c** <path>, --config**=<path> Path to config file
|
||||||
Path to config file
|
|
||||||
**-t, --bylinter**
|
|
||||||
Group reports by linter first as they appear in the config file [default]
|
|
||||||
**-f, --byfile**
|
|
||||||
Group reports by file first, linter second
|
|
||||||
**-d, --dryrun**
|
**-d, --dryrun**
|
||||||
Report what git-lint would do, but don't actually do anything.
|
Report what git-lint would do, but don't actually do anything.
|
||||||
**-q, --quiet**
|
**-q, --quiet**
|
||||||
Produce a short report of file that failed to pass.
|
Produce a short report of files that failed to pass.
|
||||||
**-h, --help**
|
**-h, --help**
|
||||||
Print a short help message
|
Print a short help message
|
||||||
**-V, --verbose**
|
|
||||||
Print a slightly more verbose long report
|
|
||||||
**-v, --version**
|
**-v, --version**
|
||||||
Print version information
|
Print version information
|
||||||
|
|
92
docs/conf.py
92
docs/conf.py
|
@ -20,7 +20,7 @@ import os
|
||||||
# directory, add these directories to sys.path here. If the directory is
|
# directory, add these directories to sys.path here. If the directory is
|
||||||
# relative to the documentation root, use os.path.abspath to make it
|
# relative to the documentation root, use os.path.abspath to make it
|
||||||
# absolute, like shown here.
|
# absolute, like shown here.
|
||||||
# sys.path.insert(0, os.path.abspath('.'))
|
#sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
# Get the project root dir, which is the parent dir of this
|
# Get the project root dir, which is the parent dir of this
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
|
@ -31,12 +31,12 @@ project_root = os.path.dirname(cwd)
|
||||||
# version is used.
|
# version is used.
|
||||||
sys.path.insert(0, project_root)
|
sys.path.insert(0, project_root)
|
||||||
|
|
||||||
import git_lint # noqa
|
import git_lint
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------
|
# -- General configuration ---------------------------------------------
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
# needs_sphinx = '1.0'
|
#needs_sphinx = '1.0'
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
@ -49,7 +49,7 @@ templates_path = ['_templates']
|
||||||
source_suffix = '.rst'
|
source_suffix = '.rst'
|
||||||
|
|
||||||
# The encoding of source files.
|
# The encoding of source files.
|
||||||
# source_encoding = 'utf-8-sig'
|
#source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
@ -69,13 +69,13 @@ release = git_lint.__version__
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
# language = None
|
#language = None
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to
|
# There are two options for replacing |today|: either, you set today to
|
||||||
# some non-false value, then it is used:
|
# some non-false value, then it is used:
|
||||||
# today = ''
|
#today = ''
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
# today_fmt = '%B %d, %Y'
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
|
@ -83,60 +83,60 @@ exclude_patterns = ['_build']
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all
|
# The reST default role (used for this markup: `text`) to use for all
|
||||||
# documents.
|
# documents.
|
||||||
# default_role = None
|
#default_role = None
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
# add_function_parentheses = True
|
#add_function_parentheses = True
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
# If true, the current module name will be prepended to all description
|
||||||
# unit titles (such as .. function::).
|
# unit titles (such as .. function::).
|
||||||
# add_module_names = True
|
#add_module_names = True
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
# output. They are ignored by default.
|
# output. They are ignored by default.
|
||||||
# show_authors = False
|
#show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
# modindex_common_prefix = []
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
# If true, keep warnings as "system message" paragraphs in the built
|
# If true, keep warnings as "system message" paragraphs in the built
|
||||||
# documents.
|
# documents.
|
||||||
# keep_warnings = False
|
#keep_warnings = False
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------
|
# -- Options for HTML output -------------------------------------------
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
html_theme = 'alabaster'
|
html_theme = 'default'
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a
|
# Theme options are theme-specific and customize the look and feel of a
|
||||||
# theme further. For a list of options available for each theme, see the
|
# theme further. For a list of options available for each theme, see the
|
||||||
# documentation.
|
# documentation.
|
||||||
# html_theme_options = {}
|
#html_theme_options = {}
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
# html_theme_path = []
|
#html_theme_path = []
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
# "<project> v<release> documentation".
|
# "<project> v<release> documentation".
|
||||||
# html_title = None
|
#html_title = None
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as
|
# A shorter title for the navigation bar. Default is the same as
|
||||||
# html_title.
|
# html_title.
|
||||||
# html_short_title = None
|
#html_short_title = None
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the
|
# The name of an image file (relative to this directory) to place at the
|
||||||
# top of the sidebar.
|
# top of the sidebar.
|
||||||
# html_logo = None
|
#html_logo = None
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon
|
# The name of an image file (within the static path) to use as favicon
|
||||||
# of the docs. This file should be a Windows icon file (.ico) being
|
# of the docs. This file should be a Windows icon file (.ico) being
|
||||||
# 16x16 or 32x32 pixels large.
|
# 16x16 or 32x32 pixels large.
|
||||||
# html_favicon = None
|
#html_favicon = None
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets)
|
# Add any paths that contain custom static files (such as style sheets)
|
||||||
# here, relative to this directory. They are copied after the builtin
|
# here, relative to this directory. They are copied after the builtin
|
||||||
|
@ -146,46 +146,46 @@ html_static_path = ['_static']
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page
|
# If not '', a 'Last updated on:' timestamp is inserted at every page
|
||||||
# bottom, using the given strftime format.
|
# bottom, using the given strftime format.
|
||||||
# html_last_updated_fmt = '%b %d, %Y'
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
# html_use_smartypants = True
|
#html_use_smartypants = True
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
# html_sidebars = {}
|
#html_sidebars = {}
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names
|
# Additional templates that should be rendered to pages, maps page names
|
||||||
# to template names.
|
# to template names.
|
||||||
# html_additional_pages = {}
|
#html_additional_pages = {}
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
# html_domain_indices = True
|
#html_domain_indices = True
|
||||||
|
|
||||||
# If false, no index is generated.
|
# If false, no index is generated.
|
||||||
# html_use_index = True
|
#html_use_index = True
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
# If true, the index is split into individual pages for each letter.
|
||||||
# html_split_index = False
|
#html_split_index = False
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
# If true, links to the reST sources are added to the pages.
|
||||||
# html_show_sourcelink = True
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
# If true, "Created using Sphinx" is shown in the HTML footer.
|
# If true, "Created using Sphinx" is shown in the HTML footer.
|
||||||
# Default is True.
|
# Default is True.
|
||||||
# html_show_sphinx = True
|
#html_show_sphinx = True
|
||||||
|
|
||||||
# If true, "(C) Copyright ..." is shown in the HTML footer.
|
# If true, "(C) Copyright ..." is shown in the HTML footer.
|
||||||
# Default is True.
|
# Default is True.
|
||||||
# html_show_copyright = True
|
#html_show_copyright = True
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages
|
# If true, an OpenSearch description file will be output, and all pages
|
||||||
# will contain a <link> tag referring to it. The value of this option
|
# will contain a <link> tag referring to it. The value of this option
|
||||||
# must be the base URL from which the finished HTML is served.
|
# must be the base URL from which the finished HTML is served.
|
||||||
# html_use_opensearch = ''
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
# html_file_suffix = None
|
#html_file_suffix = None
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'git_lintdoc'
|
htmlhelp_basename = 'git_lintdoc'
|
||||||
|
@ -195,13 +195,13 @@ htmlhelp_basename = 'git_lintdoc'
|
||||||
|
|
||||||
latex_elements = {
|
latex_elements = {
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
# 'papersize': 'letterpaper',
|
#'papersize': 'letterpaper',
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
# 'pointsize': '10pt',
|
#'pointsize': '10pt',
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
# Additional stuff for the LaTeX preamble.
|
||||||
# 'preamble': '',
|
#'preamble': '',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
@ -215,23 +215,23 @@ latex_documents = [
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at
|
# The name of an image file (relative to this directory) to place at
|
||||||
# the top of the title page.
|
# the top of the title page.
|
||||||
# latex_logo = None
|
#latex_logo = None
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings
|
# For "manual" documents, if this is true, then toplevel headings
|
||||||
# are parts, not chapters.
|
# are parts, not chapters.
|
||||||
# latex_use_parts = False
|
#latex_use_parts = False
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
# If true, show page references after internal links.
|
||||||
# latex_show_pagerefs = False
|
#latex_show_pagerefs = False
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
# latex_show_urls = False
|
#latex_show_urls = False
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
# latex_appendices = []
|
#latex_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
# latex_domain_indices = True
|
#latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output ------------------------------------
|
# -- Options for manual page output ------------------------------------
|
||||||
|
@ -245,7 +245,7 @@ man_pages = [
|
||||||
]
|
]
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
# man_show_urls = False
|
#man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ----------------------------------------
|
# -- Options for Texinfo output ----------------------------------------
|
||||||
|
@ -263,13 +263,13 @@ texinfo_documents = [
|
||||||
]
|
]
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
# texinfo_appendices = []
|
#texinfo_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
# texinfo_domain_indices = True
|
#texinfo_domain_indices = True
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
# texinfo_show_urls = 'footnote'
|
#texinfo_show_urls = 'footnote'
|
||||||
|
|
||||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||||
# texinfo_no_detailmenu = False
|
#texinfo_no_detailmenu = False
|
||||||
|
|
|
@ -12,30 +12,6 @@ git_lint.git_lint module
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
git_lint.option_handler module
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
.. automodule:: git_lint.option_handler
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
git_lint.options module
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. automodule:: git_lint.options
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
git_lint.reporters module
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
.. automodule:: git_lint.reporters
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
|
@ -3,12 +3,8 @@
|
||||||
You can adapt this file completely to your liking, but it should at least
|
You can adapt this file completely to your liking, but it should at least
|
||||||
contain the root `toctree` directive.
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
Git Lint
|
Welcome to Git Lint's documentation!
|
||||||
========
|
======================================
|
||||||
|
|
||||||
Git Lint provides a new git command that automatically runs identifiable linters (style
|
|
||||||
and syntax quality assurance programs) against changed files in your current git
|
|
||||||
repository or staging area.
|
|
||||||
|
|
||||||
Contents:
|
Contents:
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ To install Git Lint, run this command in your terminal:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ pip install git-linter
|
$ pip install git_lint
|
||||||
|
|
||||||
If you don't have `pip`_ installed, this `Python installation guide`_ can guide
|
If you don't have `pip`_ installed, this `Python installation guide`_ can guide
|
||||||
you through the process.
|
you through the process.
|
||||||
|
@ -30,13 +30,13 @@ You can either clone the public repository:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ git clone git://github.com/elfsternberg/git-linter
|
$ git clone git://github.com/elfsternberg/git_lint
|
||||||
|
|
||||||
Or download the `tarball`_:
|
Or download the `tarball`_:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ curl -OL https://github.com/elfsternberg/git-linter/tarball/master
|
$ curl -OL https://github.com/elfsternberg/git_lint/tarball/master
|
||||||
|
|
||||||
Once you have a copy of the source, you can install it with:
|
Once you have a copy of the source, you can install it with:
|
||||||
|
|
||||||
|
@ -44,8 +44,10 @@ Once you have a copy of the source, you can install it with:
|
||||||
|
|
||||||
$ python setup.py install
|
$ python setup.py install
|
||||||
|
|
||||||
.. _Github repo: https://github.com/elfsternberg/git-linter
|
.. _Github repo: https://github.com/elfsternberg/git_lint
|
||||||
.. _tarball: https://github.com/elfsternberg/git-linter/tarball/master
|
.. _tarball: https://github.com/elfsternberg/git_lint/tarball/master
|
||||||
|
|
||||||
Once installed, please copy the '.git-lint' example file. You may install this either in
|
Once installed, you may run the 'git lint --make-config' command, which
|
||||||
your home or repository directory as ``.git-lint``.
|
will generate a simple configuration file. You may install this either
|
||||||
|
in your home directory as ``.git-lint.conf`` or in your project's git
|
||||||
|
directory as ``.git/lint/git-lint.conf``
|
||||||
|
|
|
@ -12,13 +12,24 @@ git lint [options] [filenames]
|
||||||
Options
|
Options
|
||||||
-------
|
-------
|
||||||
|
|
||||||
.. include:: arguments.rst
|
.. include: arguments.rst
|
||||||
|
|
||||||
As a pre-commit hook:
|
As a pre-commit hook:
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
There's a file, pre-commit, in the /bin directory with the project. (Or you can download
|
.. code-block:: python
|
||||||
it from the github repository.) Install it in you .git/hooks/pre-commit file, and
|
|
||||||
chmod +x .git/hooks/pre-commit.
|
#!/usr/bin/env python
|
||||||
|
import git_lint
|
||||||
|
git_lint.run_precommit(staging = True, timestamps = True)
|
||||||
|
|
||||||
|
Install this file in your project's ``.git/hooks/pre-commit``, and set
|
||||||
|
the file's executable flag to ``true``:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
chmod +x pre-commit
|
||||||
|
|
||||||
|
Please see the :ref:`api` for more details on options taken by the
|
||||||
|
``run_precommit()`` and ``run_gitlint`` commands.
|
||||||
|
|
||||||
The pre-commit hook is *experimental*. Please be careful with it.
|
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
__author__ = 'Kenneth M. "Elf" Sternberg'
|
__author__ = 'Kenneth M. "Elf" Sternberg'
|
||||||
__email__ = 'elf.sternberg@gmail.com'
|
__email__ = 'elf.sternberg@gmail.com'
|
||||||
__version__ = '0.0.7'
|
__version__ = '0.0.4'
|
||||||
|
|
||||||
__all__ = ['git_lint']
|
__all__ = ['git_lint']
|
||||||
|
|
|
@ -5,20 +5,75 @@ from .option_handler import cleanup_options
|
||||||
from .reporters import print_report, print_help, print_linters
|
from .reporters import print_report, print_help, print_linters
|
||||||
from .git_lint import load_config, run_linters, git_base
|
from .git_lint import load_config, run_linters, git_base
|
||||||
from getopt import GetoptError
|
from getopt import GetoptError
|
||||||
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
watchdog = False
|
||||||
|
try:
|
||||||
|
import watchdog
|
||||||
|
from watchdog.observers import Observer
|
||||||
|
from watchdog.events import RegexMatchingEventHandler
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
NAME = 'git-lint'
|
NAME = 'git-lint'
|
||||||
VERSION = '0.0.7'
|
VERSION = '0.0.4'
|
||||||
|
|
||||||
|
|
||||||
|
def remove_unavailable_options(options):
|
||||||
|
failures = [] + ((watchdog == False and ['monitor']) or [])
|
||||||
|
return filter(lambda i: i[1] not in failures, options)
|
||||||
|
|
||||||
|
|
||||||
|
def monitor(options, config, filenames):
|
||||||
|
|
||||||
|
observer = watchdog.observers.Observer()
|
||||||
|
skip = ['\.git/']
|
||||||
|
|
||||||
|
def run_monitor_linters():
|
||||||
|
(results,
|
||||||
|
unlintable_filenames,
|
||||||
|
cant_lint_filenames,
|
||||||
|
broken_linter_names,
|
||||||
|
unfindable_filenames) = run_linters(options, config, filenames)
|
||||||
|
|
||||||
|
print_report(results,
|
||||||
|
unlintable_filenames,
|
||||||
|
cant_lint_filenames,
|
||||||
|
broken_linter_names,
|
||||||
|
unfindable_filenames,
|
||||||
|
options)
|
||||||
|
|
||||||
|
class LintMonitor(RegexMatchingEventHandler):
|
||||||
|
def __init__(self):
|
||||||
|
super(LintMonitor, self).__init__(ignore_regexes=skip)
|
||||||
|
|
||||||
|
def on_created(self, event):
|
||||||
|
run_monitor_linters()
|
||||||
|
|
||||||
|
def on_modified(self, event):
|
||||||
|
run_monitor_linters()
|
||||||
|
|
||||||
|
observer.schedule(LintMonitor(), git_base, recursive=True)
|
||||||
|
observer.start()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
observer.stop()
|
||||||
|
observer.join()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if git_base is None:
|
if git_base is None:
|
||||||
sys.exit(_('A git repository was not found.'))
|
sys.exit(_('A git repository was not found.'))
|
||||||
|
|
||||||
(options, filenames, excluded_commands) = cleanup_options(OPTIONS, sys.argv)
|
initial_options = remove_unavailable_options(OPTIONS)
|
||||||
|
(options, filenames, excluded_commands) = cleanup_options(initial_options, sys.argv)
|
||||||
|
|
||||||
if len(excluded_commands) > 0:
|
if len(excluded_commands) > 0:
|
||||||
print(_('These command line options were ignored due to option precedence.'))
|
print(_('These command line options were ignored due to option precedence.'))
|
||||||
|
@ -29,7 +84,7 @@ def main():
|
||||||
config = load_config(options, git_base)
|
config = load_config(options, git_base)
|
||||||
|
|
||||||
if 'help' in options:
|
if 'help' in options:
|
||||||
print_help(OPTIONS, NAME)
|
print_help(initial_options, NAME)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if 'version' in options:
|
if 'version' in options:
|
||||||
|
@ -43,6 +98,9 @@ def main():
|
||||||
print_linters(config, broken_linter_names)
|
print_linters(config, broken_linter_names)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
if 'monitor' in options:
|
||||||
|
return monitor(options, config, filenames)
|
||||||
|
|
||||||
(results,
|
(results,
|
||||||
unlintable_filenames,
|
unlintable_filenames,
|
||||||
cant_lint_filenames,
|
cant_lint_filenames,
|
||||||
|
@ -62,7 +120,7 @@ def main():
|
||||||
return max([i[2] for i in results if len(i)])
|
return max([i[2] for i in results if len(i)])
|
||||||
|
|
||||||
except GetoptError as err:
|
except GetoptError as err:
|
||||||
print_help(OPTIONS)
|
print_help(initial_options)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,18 +9,11 @@ import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import configparser
|
import configparser
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
import ConfigParser as configparser
|
import ConfigParser as configparser
|
||||||
|
|
||||||
try: # noqa: F401
|
|
||||||
from typing import Dict, List, Text, Any, Optional, Union, Callable, Tuple # noqa: F401
|
|
||||||
except: # noqa: F401
|
|
||||||
pass # noqa: F401
|
|
||||||
|
|
||||||
|
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,20 +24,7 @@ _ = gettext.gettext
|
||||||
# |___/
|
# |___/
|
||||||
|
|
||||||
|
|
||||||
# (commandLineDictionary, repositoryLocation) -> (configurationDictionary | exit)
|
def find_config_file(options, base):
|
||||||
def load_config(options, base):
|
|
||||||
"""Loads the git-lint configuration file.
|
|
||||||
|
|
||||||
Returns the configuration file as a dictionary of dictionaries.
|
|
||||||
Performs substitutions as specified in the SafeConfigParser
|
|
||||||
specification; the only one performed currently is the 'repodir'
|
|
||||||
will be replaced with the base directory of the repository.
|
|
||||||
Combined with the option to specify the .git-lint configuration as
|
|
||||||
a directory, this allows users to keep per-project configuration
|
|
||||||
files for specific linters.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def find_config_file(options, base):
|
|
||||||
""" Returns the configuration file from a prioritized list of locations.
|
""" Returns the configuration file from a prioritized list of locations.
|
||||||
|
|
||||||
Locations are prioritized as:
|
Locations are prioritized as:
|
||||||
|
@ -76,6 +56,20 @@ def load_config(options, base):
|
||||||
|
|
||||||
return matches[0]
|
return matches[0]
|
||||||
|
|
||||||
|
|
||||||
|
# (commandLineDictionary, repositoryLocation) -> (configurationDictionary | exit)
|
||||||
|
def load_config(options, base):
|
||||||
|
"""Loads the git-lint configuration file.
|
||||||
|
|
||||||
|
Returns the configuration file as a dictionary of dictionaries.
|
||||||
|
Performs substitutions as specified in the SafeConfigParser
|
||||||
|
specification; the only one performed currently is the 'repodir'
|
||||||
|
will be replaced with the base directory of the repository.
|
||||||
|
Combined with the option to specify the .git-lint configuration as
|
||||||
|
a directory, this allows users to keep per-project configuration
|
||||||
|
files for specific linters.
|
||||||
|
"""
|
||||||
|
|
||||||
Linter = namedtuple('Linter', ['name', 'linter'])
|
Linter = namedtuple('Linter', ['name', 'linter'])
|
||||||
path = find_config_file(options, base)
|
path = find_config_file(options, base)
|
||||||
configloader = configparser.SafeConfigParser()
|
configloader = configparser.SafeConfigParser()
|
||||||
|
@ -152,6 +146,10 @@ git_head = get_git_head()
|
||||||
# \___/ \__|_|_|_|\__|_\___/__/
|
# \___/ \__|_|_|_|\__|_\___/__/
|
||||||
#
|
#
|
||||||
|
|
||||||
|
def base_file_cleaner(files):
|
||||||
|
return [file.replace(git_base + '/', '', 1) for file in files]
|
||||||
|
|
||||||
|
|
||||||
class MatchFilter:
|
class MatchFilter:
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
@ -174,34 +172,37 @@ class MatchFilter:
|
||||||
# \___|_||_\___\__|_\_\ |_|_|_||_\__\___|_| /__/
|
# \___|_||_\___\__|_\_\ |_|_|_||_\__\___|_| /__/
|
||||||
#
|
#
|
||||||
|
|
||||||
def linter_exists(linter, label):
|
def executable_exists(script, label):
|
||||||
if not len(linter):
|
if not len(script):
|
||||||
sys.exit(_('Syntax error in linter configuration for {} ').format(label))
|
sys.exit(
|
||||||
|
_('Syntax error in command configuration for {} ').format(label))
|
||||||
|
|
||||||
lintername = linter.split(' ').pop(0)
|
scriptname = script.split(' ').pop(0)
|
||||||
if not len(lintername):
|
if not len(scriptname):
|
||||||
sys.exit(_('Syntax error in linter configuration for {} ').format(label))
|
sys.exit(
|
||||||
|
_('Syntax error in command configuration for {} ').format(label))
|
||||||
|
|
||||||
def is_executable(path):
|
def is_executable(path):
|
||||||
return os.path.exists(path) and os.access(path, os.X_OK)
|
return os.path.exists(path) and os.access(path, os.X_OK)
|
||||||
|
|
||||||
if lintername.startswith('/'):
|
if scriptname.startswith('/'):
|
||||||
return (is_executable(lintername) and lintername) or None
|
return (is_executable(scriptname) and scriptname) or None
|
||||||
|
|
||||||
# shutil.which() doesn't appear until Python 3, darnit.
|
# shutil.which() doesn't appear until Python 3, darnit.
|
||||||
possibles = [path for path in
|
possibles = [path for path in
|
||||||
[os.path.join(path, lintername)
|
[os.path.join(path, scriptname)
|
||||||
for path in os.environ.get('PATH').split(':')]
|
for path in os.environ.get('PATH').split(':')]
|
||||||
if is_executable(path)]
|
if is_executable(path)]
|
||||||
|
|
||||||
return (len(possibles) and possibles.pop(0)) or False
|
return (len(possibles) and possibles.pop(0)) or False
|
||||||
|
|
||||||
|
|
||||||
def get_linter_status(config):
|
def get_working_linter_names(config):
|
||||||
def get_working_linter_names(config):
|
|
||||||
return [i.name for i in config
|
return [i.name for i in config
|
||||||
if linter_exists(i.linter['command'], i.name)]
|
if executable_exists(i.linter['command'], i.name)]
|
||||||
|
|
||||||
|
|
||||||
|
def get_linter_status(config):
|
||||||
working_linter_names = get_working_linter_names(config)
|
working_linter_names = get_working_linter_names(config)
|
||||||
broken_linter_names = (set([i.name for i in config]) - set(working_linter_names))
|
broken_linter_names = (set([i.name for i in config]) - set(working_linter_names))
|
||||||
return working_linter_names, broken_linter_names
|
return working_linter_names, broken_linter_names
|
||||||
|
@ -275,11 +276,6 @@ def get_filelist(options, extras):
|
||||||
|
|
||||||
return check_for_conflicts(parse_stream([], stream))
|
return check_for_conflicts(parse_stream([], stream))
|
||||||
|
|
||||||
def revision_list():
|
|
||||||
cmd = ['diff', '--name-only', '-z', options.get('revision')]
|
|
||||||
return [entry for entry in get_git_response(cmd).split(u'\x00')
|
|
||||||
if len(entry) > 0]
|
|
||||||
|
|
||||||
def staging_list():
|
def staging_list():
|
||||||
""" Return the list of files added or modified to the stage """
|
""" Return the list of files added or modified to the stage """
|
||||||
|
|
||||||
|
@ -311,9 +307,6 @@ def get_filelist(options, extras):
|
||||||
working_directory_trans = base_file_filter
|
working_directory_trans = base_file_filter
|
||||||
|
|
||||||
file_list_generator = working_list
|
file_list_generator = working_list
|
||||||
if 'revision' in options:
|
|
||||||
file_list_generator = revision_list
|
|
||||||
working_directory_trans = base_file_filter
|
|
||||||
if 'all' in options:
|
if 'all' in options:
|
||||||
file_list_generator = all_list
|
file_list_generator = all_list
|
||||||
if 'staging' in options:
|
if 'staging' in options:
|
||||||
|
@ -329,33 +322,33 @@ def get_filelist(options, extras):
|
||||||
# |___/ |___/ |_| |_|
|
# |___/ |___/ |_| |_|
|
||||||
|
|
||||||
|
|
||||||
class StagingRunner:
|
class Runner:
|
||||||
def __init__(self, filenames):
|
def __init__(self, options):
|
||||||
self.filenames = filenames
|
self.runner = ('staging' in options and Runner.staging_wrapper) or Runner.workspace_wrapper
|
||||||
|
|
||||||
def __enter__(self):
|
def __call__(self, run_linters, filenames):
|
||||||
|
return self.runner(run_linters, filenames)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def staging_wrapper(run_linters, filenames):
|
||||||
def time_gather(f):
|
def time_gather(f):
|
||||||
stats = os.stat(f)
|
stats = os.stat(f)
|
||||||
return (f, (stats.st_atime, stats.st_mtime))
|
return (f, (stats.st_atime, stats.st_mtime))
|
||||||
self.times = [time_gather(filename) for filename in self.filenames]
|
|
||||||
|
times = [time_gather(file) for file in filenames]
|
||||||
run_git_command(['stash', '--keep-index'])
|
run_git_command(['stash', '--keep-index'])
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
results = run_linters()
|
||||||
run_git_command(['reset', '--hard'])
|
run_git_command(['reset', '--hard'])
|
||||||
run_git_command(['stash', 'pop', '--quiet', '--index'])
|
run_git_command(['stash', 'pop', '--quiet', '--index'])
|
||||||
for (filename, timepair) in self.times:
|
|
||||||
|
for (filename, timepair) in times:
|
||||||
os.utime(filename, timepair)
|
os.utime(filename, timepair)
|
||||||
|
return results
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
class WorkspaceRunner(object):
|
def workspace_wrapper(run_linters, filenames):
|
||||||
def __init__(self, filenames):
|
return run_linters()
|
||||||
pass
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# ___ _ _ _
|
# ___ _ _ _
|
||||||
|
@ -379,7 +372,7 @@ class Linters:
|
||||||
"""Run one linter against one file.
|
"""Run one linter against one file.
|
||||||
|
|
||||||
If the result matches the error condition specified in the configuration file,
|
If the result matches the error condition specified in the configuration file,
|
||||||
return the error code and messages, otherwise return nothing.
|
return the error code and messages, either return nothing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cmd = linter['command'] + ' "' + filename + '"'
|
cmd = linter['command'] + ' "' + filename + '"'
|
||||||
|
@ -432,9 +425,6 @@ class Linters:
|
||||||
|
|
||||||
|
|
||||||
def run_linters(options, config, extras=[]):
|
def run_linters(options, config, extras=[]):
|
||||||
if 'pr' in options:
|
|
||||||
options.pop('pr')
|
|
||||||
options['revision'] = 'HEAD^..HEAD'
|
|
||||||
|
|
||||||
def build_config_subset(keys):
|
def build_config_subset(keys):
|
||||||
""" Returns a subset of the configuration, with only those linters mentioned in keys """
|
""" Returns a subset of the configuration, with only those linters mentioned in keys """
|
||||||
|
@ -444,31 +434,21 @@ def run_linters(options, config, extras=[]):
|
||||||
all_filenames, unfindable_filenames = get_filelist(options, extras)
|
all_filenames, unfindable_filenames = get_filelist(options, extras)
|
||||||
|
|
||||||
is_lintable = MatchFilter(config)
|
is_lintable = MatchFilter(config)
|
||||||
|
|
||||||
lintable_filenames = set([filename for filename in all_filenames
|
lintable_filenames = set([filename for filename in all_filenames
|
||||||
if is_lintable(filename)])
|
if is_lintable(filename)])
|
||||||
|
|
||||||
unlintable_filenames = set(all_filenames) - lintable_filenames
|
unlintable_filenames = set(all_filenames) - lintable_filenames
|
||||||
|
|
||||||
# Filter the linter config down to the selected ones.
|
|
||||||
if 'only' in options:
|
|
||||||
config = [linter for linter in config
|
|
||||||
if linter.name in options['only']]
|
|
||||||
elif 'exclude' in options:
|
|
||||||
config = [linter for linter in config
|
|
||||||
if linter.name not in options['exclude']]
|
|
||||||
if not len(config):
|
|
||||||
raise RuntimeError('No linters left to run! Be less strict with --only and --exclude.')
|
|
||||||
|
|
||||||
working_linter_names, broken_linter_names = get_linter_status(config)
|
working_linter_names, broken_linter_names = get_linter_status(config)
|
||||||
|
|
||||||
cant_lint_filter = MatchFilter(build_config_subset(
|
cant_lint_filter = MatchFilter(build_config_subset(
|
||||||
broken_linter_names))
|
broken_linter_names))
|
||||||
|
|
||||||
cant_lint_filenames = [filename for filename in lintable_filenames
|
cant_lint_filenames = [filename for filename in lintable_filenames
|
||||||
if cant_lint_filter(filename)]
|
if cant_lint_filter(filename)]
|
||||||
|
|
||||||
runner = WorkspaceRunner
|
runner = Runner(options)
|
||||||
if 'staging' in options:
|
|
||||||
runner = StagingRunner
|
|
||||||
|
|
||||||
linters = Linters(build_config_subset(working_linter_names),
|
linters = Linters(build_config_subset(working_linter_names),
|
||||||
sorted(lintable_filenames))
|
sorted(lintable_filenames))
|
||||||
|
@ -478,8 +458,7 @@ def run_linters(options, config, extras=[]):
|
||||||
return (dryrun_results, unlintable_filenames, cant_lint_filenames,
|
return (dryrun_results, unlintable_filenames, cant_lint_filenames,
|
||||||
broken_linter_names, unfindable_filenames)
|
broken_linter_names, unfindable_filenames)
|
||||||
|
|
||||||
with runner(lintable_filenames):
|
results = runner(linters, lintable_filenames)
|
||||||
results = linters()
|
|
||||||
|
|
||||||
return (results, unlintable_filenames, cant_lint_filenames,
|
return (results, unlintable_filenames, cant_lint_filenames,
|
||||||
broken_linter_names, unfindable_filenames)
|
broken_linter_names, unfindable_filenames)
|
||||||
|
|
|
@ -2,18 +2,8 @@
|
||||||
# Author: Elf M. Sternberg
|
# Author: Elf M. Sternberg
|
||||||
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from collections import namedtuple
|
|
||||||
import getopt
|
import getopt
|
||||||
|
|
||||||
try: # noqa: F401
|
|
||||||
from typing import Dict, List, Text, Any, Optional, Union, Callable, Tuple # noqa: F401
|
|
||||||
except: # noqa: F401
|
|
||||||
pass # noqa: F401
|
|
||||||
|
|
||||||
|
|
||||||
Option = namedtuple('Option', ['short', 'long', 'takes', 'help', 'conflicts']) # type: str, str, str, str, List[str]
|
|
||||||
Arguments = namedtuple('Arguments', ['arguments', 'filenames', 'excluded']) # type: Dict[str, str], List[str], List[str]
|
|
||||||
|
|
||||||
# This was a lot shorter and smarter in Hy...
|
# This was a lot shorter and smarter in Hy...
|
||||||
|
|
||||||
# A lot of what you see here is separated from git_lint itself, since this will not be
|
# A lot of what you see here is separated from git_lint itself, since this will not be
|
||||||
|
@ -27,7 +17,6 @@ Arguments = namedtuple('Arguments', ['arguments', 'filenames', 'excluded']) # t
|
||||||
|
|
||||||
|
|
||||||
def cleanup_options(options, commandline):
|
def cleanup_options(options, commandline):
|
||||||
# type: (List[Option], List[str]) -> Arguments
|
|
||||||
"""Takes a table of options and the commandline, and returns a
|
"""Takes a table of options and the commandline, and returns a
|
||||||
dictionary of those options that appear on the commandline
|
dictionary of those options that appear on the commandline
|
||||||
along with any extra arguments.
|
along with any extra arguments.
|
||||||
|
@ -35,63 +24,52 @@ def cleanup_options(options, commandline):
|
||||||
:param List(Tuple (string, string, boolean, string, List(string))) options,
|
:param List(Tuple (string, string, boolean, string, List(string))) options,
|
||||||
The table of options: One-letter option, long option, takes arguments,
|
The table of options: One-letter option, long option, takes arguments,
|
||||||
Help text, list of (long) options superseded by this one.
|
Help text, list of (long) options superseded by this one.
|
||||||
|
|
||||||
: param List(strings) commandline
|
: param List(strings) commandline
|
||||||
The arguments as received by the start-up process
|
The arguments as received by the start-up process
|
||||||
|
|
||||||
: returns List(strings), List(strings), List(strings)
|
|
||||||
The longopt dictionary of arguments and associated values (if any)
|
|
||||||
The list of filenames left after argument processing
|
|
||||||
The longopt list of arguments that were excluded by argument precedence
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def make_option_streamliner(options):
|
def make_option_streamliner(options):
|
||||||
# type: (List[Option]) -> Callable[[Dict[str, str], Option], Dict[str, str]]
|
|
||||||
|
|
||||||
"""Takes a list of option tuples, and returns a function that takes
|
"""Takes a list of option tuples, and returns a function that takes
|
||||||
the output of getopt and reduces it to the longopt key and
|
the output of getopt and reduces it to the longopt key and
|
||||||
associated values as a dictionary.
|
associated values as a dictionary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fullset = {} # type: Dict[str, str]
|
fullset = {}
|
||||||
for option in options:
|
for option in options:
|
||||||
if option.long:
|
if option[1]:
|
||||||
fullset['--' + option.long] = option.long
|
fullset['--' + option[1]] = option[1]
|
||||||
if option.short:
|
if option[0]:
|
||||||
fullset['-' + option.short] = option.long
|
fullset['-' + option[0]] = option[1]
|
||||||
|
|
||||||
def streamliner(acc, it):
|
def streamliner(acc, it):
|
||||||
# type: (Dict[str, str], Option) -> Dict[str, str]
|
|
||||||
acc[fullset[it[0]]] = it[1]
|
acc[fullset[it[0]]] = it[1]
|
||||||
return acc
|
return acc
|
||||||
|
|
||||||
return streamliner
|
return streamliner
|
||||||
|
|
||||||
def remove_conflicted_options(options, request):
|
def remove_conflicted_options(options, request):
|
||||||
# type: (List[Option], Dict[str, str]) -> Tuple[List[str], List[str]]
|
|
||||||
"""Takes our list of option tuples, and a cleaned copy of what was
|
"""Takes our list of option tuples, and a cleaned copy of what was
|
||||||
requested from getopt, and returns a copy of the request
|
requested from getopt, and returns a copy of the request
|
||||||
without any options that are marked as superseded, along with
|
without any options that are marked as superseded, along with
|
||||||
the list of superseded options
|
the list of superseded options
|
||||||
"""
|
"""
|
||||||
def get_excluded_keys(memo, option):
|
def get_excluded_keys(memo, opt):
|
||||||
return memo + option.conflicts
|
return memo + ((len(opt) > 4 and opt[4]) or [])
|
||||||
|
|
||||||
keys = request.keys()
|
keys = request.keys()
|
||||||
marked = [option for option in options if option.long in keys]
|
marked = [option for option in options if option[1] in keys]
|
||||||
exclude = reduce(get_excluded_keys, marked, [])
|
exclude = reduce(get_excluded_keys, marked, [])
|
||||||
excluded = [key for key in keys if key in exclude]
|
excluded = [key for key in keys if key in exclude]
|
||||||
cleaned = {key: request[key] for key in keys
|
cleaned = {key: request[key] for key in keys
|
||||||
if key not in excluded}
|
if key not in excluded}
|
||||||
return (cleaned, excluded)
|
return (cleaned, excluded)
|
||||||
|
|
||||||
def shortoptstogo(i):
|
def shortoptstogo(i): return i[0] + ((i[2] and ':') or '')
|
||||||
return i.short + ((i.takes and ':') or '')
|
|
||||||
|
|
||||||
def longoptstogo(i):
|
def longoptstogo(i): return i[1] + ((i[2] and '=') or '')
|
||||||
return i.long + ((i.takes and '=') or '')
|
|
||||||
|
|
||||||
optstringsshort = ''.join([shortoptstogo(opt) for opt in options if opt.short])
|
optstringsshort = ''.join([shortoptstogo(opt) for opt in options])
|
||||||
optstringslong = [longoptstogo(opt) for opt in options]
|
optstringslong = [longoptstogo(opt) for opt in options]
|
||||||
(chosen_options, filenames) = getopt.getopt(commandline[1:],
|
(chosen_options, filenames) = getopt.getopt(commandline[1:],
|
||||||
optstringsshort,
|
optstringsshort,
|
||||||
|
@ -104,4 +82,4 @@ def cleanup_options(options, commandline):
|
||||||
(ret, excluded) = remove_conflicted_options(
|
(ret, excluded) = remove_conflicted_options(
|
||||||
options, reduce(streamline_options, chosen_options, {}))
|
options, reduce(streamline_options, chosen_options, {}))
|
||||||
|
|
||||||
return Arguments(ret, filenames, excluded)
|
return (ret, filenames, excluded)
|
||||||
|
|
|
@ -1,45 +1,40 @@
|
||||||
import gettext
|
import gettext
|
||||||
from option_handler import Option
|
|
||||||
|
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
OPTIONS = [
|
OPTIONS = [
|
||||||
Option('o', 'only', True,
|
('o', 'only', True,
|
||||||
_('A comma-separated list of only those linters to run'), ['exclude']),
|
_('A comma-separated list of only those linters to run'), ['exclude']),
|
||||||
Option('x', 'exclude', True,
|
('x', 'exclude', True,
|
||||||
_('A comma-separated list of linters to skip'), []),
|
_('A comma-separated list of linters to skip'), []),
|
||||||
Option('l', 'linters', False,
|
('l', 'linters', False,
|
||||||
_('Show the list of configured linters'), []),
|
_('Show the list of configured linters'), []),
|
||||||
Option('b', 'base', False,
|
('b', 'base', False,
|
||||||
_('Check all changed files in the repository, not just those in the current directory.'), []),
|
_('Check all changed files in the repository, not just those in the current directory.'), []),
|
||||||
Option('a', 'all', False,
|
('a', 'all', False,
|
||||||
_('Scan all files in the repository, not just those that have changed.'), ['revision']),
|
_('Scan all files in the repository, not just those that have changed.'), []),
|
||||||
Option('r', 'revision', True,
|
('e', 'every', False,
|
||||||
_('Scan all files changed between revisions'), []),
|
|
||||||
Option(None, 'pr', False,
|
|
||||||
_('Scan all files changed between head and previous check-in'), ['revision']),
|
|
||||||
Option('e', 'every', False,
|
|
||||||
_('Short for -b -a: scan everything'), []),
|
_('Short for -b -a: scan everything'), []),
|
||||||
Option('w', 'workspace', False,
|
('w', 'workspace', False,
|
||||||
_('Scan the workspace'), ['staging']),
|
_('Scan the workspace'), ['staging']),
|
||||||
Option('s', 'staging', False,
|
('s', 'staging', False,
|
||||||
_('Scan the staging area (useful for pre-commit).'), []),
|
_('Scan the staging area (useful for pre-commit).'), ['monitor']),
|
||||||
# ('g', 'changes', False,
|
('g', 'changes', False,
|
||||||
# _("Report lint failures only for diff'd sections"), ['complete']),
|
_("Report lint failures only for diff'd sections"), ['complete']),
|
||||||
# ('p', 'complete', False,
|
('p', 'complete', False,
|
||||||
# _('Report lint failures for all files'), []),
|
_('Report lint failures for all files'), []),
|
||||||
Option('t', 'bylinter', False,
|
('t', 'bylinter', False,
|
||||||
_('Group the reports by linter first as they appear in the config file [default]'), []),
|
_('Group the reports by linter first as they appear in the config file [default]'), []),
|
||||||
Option('f', 'byfile', False,
|
('f', 'byfile', False,
|
||||||
_('Group the reports by file first'), []),
|
_('Group the reports by file first'), []),
|
||||||
Option('d', 'dryrun', False,
|
('d', 'dryrun', False,
|
||||||
_('Dry run - report what would be done, but do not run linters'), []),
|
_('Dry run - report what would be done, but do not run linters'), []),
|
||||||
Option('c', 'config', True,
|
('m', 'monitor', False,
|
||||||
|
_('Run continuously, monitoring filesytem for changes'), []),
|
||||||
|
('c', 'config', True,
|
||||||
_('Path to config file'), []),
|
_('Path to config file'), []),
|
||||||
Option('h', 'help', False,
|
('h', 'help', False,
|
||||||
_('This help message'), []),
|
_('This help message'), []),
|
||||||
Option('V', 'verbose', False,
|
('v', 'version', False,
|
||||||
_('A slightly more verbose output'), []),
|
|
||||||
Option('v', 'version', False,
|
|
||||||
_('Version information'), [])
|
_('Version information'), [])
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,10 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from functools import reduce
|
|
||||||
from .git_lint import load_config, run_linters, git_base
|
|
||||||
import operator
|
|
||||||
import gettext
|
import gettext
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
|
||||||
def print_report(results, unlintable_filenames, cant_lint_filenames,
|
# ICK. Mutation, references, and hidden assignment.
|
||||||
broken_linter_names, unfindable_filenames, options={'bylinter': True}):
|
def group_by(iterable, field_id):
|
||||||
|
|
||||||
def base_file_cleaner(files):
|
|
||||||
return [file.replace(git_base + '/', '', 1) for file in files]
|
|
||||||
|
|
||||||
# ICK. Mutation, references, and hidden assignment.
|
|
||||||
def group_by(iterable, field_id):
|
|
||||||
results = []
|
results = []
|
||||||
keys = {}
|
keys = {}
|
||||||
for obj in iterable:
|
for obj in iterable:
|
||||||
|
@ -25,46 +16,37 @@ def print_report(results, unlintable_filenames, cant_lint_filenames,
|
||||||
results.append((key, keys[key]))
|
results.append((key, keys[key]))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def print_report(results, unlintable_filenames, cant_lint_filenames,
|
||||||
|
broken_linter_names, unfindable_filenames, options={'bylinter': True}):
|
||||||
sort_position = 1
|
sort_position = 1
|
||||||
grouping = _('Linter: {}')
|
grouping = _('Linter: {}')
|
||||||
if 'byfile' in options:
|
if 'byfile' in options:
|
||||||
sort_position = 0
|
sort_position = 0
|
||||||
grouping = _('Filename: {}')
|
grouping = _('Filename: {}')
|
||||||
grouped_results = group_by(results, sort_position)
|
grouped_results = group_by(results, sort_position)
|
||||||
|
|
||||||
for group in grouped_results:
|
for group in grouped_results:
|
||||||
messages = reduce(operator.add, [item[3] for item in group[1]], [])
|
|
||||||
if len(messages) == 0:
|
|
||||||
continue
|
|
||||||
print(grouping.format(group[0]))
|
print(grouping.format(group[0]))
|
||||||
for (filename, lintername, returncode, text) in group[1]:
|
for (filename, lintername, returncode, text) in group[1]:
|
||||||
if text:
|
print('\n'.join(text))
|
||||||
print('\n'.join(base_file_cleaner(text)))
|
|
||||||
print('')
|
print('')
|
||||||
print ('')
|
if len(broken_linter_names):
|
||||||
|
print(_('These linters could not be run:'), ','.join(broken_linter_names))
|
||||||
if len(broken_linter_names) and (len(cant_lint_filenames) or ('verbose' in options)):
|
|
||||||
print(_('Linters not found:'), ','.join(broken_linter_names))
|
|
||||||
if len(cant_lint_filenames):
|
if len(cant_lint_filenames):
|
||||||
print(' ' + _('Files not linted:'))
|
print(_('As a result, these files were not linted:'))
|
||||||
print('\n'.join([' {}'.format(f) for f in cant_lint_filenames]))
|
print('\n'.join([' {}'.format(f) for f in cant_lint_filenames]))
|
||||||
print('')
|
if len(unlintable_filenames):
|
||||||
|
print(_('The following files had no recognizeable linters:'))
|
||||||
if len(unlintable_filenames) and ('verbose' in options):
|
|
||||||
print(_('No recognizeable linters for:'))
|
|
||||||
print('\n'.join([' {}'.format(f) for f in unlintable_filenames]))
|
print('\n'.join([' {}'.format(f) for f in unlintable_filenames]))
|
||||||
print('')
|
|
||||||
|
|
||||||
if len(unfindable_filenames):
|
if len(unfindable_filenames):
|
||||||
print(_('Files not found:'))
|
print(_('The following files could not be found:'))
|
||||||
print('\n'.join([' {}'.format(f) for f in unfindable_filenames]))
|
print('\n'.join([' {}'.format(f) for f in unfindable_filenames]))
|
||||||
print('')
|
|
||||||
|
|
||||||
|
|
||||||
def print_help(options, name):
|
def print_help(options, name):
|
||||||
print(_('Usage: {} [options] [filenames]').format(name))
|
print(_('Usage: {} [options] [filenames]').format(name))
|
||||||
for item in options:
|
for item in options:
|
||||||
print(' {:<2} --{:<12} {}'.format((item[0] and ('-' + item[0])) or '', item[1], item[3]))
|
print(' -{:<1} --{:<12} {}'.format(item[0], item[1], item[3]))
|
||||||
|
|
||||||
|
|
||||||
def print_version(name, version):
|
def print_version(name, version):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.0.7
|
current_version = 0.0.4
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
|
|
||||||
|
@ -16,4 +16,3 @@ universal = 1
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
exclude = docs
|
exclude = docs
|
||||||
|
|
||||||
|
|
21
setup.py
21
setup.py
|
@ -1,22 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import re
|
# -*- coding: utf-8 -*-
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
|
|
||||||
def get_data_files(prefix):
|
|
||||||
if prefix.startswith('/System/Library/Frameworks'):
|
|
||||||
return []
|
|
||||||
return [(os.path.join(prefix, 'share/man/man1'), ['docs/_build/man/git-lint.1'])]
|
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument('--prefix', default='',
|
|
||||||
help='prefix to install data files')
|
|
||||||
opts, _ = parser.parse_known_args(sys.argv)
|
|
||||||
prefix = opts.prefix or sys.prefix or '/usr'
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -39,7 +24,7 @@ test_requirements = [
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='git_linter',
|
name='git_linter',
|
||||||
version='0.0.7',
|
version='0.0.4',
|
||||||
description="A git command to lint everything in your workspace (or stage) that was changed since the last commit.",
|
description="A git command to lint everything in your workspace (or stage) that was changed since the last commit.",
|
||||||
long_description=readme + '\n\n' + history,
|
long_description=readme + '\n\n' + history,
|
||||||
author='Kenneth M. "Elf" Sternberg',
|
author='Kenneth M. "Elf" Sternberg',
|
||||||
|
@ -51,11 +36,9 @@ setup(
|
||||||
package_dir={'git_lint':
|
package_dir={'git_lint':
|
||||||
'git_lint'},
|
'git_lint'},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
data_files=get_data_files(prefix),
|
|
||||||
install_requires=requirements,
|
install_requires=requirements,
|
||||||
license="MIT",
|
license="MIT",
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
scripts=['bin/git-lint-style'],
|
|
||||||
keywords='git lint style syntaxt development',
|
keywords='git lint style syntaxt development',
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
|
|
|
@ -102,17 +102,6 @@ def test_02_empty_repository():
|
||||||
assert stderr.startswith('No configuration file found,')
|
assert stderr.startswith('No configuration file found,')
|
||||||
|
|
||||||
|
|
||||||
# It should behave well when the repository is empty and we're
|
|
||||||
# running against staging.
|
|
||||||
|
|
||||||
def test_02b_empty_repository():
|
|
||||||
with gittemp() as path:
|
|
||||||
os.chdir(path)
|
|
||||||
shell('git init')
|
|
||||||
(stdout, stderr, rc) = fullshell('git lint -s')
|
|
||||||
assert stderr.startswith('No configuration file found,')
|
|
||||||
|
|
||||||
|
|
||||||
def test_03_simple_repository():
|
def test_03_simple_repository():
|
||||||
with gittemp() as path:
|
with gittemp() as path:
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/hy ; -*- mode: clojure -*-
|
||||||
|
(import subprocess os sys os.path)
|
||||||
|
|
||||||
|
(defn fmit [formatstring iterable]
|
||||||
|
(apply .format (+ [formatstring] (list iterable))))
|
||||||
|
|
||||||
|
(defmain [&rest args]
|
||||||
|
(let [[git_dir (subprocess.check_output ["git" "rev-parse" "--show-toplevel"])]]
|
||||||
|
(.append sys.path (os.path.join git_dir "git_lint"))
|
||||||
|
(import [git_lint [git_lint]])
|
||||||
|
(print (.join "\n" (map (fn [i] (if (get i 2)
|
||||||
|
(+ (fmit "\item[\oOptArg{{-{0}}}{{ names}} " i)
|
||||||
|
(fmit "\oOptArg{{--{1}}}={{ names}}] " i)
|
||||||
|
(fmit "{3}" i))
|
||||||
|
(+ (fmit "\item[\oOptArg{{-{0}}} " i)
|
||||||
|
(fmit "\oOptArg{{--{1}}}] " i)
|
||||||
|
(fmit "{3}" i))))
|
||||||
|
git_lint.OPTIONS_LIST)))))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue