git-difff / app.py
soiz1's picture
Update app.py
5292ade verified
import os
import shutil
import tempfile
import subprocess
from pathlib import Path
import gradio as gr
# 一時ディレクトリ作成
temp_root = tempfile.mkdtemp()
REPO_TARGET_NAME = "target_repo"
REPO_SOURCE_NAME = "source_repo"
# GitHubリポジトリをクローン
def clone_repo(url, name):
dest = os.path.join(temp_root, name)
if os.path.exists(dest):
shutil.rmtree(dest)
try:
subprocess.run(["git", "clone", url, dest], check=True, capture_output=True, text=True)
return dest, None
except subprocess.CalledProcessError as e:
return None, f"クローン失敗: {e.stderr.strip()}"
# 差分ファイル一覧取得(.git関連除外)
def get_relative_diff_files(repo_a_path, repo_b_path):
try:
result = subprocess.run(
["git", "diff", "--no-index", "--name-only", repo_a_path, repo_b_path],
check=False, capture_output=True, text=True
)
files = result.stdout.strip().split('\n')
clean_files = []
for f in files:
f = f.strip()
if not f:
continue
rel = f.replace(repo_a_path + os.sep, "").replace(repo_b_path + os.sep, "")
if not rel.startswith(".git") and ".git/" not in rel:
clean_files.append(rel)
return clean_files
except Exception as e:
return [f"エラー: {str(e)}"]
# 差分を取得してUIに反映
def run_comparison(target_url, source_url):
repo_a, err1 = clone_repo(target_url, REPO_TARGET_NAME)
repo_b, err2 = clone_repo(source_url, REPO_SOURCE_NAME)
if err1:
return [], [], f"対象リポジトリのクローンに失敗しました: {err1}"
if err2:
return [], [], f"比較元リポジトリのクローンに失敗しました: {err2}"
diff_files = get_relative_diff_files(repo_a, repo_b)
return gr.update(choices=diff_files, value=diff_files), diff_files, ""
# 選択ファイルをコピー
def copy_files(selected_files):
repo_a = os.path.join(temp_root, REPO_TARGET_NAME)
repo_b = os.path.join(temp_root, REPO_SOURCE_NAME)
updated = []
for rel_path in selected_files:
src = os.path.abspath(os.path.join(repo_b, rel_path))
dst = os.path.abspath(os.path.join(repo_a, rel_path))
if not os.path.exists(src):
continue
if src == dst:
continue
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy2(src, dst)
updated.append(rel_path)
return f"{len(updated)} 個のファイルをコピーしました:\n" + "\n".join(updated)
# 差分が存在するかチェックしてGitHubへPush
def git_commit_and_push(token, commit_message, user_name, user_email):
repo_path = os.path.join(temp_root, REPO_TARGET_NAME)
if not os.path.exists(repo_path):
return "エラー:対象リポジトリが存在しません"
try:
# Git user設定
subprocess.run(["git", "config", "user.name", user_name], cwd=repo_path, check=True)
subprocess.run(["git", "config", "user.email", user_email], cwd=repo_path, check=True)
subprocess.run(["git", "add", "."], cwd=repo_path, check=True)
# 差分が存在するか確認
status = subprocess.run(["git", "status", "--porcelain"], cwd=repo_path, capture_output=True, text=True)
if not status.stdout.strip():
return "⚠️ 変更がないため、コミットとPushはスキップされました"
subprocess.run(["git", "commit", "-m", commit_message], cwd=repo_path, check=True)
remote_get = subprocess.run(["git", "remote", "get-url", "origin"],
cwd=repo_path, check=True, capture_output=True, text=True)
remote_url = remote_get.stdout.strip()
if remote_url.startswith("https://"):
remote_with_token = remote_url.replace("https://", f"https://{token}@")
else:
return "HTTPS URL の GitHub リモートのみ対応しています"
subprocess.run(["git", "remote", "set-url", "origin", remote_with_token], cwd=repo_path, check=True)
subprocess.run(["git", "push", "origin", "main"], cwd=repo_path, check=True)
return "✅ Push 成功しました"
except subprocess.CalledProcessError as e:
return f"Gitエラー: {e.stderr or e.stdout or str(e)}"
except Exception as e:
return f"エラー: {str(e)}"
# Gradio UI定義
with gr.Blocks(title="Git差分アップデーター") as demo:
gr.Markdown("## 🔄 Gitリポジトリ差分アップデート + GitHub Push")
with gr.Row():
target_url = gr.Textbox(label="対象リポジトリURL(上書き先)")
source_url = gr.Textbox(label="比較元リポジトリURL(コピー元)")
diff_btn = gr.Button("差分取得&クローン")
diff_status = gr.Textbox(label="ステータス", interactive=False)
error_msg = gr.Textbox(label="エラー", visible=False, interactive=False)
diff_checkboxes = gr.CheckboxGroup(label="差分ファイル一覧(コピーしたいものを選択)", choices=[])
copy_btn = gr.Button("選択ファイルを上書きコピー")
copy_result = gr.Textbox(label="コピー結果", lines=10, interactive=False)
gr.Markdown("### 🔐 GitHub Push 設定")
token_input = gr.Textbox(label="GitHub Personal Access Token(公開しないでください)", type="password")
user_name = gr.Textbox(label="Gitユーザー名", value="your-name")
user_email = gr.Textbox(label="Gitメールアドレス", value="your@email.com")
commit_msg = gr.Textbox(label="コミットメッセージ", value="Update from comparison tool")
push_btn = gr.Button("Push(mainブランチへ)")
push_result = gr.Textbox(label="Push結果", lines=3, interactive=False)
diff_btn.click(fn=run_comparison, inputs=[target_url, source_url],
outputs=[diff_checkboxes, diff_status, error_msg])
copy_btn.click(fn=copy_files, inputs=[diff_checkboxes], outputs=copy_result)
push_btn.click(fn=git_commit_and_push,
inputs=[token_input, commit_msg, user_name, user_email],
outputs=push_result)
demo.launch()