git-format-staged 3.1.1 → 3.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +12 -14
  2. package/git-format-staged +258 -129
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # git-format-staged
2
2
 
3
- [![Build Status](https://travis-ci.org/hallettj/git-format-staged.svg?branch=master)](https://travis-ci.org/hallettj/git-format-staged)
4
-
5
3
  Consider a project where you want all code formatted consistently. So you use
6
4
  a formatting command. (For example I use [prettier][] in my Javascript and
7
5
  Typescript projects.) You want to make sure that everyone working on the project
@@ -39,13 +37,13 @@ patch step can be disabled with the `--no-update-working-tree` option.
39
37
 
40
38
  Install via the CLI:
41
39
 
42
- $ nix profile add github:hallettj/git-format-staged
40
+ $ nix profile install github:hallettj/git-format-staged
43
41
 
44
42
  Or add to your flake imports, and use the `default` package output.
45
43
 
46
44
  ### Install with NPM
47
45
 
48
- Requires Python version 3 or 2.7.
46
+ Requires Python 3.8 or later.
49
47
 
50
48
  Install as a development dependency in a project that uses npm packages:
51
49
 
@@ -57,7 +55,7 @@ Or install globally:
57
55
 
58
56
  ### Or just copy the script
59
57
 
60
- Requires Python version 3 or 2.7.
58
+ Requires Python 3.8 or later.
61
59
 
62
60
  If you do not use the above methods you can copy the
63
61
  [`git-format-staged`](./git-format-staged) script from this repository and
@@ -73,7 +71,7 @@ For detailed information run:
73
71
  The command expects a shell command to run a formatter, and one or more file
74
72
  patterns to identify which files should be formatted. For example:
75
73
 
76
- $ git-format-staged --formatter 'prettier --stdin-filepath "{}"' 'src/*.js'
74
+ $ git-format-staged --formatter 'prettier --stdin-filepath {}' 'src/*.js'
77
75
 
78
76
  That will format all files under `src/` and its subdirectories using
79
77
  `prettier`. The file pattern is tested against staged files using Python's
@@ -90,11 +88,11 @@ normal shell globbing. So if you need to match multiple patterns, you should
90
88
  pass multiple arguments with different patterns, and they will be grouped.
91
89
  So instead of e.g. `'src/**/*.{js,jsx,ts}'`, you would use:
92
90
 
93
- $ git-format-staged --formatter 'prettier --stdin-filepath "{}"' 'src/*.js' 'src/*.jsx' 'src/*.ts'
91
+ $ git-format-staged --formatter 'prettier --stdin-filepath {}' 'src/*.js' 'src/*.jsx' 'src/*.ts'
94
92
 
95
93
  Files can be excluded by prefixing a pattern with `!`. For example:
96
94
 
97
- $ git-format-staged --formatter 'prettier --stdin-filepath "{}"' '*.js' '!flow-typed/*'
95
+ $ git-format-staged --formatter 'prettier --stdin-filepath {}' '*.js' '!flow-typed/*'
98
96
 
99
97
  Patterns are evaluated from left-to-right: if a file matches multiple patterns
100
98
  the right-most pattern determines whether the file is included or excluded.
@@ -104,11 +102,11 @@ control. So it is not necessary to explicitly exclude stuff like
104
102
  `node_modules/`.
105
103
 
106
104
  The formatter command may include a placeholder, `{}`, which will be replaced
107
- with the path of the file that is being formatted. This is useful if your
108
- formatter needs to know the file extension to determine how to format or to
109
- lint each file. For example:
105
+ with the path of the file that is being formatted (with appropriate quoting).
106
+ This is useful if your formatter needs to know the file extension to determine
107
+ how to format or to lint each file. For example:
110
108
 
111
- $ git-format-staged -f 'prettier --stdin-filepath "{}"' '*.js' '*.css'
109
+ $ git-format-staged -f 'prettier --stdin-filepath {}' '*.js' '*.css'
112
110
 
113
111
  Do not attempt to read or write to `{}` in your formatter command! The
114
112
  placeholder exists only for referencing the file name and path.
@@ -120,7 +118,7 @@ prevent files from being committed if they do not conform to style rules. You
120
118
  can use git-format-staged with the `--no-write` option, and supply a lint
121
119
  command instead of a format command. Here is an example using ESLint:
122
120
 
123
- $ git-format-staged --no-write -f 'eslint --stdin --stdin-filename "{}" >&2' 'src/*.js'
121
+ $ git-format-staged --no-write -f 'eslint --stdin --stdin-filename {} >&2' 'src/*.js'
124
122
 
125
123
  If this command is run in a pre-commit hook, and the lint command fails the
126
124
  commit will be aborted and error messages will be displayed. The lint command
@@ -146,7 +144,7 @@ Add a `prepare` script to install husky when running `npm install`:
146
144
 
147
145
  Add the pre-commit hook:
148
146
 
149
- $ npx husky add .husky/pre-commit "git-format-staged --formatter 'prettier --stdin-filepath \"{}\"' '*.js' '*.ts'"
147
+ $ npx husky add .husky/pre-commit "git-format-staged --formatter 'prettier --stdin-filepath {}' '*.js' '*.ts'"
150
148
  $ git add .husky/pre-commit
151
149
 
152
150
  Once again note that the formatter command and the `'*.js'` and `'*.ts'`
package/git-format-staged CHANGED
@@ -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-filepath "{}"' '*.js'
10
+ # Example: git-format-staged --formatter 'prettier --stdin-filepath {}' '*.js'
11
11
  #
12
- # Tested with Python 3.10 and Python 2.7.
12
+ # Tested with Python versions 3.8 - 3.15.
13
13
  #
14
14
  # Original author: Jesse Hallett <jesse@sitr.us>
15
15
 
@@ -19,51 +19,94 @@ from fnmatch import fnmatch
19
19
  from gettext import gettext as _
20
20
  import os
21
21
  import re
22
+ import shlex
23
+ import signal
22
24
  import subprocess
23
25
  import sys
24
26
 
25
- # The string 3.1.1 is replaced during the publish process.
26
- VERSION = '3.1.1'
27
+ # The string 3.1.3 is replaced during the publish process.
28
+ VERSION = "3.1.3"
27
29
  PROG = sys.argv[0]
28
30
 
31
+
29
32
  def info(msg):
30
33
  print(msg, file=sys.stdout)
31
34
 
32
- def warn(msg):
33
- print('{}: warning: {}'.format(PROG, msg), file=sys.stderr)
35
+
36
+ def info_stderr(msg: str):
37
+ print(msg, file=sys.stderr)
38
+
39
+
40
+ def warn(msg: str):
41
+ print("{}: warning: {}".format(PROG, msg), file=sys.stderr)
42
+
34
43
 
35
44
  def fatal(msg):
36
- print('{}: error: {}'.format(PROG, msg), file=sys.stderr)
45
+ print("{}: error: {}".format(PROG, msg), file=sys.stderr)
37
46
  exit(1)
38
47
 
39
- def format_staged_files(file_patterns, formatter, git_root, update_working_tree=True, write=True, verbose=False):
48
+
49
+ def format_staged_files(
50
+ file_patterns,
51
+ formatter,
52
+ git_root,
53
+ update_working_tree=True,
54
+ write=True,
55
+ verbose=False,
56
+ ):
57
+ common_opts = [
58
+ "--cached",
59
+ "--diff-filter=AM", # select only file additions and modifications
60
+ "--no-renames",
61
+ "HEAD",
62
+ ]
63
+
40
64
  try:
41
- output = subprocess.check_output([
42
- 'git', 'diff-index',
43
- '--cached',
44
- '--diff-filter=AM', # select only file additions and modifications
45
- '--no-renames',
46
- 'HEAD'
47
- ])
65
+ staged_files = {
66
+ path.decode("utf-8")
67
+ for path in subprocess.check_output(
68
+ ["git", "diff", "--name-only"] + common_opts
69
+ ).splitlines()
70
+ }
71
+
72
+ output = subprocess.check_output(["git", "diff-index"] + common_opts)
48
73
  for line in output.splitlines():
49
- entry = parse_diff(line.decode('utf-8'))
50
- entry_path = normalize_path(entry['src_path'], relative_to=git_root)
51
- if entry['dst_mode'] == '120000':
74
+ entry = parse_diff(line.decode("utf-8"))
75
+ entry_path = normalize_path(entry["src_path"], relative_to=git_root)
76
+ if entry["dst_mode"] == "120000":
52
77
  # Do not process symlinks
53
78
  continue
54
79
  if not (matches_some_path(file_patterns, entry_path)):
55
80
  continue
56
- if format_file_in_index(formatter, entry, update_working_tree=update_working_tree, write=write, verbose=verbose):
57
- info('Reformatted {} with {}'.format(entry['src_path'], formatter))
81
+ if (
82
+ entry["src_mode"] is None
83
+ and object_is_empty(entry["dst_hash"])
84
+ and entry["src_path"] not in staged_files
85
+ ):
86
+ # File is not staged, it's tracked only with `--intent-to-add` and won't get committed
87
+ continue
88
+ if format_file_in_index(
89
+ formatter,
90
+ entry,
91
+ update_working_tree=update_working_tree,
92
+ write=write,
93
+ verbose=verbose,
94
+ ):
95
+ info("Reformatted {} with {}".format(entry["src_path"], formatter))
58
96
  except Exception as err:
59
97
  fatal(str(err))
60
98
 
99
+
61
100
  # Run formatter on file in the git index. Creates a new git object with the
62
101
  # result, and replaces the content of the file in the index with that object.
63
102
  # Returns hash of the new object if formatting produced any changes.
64
- def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=True, verbose=False):
65
- orig_hash = diff_entry['dst_hash']
66
- new_hash = format_object(formatter, orig_hash, diff_entry['src_path'], verbose=verbose)
103
+ def format_file_in_index(
104
+ formatter, diff_entry, update_working_tree=True, write=True, verbose=False
105
+ ):
106
+ orig_hash = diff_entry["dst_hash"]
107
+ new_hash = format_object(
108
+ formatter, orig_hash, diff_entry["src_path"], verbose=verbose
109
+ )
67
110
 
68
111
  # If the new hash is the same then the formatter did not make any changes.
69
112
  if not write or new_hash == orig_hash:
@@ -79,128 +122,210 @@ def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=
79
122
 
80
123
  if update_working_tree:
81
124
  try:
82
- patch_working_file(diff_entry['src_path'], orig_hash, new_hash)
125
+ patch_working_file(diff_entry["src_path"], orig_hash, new_hash)
83
126
  except Exception as err:
84
127
  # Errors patching working tree files are not fatal
85
128
  warn(str(err))
86
129
 
87
130
  return new_hash
88
131
 
89
- file_path_placeholder = re.compile(r'\{\}')
132
+
133
+ # Match {}, and to avoid breaking quoting from shlex also match and remove surrounding quotes. This
134
+ # is important for backward compatibility because previous version of git-format-staged did not use
135
+ # shlex quoting, and required manual quoting.
136
+ file_path_placeholder = re.compile(r"(['\"]?)\{\}(\1)")
137
+
90
138
 
91
139
  # Run formatter on a git blob identified by its hash. Writes output to a new git
92
140
  # blob, and returns the hash of the new blob.
93
141
  def format_object(formatter, object_hash, file_path, verbose=False):
94
142
  get_content = subprocess.Popen(
95
- ['git', 'cat-file', '-p', object_hash],
96
- stdout=subprocess.PIPE
97
- )
98
- command = re.sub(file_path_placeholder, file_path, formatter)
143
+ ["git", "cat-file", "-p", object_hash], stdout=subprocess.PIPE
144
+ )
145
+
146
+ command = re.sub(file_path_placeholder, shlex.quote(file_path), formatter)
99
147
  if verbose:
100
- info(command)
148
+ info_stderr(command)
101
149
  format_content = subprocess.Popen(
102
- command,
103
- shell=True,
104
- stdin=get_content.stdout,
105
- stdout=subprocess.PIPE
106
- )
107
- write_object = subprocess.Popen(
108
- ['git', 'hash-object', '-w', '--stdin'],
109
- stdin=format_content.stdout,
110
- stdout=subprocess.PIPE
111
- )
150
+ command, shell=True, stdin=get_content.stdout, stdout=subprocess.PIPE
151
+ )
112
152
 
113
- get_content.stdout.close()
114
- format_content.stdout.close()
115
-
116
- if get_content.wait() != 0:
117
- raise ValueError('unable to read file content from object database: ' + object_hash)
153
+ write_object = subprocess.Popen(
154
+ ["git", "hash-object", "-w", "--stdin"],
155
+ stdin=format_content.stdout,
156
+ stdout=subprocess.PIPE,
157
+ )
158
+
159
+ # Close the parent process reference to stdout, leaving only references in the child processes.
160
+ # This way if the downstream process terminates while format_content is still running,
161
+ # format_content will be terminated with a SIGPIPE signal.
162
+ format_content.stdout.close() # pyright: ignore[reportOptionalMemberAccess]
163
+
164
+ # On the other hand we don't close get_content.stdout() so that we can check if there is unread
165
+ # data left after the formatter has finished.
166
+
167
+ # Read output from the last process in the pipe, and block until that process has completed.
168
+ # It's important to block on the last process completing before waiting for the other sub
169
+ # processes to finish.
170
+ new_hash, _err = write_object.communicate()
171
+
172
+ # The first two pipe processes should have completed by now. Block to verify that we have exit
173
+ # statuses from them.
174
+ try:
175
+ # Use communicate() to check for any unread output from get_content.
176
+ get_content_unread_stdout, _ = get_content.communicate(timeout=5)
177
+ get_content.stdout.close() # pyright: ignore[reportOptionalMemberAccess]
178
+ format_content_exit_status = format_content.wait(timeout=5)
179
+ except subprocess.TimeoutExpired as exception:
180
+ raise Exception(
181
+ "the formatter command did not terminate as expected"
182
+ ) from exception
183
+
184
+ # An error from format_content is most relevant to the user, so prioritize displaying this error
185
+ # message in case multiple things went wrong.
186
+ if format_content_exit_status != 0:
187
+ raise Exception(
188
+ f"formatter exited with non-zero status ({format_content_exit_status}) while processing {file_path}"
189
+ )
118
190
 
119
- if format_content.wait() != 0:
120
- raise Exception('formatter exited with non-zero status') # TODO: capture stderr from format command
191
+ # If the formatter exited before reading all input then get_content might have been terminated
192
+ # by a SIGPIPE signal. This is probably incorrect behavior from the formatter command, but is
193
+ # allowed by design (for now). So we emit a warning, but will not fail.
194
+
195
+ # If there was unread output from get_content that's an indication that the formatter command is
196
+ # probably not configured correctly. But program design allows the formatter command do do what
197
+ # it wants. So this is a warning, not a hard error.
198
+ #
199
+ # A SIGPIPE termination to get_content would also indicate unread output. This should not
200
+ # happen, but it doesn't hurt to check.
201
+ if len(get_content_unread_stdout) > 0 or get_content.returncode == -signal.SIGPIPE:
202
+ warn(
203
+ f"the formatter command exited before reading all content from {file_path}"
204
+ )
121
205
 
122
- new_hash, err = write_object.communicate()
206
+ if get_content.returncode != 0 and get_content.returncode != -signal.SIGPIPE:
207
+ if verbose:
208
+ info_stderr(
209
+ f"non-zero exit status from `git cat-file -p {object_hash}`\n"
210
+ + f"exit status: {get_content.returncode}\n"
211
+ + f"file path: {file_path}\n"
212
+ )
213
+ raise ValueError(
214
+ f"unable to read file content for {file_path} from object database."
215
+ )
123
216
 
124
217
  if write_object.returncode != 0:
125
- raise Exception('unable to write formatted content to object database')
218
+ if verbose:
219
+ info_stderr(
220
+ f"non-zero exit status from `git hash-object -w --stdin`\n"
221
+ + f"exit status: {write_object.returncode}\n"
222
+ + f"file path: {file_path}\n"
223
+ )
224
+ raise Exception("unable to write formatted content to object database")
225
+
226
+ return new_hash.decode("utf-8").rstrip()
126
227
 
127
- return new_hash.decode('utf-8').rstrip()
128
228
 
129
229
  def object_is_empty(object_hash):
130
230
  get_content = subprocess.Popen(
131
- ['git', 'cat-file', '-p', object_hash],
132
- stdout=subprocess.PIPE
133
- )
231
+ ["git", "cat-file", "-p", object_hash], stdout=subprocess.PIPE
232
+ )
134
233
  content, err = get_content.communicate()
135
234
 
136
235
  if get_content.returncode != 0:
137
- raise Exception('unable to verify content of formatted object')
236
+ raise Exception("unable to verify content of formatted object")
138
237
 
139
238
  return not content
140
239
 
240
+
141
241
  def replace_file_in_index(diff_entry, new_object_hash):
142
- subprocess.check_call(['git', 'update-index',
143
- '--cacheinfo', '{},{},{}'.format(
144
- diff_entry['dst_mode'],
145
- new_object_hash,
146
- diff_entry['src_path']
147
- )])
242
+ subprocess.check_call(
243
+ [
244
+ "git",
245
+ "update-index",
246
+ "--cacheinfo",
247
+ "{},{},{}".format(
248
+ diff_entry["dst_mode"], new_object_hash, diff_entry["src_path"]
249
+ ),
250
+ ]
251
+ )
252
+
148
253
 
149
254
  def patch_working_file(path, orig_object_hash, new_object_hash):
150
255
  patch = subprocess.check_output(
151
- ['git', 'diff', '--no-ext-diff', '--color=never', orig_object_hash, new_object_hash]
152
- )
256
+ [
257
+ "git",
258
+ "diff",
259
+ "--no-ext-diff",
260
+ "--color=never",
261
+ orig_object_hash,
262
+ new_object_hash,
263
+ ]
264
+ )
153
265
 
154
266
  # Substitute object hashes in patch header with path to working tree file
155
- patch_b = patch.replace(orig_object_hash.encode(), path.encode()).replace(new_object_hash.encode(), path.encode())
267
+ patch_b = patch.replace(orig_object_hash.encode(), path.encode()).replace(
268
+ new_object_hash.encode(), path.encode()
269
+ )
156
270
 
157
271
  apply_patch = subprocess.Popen(
158
- ['git', 'apply', '-'],
159
- stdin=subprocess.PIPE,
160
- stdout=subprocess.PIPE,
161
- stderr=subprocess.PIPE
162
- )
272
+ ["git", "apply", "-"],
273
+ stdin=subprocess.PIPE,
274
+ stdout=subprocess.PIPE,
275
+ stderr=subprocess.PIPE,
276
+ )
163
277
 
164
278
  output, err = apply_patch.communicate(input=patch_b)
165
279
 
166
280
  if apply_patch.returncode != 0:
167
- raise Exception('could not apply formatting changes to working tree file {}'.format(path))
281
+ raise Exception(
282
+ "could not apply formatting changes to working tree file {}".format(path)
283
+ )
284
+
168
285
 
169
286
  # Format: src_mode dst_mode src_hash dst_hash status/score? src_path dst_path?
170
- diff_pat = re.compile(r'^:(\d+) (\d+) ([a-f0-9]+) ([a-f0-9]+) ([A-Z])(\d+)?\t([^\t]+)(?:\t([^\t]+))?$')
287
+ diff_pat = re.compile(
288
+ r"^:(\d+) (\d+) ([a-f0-9]+) ([a-f0-9]+) ([A-Z])(\d+)?\t([^\t]+)(?:\t([^\t]+))?$"
289
+ )
290
+
171
291
 
172
292
  # Parse output from `git diff-index`
173
293
  def parse_diff(diff):
174
294
  m = diff_pat.match(diff)
175
295
  if not m:
176
- raise ValueError('Failed to parse diff-index line: ' + diff)
296
+ raise ValueError("Failed to parse diff-index line: " + diff)
177
297
  return {
178
- 'src_mode': unless_zeroed(m.group(1)),
179
- 'dst_mode': unless_zeroed(m.group(2)),
180
- 'src_hash': unless_zeroed(m.group(3)),
181
- 'dst_hash': unless_zeroed(m.group(4)),
182
- 'status': m.group(5),
183
- 'score': int(m.group(6)) if m.group(6) else None,
184
- 'src_path': m.group(7),
185
- 'dst_path': m.group(8)
186
- }
187
-
188
- zeroed_pat = re.compile(r'^0+$')
298
+ "src_mode": unless_zeroed(m.group(1)),
299
+ "dst_mode": unless_zeroed(m.group(2)),
300
+ "src_hash": unless_zeroed(m.group(3)),
301
+ "dst_hash": unless_zeroed(m.group(4)),
302
+ "status": m.group(5),
303
+ "score": int(m.group(6)) if m.group(6) else None,
304
+ "src_path": m.group(7),
305
+ "dst_path": m.group(8),
306
+ }
307
+
308
+
309
+ zeroed_pat = re.compile(r"^0+$")
310
+
189
311
 
190
312
  # Returns the argument unless the argument is a string of zeroes, in which case
191
313
  # returns `None`
192
314
  def unless_zeroed(s):
193
315
  return s if not zeroed_pat.match(s) else None
194
316
 
317
+
195
318
  def get_git_root():
196
- return subprocess.check_output(
197
- ['git', 'rev-parse', '--show-toplevel']
198
- ).decode('utf-8').rstrip()
319
+ return (
320
+ subprocess.check_output(["git", "rev-parse", "--show-toplevel"])
321
+ .decode("utf-8")
322
+ .rstrip()
323
+ )
324
+
199
325
 
200
326
  def normalize_path(p, relative_to=None):
201
- return os.path.abspath(
202
- os.path.join(relative_to, p) if relative_to else p
203
- )
327
+ return os.path.abspath(os.path.join(relative_to, p) if relative_to else p)
328
+
204
329
 
205
330
  def matches_some_path(patterns, target):
206
331
  is_match = False
@@ -210,70 +335,74 @@ def matches_some_path(patterns, target):
210
335
  is_match = is_pattern_positive
211
336
  return is_match
212
337
 
338
+
213
339
  # Checks for a '!' as the first character of a pattern, returns the rest of the
214
340
  # pattern in a tuple. The tuple takes the form (is_pattern_positive, pattern).
215
341
  # For example:
216
342
  # from_signed_pattern('!pat') == (False, 'pat')
217
343
  # from_signed_pattern('pat') == (True, 'pat')
218
344
  def from_signed_pattern(pattern):
219
- if pattern[0] == '!':
345
+ if pattern[0] == "!":
220
346
  return (False, pattern[1:])
221
347
  else:
222
348
  return (True, pattern)
223
349
 
350
+
224
351
  class CustomArgumentParser(argparse.ArgumentParser):
225
352
  def parse_args(self, args=None, namespace=None):
226
353
  args, argv = self.parse_known_args(args, namespace)
227
354
  if argv:
228
355
  msg = argparse._(
229
- 'unrecognized arguments: %s. Do you need to quote your formatter command?'
230
- )
231
- self.error(msg % ' '.join(argv))
356
+ "unrecognized arguments: %s. Do you need to quote your formatter command?"
357
+ )
358
+ self.error(msg % " ".join(argv))
232
359
  return args
233
360
 
234
- if __name__ == '__main__':
361
+
362
+ if __name__ == "__main__":
235
363
  parser = CustomArgumentParser(
236
- description='Transform staged files using a formatting command that accepts content via stdin and produces a result via stdout.',
237
- epilog='Example: %(prog)s --formatter "prettier --stdin-filepath \'{}\'" "src/*.js" "test/*.js"'
238
- )
364
+ description="Transform staged files using a formatting command that accepts content via stdin and produces a result via stdout.",
365
+ epilog='Example: %(prog)s --formatter "prettier --stdin-filepath {}" "src/*.js" "test/*.js"',
366
+ )
239
367
  parser.add_argument(
240
- '--formatter', '-f',
241
- required=True,
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 \'{}\'")'
243
- )
368
+ "--formatter",
369
+ "-f",
370
+ required=True,
371
+ 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 (with appropriate quoting). (Example: "prettier --stdin-filepath {}")',
372
+ )
244
373
  parser.add_argument(
245
- '--no-update-working-tree',
246
- action='store_true',
247
- help='By default formatting changes made to staged file content will also be applied to working tree files via a patch. This option disables that behavior, leaving working tree files untouched.'
248
- )
374
+ "--no-update-working-tree",
375
+ action="store_true",
376
+ help="By default formatting changes made to staged file content will also be applied to working tree files via a patch. This option disables that behavior, leaving working tree files untouched.",
377
+ )
249
378
  parser.add_argument(
250
- '--no-write',
251
- action='store_true',
252
- help='Prevents %(prog)s from modifying staged or working tree files. You can use this option to check staged changes with a linter instead of formatting. With this option stdout from the formatter command is ignored. Example: %(prog)s --no-write -f "eslint --stdin --stdin-filename \'{}\' >&2" "*.js"'
253
- )
379
+ "--no-write",
380
+ action="store_true",
381
+ help='Prevents %(prog)s from modifying staged or working tree files. You can use this option to check staged changes with a linter instead of formatting. With this option stdout from the formatter command is ignored. Example: %(prog)s --no-write -f "eslint --stdin --stdin-filename {} >&2" "*.js"',
382
+ )
254
383
  parser.add_argument(
255
- '--version',
256
- action='version',
257
- version='%(prog)s version {}'.format(VERSION),
258
- help='Display version of %(prog)s'
259
- )
384
+ "--version",
385
+ action="version",
386
+ version="%(prog)s version {}".format(VERSION),
387
+ help="Display version of %(prog)s",
388
+ )
260
389
  parser.add_argument(
261
- '--verbose',
262
- help='Show the formatting commands that are running',
263
- action='store_true'
264
- )
390
+ "--verbose",
391
+ help="Show the formatting commands that are running",
392
+ action="store_true",
393
+ )
265
394
  parser.add_argument(
266
- 'files',
267
- nargs='+',
268
- help='Patterns that specify files to format. The formatter will only transform staged files that are given here. Patterns may be literal file paths, or globs which will be tested against staged file paths using Python\'s fnmatch function. For example "src/*.js" will match all files with a .js extension in src/ and its subdirectories. Patterns may be negated to exclude files using a "!" character. Patterns are evaluated left-to-right. (Example: "main.js" "src/*.js" "test/*.js" "!test/todo/*")'
269
- )
395
+ "files",
396
+ nargs="+",
397
+ help='Patterns that specify files to format. The formatter will only transform staged files that are given here. Patterns may be literal file paths, or globs which will be tested against staged file paths using Python\'s fnmatch function. For example "src/*.js" will match all files with a .js extension in src/ and its subdirectories. Patterns may be negated to exclude files using a "!" character. Patterns are evaluated left-to-right. (Example: "main.js" "src/*.js" "test/*.js" "!test/todo/*")',
398
+ )
270
399
  args = parser.parse_args()
271
- files = vars(args)['files']
400
+ files = vars(args)["files"]
272
401
  format_staged_files(
273
- file_patterns=files,
274
- formatter=vars(args)['formatter'],
275
- git_root=get_git_root(),
276
- update_working_tree=not vars(args)['no_update_working_tree'],
277
- write=not vars(args)['no_write'],
278
- verbose=vars(args)['verbose']
279
- )
402
+ file_patterns=files,
403
+ formatter=vars(args)["formatter"],
404
+ git_root=get_git_root(),
405
+ update_working_tree=not vars(args)["no_update_working_tree"],
406
+ write=not vars(args)["no_write"],
407
+ verbose=vars(args)["verbose"],
408
+ )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-format-staged",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "description": "Git command to transform staged files according to a command that accepts file content on stdin and produces output on stdout.",
5
5
  "scripts": {
6
6
  "test": "ava",
@@ -29,17 +29,17 @@
29
29
  "git-format-staged"
30
30
  ],
31
31
  "devDependencies": {
32
- "@commitlint/cli": "^8.3.5",
33
- "@commitlint/config-conventional": "^8.3.4",
32
+ "@commitlint/cli": "^19.0.3",
33
+ "@commitlint/config-conventional": "^19.0.3",
34
34
  "@types/fs-extra": "^8.1.0",
35
35
  "@types/tmp": "^0.1.0",
36
36
  "ava": "^3.8.1",
37
37
  "eslint": "^5.16.0",
38
38
  "fs-extra": "^9.0.0",
39
- "husky": "^4.2.5",
39
+ "husky": "^9.0.11",
40
40
  "micromatch": "^4.0.2",
41
41
  "prettier-standard": "^9.1.1",
42
- "semantic-release": "^19.0.2",
42
+ "semantic-release": "^23.0.2",
43
43
  "strip-indent": "^3.0.0",
44
44
  "tmp": "0.2.0",
45
45
  "ts-node": "^8.9.1",