1
- """CLI functionality for vcspull add-from-fs ."""
1
+ """Filesystem scanning functionality for vcspull."""
2
2
3
3
from __future__ import annotations
4
4
5
5
import logging
6
- import os # For os.walk
6
+ import os
7
7
import pathlib
8
- import subprocess # For git commands
8
+ import subprocess
9
9
import typing as t
10
10
11
- import yaml # For YAML processing
11
+ import yaml
12
12
13
- from vcspull .config import ( # Assuming these are useful
14
- expand_dir ,
15
- find_home_config_files ,
16
- save_config_yaml ,
17
- )
13
+ from vcspull .config import expand_dir , find_home_config_files , save_config_yaml
18
14
19
15
if t .TYPE_CHECKING :
20
16
import argparse
21
- # Add any necessary type imports
22
17
23
18
log = logging .getLogger (__name__ )
24
19
25
20
26
21
def get_git_origin_url (repo_path : pathlib .Path ) -> str | None :
27
- """Get the URL of the 'origin' remote for a git repository."""
22
+ """Get the origin URL from a git repository.
23
+
24
+ Parameters
25
+ ----------
26
+ repo_path : pathlib.Path
27
+ Path to the git repository
28
+
29
+ Returns
30
+ -------
31
+ str | None
32
+ The origin URL if found, None otherwise
33
+ """
28
34
try :
29
35
result = subprocess .run (
30
36
["git" , "config" , "--get" , "remote.origin.url" ],
@@ -40,7 +46,7 @@ def get_git_origin_url(repo_path: pathlib.Path) -> str | None:
40
46
41
47
42
48
def create_add_from_fs_subparser (parser : argparse .ArgumentParser ) -> None :
43
- """Configure :py:class:`argparse.ArgumentParser` for `` vcspull add-from-fs``."""
49
+ """Create `` vcspull add-from-fs`` argument subparser ."""
44
50
parser .add_argument (
45
51
"-c" ,
46
52
"--config" ,
@@ -82,7 +88,21 @@ def add_from_filesystem(
82
88
base_dir_key_arg : str | None ,
83
89
yes : bool ,
84
90
) -> None :
85
- """Scan a directory and add found git repositories to the vcspull configuration."""
91
+ """Scan filesystem for git repositories and add to vcspull config.
92
+
93
+ Parameters
94
+ ----------
95
+ scan_dir_str : str
96
+ Directory to scan for git repositories
97
+ config_file_path_str : str | None
98
+ Path to config file, or None to use default
99
+ recursive : bool
100
+ Whether to scan subdirectories recursively
101
+ base_dir_key_arg : str | None
102
+ Base directory key to use in config (overrides automatic detection)
103
+ yes : bool
104
+ Whether to skip confirmation prompt
105
+ """
86
106
scan_dir = expand_dir (pathlib .Path (scan_dir_str ))
87
107
88
108
config_file_path : pathlib .Path
@@ -115,8 +135,12 @@ def add_from_filesystem(
115
135
"Aborting." ,
116
136
)
117
137
return
118
- except Exception as e :
119
- log .exception (f"Error loading YAML from { config_file_path } . Aborting." )
138
+ except Exception :
139
+ log .error (f"Error loading YAML from { config_file_path } . Aborting." )
140
+ if log .isEnabledFor (logging .DEBUG ):
141
+ import traceback
142
+
143
+ traceback .print_exc ()
120
144
return
121
145
else :
122
146
log .info (
@@ -127,46 +151,72 @@ def add_from_filesystem(
127
151
tuple [str , str , str ]
128
152
] = [] # (repo_name, repo_url, determined_base_key)
129
153
130
- for root , dirs , _ in os .walk (scan_dir ):
131
- if ".git" in dirs :
132
- repo_path = pathlib .Path (root )
133
- repo_name = repo_path .name
134
- repo_url = get_git_origin_url (repo_path )
154
+ if recursive :
155
+ for root , dirs , _ in os .walk (scan_dir ):
156
+ if ".git" in dirs :
157
+ repo_path = pathlib .Path (root )
158
+ repo_name = repo_path .name
159
+ repo_url = get_git_origin_url (repo_path )
135
160
136
- if not repo_url :
137
- log .warning (
138
- f"Could not determine remote URL for git repository at "
139
- f"{ repo_path } . Skipping." ,
140
- )
141
- continue
142
-
143
- determined_base_key : str
144
- if base_dir_key_arg :
145
- determined_base_key = (
146
- base_dir_key_arg
147
- if base_dir_key_arg .endswith ("/" )
148
- else base_dir_key_arg + "/"
149
- )
150
- else :
151
- # Try to make it relative to home if possible, otherwise absolute path
152
- # of scan_dir parent. This should be the parent of the repo_path itself,
153
- # relative to scan_dir or absolute. Or simply use scan_dir as the key.
154
- # Let's use the provided scan_dir (normalized) as the default key if
155
- # not given.
156
- try :
161
+ if not repo_url :
162
+ log .warning (
163
+ f"Could not determine remote URL for git repository at "
164
+ f"{ repo_path } . Skipping." ,
165
+ )
166
+ continue
167
+
168
+ determined_base_key : str
169
+ if base_dir_key_arg :
157
170
determined_base_key = (
158
- "~/" + str (scan_dir .relative_to (pathlib .Path .home ())) + "/"
171
+ base_dir_key_arg
172
+ if base_dir_key_arg .endswith ("/" )
173
+ else base_dir_key_arg + "/"
159
174
)
160
- except ValueError :
161
- determined_base_key = str (scan_dir .resolve ()) + "/"
175
+ else :
176
+ try :
177
+ determined_base_key = (
178
+ "~/" + str (scan_dir .relative_to (pathlib .Path .home ())) + "/"
179
+ )
180
+ except ValueError :
181
+ determined_base_key = str (scan_dir .resolve ()) + "/"
162
182
163
- if not determined_base_key .endswith ("/" ): # Ensure trailing slash
164
- determined_base_key += "/"
183
+ if not determined_base_key .endswith ("/" ):
184
+ determined_base_key += "/"
165
185
166
- found_repos .append ((repo_name , repo_url , determined_base_key ))
186
+ found_repos .append ((repo_name , repo_url , determined_base_key ))
187
+ else :
188
+ # Non-recursive: only check immediate subdirectories
189
+ for item in scan_dir .iterdir ():
190
+ if item .is_dir () and (item / ".git" ).is_dir ():
191
+ repo_name = item .name
192
+ repo_url = get_git_origin_url (item )
193
+
194
+ if not repo_url :
195
+ log .warning (
196
+ f"Could not determine remote URL for git repository at "
197
+ f"{ item } . Skipping." ,
198
+ )
199
+ continue
200
+
201
+ determined_base_key : str
202
+ if base_dir_key_arg :
203
+ determined_base_key = (
204
+ base_dir_key_arg
205
+ if base_dir_key_arg .endswith ("/" )
206
+ else base_dir_key_arg + "/"
207
+ )
208
+ else :
209
+ try :
210
+ determined_base_key = (
211
+ "~/" + str (scan_dir .relative_to (pathlib .Path .home ())) + "/"
212
+ )
213
+ except ValueError :
214
+ determined_base_key = str (scan_dir .resolve ()) + "/"
167
215
168
- if not recursive :
169
- break # Only process top-level if not recursive
216
+ if not determined_base_key .endswith ("/" ):
217
+ determined_base_key += "/"
218
+
219
+ found_repos .append ((repo_name , repo_url , determined_base_key ))
170
220
171
221
if not found_repos :
172
222
log .info (f"No git repositories found in { scan_dir } . Nothing to add." )
@@ -223,13 +273,17 @@ def add_from_filesystem(
223
273
f"Adding '{ repo_name } ' ({ repo_url } ) under '{ determined_base_key } '." ,
224
274
)
225
275
changes_made = True
226
- # else: already handled by the confirmation logic or --yes skip
227
276
228
277
if changes_made :
229
278
try :
230
279
save_config_yaml (config_file_path , raw_config )
231
280
log .info (f"Successfully updated { config_file_path } ." )
232
- except Exception as e :
233
- log .exception (f"Error saving config to { config_file_path } " )
281
+ except Exception :
282
+ log .error (f"Error saving config to { config_file_path } " )
283
+ if log .isEnabledFor (logging .DEBUG ):
284
+ import traceback
285
+
286
+ traceback .print_exc ()
287
+ raise
234
288
else :
235
289
log .info ("No changes made to the configuration." )
0 commit comments