git-repo-explorer / git_diff_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
def get_latest_commit_diff(repo_source: str, commit_depth: int = 1) -> Union[str, Dict[str, List[str]]]:
"""
Returns the differences made in a specified commit of a git repository.
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 diff for the second-to-last commit.
Returns:
A string containing the git diff output or a dictionary with structured diff information
"""
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}"
# Get the diff from the commit
diff_cmd = ["git", "-C", repo_path, "show", "--name-status", commit_hash]
diff_output = subprocess.check_output(diff_cmd).decode('utf-8')
# For more detailed content diff including the changes
content_diff_cmd = ["git", "-C", repo_path, "show", commit_hash]
content_diff = subprocess.check_output(content_diff_cmd).decode('utf-8')
return content_diff
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 main.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 diff for repository: {repo_source} (commit depth: {commit_depth})")
diff = get_latest_commit_diff(repo_source, commit_depth)
print(diff)