mirror of
https://github.com/AdaCore/git-hooks.git
synced 2026-02-12 12:43:11 -08:00
This lifts a limitation where non-fast-forward updates could only be allowed for references whose name start with 'refs/heads/' (the standard namespace for branches). As it happens, GCC is using branch names in a different namespace. So this commit changes this config option to match the entire reference name, rather than just the branch name. There is a break in compatibility for the repositories already using this configuration, but this change takes care of detecting this situation and provide an informative message explaining the likely situation. Change-Id: I330d15ccef448e815c94a620f008275f1e1914db TN: T209-002
124 lines
5.1 KiB
Python
Executable File
124 lines
5.1 KiB
Python
Executable File
#! /usr/bin/env python
|
|
"""A module to handle the fast-forward policies...
|
|
|
|
This module can also be called as a script, and returns non-zero if
|
|
it detects that a non-fast-forward update being attempted on a branch
|
|
where it is not allowed. When such an error is detected, an informative
|
|
error message should be printed on stderr.
|
|
|
|
Usage: fast_forward.py REF_NAME OLD_REV NEW_REV
|
|
|
|
The arguments REF_NAME, OLD_REV and NEW_REV are identical
|
|
to the arguments used when git calls the "update" hook.
|
|
|
|
"""
|
|
|
|
import re
|
|
import sys
|
|
|
|
from config import git_config
|
|
from errors import InvalidUpdate
|
|
from git import git
|
|
from utils import warn
|
|
|
|
# A list of regular expressions that match the references where
|
|
# it will always be OK to do a non-fast-forward update (aka
|
|
# a "forced update").
|
|
FORCED_UPDATE_OK_REFS = ("refs/heads/topic/.*",)
|
|
|
|
# The error message shown to the user when rejecting a non-fast-forward
|
|
# update.
|
|
NON_FAST_FORWARD_ERROR_MESSAGE = """\
|
|
Non-fast-forward updates are not allowed for this reference.
|
|
Please rebase your changes on top of the latest HEAD,
|
|
and then try pushing again."""
|
|
|
|
# A warning added at the end of the NON_FAST_FORWARD_ERROR_MESSAGE
|
|
# when we believe the user is trying to push a non-fast-forward update
|
|
# to a branch that looks like it should be allowed, and yet isn't,
|
|
# because this repository's hooks.non-fast-forward configuration
|
|
# appears to be following the previous (now defunct) semantics.
|
|
|
|
OLD_STYLE_CONFIG_WARNING = """\
|
|
Note: It looks like the hooks.non-fast-forward configuration
|
|
for your repository is set to only match the name of the branch
|
|
being updated (e.g. "master"), which is how this configuration
|
|
option was originally interpreted. However, the semantics of
|
|
this option has since been changed and its values must now match
|
|
the reference name (e.g. "refs/heads/master"). If you believe
|
|
this non-fast-forward update should be allowed on this branch,
|
|
contact your repository adminstrator to review the repository's
|
|
hooks.non-fast-forward option configuration."""
|
|
|
|
|
|
def check_fast_forward(ref_name, old_rev, new_rev):
|
|
"""Raise InvalidUpdate if the update violates the fast-forward policy.
|
|
|
|
PARAMETERS
|
|
ref_name: The name of the reference being update (Eg:
|
|
refs/heads/master).
|
|
old_rev: The commit SHA1 of the reference before the update.
|
|
new_rev: The new commit SHA1 that the reference will point to
|
|
if the update is accepted.
|
|
"""
|
|
# Non-fast-foward updates can be characterized by the fact that
|
|
# there is at least one commit that is accessible from the old
|
|
# revision which would no longer be accessible from the new revision.
|
|
if git.rev_list("%s..%s" % (new_rev, old_rev)) == "":
|
|
# This is a fast-forward update.
|
|
return
|
|
|
|
# Non-fast-forward update. See if this is one of the references
|
|
# where such an update is allowed.
|
|
ok_refs = git_config('hooks.allow-non-fast-forward')
|
|
|
|
for ok_ref_re in ok_refs + FORCED_UPDATE_OK_REFS:
|
|
if re.match(ok_ref_re, ref_name) is not None:
|
|
# This is one of the branches where a non-fast-forward update
|
|
# is allowed. Allow the update, but print a warning for
|
|
# the user, just to make sure he is completely aware of
|
|
# the changes that just took place.
|
|
warn("!!! WARNING: This is *NOT* a fast-forward update.")
|
|
warn("!!! WARNING: You may have removed some important commits.")
|
|
return
|
|
|
|
# This non-fast-forward update is not allowed.
|
|
err_msg = NON_FAST_FORWARD_ERROR_MESSAGE
|
|
|
|
# In the previous version of these hooks, the allow-non-fast-forward
|
|
# config was assuming that all such updates would be on references
|
|
# whose name starts with 'refs/heads/'. This is no longer the case.
|
|
# For repositories using this configuration option with the old
|
|
# semantics, non-fast-forward updates will now start getting rejected.
|
|
#
|
|
# To help users facing this situation understand what's going on,
|
|
# see if this non-fast-forward update would have been accepted
|
|
# when interpreting the config option the old way; if yes, then
|
|
# we are probably in a situation where it's the config rather than
|
|
# the update that's a problem. Add some additional information
|
|
# to the error message in order to help him understand what's
|
|
# is likely happening.
|
|
if ref_name.startswith('refs/heads/'):
|
|
for ok_ref_re in ['refs/heads/' + branch.strip()
|
|
for branch in ok_refs]:
|
|
if re.match(ok_ref_re, ref_name) is not None:
|
|
err_msg += '\n\n' + OLD_STYLE_CONFIG_WARNING
|
|
break
|
|
|
|
raise InvalidUpdate(*err_msg.splitlines())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# First, retrieve the command-line arguments.
|
|
if len(sys.argv) != 4:
|
|
warn("Error(%s): Invalid usage, wrong number of arguments (%d)"
|
|
% (sys.argv[0], len(sys.argv)))
|
|
sys.exit(1)
|
|
try:
|
|
check_fast_forward(sys.argv[1], sys.argv[2], sys.argv[3])
|
|
except InvalidUpdate, E:
|
|
# The update was rejected. Print the rejection reason, and
|
|
# exit with a nonzero status.
|
|
warn(*E)
|
|
sys.exit(1)
|