GitHub Actionsを使って「毎日自動でAPIからデータを取得し、GitHubリポジトリにコミット&プッシュする」というバッチ処理を運用していると、突然エラーが発生して止まってしまうことがあります。

今回は、GitHub Actionsの実行を「Re-run(再実行)」した際に発生したプッシュ拒否エラー(rejected)の原因と、それを自動で回避するための「恒久対策(git pull --rebase)」について解説します。

スポンサーリンク

1. 発生したエラー:git push が「rejected」で失敗する

GitHub Actionsのログを確認したところ、以下のようなエラーを出して終了していました。

[main b9ffcc8] chore: update daily YouTube stats [skip ci]
 15 files changed, 92 insertions(+)
To https://github.com/your-username/your-repo
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/your-username/your-repo'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
Error: Process completed with exit code 1.

エラーメッセージには [rejected] main -> main (fetch first) とあります。 これは、「GitHubのリモート側に、あなたの知らない最新のコミットがあるため、プッシュ(保存)できません。先にダウンロード(fetch)してください」というGitの競合エラーです。

2. 原因:「Re-run」と「Run workflow」の決定的な違い

なぜ、データ収集のバッチを実行しただけなのに競合が発生してしまったのでしょうか?

原因は、GitHub Actionsの実行ボタンである 「Re-run jobs(ジョブの再実行)」 を選択したことにありました。

🧐 「Re-run」は過去の状態をタイムトラベルして実行する

Re-run は、「そのワークフローが最初に起動した時点(過去の特定のコミット時点)のコード」 をもう一度引っ張り出して実行する機能です。

もし、そのワークフローが失敗した後に、開発者がローカルから新しいコード(新機能やバグ修正など)をGitHubにプッシュしていた場合、Re-run されたActions環境は 「開発者が最新コードをプッシュする前の古い状態」 で動き出します。

そのため、Actionsが古い状態のまま処理を終えて git push しようとすると、GitHubサーバーから「おっと、すでに開発者がプッシュした最新のコミットがあるから、古い歴史からのプッシュは受け付けられないよ」と拒否されてしまうのです。

💡 解決策:最新状態から動かす「Run workflow」

一方、「Run workflow(ワークフローの実行)」 は、現在のリポジトリの 最新コミット(mainブランチの最新状態) から処理を新しく開始します。

そのため、ローカルから追加でプッシュしたコードもしっかりと取り込まれた状態で動作するため、余計な競合を起こすことなく正常にプッシュが成功します。

スポンサーリンク

3. 【恒久対策】二度と競合エラーを起こさないためのワークフロー改善

「Re-run」と「Run workflow」の違いは分かりましたが、手動の開発プッシュと自動バッチの実行タイミングがたまたま重なったときなどにも、同じプッシュ拒否エラー(Exit Code 1)は発生してしまいます。

これを完全に自動で回避するために、GitHub Actionsのワークフロー定義ファイル(.github/workflows/*.yml)を改善しましょう。

対策:プッシュの直前に git pull --rebase を挟む

従来のワークフローでは、コミットした後にそのまま git push を行っていました。これを、プッシュする直前に git pull --rebase を実行する ように書き換えます。

❌ 修正前のコード

- name: Commit and Push
  run: |
    git add data/
    git commit -m "chore: update daily stats"
    git push

⭕ 修正後のコード

- name: Commit and Push (競合防止)
  run: |
    git add data/
    git commit -m "chore: update daily stats"
    # プッシュの前にリモートの最新を取得し、その上に自分の変更を乗せる
    git pull --rebase origin main
    git push

❓ --rebase を挟むとどうなるのか?

git pull --rebase origin main を挟むことで、ロボット(Actions)は以下のように賢く立ち振る舞います。

  1. プッシュしようとした際、リモートに人間の最新コミットがあるのを検知する。
  2. ロボットは一旦、自分のコミット(自動生成したデータ)を脇に置いておく。
  3. リモートから人間がプッシュした最新コードをダウンロードして取り込む(git pull)。
  4. 取り込んだ最新状態の 一番上に、脇に置いていた自動生成データをきれいに乗せ直す(--rebase)。
  5. その状態で改めて安全にプッシュする。

これにより、人間と自動バッチのタイミングがどれだけ重なっても、ロボット側が空気を読んで自動で合流してくれるため、バッチ処理がプッシュエラーで落ちることは二度となくなります。

4. まとめ

  • Re-run は過去の時点でのやり直しのため、後からプッシュされたコードと競合しやすい。
  • Run workflow は常に最新状態からの新規実行のため安全。
  • 自動コミット&プッシュを行うGitHub Actionsには、git pull --rebase origin main && git push を設定しておくのが運用の大原則!

自動化ワークフローを構築する際は、ぜひこの「競合自動回避」の1行を組み込んでみてください!


この記事が気に入ったら『目黒で働く分析担当の作業メモ』ご支援をお願いします!

※OFUSEに飛びます


おすすめの記事