From: Nadav Har'El <[email protected]>
Committer: WALDEMAR KOZACZUK <[email protected]>
Branch: master

Add script for merging a github pull request

Although github pull requests are convenient way to track pending
patches and to review small or large patch sets, it's not obvious
for maintainers how to try such PRs locally, and also when
eventually deciding to merge the PR, github provides a "merge"
button in its GUI with multiple problems:

  1. It often botches the committer's address
  2. It "merges" even single-patch PRs. This is not OSv's
     tradition - single patches should remain a single patch,
     not a merge of a single-patch branch.

The script in this patch, scripts/pull_github_pr.sh, solves
both problems: If the maintainer runs,

  scripts/pull_github_pr.sh 1245

Fetches PR number 1245 locally, you can then compile and test
it yourself, and if you're pleased, finally push it.

A complete session may look something like this:

  # get your working directory to modern master, as usual
  git checkout master
  git pull
  git submodule update --recursive

  # Pull pull request 1245:
  scripts/pull_github_pr.sh 1245
  # Now maybe compile OSv, test, it, and so on.
  # When you're pleased, and want to merge the PR, do:
  git push

This script is based on a similar script from the Scylla project.

The script works slightly differently for single-patch vs.
multiple-patch PRs:

1. For a multiple-patch PR, the PR branch is merged. The
   first comment in the PR is used as the content of the
   merge commit so PR authors should edit this first comment
   to have good content.
   When this is merged into master, github automatically
   closes the PR as "merged".

2. For a single-patch PR, the PR branch is *not* merged,
   instead the single patch is pushed as a single patch.
   Github doesn't realise that the branch was "merged"
   so instead the script adds a "Closes" tag to the
   commit message so that the PR is closed automatically
   when the result is merged.

Signed-off-by: Nadav Har'El <[email protected]>

---
diff --git a/scripts/pull_github_pr.sh b/scripts/pull_github_pr.sh
--- a/scripts/pull_github_pr.sh
+++ b/scripts/pull_github_pr.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+# Script for pulling a github pull request
+# along with generating a merge commit message.
+# Example usage for pull request #6007 and master branch:
+# git fetch
+# git checkout origin/master
+# ./scripts/pull_github_pr.sh 6007
+
+set -e
+
+if [[ $# != 1 ]]; then
+       echo Please provide a github pull request number
+       exit 1
+fi
+
+for required in jq curl; do
+       if ! type $required >& /dev/null; then
+               echo Please install $required first
+               exit 1
+       fi
+done
+
+curl() {
+    local opts=()
+    if [[ -n "$GITHUB_LOGIN" && -n "$GITHUB_TOKEN" ]]; then
+        opts+=(--user "${GITHUB_LOGIN}:${GITHUB_TOKEN}")
+    fi
+    command curl "${opts[@]}" "$@"
+}
+
+NL=$'\n'
+
+PR_NUM=$1
+# convert full repo URL to its project/repo part, in case of failure default 
to origin/master:
+REMOTE_SLASH_BRANCH="$(git rev-parse --abbrev-ref --symbolic-full-name  
@{upstream} \
+     || git rev-parse --abbrev-ref --symbolic-full-name master@{upstream} \
+     || echo 'origin/master')"
+REMOTE="${REMOTE_SLASH_BRANCH%/*}"
+REMOTE_URL="$(git config --get "remote.$REMOTE.url")"
+PROJECT=`sed 's/[email protected]://;s#https://github.com/##;s/\.git$//;' 
<<<"${REMOTE_URL}"`
+PR_PREFIX=https://api.github.com/repos/$PROJECT/pulls
+
+echo "Fetching info on PR #$PR_NUM... "
+PR_DATA=$(curl -s $PR_PREFIX/$PR_NUM)
+MESSAGE=$(jq -r .message <<< $PR_DATA)
+if [ "$MESSAGE" != null ]
+then
+    # Error message, probably "Not Found".
+    echo "$MESSAGE"
+    exit 1
+fi
+PR_TITLE=$(jq -r .title <<< $PR_DATA)
+echo "    $PR_TITLE"
+PR_DESCR=$(jq -r .body <<< $PR_DATA)
+PR_LOGIN=$(jq -r .head.user.login <<< $PR_DATA)
+echo -n "Fetching full name of author $PR_LOGIN... "
+USER_NAME=$(curl -s "https://api.github.com/users/$PR_LOGIN"; | jq -r .name)
+echo "$USER_NAME"
+
+git fetch "$REMOTE" pull/$PR_NUM/head
+
+nr_commits=$(git log --pretty=oneline HEAD..FETCH_HEAD | wc -l)
+
+closes="${NL}${NL}Closes #${PR_NUM}${NL}"
+
+if [[ $nr_commits == 1 ]]; then
+       commit=$(git log --pretty=oneline HEAD..FETCH_HEAD | awk '{print $1}')
+       message="$(git log -1 "$commit" --format="format:%s%n%n%b")"
+       if ! git cherry-pick $commit
+       then
+               echo "Cherry-pick failed. You are now in a subshell. Either 
resolve with git cherry-pick --continue or git cherry-pick --abort, then exit 
the subshell"
+               head_before=$(git rev-parse HEAD)
+               bash
+               head_after=$(git rev-parse HEAD)
+               if [[ "$head_before" = "$head_after" ]]; then
+                       exit 1
+               fi
+       fi
+       git commit --amend -m "${message}${closes}"
+else
+       git merge --no-ff --log=1000 FETCH_HEAD -m "Merge '$PR_TITLE' from 
$USER_NAME" -m "${PR_DESCR}${closes}"
+fi
+git commit --amend # for a manual double-check

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/000000000000c073a80602e96727%40google.com.

Reply via email to