diff --git a/util/git-replay-merge.sh b/util/git-replay-merge.sh deleted file mode 100755 index 2bc0a839ae..0000000000 --- a/util/git-replay-merge.sh +++ /dev/null @@ -1,265 +0,0 @@ -#!/bin/bash - -# Copyright (C) Internet Systems Consortium, Inc. ("ISC") -# -# SPDX-License-Identifier: MPL-2.0 -# -# 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. - -set -e - -SELF="$(basename $0)" -SELF="${SELF/-/ }" - -STATE_FILE=".git/REPLAY_MERGE" -DONT_TAG=${DONT_TAG:=false} -DONT_PUSH=${DONT_PUSH:=false} -DONT_ACCEPT=${DONT_ACCEPT:=false} - -die() { - for MESSAGE in "$@"; do - echo -e "${MESSAGE}" >&2 - done - exit 1 -} - -die_with_usage() { - die "Usage:" \ - "" \ - "${SELF} [options] " \ - "${SELF} --continue" \ - "${SELF} --abort" \ - "" \ - "options:" \ - " --no-push" \ - " --no-tag" \ - "" -} - -die_with_continue_instructions() { - die "" \ - "Replay interrupted. Conflicts need to be fixed manually." \ - "When done, run \"${SELF} --continue\"." \ - "Use \"${SELF} --abort\" to abort the replay." -} - -die_before_push() { - die "" \ - "Replay finished locally. Now check the result in ${REPLAY_BRANCH}." \ - "When done, run \"${SELF} --continue\" to push and create MR in gitlab." \ - "Use \"${SELF} --abort\" to abort the replay." -} - -die_if_wrong_dir() { - if [[ ! -d ".git" ]]; then - die "You need to run this command from the toplevel of the working tree." - fi -} - -die_if_not_in_progress() { - die_if_wrong_dir - if [[ ! -f "${STATE_FILE}" ]]; then - die "No replay-merge in progress?" - fi -} - -die_if_in_progress() { - die_if_wrong_dir - if [[ -f "${STATE_FILE}" ]]; then - die "Another replay-merge in progress. Use --continue or --abort." - fi -} - -die_if_local_behind_target() { - TARGET_REF_HEAD="$(git rev-list --max-count=1 "${TARGET_REF}")" - if [[ "$(git merge-base "${TARGET_REF}" "${TARGET_BRANCH}")" != "${TARGET_REF_HEAD}" ]]; then - die "Local branch ${TARGET_BRANCH} is behind ${TARGET_REF}, cannot merge into it." \ - "Update or remove the local branch, then run \"${SELF} --continue\"." \ - "Use \"${SELF} --abort\" to abort the replay." - fi -} - -branch_exists() { - ESCAPED_BRANCH_NAME=${1//\//\\\/} - BRANCH_REGEX="/^(remotes\/)?${ESCAPED_BRANCH_NAME}$/" - if [[ -n "$(git branch -a | awk "\$NF ~ ${BRANCH_REGEX} {print \$NF}")" ]]; then - return 0 - else - return 1 - fi -} - -go() { - # Process parameters. - SOURCE_COMMIT="$1" - TARGET_REMOTE="$2" - TARGET_BRANCH="$3" - TARGET_REF="${TARGET_REMOTE}/${TARGET_BRANCH}" - # Establish the range of commits comprising the source branch. - REPLAY_COMMIT_RANGE="$( - git show --format="%P" "${SOURCE_COMMIT}" 2>&1 \ - | sed -n "1s/\([0-9a-f]\{40\}\) \([0-9a-f]\{40\}\)/\1..\2/p;" - )" - if [[ -z "${REPLAY_COMMIT_RANGE}" ]]; then - die "${SOURCE_COMMIT} is not a valid merge commit ID." - fi - # Extract the name of the source branch. - SOURCE_BRANCH="$( - git log --max-count=1 --format="%B" "${SOURCE_COMMIT}" \ - | sed -n "s/^Merge branch '\([^'][^']*\).*/\1/p;" \ - | head -n 1 - )" - if [[ -z "${SOURCE_BRANCH}" ]]; then - die "Unable to extract source branch name from ${SOURCE_COMMIT}." - fi - # Ensure the target ref is valid. - if ! branch_exists "${TARGET_REF}"; then - die "${TARGET_REF} is not a valid replay target." - fi - # Abort if a local branch with the name about to be used for replaying - # the merge already exists. - REPLAY_BRANCH="${SOURCE_BRANCH}-${TARGET_BRANCH}" - if branch_exists "${REPLAY_BRANCH}"; then - die "Local branch with name ${REPLAY_BRANCH} already exists." \ - "Cannot use it for replaying a merge." - fi - # Get the name of the currently checked out branch so that it can be - # checked out again once the replay is finished. - CHECKED_OUT_BRANCH="$(git branch | awk "\$1 == \"*\" {print \$2}")" - # Store state in case it needs to be restored later. - cat <<-EOF >"${STATE_FILE}" - CHECKED_OUT_BRANCH="${CHECKED_OUT_BRANCH}" - SOURCE_COMMIT="${SOURCE_COMMIT}" - SOURCE_BRANCH="${SOURCE_BRANCH}" - REPLAY_BRANCH="${REPLAY_BRANCH}" - TARGET_REMOTE="${TARGET_REMOTE}" - TARGET_BRANCH="${TARGET_BRANCH}" - TARGET_REF="${TARGET_REF}" - EOF - # Announce the plan. - echo "Attempting to replay ${REPLAY_COMMIT_RANGE} on top of ${TARGET_REF} in ${REPLAY_BRANCH}..." - # Switch to the replay branch. - git checkout -t -b "${REPLAY_BRANCH}" "${TARGET_REF}" >/dev/null - # Try replaying the branch. If there is any conflict, the command will - # fail, which means we need to bail and let the user fix the current - # cherry-pick manually, expecting "git replay-merge --continue" to be - # used afterwards. If there is no conflict, just proceed with what - # --continue would do. - if ! git cherry-pick -x "${REPLAY_COMMIT_RANGE}"; then - die_with_continue_instructions - fi - resume -} - -resume() { - # If cherry-picking has not yet been completed, resume it. If it - # fails, bail. If if succeeds, we can proceed with merging. - if [[ -f ".git/sequencer/todo" ]]; then - if ! git cherry-pick --continue; then - die_with_continue_instructions - fi - fi - - if $DONT_PUSH; then - die_before_push - fi - - if $DONT_ACCEPT; then - AUTO_MERGE="" - else - AUTO_MERGE="merge_request.merge_when_pipeline_succeeds" - fi - - TITLE="" - LABEL_VERSION="" - LABEL_BACKPORT="" - - if ! $DONT_TAG && [[ $TARGET_BRANCH == bind-9.[0-9][0-9] ]]; then - version="${TARGET_BRANCH#bind-}" - - TITLE="$(git show --format=%b ${SOURCE_COMMIT} | head -n 1)" - TITLE="merge_request.title=[${version}] ${TITLE}" - - LABEL_VERSION="merge_request.label=v${version}" - LABEL_BACKPORT="merge_request.label=Backport" - fi - - git push -u ${TARGET_REMOTE} \ - -o merge_request.create \ - -o merge_request.remove_source_branch \ - -o "merge_request.target=${TARGET_BRANCH}" \ - ${AUTO_MERGE:+-o} "${AUTO_MERGE}" \ - ${TITLE:+-o} "${TITLE}" \ - ${LABEL_VERSION:+-o} "${LABEL_VERSION}" \ - ${LABEL_BACKPORT:+-o} "${LABEL_BACKPORT}" \ - "${REPLAY_BRANCH}:${REPLAY_BRANCH}" - - cleanup - exit 0 -} - -cleanup() { - # Restore working copy state from before the replay was started, - # ignoring any potential errors to prevent "set -e" from interfering. - { - git merge --abort - git cherry-pick --abort - git checkout "${CHECKED_OUT_BRANCH}" - } &>/dev/null || true - rm -f "${STATE_FILE}" -} - -cd $(git rev-parse --show-toplevel) - -next_action="go" -args=3 -while [[ $# -ge 1 ]]; do - case "$1" in - "--no-push") - DONT_PUSH=true - ;; - "--push") - DONT_PUSH=false - ;; - "--no-tag") - DONT_TAG=true - ;; - "--tag") - DONT_TAG=false - ;; - "--abort") - die_if_not_in_progress - source "${STATE_FILE}" - next_action="cleanup" - args=0 - shift - break - ;; - "--continue") - die_if_not_in_progress - source "${STATE_FILE}" - next_action="resume" - args=0 - shift - break - ;; - --*) - die_with_usage - ;; - *) - break - ;; - esac - shift -done - -if [[ $# -ne $args ]]; then - die_with_usage -fi - -$next_action "$@"