CoolFace
Apppublic

soiz1/git-difff

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
app.py148 linesDownload Raw Back to root
1import os2import shutil3import tempfile4import subprocess5from pathlib import Path6import gradio as gr7 8# 一時ディレクトリ作成9temp_root = tempfile.mkdtemp()10REPO_TARGET_NAME = "target_repo"11REPO_SOURCE_NAME = "source_repo"12 13# GitHubリポジトリをクローン14def clone_repo(url, name):15    dest = os.path.join(temp_root, name)16    if os.path.exists(dest):17        shutil.rmtree(dest)18    try:19        subprocess.run(["git", "clone", url, dest], check=True, capture_output=True, text=True)20        return dest, None21    except subprocess.CalledProcessError as e:22        return None, f"クローン失敗: {e.stderr.strip()}"23 24# 差分ファイル一覧取得(.git関連除外)25def get_relative_diff_files(repo_a_path, repo_b_path):26    try:27        result = subprocess.run(28            ["git", "diff", "--no-index", "--name-only", repo_a_path, repo_b_path],29            check=False, capture_output=True, text=True30        )31        files = result.stdout.strip().split('\n')32        clean_files = []33        for f in files:34            f = f.strip()35            if not f:36                continue37            rel = f.replace(repo_a_path + os.sep, "").replace(repo_b_path + os.sep, "")38            if not rel.startswith(".git") and ".git/" not in rel:39                clean_files.append(rel)40        return clean_files41    except Exception as e:42        return [f"エラー: {str(e)}"]43 44# 差分を取得してUIに反映45def run_comparison(target_url, source_url):46    repo_a, err1 = clone_repo(target_url, REPO_TARGET_NAME)47    repo_b, err2 = clone_repo(source_url, REPO_SOURCE_NAME)48 49    if err1:50        return [], [], f"対象リポジトリのクローンに失敗しました: {err1}"51    if err2:52        return [], [], f"比較元リポジトリのクローンに失敗しました: {err2}"53 54    diff_files = get_relative_diff_files(repo_a, repo_b)55    return gr.update(choices=diff_files, value=diff_files), diff_files, ""56 57# 選択ファイルをコピー58def copy_files(selected_files):59    repo_a = os.path.join(temp_root, REPO_TARGET_NAME)60    repo_b = os.path.join(temp_root, REPO_SOURCE_NAME)61 62    updated = []63    for rel_path in selected_files:64        src = os.path.abspath(os.path.join(repo_b, rel_path))65        dst = os.path.abspath(os.path.join(repo_a, rel_path))66 67        if not os.path.exists(src):68            continue69        if src == dst:70            continue71 72        os.makedirs(os.path.dirname(dst), exist_ok=True)73        shutil.copy2(src, dst)74        updated.append(rel_path)75 76    return f"{len(updated)} 個のファイルをコピーしました:\n" + "\n".join(updated)77 78# 差分が存在するかチェックしてGitHubへPush79def git_commit_and_push(token, commit_message, user_name, user_email):80    repo_path = os.path.join(temp_root, REPO_TARGET_NAME)81    if not os.path.exists(repo_path):82        return "エラー:対象リポジトリが存在しません"83 84    try:85        # Git user設定86        subprocess.run(["git", "config", "user.name", user_name], cwd=repo_path, check=True)87        subprocess.run(["git", "config", "user.email", user_email], cwd=repo_path, check=True)88 89        subprocess.run(["git", "add", "."], cwd=repo_path, check=True)90 91        # 差分が存在するか確認92        status = subprocess.run(["git", "status", "--porcelain"], cwd=repo_path, capture_output=True, text=True)93        if not status.stdout.strip():94            return "⚠️ 変更がないため、コミットとPushはスキップされました"95 96        subprocess.run(["git", "commit", "-m", commit_message], cwd=repo_path, check=True)97 98        remote_get = subprocess.run(["git", "remote", "get-url", "origin"],99                                    cwd=repo_path, check=True, capture_output=True, text=True)100        remote_url = remote_get.stdout.strip()101 102        if remote_url.startswith("https://"):103            remote_with_token = remote_url.replace("https://", f"https://{token}@")104        else:105            return "HTTPS URL の GitHub リモートのみ対応しています"106 107        subprocess.run(["git", "remote", "set-url", "origin", remote_with_token], cwd=repo_path, check=True)108        subprocess.run(["git", "push", "origin", "main"], cwd=repo_path, check=True)109 110        return "✅ Push 成功しました"111    except subprocess.CalledProcessError as e:112        return f"Gitエラー: {e.stderr or e.stdout or str(e)}"113    except Exception as e:114        return f"エラー: {str(e)}"115 116# Gradio UI定義117with gr.Blocks(title="Git差分アップデーター") as demo:118    gr.Markdown("## 🔄 Gitリポジトリ差分アップデート + GitHub Push")119 120    with gr.Row():121        target_url = gr.Textbox(label="対象リポジトリURL(上書き先)")122        source_url = gr.Textbox(label="比較元リポジトリURL(コピー元)")123 124    diff_btn = gr.Button("差分取得&クローン")125    diff_status = gr.Textbox(label="ステータス", interactive=False)126    error_msg = gr.Textbox(label="エラー", visible=False, interactive=False)127    diff_checkboxes = gr.CheckboxGroup(label="差分ファイル一覧(コピーしたいものを選択)", choices=[])128 129    copy_btn = gr.Button("選択ファイルを上書きコピー")130    copy_result = gr.Textbox(label="コピー結果", lines=10, interactive=False)131 132    gr.Markdown("### 🔐 GitHub Push 設定")133    token_input = gr.Textbox(label="GitHub Personal Access Token(公開しないでください)", type="password")134    user_name = gr.Textbox(label="Gitユーザー名", value="your-name")135    user_email = gr.Textbox(label="Gitメールアドレス", value="your@email.com")136    commit_msg = gr.Textbox(label="コミットメッセージ", value="Update from comparison tool")137    push_btn = gr.Button("Push(mainブランチへ)")138    push_result = gr.Textbox(label="Push結果", lines=3, interactive=False)139 140    diff_btn.click(fn=run_comparison, inputs=[target_url, source_url],141                   outputs=[diff_checkboxes, diff_status, error_msg])142    copy_btn.click(fn=copy_files, inputs=[diff_checkboxes], outputs=copy_result)143    push_btn.click(fn=git_commit_and_push,144                   inputs=[token_input, commit_msg, user_name, user_email],145                   outputs=push_result)146 147demo.launch()148