import subprocess import os import logging from datetime import datetime # ---------------- CONFIG ---------------- GITHUB_USER = "" GITHUB_EMAIL = "" GITHUB_TOKEN = "" # <-- Put your GitHub PAT here # Folder where working LFS-migrated repos exist PROJECTS_DIR = "./git_repo_project_files" # ---------------- Logging Setup ---------------- log_filename = f"lfs_heatmap_update_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log" logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", handlers=[ logging.FileHandler(log_filename), logging.StreamHandler() ] ) # ---------------- FUNCTIONS ---------------- def list_local_repos(): """Return a list of folder names in PROJECTS_DIR""" repos = [] for entry in os.listdir(PROJECTS_DIR): full_path = os.path.join(PROJECTS_DIR, entry) if os.path.isdir(full_path) and os.path.exists(os.path.join(full_path, ".git")): repos.append(entry) return repos def process_repo(repo_name): local_path = os.path.join(PROJECTS_DIR, repo_name) github_url = f"https://{GITHUB_TOKEN}@github.com/{GITHUB_USER}/{repo_name}.git" try: logging.info(f"Fetching latest commits for {repo_name}...") subprocess.run(["git", "-C", local_path, "fetch", "--all"], check=True) # Rewrite commits to GitHub email (preserve dates) logging.info(f"Rewriting commits in {repo_name} to GitHub email...") subprocess.run([ "git", "-C", local_path, "filter-repo", "--email-callback", f'return b"{GITHUB_EMAIL}"', "--force" ], check=True) # Ensure 'github' remote exists remotes = subprocess.run( ["git", "-C", local_path, "remote"], capture_output=True, text=True ).stdout.splitlines() if "github" not in remotes: logging.info(f"Adding 'github' remote for {repo_name}...") subprocess.run(["git", "-C", local_path, "remote", "add", "github", github_url], check=True) else: logging.info(f"Updating 'github' remote URL for {repo_name}...") subprocess.run(["git", "-C", local_path, "remote", "set-url", "github", github_url], check=True) # Push all refs to GitHub logging.info(f"Pushing all branches and tags of {repo_name} to GitHub...") subprocess.run(["git", "-C", local_path, "push", "--mirror", "github"], check=True) logging.info(f"✅ Finished processing {repo_name}") except subprocess.CalledProcessError as e: logging.error(f"❌ Error processing {repo_name}: {e}") # ---------------- MAIN ---------------- def main(): logging.info(f"Starting LFS heatmap update for GitHub user: {GITHUB_USER}") existing_repos = list_local_repos() if not existing_repos: logging.info(f"No repos found in {PROJECTS_DIR}. Nothing to process.") return for repo_name in existing_repos: logging.info(f"Processing repo: {repo_name}") process_repo(repo_name) logging.info(f"All done! Log saved to {log_filename}") if __name__ == "__main__": main()