github2gerrit 0.1.4__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.
- github2gerrit/cli.py +196 -108
- github2gerrit/config.py +207 -5
- github2gerrit/core.py +542 -398
- github2gerrit/duplicate_detection.py +375 -193
- github2gerrit/gerrit_urls.py +256 -0
- github2gerrit/github_api.py +15 -20
- github2gerrit/gitutils.py +49 -13
- github2gerrit/models.py +1 -0
- github2gerrit/similarity.py +458 -0
- github2gerrit/ssh_discovery.py +365 -0
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/METADATA +24 -25
- github2gerrit-0.1.6.dist-info/RECORD +17 -0
- github2gerrit-0.1.4.dist-info/RECORD +0 -14
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/WHEEL +0 -0
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/entry_points.txt +0 -0
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,256 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
# SPDX-FileCopyrightText: 2025 The Linux Foundation
|
3
|
+
"""
|
4
|
+
Centralized Gerrit URL construction utilities.
|
5
|
+
|
6
|
+
This module provides a unified way to construct Gerrit URLs, ensuring
|
7
|
+
consistent handling of GERRIT_HTTP_BASE_PATH and eliminating the need
|
8
|
+
for manual URL construction throughout the codebase.
|
9
|
+
"""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
import logging
|
14
|
+
import os
|
15
|
+
from urllib.parse import urljoin
|
16
|
+
|
17
|
+
|
18
|
+
log = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
|
21
|
+
class GerritUrlBuilder:
|
22
|
+
"""
|
23
|
+
Centralized builder for Gerrit URLs with consistent base path handling.
|
24
|
+
|
25
|
+
This class encapsulates all Gerrit URL construction logic, ensuring that
|
26
|
+
GERRIT_HTTP_BASE_PATH is properly handled in all contexts. It provides
|
27
|
+
methods for building different types of URLs (API, web, hooks) and handles
|
28
|
+
the common fallback patterns used throughout the application.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, host: str, base_path: str | None = None):
|
32
|
+
"""
|
33
|
+
Initialize the URL builder for a specific Gerrit host.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
host: Gerrit hostname (without protocol)
|
37
|
+
base_path: Optional base path override. If None, reads from
|
38
|
+
GERRIT_HTTP_BASE_PATH environment variable.
|
39
|
+
"""
|
40
|
+
self.host = host.strip()
|
41
|
+
|
42
|
+
# Normalize base path - remove leading/trailing slashes and whitespace
|
43
|
+
if base_path is not None:
|
44
|
+
self._base_path = base_path.strip().strip("/")
|
45
|
+
else:
|
46
|
+
self._base_path = os.getenv("GERRIT_HTTP_BASE_PATH", "").strip().strip("/")
|
47
|
+
|
48
|
+
log.debug(
|
49
|
+
"GerritUrlBuilder initialized for host=%s, base_path='%s'",
|
50
|
+
self.host,
|
51
|
+
self._base_path,
|
52
|
+
)
|
53
|
+
|
54
|
+
@property
|
55
|
+
def base_path(self) -> str:
|
56
|
+
"""Get the normalized base path."""
|
57
|
+
return self._base_path
|
58
|
+
|
59
|
+
@property
|
60
|
+
def has_base_path(self) -> bool:
|
61
|
+
"""Check if a base path is configured."""
|
62
|
+
return bool(self._base_path)
|
63
|
+
|
64
|
+
def _build_base_url(self, base_path_override: str | None = None) -> str:
|
65
|
+
"""
|
66
|
+
Build the base URL with optional base path override.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
base_path_override: Optional base path to use instead of the instance default
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
Base URL with trailing slash
|
73
|
+
"""
|
74
|
+
path = base_path_override if base_path_override is not None else self._base_path
|
75
|
+
if path:
|
76
|
+
return f"https://{self.host}/{path}/"
|
77
|
+
else:
|
78
|
+
return f"https://{self.host}/"
|
79
|
+
|
80
|
+
def api_url(self, endpoint: str = "", base_path_override: str | None = None) -> str:
|
81
|
+
"""
|
82
|
+
Build a Gerrit REST API URL.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
endpoint: API endpoint path (e.g., "/changes/", "/accounts/self")
|
86
|
+
base_path_override: Optional base path override for fallback scenarios
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
Complete API URL
|
90
|
+
"""
|
91
|
+
base_url = self._build_base_url(base_path_override)
|
92
|
+
# Ensure endpoint starts with / for proper URL joining
|
93
|
+
if endpoint and not endpoint.startswith("/"):
|
94
|
+
endpoint = "/" + endpoint
|
95
|
+
return urljoin(base_url, endpoint.lstrip("/"))
|
96
|
+
|
97
|
+
def web_url(self, path: str = "", base_path_override: str | None = None) -> str:
|
98
|
+
"""
|
99
|
+
Build a Gerrit web UI URL.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
path: Web path (e.g., "c/project/+/123", "dashboard")
|
103
|
+
base_path_override: Optional base path override for fallback scenarios
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
Complete web URL
|
107
|
+
"""
|
108
|
+
base_url = self._build_base_url(base_path_override)
|
109
|
+
if path:
|
110
|
+
# Remove leading slash if present to avoid double slashes
|
111
|
+
path = path.lstrip("/")
|
112
|
+
return urljoin(base_url, path)
|
113
|
+
return base_url.rstrip("/")
|
114
|
+
|
115
|
+
def change_url(
|
116
|
+
self,
|
117
|
+
project: str,
|
118
|
+
change_number: int,
|
119
|
+
base_path_override: str | None = None,
|
120
|
+
) -> str:
|
121
|
+
"""
|
122
|
+
Build a URL for a specific Gerrit change.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
project: Gerrit project name
|
126
|
+
change_number: Gerrit change number
|
127
|
+
base_path_override: Optional base path override for fallback scenarios
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
Complete change URL
|
131
|
+
"""
|
132
|
+
# Don't URL-encode project names - Gerrit expects them as-is (backward compatibility)
|
133
|
+
path = f"c/{project}/+/{change_number}"
|
134
|
+
return self.web_url(path, base_path_override)
|
135
|
+
|
136
|
+
def hook_url(self, hook_name: str, base_path_override: str | None = None) -> str:
|
137
|
+
"""
|
138
|
+
Build a URL for downloading Gerrit hooks.
|
139
|
+
|
140
|
+
Args:
|
141
|
+
hook_name: Name of the hook (e.g., "commit-msg")
|
142
|
+
base_path_override: Optional base path override for fallback scenarios
|
143
|
+
|
144
|
+
Returns:
|
145
|
+
Complete hook download URL
|
146
|
+
"""
|
147
|
+
path = f"tools/hooks/{hook_name}"
|
148
|
+
return self.web_url(path, base_path_override)
|
149
|
+
|
150
|
+
def get_api_url_candidates(self, endpoint: str = "") -> list[str]:
|
151
|
+
"""
|
152
|
+
Get a list of candidate API URLs for fallback scenarios.
|
153
|
+
|
154
|
+
This method returns URLs in order of preference:
|
155
|
+
1. URL with configured base path (if any)
|
156
|
+
2. URL with /r/ base path (common fallback)
|
157
|
+
3. URL with no base path (root)
|
158
|
+
|
159
|
+
Args:
|
160
|
+
endpoint: API endpoint path
|
161
|
+
|
162
|
+
Returns:
|
163
|
+
List of candidate URLs to try
|
164
|
+
"""
|
165
|
+
candidates = []
|
166
|
+
|
167
|
+
# Primary URL with configured base path
|
168
|
+
if self.has_base_path:
|
169
|
+
candidates.append(self.api_url(endpoint))
|
170
|
+
|
171
|
+
# Common fallback: /r/ base path
|
172
|
+
if self._base_path != "r":
|
173
|
+
candidates.append(self.api_url(endpoint, base_path_override="r"))
|
174
|
+
|
175
|
+
# Final fallback: no base path
|
176
|
+
if self.has_base_path:
|
177
|
+
candidates.append(self.api_url(endpoint, base_path_override=""))
|
178
|
+
|
179
|
+
# If no base path was configured, add the primary URL
|
180
|
+
if not self.has_base_path:
|
181
|
+
candidates.append(self.api_url(endpoint))
|
182
|
+
|
183
|
+
return candidates
|
184
|
+
|
185
|
+
def get_hook_url_candidates(self, hook_name: str) -> list[str]:
|
186
|
+
"""
|
187
|
+
Get a list of candidate hook URLs for fallback scenarios.
|
188
|
+
|
189
|
+
This method returns URLs in order of preference for downloading hooks:
|
190
|
+
1. URL with configured base path (if any)
|
191
|
+
2. URL with /r/ base path (common for hooks)
|
192
|
+
3. URL with no base path (root)
|
193
|
+
|
194
|
+
Args:
|
195
|
+
hook_name: Name of the hook to download
|
196
|
+
|
197
|
+
Returns:
|
198
|
+
List of candidate URLs to try
|
199
|
+
"""
|
200
|
+
candidates = []
|
201
|
+
|
202
|
+
# Primary URL with configured base path
|
203
|
+
if self.has_base_path:
|
204
|
+
candidates.append(self.hook_url(hook_name))
|
205
|
+
|
206
|
+
# Common fallback: /r/ base path (very common for hooks)
|
207
|
+
if self._base_path != "r":
|
208
|
+
candidates.append(self.hook_url(hook_name, base_path_override="r"))
|
209
|
+
|
210
|
+
# Final fallback: no base path
|
211
|
+
if self.has_base_path:
|
212
|
+
candidates.append(self.hook_url(hook_name, base_path_override=""))
|
213
|
+
|
214
|
+
# If no base path was configured, add the primary URL
|
215
|
+
if not self.has_base_path:
|
216
|
+
candidates.append(self.hook_url(hook_name))
|
217
|
+
|
218
|
+
return candidates
|
219
|
+
|
220
|
+
def get_web_base_path(self, base_path_override: str | None = None) -> str:
|
221
|
+
"""
|
222
|
+
Get the web base path for URL construction.
|
223
|
+
|
224
|
+
This is useful when you need just the path component for manual URL building.
|
225
|
+
|
226
|
+
Args:
|
227
|
+
base_path_override: Optional base path override
|
228
|
+
|
229
|
+
Returns:
|
230
|
+
Web base path with leading and trailing slashes (e.g., "/r/", "/")
|
231
|
+
"""
|
232
|
+
path = base_path_override if base_path_override is not None else self._base_path
|
233
|
+
if path:
|
234
|
+
return f"/{path}/"
|
235
|
+
else:
|
236
|
+
return "/"
|
237
|
+
|
238
|
+
def __repr__(self) -> str:
|
239
|
+
"""String representation for debugging."""
|
240
|
+
return f"GerritUrlBuilder(host='{self.host}', base_path='{self._base_path}')"
|
241
|
+
|
242
|
+
|
243
|
+
def create_gerrit_url_builder(host: str, base_path: str | None = None) -> GerritUrlBuilder:
|
244
|
+
"""
|
245
|
+
Factory function to create a GerritUrlBuilder instance.
|
246
|
+
|
247
|
+
This is the preferred way to create URL builders throughout the application.
|
248
|
+
|
249
|
+
Args:
|
250
|
+
host: Gerrit hostname
|
251
|
+
base_path: Optional base path override
|
252
|
+
|
253
|
+
Returns:
|
254
|
+
Configured GerritUrlBuilder instance
|
255
|
+
"""
|
256
|
+
return GerritUrlBuilder(host, base_path)
|
github2gerrit/github_api.py
CHANGED
@@ -30,6 +30,12 @@ from typing import TypeVar
|
|
30
30
|
from typing import cast
|
31
31
|
|
32
32
|
|
33
|
+
# Error message constants to comply with TRY003
|
34
|
+
_MSG_PYGITHUB_REQUIRED = "PyGithub required"
|
35
|
+
_MSG_MISSING_GITHUB_TOKEN = "missing GITHUB_TOKEN" # noqa: S105
|
36
|
+
_MSG_BAD_GITHUB_REPOSITORY = "bad GITHUB_REPOSITORY"
|
37
|
+
|
38
|
+
|
33
39
|
class GithubExceptionType(Exception):
|
34
40
|
pass
|
35
41
|
|
@@ -38,9 +44,7 @@ class RateLimitExceededExceptionType(GithubExceptionType):
|
|
38
44
|
pass
|
39
45
|
|
40
46
|
|
41
|
-
def _load_github_classes() -> tuple[
|
42
|
-
Any | None, type[BaseException], type[BaseException]
|
43
|
-
]:
|
47
|
+
def _load_github_classes() -> tuple[Any | None, type[BaseException], type[BaseException]]:
|
44
48
|
try:
|
45
49
|
exc_mod = import_module("github.GithubException")
|
46
50
|
ge = exc_mod.GithubException
|
@@ -65,7 +69,7 @@ else:
|
|
65
69
|
|
66
70
|
class Github: # type: ignore[no-redef]
|
67
71
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
68
|
-
raise RuntimeError(
|
72
|
+
raise RuntimeError(_MSG_PYGITHUB_REQUIRED)
|
69
73
|
|
70
74
|
|
71
75
|
class GhIssueComment(Protocol):
|
@@ -171,8 +175,7 @@ def _retry_on_github(
|
|
171
175
|
raise
|
172
176
|
delay = _backoff_delay(attempt)
|
173
177
|
log.warning(
|
174
|
-
"GitHub call failed (attempt %d): %s; retrying in "
|
175
|
-
"%.2fs",
|
178
|
+
"GitHub call failed (attempt %d): %s; retrying in %.2fs",
|
176
179
|
attempt,
|
177
180
|
exc,
|
178
181
|
delay,
|
@@ -201,7 +204,7 @@ def build_client(token: str | None = None) -> GhClient:
|
|
201
204
|
"""
|
202
205
|
tok = token or _getenv_str("GITHUB_TOKEN")
|
203
206
|
if not tok:
|
204
|
-
raise ValueError(
|
207
|
+
raise ValueError(_MSG_MISSING_GITHUB_TOKEN)
|
205
208
|
# per_page improves pagination; adjust as needed.
|
206
209
|
base_url = _getenv_str("GITHUB_API_URL")
|
207
210
|
if not base_url:
|
@@ -215,23 +218,17 @@ def build_client(token: str | None = None) -> GhClient:
|
|
215
218
|
if auth_factory is not None and hasattr(auth_factory, "Token"):
|
216
219
|
auth_obj = auth_factory.Token(tok)
|
217
220
|
if base_url:
|
218
|
-
client_any = Github(
|
219
|
-
auth=auth_obj, per_page=100, base_url=base_url
|
220
|
-
)
|
221
|
+
client_any = Github(auth=auth_obj, per_page=100, base_url=base_url)
|
221
222
|
else:
|
222
223
|
client_any = Github(auth=auth_obj, per_page=100)
|
223
224
|
else:
|
224
225
|
if base_url:
|
225
|
-
client_any = Github(
|
226
|
-
login_or_token=tok, per_page=100, base_url=base_url
|
227
|
-
)
|
226
|
+
client_any = Github(login_or_token=tok, per_page=100, base_url=base_url)
|
228
227
|
else:
|
229
228
|
client_any = Github(login_or_token=tok, per_page=100)
|
230
229
|
except Exception:
|
231
230
|
if base_url:
|
232
|
-
client_any = Github(
|
233
|
-
login_or_token=tok, per_page=100, base_url=base_url
|
234
|
-
)
|
231
|
+
client_any = Github(login_or_token=tok, per_page=100, base_url=base_url)
|
235
232
|
else:
|
236
233
|
client_any = Github(login_or_token=tok, per_page=100)
|
237
234
|
return cast(GhClient, client_any)
|
@@ -242,7 +239,7 @@ def get_repo_from_env(client: GhClient) -> GhRepository:
|
|
242
239
|
"""Return the repository object based on GITHUB_REPOSITORY."""
|
243
240
|
full = _getenv_str("GITHUB_REPOSITORY")
|
244
241
|
if not full or "/" not in full:
|
245
|
-
raise ValueError(
|
242
|
+
raise ValueError(_MSG_BAD_GITHUB_REPOSITORY)
|
246
243
|
repo = client.get_repo(full)
|
247
244
|
return repo
|
248
245
|
|
@@ -327,7 +324,5 @@ def close_pr(pr: GhPullRequest, *, comment: str | None = None) -> None:
|
|
327
324
|
try:
|
328
325
|
create_pr_comment(pr, comment)
|
329
326
|
except Exception as exc:
|
330
|
-
log.warning(
|
331
|
-
"Failed to add close comment to PR #%s: %s", pr.number, exc
|
332
|
-
)
|
327
|
+
log.warning("Failed to add close comment to PR #%s: %s", pr.number, exc)
|
333
328
|
pr.edit(state="closed")
|
github2gerrit/gitutils.py
CHANGED
@@ -20,6 +20,20 @@ from collections.abc import Mapping
|
|
20
20
|
from collections.abc import Sequence
|
21
21
|
from dataclasses import dataclass
|
22
22
|
from pathlib import Path
|
23
|
+
from typing import Any
|
24
|
+
|
25
|
+
|
26
|
+
def _is_verbose_mode() -> bool:
|
27
|
+
"""Check if verbose mode is enabled via environment variable."""
|
28
|
+
return os.getenv("G2G_VERBOSE", "").lower() in ("true", "1", "yes")
|
29
|
+
|
30
|
+
|
31
|
+
def _log_exception_conditionally(logger: logging.Logger, message: str, *args: Any) -> None:
|
32
|
+
"""Log exception with traceback only if verbose mode is enabled."""
|
33
|
+
if _is_verbose_mode():
|
34
|
+
logger.exception(message, *args)
|
35
|
+
else:
|
36
|
+
logger.error(message, *args)
|
23
37
|
|
24
38
|
|
25
39
|
__all__ = [
|
@@ -41,6 +55,9 @@ __all__ = [
|
|
41
55
|
"run_cmd_with_retries",
|
42
56
|
]
|
43
57
|
|
58
|
+
# Error message constants to comply with TRY003
|
59
|
+
_MSG_COMMIT_NO_MESSAGE = "Either message or message_file must be provided"
|
60
|
+
|
44
61
|
|
45
62
|
_LOGGER_NAME = "github2gerrit.git"
|
46
63
|
log = logging.getLogger(_LOGGER_NAME)
|
@@ -48,10 +65,7 @@ if not log.handlers:
|
|
48
65
|
# Provide a minimal default if the app has not configured logging.
|
49
66
|
level_name = os.getenv("G2G_LOG_LEVEL", "INFO").upper()
|
50
67
|
level = getattr(logging, level_name, logging.INFO)
|
51
|
-
fmt = (
|
52
|
-
"%(asctime)s %(levelname)-8s %(name)s "
|
53
|
-
"%(filename)s:%(lineno)d | %(message)s"
|
54
|
-
)
|
68
|
+
fmt = "%(asctime)s %(levelname)-8s %(name)s %(filename)s:%(lineno)d | %(message)s"
|
55
69
|
logging.basicConfig(level=level, format=fmt)
|
56
70
|
|
57
71
|
|
@@ -170,6 +184,7 @@ def run_cmd(
|
|
170
184
|
- Raises CommandError on failure when check=True.
|
171
185
|
"""
|
172
186
|
masks = list(masks or [])
|
187
|
+
env = env or non_interactive_env()
|
173
188
|
env_full = _merge_env(None, env)
|
174
189
|
|
175
190
|
log.debug("Executing: %s", _format_cmd_for_log(cmd, masks))
|
@@ -187,7 +202,7 @@ def run_cmd(
|
|
187
202
|
)
|
188
203
|
except subprocess.TimeoutExpired as exc:
|
189
204
|
msg = f"Command timed out: {cmd!r}"
|
190
|
-
log
|
205
|
+
_log_exception_conditionally(log, msg)
|
191
206
|
# TimeoutExpired carries 'output' and 'stderr' attributes,
|
192
207
|
# which may be bytes depending on invocation context.
|
193
208
|
out = getattr(exc, "output", None)
|
@@ -201,7 +216,7 @@ def run_cmd(
|
|
201
216
|
) from exc
|
202
217
|
except OSError as exc:
|
203
218
|
msg = f"Failed to execute command: {cmd!r} ({exc})"
|
204
|
-
log
|
219
|
+
_log_exception_conditionally(log, msg)
|
205
220
|
raise CommandError(msg, cmd=cmd) from exc
|
206
221
|
|
207
222
|
result = CommandResult(
|
@@ -235,6 +250,29 @@ def run_cmd(
|
|
235
250
|
return result
|
236
251
|
|
237
252
|
|
253
|
+
def non_interactive_env() -> dict[str, str]:
|
254
|
+
"""Return a non-interactive SSH/Git environment to bypass local
|
255
|
+
agents/keychains."""
|
256
|
+
return {
|
257
|
+
"GIT_SSH_COMMAND": (
|
258
|
+
"ssh -F /dev/null "
|
259
|
+
"-o IdentitiesOnly=yes "
|
260
|
+
"-o IdentityAgent=none "
|
261
|
+
"-o BatchMode=yes "
|
262
|
+
"-o PreferredAuthentications=publickey "
|
263
|
+
"-o StrictHostKeyChecking=yes "
|
264
|
+
"-o PasswordAuthentication=no "
|
265
|
+
"-o PubkeyAcceptedKeyTypes=+ssh-rsa "
|
266
|
+
"-o ConnectTimeout=10"
|
267
|
+
),
|
268
|
+
"SSH_AUTH_SOCK": "",
|
269
|
+
"SSH_AGENT_PID": "",
|
270
|
+
"SSH_ASKPASS": "/usr/bin/false",
|
271
|
+
"DISPLAY": "",
|
272
|
+
"SSH_ASKPASS_REQUIRE": "never",
|
273
|
+
}
|
274
|
+
|
275
|
+
|
238
276
|
def run_cmd_with_retries(
|
239
277
|
cmd: Sequence[str],
|
240
278
|
*,
|
@@ -252,6 +290,7 @@ def run_cmd_with_retries(
|
|
252
290
|
The default retry predicate considers common transient git errors.
|
253
291
|
"""
|
254
292
|
masks = list(masks or [])
|
293
|
+
env = env or non_interactive_env()
|
255
294
|
|
256
295
|
def _default_retry_on(res: CommandResult) -> bool:
|
257
296
|
return res.returncode != 0 and _is_transient_git_error(res.stderr)
|
@@ -289,8 +328,7 @@ def run_cmd_with_retries(
|
|
289
328
|
if predicate(res):
|
290
329
|
delay = _backoff_delay(attempt)
|
291
330
|
log.warning(
|
292
|
-
"Retrying (attempt %d) after transient error; delay %.1fs. "
|
293
|
-
"cmd=%s",
|
331
|
+
"Retrying (attempt %d) after transient error; delay %.1fs. cmd=%s",
|
294
332
|
attempt,
|
295
333
|
delay,
|
296
334
|
_format_cmd_for_log(cmd, masks),
|
@@ -351,7 +389,7 @@ def git_quiet(
|
|
351
389
|
return run_cmd(
|
352
390
|
cmd,
|
353
391
|
cwd=cwd,
|
354
|
-
env=env,
|
392
|
+
env=env or non_interactive_env(),
|
355
393
|
timeout=timeout,
|
356
394
|
check=False,
|
357
395
|
)
|
@@ -471,7 +509,7 @@ def git_commit_new(
|
|
471
509
|
) -> None:
|
472
510
|
"""Create a new commit using message or message_file."""
|
473
511
|
if not message and not message_file:
|
474
|
-
raise ValueError(
|
512
|
+
raise ValueError(_MSG_COMMIT_NO_MESSAGE)
|
475
513
|
|
476
514
|
args: list[str] = ["commit"]
|
477
515
|
if signoff:
|
@@ -599,9 +637,7 @@ def git_config_get_all(
|
|
599
637
|
try:
|
600
638
|
res = git_quiet(args, cwd=None)
|
601
639
|
if res.returncode == 0:
|
602
|
-
values = [
|
603
|
-
ln.strip() for ln in res.stdout.splitlines() if ln.strip()
|
604
|
-
]
|
640
|
+
values = [ln.strip() for ln in res.stdout.splitlines() if ln.strip()]
|
605
641
|
return values
|
606
642
|
else:
|
607
643
|
return []
|