summaryrefslogtreecommitdiff
path: root/bind/bind9/util/branchsync.sh
blob: 0a3855384df25a1aab6aefb2b7c17fdf6914109f (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/bin/sh
#
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.

# branchsync:
# pull in changes from a main BIND 9 release branch to a subscription
# branch (e.g., from v9_9 to v9_9_sub).  This excludes commits from the
# 'tinderbox' user (copyright updates and doc regeneration) because those
# will be taken care of in the subscription branch itself.
#
# Most of the time, changes in the main branch will cherry-pick cleanly
# into the subscription branch. When one fails, we reset to the last
# commit that went cleanly, and send mail -- or, if running in interactive
# mode, we stop and allow the user to resolve conflicts.
#
# NOTE: This does not push changes to the repository; currently that's up
# to the caller.
#
# Usage:
# branchsync.sh [ -i ] [ -c ]
#   -i: interactive mode (don't reset and send mail)
#   -c: continue (conflicts are resolved; edit message and commit)

restore_files () {
    # restore the copyrights and api files
    git checkout HEAD -- util/copyrights lib/*/api
    # restore the generated documentation
    git checkout HEAD -- doc/arm/*.html doc/arm/Bv9ARM.pdf
    git checkout HEAD -- bin/*/*.html bin/*/*.[0-9]
    # don't update the EXCLUDED file
    if [ -f EXCLUDED ]; then
        git checkout HEAD -- EXCLUDED
    fi
}

savehash () {
    cat <<EOF > $1.new
SOURCEBRANCH=$2
LASTHASH=$3
EOF
    mv -f $1.new $1
    git add branchsync.dat
}

thisbranch () {
    git branch | grep '^\*' | cut -b3-
}

docommit () {
    # skip the commit if we're only updating branchsync.dat
    status=`git status -suno | grep branchsync.dat`
    if [ -z "$status" ]; then
        return
    fi

    # pull in the log message from the cherry-picked commit
    git log -1 --pretty=format:%s%n%b $2 > orig_commit_msg.tmp
    author=`git log -1 --pretty=format:"%aN <%aE>" $2`
    firstline=`head -1 orig_commit_msg.tmp | sed 's/^\[[a-z0-9_]*\] //'`
    tail -n +2 orig_commit_msg.tmp > remainder.tmp
    firstline="[$BRANCH] $firstline"
    echo $firstline > commit_msg.tmp
    cat remainder.tmp >> commit_msg.tmp
    echo "pulled from $1 by script." >> commit_msg.tmp
    echo "hash: $2" >> commit_msg.tmp
    msg=`cat commit_msg.tmp`
    rm -f orig_commit_msg.tmp commit_msg.tmp remainder.tmp

    # commit
    git commit --no-verify --no-edit --author="$author" -m "$msg" || exit 1
}

BRANCH=`thisbranch`

if [ ! -f branchsync.dat ]; then
    echo "$0: branchsync data file not found"
    exit 0
fi

. branchsync.dat

# check arguments
interactive=
continuing=
case $1 in
    '-i') interactive=yes
          ;;
    '-c') docommit $SOURCEBRANCH $LASTHASH
          interactive=yes
          continuing=yes
          ;;
    *)    if [ $# -ne 0 ]; then
             echo "Usage: $0 [ -i ] [ -c ]" 1>&2
             exit 1
          fi
          ;;
esac

if [ -z "$continuing" ]; then
    status=`git status -suno`
    if [ -n "$status" ]; then
         echo "Work tree is not clean. Clean up, or run with -c:"
         echo "$status"
         exit 1
    fi

    # make sure both branches are synced to the source repository
    git pull origin $BRANCH > /dev/null 2>&1
    git checkout -f $SOURCEBRANCH > /dev/null 2>&1
    git pull origin $SOURCEBRANCH > /dev/null 2>&1
    git checkout -f $BRANCH > /dev/null 2>&1
fi

# loop through commits looking for ones that should be cherry-picked
git log $SOURCEBRANCH --first-parent --reverse --format='%H %aN' $LASTHASH..$SOURCEBRANCH | \
  awk '$0 !~ /Tinderbox/ {print $1}' | {
    while read hash; do
        mainline=
        if [ `git cat-file -p ${hash} | grep '^parent [0-9a-f][0-9a-f]*$' | wc -l` -gt 1 ]; then
            mainline="-m 1 "
        fi
        if git cherry-pick ${mainline} -xn ${hash}; then
            # cherry-pick was clean
            # restore the files that we don't want updated automatically
            restore_files

            # note which hash we're merging
            savehash branchsync.dat $SOURCEBRANCH $hash

            # fix the commit message, and commit
            docommit $SOURCEBRANCH $hash

            # move on to the next commit
            continue
        elif [ -n "$interactive" ]; then
            # interactive mode -- wait for user to fix things
            # first restore the files that we don't want updated automatically
            restore_files

            # note which hash we're merging
            savehash branchsync.dat $SOURCEBRANCH $hash
        else
            # noninteractive mode
            # reset everything
            git reset --hard

            # build mail message
            subject="Branch sync to $BRANCH failed"
            cat << EOF > /tmp/branchmsg.$$
Attempt to cherry pick ${hash}
to $BRANCH failed.

Commit message of change was:
`git log -1 --pretty=format:%s%n%b ${hash}`
EOF

            # send mail
            cat /tmp/branchmsg.$$ | mail -s "$subject" bind-changes@isc.org
            rm /tmp/branchmsg.$$
        fi

        break
    done
}