Git Hooks - Atlassian Git Tutorial
Git Hooks - Atlassian Git Tutorial
Conceptual
Overview
All Git hooks are ordinary scripts that Git executes
when certain events occur in the repository. This
makes them very easy to install and configure.
Installing Hooks
Hooks reside in the .git/hooks directory of every
Git repository. Git automatically populates this
directory with example scripts when you initialize a
repository. If you take a look inside .git/hooks ,
you’ll find the following files:
applypatch-msg.sample pre-push.sample
commit-msg.sample pre-rebase.sample
post-update.sample prepare-commit-msg.s
pre-applypatch.sample update.sample
pre-commit.sample
#!/bin/sh
echo "# Please include a useful commit message!
Scripting Languages
The built-in scripts are mostly shell and PERL
scripts, but you can use any scripting language you
like as long as it can be run as an executable. The
shebang line ( #!/bin/sh ) in each script defines
how your file should be interpreted. So, to use a
different language, all you have to do is change it to
the path of your interpreter.
#!/usr/bin/env python
import sys, os
commit_msg_filepath = sys.argv[1]
with open(commit_msg_filepath, 'w') as f:
f.write("# Please include a useful commit m
Scope of Hooks
Hooks are local to any given Git repository, and they
are not copied over to the new repository when you
run git clone . And, since hooks are local, they can
be altered by anybody with access to the repository.
pre-commit
prepare-commit-msg
commit-msg
post-commit
post-checkout
pre-rebase
Search Tutorials
Enter Your Email For Git News
The first 4 hooks let you plug into the entire commit
life cycle, and the final 2 let you perform some extra
Learn Git actions or safety checks for the git checkout and
git rebase commands, respectively.
Beginner All of the pre- hooks let you alter the action that’s
about to take place, while the post- hooks are used
only for notifications.
We’ll also see some useful techniques for parsing
Getting Started hook arguments and requesting information about
the repository using lower-level Git commands.
Collaborating
Pre-Commit
The pre-commit script is executed every time you
run git commit before Git asks the developer for a
Migrating to Git
commit message or generates a commit object. You
can use this hook to inspect the snapshot that is
about to be committed. For example, you may want
Advanced Tips to run some automated tests that make sure the
Advanced Git Tutorials commit doesn’t break any existing functionality.
Merging vs. Rebasing
Resetting, Checking Out, No arguments are passed to the pre-commit script,
and Reverting and exiting with a non-zero status aborts the entire
Advanced Git log commit. Let’s take a look at a simplified (and more
Git Hooks verbose) version of the built-in pre-commit hook.
This script aborts the commit if it finds any
Conceptual Overview
whitespace errors, as defined by the
Local Hooks
git diff-index command (trailing whitespace,
Server-side Hooks
lines with only whitespace, and a space followed by
Summary
a tab inside the initial indent of a line are considered
Refs and the Reflog errors by default).
Git submodules
Git subtree
#!/bin/sh
Large repositories in Git
# Check if this is the initial commit
Git LFS if git rev-parse --verify HEAD >/dev/null 2>&1
then
Git gc echo "pre-commit: About to create a new com
Git prune against=HEAD
else
Git Bash echo "pre-commit: About to create the first c
against=4b825dc642cb6eb9a060e54bf8d69288fbe
How to store dotfiles fi
Git Cherry Pick
# Use git diff-index to check for whitespace er
GitK echo "pre-commit: Testing for whitespace errors.
Git-show if ! git diff-index --check --cached $against
then
echo "pre-commit: Aborting commit due to wh
exit 1
else
echo "pre-commit: No whitespace errors :)"
exit 0
fi
#!/usr/bin/env python
import sys, os, re
from subprocess import check_output
# Collect the parameters
commit_msg_filepath = sys.argv[1]
if len(sys.argv) > 2:
commit_type = sys.argv[2]
else:
commit_type = ''
if len(sys.argv) > 3:
commit_hash = sys.argv[3]
else:
commit_hash = ''
print "prepare-commit-msg: File: %s\nType: %s\nH
# Figure out which branch we're on
branch = check_output(['git', 'symbolic-ref'
print "prepare-commit-msg: On branch '%s'" % br
# Populate the commit message with the issue #,
if branch.startswith('issue-'):
print "prepare-commit-msg: Oh hey, it's an i
result = re.match('issue-(.*)', branch)
issue_number = result.group(1)
with open(commit_msg_filepath, 'r+') as f:
content = f.read()
f.seek(0, 0)
f.write("ISSUE-%s %s" % (issue_number, c
ISSUE-224
# Please enter the commit message for your chan
# with '#' will be ignored, and an empty messag
# On branch issue-224
# Changes to be committed:
# modified: test.txt
#!/usr/bin/env python
import sys, os, re
from subprocess import check_output
# Collect the parameters
commit_msg_filepath = sys.argv[1]
# Figure out which branch we're on
branch = check_output(['git', 'symbolic-ref'
print "commit-msg: On branch '%s'" % branch
# Check the commit message if we're on an issue
if branch.startswith('issue-'):
print "commit-msg: Oh hey, it's an issue bra
result = re.match('issue-(.*)', branch)
issue_number = result.group(1)
required_message = "ISSUE-%s" % issue_numbe
with open(commit_msg_filepath, 'r') as f:
content = f.read()
if not content.startswith(required_messa
print "commit-msg: ERROR! The commi
sys.exit(1)
Post-Commit
The post-commit hook is called immediately after
the commit-msg hook. It can’t change the outcome
of the git commit operation, so it’s used primarily
for notification purposes.
#!/usr/bin/env python
import smtplib
from email.mime.text import MIMEText
from subprocess import check_output
# Get the git log --stat entry of the new commi
log = check_output(['git', 'log', '-1', '--stat
# Create a plaintext email message
msg = MIMEText("Look, I'm actually doing some w
msg['Subject'] = 'Git post-commit hook notificat
msg['From'] = '[email protected]'
msg['To'] = '[email protected]'
# Send the message
SMTP_SERVER = 'smtp.example.com'
SMTP_PORT = 587
session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo()
session.login(msg['From'], 'secretPassword'
session.sendmail(msg['From'], msg['To'], msg.as_
session.quit()
Post-Checkout
The post-checkout hook works a lot like the
post-commit hook, but it’s called whenever you
successfully check out a reference with
git checkout . This is nice for clearing out your
working directory of generated files that would
otherwise cause confusion.
#!/usr/bin/env python
import sys, os, re
from subprocess import check_output
# Collect the parameters
previous_head = sys.argv[1]
new_head = sys.argv[2]
is_branch_checkout = sys.argv[3]
if is_branch_checkout == "0":
print "post-checkout: This is a file checkout
sys.exit(0)
print "post-checkout: Deleting all '.pyc' files i
for root, dirs, files in os.walk('.'):
for filename in files:
ext = os.path.splitext(filename)[1]
if ext == '.pyc':
os.unlink(os.path.join(root, filename
Pre-Rebase
The pre-rebase hook is called before git rebase
changes anything, making it a good place to make
sure something terrible isn’t about to happen.
#!/bin/sh
# Disallow all rebasing
echo "pre-rebase: Rebasing is dangerous. Don't
exit 1
Now, every time you run git rebase , you’ll see this
message:
pre-rebase: Rebasing is dangerous. Don't do it.
The pre-rebase hook refused to rebase.
Server-side Hooks
Server-side hooks work just like local ones, except
they reside in server-side repositories (e.g., a
central repository, or a developer’s public
repository). When attached to the official repository,
some of these can serve as a way to enforce policy
by rejecting certain commits.
pre-receive
update
post-receive
Pre-Receive
The pre-receive hook is executed every time
somebody uses git push to push commits to the
repository. It should always reside in the remote
repository that is the destination of the push, not in
the originating repository.
<old-value> <new-value> <ref-name>
You can see how this hook works using a very basic
pre-receive script that simply reads in the pushed
refs and prints them out.
#!/usr/bin/env python
import sys
import fileinput
# Read in each ref that the user is trying to u
for line in fileinput.input():
print "pre-receive: Trying to push ref: %s"
# Abort the push
# sys.exit(1)
Update
The update hook is called after pre-receive , and it
works much the same way. It’s still called before
anything is actually updated, but it’s called
separately for each ref that was pushed. That
means if the user tries to push 4 branches, update
is executed 4 times. Unlike pre-receive , this hook
doesn’t need to read from standard input. Instead, it
accepts the following 3 arguments:
1. The name of the ref being updated
#!/usr/bin/env python
import sys
branch = sys.argv[1]
old_commit = sys.argv[2]
new_commit = sys.argv[3]
print "Moving '%s' from %s to %s" % (branch, ol
# Abort pushing only this branch
# sys.exit(1)
Post-Receive
The post-receive hook gets called after a
successful push operation, making it a good place
to perform notifications. For many workflows, this is
a better place to trigger notifications than
post-commit because the changes are available on
a public server instead of residing only on the user’s
local machine. Emailing other developers and
triggering a continuous integration system are
common use cases for post-receive .
Summary
In this article, we learned how Git hooks can be
used to alter internal behavior and receive
notifications when certain events occur in a
repository. Hooks are ordinary scripts that reside in
the .git/hooks repository, which makes them very
easy to install and customize.
Next up:
Refs and
the
Reflog
START NEXT TUTORIAL
Powered By