CalleCaje commited on
Commit
bd89e7e
Β·
1 Parent(s): a99b3a1

feat: create git repository explorer app with diff, tree, and README viewing capabilities

Browse files
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
__pycache__/git_diff_utils.cpython-313.pyc ADDED
Binary file (5.26 kB). View file
 
__pycache__/git_readme_utils.cpython-313.pyc ADDED
Binary file (7.32 kB). View file
 
__pycache__/git_tree_utils.cpython-313.pyc ADDED
Binary file (6.36 kB). View file
 
app.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import sys
3
+ from git_diff_utils import get_latest_commit_diff
4
+ from git_tree_utils import get_repo_tree_structure, print_tree
5
+ from git_readme_utils import get_repo_readme
6
+
7
+ def get_diff(repo_source, commit_depth=1):
8
+ """
9
+ Get the diff of a specific commit from a git repository.
10
+
11
+ Args:
12
+ repo_source (str): Path to local repository or URL to remote repository
13
+ commit_depth (int, optional): How many commits back to check (default=1 for latest commit)
14
+
15
+ Returns:
16
+ str: The git diff output formatted as text
17
+ """
18
+ try:
19
+ commit_depth = int(commit_depth)
20
+ if commit_depth < 1:
21
+ return "Error: commit_depth must be a positive integer"
22
+ except ValueError:
23
+ return "Error: commit_depth must be an integer"
24
+
25
+ print(f"Getting diff for repository: {repo_source} (commit depth: {commit_depth})")
26
+ diff = get_latest_commit_diff(repo_source, commit_depth)
27
+ return diff
28
+
29
+ def get_tree(repo_source, commit_depth=1):
30
+ """
31
+ Get the tree structure of a git repository at a specific commit.
32
+
33
+ Args:
34
+ repo_source (str): Path to local repository or URL to remote repository
35
+ commit_depth (int, optional): How many commits back to check (default=1 for latest commit)
36
+
37
+ Returns:
38
+ str: The formatted tree structure as text
39
+ """
40
+ try:
41
+ commit_depth = int(commit_depth)
42
+ if commit_depth < 1:
43
+ return "Error: commit_depth must be a positive integer"
44
+ except ValueError:
45
+ return "Error: commit_depth must be an integer"
46
+
47
+ print(f"Getting tree structure for repository: {repo_source} (commit depth: {commit_depth})")
48
+ tree = get_repo_tree_structure(repo_source, commit_depth)
49
+
50
+ if isinstance(tree, dict):
51
+ # Capture the output of print_tree to a string
52
+ import io
53
+ from contextlib import redirect_stdout
54
+
55
+ f = io.StringIO()
56
+ with redirect_stdout(f):
57
+ print("Repository Structure:")
58
+ print_tree(tree)
59
+ output = f.getvalue()
60
+ return output
61
+ else:
62
+ return tree # Error message
63
+
64
+ def get_readme(repo_source, commit_depth=1):
65
+ """
66
+ Get the README content from a git repository at a specific commit.
67
+
68
+ Args:
69
+ repo_source (str): Path to local repository or URL to remote repository
70
+ commit_depth (int, optional): How many commits back to check (default=1 for latest commit)
71
+
72
+ Returns:
73
+ str: The content of the README file or an error message
74
+ """
75
+ try:
76
+ commit_depth = int(commit_depth)
77
+ if commit_depth < 1:
78
+ return "Error: commit_depth must be a positive integer"
79
+ except ValueError:
80
+ return "Error: commit_depth must be an integer"
81
+
82
+ print(f"Getting README for repository: {repo_source} (commit depth: {commit_depth})")
83
+ result = get_repo_readme(repo_source, commit_depth)
84
+
85
+ if isinstance(result, tuple):
86
+ readme_file, readme_content = result
87
+ return f"README File: {readme_file}\n\n{readme_content}"
88
+ else:
89
+ return result # Error message
90
+
91
+ # Create the Gradio interface
92
+ with gr.Blocks(title="Git Repository Explorer") as demo:
93
+ gr.Markdown("# Git Repository Explorer")
94
+
95
+ # Feature selection
96
+ feature = gr.Radio(
97
+ choices=["View Commit Diff", "View Repository Structure", "View README"],
98
+ value="View Commit Diff",
99
+ label="Select Feature"
100
+ )
101
+
102
+ # Common inputs for all features
103
+ with gr.Row():
104
+ with gr.Column():
105
+ repo_input = gr.Textbox(
106
+ label="Repository Path or URL",
107
+ placeholder="Enter local path or remote URL (e.g., https://github.com/username/repo.git)",
108
+ lines=1
109
+ )
110
+
111
+ depth_input = gr.Number(
112
+ label="Commit Depth",
113
+ value=1,
114
+ minimum=1,
115
+ step=1,
116
+ info="How many commits back to check (1 = latest commit)"
117
+ )
118
+
119
+ submit_btn = gr.Button("Execute", variant="primary")
120
+
121
+ # Output area
122
+ output = gr.TextArea(
123
+ label="Output",
124
+ lines=20,
125
+ max_lines=50,
126
+ show_copy_button=True
127
+ )
128
+
129
+ # Function to handle feature selection and execution
130
+ def process_request(feature_selection, repo_source, commit_depth):
131
+ if feature_selection == "View Commit Diff":
132
+ return get_diff(repo_source, commit_depth)
133
+ elif feature_selection == "View Repository Structure":
134
+ return get_tree(repo_source, commit_depth)
135
+ elif feature_selection == "View README":
136
+ return get_readme(repo_source, commit_depth)
137
+ return "Please select a feature"
138
+
139
+ # Update button text based on feature selection
140
+ def update_button_text(feature_selection):
141
+ if feature_selection == "View Commit Diff":
142
+ return "Get Diff"
143
+ elif feature_selection == "View Repository Structure":
144
+ return "Get Tree Structure"
145
+ elif feature_selection == "View README":
146
+ return "Get README"
147
+ return "Execute"
148
+
149
+ # Update output label based on feature selection
150
+ def update_output_label(feature_selection):
151
+ if feature_selection == "View Commit Diff":
152
+ return "Diff Output"
153
+ elif feature_selection == "View Repository Structure":
154
+ return "Repository Structure"
155
+ elif feature_selection == "View README":
156
+ return "README Content"
157
+ return "Output"
158
+
159
+ # Set up events
160
+ feature.change(fn=update_button_text, inputs=feature, outputs=submit_btn)
161
+ feature.change(fn=update_output_label, inputs=feature, outputs=output)
162
+
163
+ submit_btn.click(
164
+ fn=process_request,
165
+ inputs=[feature, repo_input, depth_input],
166
+ outputs=output
167
+ )
168
+
169
+ gr.Markdown("""
170
+ ### Instructions:
171
+ 1. Select the feature you want to use
172
+ 2. Enter a git repository path or URL
173
+ 3. Specify how many commits back to check (default: 1 for the latest commit)
174
+ 4. Click the execute button
175
+
176
+ This tool can handle both local repositories and remote URLs automatically.
177
+
178
+ #### Features:
179
+ - **View Commit Diff**: Shows the differences made in a specific commit
180
+ - **View Repository Structure**: Displays the tree structure of the repository at a specific commit
181
+ - **View README**: Shows the content of the README file if present in the repository
182
+ """)
183
+
184
+ # Launch the app with MCP server enabled
185
+ if __name__ == "__main__":
186
+ #demo.launch(share=True, mcp_server=True)
187
+ demo.launch(share=False, server_name="0.0.0.0", server_port=7860, mcp_server=True)
git_diff_utils.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import shutil
4
+ import os
5
+ from typing import Optional, Union, Dict, List
6
+
7
+
8
+ def get_latest_commit_diff(repo_source: str, commit_depth: int = 1) -> Union[str, Dict[str, List[str]]]:
9
+ """
10
+ Returns the differences made in a specified commit of a git repository.
11
+
12
+ Args:
13
+ repo_source: Path to the git repository or URL to a remote repository.
14
+ Function automatically detects if it's a local path or remote URL.
15
+ commit_depth: How many commits back to check (default=1 for the latest commit).
16
+ For example, 2 would return the diff for the second-to-last commit.
17
+
18
+ Returns:
19
+ A string containing the git diff output or a dictionary with structured diff information
20
+ """
21
+ temp_dir = None
22
+ try:
23
+ # Determine if repo_source is a remote URL or local path
24
+ is_remote = repo_source.startswith(('http://', 'https://', 'git://', 'ssh://')) or repo_source.endswith('.git')
25
+
26
+ # If it's a remote repository, clone it to a temporary directory
27
+ if is_remote:
28
+ temp_dir = tempfile.mkdtemp()
29
+ print(f"Cloning remote repository: {repo_source}")
30
+ clone_cmd = ["git", "clone", repo_source, temp_dir]
31
+ subprocess.check_output(clone_cmd)
32
+ repo_path = temp_dir
33
+ else:
34
+ repo_path = repo_source
35
+
36
+ # Validate commit_depth parameter
37
+ if commit_depth < 1:
38
+ return "Error: commit_depth must be a positive integer"
39
+
40
+ # Get the commit hash for the specified depth
41
+ if commit_depth == 1:
42
+ # For the latest commit
43
+ commit_ref = "HEAD"
44
+ else:
45
+ # For earlier commits: HEAD~1, HEAD~2, etc. (0-indexed in git, so we subtract 1)
46
+ commit_ref = f"HEAD~{commit_depth-1}"
47
+
48
+ # Get the commit hash
49
+ commit_cmd = ["git", "-C", repo_path, "rev-parse", commit_ref]
50
+ try:
51
+ commit_hash = subprocess.check_output(commit_cmd).decode('utf-8').strip()
52
+ except subprocess.CalledProcessError:
53
+ return f"Error: Could not find commit at depth {commit_depth}"
54
+
55
+ # Get the diff from the commit
56
+ diff_cmd = ["git", "-C", repo_path, "show", "--name-status", commit_hash]
57
+ diff_output = subprocess.check_output(diff_cmd).decode('utf-8')
58
+
59
+ # For more detailed content diff including the changes
60
+ content_diff_cmd = ["git", "-C", repo_path, "show", commit_hash]
61
+ content_diff = subprocess.check_output(content_diff_cmd).decode('utf-8')
62
+
63
+ return content_diff
64
+
65
+ except subprocess.CalledProcessError as e:
66
+ return f"Error executing git command: {str(e)}"
67
+ except Exception as e:
68
+ return f"Unexpected error: {str(e)}"
69
+ finally:
70
+ # Clean up temporary directory if it was created
71
+ if temp_dir and os.path.exists(temp_dir):
72
+ shutil.rmtree(temp_dir)
73
+
74
+
75
+ # Example usage
76
+ if __name__ == "__main__":
77
+ import sys
78
+
79
+ if len(sys.argv) < 2:
80
+ print("Usage: python main.py <repo_path_or_url> [commit_depth]")
81
+ print(" repo_path_or_url: Path to local repository or URL to remote repository")
82
+ print(" commit_depth: Optional - How many commits back to check (default=1 for latest commit)")
83
+ sys.exit(1)
84
+
85
+ repo_source = sys.argv[1]
86
+ commit_depth = 1 # Default to latest commit
87
+
88
+ # Check if commit_depth was provided
89
+ if len(sys.argv) > 2:
90
+ try:
91
+ commit_depth = int(sys.argv[2])
92
+ if commit_depth < 1:
93
+ print("Error: commit_depth must be a positive integer")
94
+ sys.exit(1)
95
+ except ValueError:
96
+ print("Error: commit_depth must be an integer")
97
+ sys.exit(1)
98
+
99
+ print(f"Getting diff for repository: {repo_source} (commit depth: {commit_depth})")
100
+ diff = get_latest_commit_diff(repo_source, commit_depth)
101
+ print(diff)
git_readme_utils.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import shutil
4
+ import os
5
+ from typing import Optional, Union, Dict, List, Tuple
6
+
7
+
8
+ def get_repo_readme(repo_source: str, commit_depth: int = 1) -> Union[Tuple[str, str], str]:
9
+ """
10
+ Returns the README content of a git repository at a specific commit.
11
+
12
+ Args:
13
+ repo_source: Path to the git repository or URL to a remote repository.
14
+ Function automatically detects if it's a local path or remote URL.
15
+ commit_depth: How many commits back to check (default=1 for the latest commit).
16
+ For example, 2 would return the README from the second-to-last commit.
17
+
18
+ Returns:
19
+ A tuple containing (readme_filename, readme_content) if README is found,
20
+ or an error string if something fails or no README is found
21
+ """
22
+ temp_dir = None
23
+ try:
24
+ # Determine if repo_source is a remote URL or local path
25
+ is_remote = repo_source.startswith(('http://', 'https://', 'git://', 'ssh://')) or repo_source.endswith('.git')
26
+
27
+ # If it's a remote repository, clone it to a temporary directory
28
+ if is_remote:
29
+ temp_dir = tempfile.mkdtemp()
30
+ print(f"Cloning remote repository: {repo_source}")
31
+ clone_cmd = ["git", "clone", repo_source, temp_dir]
32
+ subprocess.check_output(clone_cmd)
33
+ repo_path = temp_dir
34
+ else:
35
+ repo_path = repo_source
36
+
37
+ # Validate commit_depth parameter
38
+ if commit_depth < 1:
39
+ return "Error: commit_depth must be a positive integer"
40
+
41
+ # Get the commit hash for the specified depth
42
+ if commit_depth == 1:
43
+ # For the latest commit
44
+ commit_ref = "HEAD"
45
+ else:
46
+ # For earlier commits: HEAD~1, HEAD~2, etc. (0-indexed in git, so we subtract 1)
47
+ commit_ref = f"HEAD~{commit_depth-1}"
48
+
49
+ # Get the commit hash
50
+ commit_cmd = ["git", "-C", repo_path, "rev-parse", commit_ref]
51
+ try:
52
+ commit_hash = subprocess.check_output(commit_cmd).decode('utf-8').strip()
53
+ except subprocess.CalledProcessError:
54
+ return f"Error: Could not find commit at depth {commit_depth}"
55
+
56
+ # Common README file patterns to check
57
+ readme_patterns = [
58
+ "README.md",
59
+ "README.txt",
60
+ "README",
61
+ "readme.md",
62
+ "Readme.md",
63
+ "ReadMe.md",
64
+ "readme.txt",
65
+ "README.markdown",
66
+ "readme.markdown"
67
+ ]
68
+
69
+ # Check for the existence of README files
70
+ ls_files_cmd = ["git", "-C", repo_path, "ls-tree", "--name-only", commit_hash]
71
+ files = subprocess.check_output(ls_files_cmd).decode('utf-8').strip().split('\n')
72
+
73
+ readme_file = None
74
+ for pattern in readme_patterns:
75
+ if pattern in files:
76
+ readme_file = pattern
77
+ break
78
+
79
+ # Also check in the root directory
80
+ if not readme_file:
81
+ for pattern in readme_patterns:
82
+ if any(f == pattern or f.endswith(f"/{pattern}") for f in files):
83
+ readme_file = next((f for f in files if f == pattern or f.endswith(f"/{pattern}")), None)
84
+ break
85
+
86
+ if not readme_file:
87
+ return "No README file found in the repository"
88
+
89
+ # Get the content of the README file at the specified commit
90
+ show_cmd = ["git", "-C", repo_path, "show", f"{commit_hash}:{readme_file}"]
91
+ try:
92
+ readme_content = subprocess.check_output(show_cmd).decode('utf-8')
93
+ return (readme_file, readme_content)
94
+ except subprocess.CalledProcessError:
95
+ return f"Error: Could not read {readme_file} at commit {commit_hash}"
96
+
97
+ except subprocess.CalledProcessError as e:
98
+ return f"Error executing git command: {str(e)}"
99
+ except Exception as e:
100
+ return f"Unexpected error: {str(e)}"
101
+ finally:
102
+ # Clean up temporary directory if it was created
103
+ if temp_dir and os.path.exists(temp_dir):
104
+ shutil.rmtree(temp_dir)
105
+
106
+
107
+ # Example usage
108
+ if __name__ == "__main__":
109
+ import sys
110
+
111
+ if len(sys.argv) < 2:
112
+ print("Usage: python git_readme_utils.py <repo_path_or_url> [commit_depth]")
113
+ print(" repo_path_or_url: Path to local repository or URL to remote repository")
114
+ print(" commit_depth: Optional - How many commits back to check (default=1 for latest commit)")
115
+ sys.exit(1)
116
+
117
+ repo_source = sys.argv[1]
118
+ commit_depth = 1 # Default to latest commit
119
+
120
+ # Check if commit_depth was provided
121
+ if len(sys.argv) > 2:
122
+ try:
123
+ commit_depth = int(sys.argv[2])
124
+ if commit_depth < 1:
125
+ print("Error: commit_depth must be a positive integer")
126
+ sys.exit(1)
127
+ except ValueError:
128
+ print("Error: commit_depth must be an integer")
129
+ sys.exit(1)
130
+
131
+ print(f"Getting README for repository: {repo_source} (commit depth: {commit_depth})")
132
+ result = get_repo_readme(repo_source, commit_depth)
133
+
134
+ if isinstance(result, tuple):
135
+ readme_file, readme_content = result
136
+ print(f"\nFound README file: {readme_file}\n")
137
+ print("=" * 80)
138
+ print(readme_content)
139
+ print("=" * 80)
140
+ else:
141
+ print(result) # Error message
git_tree_utils.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import shutil
4
+ import os
5
+ from typing import Optional, Union, Dict, List, Any
6
+
7
+
8
+ def get_repo_tree_structure(repo_source: str, commit_depth: int = 1) -> Union[Dict[str, Any], str]:
9
+ """
10
+ Returns the tree structure of a git repository at a specific commit.
11
+
12
+ Args:
13
+ repo_source: Path to the git repository or URL to a remote repository.
14
+ Function automatically detects if it's a local path or remote URL.
15
+ commit_depth: How many commits back to check (default=1 for the latest commit).
16
+ For example, 2 would return the tree for the second-to-last commit.
17
+
18
+ Returns:
19
+ A dictionary representing the repository structure tree or error string if something fails
20
+ """
21
+ temp_dir = None
22
+ try:
23
+ # Determine if repo_source is a remote URL or local path
24
+ is_remote = repo_source.startswith(('http://', 'https://', 'git://', 'ssh://')) or repo_source.endswith('.git')
25
+
26
+ # If it's a remote repository, clone it to a temporary directory
27
+ if is_remote:
28
+ temp_dir = tempfile.mkdtemp()
29
+ print(f"Cloning remote repository: {repo_source}")
30
+ clone_cmd = ["git", "clone", repo_source, temp_dir]
31
+ subprocess.check_output(clone_cmd)
32
+ repo_path = temp_dir
33
+ else:
34
+ repo_path = repo_source
35
+
36
+ # Validate commit_depth parameter
37
+ if commit_depth < 1:
38
+ return "Error: commit_depth must be a positive integer"
39
+
40
+ # Get the commit hash for the specified depth
41
+ if commit_depth == 1:
42
+ # For the latest commit
43
+ commit_ref = "HEAD"
44
+ else:
45
+ # For earlier commits: HEAD~1, HEAD~2, etc. (0-indexed in git, so we subtract 1)
46
+ commit_ref = f"HEAD~{commit_depth-1}"
47
+
48
+ # Get the commit hash
49
+ commit_cmd = ["git", "-C", repo_path, "rev-parse", commit_ref]
50
+ try:
51
+ commit_hash = subprocess.check_output(commit_cmd).decode('utf-8').strip()
52
+ except subprocess.CalledProcessError:
53
+ return f"Error: Could not find commit at depth {commit_depth}"
54
+
55
+ # Use git ls-tree recursively to get the repository structure
56
+ ls_tree_cmd = ["git", "-C", repo_path, "ls-tree", "-r", "--name-only", commit_hash]
57
+ file_list = subprocess.check_output(ls_tree_cmd).decode('utf-8').strip().split('\n')
58
+
59
+ # Build a tree structure from the file paths
60
+ root = {}
61
+ for file_path in file_list:
62
+ if not file_path: # Skip empty paths
63
+ continue
64
+
65
+ parts = file_path.split('/')
66
+ current = root
67
+
68
+ # Navigate through path components, creating nested dictionaries as needed
69
+ for i, part in enumerate(parts):
70
+ if i == len(parts) - 1: # It's a file (last part of path)
71
+ current[part] = None # Files have None value
72
+ else: # It's a directory
73
+ if part not in current:
74
+ current[part] = {} # Create directory dict if it doesn't exist
75
+ current = current[part] # Move into the directory
76
+
77
+ return root
78
+
79
+ except subprocess.CalledProcessError as e:
80
+ return f"Error executing git command: {str(e)}"
81
+ except Exception as e:
82
+ return f"Unexpected error: {str(e)}"
83
+ finally:
84
+ # Clean up temporary directory if it was created
85
+ if temp_dir and os.path.exists(temp_dir):
86
+ shutil.rmtree(temp_dir)
87
+
88
+
89
+ # Helper function to print the tree structure in a more readable format
90
+ def print_tree(tree, indent=""):
91
+ """
92
+ Print the repository tree structure in a readable format
93
+
94
+ Args:
95
+ tree: Dictionary representing the repository structure
96
+ indent: Current indentation level string (used in recursion)
97
+ """
98
+ for key, value in sorted(tree.items()):
99
+ if value is None: # It's a file
100
+ print(f"{indent}β”œβ”€β”€ {key}")
101
+ else: # It's a directory
102
+ print(f"{indent}β”œβ”€β”€ {key}/")
103
+ print_tree(value, indent + "β”‚ ")
104
+
105
+
106
+ # Example usage
107
+ if __name__ == "__main__":
108
+ import sys
109
+
110
+ if len(sys.argv) < 2:
111
+ print("Usage: python git_tree_utils.py <repo_path_or_url> [commit_depth]")
112
+ print(" repo_path_or_url: Path to local repository or URL to remote repository")
113
+ print(" commit_depth: Optional - How many commits back to check (default=1 for latest commit)")
114
+ sys.exit(1)
115
+
116
+ repo_source = sys.argv[1]
117
+ commit_depth = 1 # Default to latest commit
118
+
119
+ # Check if commit_depth was provided
120
+ if len(sys.argv) > 2:
121
+ try:
122
+ commit_depth = int(sys.argv[2])
123
+ if commit_depth < 1:
124
+ print("Error: commit_depth must be a positive integer")
125
+ sys.exit(1)
126
+ except ValueError:
127
+ print("Error: commit_depth must be an integer")
128
+ sys.exit(1)
129
+
130
+ print(f"Getting repository structure for: {repo_source} (commit depth: {commit_depth})")
131
+ tree = get_repo_tree_structure(repo_source, commit_depth)
132
+
133
+ if isinstance(tree, dict):
134
+ print("\nRepository Structure:")
135
+ print_tree(tree)
136
+ else:
137
+ print(tree) # Error message
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio>=4.0.0
2
+ GitPython