Cursor Origin移行前のGit棚卸しをPythonで行う
Cursorがコードホスティングの「Origin」を始めたという話を見ました。
僕なら先にリポジトリを数えます。動くもの、放置されたもの、接続先がないもの。ここが曖昧なまま対象を選ぶと、古い検証用フォルダまで運ぶことになります。
移行は「今も面倒を見るコードは何件か」を決める作業です。ローカルPCのGitリポジトリを走査し、CSVにするPythonを置きます。
最初にほしいのは現状表
営業・企画のチームで先に必要なのは、次の情報でした。
| 列 | 見る理由 |
|---|---|
| リポジトリの場所 | 同名フォルダを取り違えない |
| 現在のブランチ |
main 以外の運用を拾う |
| remote URL | 今の接続先を確かめる |
| 最終コミット日 | 止まった案件を分ける |
| 判定 | 誰に何を確認するか決める |
ここ地味に効きます。フォルダ一覧だけでは、古いpush先や master が残る案件を見落とします。接続先は .git/config に残るため、手元の origin は勝手に揃いません。
昨日、古い提案用フォルダを見返したら、リポジトリのつもりで置いていたものが単なるZIP展開先でした。
Git情報をCSVへ出すPython
.git を探し、ブランチ、remote、最終コミット日を git_inventory.csv に書きます。
import argparse
import csv
import subprocess
from datetime import date, datetime
from pathlib import Path
def git(repo, *args):
result = subprocess.run(
["git", "-C", str(repo), *args], text=True,
capture_output=True, check=False,
)
return result.stdout.strip() if result.returncode == 0 else ""
def action(last_commit, urls, stale_days):
if not last_commit:
return "初期化のみ"
if not urls:
return "接続先なし"
committed = datetime.strptime(last_commit, "%Y-%m-%d").date()
return "停止候補" if (date.today() - committed).days >= stale_days else "確認対象"
parser = argparse.ArgumentParser()
parser.add_argument("root", type=Path)
parser.add_argument("--output", type=Path, default=Path("git_inventory.csv"))
parser.add_argument("--stale-days", type=int, default=180)
args = parser.parse_args()
rows = []
for marker in sorted(args.root.expanduser().resolve().rglob(".git")):
repo = marker.parent # .git がファイルになるGit worktreeも対象
remotes = git(repo, "remote").splitlines()
urls = [f"{name}={url}" for name in remotes
for url in git(repo, "remote", "get-url", "--all", name).splitlines()]
last = git(repo, "log", "-1", "--format=%cs")
days = "" if not last else (date.today() - datetime.strptime(last, "%Y-%m-%d").date()).days
rows.append({
"repo": str(repo.relative_to(args.root.expanduser().resolve())),
"branch": git(repo, "branch", "--show-current") or "detached",
"remote_urls": " | ".join(urls),
"last_commit": last,
"days_since_commit": days,
"action": action(last, urls, args.stale_days),
})
fields = ["repo", "branch", "remote_urls", "last_commit", "days_since_commit", "action"]
with args.output.open("w", newline="", encoding="utf-8-sig") as file:
writer = csv.DictWriter(file, fieldnames=fields)
writer.writeheader()
writer.writerows(rows)
print(f"{len(rows)}件を {args.output} に出力しました")
案件フォルダが ~/work なら、実行はこれだけです。
python git_inventory.py ~/work --output git_inventory.csv --stale-days 180
僕のテスト用フォルダでは、通常の案件、291日前の案件、コミットなしの試作フォルダを置き、次の3件が出ました。
repo,branch,remote_urls,last_commit,days_since_commit,action
active-app,main,origin=../remote.git,2026-08-19,0,確認対象
old-tool,master,origin=../remote.git,2025-11-01,291,停止候補
scratch,main,,,,初期化のみ
--stale-days 180 は半年以上コミットがないものを停止候補にする設定です。3か月で見直すなら 90 に変えるだけです。utf-8-sig なので、Windows版Excelでも列名が文字化けしにくくなります。
停止候補は、削除リストにしない
最終コミットが古くても、提案のデモや月次処理の雛形は残す理由があります。
CSVではまず 停止候補 をフィルタし、案件名を知っている人に確認を回します。返事がなければ移行から外す。削除もしない。手元に残せばよいです。
一番先に見たいのは 接続先なし の行です。ローカルだけで進んだコードは、コピー漏れが起きやすい。URLが空の行だけを別タブに出すと、確認依頼が短くなります。
親フォルダを大きくしすぎると走査に時間がかかります。~/work のように案件を集めた場所を指定するのが現実的です。共有前には、URLに顧客名や社内ホスト名が含まれないか見ておきます。
で、現場でどう使うか
新しいホスティングを試す前に、CSVを1枚出します。確認対象 と 接続先なし を担当者と見ます。残す案件だけ決めてから、移行先の検証環境へ持っていく。この順番なら、サービスの勢いに引っ張られずに済みます。
僕は移行作業の最初の30分を、この表に使うようにしています。面倒な整理ですが、移行後に「どこへ行った?」を探す半日よりはずっと安いです。