reposnap 0.6.2__py3-none-any.whl → 0.6.3__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/controllers/project_controller.py +47 -57
- reposnap/models/file_tree.py +2 -1
- {reposnap-0.6.2.dist-info → reposnap-0.6.3.dist-info}/METADATA +1 -1
- {reposnap-0.6.2.dist-info → reposnap-0.6.3.dist-info}/RECORD +7 -7
- {reposnap-0.6.2.dist-info → reposnap-0.6.3.dist-info}/WHEEL +0 -0
- {reposnap-0.6.2.dist-info → reposnap-0.6.3.dist-info}/entry_points.txt +0 -0
- {reposnap-0.6.2.dist-info → reposnap-0.6.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,6 @@
|
|
1
|
-
# src/reposnap/controllers/project_controller.py
|
2
|
-
|
3
1
|
import logging
|
4
2
|
from pathlib import Path
|
5
3
|
from reposnap.core.file_system import FileSystem
|
6
|
-
from reposnap.core.markdown_generator import MarkdownGenerator
|
7
4
|
from reposnap.models.file_tree import FileTree
|
8
5
|
import pathspec
|
9
6
|
from typing import List, Optional
|
@@ -11,35 +8,33 @@ from typing import List, Optional
|
|
11
8
|
class ProjectController:
|
12
9
|
def __init__(self, args: Optional[object] = None):
|
13
10
|
self.logger = logging.getLogger(__name__)
|
11
|
+
# Always determine repository root using Git (or cwd)
|
12
|
+
self.root_dir = self._get_repo_root().resolve()
|
14
13
|
if args:
|
15
|
-
# Support both new 'paths' (multiple paths) and legacy 'path'
|
16
|
-
if hasattr(args, 'paths'):
|
17
|
-
input_paths = [Path(p) for p in args.paths]
|
18
|
-
else:
|
19
|
-
input_paths = [Path(args.path)]
|
20
14
|
self.args = args
|
21
|
-
#
|
22
|
-
|
23
|
-
# Convert provided paths to be relative to the repository root.
|
15
|
+
# Treat positional arguments as literal file/directory names.
|
16
|
+
input_paths = [Path(p) for p in (args.paths if hasattr(args, 'paths') else [args.path])]
|
24
17
|
self.input_paths = []
|
25
18
|
for p in input_paths:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
19
|
+
candidate = (self.root_dir / p).resolve()
|
20
|
+
if candidate.exists():
|
21
|
+
try:
|
22
|
+
rel = candidate.relative_to(self.root_dir)
|
23
|
+
if rel != Path('.'):
|
24
|
+
self.input_paths.append(rel)
|
25
|
+
except ValueError:
|
26
|
+
self.logger.warning(f"Path {p} is not under repository root {self.root_dir}. Ignoring.")
|
27
|
+
else:
|
28
|
+
self.logger.warning(f"Path {p} does not exist relative to repository root {self.root_dir}.")
|
33
29
|
self.output_file: Path = Path(args.output).resolve() if args.output else self.root_dir / 'output.md'
|
34
30
|
self.structure_only: bool = args.structure_only if hasattr(args, 'structure_only') else False
|
35
31
|
self.include_patterns: List[str] = args.include if hasattr(args, 'include') else []
|
36
32
|
self.exclude_patterns: List[str] = args.exclude if hasattr(args, 'exclude') else []
|
37
33
|
else:
|
38
|
-
self.
|
34
|
+
self.args = None
|
39
35
|
self.input_paths = []
|
40
|
-
self.output_file =
|
36
|
+
self.output_file = self.root_dir / 'output.md'
|
41
37
|
self.structure_only = False
|
42
|
-
self.args = None
|
43
38
|
self.include_patterns = []
|
44
39
|
self.exclude_patterns = []
|
45
40
|
self.file_tree: Optional[FileTree] = None
|
@@ -49,25 +44,9 @@ class ProjectController:
|
|
49
44
|
|
50
45
|
def _get_repo_root(self) -> Path:
|
51
46
|
"""
|
52
|
-
Determine the repository root
|
53
|
-
use the
|
54
|
-
git repository working tree directory (or current directory if not a git repo).
|
47
|
+
Determine the repository root using Git if available,
|
48
|
+
otherwise use the current directory.
|
55
49
|
"""
|
56
|
-
if self.args is not None:
|
57
|
-
candidate_paths = []
|
58
|
-
if hasattr(self.args, 'paths'):
|
59
|
-
for p in self.args.paths:
|
60
|
-
candidate = Path(p).resolve()
|
61
|
-
if candidate.exists():
|
62
|
-
candidate_paths.append(candidate)
|
63
|
-
elif hasattr(self.args, 'path'):
|
64
|
-
candidate = Path(self.args.path).resolve()
|
65
|
-
if candidate.exists():
|
66
|
-
candidate_paths.append(candidate)
|
67
|
-
if candidate_paths:
|
68
|
-
from os.path import commonpath
|
69
|
-
common = Path(commonpath([str(p) for p in candidate_paths]))
|
70
|
-
return common
|
71
50
|
from git import Repo, InvalidGitRepositoryError
|
72
51
|
try:
|
73
52
|
repo = Repo(Path.cwd(), search_parent_directories=True)
|
@@ -104,25 +83,33 @@ class ProjectController:
|
|
104
83
|
return files
|
105
84
|
|
106
85
|
def collect_file_tree(self) -> None:
|
107
|
-
self.logger.info("Collecting files
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
#
|
86
|
+
self.logger.info("Collecting files from Git tracked files if available.")
|
87
|
+
try:
|
88
|
+
from reposnap.core.git_repo import GitRepo
|
89
|
+
git_repo = GitRepo(self.root_dir)
|
90
|
+
all_files = git_repo.get_git_files()
|
91
|
+
self.logger.debug(f"Git tracked files: {all_files}")
|
92
|
+
except Exception as e:
|
93
|
+
self.logger.warning(f"Error obtaining Git tracked files: {e}.")
|
94
|
+
all_files = []
|
95
|
+
# If Git returns an empty list but files exist on disk, fall back to filesystem scan.
|
96
|
+
if not all_files:
|
97
|
+
file_list = [p for p in self.root_dir.rglob("*") if p.is_file()]
|
98
|
+
if file_list:
|
99
|
+
self.logger.info("Git tracked files empty, using filesystem scan fallback.")
|
100
|
+
all_files = []
|
101
|
+
for path in file_list:
|
102
|
+
try:
|
103
|
+
rel = path.relative_to(self.root_dir)
|
104
|
+
all_files.append(rel)
|
105
|
+
except ValueError:
|
106
|
+
continue
|
117
107
|
all_files = self._apply_include_exclude(all_files)
|
118
|
-
self.logger.debug(f"All files after include/exclude
|
108
|
+
self.logger.debug(f"All files after applying include/exclude: {all_files}")
|
119
109
|
if self.input_paths:
|
120
110
|
trees = []
|
121
111
|
for input_path in self.input_paths:
|
122
|
-
subset = [
|
123
|
-
f for f in all_files
|
124
|
-
if f == input_path or f.parts[:len(input_path.parts)] == input_path.parts
|
125
|
-
]
|
112
|
+
subset = [f for f in all_files if f == input_path or list(f.parts[:len(input_path.parts)]) == list(input_path.parts)]
|
126
113
|
self.logger.debug(f"Files for input path '{input_path}': {subset}")
|
127
114
|
if subset:
|
128
115
|
tree = FileSystem(self.root_dir).build_tree_structure(subset)
|
@@ -164,6 +151,7 @@ class ProjectController:
|
|
164
151
|
|
165
152
|
def generate_output(self) -> None:
|
166
153
|
self.logger.info("Starting Markdown generation.")
|
154
|
+
from reposnap.core.markdown_generator import MarkdownGenerator
|
167
155
|
markdown_generator = MarkdownGenerator(
|
168
156
|
root_dir=self.root_dir,
|
169
157
|
output_file=self.output_file,
|
@@ -175,6 +163,7 @@ class ProjectController:
|
|
175
163
|
def generate_output_from_selected(self, selected_files: set) -> None:
|
176
164
|
self.logger.info("Generating Markdown from selected files.")
|
177
165
|
pruned_tree = self.file_tree.prune_tree(selected_files)
|
166
|
+
from reposnap.core.markdown_generator import MarkdownGenerator
|
178
167
|
markdown_generator = MarkdownGenerator(
|
179
168
|
root_dir=self.root_dir,
|
180
169
|
output_file=self.output_file,
|
@@ -194,14 +183,15 @@ class ProjectController:
|
|
194
183
|
gitignore_path = self.root_dir / '.gitignore'
|
195
184
|
if not gitignore_path.exists():
|
196
185
|
for parent in self.root_dir.parents:
|
197
|
-
|
198
|
-
if
|
186
|
+
candidate = parent / '.gitignore'
|
187
|
+
if candidate.exists():
|
188
|
+
gitignore_path = candidate
|
199
189
|
break
|
200
190
|
else:
|
201
191
|
gitignore_path = None
|
202
192
|
if gitignore_path and gitignore_path.exists():
|
203
193
|
with gitignore_path.open('r') as gitignore:
|
204
|
-
patterns = gitignore.
|
194
|
+
patterns = [line.strip() for line in gitignore if line.strip() and not line.strip().startswith('#')]
|
205
195
|
self.logger.debug(f"Loaded .gitignore patterns from {gitignore_path.parent}: {patterns}")
|
206
196
|
return patterns
|
207
197
|
else:
|
reposnap/models/file_tree.py
CHANGED
@@ -48,7 +48,8 @@ class FileTree:
|
|
48
48
|
if filtered_value:
|
49
49
|
filtered_subtree[key] = filtered_value
|
50
50
|
else:
|
51
|
-
if
|
51
|
+
# Exclude the file if either the full path OR its basename matches a .gitignore pattern.
|
52
|
+
if not spec.match_file(current_path) and not spec.match_file(Path(current_path).name):
|
52
53
|
filtered_subtree[key] = value
|
53
54
|
return filtered_subtree
|
54
55
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
reposnap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
reposnap/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
reposnap/controllers/project_controller.py,sha256=
|
3
|
+
reposnap/controllers/project_controller.py,sha256=u3agILanSms5Gx4D5e6EWhHrb6B08saz5udct8yVS-s,9353
|
4
4
|
reposnap/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
reposnap/core/file_system.py,sha256=82gwvmgrsWf63paMrIz-Z0eqIjbqt9_-vujdXlJJoFE,1074
|
6
6
|
reposnap/core/git_repo.py,sha256=2u_ILkV-Ur7qr1WHmHM2yg44Ggft61RsdbZLsZaQ5NU,1256
|
@@ -9,11 +9,11 @@ reposnap/interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
9
9
|
reposnap/interfaces/cli.py,sha256=JzTNDibzuRRmnWg-gBfKJ2tSlh-NYSL_3q6J-Erjrr8,1374
|
10
10
|
reposnap/interfaces/gui.py,sha256=pzWQbW55gBNZu4tXRdBFic39upGtYxew91FSiEvalj0,5421
|
11
11
|
reposnap/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
reposnap/models/file_tree.py,sha256=
|
12
|
+
reposnap/models/file_tree.py,sha256=0WcSDbFH5pSZHyWxWtmz-FF4_ELnZ3Byz2iXN4Tpijw,3206
|
13
13
|
reposnap/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
reposnap/utils/path_utils.py,sha256=7072816LCP8Q8XBydn0iknmfrObPO_-2rFqpbAvPrjY,501
|
15
|
-
reposnap-0.6.
|
16
|
-
reposnap-0.6.
|
17
|
-
reposnap-0.6.
|
18
|
-
reposnap-0.6.
|
19
|
-
reposnap-0.6.
|
15
|
+
reposnap-0.6.3.dist-info/METADATA,sha256=wPRE4NKJuzwMKTyYD8e2nPUYUNjwM81kCEsVz18yWTY,5348
|
16
|
+
reposnap-0.6.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
17
|
+
reposnap-0.6.3.dist-info/entry_points.txt,sha256=o3GyO7bpR0dujPCjsvvZMPv4pXNJlFwD49_pA1r5FOA,102
|
18
|
+
reposnap-0.6.3.dist-info/licenses/LICENSE,sha256=Aj7WCYBXi98pvi723HPn4GDRyjxToNWb3PC6j1_lnPk,1069
|
19
|
+
reposnap-0.6.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|