シェルでGithubリポジトリのmasterブランチの名前をmainブランチに変更する

2023-03-27Bash,Git,GitHub

はじめに

随分前のことですが、新規に作成される Github リポジトリのデフォルトブランチ名が master から main に変更となりました。

ところが既存の Github リポジトリは当然 master というブランチ名のままです。

機会があれば昨今の風潮にのって一括修正しようかなぁ、と思っていましたが、ブログネタについでにシェルでブランチ名の変更を行ってみました。

大量に Github リポジトリを持っている組織などでも利用できるのではないでしょうか。

検証環境

$ uname -moi
x86_64 MacBookPro16,1 Darwin

$ bash -version | head -n 1
GNU bash, version 5.1.8(1)-release (x86_64-apple-darwin20.3.0)

$ git --version
git version 2.31.1

$ gh --version
gh version 1.10.2 (2021-05-19)
https://github.com/cli/cli/releases/tag/v1.10.2

今回利用するツール

今回は以下のツールを利用します。

作成したシェルスクリプト

コマンドを 1 つ 1 つ解説していこうかと思いましたが、作ってしまってそれをお見せしたほうが良いかと思いました。
作成したシェルスクリプトを掲載します。

master2main.sh

#!/usr/bin/env bash

# おまじない
set -o errexit
set -o nounset

# スクリプトを実行しているディレクトリが.git管理されていることが前提
[[ -d ./.git ]] || {
  echo "[ERROR] Gitのワークディレクトリでスクリプトを実行してください。" >&2
  exit 100
}

echo "=== START ==="

REPO=$(git remote get-url origin | grep -o '[^/:]\+/[^/:]\+$' | sed 's/\.git$//')

# upstreamリポジトリかどうかは適宜変更してください
REMOTE=origin

# まずはGitのリモートリポジトリの最新master情報を取得
git fetch "${REMOTE}" master

# masterブランチからmainブランチを分岐させる
git checkout -b main "${REMOTE}/master" \
  --no-track
# mainブランチをリモートにプッシュ
git push -u "${REMOTE}" main
# リポジトリのHEADをmainブランチに切り替え
git remote set-head "${REMOTE}" main

# Github上のデフォルトブランチをmainブランチに切り替え
gh api -XPATCH "repos/${REPO}" -f default_branch=main >/dev/null

# Github上の全プルリクのbaseもmainブランチに切り替え
for NUM in $(
  gh pr list -B master -L999 \
    | cut -f 1
); do
  gh api -XPATCH "repos/${REPO}/pulls/${NUM}" -f base=main >/dev/null
done

# Githのリモートリポジトリからmasterブランチを削除
git push \
  --delete origin master

# ローカルのmasterブランチは必要があれば消してください
# git branch -D master

# 確認のためにGithubリポジトリのWebページを開くこともできます
# gh repo view --web

echo "=== FINISH ==="

使い方

使い方は簡単です。

master ブランチを持っている Github ワークディレクトリ ( git clone したディレクトリのこと。 .git ディレクトリがあるはずですね。 ) で、スクリプトを実行するだけです。

$ pwd
/tmp/genzouw/my-repo

# シェルスクリプトはどこに置かれていても構いません
$ ~/bin/master2main.sh
=== START ===
From ssh://github.com/genzouw/my-repo
 * branch            master     -> FETCH_HEAD
Switched to a new branch 'main'
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote:
remote: Create a pull request for 'main' on GitHub by visiting:
remote:      https://github.com/genzouw/my-repo/pull/new/main
remote:
To ssh://github.com/genzouw/my-repo.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
To ssh://github.com/genzouw/my-repo.git
 - [deleted]         master
=== FINISH ===

# リポジトリがどうなったかを確認(ローカルのmasterブランチは一応残しています)
$ git branch -a
* main
  master
  remotes/origin/HEAD -> origin/main
  remotes/origin/main

複数の Github リポジトリを一括更新する

対象となる Git ワークディレクトリの一覧があれば、ループで回して一括更新できるはずです。

dirs=(
  dir_a
  dir_b
  dir_c
)

for dir in ${dirs[@]}; do
  cd "${dir}"
  ~/bin/master2main.sh
done

main ブランチへのリネーム後にメンバが実施する作業

以下のコマンドを実行し、 リモートリポジトリの main ブランチ、master ブランチへの修正をワークディレクトリに反映します。

$ git fetch --all --tags --prune
Fetching origin
From ssh://github.com/genzouw/sqlite
 * [new branch]      main       -> origin/main

$ git branch -a
warning: ignoring broken ref refs/remotes/origin/HEAD
* master
  remotes/origin/main

# 不要であればワークディレクトリ
$ git switch main
Branch 'main' set up to track remote branch 'main' from 'origin'.
Switched to a new branch 'main'

$ git branch -D master
Deleted branch master (was xxxxxxx).

せっかくなので Gist に公開しました

こちらはコメントが英語なので、苦手な方はエントリのコードを参照ください。

ひとこと

実際、これだけでは済まないですよね。
修正が必要だろうと思いつくものを上げると

1 リポジトリずつ、段階的に実施となるでしょう。

2023-03-27Bash,Git,GitHub