Get tests passing again.
[git-central.git] / server / update-stable
1 #!/bin/bash
2
3 #
4 # This enforces stable moving in approved ways.
5 #
6 # Specifically:
7 #
8 # * stable must move by only 1 commit-per-push
9 # * the stable commit must have 2 and only 2 parents
10 #   * The first parent must be the previous stable commit
11 #   * The second parent is the tip of the candidate branch being released
12 # * the stable commit must have the same contents as the candidate tip
13 #   * Any merge conflicts should have been resolved in the candidate tip
14 #     by pulling stable into the candidate and having qa/tests done--pulling
15 #     candidate into stable should then apply cleanly
16 #
17 # For DAG aesthetics, we prefer stable only moving in the approved way,
18 # which is via empty (no change) merge commits. The rationale is that
19 # in the DAG we want a simple, one-commit move from each release to the
20 # next.
21 #
22 # We started out with:
23 #
24 # * -- A                stable
25 #  \    \
26 #   \    * -- * -- B    topic1
27 #    \           /
28 #     * -- * -- *       topic2
29 #
30 # And then publishing stable was a matter of fast-forwarding
31 # from A to B.
32 #
33 # In a complicated (non-rebased) DAG, this becomes hard to follow,
34 # so want we want instead is:
35 #
36 # * -- A ----------- C  stable
37 #  \    \           /
38 #   \    * -- * -- B    topic1
39 #    \           /
40 #     * -- * -- *       topic2
41 #
42 # Where commit C lists as it's first parent the prior stable
43 # commit and as it's second parent the release candidate. No
44 # other parents are allowed (e.g. no octopus merges here, which
45 # would insinuate qa didn't happen on the merged result).
46 #
47 # Also, we want to enforce that C does not actually introduce
48 # any diffs to the files between B and C--as they would be changes
49 # that QA does not see.
50 #
51
52 . $(dirname $0)/functions
53
54 refname="$1"
55 oldrev="$2"
56 newrev="$3"
57
58 case "$refname" in
59         refs/heads/*)
60                 short_refname=${refname##refs/heads/}
61                 ;;
62         *)
63                 exit 0
64                 ;;
65 esac
66
67 set_change_type
68 if [ "$change_type" == "delete" ] ; then
69         exit 0
70 fi
71
72 # create/delete is okay
73 if [ "$change_type" != "update" ] ; then
74         exit 0
75 fi
76
77 if [ "$short_refname" == "stable" ] ; then
78         # Stable enforcement
79
80         # read backwards:
81         # - all commits from old..new
82         # - unless they were already pointed to by a branch
83         # = all new commits on stable
84         count=$(git rev-parse --not --branches | git rev-list --stdin $oldrev..$newrev | wc -l)
85         if [ "$count" -ne "1" ] ; then
86                 display_error_message "Moving stable must entail a single commit"
87                 exit 1
88         fi
89
90         number_of_parents=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | wc -l)
91         if [ "$number_of_parents" -ne "2" ] ; then
92                 display_error_message "Moving stable must entail a merge commit"
93                 exit 1
94         fi
95
96         first_parent=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | head --lines=1)
97         if [ "$first_parent" != "$oldrev" ] ; then
98                 display_error_message "Moving stable must have the previous stable as the first parent"
99                 exit 1
100         fi
101
102         second_parent=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | tail --lines=1)
103         changed_lines=$(git diff $second_parent..$newrev | wc -l)
104         if [ "$changed_lines" -ne "0" ] ; then
105                 display_error_message "Moving stable must not result in any changes from $second_parent"
106                 exit 1
107         fi
108 fi
109