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?

Cursor Origin移行前のGit棚卸しをPythonで行う

0
Posted at

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分を、この表に使うようにしています。面倒な整理ですが、移行後に「どこへ行った?」を探す半日よりはずっと安いです。

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?