Original article Use Git Hooks to Automate Necessary but Annoying Tasks
Some tasks really need to be run after pulling the code or checking out to a branch such as updating dependencies or migrate database, re-index ctags
to improve the experience better when developing applications. Besides, developers often forget to do it, leading to errors. To identify the recent problem there is a set of git
hooks with dotfiles that automate those tasks.
Git Hooks
Git has a commonly used feature hooks . You can think of a hook as an event that activates before and after some state during modification control. The hooks to pay attention include:
prepare-commit-msg
– Fire before commit message.pre-commit
– Fire before agit commit
.post-commit
– Fire after agit commit
.post-checkout
– Fire after changing branches.post-merge
– Fire after merging branches.pre-push
– Fire before code pushed to a remote.
Expanded Hooks
The Dotfiles convention for extension is a place to customize the hooks in the {pre,post}-$EVENT
in the {~/.git_template.local/hooks}
. At this point, whatever we add to the hook files will automatically execute the tasks that we often forget.
Tasks are often forgotten
Forget running re-index
ctags
!
The following lines of code will re-index for you after each git
command
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token comment"># ~/.git_template.local/hooks/ctags</span> <span class="token comment">#!/bin/sh</span> <span class="token builtin class-name">set</span> -e <span class="token assign-left variable"><span class="token environment constant">PATH</span></span> <span class="token operator">=</span> <span class="token string">"/usr/local/bin: <span class="token environment constant">$PATH</span> "</span> <span class="token assign-left variable">dir</span> <span class="token operator">=</span> <span class="token string">" <span class="token variable"><span class="token variable">$(</span> <span class="token function">git</span> rev-parse --git-dir <span class="token variable">)</span></span> "</span> <span class="token builtin class-name">trap</span> <span class="token string">'rm -f " <span class="token variable">$dir</span> / <span class="token variable">$$</span> .tags"'</span> EXIT <span class="token function">git</span> ls-files <span class="token operator">|</span> <span class="token string">" <span class="token variable">${CTAGS <span class="token operator">:-</span> ctags}</span> "</span> --tag-relative <span class="token operator">=</span> yes -L - -f <span class="token string">" <span class="token variable">$dir</span> / <span class="token variable">$$</span> .tags"</span> --languages <span class="token operator">=</span> -javascript,sql <span class="token function">mv</span> <span class="token string">" <span class="token variable">$dir</span> / <span class="token variable">$$</span> .tags"</span> <span class="token string">" <span class="token variable">$dir</span> /tags"</span> |
Forget running
bundle install
after checkingout to another branch
Automatically install new gems
1 2 3 4 5 6 7 8 9 10 | <span class="token comment"># ~/.git_template.local/hooks/post-checkout</span> <span class="token comment"># use `hookup` gem if it's installed</span> <span class="token keyword">if</span> <span class="token builtin class-name">command</span> -v hookup <span class="token operator">></span> /dev/null <span class="token punctuation">;</span> <span class="token keyword">then</span> hookup post-checkout <span class="token string">" <span class="token variable"><a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a></span> "</span> <span class="token keyword">else</span> <span class="token comment"># otherwise, do it yourself</span> <span class="token punctuation">[</span> -f Gemfile <span class="token punctuation">]</span> <span class="token operator">&&</span> bundle <span class="token function">install</span> <span class="token operator">></span> /dev/null <span class="token operator">&</span> <span class="token keyword">fi</span> |
Forget running the pending migrations
Automatically run migrations
1 2 3 4 5 6 7 8 9 10 | <span class="token comment"># ~/.git_template.local/hooks/post-checkout</span> <span class="token comment"># use `hookup` gem if it's installed</span> <span class="token keyword">if</span> <span class="token builtin class-name">command</span> -v hookup <span class="token operator">></span> /dev/null <span class="token punctuation">;</span> <span class="token keyword">then</span> hookup post-checkout <span class="token string">" <span class="token variable"><a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a></span> "</span> <span class="token keyword">else</span> <span class="token comment"># otherwise, do it yourself</span> <span class="token punctuation">[</span> -f db/schema.rb <span class="token punctuation">]</span> <span class="token operator">&&</span> bin/rake db:migrate <span class="token operator">></span> /dev/null <span class="token operator">&</span> <span class="token keyword">fi</span> |
Write API document with fdoc but forget to generate pages
Automatically generate HTML docs
1 2 3 4 | <span class="token comment"># ~/.git_template.local/hooks/post-checkout</span> bin/fdoc convert ./spec/fixtures --output <span class="token operator">=</span> ./html <span class="token operator">></span> /dev/null <span class="token operator">&</span> |
Forget a Go commitment to the standard code format for the files
Run go fmt
before commit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token comment"># ~/.git_template.local/hooks/pre-commit</span> <span class="token assign-left variable">gofiles</span> <span class="token operator">=</span> <span class="token variable"><span class="token variable">$(</span> <span class="token function">git</span> <span class="token function">diff</span> --cached --name-only --diff-filter <span class="token operator">=</span> ACM <span class="token operator">|</span> <span class="token function">grep</span> <span class="token string">'.go$'</span> <span class="token variable">)</span></span> <span class="token punctuation">[</span> -z <span class="token string">" <span class="token variable">$gofiles</span> "</span> <span class="token punctuation">]</span> <span class="token operator">&&</span> <span class="token builtin class-name">exit</span> <span class="token number">0</span> <span class="token keyword">function</span> <span class="token function-name function">checkfmt</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token assign-left variable">unformatted</span> <span class="token operator">=</span> <span class="token variable"><span class="token variable">$(</span> gofmt -l $gofiles <span class="token variable">)</span></span> <span class="token punctuation">[</span> -z <span class="token string">" <span class="token variable">$unformatted</span> "</span> <span class="token punctuation">]</span> <span class="token operator">&&</span> <span class="token builtin class-name">return</span> <span class="token number">0</span> <span class="token builtin class-name">echo</span> <span class="token operator">></span> <span class="token file-descriptor important">&2</span> <span class="token string">"Go files must be formatted with gofmt. Please run:"</span> <span class="token keyword">for</span> <span class="token for-or-select variable">fn</span> <span class="token keyword">in</span> <span class="token variable">$unformatted</span> <span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token operator">></span> <span class="token file-descriptor important">&2</span> <span class="token string">" gofmt -w <span class="token environment constant">$PWD</span> / <span class="token variable">$fn</span> "</span> <span class="token keyword">done</span> <span class="token builtin class-name">return</span> <span class="token number">1</span> <span class="token punctuation">}</span> checkfmt <span class="token operator">||</span> <span class="token assign-left variable">fail</span> <span class="token operator">=</span> yes <span class="token punctuation">[</span> -z <span class="token string">" <span class="token variable">$fail</span> "</span> <span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token builtin class-name">exit</span> <span class="token number">1</span> <span class="token builtin class-name">exit</span> <span class="token number">0</span> |
In there you can learn more about the Hookup gem for Rails including the Bundler hook versions and migrations.
We can focus on what’s more important by simplifying and automating small parts of the development process.
Thanks for reading my writing.