git-recap 0.1.5__py3-none-any.whl → 0.1.6__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.
- git_recap/cli.py +270 -0
- git_recap/fetcher.py +27 -3
- git_recap/providers/__init__.py +3 -1
- git_recap/providers/azure_fetcher.py +86 -29
- git_recap/providers/base_fetcher.py +30 -0
- git_recap/providers/github_fetcher.py +79 -11
- git_recap/providers/gitlab_fetcher.py +67 -2
- git_recap/providers/local_fetcher.py +390 -0
- git_recap/providers/url_fetcher.py +88 -12
- {git_recap-0.1.5.dist-info → git_recap-0.1.6.dist-info}/METADATA +15 -10
- git_recap-0.1.6.dist-info/RECORD +17 -0
- {git_recap-0.1.5.dist-info → git_recap-0.1.6.dist-info}/WHEEL +1 -1
- git_recap-0.1.6.dist-info/entry_points.txt +2 -0
- git_recap-0.1.5.dist-info/RECORD +0 -14
- {git_recap-0.1.5.dist-info → git_recap-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {git_recap-0.1.5.dist-info → git_recap-0.1.6.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,6 @@ import os
|
|
|
2
2
|
import re
|
|
3
3
|
import shutil
|
|
4
4
|
import subprocess
|
|
5
|
-
from pathlib import Path
|
|
6
5
|
import tempfile
|
|
7
6
|
from typing import List, Dict, Any, Optional
|
|
8
7
|
from datetime import datetime
|
|
@@ -36,7 +35,7 @@ class URLFetcher(BaseFetcher):
|
|
|
36
35
|
)
|
|
37
36
|
self.url = self._normalize_url(url)
|
|
38
37
|
self.temp_dir = None
|
|
39
|
-
|
|
38
|
+
self.repo_path = None
|
|
40
39
|
self._clone_repo()
|
|
41
40
|
|
|
42
41
|
def _normalize_url(self, url: str) -> str:
|
|
@@ -59,7 +58,7 @@ class URLFetcher(BaseFetcher):
|
|
|
59
58
|
capture_output=True,
|
|
60
59
|
text=True,
|
|
61
60
|
check=True,
|
|
62
|
-
timeout=10
|
|
61
|
+
timeout=10
|
|
63
62
|
)
|
|
64
63
|
if not result.stdout.strip():
|
|
65
64
|
raise ValueError(f"URL {self.url} points to an empty repository")
|
|
@@ -71,8 +70,8 @@ class URLFetcher(BaseFetcher):
|
|
|
71
70
|
def _clone_repo(self) -> None:
|
|
72
71
|
"""Clone the repository to a temporary directory with all branches."""
|
|
73
72
|
self.temp_dir = tempfile.mkdtemp(prefix="gitrecap_")
|
|
73
|
+
self.repo_path = self.temp_dir
|
|
74
74
|
try:
|
|
75
|
-
# First clone with --no-checkout to save bandwidth
|
|
76
75
|
subprocess.run(
|
|
77
76
|
["git", "clone", "--no-checkout", self.url, self.temp_dir],
|
|
78
77
|
check=True,
|
|
@@ -81,7 +80,6 @@ class URLFetcher(BaseFetcher):
|
|
|
81
80
|
timeout=300
|
|
82
81
|
)
|
|
83
82
|
|
|
84
|
-
# Fetch all branches
|
|
85
83
|
subprocess.run(
|
|
86
84
|
["git", "-C", self.temp_dir, "fetch", "--all"],
|
|
87
85
|
check=True,
|
|
@@ -90,7 +88,6 @@ class URLFetcher(BaseFetcher):
|
|
|
90
88
|
timeout=300
|
|
91
89
|
)
|
|
92
90
|
|
|
93
|
-
# Verify the cloned repository has at least one commit
|
|
94
91
|
verify_result = subprocess.run(
|
|
95
92
|
["git", "-C", self.temp_dir, "rev-list", "--count", "--all"],
|
|
96
93
|
capture_output=True,
|
|
@@ -138,7 +135,6 @@ class URLFetcher(BaseFetcher):
|
|
|
138
135
|
check=True
|
|
139
136
|
)
|
|
140
137
|
branches = [b.strip() for b in result.stdout.splitlines() if b.strip()]
|
|
141
|
-
# Filter out HEAD reference if present
|
|
142
138
|
return [b for b in branches if not b.endswith('/HEAD')]
|
|
143
139
|
except subprocess.CalledProcessError:
|
|
144
140
|
return []
|
|
@@ -154,7 +150,7 @@ class URLFetcher(BaseFetcher):
|
|
|
154
150
|
"log",
|
|
155
151
|
"--pretty=format:%H|%an|%ad|%s",
|
|
156
152
|
"--date=iso",
|
|
157
|
-
"--all"
|
|
153
|
+
"--all"
|
|
158
154
|
]
|
|
159
155
|
|
|
160
156
|
if self.start_date:
|
|
@@ -173,7 +169,7 @@ class URLFetcher(BaseFetcher):
|
|
|
173
169
|
capture_output=True,
|
|
174
170
|
text=True,
|
|
175
171
|
check=True,
|
|
176
|
-
timeout=120
|
|
172
|
+
timeout=120
|
|
177
173
|
)
|
|
178
174
|
return self._parse_git_log(result.stdout)
|
|
179
175
|
except subprocess.TimeoutExpired:
|
|
@@ -206,7 +202,7 @@ class URLFetcher(BaseFetcher):
|
|
|
206
202
|
"timestamp": timestamp
|
|
207
203
|
})
|
|
208
204
|
except ValueError:
|
|
209
|
-
continue
|
|
205
|
+
continue
|
|
210
206
|
|
|
211
207
|
return entries
|
|
212
208
|
|
|
@@ -290,12 +286,92 @@ class URLFetcher(BaseFetcher):
|
|
|
290
286
|
"""
|
|
291
287
|
raise NotImplementedError("Pull request creation is not supported for generic Git URLs (URLFetcher).")
|
|
292
288
|
|
|
289
|
+
def get_authors(self, repo_names: List[str]) -> List[Dict[str, str]]:
|
|
290
|
+
"""
|
|
291
|
+
Retrieve unique authors from cloned repository using git log.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
repo_names: Not used for URL fetcher (single repo only).
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
List of unique author dictionaries with name and email.
|
|
298
|
+
"""
|
|
299
|
+
authors_set = set()
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
if not hasattr(self, 'repo_path') or not os.path.exists(self.repo_path):
|
|
303
|
+
print("Repository not cloned yet")
|
|
304
|
+
return []
|
|
305
|
+
|
|
306
|
+
cmd = [
|
|
307
|
+
'git', '-C', self.repo_path, 'log',
|
|
308
|
+
'--all',
|
|
309
|
+
'--format=%an|%ae'
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
result = subprocess.run(
|
|
313
|
+
cmd,
|
|
314
|
+
capture_output=True,
|
|
315
|
+
text=True,
|
|
316
|
+
check=True
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
for line in result.stdout.strip().split('\n'):
|
|
320
|
+
if '|' in line:
|
|
321
|
+
name, email = line.split('|', 1)
|
|
322
|
+
authors_set.add((name.strip(), email.strip()))
|
|
323
|
+
|
|
324
|
+
cmd_committer = [
|
|
325
|
+
'git', '-C', self.repo_path, 'log',
|
|
326
|
+
'--all',
|
|
327
|
+
'--format=%cn|%ce'
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
result_committer = subprocess.run(
|
|
331
|
+
cmd_committer,
|
|
332
|
+
capture_output=True,
|
|
333
|
+
text=True,
|
|
334
|
+
check=True
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
for line in result_committer.stdout.strip().split('\n'):
|
|
338
|
+
if '|' in line:
|
|
339
|
+
name, email = line.split('|', 1)
|
|
340
|
+
authors_set.add((name.strip(), email.strip()))
|
|
341
|
+
|
|
342
|
+
authors_list = [
|
|
343
|
+
{"name": name, "email": email}
|
|
344
|
+
for name, email in sorted(authors_set)
|
|
345
|
+
]
|
|
346
|
+
|
|
347
|
+
return authors_list
|
|
348
|
+
|
|
349
|
+
except subprocess.CalledProcessError as e:
|
|
350
|
+
print(f"Git command failed: {e}")
|
|
351
|
+
return []
|
|
352
|
+
except Exception as e:
|
|
353
|
+
print(f"Error in get_authors: {e}")
|
|
354
|
+
return []
|
|
355
|
+
|
|
356
|
+
def get_current_author(self) -> Optional[Dict[str, str]]:
|
|
357
|
+
"""
|
|
358
|
+
Retrieve the current authenticated user's information.
|
|
359
|
+
|
|
360
|
+
For URL-based cloning, there is no authenticated user context,
|
|
361
|
+
so this method always returns None.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
None: URL fetcher has no default author.
|
|
365
|
+
"""
|
|
366
|
+
return None
|
|
367
|
+
|
|
293
368
|
def clear(self) -> None:
|
|
294
369
|
"""Clean up temporary directory."""
|
|
295
370
|
if self.temp_dir and os.path.exists(self.temp_dir):
|
|
296
371
|
try:
|
|
297
372
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
|
298
373
|
except Exception:
|
|
299
|
-
pass
|
|
374
|
+
pass
|
|
300
375
|
finally:
|
|
301
|
-
self.temp_dir = None
|
|
376
|
+
self.temp_dir = None
|
|
377
|
+
self.repo_path = None
|
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: git-recap
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: A modular Python tool that aggregates and formats user-authored messages from repositories.
|
|
5
|
-
Author: Bruno V.
|
|
6
|
-
|
|
5
|
+
Author-email: "Bruno V." <bruno.vitorino@tecnico.ulisboa.pt>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/BrunoV21/GitRecap
|
|
8
|
+
Project-URL: Documentation, https://github.com/BrunoV21/GitRecap#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/BrunoV21/GitRecap.git
|
|
10
|
+
Project-URL: Issues, https://github.com/BrunoV21/GitRecap/issues
|
|
11
|
+
Keywords: git,github,gitlab,azure-devops,version-control,repository
|
|
7
12
|
Classifier: Programming Language :: Python :: 3
|
|
8
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
14
|
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.10
|
|
10
16
|
Description-Content-Type: text/markdown
|
|
11
17
|
License-File: LICENSE
|
|
12
18
|
Requires-Dist: PyGithub==2.6.1
|
|
13
19
|
Requires-Dist: azure-devops==7.1.0b4
|
|
14
20
|
Requires-Dist: python-gitlab==5.6.0
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
24
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
25
|
+
Requires-Dist: flake8>=5.0.0; extra == "dev"
|
|
26
|
+
Requires-Dist: mypy>=0.990; extra == "dev"
|
|
20
27
|
Dynamic: license-file
|
|
21
|
-
Dynamic: requires-dist
|
|
22
|
-
Dynamic: summary
|
|
23
28
|
|
|
24
29
|
<a href="https://www.uneed.best/tool/gitrecap">
|
|
25
30
|
<img src="https://www.uneed.best/POTD2A.png" style="width: 250px;" alt="Uneed POTD2 Badge" />
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
git_recap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
git_recap/cli.py,sha256=oWSHWiIW1I6XyxyzDRVyMukCKs467KaIgDJzg40IsAg,8530
|
|
3
|
+
git_recap/fetcher.py,sha256=7_EZ3Af5jP_dldPbGrgpT-Gzmc1gg1NDxY2TfRuAnyc,3552
|
|
4
|
+
git_recap/utils.py,sha256=NAG0cvm1sYKEQD5E9PueduB3CZQRTahrioxZNgwwmi4,5481
|
|
5
|
+
git_recap/providers/__init__.py,sha256=pGAdkLeklLMD8feL7gZvEeHvsSvZlGpezbPOPKsqH3o,409
|
|
6
|
+
git_recap/providers/azure_fetcher.py,sha256=_hAgt1Za-uGkRuve7-FKq_bQ9-nAlLexD0GsgTBl_1k,14675
|
|
7
|
+
git_recap/providers/base_fetcher.py,sha256=ZANa-vdSpI_wDQ2U0QillSx6V4ljqxuFBn9bjSqq9bg,8633
|
|
8
|
+
git_recap/providers/github_fetcher.py,sha256=n87yYlYmsUyi3O0usqXyYP4ZJ-x3FFE-o9e_tLjDGv0,21638
|
|
9
|
+
git_recap/providers/gitlab_fetcher.py,sha256=IHC4CE3E_0ri2AhS7a5DPesOrSl5YNcP_hYBHzbIZGo,12376
|
|
10
|
+
git_recap/providers/local_fetcher.py,sha256=oCeddwzE7cJzMLCPWCOFRkNqvH45EM2KxxsOg3W5LME,12781
|
|
11
|
+
git_recap/providers/url_fetcher.py,sha256=pSd2PSeYpPsHdws6LsQ3ruZdDd9rz5LN6U7dxF_eHLU,12939
|
|
12
|
+
git_recap-0.1.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
13
|
+
git_recap-0.1.6.dist-info/METADATA,sha256=jXU579yPa9QJHb0s0dII9P35FxYI7tkgmUetuGkAZF8,5404
|
|
14
|
+
git_recap-0.1.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
15
|
+
git_recap-0.1.6.dist-info/entry_points.txt,sha256=28MZTffgA2q41JRbRHe1TQ-u1XtsyJK021ThKbp0bSU,49
|
|
16
|
+
git_recap-0.1.6.dist-info/top_level.txt,sha256=1JUKd3WPB8c3LcD1deIW-1UTmYzA0zJqwugAz72YZ_o,10
|
|
17
|
+
git_recap-0.1.6.dist-info/RECORD,,
|
git_recap-0.1.5.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
git_recap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
git_recap/fetcher.py,sha256=oRlenzd9OsiBkCtpUgSZGlUoUnVpdkolUZhFOF1USBs,2677
|
|
3
|
-
git_recap/utils.py,sha256=NAG0cvm1sYKEQD5E9PueduB3CZQRTahrioxZNgwwmi4,5481
|
|
4
|
-
git_recap/providers/__init__.py,sha256=njrsu58KB60-wN78P0egm1lkQLIRq_PHLKM4WBt39Os,330
|
|
5
|
-
git_recap/providers/azure_fetcher.py,sha256=ex1XX1SZ5xgWDSEQdsAtaKGaOK21pem_sWdoPZe2DNE,13152
|
|
6
|
-
git_recap/providers/base_fetcher.py,sha256=yqVHTVAPlsDPD85SESx1u6SHeT_I9XLv41SnFo_ESPQ,7472
|
|
7
|
-
git_recap/providers/github_fetcher.py,sha256=pSvpaAoE2-lK-rEwvpliOZh-oIiI0SulHMuiYaqiEts,19239
|
|
8
|
-
git_recap/providers/gitlab_fetcher.py,sha256=i9W00H-DLyc-R--KxWbVy4CSDbORf_BmWcKveWulyzs,9752
|
|
9
|
-
git_recap/providers/url_fetcher.py,sha256=hwASWC8j17P37qLoQxO6WeT1EZ2AqwtYc9kiXKHLBhQ,10753
|
|
10
|
-
git_recap-0.1.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
-
git_recap-0.1.5.dist-info/METADATA,sha256=CbKCAeuSX9PBSxb8HpYea_OZi8dlzgIpB44Vgur9aNY,4946
|
|
12
|
-
git_recap-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
-
git_recap-0.1.5.dist-info/top_level.txt,sha256=1JUKd3WPB8c3LcD1deIW-1UTmYzA0zJqwugAz72YZ_o,10
|
|
14
|
-
git_recap-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|