git-format-staged 2.0.0 → 2.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.
package/README.md CHANGED
@@ -61,7 +61,7 @@ For detailed information run:
61
61
  The command expects a shell command to run a formatter, and one or more file
62
62
  patterns to identify which files should be formatted. For example:
63
63
 
64
- $ git-format-staged --formatter 'prettier --stdin' 'src/*.js'
64
+ $ git-format-staged --formatter 'prettier --stdin-filepath "{}"' 'src/*.js'
65
65
 
66
66
  That will format all files under `src/` and its subdirectories using
67
67
  `prettier`. The file pattern is tested against staged files using Python's
@@ -75,7 +75,7 @@ content to `stdout`.
75
75
 
76
76
  Files can be excluded by prefixing a pattern with `!`. For example:
77
77
 
78
- $ git-format-staged --formatter 'prettier --stdin' '*.js' '!flow-typed/*'
78
+ $ git-format-staged --formatter 'prettier --stdin-filepath "{}"' '*.js' '!flow-typed/*'
79
79
 
80
80
  Patterns are evaluated from left-to-right: if a file matches multiple patterns
81
81
  the right-most pattern determines whether the file is included or excluded.
@@ -84,6 +84,16 @@ git-format-staged never operates on files that are excluded from version
84
84
  control. So it is not necessary to explicitly exclude stuff like
85
85
  `node_modules/`.
86
86
 
87
+ The formatter command may include a placeholder, `{}`, which will be replaced
88
+ with the path of the file that is being formatted. This is useful if your
89
+ formatter needs to know the file extension to determine how to format or to
90
+ lint each file. For example:
91
+
92
+ $ git-format-staged -f 'prettier --stdin-filepath "{}"' '*.js' '*.css'
93
+
94
+ Do not attempt to read or write to `{}` in your formatter command! The
95
+ placeholder exists only for referencing the file name and path.
96
+
87
97
  ### Check staged changes with a linter without formatting
88
98
 
89
99
  Perhaps you do not want to reformat files automatically; but you do want to
@@ -91,7 +101,7 @@ prevent files from being committed if they do not conform to style rules. You
91
101
  can use git-format-staged with the `--no-write` option, and supply a lint
92
102
  command instead of a format command. Here is an example using ESLint:
93
103
 
94
- $ git-format-staged --no-write -f 'eslint --stdin >&2' 'src/*.js'
104
+ $ git-format-staged --no-write -f 'eslint --stdin --stdin-filename "{}" >&2' 'src/*.js'
95
105
 
96
106
  If this command is run in a pre-commit hook, and the lint command fails the
97
107
  commit will be aborted and error messages will be displayed. The lint command
@@ -166,6 +176,10 @@ Some more comparisons:
166
176
  lint-staged does provide powerful configuration options around which files
167
177
  should be linted or formatted, what should happen before and after linting,
168
178
  and so on.
179
+ - [pretty-quick][] formats staged files with prettier. By default pretty-quick
180
+ will abort the commit if files are partially staged to allow the user to
181
+ decide how to re-stage changes from formatting. The result is more manual
182
+ effort compared to git-format-staged.
169
183
  - the one-liner
170
184
  `git diff --diff-filter=d --cached | grep '^[+-]' | grep -Ev '^(--- a/|\+\+\+ b/)' | LINT_COMMAND`
171
185
  (described [here][lint changed hunks]) extracts changed hunks and feeds them
@@ -175,5 +189,6 @@ Some more comparisons:
175
189
 
176
190
  [precise-commits]: https://github.com/nrwl/precise-commits
177
191
  [pre-commit]: https://pre-commit.com/#pre-commit-during-commits
192
+ [pretty-quick]: https://www.npmjs.com/package/pretty-quick
178
193
  [lint-staged]: https://github.com/okonet/lint-staged
179
194
  [lint changed hunks]: https://github.com/okonet/lint-staged/issues/62#issuecomment-383217916
package/git-format-staged CHANGED
@@ -7,7 +7,7 @@
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
12
  # Tested with Python 3.6 and Python 2.7.
13
13
  #
@@ -22,8 +22,8 @@ import re
22
22
  import subprocess
23
23
  import sys
24
24
 
25
- # The string 2.0.0 is replaced during the publish process.
26
- VERSION = '2.0.0'
25
+ # The string 2.1.3 is replaced during the publish process.
26
+ VERSION = '2.1.3'
27
27
  PROG = sys.argv[0]
28
28
 
29
29
  def info(msg):
@@ -48,6 +48,9 @@ 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
56
  if format_file_in_index(formatter, entry, update_working_tree=update_working_tree, write=write):
@@ -60,12 +63,18 @@ def format_staged_files(file_patterns, formatter, git_root, update_working_tree=
60
63
  # Returns hash of the new object if formatting produced any changes.
61
64
  def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=True):
62
65
  orig_hash = diff_entry['dst_hash']
63
- new_hash = format_object(formatter, orig_hash)
66
+ new_hash = format_object(formatter, orig_hash, diff_entry['src_path'])
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:
67
70
  return None
68
71
 
72
+ # If the content of the new object is empty then the formatter did not
73
+ # produce any output. We want to abort instead of replacing the file with an
74
+ # empty one.
75
+ if object_is_empty(new_hash):
76
+ return None
77
+
69
78
  replace_file_in_index(diff_entry, new_hash)
70
79
 
71
80
  if update_working_tree:
@@ -77,16 +86,17 @@ def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=
77
86
 
78
87
  return new_hash
79
88
 
89
+ file_path_placeholder = re.compile('\{\}')
80
90
 
81
91
  # Run formatter on a git blob identified by its hash. Writes output to a new git
82
92
  # blob, and returns the hash of the new blob.
83
- def format_object(formatter, object_hash):
93
+ def format_object(formatter, object_hash, file_path):
84
94
  get_content = subprocess.Popen(
85
95
  ['git', 'cat-file', '-p', object_hash],
86
96
  stdout=subprocess.PIPE
87
97
  )
88
98
  format_content = subprocess.Popen(
89
- formatter,
99
+ re.sub(file_path_placeholder, file_path, formatter),
90
100
  shell=True,
91
101
  stdin=get_content.stdout,
92
102
  stdout=subprocess.PIPE
@@ -113,6 +123,18 @@ def format_object(formatter, object_hash):
113
123
 
114
124
  return new_hash.decode('utf-8').rstrip()
115
125
 
126
+ def object_is_empty(object_hash):
127
+ get_content = subprocess.Popen(
128
+ ['git', 'cat-file', '-p', object_hash],
129
+ stdout=subprocess.PIPE
130
+ )
131
+ content, err = get_content.communicate()
132
+
133
+ if get_content.returncode != 0:
134
+ raise Exception('unable to verify content of formatted object')
135
+
136
+ return not content
137
+
116
138
  def replace_file_in_index(diff_entry, new_object_hash):
117
139
  subprocess.check_call(['git', 'update-index',
118
140
  '--cacheinfo', '{},{},{}'.format(
@@ -209,12 +231,12 @@ class CustomArgumentParser(argparse.ArgumentParser):
209
231
  if __name__ == '__main__':
210
232
  parser = CustomArgumentParser(
211
233
  description='Transform staged files using a formatting command that accepts content via stdin and produces a result via stdout.',
212
- epilog='Example: %(prog)s --formatter "prettier --stdin" "src/*.js" "test/*.js"'
234
+ epilog='Example: %(prog)s --formatter "prettier --stdin-filepath \'{}\'" "src/*.js" "test/*.js"'
213
235
  )
214
236
  parser.add_argument(
215
237
  '--formatter', '-f',
216
238
  required=True,
217
- help='Shell command to format files, will run once per file. (Example: "prettier --stdin")'
239
+ 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 \'{}\'")'
218
240
  )
219
241
  parser.add_argument(
220
242
  '--no-update-working-tree',
@@ -224,7 +246,7 @@ if __name__ == '__main__':
224
246
  parser.add_argument(
225
247
  '--no-write',
226
248
  action='store_true',
227
- 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 >&2" "*.js"'
249
+ 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"'
228
250
  )
229
251
  parser.add_argument(
230
252
  '--version',
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "git-format-staged",
3
- "version": "2.0.0",
3
+ "version": "2.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
- "precommit": "./git-format-staged --formatter prettier-standard '*.js'",
7
- "commitmsg": "commitlint -e $GIT_PARAMS",
8
6
  "test": "ava",
9
7
  "prepublishOnly": "sed -i \"s/\\$VERSION/$npm_package_version/\" git-format-staged",
10
8
  "semantic-release": "semantic-release"
11
9
  },
12
- "bin": "./git-format-staged",
10
+ "bin": {
11
+ "git-format-staged": "git-format-staged"
12
+ },
13
13
  "main": "./no-main.js",
14
14
  "repository": {
15
15
  "type": "git",
@@ -29,32 +29,40 @@
29
29
  "git-format-staged"
30
30
  ],
31
31
  "release": {
32
- "branch": "master"
32
+ "branches": "master"
33
33
  },
34
34
  "devDependencies": {
35
- "@babel/plugin-transform-flow-strip-types": "^7.0.0-beta.46",
36
- "@commitlint/cli": "^6.2.0",
37
- "@commitlint/config-angular": "^6.1.3",
38
- "ava": "^1.0.0-beta.4",
39
- "flow-bin": "^0.72.0",
40
- "fs-extra": "^6.0.1",
41
- "husky": "^0.14.3",
42
- "prettier-standard": "^8.0.1",
43
- "semantic-release": "^15.5.0",
44
- "strip-indent": "^2.0.0",
45
- "tmp": "0.0.33"
35
+ "@commitlint/cli": "^8.3.5",
36
+ "@commitlint/config-conventional": "^8.3.4",
37
+ "@types/fs-extra": "^8.1.0",
38
+ "@types/tmp": "^0.1.0",
39
+ "ava": "^3.8.1",
40
+ "eslint": "^5.16.0",
41
+ "fs-extra": "^9.0.0",
42
+ "husky": "^4.2.5",
43
+ "micromatch": "^4.0.2",
44
+ "prettier-standard": "^9.1.1",
45
+ "semantic-release": "^17.2.3",
46
+ "strip-indent": "^3.0.0",
47
+ "tmp": "0.2.0",
48
+ "ts-node": "^8.9.1",
49
+ "typescript": "^3.8.3"
46
50
  },
47
51
  "ava": {
48
- "babel": {
49
- "testOptions": {
50
- "plugins": [
51
- "@babel/plugin-transform-flow-strip-types"
52
- ]
53
- }
54
- },
55
- "color": false,
52
+ "extensions": [
53
+ "ts"
54
+ ],
55
+ "require": [
56
+ "ts-node/register"
57
+ ],
56
58
  "files": [
57
- "test/**/*_test.js"
59
+ "test/**/*_test.ts"
58
60
  ]
61
+ },
62
+ "husky": {
63
+ "hooks": {
64
+ "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS",
65
+ "pre-commit": "./git-format-staged --formatter prettier-standard '*.js'"
66
+ }
59
67
  }
60
68
  }