go.etcd.io/etcd@v3.3.27+incompatible/hack/patch/cherrypick.sh (about) 1 #!/usr/bin/env bash 2 3 # Based on github.com/kubernetes/kubernetes/blob/v1.8.2/hack/cherry_pick_pull.sh 4 5 # Checkout a PR from GitHub. (Yes, this is sitting in a Git tree. How 6 # meta.) Assumes you care about pulls from remote "upstream" and 7 # checks thems out to a branch named: 8 # automated-cherry-pick-of-<pr>-<target branch>-<timestamp> 9 10 set -o errexit 11 set -o nounset 12 set -o pipefail 13 14 declare -r ETCD_ROOT="$(dirname "${BASH_SOURCE}")/../.." 15 cd "${ETCD_ROOT}" 16 17 declare -r STARTINGBRANCH=$(git symbolic-ref --short HEAD) 18 declare -r REBASEMAGIC="${ETCD_ROOT}/.git/rebase-apply" 19 DRY_RUN=${DRY_RUN:-""} 20 REGENERATE_DOCS=${REGENERATE_DOCS:-""} 21 UPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream} 22 FORK_REMOTE=${FORK_REMOTE:-origin} 23 24 if [[ -z ${GITHUB_USER:-} ]]; then 25 echo "Please export GITHUB_USER=<your-user> (or GH organization, if that's where your fork lives)" 26 exit 1 27 fi 28 29 if ! which hub > /dev/null; then 30 echo "Can't find 'hub' tool in PATH, please install from https://github.com/github/hub" 31 exit 1 32 fi 33 34 if [[ "$#" -lt 2 ]]; then 35 echo "${0} <remote branch> <pr-number>...: cherry pick one or more <pr> onto <remote branch> and leave instructions for proposing pull request" 36 echo 37 echo " Checks out <remote branch> and handles the cherry-pick of <pr> (possibly multiple) for you." 38 echo " Examples:" 39 echo " $0 upstream/release-3.14 12345 # Cherry-picks PR 12345 onto upstream/release-3.14 and proposes that as a PR." 40 echo " $0 upstream/release-3.14 12345 56789 # Cherry-picks PR 12345, then 56789 and proposes the combination as a single PR." 41 echo 42 echo " Set the DRY_RUN environment var to skip git push and creating PR." 43 echo " This is useful for creating patches to a release branch without making a PR." 44 echo " When DRY_RUN is set the script will leave you in a branch containing the commits you cherry-picked." 45 echo 46 echo " Set the REGENERATE_DOCS environment var to regenerate documentation for the target branch after picking the specified commits." 47 echo " This is useful when picking commits containing changes to API documentation." 48 echo 49 echo " Set UPSTREAM_REMOTE (default: upstream) and FORK_REMOTE (default: origin)" 50 echo " To override the default remote names to what you have locally." 51 exit 2 52 fi 53 54 if git_status=$(git status --porcelain --untracked=no 2>/dev/null) && [[ -n "${git_status}" ]]; then 55 echo "!!! Dirty tree. Clean up and try again." 56 exit 1 57 fi 58 59 if [[ -e "${REBASEMAGIC}" ]]; then 60 echo "!!! 'git rebase' or 'git am' in progress. Clean up and try again." 61 exit 1 62 fi 63 64 declare -r BRANCH="$1" 65 shift 1 66 declare -r PULLS=( "$@" ) 67 68 function join { local IFS="$1"; shift; echo "$*"; } 69 declare -r PULLDASH=$(join - "${PULLS[@]/#/#}") # Generates something like "#12345-#56789" 70 declare -r PULLSUBJ=$(join " " "${PULLS[@]/#/#}") # Generates something like "#12345 #56789" 71 72 echo "+++ Updating remotes..." 73 git remote update "${UPSTREAM_REMOTE}" "${FORK_REMOTE}" 74 75 if ! git log -n1 --format=%H "${BRANCH}" >/dev/null 2>&1; then 76 echo "!!! '${BRANCH}' not found. The second argument should be something like ${UPSTREAM_REMOTE}/release-0.21." 77 echo " (In particular, it needs to be a valid, existing remote branch that I can 'git checkout'.)" 78 exit 1 79 fi 80 81 declare -r NEWBRANCHREQ="automated-cherry-pick-of-${PULLDASH}" # "Required" portion for tools. 82 declare -r NEWBRANCH="$(echo "${NEWBRANCHREQ}-${BRANCH}" | sed 's/\//-/g')" 83 declare -r NEWBRANCHUNIQ="${NEWBRANCH}-$(date +%s)" 84 echo "+++ Creating local branch ${NEWBRANCHUNIQ}" 85 86 cleanbranch="" 87 prtext="" 88 gitamcleanup=false 89 function return_to_kansas { 90 if [[ "${gitamcleanup}" == "true" ]]; then 91 echo 92 echo "+++ Aborting in-progress git am." 93 git am --abort >/dev/null 2>&1 || true 94 fi 95 96 # return to the starting branch and delete the PR text file 97 if [[ -z "${DRY_RUN}" ]]; then 98 echo 99 echo "+++ Returning you to the ${STARTINGBRANCH} branch and cleaning up." 100 git checkout -f "${STARTINGBRANCH}" >/dev/null 2>&1 || true 101 if [[ -n "${cleanbranch}" ]]; then 102 git branch -D "${cleanbranch}" >/dev/null 2>&1 || true 103 fi 104 if [[ -n "${prtext}" ]]; then 105 rm "${prtext}" 106 fi 107 fi 108 } 109 trap return_to_kansas EXIT 110 111 SUBJECTS=() 112 function make-a-pr() { 113 local rel="$(basename "${BRANCH}")" 114 echo 115 echo "+++ Creating a pull request on GitHub at ${GITHUB_USER}:${NEWBRANCH}" 116 117 # This looks like an unnecessary use of a tmpfile, but it avoids 118 # https://github.com/github/hub/issues/976 Otherwise stdin is stolen 119 # when we shove the heredoc at hub directly, tickling the ioctl 120 # crash. 121 prtext="$(mktemp -t prtext.XXXX)" # cleaned in return_to_kansas 122 cat >"${prtext}" <<EOF 123 Automated cherry pick of ${PULLSUBJ} 124 125 Cherry pick of ${PULLSUBJ} on ${rel}. 126 127 $(printf '%s\n' "${SUBJECTS[@]}") 128 EOF 129 130 hub pull-request -F "${prtext}" -h "${GITHUB_USER}:${NEWBRANCH}" -b "coreos:${rel}" 131 } 132 133 git checkout -b "${NEWBRANCHUNIQ}" "${BRANCH}" 134 cleanbranch="${NEWBRANCHUNIQ}" 135 136 gitamcleanup=true 137 for pull in "${PULLS[@]}"; do 138 echo "+++ Downloading patch to /tmp/${pull}.patch (in case you need to do this again)" 139 curl -o "/tmp/${pull}.patch" -sSL "http://github.com/coreos/etcd/pull/${pull}.patch" 140 echo 141 echo "+++ About to attempt cherry pick of PR. To reattempt:" 142 echo " $ git am -3 /tmp/${pull}.patch" 143 echo 144 git am -3 "/tmp/${pull}.patch" || { 145 conflicts=false 146 while unmerged=$(git status --porcelain | grep ^U) && [[ -n ${unmerged} ]] \ 147 || [[ -e "${REBASEMAGIC}" ]]; do 148 conflicts=true # <-- We should have detected conflicts once 149 echo 150 echo "+++ Conflicts detected:" 151 echo 152 (git status --porcelain | grep ^U) || echo "!!! None. Did you git am --continue?" 153 echo 154 echo "+++ Please resolve the conflicts in another window (and remember to 'git add / git am --continue')" 155 read -p "+++ Proceed (anything but 'y' aborts the cherry-pick)? [y/n] " -r 156 echo 157 if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then 158 echo "Aborting." >&2 159 exit 1 160 fi 161 done 162 163 if [[ "${conflicts}" != "true" ]]; then 164 echo "!!! git am failed, likely because of an in-progress 'git am' or 'git rebase'" 165 exit 1 166 fi 167 } 168 169 # set the subject 170 subject=$(grep -m 1 "^Subject" "/tmp/${pull}.patch" | sed -e 's/Subject: \[PATCH//g' | sed 's/.*] //') 171 SUBJECTS+=("#${pull}: ${subject}") 172 173 # remove the patch file from /tmp 174 rm -f "/tmp/${pull}.patch" 175 done 176 gitamcleanup=false 177 178 # Re-generate docs (if needed) 179 if [[ -n "${REGENERATE_DOCS}" ]]; then 180 echo 181 echo "Regenerating docs..." 182 if ! hack/generate-docs.sh; then 183 echo 184 echo "hack/generate-docs.sh FAILED to complete." 185 exit 1 186 fi 187 fi 188 189 if [[ -n "${DRY_RUN}" ]]; then 190 echo "!!! Skipping git push and PR creation because you set DRY_RUN." 191 echo "To return to the branch you were in when you invoked this script:" 192 echo 193 echo " git checkout ${STARTINGBRANCH}" 194 echo 195 echo "To delete this branch:" 196 echo 197 echo " git branch -D ${NEWBRANCHUNIQ}" 198 exit 0 199 fi 200 201 if git remote -v | grep ^${FORK_REMOTE} | grep etcd/etcd.git; then 202 echo "!!! You have ${FORK_REMOTE} configured as your etcd/etcd.git" 203 echo "This isn't normal. Leaving you with push instructions:" 204 echo 205 echo "+++ First manually push the branch this script created:" 206 echo 207 echo " git push REMOTE ${NEWBRANCHUNIQ}:${NEWBRANCH}" 208 echo 209 echo "where REMOTE is your personal fork (maybe ${UPSTREAM_REMOTE}? Consider swapping those.)." 210 echo "OR consider setting UPSTREAM_REMOTE and FORK_REMOTE to different values." 211 echo 212 make-a-pr 213 cleanbranch="" 214 exit 0 215 fi 216 217 echo 218 echo "+++ I'm about to do the following to push to GitHub (and I'm assuming ${FORK_REMOTE} is your personal fork):" 219 echo 220 echo " git push ${FORK_REMOTE} ${NEWBRANCHUNIQ}:${NEWBRANCH}" 221 echo 222 read -p "+++ Proceed (anything but 'y' aborts the cherry-pick)? [y/n] " -r 223 if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then 224 echo "Aborting." >&2 225 exit 1 226 fi 227 228 git push "${FORK_REMOTE}" -f "${NEWBRANCHUNIQ}:${NEWBRANCH}" 229 make-a-pr