-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgitcodes.py
More file actions
260 lines (228 loc) · 10.5 KB
/
Copy pathgitcodes.py
File metadata and controls
260 lines (228 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import os
import sys
import git # type: ignore
import requests # type: ignore
class GitCodes:
def __init__(self):
self.TOKEN_COUNTER = 0
def get_repo(self, path, logger):
"""
Get the Git repository at the specified path.
Parameters:
- path (str): Path to the cloned Git repository.
- logger: Logger instance for logging.
Returns:
- repo: Git repository object.
"""
logger.info(f"Attempting to get repository at path: {path}")
try:
repo = git.Repo(path)
logger.info(f"Successfully obtained repository: {repo.working_tree_dir}")
return repo
except git.exc.InvalidGitRepositoryError:
logger.error(f"Invalid Git repository at path: {path}")
sys.exit(1)
except Exception as e:
logger.error(f"Unexpected error accessing repository: {e}")
sys.exit(1)
def get_issue_commits(self, repo, issue_number, logger):
"""
Retrieve commits related to a specific issue.
Parameters:
- repo: Git repository object.
- issue_number (int): Issue number to search for.
- logger: Logger instance for logging.
Returns:
- issue_commits (list): List of commit objects related to the issue.
"""
logger.info(f"Searching for commits related to issue number: {issue_number}")
issue_commits = []
try:
for commit in repo.iter_commits():
if f'#{issue_number}' in commit.message:
logger.debug(f"Found commit related to issue #{issue_number}: {commit.hexsha}")
issue_commits.append(commit)
logger.info(f"Total commits found for issue #{issue_number}: {len(issue_commits)}")
return issue_commits
except Exception as e:
logger.error(f"Error while fetching commits for issue #{issue_number}: {e}")
return issue_commits
def get_commit_files(self, repo, commit_hash, logger):
"""
Get files changed in a specific commit.
Parameters:
- repo: Git repository object.
- commit_hash (str): SHA hash of the commit.
- logger: Logger instance for logging.
Returns:
- files (dict): Dictionary of files changed in the commit with stats.
"""
logger.info(f"Getting files changed in commit: {commit_hash}")
try:
commit_to_check = repo.commit(commit_hash)
files = commit_to_check.stats.files
logger.debug(f"Files changed in commit {commit_hash}: {files}")
return files
except Exception as e:
logger.error(f"Error fetching files for commit {commit_hash}: {e}")
return {}
def get_issue_description(self, issue_number, owner, repo_name, logger):
"""
Fetch the description of a GitHub issue.
Parameters:
- issue_number (int): Issue number.
- owner (str): Repository owner.
- repo_name (str): Repository name.
- logger: Logger instance for logging.
Returns:
- description (str): Description of the issue.
"""
token = os.environ.get(f'GITHUB_TOKEN{self.TOKEN_COUNTER}')
url = f"https://api.github.com/repos/{owner}/{repo_name}/issues/{issue_number}"
headers = {"Authorization": f"token {token}"} if token else {}
logger.info(f"Fetching description for issue #{issue_number}")
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
description = response.json().get('body', f"Issue #{issue_number}")
logger.debug(f"Issue #{issue_number} description fetched successfully.")
return description
elif response.status_code == 403:
self.TOKEN_COUNTER = (self.TOKEN_COUNTER + 1) % 10
token = os.environ.get(f'GITHUB_TOKEN{self.TOKEN_COUNTER}')
headers = {"Authorization": f"token {token}"} if token else {}
response = requests.get(url, headers=headers)
if response.status_code == 200:
description = response.json().get('body', f"Issue #{issue_number}")
logger.debug(f"Issue #{issue_number} description fetched successfully.")
return description
else:
logger.warning(f"Last issue not found or access denied. Status Code: {response.status_code}")
return -1
else:
logger.warning(f"Issue #{issue_number} not found or access denied. Status Code: {response.status_code}")
return f"Issue #{issue_number} (not found)"
except Exception as e:
logger.error(f"Error fetching issue description for #{issue_number}: {e}")
return f"Issue #{issue_number} (error fetching description)"
def get_last_closed_issue(self, repo, owner, repo_name, logger):
"""
Fetch the description of a GitHub issue.
Parameters:
- issue_number (int): Issue number.
- owner (str): Repository owner.
- repo_name (str): Repository name.
- logger: Logger instance for logging.
Returns:
- description (str): Description of the issue.
"""
token = os.environ.get(f'GITHUB_TOKEN{self.TOKEN_COUNTER}')
url = f"https://api.github.com/repos/{owner}/{repo_name}/issues"
headers = {"Authorization": f"token {token}"} if token else {}
params = {
"state": "closed",
"per_page": "1",
"direction": "desc"
}
logger.info(f"Fetching the last close issue.")
try:
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
last_issue = response.json()[0]["number"]
return last_issue
elif response.status_code == 403:
self.TOKEN_COUNTER = (self.TOKEN_COUNTER + 1) % 10
token = os.environ.get(f'GITHUB_TOKEN{self.TOKEN_COUNTER}')
headers = {"Authorization": f"token {token}"} if token else {}
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
last_issue = response.json()[0]["number"]
return last_issue
else:
logger.warning(f"Last issue not found or access denied. Status Code: {response.status_code}")
return -1
else:
logger.warning(f"Last issue not found or access denied. Status Code: {response.status_code}")
return -1
except Exception as e:
logger.error(f"Error fetching last issue: {e}")
return -1
def get_codebase_before_commit(self, repo, commit, logger):
"""
Extract the codebase right before the specified commit.
Parameters:
- repo: Git repository object.
- commit: Git commit object.
- logger: Logger instance for logging.
Returns:
- codebase (str): Concatenated contents of all relevant files before the commit.
"""
logger.info(f"Extracting codebase before commit: {commit.hexsha}")
try:
parents = commit.parents
if not parents:
logger.warning(f"Commit {commit.hexsha} has no parents. Returning empty codebase.")
return {}
parent_commit = parents[0]
codebase = {}
tree = parent_commit.tree
total_len = 0
for blob in tree.traverse():
if blob.type == 'blob':
try:
# Filter for specific file types if necessary
if not blob.path.endswith(('.py', '.js', '.java', '.cpp', '.c', '.rb', '.go', '.ts', '.dart', '.html')):
continue
# Decode the blob content as UTF-8 text
content = blob.data_stream.read().decode('utf-8')
total_len += len(content)
codebase[blob.path] = content
except UnicodeDecodeError:
# Skip binary files or files with decoding issues
logger.debug(f"Skipping non-text file: {blob.path}")
continue
# print('='*10)
# print(total_len)
# print('='*10)
logger.info(f"Extracted codebase before commit {commit.hexsha}")
return codebase
except Exception as e:
logger.error(f"Error extracting codebase before commit {commit.hexsha}: {e}")
return {}
def get_codebase_after_commit(self, repo, commit, logger):
"""
Extract the codebase right after the specified commit.
Parameters:
- repo: Git repository object.
- commit: Git commit object.
- logger: Logger instance for logging.
Returns:
- codebase (str): Concatenated contents of all relevant files before the commit.
"""
logger.info(f"Extracting codebase after commit: {commit.hexsha}")
try:
codebase = {}
tree = commit.tree
if not tree:
logger.warning(f"Commit {commit.hexsha} has no tree. Returning empty codebase.")
return {}
total_len = 0
for blob in tree.traverse():
if blob.type == 'blob':
try:
# Filter for specific file types if necessary
if not blob.path.endswith(('.py', '.js', '.java', '.cpp', '.c', '.rb', '.go', '.ts')):
continue
# Decode the blob content as UTF-8 text
content = blob.data_stream.read().decode('utf-8')
total_len += len(content)
codebase[blob.path] = content
except UnicodeDecodeError:
# Skip binary files or files with decoding issues
logger.debug(f"Skipping non-text file: {blob.path}")
continue
logger.info(f"Extracted codebase after commit {commit.hexsha}")
return codebase
except Exception as e:
logger.error(f"Error extracting codebase after commit {commit.hexsha}: {e}")
return {}