git-repo-explorer / git_readme_utils.py
CalleCaje's picture
feat: create git repository explorer app with diff, tree, and README viewing capabilities
bd89e7e
import subprocess
import tempfile
import shutil
import os
from typing import Optional, Union, Dict, List, Tuple
def get_repo_readme(repo_source: str, commit_depth: int = 1) -> Union[Tuple[str, str], str]:
"""
Returns the README content of a git repository at a specific commit.
Args:
repo_source: Path to the git repository or URL to a remote repository.
Function automatically detects if it's a local path or remote URL.
commit_depth: How many commits back to check (default=1 for the latest commit).
For example, 2 would return the README from the second-to-last commit.
Returns:
A tuple containing (readme_filename, readme_content) if README is found,
or an error string if something fails or no README is found
"""
temp_dir = None
try:
# Determine if repo_source is a remote URL or local path
is_remote = repo_source.startswith(('http://', 'https://', 'git://', 'ssh://')) or repo_source.endswith('.git')
# If it's a remote repository, clone it to a temporary directory
if is_remote:
temp_dir = tempfile.mkdtemp()
print(f"Cloning remote repository: {repo_source}")
clone_cmd = ["git", "clone", repo_source, temp_dir]
subprocess.check_output(clone_cmd)
repo_path = temp_dir
else:
repo_path = repo_source
# Validate commit_depth parameter
if commit_depth < 1:
return "Error: commit_depth must be a positive integer"
# Get the commit hash for the specified depth
if commit_depth == 1:
# For the latest commit
commit_ref = "HEAD"
else:
# For earlier commits: HEAD~1, HEAD~2, etc. (0-indexed in git, so we subtract 1)
commit_ref = f"HEAD~{commit_depth-1}"
# Get the commit hash
commit_cmd = ["git", "-C", repo_path, "rev-parse", commit_ref]
try:
commit_hash = subprocess.check_output(commit_cmd).decode('utf-8').strip()
except subprocess.CalledProcessError:
return f"Error: Could not find commit at depth {commit_depth}"
# Common README file patterns to check
readme_patterns = [
"README.md",
"README.txt",
"README",
"readme.md",
"Readme.md",
"ReadMe.md",
"readme.txt",
"README.markdown",
"readme.markdown"
]
# Check for the existence of README files
ls_files_cmd = ["git", "-C", repo_path, "ls-tree", "--name-only", commit_hash]
files = subprocess.check_output(ls_files_cmd).decode('utf-8').strip().split('\n')
readme_file = None
for pattern in readme_patterns:
if pattern in files:
readme_file = pattern
break
# Also check in the root directory
if not readme_file:
for pattern in readme_patterns:
if any(f == pattern or f.endswith(f"/{pattern}") for f in files):
readme_file = next((f for f in files if f == pattern or f.endswith(f"/{pattern}")), None)
break
if not readme_file:
return "No README file found in the repository"
# Get the content of the README file at the specified commit
show_cmd = ["git", "-C", repo_path, "show", f"{commit_hash}:{readme_file}"]
try:
readme_content = subprocess.check_output(show_cmd).decode('utf-8')
return (readme_file, readme_content)
except subprocess.CalledProcessError:
return f"Error: Could not read {readme_file} at commit {commit_hash}"
except subprocess.CalledProcessError as e:
return f"Error executing git command: {str(e)}"
except Exception as e:
return f"Unexpected error: {str(e)}"
finally:
# Clean up temporary directory if it was created
if temp_dir and os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
# Example usage
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Usage: python git_readme_utils.py <repo_path_or_url> [commit_depth]")
print(" repo_path_or_url: Path to local repository or URL to remote repository")
print(" commit_depth: Optional - How many commits back to check (default=1 for latest commit)")
sys.exit(1)
repo_source = sys.argv[1]
commit_depth = 1 # Default to latest commit
# Check if commit_depth was provided
if len(sys.argv) > 2:
try:
commit_depth = int(sys.argv[2])
if commit_depth < 1:
print("Error: commit_depth must be a positive integer")
sys.exit(1)
except ValueError:
print("Error: commit_depth must be an integer")
sys.exit(1)
print(f"Getting README for repository: {repo_source} (commit depth: {commit_depth})")
result = get_repo_readme(repo_source, commit_depth)
if isinstance(result, tuple):
readme_file, readme_content = result
print(f"\nFound README file: {readme_file}\n")
print("=" * 80)
print(readme_content)
print("=" * 80)
else:
print(result) # Error message