3 # This script can be installed as a git update hook.
5 # It can also be installed as a gitolite 'hooklet' in the
6 # hooks/common/update.secondary.d/ directory.
8 # NOTE: this script runs under the same assumptions as the 'update' hook, so
9 # the starting directory must be maintained and arguments must be passed on.
16 # Only enforce policy on the master branch
17 exit 0 if $refname != 'refs/heads/master'
19 puts "Enforcing Policies... \n(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
21 $regex = /\[ref: (\d+)\]/
23 $broken_commit_message = /Please enter a commit message to explain why this merge is necessary/
24 $wrong_way_merge_master = /Merge( remote-tracking)? branch '([^\/]+\/)?master' into/
25 $merge_master = /Merge branch '[^']+'((?! into)| into master)/
26 $pull_merge = /Merge branch 'master' of /
27 $refs_or_closes_or_no_issue = /(refs #|closes #|no issue #)/i
29 # enforced custom commit message format
30 def check_message_format
31 all_revs = `git rev-list --first-parent #{$oldrev}..#{$newrev}`.split("\n")
32 merge_revs = `git rev-list --first-parent --min-parents=2 #{$oldrev}..#{$newrev}`.split("\n")
33 # single_revs = `git rev-list --first-parent --max-parents=1 #{$oldrev}..#{$newrev}`.split("\n")
36 merge_revs.each do |rev|
37 message = `git cat-file commit #{rev} | sed '1,/^$/d'`
38 if $wrong_way_merge_master.match(message)
39 puts "\n[POLICY] This appears to be a merge from master into a feature\n"
40 puts "\nbranch. Commits to master must merge from the feature\n"
41 puts "\nbranch into master.\n\n"
42 puts "\n******************************************************************\n"
43 puts "\nOffending commit: #{rev}\n\n"
44 puts "\nOffending commit message:\n\n"
46 puts "\n******************************************************************\n"
49 elsif $pull_merge.match(message)
50 puts "\n[POLICY] This appears to be a git pull merge of remote master into local\n"
51 puts "\nmaster. In order to maintain a linear first-parent history of master,\n"
52 puts "\nplease reset your branch and remerge or rebase using the latest master.\n"
53 puts "\n******************************************************************\n"
54 puts "\nOffending commit: #{rev}\n\n"
55 puts "\nOffending commit message:\n\n"
57 puts "\n******************************************************************\n"
60 elsif not $merge_master.match(message) and not
61 puts "\n[POLICY] This does not appear to be a merge of a feature\n"
62 puts "\nbranch into master. Merges must follow the format\n"
63 puts "\n\"Merge branch 'feature-branch'\".\n"
64 puts "\n******************************************************************\n"
65 puts "\nOffending commit: #{rev}\n\n"
66 puts "\nOffending commit message:\n\n"
68 puts "\n******************************************************************\n"
74 all_revs.each do |rev|
75 message = `git cat-file commit #{rev} | sed '1,/^$/d'`
76 if $broken_commit_message.match(message)
77 puts "\n[POLICY] Rejected broken commit message for including boilerplate\n"
78 puts "\ninstruction text.\n\n"
79 puts "\n******************************************************************\n"
80 puts "\nOffending commit: #{rev}\n\n"
81 puts "\nOffending commit message:\n\n"
83 puts "\n******************************************************************\n"
88 if not $refs_or_closes_or_no_issue.match(message)
89 puts "\n[POLICY] All commits to master must include an issue using \"refs #\" or\n"
90 puts "\n\"closes #\", or specify \"no issue #\"\n\n"
91 puts "\n******************************************************************\n"
92 puts "\nOffending commit: #{rev}\n\n"
93 puts "\nOffending commit message:\n\n"
95 puts "\n******************************************************************\n"