The challenge is that version control on text files (list of lines) is fairly straight forward and language agnostic, while OTOH the VCS would have to run after the language parser processes on the text file, and then be able to interface with the AST generated by the parser (for multiple languages). I presume this would become easy if different languages have the same parsing setup, or we have language/project-specific VCS tools. While the answers are not obvious (to me, at least) this definitely seems like the right direction to move in, instead of getting hung up on plain text and formatting issues.
But yeah, otherwise everyone could just have their own preferred formatter/settings and we could commit whatever representation is most optimal for diffing to version control (e.x. multi-line object literals, trailing commas in arrays/args, etc).
You could use something more compact like a binary AST but for compatibility with "legacy" tools that deal with text I think it would be better to use a more verbose text-based representation.
Python 2.7.15 (default, Feb 14 2019, 16:26:56)
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.10.44.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> ast.dump(ast.parse('print "foo"'))
"Module(body=[Print(dest=None, values=[Str(s='foo')], nl=True)])"
but in python 3.7: Python 3.7.2 (default, Feb 14 2019, 16:12:42)
[Clang 10.0.0 (clang-1000.10.44.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> ast.parse('print "foo"')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/philsnow/.pyenv/versions/3.7.2/lib/python3.7/ast.py", line 35, in parse
return compile(source, filename, mode, PyCF_ONLY_AST)
File "<unknown>", line 1
print "foo"
^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print("foo")?
nothing changed in the text, but the AST is different. What should get committed to this hypothetical "git-ast"?The answer probably lies in creating a different abstraction , different building blocks. In git we have bytes -> blobs -> trees -> commits, where the bytes of a given file get annotated with a file name (and mode bits etc etc, gross posix things) by the tree object.
In git-ast, maybe the abstraction is bytes -> lexemes (which annotates the bytes with a language version, and thus lexer version) -> tree? I haven't gone down the thought experiment that far.
I tried to make an example with `with`, because that was introduced between 2.4 and 2.5 IIRC, and 2.4 and 2.5 aren't nearly as far apart as 2.* and 3.*, but I didn't find an easy way to install a 2.4 python (it didn't compile in pyenv on my mac, and I looked at a couple docker images before giving up).
The worst case is that you export it with the old version of the converter, and then the code changes meaning. You have silent corruption. But if you stored raw text this would always happen.
The good case is that you export it with the new version of the converter. It will notice that a variable name matches a keyword, and alert you to fix it.
Of course that doesn't help if you're setting up the formatter for the first time, or want to upgrade to a new version.
Some tools like GitHub's diff do try to deemphasize whitespace but it's pretty limited.
1. Use a tracked directory for Git hooks: `git config --local core.hooksPath 'git-hooks'`.
2. Create format.bash and lint.bash.
2.1. Run `black` and `isort` in format.bash.
2.2. Run `flake8`, `mypy`, `black` and `isort` in lint.bash.
2.3. Run lint.bash in the pre-commit hook.
2.4. By default, lint/format only files which are different from origin/master to save time: `git diff --diff-filter=d --name-only -z origin/master "[asterisk].py"`.
3. In CI, run lint.bash with `--all` or some other keyword to verify formatting on every file: `git ls-files -z -- "[asterisk].py"`.
After more than a year with this setup I absolutely adore it.
I've found git hooks to be more trouble than they're worth.
But as long as it's enforced in CI you can leave it up to individual team members how they want to do it.
You can do a format run over the whole codebase then ignore the commit in blame.
https://www.moxio.com/blog/43/ignoring-bulk-change-commits-w...
BUT
I'm tired of seeing diff chunks on github where 95% of the chunk is because black/gofmt decided to change the formatting / indentation / grouping / whatever. I want language-agnostic, AST-aware diffing, that de-emphasizes the part of a chunk that is irrelevant-in-the-world-of-auto-matic-code-formatting.