gitwise-cli 0.27.0__tar.gz → 0.29.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.
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/CHANGELOG.md +38 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/PKG-INFO +1 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/__init__.py +1 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/__main__.py +2 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/_cli_dispatch.py +2 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/_cli_parser.py +10 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/_i18n_data.json +108 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/branches.py +46 -27
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/clean.py +3 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/commit.py +23 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/conflicts.py +12 -2
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/context.py +7 -6
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/diff.py +15 -3
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/doctor.py +10 -8
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/git.py +1 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/health.py +3 -2
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/log.py +4 -3
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/merge.py +77 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/output.py +10 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/pr.py +10 -8
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/show.py +5 -3
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/snapshot.py +3 -2
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/stash.py +5 -2
- gitwise_cli-0.29.0/gitwise/status.py +133 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/suggest.py +26 -4
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/summarize.py +16 -14
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/tag.py +11 -9
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/update.py +14 -2
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/utils/git_output.py +35 -0
- gitwise_cli-0.29.0/gitwise/utils/in_progress.py +112 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/utils/json_envelope.py +17 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/utils/parsing.py +5 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/worktree.py +8 -4
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/pyproject.toml +2 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/merge.json +12 -5
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_audit.py +5 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_i18n.py +28 -0
- gitwise_cli-0.29.0/tests/test_in_progress.py +215 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_log.py +16 -2
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_snapshot.py +3 -1
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_tag.py +20 -0
- gitwise_cli-0.27.0/gitwise/status.py +0 -93
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/.gitignore +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/LICENSE +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/README.md +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/bin/gitwise +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/_cli_completions.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/_cli_introspection.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/_cli_setup_agents.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/_paths.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/_runtime_config.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/audit.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/design.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/i18n.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/optimize.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/pick.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/py.typed +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/schema.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/__init__.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/adapters/__init__.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/adapters/aider.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/adapters/base.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/adapters/codex.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/adapters/continue_adapter.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/adapters/cursor.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/adapters/opencode.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/adapters/pi.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/exec.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/format.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/plan.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/plan_gitfiles.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/plan_skills.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/__init__.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/aider.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/base.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/claude.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/codex.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/continue_adapter.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/cursor.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/opencode.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/providers/pi.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/state.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/setup_agents/types.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/sync.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/undo.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/gitwise/utils/__init__.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/install.sh +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/agents/skills/git-audit/SKILL.md +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/agents/skills/git-clean/SKILL.md +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/agents/skills/git-optimize/SKILL.md +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/aider/CONVENTIONS.md.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/aider/aider.conf.yml.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/claude/CLAUDE.md.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/claude/rules/gitwise.md +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/claude/settings.json.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/claude/skills/git-audit/SKILL.md +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/claude/skills/git-clean/SKILL.md +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/claude/skills/git-optimize/SKILL.md +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/codex/agents/gitwise.toml.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/continue/rules/gitwise.md.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/cursor/rules/gitwise.mdc.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/git-config-modern.txt +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/hooks/commit-msg +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/hooks/pre-commit +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/opencode/agents/gitwise.md.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/pi/skills/gitwise.md.template +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/audit.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/branches.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/clean.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/commands.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/commit.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/completions.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/conflicts.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/context.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/diff.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/doctor.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/health.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/log.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/optimize.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/pick.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/pr.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/schema.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/setup-agents.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/setup.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/show.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/snapshot.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/stash.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/status.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/suggest.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/summarize.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/sync.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/tag.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/undo.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/update.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/share/schemas/v1/input/worktree.json +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/__init__.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/conftest.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_adapters.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_branches.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_clean.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_commit.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_conflicts.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_context.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_design.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_diff.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_doctor.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_edge_cases.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_git.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_git_output_utils.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_health.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_json_envelope_utils.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_main.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_md_links_script.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_merge.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_optimize.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_output.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_parsing_utils.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_pick.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_pr.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_roadmap_baseline_script.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_sa_plan.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_sa_unit.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_schema_catalog.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_setup.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_setup_agents.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_show.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_stash.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_status.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_suggest.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_summarize.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_sync.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_sync_changelog_es_script.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_undo.py +0 -0
- {gitwise_cli-0.27.0 → gitwise_cli-0.29.0}/tests/test_worktree.py +0 -0
|
@@ -3,6 +3,44 @@
|
|
|
3
3
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
4
4
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## v0.29.0 (2026-06-19)
|
|
7
|
+
|
|
8
|
+
### Feat
|
|
9
|
+
|
|
10
|
+
- in-progress safety + merge --abort/--continue (Sprint 1) (#65)
|
|
11
|
+
- detect in-progress operations and guard suggest/commit (Sprint 1)
|
|
12
|
+
|
|
13
|
+
### Fix
|
|
14
|
+
|
|
15
|
+
- **in_progress**: use os.path.realpath and explicit subprocess timeout (CodeRabbit)
|
|
16
|
+
- **merge**: pick rebase subcommand when state=rebase (gemini PR#65 finding)
|
|
17
|
+
|
|
18
|
+
### Refactor
|
|
19
|
+
|
|
20
|
+
- **merge**: extract _handle_abort_or_continue (Sprint 1 multi-review)
|
|
21
|
+
|
|
22
|
+
## v0.28.0 (2026-06-19)
|
|
23
|
+
|
|
24
|
+
### BREAKING CHANGE
|
|
25
|
+
|
|
26
|
+
- three JSON contract changes for agents. (1) branches --json: current/in_worktree are now bool (were strings); ahead/behind are now int|null (were strings); upstream/tracking are now string|null. (2) log --json: date field changes from --date=iso to --date=iso-strict (e.g. "2026-06-18T14:30:00+00:00"). (3) tag list --json: date field changes from %(creatordate:iso) to %(creatordate:iso-strict), same shift as log. Agents and scripts parsing these fields must update their deserializers. With major_version_zero=true this rotates the minor (0.27.x -> 0.28.0); the auto-release workflow will bump pyproject.toml and regenerate CHANGELOG.md on push to main, surfacing all three breaks under the Breaking Changes section.
|
|
27
|
+
- `branches --json` changes types in 5 fields. `current`
|
|
28
|
+
and `in_worktree` are now bool (were "true"/"false" strings); `ahead` and
|
|
29
|
+
`behind` are now int|null (were strings); `upstream` and `tracking` are
|
|
30
|
+
now string|null (were always strings). Agents and scripts parsing these
|
|
31
|
+
fields must update their deserializers. With major_version_zero=true this
|
|
32
|
+
rotates the minor (0.27.x -> 0.28.0); the auto-release workflow will bump
|
|
33
|
+
pyproject.toml and regenerate CHANGELOG.md on push to main.
|
|
34
|
+
|
|
35
|
+
### Feat
|
|
36
|
+
|
|
37
|
+
- unified loading feedback + structured JSON (BREAKING branches/log/tag) (#64)
|
|
38
|
+
- unified loading feedback + structured JSON for status/branches
|
|
39
|
+
|
|
40
|
+
### Fix
|
|
41
|
+
|
|
42
|
+
- address CI failures and CodeRabbit review findings
|
|
43
|
+
|
|
6
44
|
## v0.27.0 (2026-06-19)
|
|
7
45
|
|
|
8
46
|
### Feat
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitwise-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.29.0
|
|
4
4
|
Summary: Python CLI for optimizing git workflows and Claude Code integration
|
|
5
5
|
Project-URL: Homepage, https://github.com/drzioner/gitwise
|
|
6
6
|
Project-URL: Repository, https://github.com/drzioner/gitwise
|
|
@@ -7,7 +7,7 @@ from ._cli_dispatch import DISPATCH
|
|
|
7
7
|
from ._cli_introspection import extract_command_token, help_payload
|
|
8
8
|
from ._cli_parser import build_parser
|
|
9
9
|
from .i18n import t
|
|
10
|
-
from .output import print_dim, print_json, set_json_pretty
|
|
10
|
+
from .output import print_dim, print_json, set_json_mode, set_json_pretty
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _is_log_json_enabled() -> bool:
|
|
@@ -100,6 +100,7 @@ def main() -> int:
|
|
|
100
100
|
reset_runtime_config()
|
|
101
101
|
|
|
102
102
|
set_json_pretty(args.json_pretty)
|
|
103
|
+
set_json_mode(args.json)
|
|
103
104
|
|
|
104
105
|
if args.lang:
|
|
105
106
|
set_locale(args.lang)
|
|
@@ -269,11 +269,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
269
269
|
p.add_argument("--yes", "-y", action="store_true", help="skip confirmation")
|
|
270
270
|
|
|
271
271
|
p = sub.add_parser("merge", help="merge/rebase with pre-flight checks", parents=[parent])
|
|
272
|
-
p.add_argument(
|
|
272
|
+
p.add_argument(
|
|
273
|
+
"branch", nargs="?", help="branch to merge/rebase from (omit with --abort/--continue)"
|
|
274
|
+
)
|
|
273
275
|
p.add_argument("--rebase", action="store_true", help="rebase instead of merge")
|
|
274
276
|
p.add_argument("--no-ff", action="store_true", help="force no-fast-forward")
|
|
275
277
|
p.add_argument("--dry-run", action="store_true", help="show checks without merging")
|
|
276
278
|
p.add_argument("--yes", "-y", action="store_true", help="skip confirmation")
|
|
279
|
+
p.add_argument("--abort", action="store_true", help="abort an in-progress merge or rebase")
|
|
280
|
+
p.add_argument(
|
|
281
|
+
"--continue",
|
|
282
|
+
dest="continue_merge",
|
|
283
|
+
action="store_true",
|
|
284
|
+
help="continue an in-progress merge or rebase after resolving conflicts",
|
|
285
|
+
)
|
|
277
286
|
|
|
278
287
|
p = sub.add_parser(
|
|
279
288
|
"conflicts", help="conflict detection and resolution helper", parents=[parent]
|
|
@@ -251,6 +251,10 @@
|
|
|
251
251
|
"es": "mensaje",
|
|
252
252
|
"en": "msg"
|
|
253
253
|
},
|
|
254
|
+
"commit_blocked_in_progress": {
|
|
255
|
+
"es": "No se puede commitear: hay una operación {state} en curso. Resuélvela o aborta primero.",
|
|
256
|
+
"en": "Cannot commit: a {state} operation is in progress. Resolve or abort it first."
|
|
257
|
+
},
|
|
254
258
|
"commit_no_message": {
|
|
255
259
|
"es": "usa -m para proveer un mensaje de commit",
|
|
256
260
|
"en": "use -m to provide a commit message"
|
|
@@ -847,6 +851,26 @@
|
|
|
847
851
|
"es": "merge",
|
|
848
852
|
"en": "merge"
|
|
849
853
|
},
|
|
854
|
+
"merge_aborted": {
|
|
855
|
+
"es": "Mezcla abortada",
|
|
856
|
+
"en": "Merge aborted"
|
|
857
|
+
},
|
|
858
|
+
"merge_abort_continue_mutually_exclusive": {
|
|
859
|
+
"es": "--abort y --continue son mutuamente excluyentes",
|
|
860
|
+
"en": "--abort and --continue are mutually exclusive"
|
|
861
|
+
},
|
|
862
|
+
"merge_branch_required": {
|
|
863
|
+
"es": "Especifica la rama a mezclar, o usa --abort/--continue para una operación pausada",
|
|
864
|
+
"en": "Specify a branch to merge, or use --abort/--continue for a paused operation"
|
|
865
|
+
},
|
|
866
|
+
"merge_continued": {
|
|
867
|
+
"es": "Mezcla continuada",
|
|
868
|
+
"en": "Merge continued"
|
|
869
|
+
},
|
|
870
|
+
"merge_no_in_progress": {
|
|
871
|
+
"es": "No hay mezcla/rebase en curso (estado actual: {state}). Usa {action} solo cuando gitwise detecta operación pausada.",
|
|
872
|
+
"en": "No merge/rebase in progress (current state: {state}). Use {action} only when gitwise detects a paused operation."
|
|
873
|
+
},
|
|
850
874
|
"merge_ok": {
|
|
851
875
|
"es": "Mezcla completada: {branch} → {into}",
|
|
852
876
|
"en": "Merge completed: {branch} → {into}"
|
|
@@ -1571,6 +1595,78 @@
|
|
|
1571
1595
|
"es": "Auditando: ejecutando git-sizer...",
|
|
1572
1596
|
"en": "Auditing: running git-sizer..."
|
|
1573
1597
|
},
|
|
1598
|
+
"status_worktree_add": {
|
|
1599
|
+
"es": "Creando worktree para {branch}...",
|
|
1600
|
+
"en": "Creating worktree for {branch}..."
|
|
1601
|
+
},
|
|
1602
|
+
"status_health_scan": {
|
|
1603
|
+
"es": "Calculando salud del repositorio...",
|
|
1604
|
+
"en": "Computing repository health..."
|
|
1605
|
+
},
|
|
1606
|
+
"status_context_scan": {
|
|
1607
|
+
"es": "Recolectando contexto del repositorio...",
|
|
1608
|
+
"en": "Gathering repository context..."
|
|
1609
|
+
},
|
|
1610
|
+
"status_snapshot_gen": {
|
|
1611
|
+
"es": "Generando snapshot...",
|
|
1612
|
+
"en": "Generating snapshot..."
|
|
1613
|
+
},
|
|
1614
|
+
"status_reading_status": {
|
|
1615
|
+
"es": "Leyendo estado del repositorio...",
|
|
1616
|
+
"en": "Reading repository status..."
|
|
1617
|
+
},
|
|
1618
|
+
"status_summarizing": {
|
|
1619
|
+
"es": "Resumiendo repositorio...",
|
|
1620
|
+
"en": "Summarizing repository..."
|
|
1621
|
+
},
|
|
1622
|
+
"status_reading_diff": {
|
|
1623
|
+
"es": "Calculando cambios...",
|
|
1624
|
+
"en": "Computing changes..."
|
|
1625
|
+
},
|
|
1626
|
+
"status_reading_log": {
|
|
1627
|
+
"es": "Leyendo historial de commits...",
|
|
1628
|
+
"en": "Reading commit history..."
|
|
1629
|
+
},
|
|
1630
|
+
"status_loading_commit": {
|
|
1631
|
+
"es": "Cargando commit...",
|
|
1632
|
+
"en": "Loading commit..."
|
|
1633
|
+
},
|
|
1634
|
+
"status_analyzing_branches": {
|
|
1635
|
+
"es": "Analizando ramas...",
|
|
1636
|
+
"en": "Analyzing branches..."
|
|
1637
|
+
},
|
|
1638
|
+
"status_analyzing_staged": {
|
|
1639
|
+
"es": "Analizando cambios preparados...",
|
|
1640
|
+
"en": "Analyzing staged changes..."
|
|
1641
|
+
},
|
|
1642
|
+
"status_detecting_conflicts": {
|
|
1643
|
+
"es": "Detectando conflictos...",
|
|
1644
|
+
"en": "Detecting conflicts..."
|
|
1645
|
+
},
|
|
1646
|
+
"status_reading_stashes": {
|
|
1647
|
+
"es": "Leyendo stashes...",
|
|
1648
|
+
"en": "Reading stashes..."
|
|
1649
|
+
},
|
|
1650
|
+
"status_reading_tags": {
|
|
1651
|
+
"es": "Leyendo tags...",
|
|
1652
|
+
"en": "Reading tags..."
|
|
1653
|
+
},
|
|
1654
|
+
"status_querying_github": {
|
|
1655
|
+
"es": "Consultando GitHub...",
|
|
1656
|
+
"en": "Querying GitHub..."
|
|
1657
|
+
},
|
|
1658
|
+
"status_checking_env": {
|
|
1659
|
+
"es": "Verificando entorno...",
|
|
1660
|
+
"en": "Checking environment..."
|
|
1661
|
+
},
|
|
1662
|
+
"status_scanning_stale": {
|
|
1663
|
+
"es": "Buscando ramas y refs obsoletos...",
|
|
1664
|
+
"en": "Scanning stale branches and refs..."
|
|
1665
|
+
},
|
|
1666
|
+
"status_updating": {
|
|
1667
|
+
"es": "Actualizando gitwise...",
|
|
1668
|
+
"en": "Updating gitwise..."
|
|
1669
|
+
},
|
|
1574
1670
|
"step_commit_graph": {
|
|
1575
1671
|
"es": "git commit-graph write --reachable --changed-paths",
|
|
1576
1672
|
"en": "git commit-graph write --reachable --changed-paths"
|
|
@@ -1595,6 +1691,10 @@
|
|
|
1595
1691
|
"es": "Sugerencia: {message}",
|
|
1596
1692
|
"en": "Suggestion: {message}"
|
|
1597
1693
|
},
|
|
1694
|
+
"suggest_blocked_in_progress": {
|
|
1695
|
+
"es": "No se puede sugerir commit: hay una operación {state} en curso. Resuélvela o aborta primero.",
|
|
1696
|
+
"en": "Cannot suggest commit: a {state} operation is in progress. Resolve or abort it first."
|
|
1697
|
+
},
|
|
1598
1698
|
"suggest_no_staged": {
|
|
1599
1699
|
"es": "No hay archivos en área de preparación",
|
|
1600
1700
|
"en": "No staged files"
|
|
@@ -1927,6 +2027,14 @@
|
|
|
1927
2027
|
"es": "update requiere una instalación vía git clone (no se encontró .git/)",
|
|
1928
2028
|
"en": "update requires a git clone installation (.git/ not found)"
|
|
1929
2029
|
},
|
|
2030
|
+
"update_no_upstream": {
|
|
2031
|
+
"es": "la rama actual '{branch}' no tiene rama de seguimiento; update solo puede actualizar una rama con upstream",
|
|
2032
|
+
"en": "current branch '{branch}' has no upstream; update can only fast-forward a tracking branch"
|
|
2033
|
+
},
|
|
2034
|
+
"update_no_upstream_hint": {
|
|
2035
|
+
"es": "cambia a tu rama principal (p.ej. 'git switch main') o configura el upstream con 'git branch --set-upstream-to=origin/{branch}'",
|
|
2036
|
+
"en": "switch to your main branch (e.g. 'git switch main') or set an upstream with 'git branch --set-upstream-to=origin/{branch}'"
|
|
2037
|
+
},
|
|
1930
2038
|
"invalid_grep_pattern": {
|
|
1931
2039
|
"es": "patrón de búsqueda inválido o peligroso: {pattern}",
|
|
1932
2040
|
"en": "invalid or unsafe grep pattern: {pattern}"
|
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
"""gitwise branches — intelligence dashboard with ahead/behind, merged, stale, worktree info."""
|
|
2
2
|
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TypedDict
|
|
5
|
+
|
|
3
6
|
from .git import require_root, stale_branches, worktree_branches
|
|
4
7
|
from .git import run as git_run
|
|
5
8
|
from .i18n import t
|
|
6
|
-
from .output import error, info, print_dim, print_json, print_table
|
|
9
|
+
from .output import error, info, print_dim, print_json, print_table, status
|
|
7
10
|
from .utils.json_envelope import ok_envelope
|
|
8
11
|
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
class BranchEntry(TypedDict):
|
|
14
|
+
name: str
|
|
15
|
+
current: bool
|
|
16
|
+
sha: str
|
|
17
|
+
subject: str
|
|
18
|
+
age: str
|
|
19
|
+
upstream: str | None
|
|
20
|
+
ahead: int | None
|
|
21
|
+
behind: int | None
|
|
22
|
+
tracking: str | None
|
|
23
|
+
in_worktree: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _parse_track_count(tracking: str, marker: str) -> int | None:
|
|
27
|
+
if marker not in tracking:
|
|
28
|
+
return None
|
|
29
|
+
raw = tracking.split(marker)[1].split(",")[0].strip().rstrip("]")
|
|
30
|
+
return int(raw) if raw.isdigit() else None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _parse_branches(raw: str, wt_branches: set[str]) -> list[BranchEntry]:
|
|
34
|
+
branches: list[BranchEntry] = []
|
|
12
35
|
for line in raw.splitlines():
|
|
13
36
|
if not line.strip():
|
|
14
37
|
continue
|
|
@@ -23,24 +46,18 @@ def _parse_branches(raw: str, wt_branches: set[str]) -> list[dict[str, str]]:
|
|
|
23
46
|
tracking = parts[5] if len(parts) > 5 else ""
|
|
24
47
|
upstream = parts[6] if len(parts) > 6 else ""
|
|
25
48
|
|
|
26
|
-
ahead = behind = ""
|
|
27
|
-
if "[ahead" in tracking:
|
|
28
|
-
ahead = tracking.split("ahead")[1].split(",")[0].strip().rstrip("]")
|
|
29
|
-
if "behind" in tracking:
|
|
30
|
-
behind = tracking.split("behind")[1].strip().rstrip("]")
|
|
31
|
-
|
|
32
49
|
branches.append(
|
|
33
50
|
{
|
|
34
51
|
"name": name,
|
|
35
|
-
"current":
|
|
52
|
+
"current": is_current,
|
|
36
53
|
"sha": sha,
|
|
37
54
|
"subject": subject,
|
|
38
55
|
"age": age,
|
|
39
|
-
"upstream": upstream,
|
|
40
|
-
"ahead": ahead,
|
|
41
|
-
"behind": behind,
|
|
42
|
-
"tracking": tracking,
|
|
43
|
-
"in_worktree":
|
|
56
|
+
"upstream": upstream or None,
|
|
57
|
+
"ahead": _parse_track_count(tracking, "ahead"),
|
|
58
|
+
"behind": _parse_track_count(tracking, "behind"),
|
|
59
|
+
"tracking": tracking or None,
|
|
60
|
+
"in_worktree": name in wt_branches,
|
|
44
61
|
}
|
|
45
62
|
)
|
|
46
63
|
return branches
|
|
@@ -72,7 +89,7 @@ def _print_stale_branches(*, names: list[str], as_json: bool) -> int:
|
|
|
72
89
|
return 0
|
|
73
90
|
|
|
74
91
|
|
|
75
|
-
def _fetch_branch_rows(*, root, remote: bool, sort: str) -> list[
|
|
92
|
+
def _fetch_branch_rows(*, root: Path, remote: bool, sort: str) -> list[BranchEntry] | None:
|
|
76
93
|
wt_branches = worktree_branches(cwd=root)
|
|
77
94
|
ref_pattern = "refs/remotes/" if remote else "refs/heads/"
|
|
78
95
|
fmt = "%(HEAD)\t%(refname:short)\t%(objectname:short)\t%(subject)\t%(committerdate:relative)\t%(upstream:track)\t%(upstream:short)"
|
|
@@ -94,7 +111,7 @@ def _fetch_branch_rows(*, root, remote: bool, sort: str) -> list[dict[str, str]]
|
|
|
94
111
|
|
|
95
112
|
|
|
96
113
|
def _build_branch_rows(
|
|
97
|
-
branches: list[
|
|
114
|
+
branches: list[BranchEntry],
|
|
98
115
|
) -> tuple[list[list[str]], set[int], int | None]:
|
|
99
116
|
rows: list[list[str]] = []
|
|
100
117
|
highlight_rows: set[int] = set()
|
|
@@ -102,28 +119,28 @@ def _build_branch_rows(
|
|
|
102
119
|
for idx, branch_item in enumerate(branches):
|
|
103
120
|
sha = branch_item["sha"][:8]
|
|
104
121
|
subject = branch_item["subject"][:40]
|
|
105
|
-
age = branch_item
|
|
122
|
+
age = branch_item["age"]
|
|
106
123
|
flags: list[str] = []
|
|
107
|
-
if branch_item
|
|
124
|
+
if branch_item["ahead"]:
|
|
108
125
|
flags.append(f"↑{branch_item['ahead']}")
|
|
109
|
-
if branch_item
|
|
126
|
+
if branch_item["behind"]:
|
|
110
127
|
flags.append(f"↓{branch_item['behind']}")
|
|
111
|
-
if branch_item
|
|
128
|
+
if branch_item["in_worktree"]:
|
|
112
129
|
flags.append("wt")
|
|
113
|
-
if branch_item
|
|
130
|
+
if branch_item["upstream"]:
|
|
114
131
|
flags.append(f"→{branch_item['upstream']}")
|
|
115
132
|
status = " ".join(flags) if flags else ""
|
|
116
133
|
name_display = (
|
|
117
|
-
f"* {branch_item['name']}" if branch_item["current"]
|
|
134
|
+
f"* {branch_item['name']}" if branch_item["current"] else branch_item["name"]
|
|
118
135
|
)
|
|
119
136
|
rows.append([name_display, sha, subject, age, status])
|
|
120
|
-
if branch_item["current"]
|
|
137
|
+
if branch_item["current"]:
|
|
121
138
|
current_idx = idx
|
|
122
139
|
highlight_rows.add(idx)
|
|
123
140
|
return rows, highlight_rows, current_idx
|
|
124
141
|
|
|
125
142
|
|
|
126
|
-
def _print_branch_table(branches: list[
|
|
143
|
+
def _print_branch_table(branches: list[BranchEntry]) -> None:
|
|
127
144
|
columns = [
|
|
128
145
|
(t("col_branch"), "name"),
|
|
129
146
|
(t("col_sha"), "sha"),
|
|
@@ -163,13 +180,15 @@ def run_branches(
|
|
|
163
180
|
return 1
|
|
164
181
|
|
|
165
182
|
if stale:
|
|
166
|
-
|
|
183
|
+
with status(t("status_analyzing_branches")):
|
|
184
|
+
names = stale_branches(cwd=root)
|
|
167
185
|
return _print_stale_branches(names=names, as_json=as_json)
|
|
168
186
|
|
|
169
187
|
if not _validate_sort_field(sort):
|
|
170
188
|
return 1
|
|
171
189
|
|
|
172
|
-
|
|
190
|
+
with status(t("status_analyzing_branches")):
|
|
191
|
+
branches = _fetch_branch_rows(root=root, remote=remote, sort=sort)
|
|
173
192
|
if branches is None:
|
|
174
193
|
return 1
|
|
175
194
|
if not branches:
|
|
@@ -22,6 +22,7 @@ from .output import (
|
|
|
22
22
|
print_header,
|
|
23
23
|
print_json,
|
|
24
24
|
print_success,
|
|
25
|
+
status,
|
|
25
26
|
warn,
|
|
26
27
|
)
|
|
27
28
|
from .utils.json_envelope import error_envelope, ok_envelope
|
|
@@ -78,7 +79,8 @@ def run_clean(
|
|
|
78
79
|
return 1
|
|
79
80
|
cwd = root
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
with status(t("status_scanning_stale")):
|
|
83
|
+
deletable, skipped = _categorize(cwd)
|
|
82
84
|
|
|
83
85
|
if dry_run:
|
|
84
86
|
if as_json:
|
|
@@ -7,6 +7,7 @@ from .git import PROTECTED_BRANCHES, current_branch, gpg_status, require_root
|
|
|
7
7
|
from .git import run as git_run
|
|
8
8
|
from .i18n import t
|
|
9
9
|
from .output import error, print_bracket, print_header, print_json
|
|
10
|
+
from .utils.in_progress import detect_in_progress, in_progress_hint
|
|
10
11
|
from .utils.json_envelope import error_envelope, ok_envelope
|
|
11
12
|
|
|
12
13
|
_CONVENTIONAL_RE = re.compile(
|
|
@@ -104,12 +105,34 @@ def run_commit(
|
|
|
104
105
|
dry_run: bool = False,
|
|
105
106
|
as_json: bool = False,
|
|
106
107
|
) -> int:
|
|
108
|
+
"""Validate a conventional-commit message and create a GPG-signed commit.
|
|
109
|
+
|
|
110
|
+
Refuses with ``in_progress_<state>`` if an operation is paused. Enforces
|
|
111
|
+
the project's amend policy (no amending pushed or protected branches)
|
|
112
|
+
and GPG readiness before delegating to ``git commit``.
|
|
113
|
+
"""
|
|
107
114
|
root, err = require_root()
|
|
108
115
|
if err:
|
|
109
116
|
return err
|
|
110
117
|
if root is None:
|
|
111
118
|
return 1
|
|
112
119
|
|
|
120
|
+
in_progress = detect_in_progress(root)
|
|
121
|
+
if in_progress["state"] != "none":
|
|
122
|
+
hint = in_progress_hint(in_progress["state"])
|
|
123
|
+
blocked_msg = t("commit_blocked_in_progress", state=in_progress["state"])
|
|
124
|
+
if as_json:
|
|
125
|
+
print_json(
|
|
126
|
+
error_envelope(
|
|
127
|
+
error=blocked_msg,
|
|
128
|
+
code=f"in_progress_{in_progress['state']}",
|
|
129
|
+
hint=hint,
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
return 1
|
|
133
|
+
error(blocked_msg, hint=hint)
|
|
134
|
+
return 1
|
|
135
|
+
|
|
113
136
|
amend_policy_rc = _validate_amend_policy(amend=amend, root=root)
|
|
114
137
|
if amend_policy_rc != 0:
|
|
115
138
|
return amend_policy_rc
|
|
@@ -5,7 +5,16 @@ from pathlib import Path
|
|
|
5
5
|
from .git import require_root
|
|
6
6
|
from .git import run as git_run
|
|
7
7
|
from .i18n import t
|
|
8
|
-
from .output import
|
|
8
|
+
from .output import (
|
|
9
|
+
error,
|
|
10
|
+
ok,
|
|
11
|
+
print_accent,
|
|
12
|
+
print_blank,
|
|
13
|
+
print_dim,
|
|
14
|
+
print_header,
|
|
15
|
+
print_json,
|
|
16
|
+
status,
|
|
17
|
+
)
|
|
9
18
|
from .utils.json_envelope import error_envelope, ok_envelope
|
|
10
19
|
from .utils.parsing import stripped_non_empty_lines, to_int
|
|
11
20
|
|
|
@@ -90,7 +99,8 @@ def run_conflicts(
|
|
|
90
99
|
if root is None:
|
|
91
100
|
return 1
|
|
92
101
|
|
|
93
|
-
|
|
102
|
+
with status(t("status_detecting_conflicts")):
|
|
103
|
+
conflicts = _find_conflict_files(root)
|
|
94
104
|
|
|
95
105
|
if not conflicts:
|
|
96
106
|
return _report_no_conflicts(as_json=as_json)
|
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from .git import require_root
|
|
6
6
|
from .git import run as git_run
|
|
7
7
|
from .i18n import t
|
|
8
|
-
from .output import info, print_blank, print_bracket, print_dim, print_header, print_json
|
|
8
|
+
from .output import info, print_blank, print_bracket, print_dim, print_header, print_json, status
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def _directory_tree(root: Path, max_depth: int = 3) -> list[str]:
|
|
@@ -108,11 +108,12 @@ def run_context(*, as_json: bool = False) -> int:
|
|
|
108
108
|
if root is None:
|
|
109
109
|
return 1
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
with status(t("status_context_scan")):
|
|
112
|
+
tree = _directory_tree(root)
|
|
113
|
+
contributors = _top_contributors(root)
|
|
114
|
+
file_types = _file_type_breakdown(root)
|
|
115
|
+
todo_fixme = _todo_fixme_counts(root)
|
|
116
|
+
topology = _branch_topology(root)
|
|
116
117
|
|
|
117
118
|
if as_json:
|
|
118
119
|
from .health import compute_health
|
|
@@ -15,8 +15,9 @@ from .output import (
|
|
|
15
15
|
print_file_status,
|
|
16
16
|
print_header,
|
|
17
17
|
print_json,
|
|
18
|
+
status,
|
|
18
19
|
)
|
|
19
|
-
from .utils.git_output import parse_name_status_entries
|
|
20
|
+
from .utils.git_output import parse_name_status_entries, status_label
|
|
20
21
|
from .utils.json_envelope import ok_envelope
|
|
21
22
|
|
|
22
23
|
DiffValue = str | int | bool
|
|
@@ -205,6 +206,9 @@ def _render_stat_output(
|
|
|
205
206
|
numstat_details=numstat_details,
|
|
206
207
|
)
|
|
207
208
|
if as_json:
|
|
209
|
+
for entry in merged_files:
|
|
210
|
+
raw_code = str(entry.get("code", entry.get("status", "")))
|
|
211
|
+
entry["status_label"] = status_label(raw_code)
|
|
208
212
|
print_json(
|
|
209
213
|
ok_envelope(
|
|
210
214
|
files=merged_files,
|
|
@@ -251,7 +255,14 @@ def _render_non_stat_output(
|
|
|
251
255
|
for line in lines:
|
|
252
256
|
parts = line.split("\t", 1)
|
|
253
257
|
if len(parts) == 2:
|
|
254
|
-
|
|
258
|
+
code = parts[0].strip()
|
|
259
|
+
files.append(
|
|
260
|
+
{
|
|
261
|
+
"status": code,
|
|
262
|
+
"status_label": status_label(code),
|
|
263
|
+
"path": parts[1].strip(),
|
|
264
|
+
}
|
|
265
|
+
)
|
|
255
266
|
|
|
256
267
|
if as_json:
|
|
257
268
|
print_json(ok_envelope(files=files, count=len(files)))
|
|
@@ -287,7 +298,8 @@ def run_diff(
|
|
|
287
298
|
|
|
288
299
|
use_stat = stat or (not staged and not name_only and not full)
|
|
289
300
|
cmd = _diff_cmd(use_stat=use_stat, staged=staged, name_only=name_only, full=full)
|
|
290
|
-
|
|
301
|
+
with status(t("status_reading_diff")):
|
|
302
|
+
result = git_run(cmd, cwd=cwd, check=False)
|
|
291
303
|
if result.returncode != 0:
|
|
292
304
|
error(t("git_diff_failed", error=result.stderr.strip()))
|
|
293
305
|
return 1
|
|
@@ -15,6 +15,7 @@ from .output import (
|
|
|
15
15
|
print_json,
|
|
16
16
|
print_kv,
|
|
17
17
|
print_status_line,
|
|
18
|
+
status,
|
|
18
19
|
warn,
|
|
19
20
|
)
|
|
20
21
|
|
|
@@ -33,17 +34,18 @@ _TOOL_INFO: dict[str, tuple[str, str]] = {
|
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def run_doctor(*, as_json: bool = False) -> int:
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
with status(t("status_checking_env")):
|
|
38
|
+
git_ver = git_version()
|
|
39
|
+
git_ok = git_ver >= MIN_GIT
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
python_ver = sys.version_info[:3]
|
|
42
|
+
python_ok = python_ver >= (3, 9)
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
platform_name = platform.system()
|
|
45
|
+
fsmonitor_supported = platform_name in ("Darwin", "Windows")
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
optional = {tool: bool(shutil.which(tool)) for tool in _OPTIONAL_TOOLS}
|
|
48
|
+
gpg = gpg_status()
|
|
47
49
|
|
|
48
50
|
result = {
|
|
49
51
|
"v": 2,
|
|
@@ -61,7 +61,7 @@ def run(
|
|
|
61
61
|
) -> subprocess.CompletedProcess[str]:
|
|
62
62
|
from .output import debug
|
|
63
63
|
|
|
64
|
-
cmd_name = args
|
|
64
|
+
cmd_name = next((arg for arg in args if not arg.startswith("-")), None)
|
|
65
65
|
actual_timeout = timeout if timeout is not None else _get_timeout(cmd_name)
|
|
66
66
|
debug(f"git {' '.join(args)}")
|
|
67
67
|
try:
|
|
@@ -12,7 +12,7 @@ from .git import (
|
|
|
12
12
|
)
|
|
13
13
|
from .git import run as git_run
|
|
14
14
|
from .i18n import t
|
|
15
|
-
from .output import print_header, print_json, print_status_line
|
|
15
|
+
from .output import print_header, print_json, print_status_line, status
|
|
16
16
|
from .utils.json_envelope import ok_envelope
|
|
17
17
|
from .utils.parsing import non_empty_lines, to_int
|
|
18
18
|
|
|
@@ -330,7 +330,8 @@ def run_health(*, as_json: bool = False) -> int:
|
|
|
330
330
|
if root is None:
|
|
331
331
|
return 1
|
|
332
332
|
|
|
333
|
-
|
|
333
|
+
with status(t("status_health_scan")):
|
|
334
|
+
h = compute_health(root)
|
|
334
335
|
|
|
335
336
|
if as_json:
|
|
336
337
|
print_json(ok_envelope(payload=h))
|
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from .git import require_root, validate_author_pattern, validate_grep_pattern
|
|
7
7
|
from .git import run as git_run
|
|
8
8
|
from .i18n import t
|
|
9
|
-
from .output import bat_pipe, error, info, print_json, print_table
|
|
9
|
+
from .output import bat_pipe, error, info, print_json, print_table, status
|
|
10
10
|
from .utils.json_envelope import ok_envelope
|
|
11
11
|
|
|
12
12
|
|
|
@@ -62,7 +62,7 @@ def _build_log_json_args(
|
|
|
62
62
|
"log",
|
|
63
63
|
f"--max-count={max_count}",
|
|
64
64
|
"--format=%H%n%h%n%an%n%ae%n%ad%n%s%n%P%n---END---",
|
|
65
|
-
"--date=iso",
|
|
65
|
+
"--date=iso-strict",
|
|
66
66
|
]
|
|
67
67
|
if author:
|
|
68
68
|
if not validate_author_pattern(author):
|
|
@@ -240,7 +240,8 @@ def _run_log_human(
|
|
|
240
240
|
)
|
|
241
241
|
except ValueError:
|
|
242
242
|
return 1
|
|
243
|
-
|
|
243
|
+
with status(t("status_reading_log")):
|
|
244
|
+
result = git_run(args, cwd=root, check=False)
|
|
244
245
|
if result.returncode != 0:
|
|
245
246
|
if result.stderr and "does not have any commits yet" in result.stderr:
|
|
246
247
|
info(t("no_commits_yet"))
|