gac 2.1.0__tar.gz → 2.3.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.
Potentially problematic release.
This version of gac might be problematic. Click here for more details.
- {gac-2.1.0 → gac-2.3.0}/PKG-INFO +21 -4
- {gac-2.1.0 → gac-2.3.0}/README.md +19 -3
- {gac-2.1.0 → gac-2.3.0}/pyproject.toml +1 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/__version__.py +1 -1
- {gac-2.1.0 → gac-2.3.0}/src/gac/ai.py +26 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/ai_utils.py +28 -13
- {gac-2.1.0 → gac-2.3.0}/src/gac/cli.py +4 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/constants.py +2 -2
- {gac-2.1.0 → gac-2.3.0}/src/gac/git.py +42 -0
- gac-2.3.0/src/gac/init_cli.py +316 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/language_cli.py +8 -8
- gac-2.3.0/src/gac/main.py +718 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/prompt.py +101 -15
- {gac-2.1.0 → gac-2.3.0}/src/gac/security.py +1 -1
- gac-2.3.0/src/gac/utils.py +270 -0
- gac-2.3.0/src/gac/workflow_utils.py +131 -0
- gac-2.1.0/src/gac/init_cli.py +0 -203
- gac-2.1.0/src/gac/main.py +0 -369
- gac-2.1.0/src/gac/utils.py +0 -132
- {gac-2.1.0 → gac-2.3.0}/.gitignore +0 -0
- {gac-2.1.0 → gac-2.3.0}/LICENSE +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/__init__.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/config.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/config_cli.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/diff_cli.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/errors.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/preprocess.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/__init__.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/anthropic.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/cerebras.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/chutes.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/custom_anthropic.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/custom_openai.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/deepseek.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/fireworks.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/gemini.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/groq.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/lmstudio.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/minimax.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/mistral.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/ollama.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/openai.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/openrouter.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/streamlake.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/synthetic.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/together.py +0 -0
- {gac-2.1.0 → gac-2.3.0}/src/gac/providers/zai.py +0 -0
{gac-2.1.0 → gac-2.3.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gac
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: LLM-powered Git commit message generator with multi-provider support
|
|
5
5
|
Project-URL: Homepage, https://github.com/cellwebb/gac
|
|
6
6
|
Project-URL: Documentation, https://github.com/cellwebb/gac#readme
|
|
@@ -25,6 +25,7 @@ Requires-Dist: click>=8.3.0
|
|
|
25
25
|
Requires-Dist: halo
|
|
26
26
|
Requires-Dist: httpcore>=1.0.9
|
|
27
27
|
Requires-Dist: httpx>=0.28.0
|
|
28
|
+
Requires-Dist: prompt-toolkit>=3.0.36
|
|
28
29
|
Requires-Dist: pydantic>=2.12.0
|
|
29
30
|
Requires-Dist: python-dotenv>=1.1.1
|
|
30
31
|
Requires-Dist: questionary
|
|
@@ -107,6 +108,7 @@ uv tool upgrade gac
|
|
|
107
108
|
- **Understands intent**: Analyzes code structure, logic, and patterns to understand the "why" behind your changes, not just what changed
|
|
108
109
|
- **Semantic awareness**: Recognizes refactoring, bug fixes, features, and breaking changes to generate contextually appropriate messages
|
|
109
110
|
- **Intelligent filtering**: Prioritizes meaningful changes while ignoring generated files, dependencies, and artifacts
|
|
111
|
+
- **Intelligent commit grouping** - Automatically group related changes into multiple logical commits with `--group`
|
|
110
112
|
|
|
111
113
|
### 📝 **Multiple Message Formats**
|
|
112
114
|
|
|
@@ -123,7 +125,7 @@ uv tool upgrade gac
|
|
|
123
125
|
|
|
124
126
|
### 💻 **Developer Experience**
|
|
125
127
|
|
|
126
|
-
- **Interactive feedback**: Type `r` to reroll, or directly type your feedback like `make it shorter` or `focus on the bug fix`
|
|
128
|
+
- **Interactive feedback**: Type `r` to reroll, `e` to edit in-place with vi/emacs keybindings, or directly type your feedback like `make it shorter` or `focus on the bug fix`
|
|
127
129
|
- **One-command workflows**: Complete workflows with flags like `gac -ayp` (stage all, auto-confirm, push)
|
|
128
130
|
- **Git integration**: Respects pre-commit and lefthook hooks, running them before expensive LLM operations
|
|
129
131
|
|
|
@@ -146,7 +148,7 @@ git add .
|
|
|
146
148
|
# Generate and commit with LLM
|
|
147
149
|
gac
|
|
148
150
|
|
|
149
|
-
# Review → y (commit) | n (cancel) | r (reroll) | or type feedback
|
|
151
|
+
# Review → y (commit) | n (cancel) | r (reroll) | e (edit) | or type feedback
|
|
150
152
|
```
|
|
151
153
|
|
|
152
154
|
### Common Commands
|
|
@@ -174,6 +176,9 @@ gac -v -s
|
|
|
174
176
|
# Quick one-liner for small changes
|
|
175
177
|
gac -o
|
|
176
178
|
|
|
179
|
+
# Group changes into logically related commits
|
|
180
|
+
gac -ag
|
|
181
|
+
|
|
177
182
|
# Debug what the LLM sees
|
|
178
183
|
gac --show-prompt
|
|
179
184
|
|
|
@@ -183,12 +188,17 @@ gac --skip-secret-scan
|
|
|
183
188
|
|
|
184
189
|
### Interactive Feedback System
|
|
185
190
|
|
|
186
|
-
Not happy with the result? You have
|
|
191
|
+
Not happy with the result? You have several options:
|
|
187
192
|
|
|
188
193
|
```bash
|
|
189
194
|
# Simple reroll (no feedback)
|
|
190
195
|
r
|
|
191
196
|
|
|
197
|
+
# Edit in-place with rich terminal editing
|
|
198
|
+
e
|
|
199
|
+
# Uses prompt_toolkit for multi-line editing with vi/emacs keybindings
|
|
200
|
+
# Press Esc+Enter or Ctrl+S to submit, Ctrl+C to cancel
|
|
201
|
+
|
|
192
202
|
# Or just type your feedback directly!
|
|
193
203
|
make it shorter and focus on the performance improvement
|
|
194
204
|
use conventional commit format with scope
|
|
@@ -197,6 +207,13 @@ explain the security implications
|
|
|
197
207
|
# Press Enter on empty input to see the prompt again
|
|
198
208
|
```
|
|
199
209
|
|
|
210
|
+
The edit feature (`e`) provides rich in-place terminal editing, allowing you to:
|
|
211
|
+
|
|
212
|
+
- **Edit naturally**: Multi-line editing with familiar vi/emacs key bindings
|
|
213
|
+
- **Make quick fixes**: Correct typos, adjust wording, or refine formatting
|
|
214
|
+
- **Add details**: Include information the LLM might have missed
|
|
215
|
+
- **Restructure**: Reorganize bullet points or change the message structure
|
|
216
|
+
|
|
200
217
|
---
|
|
201
218
|
|
|
202
219
|
## Configuration
|
|
@@ -66,6 +66,7 @@ uv tool upgrade gac
|
|
|
66
66
|
- **Understands intent**: Analyzes code structure, logic, and patterns to understand the "why" behind your changes, not just what changed
|
|
67
67
|
- **Semantic awareness**: Recognizes refactoring, bug fixes, features, and breaking changes to generate contextually appropriate messages
|
|
68
68
|
- **Intelligent filtering**: Prioritizes meaningful changes while ignoring generated files, dependencies, and artifacts
|
|
69
|
+
- **Intelligent commit grouping** - Automatically group related changes into multiple logical commits with `--group`
|
|
69
70
|
|
|
70
71
|
### 📝 **Multiple Message Formats**
|
|
71
72
|
|
|
@@ -82,7 +83,7 @@ uv tool upgrade gac
|
|
|
82
83
|
|
|
83
84
|
### 💻 **Developer Experience**
|
|
84
85
|
|
|
85
|
-
- **Interactive feedback**: Type `r` to reroll, or directly type your feedback like `make it shorter` or `focus on the bug fix`
|
|
86
|
+
- **Interactive feedback**: Type `r` to reroll, `e` to edit in-place with vi/emacs keybindings, or directly type your feedback like `make it shorter` or `focus on the bug fix`
|
|
86
87
|
- **One-command workflows**: Complete workflows with flags like `gac -ayp` (stage all, auto-confirm, push)
|
|
87
88
|
- **Git integration**: Respects pre-commit and lefthook hooks, running them before expensive LLM operations
|
|
88
89
|
|
|
@@ -105,7 +106,7 @@ git add .
|
|
|
105
106
|
# Generate and commit with LLM
|
|
106
107
|
gac
|
|
107
108
|
|
|
108
|
-
# Review → y (commit) | n (cancel) | r (reroll) | or type feedback
|
|
109
|
+
# Review → y (commit) | n (cancel) | r (reroll) | e (edit) | or type feedback
|
|
109
110
|
```
|
|
110
111
|
|
|
111
112
|
### Common Commands
|
|
@@ -133,6 +134,9 @@ gac -v -s
|
|
|
133
134
|
# Quick one-liner for small changes
|
|
134
135
|
gac -o
|
|
135
136
|
|
|
137
|
+
# Group changes into logically related commits
|
|
138
|
+
gac -ag
|
|
139
|
+
|
|
136
140
|
# Debug what the LLM sees
|
|
137
141
|
gac --show-prompt
|
|
138
142
|
|
|
@@ -142,12 +146,17 @@ gac --skip-secret-scan
|
|
|
142
146
|
|
|
143
147
|
### Interactive Feedback System
|
|
144
148
|
|
|
145
|
-
Not happy with the result? You have
|
|
149
|
+
Not happy with the result? You have several options:
|
|
146
150
|
|
|
147
151
|
```bash
|
|
148
152
|
# Simple reroll (no feedback)
|
|
149
153
|
r
|
|
150
154
|
|
|
155
|
+
# Edit in-place with rich terminal editing
|
|
156
|
+
e
|
|
157
|
+
# Uses prompt_toolkit for multi-line editing with vi/emacs keybindings
|
|
158
|
+
# Press Esc+Enter or Ctrl+S to submit, Ctrl+C to cancel
|
|
159
|
+
|
|
151
160
|
# Or just type your feedback directly!
|
|
152
161
|
make it shorter and focus on the performance improvement
|
|
153
162
|
use conventional commit format with scope
|
|
@@ -156,6 +165,13 @@ explain the security implications
|
|
|
156
165
|
# Press Enter on empty input to see the prompt again
|
|
157
166
|
```
|
|
158
167
|
|
|
168
|
+
The edit feature (`e`) provides rich in-place terminal editing, allowing you to:
|
|
169
|
+
|
|
170
|
+
- **Edit naturally**: Multi-line editing with familiar vi/emacs key bindings
|
|
171
|
+
- **Make quick fixes**: Correct typos, adjust wording, or refine formatting
|
|
172
|
+
- **Add details**: Include information the LLM might have missed
|
|
173
|
+
- **Restructure**: Reorganize bullet points or change the message structure
|
|
174
|
+
|
|
159
175
|
---
|
|
160
176
|
|
|
161
177
|
## Configuration
|
|
@@ -42,6 +42,8 @@ def generate_commit_message(
|
|
|
42
42
|
max_tokens: int = EnvDefaults.MAX_OUTPUT_TOKENS,
|
|
43
43
|
max_retries: int = EnvDefaults.MAX_RETRIES,
|
|
44
44
|
quiet: bool = False,
|
|
45
|
+
is_group: bool = False,
|
|
46
|
+
skip_success_message: bool = False,
|
|
45
47
|
) -> str:
|
|
46
48
|
"""Generate a commit message using direct API calls to AI providers.
|
|
47
49
|
|
|
@@ -116,6 +118,8 @@ def generate_commit_message(
|
|
|
116
118
|
max_tokens=max_tokens,
|
|
117
119
|
max_retries=max_retries,
|
|
118
120
|
quiet=quiet,
|
|
121
|
+
is_group=is_group,
|
|
122
|
+
skip_success_message=skip_success_message,
|
|
119
123
|
)
|
|
120
124
|
except AIError:
|
|
121
125
|
# Re-raise AIError exceptions as-is to preserve error classification
|
|
@@ -123,3 +127,25 @@ def generate_commit_message(
|
|
|
123
127
|
except Exception as e:
|
|
124
128
|
logger.error(f"Failed to generate commit message: {e}")
|
|
125
129
|
raise AIError.model_error(f"Failed to generate commit message: {e}") from e
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def generate_grouped_commits(
|
|
133
|
+
model: str,
|
|
134
|
+
prompt: list[dict[str, str]],
|
|
135
|
+
temperature: float,
|
|
136
|
+
max_tokens: int,
|
|
137
|
+
max_retries: int,
|
|
138
|
+
quiet: bool = False,
|
|
139
|
+
skip_success_message: bool = False,
|
|
140
|
+
) -> str:
|
|
141
|
+
"""Generate grouped commits JSON response."""
|
|
142
|
+
return generate_commit_message(
|
|
143
|
+
model=model,
|
|
144
|
+
prompt=prompt,
|
|
145
|
+
temperature=temperature,
|
|
146
|
+
max_tokens=max_tokens,
|
|
147
|
+
max_retries=max_retries,
|
|
148
|
+
quiet=quiet,
|
|
149
|
+
is_group=True,
|
|
150
|
+
skip_success_message=skip_success_message,
|
|
151
|
+
)
|
|
@@ -83,6 +83,8 @@ def generate_with_retries(
|
|
|
83
83
|
max_tokens: int,
|
|
84
84
|
max_retries: int,
|
|
85
85
|
quiet: bool = False,
|
|
86
|
+
is_group: bool = False,
|
|
87
|
+
skip_success_message: bool = False,
|
|
86
88
|
) -> str:
|
|
87
89
|
"""Generate content with retry logic using direct API calls."""
|
|
88
90
|
# Parse model string to determine provider and actual model
|
|
@@ -121,10 +123,11 @@ def generate_with_retries(
|
|
|
121
123
|
raise AIError.model_error("No messages provided for AI generation")
|
|
122
124
|
|
|
123
125
|
# Set up spinner
|
|
126
|
+
message_type = "commit messages" if is_group else "commit message"
|
|
124
127
|
if quiet:
|
|
125
128
|
spinner = None
|
|
126
129
|
else:
|
|
127
|
-
spinner = Halo(text=f"Generating
|
|
130
|
+
spinner = Halo(text=f"Generating {message_type} with {provider} {model_name}...", spinner="dots")
|
|
128
131
|
spinner.start()
|
|
129
132
|
|
|
130
133
|
last_exception = None
|
|
@@ -132,7 +135,7 @@ def generate_with_retries(
|
|
|
132
135
|
|
|
133
136
|
for attempt in range(max_retries):
|
|
134
137
|
try:
|
|
135
|
-
if not quiet and attempt > 0:
|
|
138
|
+
if not quiet and not skip_success_message and attempt > 0:
|
|
136
139
|
if spinner:
|
|
137
140
|
spinner.text = f"Retry {attempt + 1}/{max_retries} with {provider} {model_name}..."
|
|
138
141
|
logger.info(f"Retry attempt {attempt + 1}/{max_retries}")
|
|
@@ -145,7 +148,10 @@ def generate_with_retries(
|
|
|
145
148
|
content = provider_func(model=model_name, messages=messages, temperature=temperature, max_tokens=max_tokens)
|
|
146
149
|
|
|
147
150
|
if spinner:
|
|
148
|
-
|
|
151
|
+
if skip_success_message:
|
|
152
|
+
spinner.stop() # Stop spinner without showing success/failure
|
|
153
|
+
else:
|
|
154
|
+
spinner.succeed(f"Generated {message_type} with {provider} {model_name}")
|
|
149
155
|
|
|
150
156
|
if content is not None and content.strip():
|
|
151
157
|
return content.strip() # type: ignore[no-any-return]
|
|
@@ -160,8 +166,8 @@ def generate_with_retries(
|
|
|
160
166
|
|
|
161
167
|
# For authentication and model errors, don't retry
|
|
162
168
|
if error_type in ["authentication", "model"]:
|
|
163
|
-
if spinner:
|
|
164
|
-
spinner.fail(f"Failed to generate
|
|
169
|
+
if spinner and not skip_success_message:
|
|
170
|
+
spinner.fail(f"Failed to generate {message_type} with {provider} {model_name}")
|
|
165
171
|
|
|
166
172
|
# Create the appropriate error type based on classification
|
|
167
173
|
if error_type == "authentication":
|
|
@@ -172,23 +178,32 @@ def generate_with_retries(
|
|
|
172
178
|
if attempt < max_retries - 1:
|
|
173
179
|
# Exponential backoff
|
|
174
180
|
wait_time = 2**attempt
|
|
175
|
-
if not quiet:
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
if not quiet and not skip_success_message:
|
|
182
|
+
if attempt == 0:
|
|
183
|
+
logger.warning(f"AI generation failed, retrying in {wait_time}s: {str(e)}")
|
|
184
|
+
else:
|
|
185
|
+
logger.warning(
|
|
186
|
+
f"AI generation failed (attempt {attempt + 1}), retrying in {wait_time}s: {str(e)}"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
if spinner and not skip_success_message:
|
|
179
190
|
for i in range(wait_time, 0, -1):
|
|
180
191
|
spinner.text = f"Retry {attempt + 1}/{max_retries} in {i}s..."
|
|
181
192
|
time.sleep(1)
|
|
182
193
|
else:
|
|
183
194
|
time.sleep(wait_time)
|
|
184
195
|
else:
|
|
185
|
-
|
|
196
|
+
num_retries = max_retries
|
|
197
|
+
retry_word = "retry" if num_retries == 1 else "retries"
|
|
198
|
+
logger.error(f"AI generation failed after {num_retries} {retry_word}: {str(e)}")
|
|
186
199
|
|
|
187
|
-
if spinner:
|
|
188
|
-
spinner.fail(f"Failed to generate
|
|
200
|
+
if spinner and not skip_success_message:
|
|
201
|
+
spinner.fail(f"Failed to generate {message_type} with {provider} {model_name}")
|
|
189
202
|
|
|
190
203
|
# If we get here, all retries failed - use the last classified error type
|
|
191
|
-
|
|
204
|
+
num_retries = max_retries
|
|
205
|
+
retry_word = "retry" if num_retries == 1 else "retries"
|
|
206
|
+
error_message = f"Failed to generate {message_type} after {num_retries} {retry_word}"
|
|
192
207
|
if last_error_type == "authentication":
|
|
193
208
|
raise AIError.authentication_error(error_message) from last_exception
|
|
194
209
|
elif last_error_type == "rate_limit":
|
|
@@ -28,6 +28,7 @@ logger = logging.getLogger(__name__)
|
|
|
28
28
|
@click.group(invoke_without_command=True, context_settings={"ignore_unknown_options": True})
|
|
29
29
|
# Git workflow options
|
|
30
30
|
@click.option("--add-all", "-a", is_flag=True, help="Stage all changes before committing")
|
|
31
|
+
@click.option("--group", "-g", is_flag=True, help="Group changes into multiple logical commits")
|
|
31
32
|
@click.option("--push", "-p", is_flag=True, help="Push changes to remote after committing")
|
|
32
33
|
@click.option("--dry-run", is_flag=True, help="Dry run the commit workflow")
|
|
33
34
|
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
|
|
@@ -70,6 +71,7 @@ logger = logging.getLogger(__name__)
|
|
|
70
71
|
def cli(
|
|
71
72
|
ctx: click.Context,
|
|
72
73
|
add_all: bool = False,
|
|
74
|
+
group: bool = False,
|
|
73
75
|
log_level: str = str(config["log_level"]),
|
|
74
76
|
one_liner: bool = False,
|
|
75
77
|
push: bool = False,
|
|
@@ -109,6 +111,7 @@ def cli(
|
|
|
109
111
|
try:
|
|
110
112
|
main(
|
|
111
113
|
stage_all=add_all,
|
|
114
|
+
group=group,
|
|
112
115
|
model=model,
|
|
113
116
|
hint=hint,
|
|
114
117
|
one_liner=one_liner,
|
|
@@ -131,6 +134,7 @@ def cli(
|
|
|
131
134
|
|
|
132
135
|
ctx.obj = {
|
|
133
136
|
"add_all": add_all,
|
|
137
|
+
"group": group,
|
|
134
138
|
"log_level": log_level,
|
|
135
139
|
"one_liner": one_liner,
|
|
136
140
|
"push": push,
|
|
@@ -20,8 +20,8 @@ class EnvDefaults:
|
|
|
20
20
|
|
|
21
21
|
MAX_RETRIES: int = 3
|
|
22
22
|
TEMPERATURE: float = 1
|
|
23
|
-
MAX_OUTPUT_TOKENS: int =
|
|
24
|
-
WARNING_LIMIT_TOKENS: int =
|
|
23
|
+
MAX_OUTPUT_TOKENS: int = 4096 # includes reasoning tokens
|
|
24
|
+
WARNING_LIMIT_TOKENS: int = 32768
|
|
25
25
|
ALWAYS_INCLUDE_SCOPE: bool = False
|
|
26
26
|
SKIP_SECRET_SCAN: bool = False
|
|
27
27
|
VERBOSE: bool = False
|
|
@@ -50,6 +50,48 @@ def get_staged_files(file_type: str | None = None, existing_only: bool = False)
|
|
|
50
50
|
return []
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def get_staged_status() -> str:
|
|
54
|
+
"""Get formatted status of staged files only, excluding unstaged/untracked files.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Formatted status string with M/A/D/R indicators
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
output = run_git_command(["diff", "--name-status", "--staged"])
|
|
61
|
+
if not output:
|
|
62
|
+
return "No changes staged for commit."
|
|
63
|
+
|
|
64
|
+
status_map = {
|
|
65
|
+
"M": "modified",
|
|
66
|
+
"A": "new file",
|
|
67
|
+
"D": "deleted",
|
|
68
|
+
"R": "renamed",
|
|
69
|
+
"C": "copied",
|
|
70
|
+
"T": "typechange",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
status_lines = ["Changes to be committed:"]
|
|
74
|
+
for line in output.splitlines():
|
|
75
|
+
line = line.strip()
|
|
76
|
+
if not line:
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
# Parse status line (e.g., "M\tfile.py" or "R100\told.py\tnew.py")
|
|
80
|
+
parts = line.split("\t")
|
|
81
|
+
if len(parts) < 2:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
change_type = parts[0][0] # First char is the status (M, A, D, R, etc.)
|
|
85
|
+
file_path = parts[-1] # Last part is the new/current file path
|
|
86
|
+
|
|
87
|
+
status_label = status_map.get(change_type, "modified")
|
|
88
|
+
status_lines.append(f"\t{status_label}: {file_path}")
|
|
89
|
+
|
|
90
|
+
return "\n".join(status_lines)
|
|
91
|
+
except GitError:
|
|
92
|
+
return "No changes staged for commit."
|
|
93
|
+
|
|
94
|
+
|
|
53
95
|
def get_diff(staged: bool = True, color: bool = True, commit1: str | None = None, commit2: str | None = None) -> str:
|
|
54
96
|
"""Get the diff between commits or working tree.
|
|
55
97
|
|