git_stage_formatter 2.2.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.releaserc.yaml +3 -0
- data/.travis.yml +3 -2
- data/CHANGELOG.md +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +34 -0
- data/README.md +59 -22
- data/Rakefile +21 -0
- data/git-format-staged +26 -14
- data/git_stage_formatter.gemspec +26 -0
- data/lib/git_stage_formatter/version.rb +1 -1
- data/lib/git_stage_formatter.rb +2 -1
- data/package-lock.json +16327 -6227
- data/package.json +1 -4
- metadata +13 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64a3dd9bf65c6bb93cb17974466d6a638bf68690e61a3ec9c113db4be63a6ac1
|
4
|
+
data.tar.gz: ded69b5da94227a628edb0c2f2341f960a340a3113fa8587b6be1f0405226346
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d6523769d0021592706c94ce8f9b47bce70abaa8b74d9c025c4bc33ff65803fd60381fc0e38882dcd1993ce8b3ae9d86e21cfbf0f7a046eb89b0bbfa8d495a0
|
7
|
+
data.tar.gz: 70492bc58d979e5105524e168353a82d2216f946cf1b776d774ff21fa22e9ab31277fd6e1c1438ee321d32e5df538ab7d87b067dc06b7dd64eb07c668fb4621e
|
data/.gitignore
CHANGED
data/.releaserc.yaml
ADDED
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
git_stage_formatter (3.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
pastel (0.8.0)
|
10
|
+
tty-color (~> 0.5)
|
11
|
+
rake (13.0.6)
|
12
|
+
tty-color (0.6.0)
|
13
|
+
tty-cursor (0.7.1)
|
14
|
+
tty-prompt (0.23.1)
|
15
|
+
pastel (~> 0.8)
|
16
|
+
tty-reader (~> 0.8)
|
17
|
+
tty-reader (0.9.0)
|
18
|
+
tty-cursor (~> 0.7)
|
19
|
+
tty-screen (~> 0.8)
|
20
|
+
wisper (~> 2.0)
|
21
|
+
tty-screen (0.8.1)
|
22
|
+
wisper (2.0.1)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
arm64-darwin-21
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
bundler (~> 2)
|
29
|
+
git_stage_formatter!
|
30
|
+
rake
|
31
|
+
tty-prompt
|
32
|
+
|
33
|
+
BUNDLED WITH
|
34
|
+
2.4.18
|
data/README.md
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
[![Build Status](https://travis-ci.org/hallettj/git-format-staged.svg?branch=master)](https://travis-ci.org/hallettj/git-format-staged)
|
4
4
|
|
5
5
|
Consider a project where you want all code formatted consistently. So you use
|
6
|
-
a formatting command. (For example I use [prettier
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
a formatting command. (For example I use [prettier][] in my Javascript and
|
7
|
+
Typescript projects.) You want to make sure that everyone working on the project
|
8
|
+
runs the formatter, so you use a tool like [husky][] to install a git pre-commit
|
9
|
+
hook. The naive way to write that hook would be to:
|
10
10
|
|
11
11
|
- get a list of staged files
|
12
12
|
- run the formatter on those files
|
@@ -30,14 +30,15 @@ version of the file that is committed will be formatted properly - the warning
|
|
30
30
|
just means that working tree copy of the file has been left unformatted. The
|
31
31
|
patch step can be disabled with the `--no-update-working-tree` option.
|
32
32
|
|
33
|
-
[prettier
|
33
|
+
[prettier]: https://prettier.io/
|
34
34
|
[husky]: https://www.npmjs.com/package/husky
|
35
35
|
|
36
|
-
|
37
36
|
## How to install
|
38
37
|
|
39
38
|
Requires Python version 3 or 2.7.
|
40
39
|
|
40
|
+
### npm
|
41
|
+
|
41
42
|
Install as a development dependency in a project that uses npm packages:
|
42
43
|
|
43
44
|
$ npm install --save-dev git-format-staged
|
@@ -46,11 +47,25 @@ Or install globally:
|
|
46
47
|
|
47
48
|
$ npm install --global git-format-staged
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
### bundler
|
51
|
+
|
52
|
+
Add this gem to your Gemfile:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
gem 'git_stage_formatter'
|
56
|
+
```
|
57
|
+
|
58
|
+
### gem
|
53
59
|
|
60
|
+
Install the binary via gem:
|
61
|
+
|
62
|
+
```sh
|
63
|
+
gem install git_stage_formatter
|
64
|
+
```
|
65
|
+
|
66
|
+
### Manually
|
67
|
+
|
68
|
+
If you don't use `npm`, `bundler` or `gem`, you can copy the [`git-format-staged`](./git-format-staged) script from this repository and place it in your executable path, or somewhere convenient. The script is MIT-licensed - so you can check the script into version control in your own open source project if you wish.
|
54
69
|
|
55
70
|
## How to use
|
56
71
|
|
@@ -61,7 +76,7 @@ For detailed information run:
|
|
61
76
|
The command expects a shell command to run a formatter, and one or more file
|
62
77
|
patterns to identify which files should be formatted. For example:
|
63
78
|
|
64
|
-
$ git-format-staged --formatter 'prettier --stdin
|
79
|
+
$ git-format-staged --formatter 'prettier --stdin-filepath "{}"' 'src/*.js'
|
65
80
|
|
66
81
|
That will format all files under `src/` and its subdirectories using
|
67
82
|
`prettier`. The file pattern is tested against staged files using Python's
|
@@ -73,9 +88,16 @@ file names.
|
|
73
88
|
The formatter command must read file content from `stdin`, and output formatted
|
74
89
|
content to `stdout`.
|
75
90
|
|
91
|
+
Note that the syntax of the `fnmatch` glob match is a is a bit different from
|
92
|
+
normal shell globbing. So if you need to match multiple patterns, you should
|
93
|
+
pass multiple arguments with different patterns, and they will be grouped.
|
94
|
+
So instead of e.g. `'src/**/*.{js,jsx,ts}'`, you would use:
|
95
|
+
|
96
|
+
$ git-format-staged --formatter 'prettier --stdin-filepath "{}"' 'src/*.js' 'src/*.jsx' 'src/*.ts'
|
97
|
+
|
76
98
|
Files can be excluded by prefixing a pattern with `!`. For example:
|
77
99
|
|
78
|
-
$ git-format-staged --formatter 'prettier --stdin' '*.js' '!flow-typed/*'
|
100
|
+
$ git-format-staged --formatter 'prettier --stdin-filepath "{}"' '*.js' '!flow-typed/*'
|
79
101
|
|
80
102
|
Patterns are evaluated from left-to-right: if a file matches multiple patterns
|
81
103
|
the right-most pattern determines whether the file is included or excluded.
|
@@ -89,7 +111,7 @@ with the path of the file that is being formatted. This is useful if your
|
|
89
111
|
formatter needs to know the file extension to determine how to format or to
|
90
112
|
lint each file. For example:
|
91
113
|
|
92
|
-
$ git-format-staged -f 'prettier --stdin
|
114
|
+
$ git-format-staged -f 'prettier --stdin-filepath "{}"' '*.js' '*.css'
|
93
115
|
|
94
116
|
Do not attempt to read or write to `{}` in your formatter command! The
|
95
117
|
placeholder exists only for referencing the file name and path.
|
@@ -116,23 +138,26 @@ notation) so that you can see them.
|
|
116
138
|
Follow these steps to automatically format all Javascript files on commit in
|
117
139
|
a project that uses npm.
|
118
140
|
|
119
|
-
Install git-format-staged, husky, and a formatter (I use prettier
|
141
|
+
Install git-format-staged, husky, and a formatter (I use `prettier`):
|
120
142
|
|
121
|
-
$ npm install --save-dev git-format-staged husky prettier
|
143
|
+
$ npm install --save-dev git-format-staged husky prettier
|
122
144
|
|
123
|
-
Add a `
|
145
|
+
Add a `prepare` script to install husky when running `npm install`:
|
124
146
|
|
125
|
-
"
|
126
|
-
|
127
|
-
}
|
147
|
+
$ npm set-script prepare "husky install"
|
148
|
+
$ npm run prepare
|
128
149
|
|
129
|
-
|
130
|
-
|
150
|
+
Add the pre-commit hook:
|
151
|
+
|
152
|
+
$ npx husky add .husky/pre-commit "git-format-staged --formatter 'prettier --stdin-filepath \"{}\"' '*.js' '*.ts'"
|
153
|
+
$ git add .husky/pre-commit
|
154
|
+
|
155
|
+
Once again note that the formatter command and the `'*.js'` and `'*.ts'`
|
156
|
+
patterns are quoted!
|
131
157
|
|
132
158
|
That's it! Whenever a file is changed as a result of formatting on commit you
|
133
159
|
will see a message in the output from `git commit`.
|
134
160
|
|
135
|
-
|
136
161
|
## Comparisons to similar utilities
|
137
162
|
|
138
163
|
There are other tools that will format or lint staged files. What distinguishes
|
@@ -192,3 +217,15 @@ Some more comparisons:
|
|
192
217
|
[pretty-quick]: https://www.npmjs.com/package/pretty-quick
|
193
218
|
[lint-staged]: https://github.com/okonet/lint-staged
|
194
219
|
[lint changed hunks]: https://github.com/okonet/lint-staged/issues/62#issuecomment-383217916
|
220
|
+
|
221
|
+
# Development
|
222
|
+
|
223
|
+
To bump the lib's version, run `bundle exec rake bump[1.2.3]` (replacing the value with the desired version).
|
224
|
+
|
225
|
+
To release a new version, update the version number in `version.rb` (likely done via `rake bump` above), and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
226
|
+
|
227
|
+
# Contributing
|
228
|
+
|
229
|
+
If you spot something wrong, missing, or if you'd like to propose improvements to this project, please open an Issue or a Pull Request with your ideas and I promise to get back to you within 24 hours! 😇
|
230
|
+
|
231
|
+
For a list of issues worth tackling check out: https://github.com/rogerluan/git_stage_formatter/issues
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "tty-prompt"
|
5
|
+
|
6
|
+
task default: %i[]
|
7
|
+
|
8
|
+
desc "Sets lib version to the semantic version given, and push it to remote."
|
9
|
+
task :bump, [:v] do |_t, args|
|
10
|
+
version = args[:v] || raise("A version is required. Pass it like `rake bump[1.2.3]`")
|
11
|
+
next unless TTY::Prompt.new.yes?("Would you like to set the new version of the app to be '#{version}'?")
|
12
|
+
|
13
|
+
version_filename = Dir.glob("lib/*/version.rb").first
|
14
|
+
version_file_contents = File.read(version_filename)
|
15
|
+
new_version_file_contents = version_file_contents.gsub(/VERSION = "(?:.*)"/, "VERSION = \"#{version}\"")
|
16
|
+
File.open(version_filename, "w") { |file| file.puts new_version_file_contents }
|
17
|
+
sh("bundle install")
|
18
|
+
sh("git add #{version_filename} Gemfile.lock")
|
19
|
+
sh("git commit -m 'Bump app version to v#{version}.'")
|
20
|
+
sh("git push origin")
|
21
|
+
end
|
data/git-format-staged
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env
|
1
|
+
#!/usr/bin/env python3
|
2
2
|
#
|
3
3
|
# Git command to transform staged files according to a command that accepts file
|
4
4
|
# content on stdin and produces output on stdout. This command is useful in
|
@@ -7,9 +7,9 @@
|
|
7
7
|
# ignoring unstaged changes.
|
8
8
|
#
|
9
9
|
# Usage: git-format-staged [OPTION]... [FILE]...
|
10
|
-
# Example: git-format-staged --formatter 'prettier --stdin' '*.js'
|
10
|
+
# Example: git-format-staged --formatter 'prettier --stdin-filepath "{}"' '*.js'
|
11
11
|
#
|
12
|
-
# Tested with Python 3.
|
12
|
+
# Tested with Python 3.10 and Python 2.7.
|
13
13
|
#
|
14
14
|
# Original author: Jesse Hallett <jesse@sitr.us>
|
15
15
|
|
@@ -27,7 +27,7 @@ VERSION = '$VERSION'
|
|
27
27
|
PROG = sys.argv[0]
|
28
28
|
|
29
29
|
def info(msg):
|
30
|
-
print(msg, file=sys.
|
30
|
+
print(msg, file=sys.stdout)
|
31
31
|
|
32
32
|
def warn(msg):
|
33
33
|
print('{}: warning: {}'.format(PROG, msg), file=sys.stderr)
|
@@ -36,7 +36,7 @@ def fatal(msg):
|
|
36
36
|
print('{}: error: {}'.format(PROG, msg), file=sys.stderr)
|
37
37
|
exit(1)
|
38
38
|
|
39
|
-
def format_staged_files(file_patterns, formatter, git_root, update_working_tree=True, write=True):
|
39
|
+
def format_staged_files(file_patterns, formatter, git_root, update_working_tree=True, write=True, verbose=False):
|
40
40
|
try:
|
41
41
|
output = subprocess.check_output([
|
42
42
|
'git', 'diff-index',
|
@@ -48,9 +48,12 @@ def format_staged_files(file_patterns, formatter, git_root, update_working_tree=
|
|
48
48
|
for line in output.splitlines():
|
49
49
|
entry = parse_diff(line.decode('utf-8'))
|
50
50
|
entry_path = normalize_path(entry['src_path'], relative_to=git_root)
|
51
|
+
if entry['dst_mode'] == '120000':
|
52
|
+
# Do not process symlinks
|
53
|
+
continue
|
51
54
|
if not (matches_some_path(file_patterns, entry_path)):
|
52
55
|
continue
|
53
|
-
if format_file_in_index(formatter, entry, update_working_tree=update_working_tree, write=write):
|
56
|
+
if format_file_in_index(formatter, entry, update_working_tree=update_working_tree, write=write, verbose=verbose):
|
54
57
|
info('Reformatted {} with {}'.format(entry['src_path'], formatter))
|
55
58
|
except Exception as err:
|
56
59
|
fatal(str(err))
|
@@ -58,9 +61,9 @@ def format_staged_files(file_patterns, formatter, git_root, update_working_tree=
|
|
58
61
|
# Run formatter on file in the git index. Creates a new git object with the
|
59
62
|
# result, and replaces the content of the file in the index with that object.
|
60
63
|
# Returns hash of the new object if formatting produced any changes.
|
61
|
-
def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=True):
|
64
|
+
def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=True, verbose=False):
|
62
65
|
orig_hash = diff_entry['dst_hash']
|
63
|
-
new_hash = format_object(formatter, orig_hash, diff_entry['src_path'])
|
66
|
+
new_hash = format_object(formatter, orig_hash, diff_entry['src_path'], verbose=verbose)
|
64
67
|
|
65
68
|
# If the new hash is the same then the formatter did not make any changes.
|
66
69
|
if not write or new_hash == orig_hash:
|
@@ -87,13 +90,16 @@ file_path_placeholder = re.compile('\{\}')
|
|
87
90
|
|
88
91
|
# Run formatter on a git blob identified by its hash. Writes output to a new git
|
89
92
|
# blob, and returns the hash of the new blob.
|
90
|
-
def format_object(formatter, object_hash, file_path):
|
93
|
+
def format_object(formatter, object_hash, file_path, verbose=False):
|
91
94
|
get_content = subprocess.Popen(
|
92
95
|
['git', 'cat-file', '-p', object_hash],
|
93
96
|
stdout=subprocess.PIPE
|
94
97
|
)
|
98
|
+
command = re.sub(file_path_placeholder, file_path, formatter)
|
99
|
+
if verbose:
|
100
|
+
info(command)
|
95
101
|
format_content = subprocess.Popen(
|
96
|
-
|
102
|
+
command,
|
97
103
|
shell=True,
|
98
104
|
stdin=get_content.stdout,
|
99
105
|
stdout=subprocess.PIPE
|
@@ -142,7 +148,7 @@ def replace_file_in_index(diff_entry, new_object_hash):
|
|
142
148
|
|
143
149
|
def patch_working_file(path, orig_object_hash, new_object_hash):
|
144
150
|
patch = subprocess.check_output(
|
145
|
-
['git', 'diff', orig_object_hash, new_object_hash]
|
151
|
+
['git', 'diff', '--no-ext-diff', '--color=never', orig_object_hash, new_object_hash]
|
146
152
|
)
|
147
153
|
|
148
154
|
# Substitute object hashes in patch header with path to working tree file
|
@@ -228,12 +234,12 @@ class CustomArgumentParser(argparse.ArgumentParser):
|
|
228
234
|
if __name__ == '__main__':
|
229
235
|
parser = CustomArgumentParser(
|
230
236
|
description='Transform staged files using a formatting command that accepts content via stdin and produces a result via stdout.',
|
231
|
-
epilog='Example: %(prog)s --formatter "prettier --stdin" "src/*.js" "test/*.js"'
|
237
|
+
epilog='Example: %(prog)s --formatter "prettier --stdin-filepath \'{}\'" "src/*.js" "test/*.js"'
|
232
238
|
)
|
233
239
|
parser.add_argument(
|
234
240
|
'--formatter', '-f',
|
235
241
|
required=True,
|
236
|
-
help='Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will be replaced with a path to the file being formatted. (Example: "prettier --stdin
|
242
|
+
help='Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will be replaced with a path to the file being formatted. (Example: "prettier --stdin-filepath \'{}\'")'
|
237
243
|
)
|
238
244
|
parser.add_argument(
|
239
245
|
'--no-update-working-tree',
|
@@ -251,6 +257,11 @@ if __name__ == '__main__':
|
|
251
257
|
version='%(prog)s version {}'.format(VERSION),
|
252
258
|
help='Display version of %(prog)s'
|
253
259
|
)
|
260
|
+
parser.add_argument(
|
261
|
+
'--verbose',
|
262
|
+
help='Show the formatting commands that are running',
|
263
|
+
action='store_true'
|
264
|
+
)
|
254
265
|
parser.add_argument(
|
255
266
|
'files',
|
256
267
|
nargs='+',
|
@@ -263,5 +274,6 @@ if __name__ == '__main__':
|
|
263
274
|
formatter=vars(args)['formatter'],
|
264
275
|
git_root=get_git_root(),
|
265
276
|
update_working_tree=not vars(args)['no_update_working_tree'],
|
266
|
-
write=not vars(args)['no_write']
|
277
|
+
write=not vars(args)['no_write'],
|
278
|
+
verbose=vars(args)['verbose']
|
267
279
|
)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/git_stage_formatter/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "git_stage_formatter"
|
7
|
+
spec.version = GitStageFormatter::VERSION
|
8
|
+
spec.authors = ["Roger Oba"]
|
9
|
+
spec.email = ["rogerluan.oba@gmail.com"]
|
10
|
+
spec.summary = "Script to transform staged files using a formatting command"
|
11
|
+
spec.homepage = "https://github.com/rogerluan/git_stage_formatter"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
|
14
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
15
|
+
spec.metadata["source_code_uri"] = "https://github.com/rogerluan/git_stage_formatter"
|
16
|
+
spec.metadata["changelog_uri"] = "https://github.com/rogerluan/git_stage_formatter/blob/master/CHANGELOG.md"
|
17
|
+
|
18
|
+
# Specify which files should be added to the gem when it is released.
|
19
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
22
|
+
end
|
23
|
+
spec.bindir = "bin"
|
24
|
+
spec.executables = ['git_stage_formatter']
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
end
|
data/lib/git_stage_formatter.rb
CHANGED
@@ -2,7 +2,8 @@ require_relative "git_stage_formatter/version"
|
|
2
2
|
|
3
3
|
module GitStageFormatter
|
4
4
|
def self.run(args)
|
5
|
-
|
5
|
+
# Wrap each argument in quotes to handle spaces in paths
|
6
|
+
args = args.map { |arg| "\"#{arg}\"" }.join(' ')
|
6
7
|
script_path = File.expand_path('../git-format-staged', File.dirname(__FILE__))
|
7
8
|
exec("#{script_path} #{args}")
|
8
9
|
end
|