#!/bin/sh # # This enforces stable/candidate/topic patterns. # # Specifically: # # * stable must be moved 1 merge commit at a time (see below) # * candidates/topics are frozen once merged into stable # * topics are frozen once merged into candidates # # For DAG aesthetics, we prefer stable only moving in the approved way, # which is via empty (no change) merge commits. The rationale is that # in the DAG we want a simple, one-commit move from each release to the # next. # # We started out with: # # * -- A stable # \ \ # \ * -- * -- B topic1 # \ / # * -- * -- * topic2 # # And then publishing stable was a matter of fast-forwarding # from A to B. # # In a complicated (non-rebased) DAG, this becomes hard to follow, # so want we want instead is: # # * -- A ----------- C stable # \ \ / # \ * -- * -- B topic1 # \ / # * -- * -- * topic2 # # Where commit C lists as it's first parent the prior stable # commit and as it's second parent the release candidate. No # other parents are allowed (e.g. no octopus merges here, which # would insinuate qa didn't happen on the merged result). # # Also, we want to enforce that C does not actually introduce # any diffs to the files between B and C--as they would be changes # that QA does not see. # . $(dirname $0)/functions refname="$1" oldrev="$2" newrev="$3" case "$refname" in refs/heads/*) short_refname=${refname##refs/heads/} ;; *) exit 0 ;; esac set_change_type if [ "$change_type" == "delete" ] ; then exit 0 fi # create/delete is okay if [ "$change_type" != "update" ] ; then exit 0 fi if [ "$short_refname" == "stable" ] ; then # Stable enforcement # read backwards: # - all commits from old..new # - unless they were already pointed to by a branch # = all new commits on stable count=$(git rev-parse --not --branches | git rev-list --stdin $oldrev..$newrev | wc -l) if [ "$count" -ne "1" ] ; then display_error_message "Moving stable must entail a single commit" exit 1 fi number_of_parents=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | wc -l) if [ "$number_of_parents" -ne "2" ] ; then display_error_message "Moving stable must entail a merge commit" exit 1 fi first_parent=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | head --lines=1) if [ "$first_parent" != "$oldrev" ] ; then display_error_message "Moving stable must have the previous stable as the first parent" exit 1 fi second_parent=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | tail --lines=1) changed_lines=$(git diff $second_parent..$newrev | wc -l) if [ "$changed_lines" -ne "0" ] ; then display_error_message "Moving stable must not result in any changes from $second_parent" exit 1 fi else # Check if candidate/topic is already in stable git branch --contains "$oldrev" | grep --quiet stable if [ $? -eq 0 ] ; then # Okay, it's in stable...but was it previously just a 0-commit initial banch? git rev-list --first-parent stable | grep --quiet "$oldrev" if [ $? -ne 0 ] ; then display_error_message "$short_refname has been merged into stable" exit 1 fi fi # For now candidates can mix amongst each other so early exit case "$refname" in refs/heads/candidate*) exit 0 ;; *) ;; esac # Check if topic is already in candidates candidate=$(git branch --contains "$oldrev" | grep -oP "candidate.*" --max-count=1) if [ $? -eq 0 ] ; then # Okay, it's in stable...but was it previously just a 0-commit initial banch? git rev-list --first-parent stable | grep --quiet "$oldrev" if [ $? -ne 0 ] ; then display_error_message "$short_refname has been merged into $candidate" exit 1 fi fi fi