git-hunk 0.1.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.
Files changed (41) hide show
  1. git_hunk-0.1.0/.github/workflows/lint.yml +14 -0
  2. git_hunk-0.1.0/.github/workflows/publish.yml +18 -0
  3. git_hunk-0.1.0/.github/workflows/test.yml +19 -0
  4. git_hunk-0.1.0/.gitignore +18 -0
  5. git_hunk-0.1.0/.python-version +1 -0
  6. git_hunk-0.1.0/LICENSE +21 -0
  7. git_hunk-0.1.0/Makefile +41 -0
  8. git_hunk-0.1.0/PKG-INFO +185 -0
  9. git_hunk-0.1.0/README.md +158 -0
  10. git_hunk-0.1.0/assets/teaser.png +0 -0
  11. git_hunk-0.1.0/git_hunk/__init__.py +1 -0
  12. git_hunk-0.1.0/git_hunk/__main__.py +3 -0
  13. git_hunk-0.1.0/git_hunk/cli.py +332 -0
  14. git_hunk-0.1.0/git_hunk/git.py +58 -0
  15. git_hunk-0.1.0/git_hunk/hunk.py +249 -0
  16. git_hunk-0.1.0/git_hunk/lines.py +112 -0
  17. git_hunk-0.1.0/git_hunk/patch.py +28 -0
  18. git_hunk-0.1.0/git_hunk/ui.py +240 -0
  19. git_hunk-0.1.0/pyproject.toml +62 -0
  20. git_hunk-0.1.0/skills/git-hunk/SKILL.md +61 -0
  21. git_hunk-0.1.0/tests/__init__.py +0 -0
  22. git_hunk-0.1.0/tests/conftest.py +44 -0
  23. git_hunk-0.1.0/tests/e2e/__init__.py +0 -0
  24. git_hunk-0.1.0/tests/e2e/conftest.py +46 -0
  25. git_hunk-0.1.0/tests/e2e/discard_test.py +41 -0
  26. git_hunk-0.1.0/tests/e2e/error_test.py +72 -0
  27. git_hunk-0.1.0/tests/e2e/list_test.py +153 -0
  28. git_hunk-0.1.0/tests/e2e/show_test.py +85 -0
  29. git_hunk-0.1.0/tests/e2e/stage_test.py +105 -0
  30. git_hunk-0.1.0/tests/e2e/unstage_test.py +40 -0
  31. git_hunk-0.1.0/tests/unit/__init__.py +0 -0
  32. git_hunk-0.1.0/tests/unit/hunk/__init__.py +0 -0
  33. git_hunk-0.1.0/tests/unit/hunk/id_test.py +80 -0
  34. git_hunk-0.1.0/tests/unit/hunk/parse_diff_test.py +84 -0
  35. git_hunk-0.1.0/tests/unit/hunk/split_test.py +149 -0
  36. git_hunk-0.1.0/tests/unit/lines/__init__.py +0 -0
  37. git_hunk-0.1.0/tests/unit/lines/filter_test.py +86 -0
  38. git_hunk-0.1.0/tests/unit/lines/parse_spec_test.py +59 -0
  39. git_hunk-0.1.0/tests/unit/patch/__init__.py +0 -0
  40. git_hunk-0.1.0/tests/unit/patch/build_test.py +81 -0
  41. git_hunk-0.1.0/uv.lock +422 -0
@@ -0,0 +1,14 @@
1
+ name: Lint
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ lint:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: astral-sh/setup-uv@v6
14
+ - run: make lint
@@ -0,0 +1,18 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: read
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v6
17
+ - run: uv build
18
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,19 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v6
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - run: make test
@@ -0,0 +1,18 @@
1
+ # Build artifacts
2
+ /build/
3
+ /dist/
4
+ /*.egg-info/
5
+
6
+ # Test / lint caches
7
+ /.coverage
8
+ /.mypy_cache/
9
+ /.pytest_cache/
10
+ /.ruff_cache/
11
+
12
+ # Environment
13
+ /.env
14
+ /.env.*
15
+ /.venv/
16
+
17
+ # Tools
18
+ /.claude/
@@ -0,0 +1 @@
1
+ 3.10
git_hunk-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kentaro Wada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ ifneq ($(OS),Windows_NT)
2
+ # On Unix-based systems, use ANSI codes
3
+ BLUE = \033[36m
4
+ BOLD_BLUE = \033[1;36m
5
+ BOLD_GREEN = \033[1;32m
6
+ RED = \033[31m
7
+ YELLOW = \033[33m
8
+ BOLD = \033[1m
9
+ NC = \033[0m
10
+ endif
11
+
12
+ escape = $(subst $$,\$$,$(subst ",\",$(subst ',\',$(1))))
13
+
14
+ define exec
15
+ @echo "$(BOLD_BLUE)$(call escape,$(1))$(NC)"
16
+ @$(1)
17
+ endef
18
+
19
+ help:
20
+ @echo "$(BOLD_GREEN)Available targets:$(NC)"
21
+ @grep -E '^[a-zA-Z_-].+:.*?# .*$$' $(MAKEFILE_LIST) | \
22
+ awk 'BEGIN {FS = ":.*?# "}; \
23
+ {printf " $(BOLD_BLUE)%-20s$(NC) %s\n", $$1, $$2}'
24
+
25
+ setup: # Setup the development environment
26
+ $(call exec,uv sync)
27
+
28
+ format: # Format code
29
+ $(call exec,uv run ruff format)
30
+ $(call exec,uv run ruff check --fix)
31
+
32
+ lint:
33
+ $(call exec,uv run ruff format --check)
34
+ $(call exec,uv run ruff check)
35
+ $(call exec,uv run ty check --no-progress)
36
+
37
+ test: # Run tests
38
+ $(call exec,uv run pytest -v tests/ --numprocesses=auto)
39
+
40
+ coverage: # Run tests with coverage
41
+ $(call exec,uv run pytest -v tests/ --cov=git_hunk --cov-report=term-missing)
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: git-hunk
3
+ Version: 0.1.0
4
+ Summary: Non-interactive, programmatic git hunk staging with stable IDs.
5
+ Project-URL: Homepage, https://github.com/wkentaro/git-hunk
6
+ Project-URL: Repository, https://github.com/wkentaro/git-hunk
7
+ Project-URL: Issues, https://github.com/wkentaro/git-hunk/issues
8
+ Author: Kentaro Wada
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: automation,diff,git,hunk,staging,version-control
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Version Control :: Git
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: click>=8
25
+ Requires-Dist: rich>=13
26
+ Description-Content-Type: text/markdown
27
+
28
+ # git-hunk
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/git-hunk.svg)](https://pypi.python.org/pypi/git-hunk)
31
+ [![Python](https://img.shields.io/pypi/pyversions/git-hunk.svg)](https://pypi.python.org/pypi/git-hunk)
32
+ [![Build](https://github.com/wkentaro/git-hunk/actions/workflows/test.yml/badge.svg)](https://github.com/wkentaro/git-hunk/actions/workflows/test.yml)
33
+ [![License](https://img.shields.io/pypi/l/git-hunk.svg)](https://pypi.python.org/pypi/git-hunk)
34
+
35
+ Non-interactive, programmatic alternative to `git add -p`.
36
+
37
+ Every hunk gets a stable, content-based ID so you can inspect, filter, and
38
+ stage changes without interactive prompts.
39
+
40
+ <img src="assets/teaser.png" alt="git-hunk teaser" width="800">
41
+
42
+ ## Why?
43
+
44
+ `git add -p` requires interactive input. That makes it unusable for:
45
+
46
+ - **AI agents** (Claude Code, Codex, etc.) that need to split changes into logical commits
47
+ - **Scripts & CI/CD** that automate commit organization
48
+ - **Editor integrations** that want hunk-level staging without shelling out to a TUI
49
+
50
+ `git-hunk` solves this by assigning each hunk a stable ID and exposing simple
51
+ stage/unstage/discard commands.
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ pip install git-hunk
57
+ ```
58
+
59
+ Or with [uv](https://docs.astral.sh/uv/):
60
+
61
+ ```bash
62
+ uv tool install git-hunk
63
+ ```
64
+
65
+ Verify it works:
66
+
67
+ ```bash
68
+ git-hunk --version
69
+ ```
70
+
71
+ ### Agent skill (optional)
72
+
73
+ For Claude Code, Codex, and other AI agents, add the skill via
74
+ [skills](https://github.com/vercel-labs/skills):
75
+
76
+ ```bash
77
+ npx skills add wkentaro/git-hunk
78
+ ```
79
+
80
+ ## Quick start
81
+
82
+ ```bash
83
+ # See all hunks across staged, unstaged, and untracked files
84
+ git-hunk list
85
+
86
+ # Show the diff for a specific hunk
87
+ git-hunk show d161935
88
+
89
+ # Stage specific hunks, then commit
90
+ git-hunk stage d161935 a3f82c1
91
+ git commit -m "feat: add validation for user input"
92
+
93
+ # Stage the remaining hunks
94
+ git-hunk stage e7b4012
95
+ git commit -m "fix: handle empty response in API client"
96
+ ```
97
+
98
+ ## Usage
99
+
100
+ ### List hunks
101
+
102
+ ```bash
103
+ git-hunk list # all hunks (unstaged + staged + untracked)
104
+ git-hunk list --unstaged # unstaged hunks only
105
+ git-hunk list --staged # staged hunks only
106
+ git-hunk list src/foo.py src/bar.py # specific files
107
+ git-hunk list --json # JSON output for scripting
108
+ ```
109
+
110
+ ### Show hunks
111
+
112
+ ```bash
113
+ git-hunk show d161935 # show a single hunk
114
+ git-hunk show d161935 a3f82c1 # show multiple hunks
115
+ git-hunk show --all # show all hunks (staged + unstaged)
116
+ git-hunk show --all --staged # show all staged hunks
117
+ git-hunk show --all --unstaged # show all unstaged hunks
118
+ ```
119
+
120
+ ### Stage, unstage, discard
121
+
122
+ ```bash
123
+ git-hunk stage d161935 # stage a hunk
124
+ git-hunk stage d161935 a3f82c1 # stage multiple hunks
125
+ git-hunk stage d161935 -l 3,5-7 # stage specific lines only
126
+ git-hunk unstage d161935 # move back to working tree
127
+ git-hunk unstage d161935 -l 3,5-7 # unstage specific lines only
128
+ git-hunk discard d161935 # restore from HEAD
129
+ git-hunk discard d161935 -l ^3,^5-7 # discard excluding specific lines
130
+ ```
131
+
132
+ ### JSON output
133
+
134
+ ```bash
135
+ git-hunk list --json
136
+ ```
137
+
138
+ ```json
139
+ [
140
+ {
141
+ "id": "d161935",
142
+ "file": "src/main.py",
143
+ "status": "unstaged",
144
+ "header": "@@ -10,3 +10,5 @@",
145
+ "additions": 2,
146
+ "deletions": 0,
147
+ "diff": "..."
148
+ }
149
+ ]
150
+ ```
151
+
152
+ ## Comparison
153
+
154
+ | | Interactive | Programmatic | Hunk IDs | Line-level control | JSON output |
155
+ |---|---|---|---|---|---|
156
+ | `git add -p` | Yes | No | No | Yes | No |
157
+ | `git add <file>` | No | Yes | No | No | No |
158
+ | **`git-hunk`** | **No** | **Yes** | **Yes** | **Yes** | **Yes** |
159
+
160
+ ## How it works
161
+
162
+ 1. Parses `git diff` output into individual hunks
163
+ 2. Assigns each hunk a stable, content-based ID (SHA-256 prefix)
164
+ 3. For staging: reconstructs a minimal patch and pipes it through `git apply --cached`
165
+ 4. For discarding: reconstructs a reverse patch and applies it to the working tree
166
+
167
+ IDs are stable across partial staging -- they are derived from the changed lines,
168
+ not the `@@` line numbers that shift as you stage hunks.
169
+
170
+ ## Contributing
171
+
172
+ Bug reports, feature requests, and pull requests are welcome on
173
+ [GitHub](https://github.com/wkentaro/git-hunk).
174
+
175
+ ```bash
176
+ git clone https://github.com/wkentaro/git-hunk.git
177
+ cd git-hunk
178
+ make setup # install dependencies
179
+ make test # run tests
180
+ make lint # run linters
181
+ ```
182
+
183
+ ## License
184
+
185
+ MIT ([LICENSE](LICENSE))
@@ -0,0 +1,158 @@
1
+ # git-hunk
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/git-hunk.svg)](https://pypi.python.org/pypi/git-hunk)
4
+ [![Python](https://img.shields.io/pypi/pyversions/git-hunk.svg)](https://pypi.python.org/pypi/git-hunk)
5
+ [![Build](https://github.com/wkentaro/git-hunk/actions/workflows/test.yml/badge.svg)](https://github.com/wkentaro/git-hunk/actions/workflows/test.yml)
6
+ [![License](https://img.shields.io/pypi/l/git-hunk.svg)](https://pypi.python.org/pypi/git-hunk)
7
+
8
+ Non-interactive, programmatic alternative to `git add -p`.
9
+
10
+ Every hunk gets a stable, content-based ID so you can inspect, filter, and
11
+ stage changes without interactive prompts.
12
+
13
+ <img src="assets/teaser.png" alt="git-hunk teaser" width="800">
14
+
15
+ ## Why?
16
+
17
+ `git add -p` requires interactive input. That makes it unusable for:
18
+
19
+ - **AI agents** (Claude Code, Codex, etc.) that need to split changes into logical commits
20
+ - **Scripts & CI/CD** that automate commit organization
21
+ - **Editor integrations** that want hunk-level staging without shelling out to a TUI
22
+
23
+ `git-hunk` solves this by assigning each hunk a stable ID and exposing simple
24
+ stage/unstage/discard commands.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install git-hunk
30
+ ```
31
+
32
+ Or with [uv](https://docs.astral.sh/uv/):
33
+
34
+ ```bash
35
+ uv tool install git-hunk
36
+ ```
37
+
38
+ Verify it works:
39
+
40
+ ```bash
41
+ git-hunk --version
42
+ ```
43
+
44
+ ### Agent skill (optional)
45
+
46
+ For Claude Code, Codex, and other AI agents, add the skill via
47
+ [skills](https://github.com/vercel-labs/skills):
48
+
49
+ ```bash
50
+ npx skills add wkentaro/git-hunk
51
+ ```
52
+
53
+ ## Quick start
54
+
55
+ ```bash
56
+ # See all hunks across staged, unstaged, and untracked files
57
+ git-hunk list
58
+
59
+ # Show the diff for a specific hunk
60
+ git-hunk show d161935
61
+
62
+ # Stage specific hunks, then commit
63
+ git-hunk stage d161935 a3f82c1
64
+ git commit -m "feat: add validation for user input"
65
+
66
+ # Stage the remaining hunks
67
+ git-hunk stage e7b4012
68
+ git commit -m "fix: handle empty response in API client"
69
+ ```
70
+
71
+ ## Usage
72
+
73
+ ### List hunks
74
+
75
+ ```bash
76
+ git-hunk list # all hunks (unstaged + staged + untracked)
77
+ git-hunk list --unstaged # unstaged hunks only
78
+ git-hunk list --staged # staged hunks only
79
+ git-hunk list src/foo.py src/bar.py # specific files
80
+ git-hunk list --json # JSON output for scripting
81
+ ```
82
+
83
+ ### Show hunks
84
+
85
+ ```bash
86
+ git-hunk show d161935 # show a single hunk
87
+ git-hunk show d161935 a3f82c1 # show multiple hunks
88
+ git-hunk show --all # show all hunks (staged + unstaged)
89
+ git-hunk show --all --staged # show all staged hunks
90
+ git-hunk show --all --unstaged # show all unstaged hunks
91
+ ```
92
+
93
+ ### Stage, unstage, discard
94
+
95
+ ```bash
96
+ git-hunk stage d161935 # stage a hunk
97
+ git-hunk stage d161935 a3f82c1 # stage multiple hunks
98
+ git-hunk stage d161935 -l 3,5-7 # stage specific lines only
99
+ git-hunk unstage d161935 # move back to working tree
100
+ git-hunk unstage d161935 -l 3,5-7 # unstage specific lines only
101
+ git-hunk discard d161935 # restore from HEAD
102
+ git-hunk discard d161935 -l ^3,^5-7 # discard excluding specific lines
103
+ ```
104
+
105
+ ### JSON output
106
+
107
+ ```bash
108
+ git-hunk list --json
109
+ ```
110
+
111
+ ```json
112
+ [
113
+ {
114
+ "id": "d161935",
115
+ "file": "src/main.py",
116
+ "status": "unstaged",
117
+ "header": "@@ -10,3 +10,5 @@",
118
+ "additions": 2,
119
+ "deletions": 0,
120
+ "diff": "..."
121
+ }
122
+ ]
123
+ ```
124
+
125
+ ## Comparison
126
+
127
+ | | Interactive | Programmatic | Hunk IDs | Line-level control | JSON output |
128
+ |---|---|---|---|---|---|
129
+ | `git add -p` | Yes | No | No | Yes | No |
130
+ | `git add <file>` | No | Yes | No | No | No |
131
+ | **`git-hunk`** | **No** | **Yes** | **Yes** | **Yes** | **Yes** |
132
+
133
+ ## How it works
134
+
135
+ 1. Parses `git diff` output into individual hunks
136
+ 2. Assigns each hunk a stable, content-based ID (SHA-256 prefix)
137
+ 3. For staging: reconstructs a minimal patch and pipes it through `git apply --cached`
138
+ 4. For discarding: reconstructs a reverse patch and applies it to the working tree
139
+
140
+ IDs are stable across partial staging -- they are derived from the changed lines,
141
+ not the `@@` line numbers that shift as you stage hunks.
142
+
143
+ ## Contributing
144
+
145
+ Bug reports, feature requests, and pull requests are welcome on
146
+ [GitHub](https://github.com/wkentaro/git-hunk).
147
+
148
+ ```bash
149
+ git clone https://github.com/wkentaro/git-hunk.git
150
+ cd git-hunk
151
+ make setup # install dependencies
152
+ make test # run tests
153
+ make lint # run linters
154
+ ```
155
+
156
+ ## License
157
+
158
+ MIT ([LICENSE](LICENSE))
Binary file
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from git_hunk.cli import cli
2
+
3
+ cli()