diff --git a/tools/tools/git/ghpr/README b/tools/tools/git/ghpr/README new file mode 100644 index 000000000000..e07d56b1e09b --- /dev/null +++ b/tools/tools/git/ghpr/README @@ -0,0 +1,5 @@ +These are totally experimental + +Shared for collaboration on writing them, if you aren't +in touch with Warner about them, you may find it's super +tough sledding to use them. diff --git a/tools/tools/git/ghpr/ghpr-init.sh b/tools/tools/git/ghpr/ghpr-init.sh new file mode 100644 index 000000000000..3b1d65c56fe2 --- /dev/null +++ b/tools/tools/git/ghpr/ghpr-init.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -e + +die() { + echo $* + exit 1 +} + +# Create a fresh branch for the staging tree. +BRANCH=${1:-staging} +base=main + +if [ "$(git config branch.${BRANCH}.opabinia)" = "true" ]; then + echo "Branch ${BRANCH} has already been initialized" + # Bail if the branch already exists +else + if git rev-parse --verify ${BRANCH} > /dev/null 2>&1; then + echo "Branch ${BRANCH} already exists, skipping creation" + else + # Create the branch and tag it as the one we're using for opabinia merging. + git checkout -b ${BRANCH} ${base} || die "Can't create ${BRANCH}" + fi +fi + +git config --add --type bool branch.${BRANCH}.opabinia true || die "Can't annotate" +git config --add branch.${BRANCH}.opabinia.base ${base} || die "Can't annotate" diff --git a/tools/tools/git/ghpr/ghpr-push.sh b/tools/tools/git/ghpr/ghpr-push.sh new file mode 100644 index 000000000000..21cd9b52e41b --- /dev/null +++ b/tools/tools/git/ghpr/ghpr-push.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +set -e + +die() { + echo $* + exit 1 +} + +staging=staging + +# Iteratively try to push all the branches, then push upstream. Repeat until the upstream +# push works... +while true; do + for pr in $(git config --get-all branch.${staging}.opabinia.prs); do + upstream=$(git config --get branch.${staging}.opabinia.${pr}.upstream) + upstream_branch=$(git config --get branch.${staging}.opabinia.${pr}.upstream-branch) + + git push $upstream HEAD:$upstream_branch --force || true # bare git push gives cut and paste line + done + + if ! git push --push-option=confirm-author freebsd HEAD:main; then + git fetch freebsd + git rebase freebsd/main ${stagig} + continue + fi + break +done + +# OK, pull and rebase to catchup to these changes... +git checkout main; +git pull --rebase + +# try to cleanup +for pr in $(git config --get-all branch.${staging}.opabinia.prs); do + git branch -D PR-${pr} + git config --remove-section branch.${staging}.opabinia.${pr} +done +git config --remove-section branch.${staging}.opabinia diff --git a/tools/tools/git/ghpr/ghpr-stage.sh b/tools/tools/git/ghpr/ghpr-stage.sh new file mode 100644 index 000000000000..df9eeeeea5a3 --- /dev/null +++ b/tools/tools/git/ghpr/ghpr-stage.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +set -e + +die() { + echo $* + exit 1 +} + +update_to_upstream() ( + local staging=$1 + local base=$2 + + git checkout ${base} + git pull --rebase + git rebase -i ${base} ${staging} +) + +PR=$1 +staging=staging + +[ -n "${PR}" ] || die "Need a pr" + +if [ "$(git config branch.${staging}.opabinia)" != "true" ]; then + die "Branch ${staging} has not been initialized" +fi + +base=$(git config branch.${staging}.opabinia.base) +[ -n "${base}" ] || die "No base set on ${staging}" + +if [ -z "$(git config --get-all branch.${staging}.opabinia.prs)" ]; then + # Update ${base} if prs list is empty + update_to_upstream ${staging} ${base} +else + # Otherwise checkout staging as is + git checkout ${staging} +fi + +# OK. We always have to create a new branch for the PR. We do this into +# ${base} first (since that's what gh pr checkout does). We then then rebase +# this branch onto the staging branch, doing the rebase rewriting along the +# way. This rather convoluted setup was selected over cherry-picking and/or +# pulling a formatted patch to apply because it always applies it correctly +# and then we use git's tools to merge, pushing the problems there rather than +# trying to deal with them ourselves here. + +# In the future, PR may be a list +# Also, error handling is annoying at best. + +git branch -D PR-$PR || true +gh pr checkout $PR -b PR-$PR + +upstream=$(git config branch.PR-$PR.pushRemote) +upstream_branch=$(git config branch.PR-$PR.merge | sed -e s=refs/heads/==) + +git rebase -i ${base} --onto ${staging} --exec 'env EDITOR=$HOME/bin/git-fixup-editor git commit --amend --trailer "Reviewed-by: imp" --trailer "Pull-Request: https://github.com/freebsd/freebsd-src/pull/'"$PR"'"' +# Save the upstream data +git config --add branch.${staging}.opabinia.prs ${PR} +git config --add branch.${staging}.opabinia.${PR}.upstream ${upstream} +git config --add branch.${staging}.opabinia.${PR}.upstream-branch ${upstream_branch} +# Move the staging branch to the new tip of the tree. +git checkout -B ${staging} HEAD + +# XXX need to somehow scrape the PR for approvals, translate that to FreeBSD's name +# and add that in the Reviewed-by stuff... that's done by hand... + +# Sanity check things... not 100% right, since it checks everything we're queued up so far... +tools/build/checkstyle9.pl ${base}..${staging} + +# Bump .Dd dates? +# run before/after igor? +# Anything else?