0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

大きすぎてgit pushできないリポジトリをGitHub APIで1ファイルだけ更新する

0
Last updated at Posted at 2026-02-24

問題

Railway にデプロイしたい。でも git push が通らない。

$ git push origin master
...
error: RPC failed; curl 28 Operation too slow.
Less than 1000 bytes/sec transferred the last 10 seconds
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedly

リポジトリが巨大すぎて HTTPS push がタイムアウトする。


なぜそうなったか

もともと1つのモノリシックなリポジトリで開発していた。途中でMLモデル、HTMLレポート、大量のJSONログが混入し、気づいたら数GBに膨れ上がっていた。

.gitignore を直せばよかったが、過去のコミットにすでに含まれているので git のオブジェクトサイズは変わらない。

やりたいのは「main.py だけを Railway のデプロイ用リポジトリに反映させること」。


解決策:GitHub Contents API でファイルを直接更新

GitHub には REST API でファイルを1つだけ更新できるエンドポイントがある。

PUT /repos/{owner}/{repo}/contents/{path}

git push は不要。HTTPS でファイルの内容を送るだけ。


手順

1. 現在の SHA を取得(更新に必須)

GitHub API でファイルを更新するには、現在のファイルの sha が必要。

TOKEN="ghp_xxxxxxxxxxxxxxxxxx"
OWNER="your_username"
REPO="your-deploy-repo"
FILE="main.py"

SHA=$(curl -s \
  -H "Authorization: token $TOKEN" \
  "https://api.github.com/repos/$OWNER/$REPO/contents/$FILE" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['sha'])")

echo "SHA: $SHA"

2. ファイルを Base64 エンコード

CONTENT=$(base64 -w 0 main.py)

3. PUT でアップロード

curl -s -X PUT \
  -H "Authorization: token $TOKEN" \
  -H "Content-Type: application/json" \
  "https://api.github.com/repos/$OWNER/$REPO/contents/$FILE" \
  -d "{
    \"message\": \"Update $FILE via API\",
    \"content\": \"$CONTENT\",
    \"sha\": \"$SHA\"
  }" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('commit',{}).get('sha','ERROR'))"

コミット SHA が返ってきたら成功。Railway は GitHub の変更を検知して自動デプロイされる。


Python 版(スクリプトとして保存しておくと便利)

import os, base64, json, urllib.request

TOKEN = os.environ["GITHUB_TOKEN"]
OWNER = "your_username"
REPO  = "your-deploy-repo"
FILE  = "main.py"

def get_sha(file_path: str) -> str:
    url = f"https://api.github.com/repos/{OWNER}/{REPO}/contents/{file_path}"
    req = urllib.request.Request(url, headers={"Authorization": f"token {TOKEN}"})
    with urllib.request.urlopen(req) as res:
        return json.loads(res.read())["sha"]

def upload_file(local_path: str, remote_path: str, message: str):
    sha = get_sha(remote_path)
    with open(local_path, "rb") as f:
        content = base64.b64encode(f.read()).decode()

    payload = json.dumps({
        "message": message,
        "content": content,
        "sha": sha,
    }).encode()

    url = f"https://api.github.com/repos/{OWNER}/{REPO}/contents/{remote_path}"
    req = urllib.request.Request(
        url, data=payload, method="PUT",
        headers={
            "Authorization": f"token {TOKEN}",
            "Content-Type": "application/json",
        }
    )
    with urllib.request.urlopen(req) as res:
        result = json.loads(res.read())
        print(f"✓ commit: {result['commit']['sha'][:7]}")

upload_file("main.py", "main.py", "Deploy: update main.py")

実行:

GITHUB_TOKEN=ghp_xxx python3 deploy.py
# ✓ commit: a3f9c12

この方法の制約

項目 内容
ファイルサイズ 1ファイル最大 100MB(Base64後)
更新単位 1ファイルずつ(複数ファイルはAPIを複数回呼ぶ)
コミット 1ファイルにつき1コミット
ブランチ デフォルトブランチ以外も指定可(branch パラメータ追加)

複数ファイルを1コミットにまとめたい場合は Git Data API(Tree API)を使う必要がある(かなり複雑になる)。


本質的な解決策(参考)

このアプローチは「対症療法」。本質的には:

  1. git filter-repo で大きなファイルを履歴ごと削除
  2. 大きなバイナリは Git LFS に移す
  3. 最初からデプロイ専用リポジトリを分ける

ただし「今すぐ1ファイル更新したいだけ」というときは GitHub API が一番手っ取り早い。


まとめ

  • 巨大リポジトリからの git push はタイムアウトする
  • GitHub Contents API (PUT /repos/.../contents/...) でファイル単体を更新できる
  • SHA の取得 → Base64 エンコード → PUT の3ステップ
  • Railway は GitHub push をトリガーに自動デプロイするので、これで十分動く

🚀 関連プロジェクト

この記事は NOVE OS 開発中に得た知見をまとめたものです。

NOVE OS は Rocky Linux サーバーを 1コマンドで自動最適化するツールです。eBPF・Rust・量子計算など最先端技術を統合し、ベンチマークスコア 2749点/3000点(91.6%) を達成しています。

👉 NOVE OS 公式サイト - 無料トライアルあり

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?