Skip to content

Instantly share code, notes, and snippets.

@mattscilipoti
Forked from pixelhandler/pre-push.sh
Last active January 3, 2016 06:39
Show Gist options
  • Save mattscilipoti/8424018 to your computer and use it in GitHub Desktop.
Save mattscilipoti/8424018 to your computer and use it in GitHub Desktop.
pre-push script: Protects some branches from destructive actions.
#!/usr/bin/env ruby
# NOTE! this is a work in progress. This is not tested or used regularly.
# Ensures we do not call destructive commands on protected branches.
#
# Called by "git push" after it has checked the remote status,
# but before anything has been pushed.
#
# If this script exits with a non-zero status nothing will be pushed.
#
# Steps to install, from the root directory of your repo...
# 1. Copy the file into your repo at `.git/hooks/pre-push`
# 2. Set executable permissions, run `chmod +x .git/hooks/pre-push`
# 3. Or, use `rake hooks:pre_push` to install
#
# Try a force push to master, you should get a message `*** [Policy] never force push...`
#
# The commands below will not be allowed...
# `git push --force origin master`
# `git push --delete origin master`
# `git push origin :master`
#
# Nor will a force push while on the master branch be allowed...
# `git co master`
# `git push --force origin`
#
# Requires git 1.8.2 or newer
#
# Git 1.8.2 release notes cover the new pre-push hook:
# <https://github.com/git/git/blob/master/Documentation/RelNotes/1.8.2.txt>
#
# See Sample pre-push script:
# <https://github.com/git/git/blob/87c86dd14abe8db7d00b0df5661ef8cf147a72a3/templates/hooks--pre-push.sample>
#
# Also pulled ideas from:
# * http://blog.bigbinary.com/2013/09/19/do-not-allow-force-pusht-to-master.html
# * https://mug.im/how-to-prevent-yourself-from-force-pushing-to-master/
class ProtectedBranchesHandler
def handle
if pushing_to_protected_branch? && command_is_destructive?
reject
else
exit 0
end
end
def protected_branches
%w[master production]
end
private
def command_is_delete?(command)
command =~ /--delete/
end
def command_is_destructive?
command_is_forced_push?(current_command) || command_is_delete?(current_command)
end
def command_is_forced_push?(command)
command =~ /--force|-f|--pfush/
end
def current_branch
result = %x{git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,'}
if result =~ /^failure/
exit_as_failure result
else
result
end
end
def current_command
$(ps -ocommand= -p $PPID)
end
def exit_as_failure(messages)
messages = Array(messages)
unless messages.empty?
puts "*"*40
[messages].flatten.each do |message|
puts message
end
puts "*"*40
end
exit 1
end
def pushing_to_protected_branch?
protected_branches.include? current_branch
end
def reject
messages = ["Your attempt to run a destructive command on '#{current_branch}' has been rejected."]
messages << "If you still want to FORCE PUSH then you need to ignore the pre_push git hook by executing following command."
messages << "git push master --force --no-verify"
exit_as_failure messages
end
end
ProtectedBranchesHandler.new.handle
@dideler
Copy link

dideler commented Jan 23, 2014

This script has a sh extension and shebang line, but it looks like Ruby code to me.

Is this script still a work in progress?

@mattscilipoti
Copy link
Author

Yes, it is a work in progress. It is not tested, nor used regularly. Thanks for the insights. I have edited the file, to warn others.

@tejasbubane
Copy link

If anyone is looking for Ruby solution, this might be helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment