複数のリポジトリを並行して開発していると、1日の開始・終了時に「リモートと同期しているか」「ローカルに変更はないか」と確認して回るのも少し面倒だと思います。
なので、複数の Git リポジトリの状態を一括確認する Bash スクリプトを書きました。
実行すると下図のように「リモートと同期しているか」「ローカルに変更はないか」を一括で確認できます。
スクリプト
スクリプトは以下です。Git ホストは問いません (github.com でなくても OK です)。
以下の変数を設定ください。
-
repos— ここにローカルリポジトリ一覧を記入ください。 -
ssh_key— リポジトリに SSH 接続する場合はその秘密鍵のパスに変更してください。開始時にリポジトリに SSH 接続しますか? (y/n):と訊かれるのでyと入力した後ssh-agentに秘密鍵のパスフレーズをキャッシュさせてください。- 親プロセスでキャッシュ済の場合は訊かれません (自動で利用します)。
- もし HTTPS 接続
git ls-remote https://git_host/xxx/yyy.gitでリモートのコミットハッシュが取れるなら秘密鍵をキャッシュしなくても構いません (origin が SSH URL であっても、HTTPS URL に変換して接続します)。 - 秘密鍵がキャッシュ済かつ origin が SSH URL ならまず SSH 接続を試み、その後 HTTPS 接続を試みます。
以下の制限があります。
- ローカルが main ブランチでない場合はリモート最新コミットと比較しません。
- SSH 接続するリポジトリは秘密鍵が全て共通の想定です (ホスト混在未対応)。
-
リポジトリの origin が SSH URL だが秘密鍵をキャッシュせず HTTPS 接続を試みる場合は、SSH リポジトリの SSH URL が
git@git_host:xxx/yyy.gitで、HTTPS URL がhttps://git_host/xxx/yyy.gitである想定で URL を変換します。
check.sh
#!/usr/bin/env bash
ssh_key="${HOME}/.ssh/id_ed25519" # 秘密鍵 (リポジトリに SSH 接続するなら必須)
check_key_cached() {
# キャッシュ済み鍵一覧に ssh_key の fingerprint が含まれるか確認します
local fp="$(ssh-keygen -lf "$ssh_key" | awk '{print $2}')"
ssh-add -l 2>/dev/null | grep -q "$fp"
}
check_latest() {
# ローカル最新コミットとリモート最新コミットが同一か確認します
local branch="$(git branch --show-current)"
if [ "$branch" != "main" ]; then
echo -e "\033[93mローカルが main ブランチではない\033[0m"
return 1
fi
local local_hash="$(git rev-parse HEAD)"
local url="$(git remote get-url origin)"
local remote_hash=''
if [[ "$url" == git@*:* ]] && check_key_cached; then
# (1) origin が SSH URL かつ鍵がキャッシュ済みなら SSH 接続します
echo "リモートに SSH 接続します"
remote_hash="$(git ls-remote origin 'refs/heads/main' 2>/dev/null | awk '{print $1}')"
fi
if [[ -z "$remote_hash" ]]; then
# (2) SSH 接続しない場合、または SSH 接続に失敗した場合は HTTPS 接続します
echo "リモートに HTTPS 接続します"
https_url="$(echo "$url" | sed -E 's#^git@([^:]+):#https://\1/#')"
remote_hash="$(git ls-remote "$https_url" 'refs/heads/main' | awk '{print $1}')"
fi
if [[ -z "$remote_hash" ]]; then
echo -e "\033[93mリモートリポジトリへの接続に失敗\033[0m"
return 1
fi
echo "local : $local_hash"
echo "remote: $remote_hash"
if [ "$local_hash" = "$remote_hash" ]; then
echo -e "\033[92mローカル最新コミットとリモート最新コミットが同一です\033[0m"
else
git merge-base --is-ancestor "$remote_hash" "$local_hash" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo -e "\033[91mローカル最新コミットが未プッシュです\033[0m"
else
echo -e "\033[91mリモート最新コミットが未マージです\033[0m"
fi
fi
}
check_status() {
# ローカルに変更があるか確認します
local flag=0
local status="$(git status --porcelain)"
local msg0="\033[92mローカルに変更はありません\033[0m"
local msg1="\033[91mステージ済の変更があります\033[0m"
local msg2="\033[91m未ステージの変更があります\033[0m"
local msg3="\033[93m未追跡のファイルがあります\033[0m"
echo "$status" | grep -q '^[MADRC]' && { echo -e "$msg1"; flag=1; }
echo "$status" | grep -q '^.[MD]' && { echo -e "$msg2"; flag=1; }
echo "$status" | grep -q '^??' && { echo -e "$msg3"; flag=1; }
[ $flag -eq 0 ] && echo -e "$msg0"
}
# ========== MAIN ==========
# 秘密鍵が未キャッシュならキャッシュします
# 全リポジトリに HTTPS 接続できる (git ls-remote https://git_host/xxx/yyy.git が通る)
# 場合は秘密鍵のキャッシュは必須ではありません
# Ex. GitHub パブリックリポジトリは HTTPS 接続できます
# Ex. GitHub プライベートリポジトリでも PAT 認証などがあれば HTTPS 接続できます
if check_key_cached; then
echo "${ssh_key} はキャッシュ済です"
else
read -p "リポジトリに SSH 接続しますか? (y/n): " ans
if [ "$ans" = 'y' ]; then
ssh-add "$ssh_key" || {
eval "$(ssh-agent -s)"
ssh-add "$ssh_key"
}
fi
fi
# 各リポジトリの状態を一括確認します
repos=(
# 以下にローカルリポジトリ一覧を記入 (HOME からの相対パスで)
".local/share/chezmoi"
"workspace/nazuna"
"workspace/cookipedia"
)
for repo in "${repos[@]}"; do
echo -e "\033[96m===== $repo =====\033[0m"
repo_dir="${HOME}/${repo}"
[ -d "$repo_dir" ] || { echo -e "\033[93mディレクトリがない\033[0m"; continue; }
(
cd "$repo_dir"
check_latest
check_status
)
done
おまけ: (chezmoi 利用者向け) chezmoi リポジトリについてはローカルとソースの差異まで確認する場合
chezmoi を利用している場合、これについては「ソースディレクトリがリモートリポジトリと同期しているか」だけでは不十分で、「ローカルがソースディレクトリと同期しているか」まで確認しておきたくなると思います。
その場合、スクリプトに以下の関数を追記してください。
check_chezmoi() {
# chezmoi についてはローカルとソースの差異も確認します
if [ -z "$(chezmoi diff)" ]; then
echo -e "\033[92mローカルはソースと同一です\033[0m"
else
echo -e "\033[91mローカルとソースに差異があります\033[0m"
fi
}
そして、サブシェル内に以下を追記してください。
(
cd "$repo_dir"
check_latest
check_status
[ "$repo" = ".local/share/chezmoi" ] && check_chezmoi # これを追記
)
ローカルとソースディレクトリに差分がないと以下のようになります。

ローカルとソースディレクトリに差分があると以下のようになります。

