git-hunk 0.1.0__tar.gz → 0.2.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_hunk-0.2.0/.gitattributes +1 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/.github/workflows/lint.yml +1 -2
- {git_hunk-0.1.0 → git_hunk-0.2.0}/.github/workflows/publish.yml +2 -4
- {git_hunk-0.1.0 → git_hunk-0.2.0}/.github/workflows/test.yml +4 -4
- git_hunk-0.2.0/Makefile +41 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/PKG-INFO +20 -15
- {git_hunk-0.1.0 → git_hunk-0.2.0}/README.md +18 -13
- git_hunk-0.2.0/git_hunk/__init__.py +3 -0
- git_hunk-0.2.0/git_hunk/__main__.py +3 -0
- git_hunk-0.1.0/git_hunk/cli.py → git_hunk-0.2.0/git_hunk/_cli.py +31 -40
- git_hunk-0.1.0/git_hunk/git.py → git_hunk-0.2.0/git_hunk/_git.py +5 -4
- git_hunk-0.1.0/git_hunk/lines.py → git_hunk-0.2.0/git_hunk/_lines.py +2 -2
- git_hunk-0.1.0/git_hunk/patch.py → git_hunk-0.2.0/git_hunk/_patch.py +1 -1
- git_hunk-0.1.0/git_hunk/ui.py → git_hunk-0.2.0/git_hunk/_ui.py +4 -4
- git_hunk-0.2.0/pyproject.toml +67 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/skills/git-hunk/SKILL.md +6 -5
- git_hunk-0.2.0/taplo.toml +3 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/conftest.py +1 -1
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/show_test.py +3 -3
- {git_hunk-0.1.0/tests/unit/hunk → git_hunk-0.2.0/tests/unit/_hunk}/id_test.py +5 -5
- {git_hunk-0.1.0/tests/unit/hunk → git_hunk-0.2.0/tests/unit/_hunk}/parse_diff_test.py +1 -1
- {git_hunk-0.1.0/tests/unit/hunk → git_hunk-0.2.0/tests/unit/_hunk}/split_test.py +1 -1
- {git_hunk-0.1.0/tests/unit/lines → git_hunk-0.2.0/tests/unit/_lines}/filter_test.py +2 -2
- {git_hunk-0.1.0/tests/unit/lines → git_hunk-0.2.0/tests/unit/_lines}/parse_spec_test.py +1 -1
- git_hunk-0.1.0/tests/unit/patch/build_test.py → git_hunk-0.2.0/tests/unit/_patch_test.py +3 -3
- {git_hunk-0.1.0 → git_hunk-0.2.0}/uv.lock +369 -0
- git_hunk-0.1.0/Makefile +0 -41
- git_hunk-0.1.0/git_hunk/__init__.py +0 -1
- git_hunk-0.1.0/git_hunk/__main__.py +0 -3
- git_hunk-0.1.0/pyproject.toml +0 -62
- git_hunk-0.1.0/tests/unit/patch/__init__.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/.gitignore +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/.python-version +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/LICENSE +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/assets/teaser.png +0 -0
- /git_hunk-0.1.0/git_hunk/hunk.py → /git_hunk-0.2.0/git_hunk/_hunk.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/__init__.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/conftest.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/__init__.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/discard_test.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/error_test.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/list_test.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/stage_test.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/unstage_test.py +0 -0
- {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/unit/__init__.py +0 -0
- {git_hunk-0.1.0/tests/unit/hunk → git_hunk-0.2.0/tests/unit/_hunk}/__init__.py +0 -0
- {git_hunk-0.1.0/tests/unit/lines → git_hunk-0.2.0/tests/unit/_lines}/__init__.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* text=auto eol=lf
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
---
|
|
1
2
|
name: Test
|
|
2
|
-
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
5
|
branches: [main]
|
|
6
6
|
pull_request:
|
|
7
|
-
|
|
8
7
|
jobs:
|
|
9
8
|
test:
|
|
10
|
-
runs-on:
|
|
9
|
+
runs-on: ${{ matrix.os }}
|
|
11
10
|
strategy:
|
|
12
11
|
matrix:
|
|
13
|
-
|
|
12
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
13
|
+
python-version: ['3.10', '3.11', '3.12', '3.13']
|
|
14
14
|
steps:
|
|
15
15
|
- uses: actions/checkout@v4
|
|
16
16
|
- uses: astral-sh/setup-uv@v6
|
git_hunk-0.2.0/Makefile
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
ifneq ($(OS),Windows_NT)
|
|
2
|
+
SHELL := bash
|
|
3
|
+
endif
|
|
4
|
+
|
|
5
|
+
.PHONY: help setup format lint test coverage
|
|
6
|
+
.DEFAULT_GOAL := help
|
|
7
|
+
|
|
8
|
+
PYTEST_ARGS ?= --numprocesses=auto
|
|
9
|
+
|
|
10
|
+
define exec
|
|
11
|
+
@uv run --no-sync python -c "print('\033[1;36m$(1)\033[0m')"
|
|
12
|
+
@$(1)
|
|
13
|
+
endef
|
|
14
|
+
|
|
15
|
+
help:
|
|
16
|
+
@uv run --no-sync python -c "import re; lines=open('Makefile').read().splitlines(); print('\033[1;32mAvailable targets:\033[0m'); [print(f' \033[1;36m{m.group(1):<20s}\033[0m {m.group(2)}') for l in lines if (m:=re.match(r'^([a-zA-Z_-]+):.*?# (.+)$$',l))]"
|
|
17
|
+
|
|
18
|
+
setup: # Setup the development environment
|
|
19
|
+
$(call exec,uv sync)
|
|
20
|
+
|
|
21
|
+
lint: # Lint code
|
|
22
|
+
$(call exec,uv run ruff format --check)
|
|
23
|
+
$(call exec,uv run ruff check)
|
|
24
|
+
$(call exec,uv run ty check --no-progress)
|
|
25
|
+
$(call exec,uv run taplo fmt --check $(shell git ls-files "*.toml"))
|
|
26
|
+
$(call exec,uv run mdformat --check $(shell git ls-files "*.md"))
|
|
27
|
+
$(call exec,uv run yamlfix --check $(shell git ls-files "*.yml" "*.yaml"))
|
|
28
|
+
$(call exec,uv run typos)
|
|
29
|
+
|
|
30
|
+
format: # Format code
|
|
31
|
+
$(call exec,uv run ruff format)
|
|
32
|
+
$(call exec,uv run ruff check --fix)
|
|
33
|
+
$(call exec,uv run taplo fmt $(shell git ls-files "*.toml"))
|
|
34
|
+
$(call exec,uv run mdformat $(shell git ls-files "*.md"))
|
|
35
|
+
$(call exec,uv run yamlfix $(shell git ls-files "*.yml" "*.yaml"))
|
|
36
|
+
|
|
37
|
+
test: # Run tests
|
|
38
|
+
$(call exec,uv run pytest -v tests/ $(PYTEST_ARGS))
|
|
39
|
+
|
|
40
|
+
coverage: # Run tests with coverage
|
|
41
|
+
$(MAKE) test PYTEST_ARGS="--cov=git_hunk --cov-report=term-missing"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: git-hunk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Non-interactive, programmatic git hunk staging with stable IDs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/wkentaro/git-hunk
|
|
6
|
-
Project-URL: Repository, https://github.com/wkentaro/git-hunk
|
|
7
6
|
Project-URL: Issues, https://github.com/wkentaro/git-hunk/issues
|
|
7
|
+
Project-URL: Repository, https://github.com/wkentaro/git-hunk
|
|
8
8
|
Author: Kentaro Wada
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
@@ -27,17 +27,17 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
# git-hunk
|
|
29
29
|
|
|
30
|
-
[](https://pypi.org/project/git-hunk/)
|
|
31
|
+
[](https://pypi.org/project/git-hunk/)
|
|
32
32
|
[](https://github.com/wkentaro/git-hunk/actions/workflows/test.yml)
|
|
33
|
-
[](https://pypi.org/project/git-hunk/)
|
|
34
34
|
|
|
35
35
|
Non-interactive, programmatic alternative to `git add -p`.
|
|
36
36
|
|
|
37
37
|
Every hunk gets a stable, content-based ID so you can inspect, filter, and
|
|
38
38
|
stage changes without interactive prompts.
|
|
39
39
|
|
|
40
|
-
<img src="assets/teaser.png" alt="git-hunk teaser" width="800">
|
|
40
|
+
<img src="https://github.com/wkentaro/git-hunk/blob/main/assets/teaser.png?raw=true" alt="git-hunk teaser" width="800">
|
|
41
41
|
|
|
42
42
|
## Why?
|
|
43
43
|
|
|
@@ -70,13 +70,18 @@ git-hunk --version
|
|
|
70
70
|
|
|
71
71
|
### Agent skill (optional)
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
[skills](https://github.com/vercel-labs/skills):
|
|
73
|
+
If you use [Claude Code](https://claude.ai/code), [Codex](https://openai.com/codex), or similar AI agents:
|
|
75
74
|
|
|
76
75
|
```bash
|
|
77
76
|
npx skills add wkentaro/git-hunk
|
|
78
77
|
```
|
|
79
78
|
|
|
79
|
+
Or copy [`SKILL.md`](skills/git-hunk/SKILL.md) to
|
|
80
|
+
`~/.claude/skills/git-hunk/SKILL.md` (or `~/.agents/...`).
|
|
81
|
+
|
|
82
|
+
This installs a `/git-hunk` slash command that teaches the agent how to split
|
|
83
|
+
changes into logical commits.
|
|
84
|
+
|
|
80
85
|
## Quick start
|
|
81
86
|
|
|
82
87
|
```bash
|
|
@@ -110,11 +115,11 @@ git-hunk list --json # JSON output for scripting
|
|
|
110
115
|
### Show hunks
|
|
111
116
|
|
|
112
117
|
```bash
|
|
118
|
+
git-hunk show # show all hunks (staged + unstaged)
|
|
113
119
|
git-hunk show d161935 # show a single hunk
|
|
114
120
|
git-hunk show d161935 a3f82c1 # show multiple hunks
|
|
115
|
-
git-hunk show --
|
|
116
|
-
git-hunk show --
|
|
117
|
-
git-hunk show --all --unstaged # show all unstaged hunks
|
|
121
|
+
git-hunk show --staged # show all staged hunks
|
|
122
|
+
git-hunk show --unstaged # show all unstaged hunks
|
|
118
123
|
```
|
|
119
124
|
|
|
120
125
|
### Stage, unstage, discard
|
|
@@ -160,9 +165,9 @@ git-hunk list --json
|
|
|
160
165
|
## How it works
|
|
161
166
|
|
|
162
167
|
1. Parses `git diff` output into individual hunks
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
1. Assigns each hunk a stable, content-based ID (SHA-256 prefix)
|
|
169
|
+
1. For staging: reconstructs a minimal patch and pipes it through `git apply --cached`
|
|
170
|
+
1. For discarding: reconstructs a reverse patch and applies it to the working tree
|
|
166
171
|
|
|
167
172
|
IDs are stable across partial staging -- they are derived from the changed lines,
|
|
168
173
|
not the `@@` line numbers that shift as you stage hunks.
|
|
@@ -182,4 +187,4 @@ make lint # run linters
|
|
|
182
187
|
|
|
183
188
|
## License
|
|
184
189
|
|
|
185
|
-
MIT ([LICENSE](LICENSE))
|
|
190
|
+
MIT ([LICENSE](https://github.com/wkentaro/git-hunk/blob/main/LICENSE))
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# git-hunk
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/git-hunk/)
|
|
4
|
+
[](https://pypi.org/project/git-hunk/)
|
|
5
5
|
[](https://github.com/wkentaro/git-hunk/actions/workflows/test.yml)
|
|
6
|
-
[](https://pypi.org/project/git-hunk/)
|
|
7
7
|
|
|
8
8
|
Non-interactive, programmatic alternative to `git add -p`.
|
|
9
9
|
|
|
10
10
|
Every hunk gets a stable, content-based ID so you can inspect, filter, and
|
|
11
11
|
stage changes without interactive prompts.
|
|
12
12
|
|
|
13
|
-
<img src="assets/teaser.png" alt="git-hunk teaser" width="800">
|
|
13
|
+
<img src="https://github.com/wkentaro/git-hunk/blob/main/assets/teaser.png?raw=true" alt="git-hunk teaser" width="800">
|
|
14
14
|
|
|
15
15
|
## Why?
|
|
16
16
|
|
|
@@ -43,13 +43,18 @@ git-hunk --version
|
|
|
43
43
|
|
|
44
44
|
### Agent skill (optional)
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
[skills](https://github.com/vercel-labs/skills):
|
|
46
|
+
If you use [Claude Code](https://claude.ai/code), [Codex](https://openai.com/codex), or similar AI agents:
|
|
48
47
|
|
|
49
48
|
```bash
|
|
50
49
|
npx skills add wkentaro/git-hunk
|
|
51
50
|
```
|
|
52
51
|
|
|
52
|
+
Or copy [`SKILL.md`](skills/git-hunk/SKILL.md) to
|
|
53
|
+
`~/.claude/skills/git-hunk/SKILL.md` (or `~/.agents/...`).
|
|
54
|
+
|
|
55
|
+
This installs a `/git-hunk` slash command that teaches the agent how to split
|
|
56
|
+
changes into logical commits.
|
|
57
|
+
|
|
53
58
|
## Quick start
|
|
54
59
|
|
|
55
60
|
```bash
|
|
@@ -83,11 +88,11 @@ git-hunk list --json # JSON output for scripting
|
|
|
83
88
|
### Show hunks
|
|
84
89
|
|
|
85
90
|
```bash
|
|
91
|
+
git-hunk show # show all hunks (staged + unstaged)
|
|
86
92
|
git-hunk show d161935 # show a single hunk
|
|
87
93
|
git-hunk show d161935 a3f82c1 # show multiple hunks
|
|
88
|
-
git-hunk show --
|
|
89
|
-
git-hunk show --
|
|
90
|
-
git-hunk show --all --unstaged # show all unstaged hunks
|
|
94
|
+
git-hunk show --staged # show all staged hunks
|
|
95
|
+
git-hunk show --unstaged # show all unstaged hunks
|
|
91
96
|
```
|
|
92
97
|
|
|
93
98
|
### Stage, unstage, discard
|
|
@@ -133,9 +138,9 @@ git-hunk list --json
|
|
|
133
138
|
## How it works
|
|
134
139
|
|
|
135
140
|
1. Parses `git diff` output into individual hunks
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
1. Assigns each hunk a stable, content-based ID (SHA-256 prefix)
|
|
142
|
+
1. For staging: reconstructs a minimal patch and pipes it through `git apply --cached`
|
|
143
|
+
1. For discarding: reconstructs a reverse patch and applies it to the working tree
|
|
139
144
|
|
|
140
145
|
IDs are stable across partial staging -- they are derived from the changed lines,
|
|
141
146
|
not the `@@` line numbers that shift as you stage hunks.
|
|
@@ -155,4 +160,4 @@ make lint # run linters
|
|
|
155
160
|
|
|
156
161
|
## License
|
|
157
162
|
|
|
158
|
-
MIT ([LICENSE](LICENSE))
|
|
163
|
+
MIT ([LICENSE](https://github.com/wkentaro/git-hunk/blob/main/LICENSE))
|
|
@@ -4,35 +4,34 @@ from dataclasses import replace
|
|
|
4
4
|
import click
|
|
5
5
|
|
|
6
6
|
from . import __version__
|
|
7
|
-
from .
|
|
8
|
-
from .
|
|
9
|
-
from .
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
17
|
-
from .
|
|
18
|
-
from .
|
|
19
|
-
from .
|
|
20
|
-
from .
|
|
21
|
-
from .
|
|
22
|
-
from .
|
|
23
|
-
from .
|
|
24
|
-
from .
|
|
25
|
-
from .
|
|
26
|
-
from .
|
|
27
|
-
from .
|
|
28
|
-
from .
|
|
29
|
-
from .
|
|
30
|
-
from .
|
|
31
|
-
from .
|
|
32
|
-
from .
|
|
33
|
-
from .
|
|
34
|
-
from .
|
|
35
|
-
from .ui import print_version
|
|
7
|
+
from ._git import apply_patch
|
|
8
|
+
from ._git import discard_files
|
|
9
|
+
from ._git import get_diff
|
|
10
|
+
from ._git import get_untracked_files
|
|
11
|
+
from ._git import is_git_repo
|
|
12
|
+
from ._git import stage_files
|
|
13
|
+
from ._git import unstage_files
|
|
14
|
+
from ._hunk import Hunk
|
|
15
|
+
from ._hunk import parse_diff
|
|
16
|
+
from ._lines import filter_hunk_lines
|
|
17
|
+
from ._lines import parse_line_spec
|
|
18
|
+
from ._patch import build_patch
|
|
19
|
+
from ._ui import HELP
|
|
20
|
+
from ._ui import HELP_DISCARD
|
|
21
|
+
from ._ui import HELP_LIST
|
|
22
|
+
from ._ui import HELP_SHOW
|
|
23
|
+
from ._ui import HELP_STAGE
|
|
24
|
+
from ._ui import HELP_UNSTAGE
|
|
25
|
+
from ._ui import USAGE
|
|
26
|
+
from ._ui import USAGE_DISCARD
|
|
27
|
+
from ._ui import USAGE_STAGE
|
|
28
|
+
from ._ui import USAGE_UNSTAGE
|
|
29
|
+
from ._ui import print_applied
|
|
30
|
+
from ._ui import print_error
|
|
31
|
+
from ._ui import print_help
|
|
32
|
+
from ._ui import print_hunk_diffs
|
|
33
|
+
from ._ui import print_hunk_list
|
|
34
|
+
from ._ui import print_version
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
class CliError(Exception):
|
|
@@ -230,13 +229,11 @@ def cmd_list(
|
|
|
230
229
|
|
|
231
230
|
|
|
232
231
|
@cli.command("show", add_help_option=False)
|
|
233
|
-
@click.option("--all", "show_all", is_flag=True)
|
|
234
232
|
@click.option("--staged", is_flag=True)
|
|
235
233
|
@click.option("--unstaged", is_flag=True)
|
|
236
234
|
@click.option("-h", "--help", "show_help", is_flag=True)
|
|
237
235
|
@click.argument("ids", nargs=-1)
|
|
238
236
|
def cmd_show(
|
|
239
|
-
show_all: bool,
|
|
240
237
|
staged: bool,
|
|
241
238
|
unstaged: bool,
|
|
242
239
|
show_help: bool,
|
|
@@ -249,12 +246,6 @@ def cmd_show(
|
|
|
249
246
|
if staged and unstaged:
|
|
250
247
|
raise CliError("cannot use --staged and --unstaged together")
|
|
251
248
|
|
|
252
|
-
if show_all and ids:
|
|
253
|
-
raise CliError("--all cannot be combined with specific hunk ids")
|
|
254
|
-
|
|
255
|
-
if not show_all and not ids:
|
|
256
|
-
raise CliError("show requires at least one hunk id", usage=USAGE_SHOW)
|
|
257
|
-
|
|
258
249
|
if staged:
|
|
259
250
|
hunks, _ = _get_hunks(staged=True)
|
|
260
251
|
elif unstaged:
|
|
@@ -264,10 +255,10 @@ def cmd_show(
|
|
|
264
255
|
hunks_unstaged, _ = _get_hunks(staged=False)
|
|
265
256
|
hunks = hunks_staged + hunks_unstaged
|
|
266
257
|
|
|
267
|
-
if
|
|
268
|
-
matched = hunks
|
|
269
|
-
else:
|
|
258
|
+
if ids:
|
|
270
259
|
matched = _find_hunks_by_ids(hunks, list(ids))
|
|
260
|
+
else:
|
|
261
|
+
matched = hunks
|
|
271
262
|
|
|
272
263
|
print_hunk_diffs(matched)
|
|
273
264
|
|
|
@@ -14,12 +14,13 @@ def run_git(*args: str, input: str | None = None, check: bool = True) -> str:
|
|
|
14
14
|
result = subprocess.run(
|
|
15
15
|
["git"] + list(args),
|
|
16
16
|
capture_output=True,
|
|
17
|
-
|
|
18
|
-
input=input,
|
|
17
|
+
input=input.encode() if input is not None else None,
|
|
19
18
|
)
|
|
20
19
|
if check and result.returncode != 0:
|
|
21
|
-
raise RuntimeError(
|
|
22
|
-
|
|
20
|
+
raise RuntimeError(
|
|
21
|
+
f"git {' '.join(args)} failed: {result.stderr.decode().strip()}"
|
|
22
|
+
)
|
|
23
|
+
return result.stdout.decode()
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def get_diff(staged: bool = False, files: list[str] | None = None) -> str:
|
|
@@ -13,7 +13,7 @@ from rich.console import Console
|
|
|
13
13
|
from rich.rule import Rule
|
|
14
14
|
from rich.text import Text
|
|
15
15
|
|
|
16
|
-
from .
|
|
16
|
+
from ._hunk import Hunk
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _out() -> Console:
|
|
@@ -171,7 +171,7 @@ _LINE_OPTS = """\
|
|
|
171
171
|
Examples: -l 3,5-7 (include) -l ^3,^5-7 (exclude)""" # noqa: E501
|
|
172
172
|
|
|
173
173
|
USAGE = "[bold green]Usage:[/bold green] [bold cyan]git-hunk[/bold cyan] [cyan]<COMMAND>[/cyan]" # noqa: E501
|
|
174
|
-
USAGE_SHOW = "[bold green]Usage:[/bold green] [bold cyan]git-hunk show[/bold cyan] [cyan]
|
|
174
|
+
USAGE_SHOW = "[bold green]Usage:[/bold green] [bold cyan]git-hunk show[/bold cyan] [cyan][<id>...][/cyan] [cyan][OPTIONS][/cyan]" # noqa: E501
|
|
175
175
|
USAGE_STAGE = "[bold green]Usage:[/bold green] [bold cyan]git-hunk stage[/bold cyan] [cyan]<id>[/cyan] [cyan][<id>...][/cyan] [cyan][OPTIONS][/cyan]" # noqa: E501
|
|
176
176
|
USAGE_UNSTAGE = "[bold green]Usage:[/bold green] [bold cyan]git-hunk unstage[/bold cyan] [cyan]<id>[/cyan] [cyan][<id>...][/cyan] [cyan][OPTIONS][/cyan]" # noqa: E501
|
|
177
177
|
USAGE_DISCARD = "[bold green]Usage:[/bold green] [bold cyan]git-hunk discard[/bold cyan] [cyan]<id>[/cyan] [cyan][<id>...][/cyan] [cyan][OPTIONS][/cyan]" # noqa: E501
|
|
@@ -203,12 +203,12 @@ List hunks (unstaged, staged, and untracked by default).
|
|
|
203
203
|
[bold cyan]--json[/bold cyan] Output as JSON""" # noqa: E501
|
|
204
204
|
|
|
205
205
|
HELP_SHOW = f"""\
|
|
206
|
-
Show the diff for one or more hunks.
|
|
206
|
+
Show the diff for one or more hunks. Shows all hunks when no IDs given.
|
|
207
|
+
IDs support prefix matching.
|
|
207
208
|
|
|
208
209
|
{USAGE_SHOW}
|
|
209
210
|
|
|
210
211
|
[bold green]Options:[/bold green]
|
|
211
|
-
[bold cyan]--all[/bold cyan] Show all hunks
|
|
212
212
|
[bold cyan]--staged[/bold cyan] Show only staged hunks
|
|
213
213
|
[bold cyan]--unstaged[/bold cyan] Show only unstaged hunks"""
|
|
214
214
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "hatchling.build"
|
|
3
|
+
requires = ["hatch-vcs", "hatchling"]
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
authors = [{ name = "Kentaro Wada" }]
|
|
7
|
+
classifiers = [
|
|
8
|
+
"Development Status :: 4 - Beta",
|
|
9
|
+
"Environment :: Console",
|
|
10
|
+
"Intended Audience :: Developers",
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.10",
|
|
15
|
+
"Programming Language :: Python :: 3.11",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
19
|
+
]
|
|
20
|
+
dependencies = ["click>=8", "rich>=13"]
|
|
21
|
+
description = "Non-interactive, programmatic git hunk staging with stable IDs."
|
|
22
|
+
dynamic = ["version"]
|
|
23
|
+
keywords = ["automation", "diff", "git", "hunk", "staging", "version-control"]
|
|
24
|
+
license = "MIT"
|
|
25
|
+
name = "git-hunk"
|
|
26
|
+
readme = "README.md"
|
|
27
|
+
requires-python = ">=3.10"
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/wkentaro/git-hunk"
|
|
31
|
+
Issues = "https://github.com/wkentaro/git-hunk/issues"
|
|
32
|
+
Repository = "https://github.com/wkentaro/git-hunk"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
git-hunk = "git_hunk._cli:cli"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.version]
|
|
38
|
+
source = "vcs"
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
dev = [
|
|
42
|
+
"mdformat-frontmatter>=2.0.10",
|
|
43
|
+
"mdformat>=0.7",
|
|
44
|
+
"pytest-cov>=4",
|
|
45
|
+
"pytest-xdist>=3.8.0",
|
|
46
|
+
"pytest>=7",
|
|
47
|
+
"ruff>=0.15.9",
|
|
48
|
+
"taplo>=0.9",
|
|
49
|
+
"ty>=0.0.28",
|
|
50
|
+
"typos>=1.44",
|
|
51
|
+
"yamlfix>=1.17",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = [
|
|
56
|
+
"ANN", # flake8-annotations
|
|
57
|
+
"E", # pycodestyle
|
|
58
|
+
"F", # pyflakes
|
|
59
|
+
"I", # isort
|
|
60
|
+
"UP", # pyupgrade
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint.isort]
|
|
64
|
+
force-single-line = true
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint.per-file-ignores]
|
|
67
|
+
"__init__.py" = ["F401"]
|
|
@@ -7,7 +7,7 @@ description: |
|
|
|
7
7
|
license: MIT
|
|
8
8
|
metadata:
|
|
9
9
|
author: wkentaro
|
|
10
|
-
version:
|
|
10
|
+
version: 0.1.0
|
|
11
11
|
allowed-tools:
|
|
12
12
|
- Bash
|
|
13
13
|
---
|
|
@@ -19,18 +19,19 @@ Requires: `uv tool install git-hunk` (or `pip install git-hunk`)
|
|
|
19
19
|
## Workflow
|
|
20
20
|
|
|
21
21
|
1. `git-hunk list` — see all hunks (file, id, +/- stats). No diffs.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
1. `git-hunk show [<id>...]` when headers aren't clear enough (no args shows all).
|
|
23
|
+
1. Plan commits before staging. For each planned commit, list the hunk IDs (and `-l` line ranges for partial hunks). A single hunk may need to be split across commits. Ask the user if grouping is ambiguous.
|
|
24
|
+
1. Stage and commit each group:
|
|
25
25
|
```bash
|
|
26
26
|
git-hunk stage <id1> <id2> ...
|
|
27
27
|
git commit -m "<type>: <description>"
|
|
28
28
|
```
|
|
29
|
-
|
|
29
|
+
1. `git-hunk list` again to check nothing got left behind.
|
|
30
30
|
|
|
31
31
|
## Partial hunks
|
|
32
32
|
|
|
33
33
|
Line selection (`-l`) works with `stage`, `unstage`, and `discard` (requires single id):
|
|
34
|
+
|
|
34
35
|
- Include lines: `git-hunk stage <id> -l 3,5-7`
|
|
35
36
|
- Exclude lines: `git-hunk stage <id> -l ^3,^5-7`
|
|
36
37
|
|
|
@@ -60,7 +60,7 @@ def test_show_finds_staged_hunk_without_flag(cli: GitHunkCLI) -> None:
|
|
|
60
60
|
assert "+new" in r.stdout
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def
|
|
63
|
+
def test_show_no_args_shows_all(cli: GitHunkCLI) -> None:
|
|
64
64
|
cli.repo.write_file("a.py", "old\n")
|
|
65
65
|
cli.repo.write_file("b.py", "old\n")
|
|
66
66
|
cli.repo.git("add", ".")
|
|
@@ -69,7 +69,7 @@ def test_show_all_includes_staged_and_unstaged(cli: GitHunkCLI) -> None:
|
|
|
69
69
|
cli.repo.git("add", "a.py")
|
|
70
70
|
cli.repo.write_file("b.py", "unstaged\n")
|
|
71
71
|
|
|
72
|
-
r = cli.run("show"
|
|
72
|
+
r = cli.run("show")
|
|
73
73
|
assert r.returncode == 0
|
|
74
74
|
assert "+staged" in r.stdout
|
|
75
75
|
assert "+unstaged" in r.stdout
|
|
@@ -81,5 +81,5 @@ def test_show_staged_and_unstaged_together_errors(cli: GitHunkCLI) -> None:
|
|
|
81
81
|
cli.repo.git("commit", "-m", "init")
|
|
82
82
|
cli.repo.write_file("f.py", "new\n")
|
|
83
83
|
|
|
84
|
-
r = cli.run("show", "--staged", "--unstaged"
|
|
84
|
+
r = cli.run("show", "--staged", "--unstaged")
|
|
85
85
|
assert r.returncode != 0
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from git_hunk.
|
|
2
|
-
from git_hunk.
|
|
3
|
-
from git_hunk.
|
|
4
|
-
from git_hunk.
|
|
5
|
-
from git_hunk.
|
|
1
|
+
from git_hunk._hunk import Hunk
|
|
2
|
+
from git_hunk._hunk import _body_id
|
|
3
|
+
from git_hunk._hunk import _full_id
|
|
4
|
+
from git_hunk._hunk import _with_stable_ids
|
|
5
|
+
from git_hunk._hunk import count_changes
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def test_body_id_ignores_header_line_numbers() -> None:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
-
from git_hunk.
|
|
4
|
-
from git_hunk.
|
|
5
|
-
from git_hunk.
|
|
3
|
+
from git_hunk._hunk import Hunk
|
|
4
|
+
from git_hunk._patch import _get_file_header
|
|
5
|
+
from git_hunk._patch import build_patch
|
|
6
6
|
|
|
7
7
|
DIFF_SINGLE = (
|
|
8
8
|
"diff --git a/f.py b/f.py\n"
|