• Stars
    star
    155
  • Rank 240,864 (Top 5 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 6 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Git command to transform staged files using a formatting command

git-format-staged

Build Status

Consider a project where you want all code formatted consistently. So you use a formatting command. (For example I use prettier in my Javascript and Typescript projects.) You want to make sure that everyone working on the project runs the formatter, so you use a tool like husky to install a git pre-commit hook. The naive way to write that hook would be to:

  • get a list of staged files
  • run the formatter on those files
  • run git add to stage the results of formatting

The problem with that solution is it forces you to commit entire files. At worst this will lead to contributors to unwittingly committing changes. At best it disrupts workflow for contributors who use git add -p.

git-format-staged tackles this problem by running the formatter on the staged version of the file. Staging changes to a file actually produces a new file that exists in the git object database. git-format-staged uses some git plumbing commands to send content from that file to your formatter. The command replaces file content in the git index. The process bypasses the working tree, so any unstaged changes are ignored by the formatter, and remain unstaged.

After formatting a staged file git-format-staged computes a patch which it attempts to apply to the working tree file to keep the working tree in sync with staged changes. If patching fails you will see a warning message. The version of the file that is committed will be formatted properly - the warning just means that working tree copy of the file has been left unformatted. The patch step can be disabled with the --no-update-working-tree option.

How to install

Requires Python version 3 or 2.7.

Install as a development dependency in a project that uses npm packages:

$ npm install --save-dev git-format-staged

Or install globally:

$ npm install --global git-format-staged

If you do not use npm you can copy the git-format-staged script from this repository and place it in your executable path. The script is MIT-licensed - so you can check the script into version control in your own open source project if you wish.

How to use

For detailed information run:

$ git-format-staged --help

The command expects a shell command to run a formatter, and one or more file patterns to identify which files should be formatted. For example:

$ git-format-staged --formatter 'prettier --stdin-filepath "{}"' 'src/*.js'

That will format all files under src/ and its subdirectories using prettier. The file pattern is tested against staged files using Python's fnmatch function: each * will match nested directories in addition to file names.

The formatter command must read file content from stdin, and output formatted content to stdout.

Note that the syntax of the fnmatch glob match is a is a bit different from normal shell globbing. So if you need to match multiple patterns, you should pass multiple arguments with different patterns, and they will be grouped. So instead of e.g. 'src/**/*.{js,jsx,ts}', you would use:

$ git-format-staged --formatter 'prettier --stdin-filepath "{}"' 'src/*.js' 'src/*.jsx' 'src/*.ts'

Files can be excluded by prefixing a pattern with !. For example:

$ git-format-staged --formatter 'prettier --stdin-filepath "{}"' '*.js' '!flow-typed/*'

Patterns are evaluated from left-to-right: if a file matches multiple patterns the right-most pattern determines whether the file is included or excluded.

git-format-staged never operates on files that are excluded from version control. So it is not necessary to explicitly exclude stuff like node_modules/.

The formatter command may include a placeholder, {}, which will be replaced with the path of the file that is being formatted. This is useful if your formatter needs to know the file extension to determine how to format or to lint each file. For example:

$ git-format-staged -f 'prettier --stdin-filepath "{}"' '*.js' '*.css'

Do not attempt to read or write to {} in your formatter command! The placeholder exists only for referencing the file name and path.

Check staged changes with a linter without formatting

Perhaps you do not want to reformat files automatically; but you do want to prevent files from being committed if they do not conform to style rules. You can use git-format-staged with the --no-write option, and supply a lint command instead of a format command. Here is an example using ESLint:

$ git-format-staged --no-write -f 'eslint --stdin --stdin-filename "{}" >&2' 'src/*.js'

If this command is run in a pre-commit hook, and the lint command fails the commit will be aborted and error messages will be displayed. The lint command must read file content via stdin. Anything that the lint command outputs to stdout will be ignored. In the example above eslint is given the --stdin option to tell it to read content from stdin instead of reading files from disk, and messages from eslint are redirected to stderr (using the >&2 notation) so that you can see them.

Set up a pre-commit hook with Husky

Follow these steps to automatically format all Javascript files on commit in a project that uses npm.

Install git-format-staged, husky, and a formatter (I use prettier):

$ npm install --save-dev git-format-staged husky prettier

Add a prepare script to install husky when running npm install:

$ npm set-script prepare "husky install"
$ npm run prepare

Add the pre-commit hook:

$ npx husky add .husky/pre-commit "git-format-staged --formatter 'prettier --stdin-filepath \"{}\"' '*.js' '*.ts'"
$ git add .husky/pre-commit

Once again note that the formatter command and the '*.js' and '*.ts' patterns are quoted!

That's it! Whenever a file is changed as a result of formatting on commit you will see a message in the output from git commit.

Comparisons to similar utilities

There are other tools that will format or lint staged files. What distinguishes git-format-staged is that when a file has both staged and unstaged changes git-format-staged ignores the unstaged changes; and it leaves unstaged changes unstaged when applying formatting.

Some linters (such as precise-commits) have an option to restrict linting to certain lines or character ranges in files, which is one way to ignore unstaged changes while linting. The author is not aware of a utility other than git-format-staged that can apply any arbitrary linter so that it ignores unstaged changes.

Some other formatting utilities (such as pre-commit) use a different strategy to keep unstaged changes unstaged:

  1. stash unstaged changes
  2. apply the formatter to working tree files
  3. stage any resulting changes
  4. reapply stashed changes to the working tree.

The problem is that you may get a conflict where stashed changes cannot be automatically merged after formatting has been applied. In those cases the user has to do some manual fixing to retrieve unstaged changes. As far as the author is aware git-format-staged is the only utility that applies a formatter without touching working tree files, and then merges formatting changes to the working tree. The advantage of merging formatting changes into unstaged changes (as opposed to merging unstaged changes into formatting changes) is that git-format-staged is non-lossy: if there are conflicts between unstaged changes and formatting the unstaged changes win, and are kept in the working tree, while staged/committed files are formatted properly.

Another advantage of git-format-staged is that it has no dependencies beyond Python and git, and can be dropped into any programming language ecosystem.

Some more comparisons:

  • lint-staged lints and formats staged files. At the time of this writing it does not have an official strategy for ignoring unstaged changes when linting, or for keeping unstaged changes unstaged when formatting. But lint-staged does provide powerful configuration options around which files should be linted or formatted, what should happen before and after linting, and so on.
  • pretty-quick formats staged files with prettier. By default pretty-quick will abort the commit if files are partially staged to allow the user to decide how to re-stage changes from formatting. The result is more manual effort compared to git-format-staged.
  • the one-liner git diff --diff-filter=d --cached | grep '^[+-]' | grep -Ev '^(--- a/|\+\+\+ b/)' | LINT_COMMAND (described here) extracts changed hunks and feeds them to a linter. But linting will fail if the linter requires the entire file for context. For example a linter might report errors if it cannot see import lines.

More Repositories

1

jslint.vim

VIM plugin and command line tool for running JSLint <http://jslint.com/>. This is project is no longer under active development. See Readme for details.
JavaScript
462
star
2

safety-lens

Type-safe, functional lens library
JavaScript
73
star
3

zaml

Fast yaml serialization in pure Ruby
Ruby
55
star
4

bitable

An implementation of a distributed hash table in Javascript, using WebRTC to connect peers. This is a work-in-progress and is not yet ready to use!
JavaScript
34
star
5

LambdaCalculus

Scala implementation of a lambda calculus interpreter
Scala
28
star
6

dot-vim

This is my vim configuration.
Lua
16
star
7

sunshine

JavaScript application framework, based on React
JavaScript
13
star
8

dot-xmonad

This is my xmonad configuration.
Haskell
10
star
9

comparing-go-and-rust

This is companion code for the post http://sitr.us/2017/02/21/changes-i-would-make-to-go.html
Rust
10
star
10

config_files

My config files for *nix environments. Clone this repo in your home directory for instant configuration.
Vim Script
9
star
11

estimate_tracking_for_lighthouse

A Greasemonkey script that provides time estimate totals for open tickets on milestone pages in Lighthouse
JavaScript
9
star
12

buscatcher

Location-aware wep app displays arrival times for nearby bus lines. This is alpha software: the software and interfaces will be subject to dramatic changes
JavaScript
8
star
13

restful_captcha

A standalone server that provides CAPTCHA images and that verifies answers to CAPTCHA challenges. The main repo is now at http://github.com/copious/restful_captcha
Ruby
8
star
14

flow-cookbook-hacker-news

Example script to demonstrate type-checking API data with Flow
JavaScript
7
star
15

tagging_for_nautilus

A set of Nautilus scripts that add functions for tagging files using Tracker
6
star
16

redux-instant

Minimize boilerplate in your Redux app by inferring type-safe action creators from your reducer
TypeScript
5
star
17

rhinautheros

OAuth as a replacement for cookie authentication in web apps
JavaScript
5
star
18

github_beacon

Better Lighthouse integration for Github
Ruby
5
star
19

cluster.js

Cluster analysis implemented in JavaScript. This software is pre-alpha quality and is under heavy development.
JavaScript
5
star
20

no_creeper_griefing

Datapack for Minecraft Java edition that prevents creeper explosions from destroying blocks
mcfunction
5
star
21

openid_authentication_for_hobo

Extends Hobo's user model and authentication support to use OpenID for authentication instead of passwords.
Ruby
4
star
22

rust_graphql_server_demo

Rust
4
star
23

dot-files

This is the base configuration for my workstations.
Perl
3
star
24

redstone-designer

A little project for learning about game programming with Bevy
Rust
3
star
25

hacker-news-ts

Example to demonstrate type-checking API data with TypeScript
TypeScript
3
star
26

flow-cookbook-react

Demonstration applying Flow typechecking to a React app
JavaScript
3
star
27

graphql-starter

Example of a GraphQL client & server with end-to-end type safety using graphql-codegen
TypeScript
3
star
28

party_mix_for_rdio

Adds an "Add 25 random songs to Queue" button on the Rdio Collection view.
JavaScript
2
star
29

sendxmpp.rb

Workalike of sendxmpp implemented in Ruby with xmpp4r
2
star
30

game

my first javascript came written using Crafty
JavaScript
2
star
31

openid-example

Sample app demonstrating OpenID authentication in Node.js using Passport and Express
JavaScript
2
star
32

cloudrcs

A revision control system implemented in Ruby and based on darcs
Ruby
2
star
33

etiquette.js

Code reuse in JavaScript inspired by Clojure protocols and Haskell type classes
JavaScript
2
star
34

locutoria

experimental email client - work in progress
Haskell
2
star
35

jsonp_proxy

Adds JSONP support to any website by proxying requests and modifying the response.
Ruby
2
star
36

couch_history

Save your shell history with CouchDB. Based on Shell Sink. This is alpha software and is under heavy development.
2
star
37

monopl4r

Get the most out of your Monopoly money with cutting edge online banking.
2
star
38

projecteuler

My solutions to the problems at http://projecteuler.net/
Ruby
2
star
39

calagator.jive

Jive app that provides integration with Calagator. This is a work-in-progress - it is not yet functional.
JavaScript
1
star
40

xaos

Prototypal inheritance pattern for JavaScript
JavaScript
1
star
41

aichallenge

my work-in-progress solution to the 2011 challenge at aichallenge.org
Scala
1
star
42

sitr.us

My personal blog
JavaScript
1
star
43

hallettj.github.com

sitr.us - Jesse Hallett's personal blog
HTML
1
star
44

industrial-beans

Data packs for vanilla Minecraft that add in-world crafting, and other tweaks for automation
Makefile
1
star
45

dot-zsh

This is my vim configuration.
Shell
1
star
46

pdxjs_vote_system

1
star
47

home.nix

My personal dot files / Home Manager config
Lua
1
star