commitflow 1.0.2__tar.gz → 1.0.4__tar.gz

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.
Files changed (29) hide show
  1. {commitflow-1.0.2 → commitflow-1.0.4}/PKG-INFO +1 -1
  2. {commitflow-1.0.2 → commitflow-1.0.4}/commitflow.egg-info/PKG-INFO +1 -1
  3. commitflow-1.0.4/daily_git_assistant/__init__.py +1 -0
  4. commitflow-1.0.4/daily_git_assistant/repo_scanner.py +385 -0
  5. {commitflow-1.0.2 → commitflow-1.0.4}/setup.py +16 -1
  6. commitflow-1.0.2/daily_git_assistant/__init__.py +0 -1
  7. commitflow-1.0.2/daily_git_assistant/repo_scanner.py +0 -127
  8. {commitflow-1.0.2 → commitflow-1.0.4}/LICENSE +0 -0
  9. {commitflow-1.0.2 → commitflow-1.0.4}/README.md +0 -0
  10. {commitflow-1.0.2 → commitflow-1.0.4}/commitflow.egg-info/SOURCES.txt +0 -0
  11. {commitflow-1.0.2 → commitflow-1.0.4}/commitflow.egg-info/dependency_links.txt +0 -0
  12. {commitflow-1.0.2 → commitflow-1.0.4}/commitflow.egg-info/entry_points.txt +0 -0
  13. {commitflow-1.0.2 → commitflow-1.0.4}/commitflow.egg-info/requires.txt +0 -0
  14. {commitflow-1.0.2 → commitflow-1.0.4}/commitflow.egg-info/top_level.txt +0 -0
  15. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/config.py +0 -0
  16. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/git_utils.py +0 -0
  17. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/logger.py +0 -0
  18. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/main.py +0 -0
  19. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/modes/__init__.py +0 -0
  20. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/modes/auto.py +0 -0
  21. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/modes/interactive.py +0 -0
  22. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/modes/quick.py +0 -0
  23. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/modes/setup.py +0 -0
  24. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/scheduler.py +0 -0
  25. {commitflow-1.0.2 → commitflow-1.0.4}/daily_git_assistant/ui.py +0 -0
  26. {commitflow-1.0.2 → commitflow-1.0.4}/setup.cfg +0 -0
  27. {commitflow-1.0.2 → commitflow-1.0.4}/tests/test_commit_flow.py +0 -0
  28. {commitflow-1.0.2 → commitflow-1.0.4}/tests/test_config.py +0 -0
  29. {commitflow-1.0.2 → commitflow-1.0.4}/tests/test_git_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commitflow
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: CLI tool for maintaining consistent Git commits automatically
5
5
  Home-page: https://github.com/abhinav9444/commitflow
6
6
  Author: Abhinav Kumar Singh
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commitflow
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: CLI tool for maintaining consistent Git commits automatically
5
5
  Home-page: https://github.com/abhinav9444/commitflow
6
6
  Author: Abhinav Kumar Singh
@@ -0,0 +1 @@
1
+ __version__ = "1.0.4"
@@ -0,0 +1,385 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ import threading
5
+ import itertools
6
+ import time
7
+ from pathlib import Path
8
+ from concurrent.futures import ThreadPoolExecutor
9
+
10
+ from .git_utils import is_git_repo
11
+
12
+
13
+ # --------------------------------------------------
14
+ # Repo index file
15
+ # --------------------------------------------------
16
+
17
+ INDEX_FILE = os.path.join(Path.home(), ".commitflow_repo_index.json")
18
+
19
+
20
+ # --------------------------------------------------
21
+ # Directories to skip during scanning
22
+ # --------------------------------------------------
23
+
24
+ SKIP_DIRS = {
25
+ # Windows system
26
+ "Windows",
27
+ "Program Files",
28
+ "Program Files (x86)",
29
+ "ProgramData",
30
+ "$Recycle.Bin",
31
+ "System Volume Information",
32
+ "Recovery",
33
+ "PerfLogs",
34
+
35
+ # Windows user cache
36
+ "AppData",
37
+ "Temp",
38
+
39
+ # Development dependencies
40
+ "node_modules",
41
+ "__pycache__",
42
+ ".cache",
43
+ ".npm",
44
+ ".yarn",
45
+ ".venv",
46
+ "venv",
47
+ "env",
48
+ ".tox",
49
+ "dist",
50
+ "build",
51
+ ".mypy_cache",
52
+ ".pytest_cache",
53
+ ".gradle",
54
+
55
+ # IDE folders
56
+ ".idea",
57
+ ".vscode",
58
+
59
+ # Linux system
60
+ "proc",
61
+ "sys",
62
+ "dev",
63
+ "run",
64
+ "boot",
65
+ "lib",
66
+ "lib64",
67
+ "snap",
68
+ "var",
69
+ "lost+found",
70
+ }
71
+
72
+
73
+ # --------------------------------------------------
74
+ # Known developer directories
75
+ # --------------------------------------------------
76
+
77
+ def get_dev_directories():
78
+
79
+ home = Path.home()
80
+
81
+ candidates = [
82
+ home / "projects",
83
+ home / "Projects",
84
+ home / "dev",
85
+ home / "workspace",
86
+ home / "code",
87
+ home / "src",
88
+ home / "repos",
89
+ home / "Documents",
90
+ home / "Downloads",
91
+ home / "Pictures",
92
+ home / "Videos",
93
+ home / "Music",
94
+ ]
95
+
96
+ dev_dirs = []
97
+
98
+ for d in candidates:
99
+
100
+ if d.exists():
101
+ dev_dirs.append(str(d))
102
+
103
+ return dev_dirs
104
+
105
+
106
+ # --------------------------------------------------
107
+ # Save repo index
108
+ # --------------------------------------------------
109
+
110
+ def save_repo_index(repos):
111
+
112
+ try:
113
+ with open(INDEX_FILE, "w", encoding="utf-8") as f:
114
+ json.dump(repos, f, indent=2)
115
+
116
+ except Exception:
117
+ pass
118
+
119
+
120
+ # --------------------------------------------------
121
+ # Load repo index
122
+ # --------------------------------------------------
123
+
124
+ def load_repo_index():
125
+
126
+ if not os.path.exists(INDEX_FILE):
127
+ return []
128
+
129
+ try:
130
+ with open(INDEX_FILE, "r", encoding="utf-8") as f:
131
+ return json.load(f)
132
+
133
+ except Exception:
134
+ return []
135
+
136
+
137
+ # --------------------------------------------------
138
+ # Validate stored repo paths
139
+ # --------------------------------------------------
140
+
141
+ def validate_repo_index(repos):
142
+
143
+ valid = []
144
+
145
+ for repo in repos:
146
+
147
+ if os.path.exists(repo) and is_git_repo(repo):
148
+ valid.append(repo)
149
+
150
+ return valid
151
+
152
+
153
+ # --------------------------------------------------
154
+ # Detect system roots
155
+ # --------------------------------------------------
156
+
157
+ def get_system_roots():
158
+
159
+ roots = []
160
+
161
+ if os.name == "nt":
162
+
163
+ for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
164
+
165
+ drive = f"{letter}:\\"
166
+
167
+ if os.path.exists(drive):
168
+ roots.append(drive)
169
+
170
+ else:
171
+ roots.append("/")
172
+
173
+ return roots
174
+
175
+
176
+ # --------------------------------------------------
177
+ # Spinner progress
178
+ # --------------------------------------------------
179
+
180
+ def spinner_progress(counter, repos_found, stop_event):
181
+
182
+ spinner = itertools.cycle([
183
+ "⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"
184
+ ])
185
+
186
+ while not stop_event.is_set():
187
+
188
+ spin = next(spinner)
189
+
190
+ sys.stdout.write(
191
+ f"\rScanning system {spin} folders: {counter[0]} repos: {repos_found[0]}"
192
+ )
193
+
194
+ sys.stdout.flush()
195
+
196
+ time.sleep(0.1)
197
+
198
+ print()
199
+
200
+
201
+ # --------------------------------------------------
202
+ # Scan a directory with adaptive depth
203
+ # --------------------------------------------------
204
+
205
+ def scan_directory(base_directory, counter, repos_found):
206
+
207
+ repos = []
208
+
209
+ base_depth = base_directory.count(os.sep)
210
+
211
+ for root, dirs, files in os.walk(base_directory):
212
+
213
+ counter[0] += 1
214
+
215
+ # Directory pruning
216
+ dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
217
+
218
+ depth = root.count(os.sep) - base_depth
219
+
220
+ # Adaptive depth
221
+ if base_directory == str(Path.home()):
222
+ max_depth = 5
223
+ else:
224
+ max_depth = 3
225
+
226
+ if depth > max_depth:
227
+ dirs[:] = []
228
+ continue
229
+
230
+ # Early git detection
231
+ if ".git" in dirs:
232
+
233
+ if is_git_repo(root):
234
+
235
+ repos.append(root)
236
+ repos_found[0] += 1
237
+
238
+ dirs.remove(".git")
239
+
240
+ return repos
241
+
242
+
243
+ # --------------------------------------------------
244
+ # Scan entire filesystem (parallel)
245
+ # --------------------------------------------------
246
+
247
+ def scan_entire_system():
248
+
249
+ roots = get_system_roots()
250
+ dev_dirs = get_dev_directories()
251
+
252
+ repos = []
253
+
254
+ counter = [0]
255
+ repos_found = [0]
256
+
257
+ stop_event = threading.Event()
258
+
259
+ spinner_thread = threading.Thread(
260
+ target=spinner_progress,
261
+ args=(counter, repos_found, stop_event),
262
+ daemon=True
263
+ )
264
+
265
+ spinner_thread.start()
266
+
267
+ try:
268
+
269
+ # 1️⃣ Scan developer directories first
270
+ for dev_dir in dev_dirs:
271
+
272
+ repos.extend(scan_directory(dev_dir, counter, repos_found))
273
+
274
+ # 2️⃣ Parallel scan system roots
275
+ with ThreadPoolExecutor(max_workers=4) as executor:
276
+
277
+ futures = []
278
+
279
+ for root in roots:
280
+ futures.append(
281
+ executor.submit(scan_directory, root, counter, repos_found)
282
+ )
283
+
284
+ for f in futures:
285
+ repos.extend(f.result())
286
+
287
+ except Exception:
288
+ pass
289
+
290
+ stop_event.set()
291
+ spinner_thread.join()
292
+
293
+ return repos
294
+
295
+
296
+ # --------------------------------------------------
297
+ # Display repositories
298
+ # --------------------------------------------------
299
+
300
+ def display_repos(repos):
301
+
302
+ if not repos:
303
+
304
+ print("\nNo Git repositories found.")
305
+ return
306
+
307
+ print("\nDetected Git Repositories:\n")
308
+
309
+ for index, repo in enumerate(repos, start=1):
310
+
311
+ name = os.path.basename(repo)
312
+
313
+ print(f"{index}. {name} ({repo})")
314
+
315
+
316
+ # --------------------------------------------------
317
+ # Repo selection
318
+ # --------------------------------------------------
319
+
320
+ def select_repo(repos):
321
+
322
+ if not repos:
323
+ return None
324
+
325
+ display_repos(repos)
326
+
327
+ while True:
328
+
329
+ choice = input("\nSelect repository number (or 'r' to rescan) ➤ ").strip()
330
+
331
+ if choice.lower() == "r":
332
+ return "rescan"
333
+
334
+ try:
335
+
336
+ index = int(choice) - 1
337
+
338
+ if 0 <= index < len(repos):
339
+ return repos[index]
340
+
341
+ else:
342
+ print("Invalid selection.")
343
+
344
+ except ValueError:
345
+
346
+ print("Enter a valid number or 'r'.")
347
+
348
+
349
+ # --------------------------------------------------
350
+ # Main repo detection workflow
351
+ # --------------------------------------------------
352
+
353
+ def auto_detect_repo():
354
+
355
+ print("Scanning system for Git repositories...\n")
356
+
357
+ repos = load_repo_index()
358
+
359
+ repos = validate_repo_index(repos)
360
+
361
+ if not repos:
362
+
363
+ repos = scan_entire_system()
364
+
365
+ repos = list(set(repos))
366
+
367
+ save_repo_index(repos)
368
+
369
+ while True:
370
+
371
+ repo = select_repo(repos)
372
+
373
+ if repo == "rescan":
374
+
375
+ print("\nRescanning entire system...\n")
376
+
377
+ repos = scan_entire_system()
378
+
379
+ repos = list(set(repos))
380
+
381
+ save_repo_index(repos)
382
+
383
+ continue
384
+
385
+ return repo
@@ -18,6 +18,21 @@ else:
18
18
  long_description = ""
19
19
 
20
20
 
21
+ # -----------------------------
22
+ # Read version from package
23
+ # -----------------------------
24
+
25
+ def get_version():
26
+
27
+ version_file = Path("daily_git_assistant/__init__.py").read_text()
28
+
29
+ for line in version_file.splitlines():
30
+
31
+ if line.startswith("__version__"):
32
+
33
+ return line.split("=")[1].strip().strip('"').strip("'")
34
+
35
+
21
36
  # -----------------------------
22
37
  # Add Scripts folder to PATH
23
38
  # -----------------------------
@@ -62,7 +77,7 @@ setup(
62
77
 
63
78
  name="commitflow",
64
79
 
65
- version="1.0.2", # version bumped for PyPI update
80
+ version=get_version(),
66
81
 
67
82
  description="CLI tool for maintaining consistent Git commits automatically",
68
83
 
@@ -1 +0,0 @@
1
- __version__ = "1.0.0"
@@ -1,127 +0,0 @@
1
- import os
2
- from pathlib import Path
3
- from .git_utils import is_git_repo
4
-
5
-
6
- def scan_for_git_repos(base_directory=None, max_depth=3):
7
- """
8
- Scan a directory recursively to find Git repositories.
9
- """
10
-
11
- repos = []
12
-
13
- if base_directory is None:
14
- base_directory = Path.home()
15
-
16
- base_directory = os.path.expanduser(base_directory)
17
-
18
- for root, dirs, files in os.walk(base_directory):
19
-
20
- # limit search depth
21
- depth = root[len(base_directory):].count(os.sep)
22
- if depth > max_depth:
23
- dirs[:] = []
24
- continue
25
-
26
- if ".git" in dirs:
27
-
28
- if is_git_repo(root):
29
- repos.append(root)
30
-
31
- dirs.remove(".git")
32
-
33
- return repos
34
-
35
-
36
- def scan_common_dev_directories():
37
- """
38
- Scan common development directories automatically.
39
- """
40
-
41
- common_dirs = [
42
- os.path.join(Path.home(), "projects"),
43
- os.path.join(Path.home(), "Projects"),
44
- os.path.join(Path.home(), "Documents"),
45
- os.path.join(Path.home(), "workspace"),
46
- os.path.join(Path.home(), "dev"),
47
- ]
48
-
49
- repos = []
50
-
51
- for directory in common_dirs:
52
-
53
- if os.path.exists(directory):
54
-
55
- repos.extend(scan_for_git_repos(directory))
56
-
57
- # remove duplicates
58
- repos = list(set(repos))
59
-
60
- return repos
61
-
62
-
63
- def display_repos(repos):
64
- """
65
- Display detected repositories.
66
- """
67
-
68
- if not repos:
69
-
70
- print("No Git repositories found.")
71
- return
72
-
73
- print("\nDetected Git Repositories:\n")
74
-
75
- for index, repo in enumerate(repos, start=1):
76
-
77
- name = os.path.basename(repo)
78
-
79
- print(f"{index}. {name} ({repo})")
80
-
81
-
82
- def select_repo(repos):
83
- """
84
- Allow user to choose repository interactively.
85
- """
86
-
87
- if not repos:
88
- return None
89
-
90
- display_repos(repos)
91
-
92
- while True:
93
-
94
- try:
95
-
96
- choice = input("\nSelect repository number ➤ ").strip()
97
-
98
- index = int(choice) - 1
99
-
100
- if 0 <= index < len(repos):
101
-
102
- return repos[index]
103
-
104
- else:
105
- print("Invalid selection. Try again.")
106
-
107
- except ValueError:
108
-
109
- print("Enter a valid number.")
110
-
111
-
112
- def auto_detect_repo():
113
- """
114
- Auto detect repositories and allow selection.
115
- """
116
-
117
- print("Scanning for Git repositories...")
118
-
119
- repos = scan_common_dev_directories()
120
-
121
- if not repos:
122
-
123
- print("No repositories detected.")
124
-
125
- return None
126
-
127
- return select_repo(repos)
File without changes
File without changes
File without changes