aboutsummaryrefslogtreecommitdiff
path: root/scripts/git/pre-push.git-hook
blob: 0f016df59249616a68976aeb41714470a32e4ab2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env bash

# git pre-push hook script to:
# 0) Call the pre-commit hook, if it is available
# 1) prevent "fixup!" and "squash!" commit from ending up in main, release-*
#    or maint-*
# 2) Disallow pushing branches other than main, release-*
#    and maint-* to origin (e.g. gitweb.torproject.org)
#
# To install this script, copy it into .git/hooks/pre-push path in your
# local copy of git repository. Make sure it has permission to execute.
# Furthermore, make sure that TOR_UPSTREAM_REMOTE_NAME environment
# variable is set to local name of git remote that corresponds to upstream
# repository on e.g. git.torproject.org.
#
# The following sample script was used as starting point:
# https://github.com/git/git/blob/master/templates/hooks--pre-push.sample

# Are you adding a new check to the git hooks?
#  - Common checks belong in the pre-commit hook
#  - Push-only checks belong in the pre-push hook

echo "Running pre-push hook"

z40=0000000000000000000000000000000000000000

upstream_name=${TOR_UPSTREAM_REMOTE_NAME:-"upstream"}

# The working directory
workdir=$(git rev-parse --show-toplevel)
# The .git directory
# If $workdir is a worktree, then $gitdir is not $workdir/.git
gitdir=$(git rev-parse --git-dir)

cd "$workdir" || exit 1

remote="$1"
remote_name=$(git remote --verbose | grep "$2" | awk '{print $1}' | head -n 1)


ref_is_upstream_branch() {
    if [ "$1" == "refs/heads/main" ] ||
        [[ "$1" == refs/heads/release-* ]] ||
        [[ "$1" == refs/heads/maint-* ]]; then
        return 1
    fi
}

# shellcheck disable=SC2034
while read -r local_ref local_sha remote_ref remote_sha
do
    if [ "$local_sha" = $z40 ]; then
        # Handle delete
        :
    else
        if [ "$remote_sha" = $z40 ]; then
            # New branch, examine commits not in main
            range="main...$local_sha"
        else
            # Update to existing branch, examine new commits
            range="$remote_sha..$local_sha"
        fi

        # Call the pre-commit hook for the common checks, if it is executable
        pre_commit=${gitdir}/hooks/pre-commit
        if [ -x "$pre_commit" ]; then
            # Only check the files newly modified in this branch
            CHECK_FILTER="git diff --name-only --diff-filter=ACMR $range"
            # Use the appropriate owned tor source list to filter the changed
            # files
            # This is the layout in 0.3.5
            # Keep these lists consistent:
            #   - OWNED_TOR_C_FILES in Makefile.am
            #   - CHECK_FILES in pre-commit.git-hook and pre-push.git-hook
            #   - try_parse in check_cocci_parse.sh
            CHECK_FILES="$($CHECK_FILTER \
                                   src/lib/*/*.[ch] \
                                   src/core/*/*.[ch] \
                                   src/feature/*/*.[ch] \
                                   src/app/*/*.[ch] \
                                   src/test/*.[ch] \
                                   src/test/*/*.[ch] \
                                   src/tools/*.[ch] \
                          )"

            export TOR_EXTRA_PRE_COMMIT_CHECKS=1
            # We want word splitting here, because file names are space
            # separated
            # shellcheck disable=SC2086
            if ! "$pre_commit" $CHECK_FILES ; then
                exit 1
            fi
        fi

        if [[ "$remote_name" != "$upstream_name" ]]; then
            echo "Not pushing to upstream - refraining from further checks"
            continue
        fi

        if (ref_is_upstream_branch "$local_ref" == 0 ||
                ref_is_upstream_branch "$remote_ref"  == 0) &&
            [ "$local_ref" != "$remote_ref" ]; then
            if [ "$remote" == "origin" ]; then
                echo >&2 "Not pushing: $local_ref to $remote_ref"
                echo >&2 "If you really want to push this, use --no-verify."
                exit 1
            else
                continue
            fi
        fi

        # Check for fixup! commit
        commit=$(git rev-list -n 1 --grep '^fixup!' "$range")
        if [ -n "$commit" ]; then
            echo >&2 "Found fixup! commit in $local_ref, not pushing"
            echo >&2 "If you really want to push this, use --no-verify."
            exit 1
        fi

        # Check for squash! commit
        commit=$(git rev-list -n 1 --grep '^squash!' "$range")
        if [ -n "$commit" ]; then
            echo >&2 "Found squash! commit in $local_ref, not pushing"
            echo >&2 "If you really want to push this, use --no-verify."
            exit 1
        fi
    fi
done

exit 0