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.
Files changed (47) hide show
  1. git_hunk-0.2.0/.gitattributes +1 -0
  2. {git_hunk-0.1.0 → git_hunk-0.2.0}/.github/workflows/lint.yml +1 -2
  3. {git_hunk-0.1.0 → git_hunk-0.2.0}/.github/workflows/publish.yml +2 -4
  4. {git_hunk-0.1.0 → git_hunk-0.2.0}/.github/workflows/test.yml +4 -4
  5. git_hunk-0.2.0/Makefile +41 -0
  6. {git_hunk-0.1.0 → git_hunk-0.2.0}/PKG-INFO +20 -15
  7. {git_hunk-0.1.0 → git_hunk-0.2.0}/README.md +18 -13
  8. git_hunk-0.2.0/git_hunk/__init__.py +3 -0
  9. git_hunk-0.2.0/git_hunk/__main__.py +3 -0
  10. git_hunk-0.1.0/git_hunk/cli.py → git_hunk-0.2.0/git_hunk/_cli.py +31 -40
  11. git_hunk-0.1.0/git_hunk/git.py → git_hunk-0.2.0/git_hunk/_git.py +5 -4
  12. git_hunk-0.1.0/git_hunk/lines.py → git_hunk-0.2.0/git_hunk/_lines.py +2 -2
  13. git_hunk-0.1.0/git_hunk/patch.py → git_hunk-0.2.0/git_hunk/_patch.py +1 -1
  14. git_hunk-0.1.0/git_hunk/ui.py → git_hunk-0.2.0/git_hunk/_ui.py +4 -4
  15. git_hunk-0.2.0/pyproject.toml +67 -0
  16. {git_hunk-0.1.0 → git_hunk-0.2.0}/skills/git-hunk/SKILL.md +6 -5
  17. git_hunk-0.2.0/taplo.toml +3 -0
  18. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/conftest.py +1 -1
  19. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/show_test.py +3 -3
  20. {git_hunk-0.1.0/tests/unit/hunk → git_hunk-0.2.0/tests/unit/_hunk}/id_test.py +5 -5
  21. {git_hunk-0.1.0/tests/unit/hunk → git_hunk-0.2.0/tests/unit/_hunk}/parse_diff_test.py +1 -1
  22. {git_hunk-0.1.0/tests/unit/hunk → git_hunk-0.2.0/tests/unit/_hunk}/split_test.py +1 -1
  23. {git_hunk-0.1.0/tests/unit/lines → git_hunk-0.2.0/tests/unit/_lines}/filter_test.py +2 -2
  24. {git_hunk-0.1.0/tests/unit/lines → git_hunk-0.2.0/tests/unit/_lines}/parse_spec_test.py +1 -1
  25. git_hunk-0.1.0/tests/unit/patch/build_test.py → git_hunk-0.2.0/tests/unit/_patch_test.py +3 -3
  26. {git_hunk-0.1.0 → git_hunk-0.2.0}/uv.lock +369 -0
  27. git_hunk-0.1.0/Makefile +0 -41
  28. git_hunk-0.1.0/git_hunk/__init__.py +0 -1
  29. git_hunk-0.1.0/git_hunk/__main__.py +0 -3
  30. git_hunk-0.1.0/pyproject.toml +0 -62
  31. git_hunk-0.1.0/tests/unit/patch/__init__.py +0 -0
  32. {git_hunk-0.1.0 → git_hunk-0.2.0}/.gitignore +0 -0
  33. {git_hunk-0.1.0 → git_hunk-0.2.0}/.python-version +0 -0
  34. {git_hunk-0.1.0 → git_hunk-0.2.0}/LICENSE +0 -0
  35. {git_hunk-0.1.0 → git_hunk-0.2.0}/assets/teaser.png +0 -0
  36. /git_hunk-0.1.0/git_hunk/hunk.py → /git_hunk-0.2.0/git_hunk/_hunk.py +0 -0
  37. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/__init__.py +0 -0
  38. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/conftest.py +0 -0
  39. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/__init__.py +0 -0
  40. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/discard_test.py +0 -0
  41. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/error_test.py +0 -0
  42. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/list_test.py +0 -0
  43. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/stage_test.py +0 -0
  44. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/e2e/unstage_test.py +0 -0
  45. {git_hunk-0.1.0 → git_hunk-0.2.0}/tests/unit/__init__.py +0 -0
  46. {git_hunk-0.1.0/tests/unit/hunk → git_hunk-0.2.0/tests/unit/_hunk}/__init__.py +0 -0
  47. {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,10 +1,9 @@
1
+ ---
1
2
  name: Lint
2
-
3
3
  on:
4
4
  push:
5
5
  branches: [main]
6
6
  pull_request:
7
-
8
7
  jobs:
9
8
  lint:
10
9
  runs-on: ubuntu-latest
@@ -1,10 +1,8 @@
1
+ ---
1
2
  name: Publish to PyPI
2
-
3
3
  on:
4
4
  push:
5
- tags:
6
- - "v*"
7
-
5
+ tags: [v*]
8
6
  jobs:
9
7
  publish:
10
8
  runs-on: ubuntu-latest
@@ -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: ubuntu-latest
9
+ runs-on: ${{ matrix.os }}
11
10
  strategy:
12
11
  matrix:
13
- python-version: ["3.10", "3.11", "3.12", "3.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
@@ -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.1.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
- [![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)
30
+ [![PyPI](https://img.shields.io/pypi/v/git_hunk.svg)](https://pypi.org/project/git-hunk/)
31
+ [![Python](https://img.shields.io/pypi/pyversions/git_hunk.svg)](https://pypi.org/project/git-hunk/)
32
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)
33
+ [![License](https://img.shields.io/pypi/l/git_hunk.svg)](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
- For Claude Code, Codex, and other AI agents, add the skill via
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 --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
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
- 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
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
- [![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)
3
+ [![PyPI](https://img.shields.io/pypi/v/git_hunk.svg)](https://pypi.org/project/git-hunk/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/git_hunk.svg)](https://pypi.org/project/git-hunk/)
5
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)
6
+ [![License](https://img.shields.io/pypi/l/git_hunk.svg)](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
- For Claude Code, Codex, and other AI agents, add the skill via
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 --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
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
- 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
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))
@@ -0,0 +1,3 @@
1
+ import importlib.metadata
2
+
3
+ __version__ = importlib.metadata.version("git-hunk")
@@ -0,0 +1,3 @@
1
+ from git_hunk._cli import cli
2
+
3
+ cli()
@@ -4,35 +4,34 @@ from dataclasses import replace
4
4
  import click
5
5
 
6
6
  from . import __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_SHOW
28
- from .ui import USAGE_STAGE
29
- from .ui import USAGE_UNSTAGE
30
- from .ui import print_applied
31
- from .ui import print_error
32
- from .ui import print_help
33
- from .ui import print_hunk_diffs
34
- from .ui import print_hunk_list
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 show_all:
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
- text=True,
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(f"git {' '.join(args)} failed: {result.stderr.strip()}")
22
- return result.stdout
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:
@@ -1,8 +1,8 @@
1
1
  import re
2
2
  from dataclasses import replace
3
3
 
4
- from .hunk import Hunk
5
- from .hunk import count_changes
4
+ from ._hunk import Hunk
5
+ from ._hunk import count_changes
6
6
 
7
7
 
8
8
  def parse_line_spec(spec: str) -> tuple[set[int], bool]:
@@ -1,6 +1,6 @@
1
1
  import re
2
2
 
3
- from .hunk import Hunk
3
+ from ._hunk import Hunk
4
4
 
5
5
 
6
6
  def _get_file_header(diff_output: str, filepath: str) -> 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 .hunk import Hunk
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]<id>[/cyan] [cyan][<id>...][/cyan] [cyan][OPTIONS][/cyan]" # noqa: E501
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. IDs support prefix matching.
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: "0.1.0"
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
- 2. `git-hunk show <id> [<id>...]` or `git-hunk show --all` when headers aren't clear enough.
23
- 3. Group hunks into logical commits. Ask the user if grouping is ambiguous.
24
- 4. Stage and commit each group:
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
- 5. `git-hunk list` again to check nothing got left behind.
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
 
@@ -0,0 +1,3 @@
1
+ [formatting]
2
+ reorder_arrays = true
3
+ reorder_keys = true
@@ -5,7 +5,7 @@ import subprocess
5
5
  import pytest
6
6
  from click.testing import CliRunner
7
7
 
8
- from git_hunk.cli import cli as git_hunk_cli
8
+ from git_hunk._cli import cli as git_hunk_cli
9
9
  from tests.conftest import GitRepo
10
10
 
11
11
 
@@ -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 test_show_all_includes_staged_and_unstaged(cli: GitHunkCLI) -> None:
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", "--all")
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", "--all")
84
+ r = cli.run("show", "--staged", "--unstaged")
85
85
  assert r.returncode != 0
@@ -1,8 +1,8 @@
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
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,4 +1,4 @@
1
- from git_hunk.hunk import parse_diff
1
+ from git_hunk._hunk import parse_diff
2
2
 
3
3
 
4
4
  def test_splits_large_hunk() -> None:
@@ -1,4 +1,4 @@
1
- from git_hunk.hunk import _split_hunk
1
+ from git_hunk._hunk import _split_hunk
2
2
 
3
3
 
4
4
  def test_single_region_no_split() -> None:
@@ -1,7 +1,7 @@
1
1
  import pytest
2
2
 
3
- from git_hunk.hunk import Hunk
4
- from git_hunk.lines import filter_hunk_lines
3
+ from git_hunk._hunk import Hunk
4
+ from git_hunk._lines import filter_hunk_lines
5
5
 
6
6
 
7
7
  def _make_hunk(diff: str) -> Hunk:
@@ -1,6 +1,6 @@
1
1
  import pytest
2
2
 
3
- from git_hunk.lines import parse_line_spec
3
+ from git_hunk._lines import parse_line_spec
4
4
 
5
5
 
6
6
  def test_single_line() -> None:
@@ -1,8 +1,8 @@
1
1
  import pytest
2
2
 
3
- from git_hunk.hunk import Hunk
4
- from git_hunk.patch import _get_file_header
5
- from git_hunk.patch import build_patch
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"