mirror of
https://github.com/newren/git-filter-repo.git
synced 2024-06-12 23:24:02 +02:00
contrib, docs: make discovery of code formatting and linting easier
The desire to format or lint code throughout history has arisen several times. It's more natural to do this in filter-branch since it somewhat forces people to run external commands, but we have an example contrib demo that shows how to run an external command on each file in history that I created even before any of these requests came in and yet I still periodically get requests about it. Make lint-history ever-so-slightly easier to apply to a subset of filenames, and include its usage as an extra cheat sheet comparison for filter-branch-vs-filter-repo commands. Signed-off-by: Elijah Newren <newren@gmail.com>
This commit is contained in:
parent
bd2c9c4d4d
commit
23bec32283
|
@ -9,6 +9,7 @@ to learn how to convert over to using filter-repo.
|
|||
* [Intention of "equivalent" commands](#intention-of-equivalent-commands)
|
||||
* [Basic Differences](#basic-differences)
|
||||
* [Cheat Sheet: Conversion of Examples from the filter-branch manpage](#cheat-sheet-conversion-of-examples-from-the-filter-branch-manpage)
|
||||
* [Cheat Sheet: Additional conversion examples](#cheat-sheet-additional-conversion-examples)
|
||||
|
||||
## Half-hearted conversions
|
||||
|
||||
|
@ -309,3 +310,37 @@ Note that filter-branch accepts `--not` among the revision specifiers,
|
|||
but that appears to python to be a flag name which breaks parsing.
|
||||
So, instead of e.g. `--not C` as we might use with filter-branch, we
|
||||
can specify `^C` to filter-repo.
|
||||
|
||||
## Cheat Sheet: Additional conversion examples
|
||||
|
||||
### Running a code formatter or linter on each file with some extension
|
||||
|
||||
Running some program on a subset of files is relatively natural in
|
||||
filter-branch:
|
||||
|
||||
```shell
|
||||
git filter-branch --tree-filter '
|
||||
git ls-files -z "*.c" \
|
||||
| xargs -0 -n 1 clang-format -style=file -i
|
||||
'
|
||||
```
|
||||
|
||||
filter-repo decided not to provide a way to run an external program to
|
||||
do filtering, because most filter-branch uses of this ability are
|
||||
riddled with [safety
|
||||
problems](https://git-scm.com/docs/git-filter-branch#SAFETY) and
|
||||
[performance
|
||||
issues](https://git-scm.com/docs/git-filter-branch#PERFORMANCE).
|
||||
However, in special cases like this it's fairly safe. One can write a
|
||||
script that uses filter-repo as a library to achieve this, while also
|
||||
gaining filter-repo's automatic handling of other concerns like
|
||||
rewriting commit IDs in commit messages or pruning commits that become
|
||||
empty. In fact, one of the [contrib
|
||||
demos](../contrib/filter-repo-demos),
|
||||
[lint-history](../contrib/filter-repo-demos/lint-history), handles
|
||||
this exact type of situation already:
|
||||
|
||||
```shell
|
||||
lint-history --relevant 'return filename.endswith(b".c")' \
|
||||
clang-format -style=file -i
|
||||
```
|
||||
|
|
|
@ -13,7 +13,12 @@ NOTE: Several people have taken and modified this script for a variety
|
|||
of special cases (linting python files, linting jupyter notebooks, just
|
||||
linting java files, etc.) and posted their modifications at
|
||||
https://github.com/newren/git-filter-repo/issues/45
|
||||
Feel free to take a look and adopt some of their ideas.
|
||||
Feel free to take a look and adopt some of their ideas. Most of these
|
||||
modifications are probably strictly unnecessary since you could just make
|
||||
a lint-script that takes the filename, checks that it matches what you
|
||||
want, and then calls the real linter. But I guess folks don't like making
|
||||
an intermediate script. So I eventually added the --relevant flag for
|
||||
picking out certain files providing yet another way to handle it.
|
||||
"""
|
||||
|
||||
"""
|
||||
|
@ -22,7 +27,8 @@ Please see the
|
|||
near the top of git-filter-repo.
|
||||
"""
|
||||
|
||||
# Technically, this program could be replaced by a "one-liner"; e.g.
|
||||
# Technically, if you are only running on all non-binary files and don't care
|
||||
# about filenames, then this program could be replaced by a "one-liner"; e.g.
|
||||
# git filter-repo --force --blob-callback '
|
||||
# if not any(x == b"0" for x in blob.data[0:8192]):
|
||||
# filename = '.git/info/tmpfile'
|
||||
|
@ -34,7 +40,7 @@ near the top of git-filter-repo.
|
|||
# os.remove(filename)
|
||||
# '
|
||||
# but let's do it as a full-fledged program that imports git_filter_repo
|
||||
# anyway...
|
||||
# and show how to also do it with filename handling...
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
@ -45,8 +51,34 @@ try:
|
|||
except ImportError:
|
||||
raise SystemExit("Error: Couldn't find git_filter_repo.py. Did you forget to make a symlink to git-filter-repo named git_filter_repo.py or did you forget to put the latter in your PYTHONPATH?")
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Lint all files in history')
|
||||
example_text = '''CALLBACK
|
||||
|
||||
When you pass --relevant 'BODY', the following style of function
|
||||
will be compiled and called:
|
||||
|
||||
def is_relevant(filename):
|
||||
BODY
|
||||
|
||||
Thus, to only run on files with a ".txt" extension you would run
|
||||
lint-history --relevant 'return filename.endswith(b".txt")' ...
|
||||
|
||||
EXAMPLES
|
||||
|
||||
To run dos2unix on all non-binary files in history:
|
||||
lint-history dos2unix
|
||||
|
||||
To run eslint --fix on all .js files in history:
|
||||
lint-history --relevant 'return filename.endswith(b".js")' eslint --fix
|
||||
'''
|
||||
|
||||
parser = argparse.ArgumentParser(description='Run a program (e.g. code formatter or linter) on files in history',
|
||||
epilog = example_text,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
|
||||
parser.add_argument('--relevant', metavar="FUNCTION_BODY",
|
||||
help=("Python code for determining whether to apply linter to a "
|
||||
"given filename. Implies --filenames-important. See CALLBACK "
|
||||
"below."))
|
||||
parser.add_argument('--filenames-important', action='store_true',
|
||||
help=("By default, contents are written to a temporary file with a "
|
||||
"random name. If the linting program needs to know the file "
|
||||
|
@ -67,6 +99,8 @@ def lint_with_real_filenames(commit, metadata):
|
|||
change.blob_id = blobs_handled[change.blob_id]
|
||||
elif change.type == b'D':
|
||||
continue
|
||||
elif not is_relevant(change.filename):
|
||||
continue
|
||||
else:
|
||||
# Get the old blob contents
|
||||
cat_file_process.stdin.write(change.blob_id + b'\n')
|
||||
|
@ -104,6 +138,13 @@ def lint_non_binary_blobs(blob, metadata):
|
|||
blob.data = f.read()
|
||||
os.remove(filename)
|
||||
|
||||
if lint_args.filenames_important and not lint_args.relevant:
|
||||
lint_args.relevant = 'return True'
|
||||
if lint_args.relevant:
|
||||
body = lint_args.relevant
|
||||
exec('def is_relevant(filename):\n '+'\n '.join(body.splitlines()),
|
||||
globals())
|
||||
lint_args.filenames_important = True
|
||||
args = fr.FilteringOptions.default_options()
|
||||
args.force = True
|
||||
if lint_args.filenames_important:
|
||||
|
|
Loading…
Reference in New Issue
Block a user