blob: 4f6014093c9880ee2dfca1abd4eaef14dd4b2428 [file] [log] [blame]
Willy Tarreaube9b00f2020-02-05 04:45:18 +01001#!/usr/bin/env bash
Willy Tarreaue77a13a2020-02-04 13:50:36 +01002
3USAGE="Usage: ${0##*/} <last> <commit> [...]"
4START="$PWD"
5LAST=
6UPSTREAM=
7COMMIT=
8BRANCH=
9
10die() {
11 [ "$#" -eq 0 ] || echo "$*" >&2
12 exit 1
13}
14
15err() {
16 echo "$*" >&2
17}
18
19quit() {
20 [ "$#" -eq 0 ] || echo "$*"
21 exit 0
22}
23
24short() {
Willy Tarreau499b2982020-02-06 18:38:19 +010025 git rev-parse --short "$1"
Willy Tarreaue77a13a2020-02-04 13:50:36 +010026}
27
28# returns the latest commit ID in $REPLY. Returns 0 on success, non-zero on
29# failure with $REPLY empty.
30get_last_commit() {
31 REPLY=$(git rev-parse HEAD)
32 test -n "$REPLY"
33}
34
35# returns the name of the current branch (1.8, 1.9, etc) in $REPLY. Returns 0
36# on success, non-zero on failure with $REPLY empty.
37get_branch() {
38 local major subver ext
39 REPLY=$(git describe --tags HEAD --abbrev=0 2>/dev/null)
40 REPLY=${REPLY#v}
41 subver=${REPLY#[0-9]*.[0-9]*[-.]*[0-9].}
42 [ "${subver}" != "${REPLY}" ] || subver=""
43 major=${REPLY%.$subver}
44 ext=${major#*[0-9].*[0-9]}
45 REPLY=${major%${ext}}
46 test -n "$REPLY"
47}
48
49# returns the path to the next "up" remote in $REPLY, and zero on success
50# or non-zero when the last one was reached.
51up() {
52 REPLY=$(git remote -v | awk '/^up\t.*\(fetch\)$/{print $2}')
53 test -n "$REPLY"
54}
55
56# returns the path to the next "down" remote in $REPLY, and zero on success
57# or non-zero when the last one was reached.
58down() {
59 REPLY=$(git remote -v | awk '/^down\t.*\(fetch\)$/{print $2}')
60 test -n "$REPLY"
61}
62
63# verifies that the repository is clean of any pending changes
64check_clean() {
65 test -z "$(git status -s -uno)"
66}
67
68# verifies that HEAD is the master
69check_master() {
Willy Tarreauf9beea52020-02-07 08:26:49 +010070 test "$(git rev-parse --verify -q HEAD 2>&1)" = "$(git rev-parse --verify -q master 2>&1)"
Willy Tarreaue77a13a2020-02-04 13:50:36 +010071}
72
73# tries to switch to the master branch, only if the current one is clean. Dies on failure.
74switch_master() {
75 check_clean || die "$BRANCH: local changes, stopping on commit $COMMIT (upstream $UPSTREAM)"
76 git checkout master >/dev/null 2>&1 || die "$BRANCH: failed to checkout master, stopping on commit $COMMIT (upstream $UPSTREAM)"
77}
78
79# walk up to the first repo
80walk_up() {
81 cd "$START"
82}
83
84# updates the "up" remote repository. Returns non-zero on error.
85update_up() {
86 git remote update up >/dev/null 2>&1
87}
88
89# backports commit "$1" with a signed-off by tag. In case of failure, aborts
90# the change and returns non-zero. Unneeded cherry-picks do return an error
Ilya Shipitsin856aabc2020-04-16 23:51:34 +050091# because we don't want to accidentally backport the latest commit instead of
Willy Tarreaue77a13a2020-02-04 13:50:36 +010092# this one, and we don't know this one's ID.
93backport_commit() {
94 local empty=1
95
96 if ! git cherry-pick -sx "$1"; then
97 [ -n "$(git diff)" -o -n "$(git diff HEAD)" ] || empty=0
98 git cherry-pick --abort
99 return 1
100 fi
101}
102
103[ "$1" != "-h" -a "$1" != "--help" ] || quit "$USAGE"
104[ -n "$1" -a -n "$2" ] || die "$USAGE"
105
106LAST="$1"
107shift
108
109# go back to the root of the repo
110cd $(git rev-parse --show-toplevel)
111START="$PWD"
112
113while [ -n "$1" ]; do
Willy Tarreau499b2982020-02-06 18:38:19 +0100114 UPSTREAM="$(short $1)"
115 [ -n "$UPSTREAM" ] || die "branch $BRANCH: unknown commit ID $1, cannot backport."
116 COMMIT="$UPSTREAM"
Willy Tarreaue77a13a2020-02-04 13:50:36 +0100117 BRANCH="-source-"
118 while :; do
119 if ! down; then
120 err "branch $BRANCH: can't go further, is repository 'down' properly set ?"
121 break
122 fi
123
124 cd "$REPLY" || die "Failed to 'cd' to '$REPLY' from '$PWD', is repository 'down' properly set ?"
125
126 check_clean || die "Local changes in $PWD, stopping before backporting commit $COMMIT (upstream $UPSTREAM)"
127
128 check_master || switch_master || die "Cannot switch to 'master' branch in $PWD, stopping before backporting commit $COMMIT (upstream $UPSTREAM)"
129 get_branch || die "Failed to get branch name in $PWD, stopping before backporting commit $COMMIT (upstream $UPSTREAM)"
130 BRANCH="$REPLY"
131
132 update_up || die "$BRANCH: failed to update repository 'up', stopping before backporting commit $COMMIT (upstream $UPSTREAM)"
133
134 backport_commit "$COMMIT" || die "$BRANCH: failed to backport commit $COMMIT (upstream $UPSTREAM). Leaving repository $PWD intact."
135
136 if [ "$BRANCH" = "$LAST" ]; then
137 # reached the stop point, don't apply further
138 break
139 fi
140
141 get_last_commit || die "$BRANCH: cannot retrieve last commit ID, stopping after backporting commit $COMMIT (upstream $UPSTREAM)"
Willy Tarreau499b2982020-02-06 18:38:19 +0100142 COMMIT="$(short $REPLY)"
Willy Tarreaue77a13a2020-02-04 13:50:36 +0100143 done
144 walk_up || die "Failed to go back to $PWD, stopping *after* backporting upstream $UPSTREAM"
145 shift
146done