git-commit-message 0.5.0__tar.gz → 0.6.0__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.
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/PKG-INFO +34 -10
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/README.md +31 -9
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/pyproject.toml +3 -1
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message/__init__.py +1 -3
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message/_cli.py +66 -27
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message/_git.py +13 -13
- git_commit_message-0.6.0/src/git_commit_message/_gpt.py +604 -0
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message.egg-info/PKG-INFO +34 -10
- git_commit_message-0.6.0/src/git_commit_message.egg-info/requires.txt +3 -0
- git_commit_message-0.5.0/src/git_commit_message/_gpt.py +0 -284
- git_commit_message-0.5.0/src/git_commit_message.egg-info/requires.txt +0 -1
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/UNLICENSE +0 -0
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/setup.cfg +0 -0
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message/__main__.py +0 -0
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message.egg-info/SOURCES.txt +0 -0
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message.egg-info/dependency_links.txt +0 -0
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message.egg-info/entry_points.txt +0 -0
- {git_commit_message-0.5.0 → git_commit_message-0.6.0}/src/git_commit_message.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: git-commit-message
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Generate Git commit messages from staged changes using OpenAI GPT
|
|
5
5
|
Maintainer-email: Mina Her <minacle@live.com>
|
|
6
6
|
License: This is free and unencumbered software released into the public domain.
|
|
@@ -43,17 +43,21 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
43
43
|
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
44
44
|
Requires-Python: >=3.13
|
|
45
45
|
Description-Content-Type: text/markdown
|
|
46
|
+
Requires-Dist: babel>=2.17.0
|
|
46
47
|
Requires-Dist: openai>=2.6.1
|
|
48
|
+
Requires-Dist: tiktoken>=0.12.0
|
|
47
49
|
|
|
48
50
|
# git-commit-message
|
|
49
51
|
|
|
50
52
|
Staged changes -> GPT commit message generator.
|
|
51
53
|
|
|
54
|
+
[](https://asciinema.org/a/jk0phFqNnc5vaCiIZEYBwZOyN)
|
|
55
|
+
|
|
52
56
|
## Install (PyPI)
|
|
53
57
|
|
|
54
58
|
Install the latest released version from PyPI:
|
|
55
59
|
|
|
56
|
-
```
|
|
60
|
+
```sh
|
|
57
61
|
# User environment (recommended)
|
|
58
62
|
python -m pip install --user git-commit-message
|
|
59
63
|
|
|
@@ -69,11 +73,17 @@ python -m pip install --upgrade git-commit-message
|
|
|
69
73
|
|
|
70
74
|
Quick check:
|
|
71
75
|
|
|
72
|
-
```
|
|
76
|
+
```sh
|
|
73
77
|
git-commit-message --help
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
Set your API key (
|
|
80
|
+
Set your API key (POSIX sh):
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
export OPENAI_API_KEY="sk-..."
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Note (fish): In fish, set it as follows.
|
|
77
87
|
|
|
78
88
|
```fish
|
|
79
89
|
set -x OPENAI_API_KEY "sk-..."
|
|
@@ -81,7 +91,7 @@ set -x OPENAI_API_KEY "sk-..."
|
|
|
81
91
|
|
|
82
92
|
## Install (editable)
|
|
83
93
|
|
|
84
|
-
```
|
|
94
|
+
```sh
|
|
85
95
|
python -m pip install -e .
|
|
86
96
|
```
|
|
87
97
|
|
|
@@ -89,32 +99,45 @@ python -m pip install -e .
|
|
|
89
99
|
|
|
90
100
|
- Print commit message only:
|
|
91
101
|
|
|
92
|
-
```
|
|
102
|
+
```sh
|
|
93
103
|
git add -A
|
|
94
104
|
git-commit-message "optional extra context about the change"
|
|
95
105
|
```
|
|
96
106
|
|
|
97
107
|
- Force single-line subject only:
|
|
98
108
|
|
|
99
|
-
```
|
|
109
|
+
```sh
|
|
100
110
|
git-commit-message --one-line "optional context"
|
|
101
111
|
```
|
|
102
112
|
|
|
103
113
|
- Limit subject length (default 72):
|
|
104
114
|
|
|
105
|
-
```
|
|
115
|
+
```sh
|
|
106
116
|
git-commit-message --one-line --max-length 50 "optional context"
|
|
107
117
|
```
|
|
108
118
|
|
|
119
|
+
- Chunk long diffs by token budget (0 = single chunk + summary, -1 = disable chunking):
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
# force a single summary pass over the whole diff (default)
|
|
123
|
+
git-commit-message --chunk-tokens 0 "optional context"
|
|
124
|
+
|
|
125
|
+
# chunk the diff into ~4000-token pieces before summarising
|
|
126
|
+
git-commit-message --chunk-tokens 4000 "optional context"
|
|
127
|
+
|
|
128
|
+
# disable summarisation and use the legacy one-shot prompt
|
|
129
|
+
git-commit-message --chunk-tokens -1 "optional context"
|
|
130
|
+
```
|
|
131
|
+
|
|
109
132
|
- Commit immediately with editor:
|
|
110
133
|
|
|
111
|
-
```
|
|
134
|
+
```sh
|
|
112
135
|
git-commit-message --commit --edit "refactor parser for speed"
|
|
113
136
|
```
|
|
114
137
|
|
|
115
138
|
- Select output language/locale (default: en-GB):
|
|
116
139
|
|
|
117
|
-
```
|
|
140
|
+
```sh
|
|
118
141
|
# American English
|
|
119
142
|
git-commit-message --language en-US "optional context"
|
|
120
143
|
|
|
@@ -135,6 +158,7 @@ Environment:
|
|
|
135
158
|
- `OPENAI_API_KEY`: required
|
|
136
159
|
- `GIT_COMMIT_MESSAGE_MODEL` or `OPENAI_MODEL`: optional (default: `gpt-5-mini`)
|
|
137
160
|
- `GIT_COMMIT_MESSAGE_LANGUAGE`: optional (default: `en-GB`)
|
|
161
|
+
- `GIT_COMMIT_MESSAGE_CHUNK_TOKENS`: optional token budget per diff chunk (default: 0 = single chunk + summary; -1 disables summarisation)
|
|
138
162
|
|
|
139
163
|
## AI‑generated code notice
|
|
140
164
|
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Staged changes -> GPT commit message generator.
|
|
4
4
|
|
|
5
|
+
[](https://asciinema.org/a/jk0phFqNnc5vaCiIZEYBwZOyN)
|
|
6
|
+
|
|
5
7
|
## Install (PyPI)
|
|
6
8
|
|
|
7
9
|
Install the latest released version from PyPI:
|
|
8
10
|
|
|
9
|
-
```
|
|
11
|
+
```sh
|
|
10
12
|
# User environment (recommended)
|
|
11
13
|
python -m pip install --user git-commit-message
|
|
12
14
|
|
|
@@ -22,11 +24,17 @@ python -m pip install --upgrade git-commit-message
|
|
|
22
24
|
|
|
23
25
|
Quick check:
|
|
24
26
|
|
|
25
|
-
```
|
|
27
|
+
```sh
|
|
26
28
|
git-commit-message --help
|
|
27
29
|
```
|
|
28
30
|
|
|
29
|
-
Set your API key (
|
|
31
|
+
Set your API key (POSIX sh):
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
export OPENAI_API_KEY="sk-..."
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Note (fish): In fish, set it as follows.
|
|
30
38
|
|
|
31
39
|
```fish
|
|
32
40
|
set -x OPENAI_API_KEY "sk-..."
|
|
@@ -34,7 +42,7 @@ set -x OPENAI_API_KEY "sk-..."
|
|
|
34
42
|
|
|
35
43
|
## Install (editable)
|
|
36
44
|
|
|
37
|
-
```
|
|
45
|
+
```sh
|
|
38
46
|
python -m pip install -e .
|
|
39
47
|
```
|
|
40
48
|
|
|
@@ -42,32 +50,45 @@ python -m pip install -e .
|
|
|
42
50
|
|
|
43
51
|
- Print commit message only:
|
|
44
52
|
|
|
45
|
-
```
|
|
53
|
+
```sh
|
|
46
54
|
git add -A
|
|
47
55
|
git-commit-message "optional extra context about the change"
|
|
48
56
|
```
|
|
49
57
|
|
|
50
58
|
- Force single-line subject only:
|
|
51
59
|
|
|
52
|
-
```
|
|
60
|
+
```sh
|
|
53
61
|
git-commit-message --one-line "optional context"
|
|
54
62
|
```
|
|
55
63
|
|
|
56
64
|
- Limit subject length (default 72):
|
|
57
65
|
|
|
58
|
-
```
|
|
66
|
+
```sh
|
|
59
67
|
git-commit-message --one-line --max-length 50 "optional context"
|
|
60
68
|
```
|
|
61
69
|
|
|
70
|
+
- Chunk long diffs by token budget (0 = single chunk + summary, -1 = disable chunking):
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
# force a single summary pass over the whole diff (default)
|
|
74
|
+
git-commit-message --chunk-tokens 0 "optional context"
|
|
75
|
+
|
|
76
|
+
# chunk the diff into ~4000-token pieces before summarising
|
|
77
|
+
git-commit-message --chunk-tokens 4000 "optional context"
|
|
78
|
+
|
|
79
|
+
# disable summarisation and use the legacy one-shot prompt
|
|
80
|
+
git-commit-message --chunk-tokens -1 "optional context"
|
|
81
|
+
```
|
|
82
|
+
|
|
62
83
|
- Commit immediately with editor:
|
|
63
84
|
|
|
64
|
-
```
|
|
85
|
+
```sh
|
|
65
86
|
git-commit-message --commit --edit "refactor parser for speed"
|
|
66
87
|
```
|
|
67
88
|
|
|
68
89
|
- Select output language/locale (default: en-GB):
|
|
69
90
|
|
|
70
|
-
```
|
|
91
|
+
```sh
|
|
71
92
|
# American English
|
|
72
93
|
git-commit-message --language en-US "optional context"
|
|
73
94
|
|
|
@@ -88,6 +109,7 @@ Environment:
|
|
|
88
109
|
- `OPENAI_API_KEY`: required
|
|
89
110
|
- `GIT_COMMIT_MESSAGE_MODEL` or `OPENAI_MODEL`: optional (default: `gpt-5-mini`)
|
|
90
111
|
- `GIT_COMMIT_MESSAGE_LANGUAGE`: optional (default: `en-GB`)
|
|
112
|
+
- `GIT_COMMIT_MESSAGE_CHUNK_TOKENS`: optional token budget per diff chunk (default: 0 = single chunk + summary; -1 disables summarisation)
|
|
91
113
|
|
|
92
114
|
## AI‑generated code notice
|
|
93
115
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "git-commit-message"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "Generate Git commit messages from staged changes using OpenAI GPT"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
7
|
dependencies = [
|
|
8
|
+
"babel>=2.17.0",
|
|
8
9
|
"openai>=2.6.1",
|
|
10
|
+
"tiktoken>=0.12.0",
|
|
9
11
|
]
|
|
10
12
|
maintainers = [{ name = "Mina Her", email = "minacle@live.com" }]
|
|
11
13
|
license = { file = "UNLICENSE" }
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Command-line interface entry point.
|
|
4
2
|
|
|
5
3
|
Collect staged changes from the repository and call an OpenAI GPT model
|
|
6
4
|
to generate a commit message, or create a commit straight away.
|
|
7
5
|
"""
|
|
8
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
9
|
from argparse import ArgumentParser, Namespace
|
|
10
|
+
from os import environ
|
|
10
11
|
from pathlib import Path
|
|
11
|
-
import
|
|
12
|
+
from sys import exit as sys_exit
|
|
13
|
+
from sys import stderr
|
|
12
14
|
from typing import Final
|
|
13
15
|
|
|
14
|
-
from ._git import
|
|
16
|
+
from ._git import (
|
|
17
|
+
commit_with_message,
|
|
18
|
+
get_repo_root,
|
|
19
|
+
get_staged_diff,
|
|
20
|
+
has_staged_changes,
|
|
21
|
+
)
|
|
15
22
|
from ._gpt import (
|
|
16
23
|
generate_commit_message,
|
|
17
24
|
generate_commit_message_with_info,
|
|
@@ -19,6 +26,18 @@ from ._gpt import (
|
|
|
19
26
|
)
|
|
20
27
|
|
|
21
28
|
|
|
29
|
+
def _env_chunk_tokens_default() -> int | None:
|
|
30
|
+
"""Return chunk token default from env if valid, else None."""
|
|
31
|
+
|
|
32
|
+
raw: str | None = environ.get("GIT_COMMIT_MESSAGE_CHUNK_TOKENS")
|
|
33
|
+
if raw is None:
|
|
34
|
+
return None
|
|
35
|
+
try:
|
|
36
|
+
return int(raw)
|
|
37
|
+
except ValueError:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
22
41
|
def _build_parser() -> ArgumentParser:
|
|
23
42
|
"""Create the CLI argument parser.
|
|
24
43
|
|
|
@@ -92,12 +111,24 @@ def _build_parser() -> ArgumentParser:
|
|
|
92
111
|
help="Maximum subject (first line) length (default: 72).",
|
|
93
112
|
)
|
|
94
113
|
|
|
114
|
+
parser.add_argument(
|
|
115
|
+
"--chunk-tokens",
|
|
116
|
+
dest="chunk_tokens",
|
|
117
|
+
type=int,
|
|
118
|
+
default=None,
|
|
119
|
+
help=(
|
|
120
|
+
"Target token budget per diff chunk. "
|
|
121
|
+
"0 forces a single chunk with summarisation; -1 disables summarisation (legacy one-shot). "
|
|
122
|
+
"If omitted, uses GIT_COMMIT_MESSAGE_CHUNK_TOKENS when set (default: 0)."
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
|
|
95
126
|
return parser
|
|
96
127
|
|
|
97
128
|
|
|
98
129
|
def _run(
|
|
99
|
-
*,
|
|
100
130
|
args: Namespace,
|
|
131
|
+
/,
|
|
101
132
|
) -> int:
|
|
102
133
|
"""Main execution logic.
|
|
103
134
|
|
|
@@ -114,37 +145,45 @@ def _run(
|
|
|
114
145
|
|
|
115
146
|
repo_root: Path = get_repo_root()
|
|
116
147
|
|
|
117
|
-
if not has_staged_changes(
|
|
118
|
-
print("No staged changes. Run 'git add' and try again.", file=
|
|
148
|
+
if not has_staged_changes(repo_root):
|
|
149
|
+
print("No staged changes. Run 'git add' and try again.", file=stderr)
|
|
119
150
|
return 2
|
|
120
151
|
|
|
121
|
-
diff_text: str = get_staged_diff(
|
|
152
|
+
diff_text: str = get_staged_diff(repo_root)
|
|
122
153
|
|
|
123
154
|
hint: str | None = args.description if isinstance(args.description, str) else None
|
|
124
155
|
|
|
156
|
+
chunk_tokens: int | None = args.chunk_tokens
|
|
157
|
+
if chunk_tokens is None:
|
|
158
|
+
chunk_tokens = _env_chunk_tokens_default()
|
|
159
|
+
if chunk_tokens is None:
|
|
160
|
+
chunk_tokens = 0
|
|
161
|
+
|
|
125
162
|
result: CommitMessageResult | None = None
|
|
126
163
|
try:
|
|
127
164
|
if args.debug:
|
|
128
165
|
result = generate_commit_message_with_info(
|
|
129
|
-
|
|
130
|
-
hint
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
166
|
+
diff_text,
|
|
167
|
+
hint,
|
|
168
|
+
args.model,
|
|
169
|
+
getattr(args, "one_line", False),
|
|
170
|
+
getattr(args, "max_length", None),
|
|
171
|
+
getattr(args, "language", None),
|
|
172
|
+
chunk_tokens,
|
|
135
173
|
)
|
|
136
174
|
message = result.message
|
|
137
175
|
else:
|
|
138
176
|
message = generate_commit_message(
|
|
139
|
-
|
|
140
|
-
hint
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
177
|
+
diff_text,
|
|
178
|
+
hint,
|
|
179
|
+
args.model,
|
|
180
|
+
getattr(args, "one_line", False),
|
|
181
|
+
getattr(args, "max_length", None),
|
|
182
|
+
getattr(args, "language", None),
|
|
183
|
+
chunk_tokens,
|
|
145
184
|
)
|
|
146
185
|
except Exception as exc: # noqa: BLE001 - to preserve standard output messaging
|
|
147
|
-
print(f"Failed to generate commit message: {exc}", file=
|
|
186
|
+
print(f"Failed to generate commit message: {exc}", file=stderr)
|
|
148
187
|
return 3
|
|
149
188
|
|
|
150
189
|
# Option: force single-line message
|
|
@@ -198,9 +237,9 @@ def _run(
|
|
|
198
237
|
print(message)
|
|
199
238
|
|
|
200
239
|
if args.edit:
|
|
201
|
-
rc: int = commit_with_message(message
|
|
240
|
+
rc: int = commit_with_message(message, True, repo_root)
|
|
202
241
|
else:
|
|
203
|
-
rc = commit_with_message(message
|
|
242
|
+
rc = commit_with_message(message, False, repo_root)
|
|
204
243
|
|
|
205
244
|
return rc
|
|
206
245
|
|
|
@@ -215,8 +254,8 @@ def main() -> None:
|
|
|
215
254
|
args: Namespace = parser.parse_args()
|
|
216
255
|
|
|
217
256
|
if args.edit and not args.commit:
|
|
218
|
-
print("'--edit' must be used together with '--commit'.", file=
|
|
219
|
-
|
|
257
|
+
print("'--edit' must be used together with '--commit'.", file=stderr)
|
|
258
|
+
sys_exit(2)
|
|
220
259
|
|
|
221
|
-
code: int = _run(args
|
|
222
|
-
|
|
260
|
+
code: int = _run(args)
|
|
261
|
+
sys_exit(code)
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Git-related helper functions.
|
|
4
2
|
|
|
5
3
|
Provides repository root discovery, extraction of staged changes, and
|
|
6
4
|
creating commits from a message.
|
|
7
5
|
"""
|
|
8
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
import
|
|
10
|
+
from subprocess import CalledProcessError, check_call, check_output, run
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def get_repo_root(
|
|
14
|
-
*,
|
|
15
14
|
cwd: Path | None = None,
|
|
15
|
+
/,
|
|
16
16
|
) -> Path:
|
|
17
17
|
"""Find the repository root from the current working directory.
|
|
18
18
|
|
|
@@ -29,7 +29,7 @@ def get_repo_root(
|
|
|
29
29
|
|
|
30
30
|
start: Path = cwd or Path.cwd()
|
|
31
31
|
try:
|
|
32
|
-
out: bytes =
|
|
32
|
+
out: bytes = check_output(
|
|
33
33
|
[
|
|
34
34
|
"git",
|
|
35
35
|
"rev-parse",
|
|
@@ -37,7 +37,7 @@ def get_repo_root(
|
|
|
37
37
|
],
|
|
38
38
|
cwd=str(start),
|
|
39
39
|
)
|
|
40
|
-
except
|
|
40
|
+
except CalledProcessError as exc: # noqa: TRY003
|
|
41
41
|
raise RuntimeError("Not a Git repository.") from exc
|
|
42
42
|
|
|
43
43
|
root = Path(out.decode().strip())
|
|
@@ -45,28 +45,28 @@ def get_repo_root(
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def has_staged_changes(
|
|
48
|
-
*,
|
|
49
48
|
cwd: Path,
|
|
49
|
+
/,
|
|
50
50
|
) -> bool:
|
|
51
51
|
"""Check whether there are staged changes."""
|
|
52
52
|
|
|
53
53
|
try:
|
|
54
|
-
|
|
54
|
+
check_call(
|
|
55
55
|
["git", "diff", "--cached", "--quiet", "--exit-code"],
|
|
56
56
|
cwd=str(cwd),
|
|
57
57
|
)
|
|
58
58
|
return False
|
|
59
|
-
except
|
|
59
|
+
except CalledProcessError:
|
|
60
60
|
return True
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
def get_staged_diff(
|
|
64
|
-
*,
|
|
65
64
|
cwd: Path,
|
|
65
|
+
/,
|
|
66
66
|
) -> str:
|
|
67
67
|
"""Return the staged changes as diff text."""
|
|
68
68
|
|
|
69
|
-
out: bytes =
|
|
69
|
+
out: bytes = check_output(
|
|
70
70
|
[
|
|
71
71
|
"git",
|
|
72
72
|
"diff",
|
|
@@ -81,10 +81,10 @@ def get_staged_diff(
|
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
def commit_with_message(
|
|
84
|
-
*,
|
|
85
84
|
message: str,
|
|
86
85
|
edit: bool,
|
|
87
86
|
cwd: Path,
|
|
87
|
+
/,
|
|
88
88
|
) -> int:
|
|
89
89
|
"""Create a commit with the given message.
|
|
90
90
|
|
|
@@ -108,7 +108,7 @@ def commit_with_message(
|
|
|
108
108
|
cmd.append("--edit")
|
|
109
109
|
|
|
110
110
|
try:
|
|
111
|
-
completed =
|
|
111
|
+
completed = run(cmd, cwd=str(cwd), check=False)
|
|
112
112
|
return int(completed.returncode)
|
|
113
113
|
except OSError as exc: # e.g., editor launch failure, etc.
|
|
114
114
|
raise RuntimeError(f"Failed to run 'git commit': {exc}") from exc
|