{"componentChunkName":"component---src-templates-post-jsx","path":"/blog/012-git-hooked/","result":{"data":{"markdownRemark":{"html":"<p>There are many ways to ensure code quality, often in some form of linting and formatting tools alongside a CI tool (TravisCI, Jenkins, Bitbucket pipelines, etc). Something small like a missing semi-colon or inconsistent spacing may not change how your code runs in development, but once you minify for production it could cause errors.</p>\n<p>So what if you want to ensure your code passes <em>before</em> you push and blast out that CI build error on Slack, without having to remember to run your linting or code formatting tools manually?</p>\n<p>Enter: Git hooks.</p>\n<p>Git hooks are scripts that are run based on triggers that get fired at various points in your Git workflow, most commonly before or after committing, pushing, or receiving code. Since this is Javascript January, we’ll stick to the theme, but you can use these same patterns for any language or project you may be working on. This post assumes that you know about linting, especially in Javascript land. If not, check out <a href=\"https://eslint.org/\">ESLint</a> and <a href=\"https://prettier.io/\">Prettier</a>, my favorite linter and formatter.</p>\n<p>If you’re already a Git hook whiz, skip ahead to check out <a href=\"#side-stepping-git-hooks-altogether\">Husky</a> to learn about setting up hooks through your project's <code class=\"language-text\">package.json</code>.</p>\n<h2 id=\"how-to-use-a-git-hook\"><a href=\"#how-to-use-a-git-hook\" aria-label=\"how to use a git hook permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>How to Use a Git Hook</h2>\n<p>Your git repository is automatically initialized with some example hooks. They live in the root of your repository inside the <code class=\"language-text\">.git/hooks</code> directory and are named based on the type of trigger you want to run the script on. Your script file can be any executable, though we’ll use shell scripts for our examples.</p>\n<p>Here’s an example of the default git hooks:</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">.git/hooks\n├── applypatch-msg.sample\n├── commit-msg.sample\n├── post-checkout\n├── post-update.sample\n├── pre-applypatch.sample\n├── pre-commit.sample\n├── pre-push.sample\n├── pre-rebase.sample\n├── pre-receive.sample\n├── prepare-commit-msg.sample\n└── update.sample</code></pre></div>\n<p>To use a git hook, simply create the script and make sure it’s executable (<code class=\"language-text\">chmod +x &lt;filename&gt;</code>). If you’re using one of the sample files, make sure to remove the <code class=\"language-text\">.sample</code> extension.</p>\n<p><em>Pro-tip:</em> If you want to run the same script on different triggers, simply create the first trigger script and symlink the duplicates.</p>\n<h2 id=\"your-first-git-hooks\"><a href=\"#your-first-git-hooks\" aria-label=\"your first git hooks permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Your First Git Hooks</h2>\n<p>The following two examples are a couple of my go-to hooks that any Javascript project I’m working on has. Simply add these to your <code class=\"language-text\">.git/hooks</code> directory in the appropriate file and make sure the scripts are executable.</p>\n<h3 id=\"lint-and-format-your-code-before-committing\"><a href=\"#lint-and-format-your-code-before-committing\" aria-label=\"lint and format your code before committing permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Lint and Format Your Code before committing</h3>\n<p>This hook runs before you commit your code and will only run on your staged Javascript files. It runs Prettier for code formatting then runs ESLint to check for any linting errors. If it passes, your commit will go through. Otherwise, it will alert you and stop the commit process so you can make changes.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">#!/bin/sh\n# pre-commit\n\nSTAGED_JS_FILES=$(git diff --cached --name-only --diff-filter=ACM &quot;*.js&quot; &quot;*.jsx&quot; | tr &#39;\\n&#39; &#39; &#39;)\n[ -z &quot;$STAGED_JS_FILES&quot; ]\n\n# Git pre commit hook. ---------------------------------------------------------\n# Runs Prettier on staged .js files\necho &quot;** Formatting Javascript files...&quot;\n# Prettify all staged .js files\necho &quot;$STAGED_JS_FILES&quot; | xargs ./node_modules/.bin/prettier --write\n# Add back the modified/prettified files to staging\necho &quot;$STAGED_JS_FILES&quot; | xargs git add\n\n\n# Git pre commit hook. ---------------------------------------------------------\n# Runs ESLint on staged .js or .jsx files\n# STAGED_JS_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep &quot;.js\\{0undefined1\\}$&quot;)\nRED=&#39;\\033[1;31m&#39;\nGREEN=&#39;\\033[0;32m&#39;\nNC=&#39;\\033[0m&#39;\nPASS=true\n\nif [[ &quot;$STAGED_JS_FILES&quot; = &quot;&quot; ]]; then\n  echo &quot;\\n** No Javascript files are currently staged.&quot;\n  exit 0\nfi\n\necho &quot;\\n** Linting Javascript files...&quot;\nfor FILE in $STAGED_JS_FILES\ndo\n  ./node_modules/.bin/eslint &quot;$FILE&quot;\n\n  if [[ &quot;$?&quot; != 0 ]]; then\n    PASS=false\n  fi\ndone\n\nif ! $PASS; then\n  echo &quot;\\n${RED}COMMIT FAILED:${NC}\\n\\tYour commit contains files that should pass ESLint but do not. Please fix the ESLint errors and try again.\\n\\tTo override this validationundefined use --no-verify&quot;\n  exit 1\nelse\n  echo &quot;\\n${GREEN}COMMIT SUCCEEDED${NC}&quot;\nfi\n\nexit $?</code></pre></div>\n<h3 id=\"checking-dependencies-when-checking-out\"><a href=\"#checking-dependencies-when-checking-out\" aria-label=\"checking dependencies when checking out permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Checking Dependencies When Checking Out</h3>\n<p>This hook runs after you checkout a new branch or commit. It checks to see if your <code class=\"language-text\">package.json</code> has changed in order to check if any dependencies differ. This works great if you’re working on a feature branch that has new dependencies and you need to switch around. It looks for changes in the whole file and doesn’t account for differences in things like version numbers, so keep that in mind.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">#!/bin/bash\n# post-checkout\n\n# Git post checkout hook. ------------------------------------------------------\n# Reminds you to update requirementsundefined if necessary\n\nPREVIOUS_HEAD=$1\nNEW_HEAD=$2\nBRANCH_SWITCH=$3\n\nif [ $BRANCH_SWITCH == &quot;1&quot; -a $PREVIOUS_HEAD != $NEW_HEAD ]; then\n    # Start from the repository root.\n    cd ./$(git rev-parse --show-cdup)\n\n    # Check if requirements have been updated - npm/yarn\n    REQUIREMENTS=`git diff $PREVIOUS_HEAD $NEW_HEAD --name-status | grep &quot;package.json&quot;`\n    if [ $? -eq &quot;0&quot; ]; then\n        echo &quot;\\nThe requirements for this project may have changed. Update dependencies by running:&quot;\n        echo &quot;  yarn install&quot;\n    fi\nfi</code></pre></div>\n<h2 id=\"managing-git-hooks\"><a href=\"#managing-git-hooks\" aria-label=\"managing git hooks permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Managing Git Hooks</h2>\n<h3 id=\"manual-hook-wrangling\"><a href=\"#manual-hook-wrangling\" aria-label=\"manual hook wrangling permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Manual Hook Wrangling</h3>\n<p>Manually managing your git hooks is one of easiest way to get started, but makes sharing hooks a bit more difficult (don't worry, we'll talk about tips for this later).\nGit hooks can be easily managed locally by using a <code class=\"language-text\">.git-template</code> directory. You can store all of your “default” scripts inside of your template directory and they will automatically be added to your repository each time you run a <code class=\"language-text\">git init</code>, as long as an existing hook with the same name doesn’t already exist. You can run <code class=\"language-text\">git init</code> either when starting a project or in an existing project to grab any new Git hooks.</p>\n<h3 id=\"sharing-git-hooks\"><a href=\"#sharing-git-hooks\" aria-label=\"sharing git hooks permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Sharing Git Hooks</h3>\n<p>The traditional way of managing Git hooks has some drawbacks, particularly that since they’re stored in your <code class=\"language-text\">.git</code> directory by default. This means they aren’t tracked in your repository.</p>\n<p>You could set up a system where your hooks are tracked outside of your <code class=\"language-text\">.git</code> directory, say in a <code class=\"language-text\">.hooks</code> directory, then set up a script to symlink your files into your <code class=\"language-text\">.git/hooks</code> directory (<a href=\"https://github.com/emilyemorehouse/git-hooked/tree/githook-dir-symlinked\">check out a working example here</a>). Alternatively, you can configure Git to simply pull hooks from a different directory altogether by running <code class=\"language-text\">git config core.hooksPath .hooks</code> (<a href=\"https://github.com/emilyemorehouse/git-hooked/tree/githook-dir-config\">check out another example here</a>).</p>\n<h3 id=\"side-stepping-git-hooks-altogether\"><a href=\"#side-stepping-git-hooks-altogether\" aria-label=\"side stepping git hooks altogether permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Side-Stepping Git Hooks Altogether</h3>\n<p>If you’ve gone through the trouble of setting up linting or code formatting, you’ve probably already set up a script to run with something like <code class=\"language-text\">npm run lint</code>. With the help of a tool called <a href=\"https://github.com/typicode/husky/tree/master\">Husky</a>, we can leverage our scripts that we’ve already set up by using them as Git hooks.</p>\n<p>First, install Husky:\n<code class=\"language-text\">npm install husky --save-dev</code></p>\n<p>Then add your existing scripts as triggers in your <code class=\"language-text\">package.json</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"lint\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"eslint --ext js --ext jsx src\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"format\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"prettier 'src/**/*.{js,jsx}' --write\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"precommit\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"npm run lint &amp;&amp; npm run format\"</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  ...\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>That’s it! Husky will manage all of your Git hooks for you and will run your lint and format command as a pre-commit hook.</p>\n<p>For more information, check out the <a href=\"https://git-scm.com/book/gr/v2/Customizing-Git-Git-Hooks\">Git documentation on Git hooks</a> and check out the <a href=\"https://github.com/emilyemorehouse/git-hooked\">demo repository</a>.</p>","timeToRead":6,"excerpt":"There are many ways to ensure code quality, often in some form of linting and formatting tools alongside a CI tool (TravisCI, Jenkins…","frontmatter":{"title":"Git Hooked","cover":"https://images.unsplash.com/photo-1495482237874-2abe1e1c92dc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80","date":"2018-01-27","category":"tutorial","tags":["git","git hooks"]},"fields":{"slug":"/blog/012-git-hooked/","date":"2018-01-27T07:00:00.000Z"}}},"pageContext":{"slug":"/blog/012-git-hooked/"}}}