git-repo-explorer / git_diff_utils.py
CalleCaje's picture
feat: create git repository explorer app with diff, tree, and README viewing capabilities
bd89e7e
raw
history blame
4.02 kB
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)