reposnap 0.3.2__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reposnap/__init__.py +0 -4
- reposnap/controllers/project_controller.py +54 -6
- reposnap/core/markdown_generator.py +3 -3
- reposnap/interfaces/cli.py +8 -1
- reposnap/interfaces/gui.py +155 -0
- reposnap/models/file_tree.py +24 -0
- reposnap/utils/path_utils.py +5 -3
- reposnap-0.5.0.dist-info/METADATA +150 -0
- reposnap-0.5.0.dist-info/RECORD +19 -0
- reposnap-0.5.0.dist-info/entry_points.txt +3 -0
- reposnap-0.3.2.dist-info/METADATA +0 -88
- reposnap-0.3.2.dist-info/RECORD +0 -18
- reposnap-0.3.2.dist-info/entry_points.txt +0 -2
- {reposnap-0.3.2.dist-info → reposnap-0.5.0.dist-info}/WHEEL +0 -0
- {reposnap-0.3.2.dist-info → reposnap-0.5.0.dist-info}/licenses/LICENSE +0 -0
reposnap/__init__.py
CHANGED
@@ -11,14 +11,25 @@ from typing import List, Optional
|
|
11
11
|
|
12
12
|
|
13
13
|
class ProjectController:
|
14
|
-
def __init__(self, args: object):
|
14
|
+
def __init__(self, args: Optional[object] = None):
|
15
15
|
self.logger = logging.getLogger(__name__)
|
16
|
-
self.root_dir: Path = Path(args.path).resolve()
|
17
|
-
self.output_file: Path = Path(args.output).resolve()
|
18
|
-
self.structure_only: bool = args.structure_only
|
16
|
+
self.root_dir: Path = Path(args.path).resolve() if args else Path('.').resolve()
|
17
|
+
self.output_file: Path = Path(args.output).resolve() if args else Path('output.md').resolve()
|
18
|
+
self.structure_only: bool = args.structure_only if args else False
|
19
19
|
self.args: object = args
|
20
20
|
self.file_tree: Optional[FileTree] = None
|
21
|
-
self.gitignore_patterns: List[str] =
|
21
|
+
self.gitignore_patterns: List[str] = []
|
22
|
+
self.include_patterns: List[str] = args.include if args and hasattr(args, 'include') else []
|
23
|
+
self.exclude_patterns: List[str] = args.exclude if args and hasattr(args, 'exclude') else []
|
24
|
+
if self.root_dir:
|
25
|
+
self.gitignore_patterns = self._load_gitignore_patterns()
|
26
|
+
|
27
|
+
def set_root_dir(self, root_dir: Path) -> None:
|
28
|
+
self.root_dir = root_dir
|
29
|
+
self.gitignore_patterns = self._load_gitignore_patterns()
|
30
|
+
|
31
|
+
def get_file_tree(self) -> Optional[FileTree]:
|
32
|
+
return self.file_tree
|
22
33
|
|
23
34
|
def run(self) -> None:
|
24
35
|
self.collect_file_tree()
|
@@ -31,6 +42,30 @@ class ProjectController:
|
|
31
42
|
git_files: List[Path] = git_repo.get_git_files()
|
32
43
|
self.logger.debug(f"Git files before filtering: {git_files}")
|
33
44
|
|
45
|
+
# Adjust patterns
|
46
|
+
def adjust_patterns(patterns):
|
47
|
+
adjusted = []
|
48
|
+
for pattern in patterns:
|
49
|
+
if '*' in pattern or '?' in pattern or '[' in pattern:
|
50
|
+
adjusted.append(pattern)
|
51
|
+
else:
|
52
|
+
adjusted.append(f'*{pattern}*')
|
53
|
+
return adjusted
|
54
|
+
|
55
|
+
# Apply include patterns
|
56
|
+
if self.include_patterns:
|
57
|
+
adjusted_include_patterns = adjust_patterns(self.include_patterns)
|
58
|
+
include_spec = pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, adjusted_include_patterns)
|
59
|
+
git_files = [f for f in git_files if include_spec.match_file(f.as_posix())]
|
60
|
+
self.logger.debug(f"Git files after include patterns: {git_files}")
|
61
|
+
|
62
|
+
# Apply exclude patterns
|
63
|
+
if self.exclude_patterns:
|
64
|
+
adjusted_exclude_patterns = adjust_patterns(self.exclude_patterns)
|
65
|
+
exclude_spec = pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, adjusted_exclude_patterns)
|
66
|
+
git_files = [f for f in git_files if not exclude_spec.match_file(f.as_posix())]
|
67
|
+
self.logger.debug(f"Git files after exclude patterns: {git_files}")
|
68
|
+
|
34
69
|
self.logger.info("Building tree structure.")
|
35
70
|
file_system: FileSystem = FileSystem(self.root_dir)
|
36
71
|
tree_structure: dict = file_system.build_tree_structure(git_files)
|
@@ -52,7 +87,20 @@ class ProjectController:
|
|
52
87
|
structure_only=self.structure_only
|
53
88
|
)
|
54
89
|
markdown_generator.generate_markdown(self.file_tree.structure, self.file_tree.get_all_files())
|
55
|
-
self.logger.info("Markdown
|
90
|
+
self.logger.info(f"Markdown generated at {self.output_file}.")
|
91
|
+
|
92
|
+
def generate_output_from_selected(self, selected_files: set) -> None:
|
93
|
+
self.logger.info("Generating markdown from selected files.")
|
94
|
+
# Build a pruned tree structure based on selected files
|
95
|
+
pruned_tree = self.file_tree.prune_tree(selected_files)
|
96
|
+
markdown_generator: MarkdownGenerator = MarkdownGenerator(
|
97
|
+
root_dir=self.root_dir,
|
98
|
+
output_file=self.output_file,
|
99
|
+
structure_only=False,
|
100
|
+
hide_untoggled=True
|
101
|
+
)
|
102
|
+
markdown_generator.generate_markdown(pruned_tree, [Path(f) for f in selected_files])
|
103
|
+
self.logger.info(f"Markdown generated at {self.output_file}.")
|
56
104
|
|
57
105
|
def _load_gitignore_patterns(self) -> List[str]:
|
58
106
|
gitignore_path: Path = self.root_dir / '.gitignore'
|
@@ -7,10 +7,11 @@ from typing import List, Dict, Any
|
|
7
7
|
|
8
8
|
|
9
9
|
class MarkdownGenerator:
|
10
|
-
def __init__(self, root_dir: Path, output_file: Path, structure_only: bool = False):
|
10
|
+
def __init__(self, root_dir: Path, output_file: Path, structure_only: bool = False, hide_untoggled: bool = False):
|
11
11
|
self.root_dir: Path = root_dir.resolve()
|
12
12
|
self.output_file: Path = output_file.resolve()
|
13
13
|
self.structure_only: bool = structure_only
|
14
|
+
self.hide_untoggled: bool = hide_untoggled
|
14
15
|
self.logger = logging.getLogger(__name__)
|
15
16
|
|
16
17
|
def generate_markdown(self, tree_structure: Dict[str, Any], files: List[Path]) -> None:
|
@@ -34,7 +35,7 @@ class MarkdownGenerator:
|
|
34
35
|
with self.output_file.open('w', encoding='utf-8') as f:
|
35
36
|
f.write("# Project Structure\n\n")
|
36
37
|
f.write("```\n")
|
37
|
-
for line in format_tree(tree_structure):
|
38
|
+
for line in format_tree(tree_structure, hide_untoggled=self.hide_untoggled):
|
38
39
|
f.write(line)
|
39
40
|
f.write("```\n\n")
|
40
41
|
self.logger.debug("Header and project structure written successfully.")
|
@@ -72,4 +73,3 @@ class MarkdownGenerator:
|
|
72
73
|
f.write(f"{content}\n```\n\n")
|
73
74
|
except IOError as e:
|
74
75
|
self.logger.error(f"Error reading or writing file {file_path}: {e}")
|
75
|
-
|
reposnap/interfaces/cli.py
CHANGED
@@ -7,12 +7,18 @@ from reposnap.controllers.project_controller import ProjectController
|
|
7
7
|
|
8
8
|
def main():
|
9
9
|
parser = argparse.ArgumentParser(description='Generate a Markdown representation of a Git repository.')
|
10
|
-
parser.add_argument('path', help='Path to the Git repository or subdirectory.')
|
10
|
+
parser.add_argument('path', nargs='?', default='.', help='Path to the Git repository or subdirectory.')
|
11
11
|
parser.add_argument('-o', '--output', help='Output Markdown file', default='output.md')
|
12
12
|
parser.add_argument('--structure-only', action='store_true',
|
13
13
|
help='Only include the file structure without content.')
|
14
14
|
parser.add_argument('--debug', action='store_true', help='Enable debug-level logging.')
|
15
15
|
|
16
|
+
# New arguments for include and exclude patterns
|
17
|
+
parser.add_argument('-i', '--include', nargs='*', default=[],
|
18
|
+
help='File/folder patterns to include.')
|
19
|
+
parser.add_argument('-e', '--exclude', nargs='*', default=[],
|
20
|
+
help='File/folder patterns to exclude.')
|
21
|
+
|
16
22
|
args = parser.parse_args()
|
17
23
|
|
18
24
|
log_level = logging.DEBUG if args.debug else logging.INFO
|
@@ -21,5 +27,6 @@ def main():
|
|
21
27
|
controller = ProjectController(args)
|
22
28
|
controller.run()
|
23
29
|
|
30
|
+
|
24
31
|
if __name__ == "__main__":
|
25
32
|
main()
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# src/reposnap/interfaces/gui.py
|
2
|
+
|
3
|
+
import urwid
|
4
|
+
from pathlib import Path
|
5
|
+
from reposnap.controllers.project_controller import ProjectController
|
6
|
+
|
7
|
+
|
8
|
+
class MyCheckBox(urwid.CheckBox):
|
9
|
+
def __init__(self, label, user_data=None, **kwargs):
|
10
|
+
super().__init__(label, **kwargs)
|
11
|
+
self.user_data = user_data
|
12
|
+
|
13
|
+
|
14
|
+
class RepoSnapGUI:
|
15
|
+
def __init__(self):
|
16
|
+
self.controller = ProjectController()
|
17
|
+
self.root_dir = Path('.').resolve()
|
18
|
+
self.file_tree = None
|
19
|
+
self.selected_files = set()
|
20
|
+
|
21
|
+
self.main_loop = None
|
22
|
+
self.build_main_menu()
|
23
|
+
|
24
|
+
def build_main_menu(self):
|
25
|
+
self.root_dir_edit = urwid.Edit(('bold', "Root Directory: "), str(self.root_dir))
|
26
|
+
scan_button = urwid.Button("Scan", on_press=self.on_scan)
|
27
|
+
|
28
|
+
main_menu = urwid.Frame(
|
29
|
+
header=urwid.Text(('bold', "RepoSnap - Main Menu")),
|
30
|
+
body=urwid.Padding(
|
31
|
+
urwid.LineBox(
|
32
|
+
urwid.ListBox(
|
33
|
+
urwid.SimpleFocusListWalker([
|
34
|
+
self.root_dir_edit
|
35
|
+
])
|
36
|
+
),
|
37
|
+
title="Enter Root Directory"
|
38
|
+
),
|
39
|
+
left=2, right=2
|
40
|
+
),
|
41
|
+
footer=urwid.Padding(scan_button, align='center')
|
42
|
+
)
|
43
|
+
|
44
|
+
self.main_widget = main_menu
|
45
|
+
|
46
|
+
def on_scan(self, button):
|
47
|
+
self.root_dir = Path(self.root_dir_edit.edit_text).resolve()
|
48
|
+
self.controller.set_root_dir(self.root_dir)
|
49
|
+
self.controller.collect_file_tree()
|
50
|
+
self.file_tree = self.controller.get_file_tree()
|
51
|
+
self.build_file_tree_menu()
|
52
|
+
|
53
|
+
def build_file_tree_menu(self):
|
54
|
+
tree_widgets = self.build_tree_widget(self.file_tree.structure)
|
55
|
+
tree_listbox = urwid.ListBox(urwid.SimpleFocusListWalker(tree_widgets))
|
56
|
+
render_button = urwid.Button("Render", on_press=self.on_render)
|
57
|
+
|
58
|
+
tree_menu = urwid.Frame(
|
59
|
+
header=urwid.Text(('bold', f"File Tree of {self.root_dir}")),
|
60
|
+
body=urwid.LineBox(tree_listbox),
|
61
|
+
footer=urwid.Padding(render_button, align='center')
|
62
|
+
)
|
63
|
+
|
64
|
+
self.main_widget = tree_menu
|
65
|
+
self.refresh()
|
66
|
+
|
67
|
+
def build_tree_widget(self, tree_structure, parent_path="", level=0):
|
68
|
+
widgets = []
|
69
|
+
for key, value in sorted(tree_structure.items()):
|
70
|
+
node_path = f"{parent_path}/{key}".lstrip('/')
|
71
|
+
checkbox = MyCheckBox(
|
72
|
+
key,
|
73
|
+
user_data={'path': node_path, 'level': level},
|
74
|
+
state=False,
|
75
|
+
on_state_change=self.on_checkbox_change
|
76
|
+
)
|
77
|
+
indented_checkbox = urwid.Padding(checkbox, left=4*level)
|
78
|
+
widgets.append(indented_checkbox)
|
79
|
+
if isinstance(value, dict):
|
80
|
+
widgets.extend(self.build_tree_widget(value, node_path, level=level+1))
|
81
|
+
return widgets
|
82
|
+
|
83
|
+
def on_checkbox_change(self, checkbox, state):
|
84
|
+
user_data = checkbox.user_data
|
85
|
+
node_path = user_data['path']
|
86
|
+
level = user_data['level']
|
87
|
+
if state:
|
88
|
+
self.selected_files.add(node_path)
|
89
|
+
else:
|
90
|
+
self.selected_files.discard(node_path)
|
91
|
+
# Handle toggling all children
|
92
|
+
self.toggle_children(checkbox, state, level)
|
93
|
+
|
94
|
+
def toggle_children(self, checkbox, state, level):
|
95
|
+
listbox = self.main_widget.body.original_widget.body
|
96
|
+
walker = listbox
|
97
|
+
# Find the index of the Padding widget that contains the checkbox
|
98
|
+
idx = None
|
99
|
+
for i, widget in enumerate(walker):
|
100
|
+
if isinstance(widget, urwid.Padding) and widget.original_widget == checkbox:
|
101
|
+
idx = i
|
102
|
+
break
|
103
|
+
if idx is None:
|
104
|
+
return
|
105
|
+
idx += 1
|
106
|
+
while idx < len(walker):
|
107
|
+
widget = walker[idx]
|
108
|
+
if isinstance(widget, urwid.Padding):
|
109
|
+
checkbox_widget = widget.original_widget
|
110
|
+
widget_user_data = checkbox_widget.user_data
|
111
|
+
widget_level = widget_user_data['level']
|
112
|
+
if widget_level > level:
|
113
|
+
checkbox_widget.set_state(state, do_callback=False)
|
114
|
+
node_path = widget_user_data['path']
|
115
|
+
if state:
|
116
|
+
self.selected_files.add(node_path)
|
117
|
+
else:
|
118
|
+
self.selected_files.discard(node_path)
|
119
|
+
idx += 1
|
120
|
+
else:
|
121
|
+
break
|
122
|
+
else:
|
123
|
+
idx += 1
|
124
|
+
|
125
|
+
def on_render(self, button):
|
126
|
+
self.controller.generate_output_from_selected(self.selected_files)
|
127
|
+
message = urwid.Text(('bold', f"Markdown generated at {self.controller.output_file}"))
|
128
|
+
exit_button = urwid.Button("Exit", on_press=self.exit_program)
|
129
|
+
result_menu = urwid.Frame(
|
130
|
+
header=urwid.Text(('bold', "Success")),
|
131
|
+
body=urwid.Filler(message, valign='middle'),
|
132
|
+
footer=urwid.Padding(exit_button, align='center')
|
133
|
+
)
|
134
|
+
self.main_widget = result_menu
|
135
|
+
self.refresh()
|
136
|
+
|
137
|
+
def refresh(self):
|
138
|
+
if self.main_loop:
|
139
|
+
self.main_loop.widget = self.main_widget
|
140
|
+
|
141
|
+
def exit_program(self, button):
|
142
|
+
raise urwid.ExitMainLoop()
|
143
|
+
|
144
|
+
def run(self):
|
145
|
+
self.main_loop = urwid.MainLoop(self.main_widget)
|
146
|
+
self.main_loop.run()
|
147
|
+
|
148
|
+
|
149
|
+
def main():
|
150
|
+
app = RepoSnapGUI()
|
151
|
+
app.run()
|
152
|
+
|
153
|
+
|
154
|
+
if __name__ == "__main__":
|
155
|
+
main()
|
reposnap/models/file_tree.py
CHANGED
@@ -52,3 +52,27 @@ class FileTree:
|
|
52
52
|
filtered_subtree[key] = value
|
53
53
|
return filtered_subtree
|
54
54
|
|
55
|
+
def prune_tree(self, selected_files: set) -> Dict[str, Any]:
|
56
|
+
"""
|
57
|
+
Prunes the tree to include only the selected files and their directories.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
selected_files (set): Set of selected file paths.
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
Dict[str, Any]: Pruned tree structure.
|
64
|
+
"""
|
65
|
+
return self._prune_tree(self.structure, selected_files)
|
66
|
+
|
67
|
+
def _prune_tree(self, subtree: Dict[str, Any], selected_files: set, path_prefix: str = '') -> Dict[str, Any]:
|
68
|
+
pruned_subtree: Dict[str, Any] = {}
|
69
|
+
for key, value in subtree.items():
|
70
|
+
current_path: str = f"{path_prefix}/{key}".lstrip('/')
|
71
|
+
if isinstance(value, dict):
|
72
|
+
pruned_value: Dict[str, Any] = self._prune_tree(value, selected_files, current_path)
|
73
|
+
if pruned_value:
|
74
|
+
pruned_subtree[key] = pruned_value
|
75
|
+
else:
|
76
|
+
if current_path in selected_files:
|
77
|
+
pruned_subtree[key] = value
|
78
|
+
return pruned_subtree
|
reposnap/utils/path_utils.py
CHANGED
@@ -2,10 +2,12 @@
|
|
2
2
|
from typing import Dict, Generator, Any
|
3
3
|
|
4
4
|
|
5
|
-
def format_tree(tree: Dict[str, Any], indent: str = '') -> Generator[str, None, None]:
|
5
|
+
def format_tree(tree: Dict[str, Any], indent: str = '', hide_untoggled: bool = False) -> Generator[str, None, None]:
|
6
6
|
for key, value in tree.items():
|
7
|
-
if
|
7
|
+
if value == '<hidden>':
|
8
|
+
yield f"{indent}<...>\n"
|
9
|
+
elif isinstance(value, dict):
|
8
10
|
yield f"{indent}{key}/\n"
|
9
|
-
yield from format_tree(value, indent + ' ')
|
11
|
+
yield from format_tree(value, indent + ' ', hide_untoggled)
|
10
12
|
else:
|
11
13
|
yield f"{indent}{key}\n"
|
@@ -0,0 +1,150 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: reposnap
|
3
|
+
Version: 0.5.0
|
4
|
+
Summary: Generate a Markdown file with all contents of your project
|
5
|
+
Author: agoloborodko
|
6
|
+
License-File: LICENSE
|
7
|
+
Requires-Python: >=3.8
|
8
|
+
Requires-Dist: gitpython>=3.1.43
|
9
|
+
Requires-Dist: pathlib>=1.0.1
|
10
|
+
Requires-Dist: pathspec>=0.12.1
|
11
|
+
Requires-Dist: urwid>=2.6.15
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
|
14
|
+
# RepoSnap
|
15
|
+
|
16
|
+
## Overview
|
17
|
+
|
18
|
+
`reposnap` is a Python tool designed to generate a Markdown file that documents the structure and contents of a Git project. It provides both a command-line interface (CLI) and a graphical user interface (GUI) for ease of use. This tool is particularly useful for creating a quick overview of a project's file hierarchy, including optional syntax-highlighted code snippets.
|
19
|
+
|
20
|
+
## Features
|
21
|
+
|
22
|
+
- **Command-Line Interface (CLI)**: Quickly generate documentation from the terminal.
|
23
|
+
- **Graphical User Interface (GUI)**: A user-friendly GUI if you want to select files and directories interactively.
|
24
|
+
- **Syntax Highlighting**: Includes syntax highlighting for known file types in the generated Markdown file.
|
25
|
+
- **Structure Only Option**: The `--structure-only` flag can be used to generate the Markdown file with just the directory structure, omitting the contents of the files.
|
26
|
+
- **Gitignore Support**: Automatically respects `.gitignore` patterns to exclude files and directories.
|
27
|
+
- **Include and Exclude Patterns**: Use `--include` and `--exclude` to specify patterns for files and directories to include or exclude.
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
You can install `reposnap` using pip:
|
32
|
+
|
33
|
+
```bash
|
34
|
+
pip install reposnap
|
35
|
+
```
|
36
|
+
|
37
|
+
Alternatively, you can clone the repository and install the required dependencies:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
git clone https://github.com/username/reposnap.git
|
41
|
+
cd reposnap
|
42
|
+
pip install -r requirements.txt
|
43
|
+
```
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
### Command-Line Interface
|
48
|
+
|
49
|
+
To use `reposnap` from the command line, run it with the following options:
|
50
|
+
|
51
|
+
```bash
|
52
|
+
reposnap [-h] [-o OUTPUT] [--structure-only] [--debug] [-i INCLUDE [INCLUDE ...]] [-e EXCLUDE [EXCLUDE ...]] path
|
53
|
+
```
|
54
|
+
|
55
|
+
- `path`: Path to the Git repository or subdirectory.
|
56
|
+
- `-h, --help`: Show help message and exit.
|
57
|
+
- `-o, --output`: The name of the output Markdown file. Defaults to `output.md`.
|
58
|
+
- `--structure-only`: Generate a Markdown file that includes only the project structure, without file contents.
|
59
|
+
- `--debug`: Enable debug-level logging.
|
60
|
+
- `-i, --include`: File/folder patterns to include. For example, `-i "*.py"` includes only Python files.
|
61
|
+
- `-e, --exclude`: File/folder patterns to exclude. For example, `-e "*.md"` excludes all Markdown files.
|
62
|
+
|
63
|
+
#### Pattern Matching
|
64
|
+
|
65
|
+
- **Pattern Interpretation**: Patterns follow gitignore-style syntax but with a twist.
|
66
|
+
- **Patterns without Wildcards**: If a pattern does not contain any wildcard characters (`*`, `?`, or `[`), it is treated as `*pattern*`. This means it will match any file or directory containing `pattern` in its name.
|
67
|
+
- **Patterns with Wildcards**: If a pattern contains wildcard characters, it retains its original behavior.
|
68
|
+
|
69
|
+
- **Examples**:
|
70
|
+
- `-e "gui"`: Excludes any files or directories containing `"gui"` in their names.
|
71
|
+
- `-i "*.py"`: Includes only files ending with `.py`.
|
72
|
+
- `-e "*.test.*"`: Excludes files with `.test.` in their names.
|
73
|
+
|
74
|
+
#### Examples
|
75
|
+
|
76
|
+
1. **Generate a full project structure with file contents**:
|
77
|
+
|
78
|
+
```bash
|
79
|
+
reposnap .
|
80
|
+
```
|
81
|
+
|
82
|
+
2. **Generate a project structure only**:
|
83
|
+
|
84
|
+
```bash
|
85
|
+
reposnap my_project/ --structure-only
|
86
|
+
```
|
87
|
+
|
88
|
+
3. **Generate a Markdown file including only Python files**:
|
89
|
+
|
90
|
+
```bash
|
91
|
+
reposnap my_project/ -i "*.py"
|
92
|
+
```
|
93
|
+
|
94
|
+
4. **Generate a Markdown file excluding certain files and directories**:
|
95
|
+
|
96
|
+
```bash
|
97
|
+
reposnap my_project/ -e "tests" -e "*.md"
|
98
|
+
```
|
99
|
+
|
100
|
+
5. **Exclude files and directories containing a substring**:
|
101
|
+
|
102
|
+
```bash
|
103
|
+
reposnap my_project/ -e "gui"
|
104
|
+
```
|
105
|
+
|
106
|
+
### Graphical User Interface
|
107
|
+
|
108
|
+
`reposnap` also provides a GUI for users who prefer an interactive interface.
|
109
|
+
|
110
|
+
To launch the GUI, simply run:
|
111
|
+
|
112
|
+
```bash
|
113
|
+
reposnap-gui
|
114
|
+
```
|
115
|
+
|
116
|
+
#### Using the GUI
|
117
|
+
|
118
|
+
1. **Select Root Directory**: When the GUI opens, you can specify the root directory of your Git project. By default, it uses the current directory.
|
119
|
+
|
120
|
+
2. **Scan the Project**: Click the "Scan" button to analyze the project. The GUI will display the file tree of your project.
|
121
|
+
|
122
|
+
3. **Select Files and Directories**: Use the checkboxes to select which files and directories you want to include in the Markdown documentation. Toggling a directory checkbox will toggle all its child files and directories.
|
123
|
+
|
124
|
+
4. **Generate Markdown**: After selecting the desired files, click the "Render" button. The Markdown file will be generated and saved as `output.md` in the current directory.
|
125
|
+
|
126
|
+
5. **Exit**: Click the "Exit" button to close the GUI.
|
127
|
+
|
128
|
+
## Testing
|
129
|
+
|
130
|
+
To run the tests, use the following command:
|
131
|
+
|
132
|
+
```bash
|
133
|
+
pytest tests/
|
134
|
+
```
|
135
|
+
|
136
|
+
Ensure that you have the `pytest` library installed:
|
137
|
+
|
138
|
+
```bash
|
139
|
+
pip install pytest
|
140
|
+
```
|
141
|
+
|
142
|
+
## License
|
143
|
+
|
144
|
+
This project is licensed under the MIT License.
|
145
|
+
|
146
|
+
## Acknowledgments
|
147
|
+
|
148
|
+
- [GitPython](https://gitpython.readthedocs.io/) - Used for interacting with Git repositories.
|
149
|
+
- [pathspec](https://pathspec.readthedocs.io/) - Used for pattern matching file paths.
|
150
|
+
- [Urwid](https://urwid.org/) - Used for creating the GUI interface.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
reposnap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
reposnap/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
reposnap/controllers/project_controller.py,sha256=hDYNLE_OtusAZOb6sKyvSFtbc7j-DUuYcmFST7U5Dao,5638
|
4
|
+
reposnap/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
reposnap/core/file_system.py,sha256=82gwvmgrsWf63paMrIz-Z0eqIjbqt9_-vujdXlJJoFE,1074
|
6
|
+
reposnap/core/git_repo.py,sha256=2u_ILkV-Ur7qr1WHmHM2yg44Ggft61RsdbZLsZaQ5NU,1256
|
7
|
+
reposnap/core/markdown_generator.py,sha256=DkH7Gdo-fWevSyp9nqIyOG6qVOT6kk36xndR6X5vskM,3148
|
8
|
+
reposnap/interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
reposnap/interfaces/cli.py,sha256=tq9OfVE1NvUYrsdr2ewx6nBhG76k8E-yucwVgTo-LM8,1302
|
10
|
+
reposnap/interfaces/gui.py,sha256=pzWQbW55gBNZu4tXRdBFic39upGtYxew91FSiEvalj0,5421
|
11
|
+
reposnap/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
reposnap/models/file_tree.py,sha256=SQ1cKW066uh1F1BcF8AXuw4Q-l6rkybxjdJEcLFjewg,3052
|
13
|
+
reposnap/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
+
reposnap/utils/path_utils.py,sha256=7072816LCP8Q8XBydn0iknmfrObPO_-2rFqpbAvPrjY,501
|
15
|
+
reposnap-0.5.0.dist-info/METADATA,sha256=46SWe62RAuwLHzX6-2vUt5QchhHp8XoRpwCjvBeQLTE,5240
|
16
|
+
reposnap-0.5.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
17
|
+
reposnap-0.5.0.dist-info/entry_points.txt,sha256=o3GyO7bpR0dujPCjsvvZMPv4pXNJlFwD49_pA1r5FOA,102
|
18
|
+
reposnap-0.5.0.dist-info/licenses/LICENSE,sha256=Aj7WCYBXi98pvi723HPn4GDRyjxToNWb3PC6j1_lnPk,1069
|
19
|
+
reposnap-0.5.0.dist-info/RECORD,,
|
@@ -1,88 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.3
|
2
|
-
Name: reposnap
|
3
|
-
Version: 0.3.2
|
4
|
-
Summary: Generate a Markdown file with all contents of your project
|
5
|
-
Author: agoloborodko
|
6
|
-
License-File: LICENSE
|
7
|
-
Requires-Python: >=3.8
|
8
|
-
Requires-Dist: gitpython>=3.1.43
|
9
|
-
Requires-Dist: pathlib>=1.0.1
|
10
|
-
Requires-Dist: pathspec>=0.12.1
|
11
|
-
Description-Content-Type: text/markdown
|
12
|
-
|
13
|
-
# RepoSnap
|
14
|
-
|
15
|
-
## Overview
|
16
|
-
|
17
|
-
`reposnap` is a Python script designed to generate a Markdown file that documents the structure and contents of a git project. This tool is particularly useful for creating a quick overview of a project's file hierarchy, including optional syntax-highlighted code snippets.
|
18
|
-
|
19
|
-
## Features
|
20
|
-
|
21
|
-
- **Syntax Highlighting**: Includes syntax highlighting for known file types in the generated Markdown file.
|
22
|
-
- **Structure Only Option**: The `--structure-only` flag can be used to generate the Markdown file with just the directory structure, omitting the contents of the files.
|
23
|
-
|
24
|
-
## Installation
|
25
|
-
```bash
|
26
|
-
pip install reposnap
|
27
|
-
```
|
28
|
-
|
29
|
-
Alternatively, you can clone the repository and install the required dependencies:
|
30
|
-
|
31
|
-
```bash
|
32
|
-
git clone https://github.com/username/reposnap.git
|
33
|
-
cd reposnap
|
34
|
-
pip install -r requirements.lock
|
35
|
-
```
|
36
|
-
|
37
|
-
## Usage
|
38
|
-
|
39
|
-
### Command-Line Interface
|
40
|
-
|
41
|
-
To use `reposnap`, run it with the following options:
|
42
|
-
|
43
|
-
```bash
|
44
|
-
reposnap [-h] [-o OUTPUT] [--structure-only] [--debug] path
|
45
|
-
```
|
46
|
-
|
47
|
-
- `path`: Path to the Git repository or subdirectory
|
48
|
-
- `-h, --help`: show help message and exit
|
49
|
-
- `-o, --output`: The name of the output Markdown file. Defaults to `output.md`.
|
50
|
-
- `--structure-only`: Generate a Markdown file that includes only the project structure, without file contents.
|
51
|
-
- `--debug`: Enable debug-level logging.
|
52
|
-
|
53
|
-
### Examples
|
54
|
-
|
55
|
-
1. **Generate a full project structure with file contents**:
|
56
|
-
|
57
|
-
```bash
|
58
|
-
reposnap .
|
59
|
-
```
|
60
|
-
|
61
|
-
2. **Generate a project structure only**:
|
62
|
-
|
63
|
-
```bash
|
64
|
-
reposnap my_project/ --structure-only
|
65
|
-
```
|
66
|
-
|
67
|
-
3. **Generate a Markdown file excluding certain files and directories**:
|
68
|
-
|
69
|
-
```bash
|
70
|
-
reposnap my_project/ -o output.md
|
71
|
-
```
|
72
|
-
|
73
|
-
## Testing
|
74
|
-
|
75
|
-
To run the tests, use `rye`:
|
76
|
-
|
77
|
-
```bash
|
78
|
-
rye test
|
79
|
-
```
|
80
|
-
|
81
|
-
## License
|
82
|
-
|
83
|
-
This project is licensed under the MIT License.
|
84
|
-
|
85
|
-
## Acknowledgments
|
86
|
-
|
87
|
-
- [GitPython](https://gitpython.readthedocs.io/) - Used for interacting with Git repositories.
|
88
|
-
- [pathspec](https://pathspec.readthedocs.io/) - Used for pattern matching file paths.
|
reposnap-0.3.2.dist-info/RECORD
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
reposnap/__init__.py,sha256=FGadYKcWDEh_AL8PbUuUrcCZDlF510JZfdGhHdT6XQk,80
|
2
|
-
reposnap/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
reposnap/controllers/project_controller.py,sha256=0BPnQ0tJf0lNNgP_TSYWhU8DUUzDyCPzdwtIM6zlQbA,3131
|
4
|
-
reposnap/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
reposnap/core/file_system.py,sha256=82gwvmgrsWf63paMrIz-Z0eqIjbqt9_-vujdXlJJoFE,1074
|
6
|
-
reposnap/core/git_repo.py,sha256=2u_ILkV-Ur7qr1WHmHM2yg44Ggft61RsdbZLsZaQ5NU,1256
|
7
|
-
reposnap/core/markdown_generator.py,sha256=HJSn8ekNiGwFbMwRNZVdA6Y-4InSV3WDVdA-z8s-m0A,3032
|
8
|
-
reposnap/interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
reposnap/interfaces/cli.py,sha256=2ThdHJ0LJTWHaNuBF7PdO2xNdeRP16I-BXHumk_PfuI,961
|
10
|
-
reposnap/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
reposnap/models/file_tree.py,sha256=6r7_q-3u03_B9mMUF-2HDL6TMkFkFKhbO191hwUB_gk,2026
|
12
|
-
reposnap/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
-
reposnap/utils/path_utils.py,sha256=NbjZkSScYTISQY4Ww22sSWPf26_mig3bYTKmdSO2U2A,384
|
14
|
-
reposnap-0.3.2.dist-info/METADATA,sha256=PFh5yBs306pbvXYoBSZNhmOPLmASoEEJUr97BypZddw,2333
|
15
|
-
reposnap-0.3.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
16
|
-
reposnap-0.3.2.dist-info/entry_points.txt,sha256=aaCuB0RZOLW2--ymW8VCEOm0Qz9KvTZCUjwQFZsGaDU,43
|
17
|
-
reposnap-0.3.2.dist-info/licenses/LICENSE,sha256=Aj7WCYBXi98pvi723HPn4GDRyjxToNWb3PC6j1_lnPk,1069
|
18
|
-
reposnap-0.3.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|