git-gx 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_gx-0.2.0/LICENSE +21 -0
- git_gx-0.2.0/PKG-INFO +211 -0
- git_gx-0.2.0/README.md +177 -0
- git_gx-0.2.0/pyproject.toml +163 -0
- git_gx-0.2.0/src/gx/__init__.py +3 -0
- git_gx-0.2.0/src/gx/cli.py +85 -0
- git_gx-0.2.0/src/gx/commands/__init__.py +1 -0
- git_gx-0.2.0/src/gx/commands/clean.py +369 -0
- git_gx-0.2.0/src/gx/commands/done.py +153 -0
- git_gx-0.2.0/src/gx/commands/feat.py +199 -0
- git_gx-0.2.0/src/gx/commands/pull.py +214 -0
- git_gx-0.2.0/src/gx/commands/push.py +175 -0
- git_gx-0.2.0/src/gx/commands/status.py +565 -0
- git_gx-0.2.0/src/gx/constants.py +57 -0
- git_gx-0.2.0/src/gx/lib/__init__.py +1 -0
- git_gx-0.2.0/src/gx/lib/branch.py +279 -0
- git_gx-0.2.0/src/gx/lib/config.py +156 -0
- git_gx-0.2.0/src/gx/lib/console.py +95 -0
- git_gx-0.2.0/src/gx/lib/git.py +158 -0
- git_gx-0.2.0/src/gx/lib/options.py +18 -0
- git_gx-0.2.0/src/gx/lib/worktree.py +146 -0
git_gx-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nate Landau
|
|
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.
|
git_gx-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: git-gx
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A CLI that wraps common git commands with sensible defaults, safety guards, and helpful summaries.
|
|
5
|
+
Author: Nate Landau
|
|
6
|
+
Author-email: Nate Landau <github@natenate.org>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026 Nate Landau
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
30
|
+
Requires-Dist: rich>=14.3.3
|
|
31
|
+
Requires-Dist: typer>=0.24.1
|
|
32
|
+
Requires-Python: >=3.13, <3.15
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# gx
|
|
36
|
+
|
|
37
|
+
A CLI that wraps common git commands with sensible defaults, safety guards, and helpful summaries.
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- Auto-numbered feature branches with optional worktree isolation
|
|
42
|
+
- Push with dirty-tree warnings and default-branch confirmation
|
|
43
|
+
- Pull with automatic stash/unstash and rebase
|
|
44
|
+
- Batch cleanup of merged, gone, and empty branches
|
|
45
|
+
- Rich status dashboard showing all branches at a glance
|
|
46
|
+
- Dry-run mode (`-n`) on every command
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
gx requires Python 3.13 or higher.
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
# install via uv
|
|
54
|
+
uv tool install git-gx
|
|
55
|
+
|
|
56
|
+
# or install via pip
|
|
57
|
+
pip install git-gx
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
gx feat # create feat/1 from main
|
|
64
|
+
# ... make changes, commit ...
|
|
65
|
+
gx push # push to origin with tracking
|
|
66
|
+
# ... PR merged ...
|
|
67
|
+
gx done # checkout main, pull, delete feat/1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Commands
|
|
71
|
+
|
|
72
|
+
Every command supports `-h` for help. All commands except `status` also support `-v`/`-vv` for verbosity and `-n` for dry-run.
|
|
73
|
+
|
|
74
|
+
### `gx status`
|
|
75
|
+
|
|
76
|
+
Display a two-panel dashboard: a color-coded file tree of uncommitted changes and a table of all active branches with ahead/behind counts, file metrics, and stash counts. Running `gx` with no arguments inside a repo shows this dashboard automatically.
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
gx status # full dashboard
|
|
80
|
+
gx status -F # file tree only
|
|
81
|
+
gx status -b # branch table only
|
|
82
|
+
gx status -a # include inactive branches
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `gx feat`
|
|
86
|
+
|
|
87
|
+
Create a new feature branch from the latest default branch. Without a name, branches are auto-numbered (`feat/1`, `feat/2`, ...), filling gaps in the sequence.
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
gx feat # create feat/1 (or next available)
|
|
91
|
+
gx feat login # create feat/login
|
|
92
|
+
gx feat -w # create in a git worktree at .worktrees/feat/1
|
|
93
|
+
gx feat -w ui # create worktree at .worktrees/feat/ui
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Worktree mode (`-w`) lets you work on multiple branches simultaneously without stashing. Requires `.worktrees/` to be in `.gitignore`.
|
|
97
|
+
|
|
98
|
+
### `gx push`
|
|
99
|
+
|
|
100
|
+
Push the current branch to its remote tracking branch (or `origin/<branch>` on first push). Automatically sets up tracking.
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
gx push # push current branch
|
|
104
|
+
gx push -f # force push with --force-with-lease
|
|
105
|
+
gx push -t # push commits and all tags
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Safety guards:
|
|
109
|
+
|
|
110
|
+
- Warns about uncommitted or untracked files that won't be included
|
|
111
|
+
- Asks for confirmation before pushing to the default branch
|
|
112
|
+
- Uses `--force-with-lease` instead of `--force` to prevent overwriting others' work
|
|
113
|
+
|
|
114
|
+
### `gx pull`
|
|
115
|
+
|
|
116
|
+
Fetch and rebase the current branch onto its upstream. Handles uncommitted changes automatically.
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
gx pull # pull and rebase
|
|
120
|
+
gx pull -v # pull with debug output
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The full sequence:
|
|
124
|
+
|
|
125
|
+
1. stash uncommitted changes
|
|
126
|
+
2. fetch
|
|
127
|
+
3. rebase
|
|
128
|
+
4. update submodules (if `.gitmodules` exists)
|
|
129
|
+
5. restore stash
|
|
130
|
+
6. print a summary
|
|
131
|
+
|
|
132
|
+
If a rebase conflict occurs, gx restores your stash and prints resolution steps.
|
|
133
|
+
|
|
134
|
+
### `gx clean`
|
|
135
|
+
|
|
136
|
+
Remove branches and worktrees that are no longer needed. Fetches with `--prune` first, then finds branches that are:
|
|
137
|
+
|
|
138
|
+
- **merged** into the default branch
|
|
139
|
+
- **gone** (upstream deleted on the remote)
|
|
140
|
+
- **empty** (zero commits ahead of the default branch)
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
gx clean # interactive cleanup
|
|
144
|
+
gx clean -y # skip confirmation prompt
|
|
145
|
+
gx clean -f # include dirty worktrees
|
|
146
|
+
gx clean -n # preview what would be removed
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The current branch, `main`, `master`, and `develop` are always protected. Dirty worktrees are skipped unless you pass `--force`.
|
|
150
|
+
|
|
151
|
+
### `gx done`
|
|
152
|
+
|
|
153
|
+
Post-merge cleanup. Checks out the default branch, pulls latest changes, and deletes the feature branch you were on.
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
gx done # clean up after a merged PR
|
|
157
|
+
gx done -n # preview what would happen
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
If run from a worktree, gx removes the worktree first, then switches to the main working directory.
|
|
161
|
+
|
|
162
|
+
## Global Options
|
|
163
|
+
|
|
164
|
+
| Flag | Description |
|
|
165
|
+
| ------------------ | ------------------------------------------- |
|
|
166
|
+
| `-v` | Debug output (shows git commands) |
|
|
167
|
+
| `-vv` | Trace output (shows git stdout/stderr) |
|
|
168
|
+
| `-n` / `--dry-run` | Preview changes without executing mutations |
|
|
169
|
+
| `-h` / `--help` | Show help for any command |
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
gx works out of the box with no configuration. Optionally, create `~/.config/gx/config.toml` to customize defaults:
|
|
174
|
+
|
|
175
|
+
```toml
|
|
176
|
+
[branches]
|
|
177
|
+
prefix = "feat" # branch prefix for `gx feat`
|
|
178
|
+
protected = ["main", "master", "develop"] # branches protected from cleanup
|
|
179
|
+
|
|
180
|
+
[worktree]
|
|
181
|
+
directory = ".worktrees" # worktree base directory
|
|
182
|
+
|
|
183
|
+
[remote]
|
|
184
|
+
name = "origin" # default remote name
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
All keys are optional - only specify the ones you want to change.
|
|
188
|
+
|
|
189
|
+
### Worktree directory
|
|
190
|
+
|
|
191
|
+
The worktree directory can be relative or absolute:
|
|
192
|
+
|
|
193
|
+
- **Relative** (e.g. `.worktrees`) - resolved from the repo root. Must be in `.gitignore`.
|
|
194
|
+
- **Absolute** (e.g. `~/tmp/worktrees`) - used as-is. No `.gitignore` requirement.
|
|
195
|
+
|
|
196
|
+
### Environment variables
|
|
197
|
+
|
|
198
|
+
Override any setting per-invocation with environment variables:
|
|
199
|
+
|
|
200
|
+
| Variable | Example |
|
|
201
|
+
| ----------------------- | ------------------------------------------------ |
|
|
202
|
+
| `GX_BRANCH_PREFIX` | `GX_BRANCH_PREFIX=fix gx feat` |
|
|
203
|
+
| `GX_WORKTREE_DIRECTORY` | `GX_WORKTREE_DIRECTORY=~/wt gx feat -w` |
|
|
204
|
+
| `GX_PROTECTED_BRANCHES` | `GX_PROTECTED_BRANCHES=main,production gx clean` |
|
|
205
|
+
| `GX_REMOTE_NAME` | `GX_REMOTE_NAME=upstream gx push` |
|
|
206
|
+
|
|
207
|
+
Environment variables take priority over the config file.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|
git_gx-0.2.0/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# gx
|
|
2
|
+
|
|
3
|
+
A CLI that wraps common git commands with sensible defaults, safety guards, and helpful summaries.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Auto-numbered feature branches with optional worktree isolation
|
|
8
|
+
- Push with dirty-tree warnings and default-branch confirmation
|
|
9
|
+
- Pull with automatic stash/unstash and rebase
|
|
10
|
+
- Batch cleanup of merged, gone, and empty branches
|
|
11
|
+
- Rich status dashboard showing all branches at a glance
|
|
12
|
+
- Dry-run mode (`-n`) on every command
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
gx requires Python 3.13 or higher.
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
# install via uv
|
|
20
|
+
uv tool install git-gx
|
|
21
|
+
|
|
22
|
+
# or install via pip
|
|
23
|
+
pip install git-gx
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
gx feat # create feat/1 from main
|
|
30
|
+
# ... make changes, commit ...
|
|
31
|
+
gx push # push to origin with tracking
|
|
32
|
+
# ... PR merged ...
|
|
33
|
+
gx done # checkout main, pull, delete feat/1
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
Every command supports `-h` for help. All commands except `status` also support `-v`/`-vv` for verbosity and `-n` for dry-run.
|
|
39
|
+
|
|
40
|
+
### `gx status`
|
|
41
|
+
|
|
42
|
+
Display a two-panel dashboard: a color-coded file tree of uncommitted changes and a table of all active branches with ahead/behind counts, file metrics, and stash counts. Running `gx` with no arguments inside a repo shows this dashboard automatically.
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
gx status # full dashboard
|
|
46
|
+
gx status -F # file tree only
|
|
47
|
+
gx status -b # branch table only
|
|
48
|
+
gx status -a # include inactive branches
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### `gx feat`
|
|
52
|
+
|
|
53
|
+
Create a new feature branch from the latest default branch. Without a name, branches are auto-numbered (`feat/1`, `feat/2`, ...), filling gaps in the sequence.
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
gx feat # create feat/1 (or next available)
|
|
57
|
+
gx feat login # create feat/login
|
|
58
|
+
gx feat -w # create in a git worktree at .worktrees/feat/1
|
|
59
|
+
gx feat -w ui # create worktree at .worktrees/feat/ui
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Worktree mode (`-w`) lets you work on multiple branches simultaneously without stashing. Requires `.worktrees/` to be in `.gitignore`.
|
|
63
|
+
|
|
64
|
+
### `gx push`
|
|
65
|
+
|
|
66
|
+
Push the current branch to its remote tracking branch (or `origin/<branch>` on first push). Automatically sets up tracking.
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
gx push # push current branch
|
|
70
|
+
gx push -f # force push with --force-with-lease
|
|
71
|
+
gx push -t # push commits and all tags
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Safety guards:
|
|
75
|
+
|
|
76
|
+
- Warns about uncommitted or untracked files that won't be included
|
|
77
|
+
- Asks for confirmation before pushing to the default branch
|
|
78
|
+
- Uses `--force-with-lease` instead of `--force` to prevent overwriting others' work
|
|
79
|
+
|
|
80
|
+
### `gx pull`
|
|
81
|
+
|
|
82
|
+
Fetch and rebase the current branch onto its upstream. Handles uncommitted changes automatically.
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
gx pull # pull and rebase
|
|
86
|
+
gx pull -v # pull with debug output
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The full sequence:
|
|
90
|
+
|
|
91
|
+
1. stash uncommitted changes
|
|
92
|
+
2. fetch
|
|
93
|
+
3. rebase
|
|
94
|
+
4. update submodules (if `.gitmodules` exists)
|
|
95
|
+
5. restore stash
|
|
96
|
+
6. print a summary
|
|
97
|
+
|
|
98
|
+
If a rebase conflict occurs, gx restores your stash and prints resolution steps.
|
|
99
|
+
|
|
100
|
+
### `gx clean`
|
|
101
|
+
|
|
102
|
+
Remove branches and worktrees that are no longer needed. Fetches with `--prune` first, then finds branches that are:
|
|
103
|
+
|
|
104
|
+
- **merged** into the default branch
|
|
105
|
+
- **gone** (upstream deleted on the remote)
|
|
106
|
+
- **empty** (zero commits ahead of the default branch)
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
gx clean # interactive cleanup
|
|
110
|
+
gx clean -y # skip confirmation prompt
|
|
111
|
+
gx clean -f # include dirty worktrees
|
|
112
|
+
gx clean -n # preview what would be removed
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The current branch, `main`, `master`, and `develop` are always protected. Dirty worktrees are skipped unless you pass `--force`.
|
|
116
|
+
|
|
117
|
+
### `gx done`
|
|
118
|
+
|
|
119
|
+
Post-merge cleanup. Checks out the default branch, pulls latest changes, and deletes the feature branch you were on.
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
gx done # clean up after a merged PR
|
|
123
|
+
gx done -n # preview what would happen
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If run from a worktree, gx removes the worktree first, then switches to the main working directory.
|
|
127
|
+
|
|
128
|
+
## Global Options
|
|
129
|
+
|
|
130
|
+
| Flag | Description |
|
|
131
|
+
| ------------------ | ------------------------------------------- |
|
|
132
|
+
| `-v` | Debug output (shows git commands) |
|
|
133
|
+
| `-vv` | Trace output (shows git stdout/stderr) |
|
|
134
|
+
| `-n` / `--dry-run` | Preview changes without executing mutations |
|
|
135
|
+
| `-h` / `--help` | Show help for any command |
|
|
136
|
+
|
|
137
|
+
## Configuration
|
|
138
|
+
|
|
139
|
+
gx works out of the box with no configuration. Optionally, create `~/.config/gx/config.toml` to customize defaults:
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
[branches]
|
|
143
|
+
prefix = "feat" # branch prefix for `gx feat`
|
|
144
|
+
protected = ["main", "master", "develop"] # branches protected from cleanup
|
|
145
|
+
|
|
146
|
+
[worktree]
|
|
147
|
+
directory = ".worktrees" # worktree base directory
|
|
148
|
+
|
|
149
|
+
[remote]
|
|
150
|
+
name = "origin" # default remote name
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
All keys are optional - only specify the ones you want to change.
|
|
154
|
+
|
|
155
|
+
### Worktree directory
|
|
156
|
+
|
|
157
|
+
The worktree directory can be relative or absolute:
|
|
158
|
+
|
|
159
|
+
- **Relative** (e.g. `.worktrees`) - resolved from the repo root. Must be in `.gitignore`.
|
|
160
|
+
- **Absolute** (e.g. `~/tmp/worktrees`) - used as-is. No `.gitignore` requirement.
|
|
161
|
+
|
|
162
|
+
### Environment variables
|
|
163
|
+
|
|
164
|
+
Override any setting per-invocation with environment variables:
|
|
165
|
+
|
|
166
|
+
| Variable | Example |
|
|
167
|
+
| ----------------------- | ------------------------------------------------ |
|
|
168
|
+
| `GX_BRANCH_PREFIX` | `GX_BRANCH_PREFIX=fix gx feat` |
|
|
169
|
+
| `GX_WORKTREE_DIRECTORY` | `GX_WORKTREE_DIRECTORY=~/wt gx feat -w` |
|
|
170
|
+
| `GX_PROTECTED_BRANCHES` | `GX_PROTECTED_BRANCHES=main,production gx clean` |
|
|
171
|
+
| `GX_REMOTE_NAME` | `GX_REMOTE_NAME=upstream gx push` |
|
|
172
|
+
|
|
173
|
+
Environment variables take priority over the config file.
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
authors = [{ name = "Nate Landau", email = "github@natenate.org" }]
|
|
3
|
+
classifiers = [
|
|
4
|
+
"Programming Language :: Python :: 3.13",
|
|
5
|
+
"Programming Language :: Python :: 3.14",
|
|
6
|
+
]
|
|
7
|
+
dependencies = ["rich>=14.3.3", "typer>=0.24.1"]
|
|
8
|
+
description = "A CLI that wraps common git commands with sensible defaults, safety guards, and helpful summaries."
|
|
9
|
+
license = { file = "LICENSE" }
|
|
10
|
+
name = "git-gx"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.13,<3.15"
|
|
13
|
+
version = "0.2.0"
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
gx = "gx.cli:main"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
build-backend = "uv_build"
|
|
20
|
+
requires = ["uv_build>=0.10.12,<0.11.0"]
|
|
21
|
+
|
|
22
|
+
[tool.uv.build-backend]
|
|
23
|
+
module-name = "gx"
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = [
|
|
27
|
+
"commitizen>=4.13.9",
|
|
28
|
+
"coverage>=7.13.5",
|
|
29
|
+
"duty>=1.9.0",
|
|
30
|
+
"prek>=0.3.6",
|
|
31
|
+
"pytest-clarity>=1.0.1",
|
|
32
|
+
"pytest-cov>=7.1.0",
|
|
33
|
+
"pytest-devtools>=1.0.0",
|
|
34
|
+
"pytest-mock>=3.15.1",
|
|
35
|
+
"pytest-repeat>=0.9.4",
|
|
36
|
+
"pytest-sugar>=1.1.1",
|
|
37
|
+
"pytest-xdist>=3.8.0",
|
|
38
|
+
"pytest>=9.0.2",
|
|
39
|
+
"ruff>=0.15.7",
|
|
40
|
+
"shellcheck-py>=0.11.0.1",
|
|
41
|
+
"ty>=0.0.24",
|
|
42
|
+
"typos>=1.44.0",
|
|
43
|
+
"yamllint>=1.38.0",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.commitizen]
|
|
47
|
+
bump_message = "bump(release): v$current_version → v$new_version"
|
|
48
|
+
changelog_merge_prerelease = true
|
|
49
|
+
tag_format = "v$version"
|
|
50
|
+
update_changelog_on_bump = true
|
|
51
|
+
version_files = ["src/gx/__init__.py:__version__"]
|
|
52
|
+
version_provider = "uv"
|
|
53
|
+
|
|
54
|
+
[tool.coverage.report] # https://coverage.readthedocs.io/en/latest/config.html#report
|
|
55
|
+
exclude_lines = [
|
|
56
|
+
'def __repr__',
|
|
57
|
+
'except [\w\s\._]+ as .*:',
|
|
58
|
+
'if TYPE_CHECKING',
|
|
59
|
+
'pragma: no cover',
|
|
60
|
+
]
|
|
61
|
+
fail_under = 10
|
|
62
|
+
show_missing = true
|
|
63
|
+
skip_covered = true
|
|
64
|
+
skip_empty = true
|
|
65
|
+
|
|
66
|
+
[tool.coverage.run]
|
|
67
|
+
branch = true
|
|
68
|
+
command_line = "--module pytest"
|
|
69
|
+
data_file = ".cache/coverage"
|
|
70
|
+
omit = ["tests/*"]
|
|
71
|
+
source = ["src"]
|
|
72
|
+
|
|
73
|
+
[tool.coverage.xml]
|
|
74
|
+
output = ".cache/coverage.xml"
|
|
75
|
+
|
|
76
|
+
[tool.pytest.ini_options]
|
|
77
|
+
|
|
78
|
+
addopts = "--color=yes --doctest-modules --strict-config --strict-markers -n auto --dist loadfile"
|
|
79
|
+
cache_dir = ".cache/pytest"
|
|
80
|
+
columns = 120
|
|
81
|
+
filterwarnings = ['error', 'ignore:.*Pydantic.*:UserWarning', 'ignore::DeprecationWarning:']
|
|
82
|
+
markers = ["serial"]
|
|
83
|
+
set_columns = true
|
|
84
|
+
testpaths = ["tests"]
|
|
85
|
+
xfail_strict = true
|
|
86
|
+
|
|
87
|
+
[tool.ruff] # https://github.com/charliermarsh/ruff
|
|
88
|
+
|
|
89
|
+
exclude = [".cache", ".git", ".venv", "build", "dist", "reference"]
|
|
90
|
+
fix = true
|
|
91
|
+
line-length = 100
|
|
92
|
+
output-format = "grouped"
|
|
93
|
+
src = ["src", "tests"]
|
|
94
|
+
target-version = "py313"
|
|
95
|
+
[tool.ruff.lint]
|
|
96
|
+
ignore = [
|
|
97
|
+
"ANN002", # Missing type annotation for `*args`
|
|
98
|
+
"ANN003", # Missing type annotation for `**kwargs`
|
|
99
|
+
"ANN204", # missing return type annotation for special method `__init__`
|
|
100
|
+
# "B006", # mutable-argument-default
|
|
101
|
+
# "B008", # function-call-in-default-argument
|
|
102
|
+
"COM812", # Trailing comma missing"
|
|
103
|
+
"CPY001", # Missing copyright notice at top of file
|
|
104
|
+
"D107", # undocumented-public-init
|
|
105
|
+
"E501", # line-too-long
|
|
106
|
+
"PLC0415", # Imports should be at top of file
|
|
107
|
+
# "FBT001", # Boolean-typed positional argument in function definition
|
|
108
|
+
# "FBT002", # Boolean-typed positional argument in function definition
|
|
109
|
+
"FIX002", # Line contains TODO, consider resolving the issue
|
|
110
|
+
"S311", # suspicious-non-cryptographic-random-usage
|
|
111
|
+
"TD001", # invalid-todo-tag
|
|
112
|
+
"TD002", # Missing author in TODO
|
|
113
|
+
"TD003", # Missing issue link on the line following this TODO
|
|
114
|
+
]
|
|
115
|
+
per-file-ignores = { "tests/**/*.py" = [
|
|
116
|
+
"A002",
|
|
117
|
+
"A003",
|
|
118
|
+
"ANN001", # Missing type annotation for function argument `cls`
|
|
119
|
+
"ANN002", # Missing type annotation for `*args`
|
|
120
|
+
"ANN003", # Missing type annotation for `**kwargs`
|
|
121
|
+
"ANN201", # Missing return type annotation
|
|
122
|
+
"ARG001", # Unused function argument
|
|
123
|
+
"ARG002", # Unused method argument
|
|
124
|
+
"ARG005", # Unused lambda argument
|
|
125
|
+
"D102",
|
|
126
|
+
"ERA001", # Commented out code
|
|
127
|
+
"F403",
|
|
128
|
+
"F405", # May be undefined from type imports
|
|
129
|
+
"FBT001", # Boolean-typed positional argument in function definition
|
|
130
|
+
"PGH003", # Use specific rule codes when ignoring type issues
|
|
131
|
+
"PLC0415", # Imports should be at top of file
|
|
132
|
+
"PLR0913",
|
|
133
|
+
"PLR2004",
|
|
134
|
+
"PLW0108", # Lambda may be unnecessary; consider inlining inner function
|
|
135
|
+
"S101",
|
|
136
|
+
"SLF001", # Calling private method
|
|
137
|
+
"W292", # No blank line at end of file - included b/c cursor struggles to add a trailing newline and burns through requests trying to fix this linting error.
|
|
138
|
+
] }
|
|
139
|
+
select = ["ALL"]
|
|
140
|
+
unfixable = [
|
|
141
|
+
"ERA001", # Commented out code
|
|
142
|
+
"F401", # unused-import
|
|
143
|
+
"F841", # unused-variable
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
[tool.ruff.lint.mccabe]
|
|
147
|
+
# Unlike Flake8, default to a complexity level of 10.
|
|
148
|
+
max-complexity = 10
|
|
149
|
+
|
|
150
|
+
[tool.ruff.lint.pydocstyle]
|
|
151
|
+
convention = "google"
|
|
152
|
+
|
|
153
|
+
[tool.ruff.lint.pylint]
|
|
154
|
+
max-args = 7
|
|
155
|
+
|
|
156
|
+
[tool.ruff.lint.flake8-annotations]
|
|
157
|
+
allow-star-arg-any = true
|
|
158
|
+
|
|
159
|
+
[tool.ruff.format]
|
|
160
|
+
indent-style = "space"
|
|
161
|
+
line-ending = "auto"
|
|
162
|
+
quote-style = "double"
|
|
163
|
+
skip-magic-trailing-comma = false
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""GX CLI - A wrapper around common git commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from typer import rich_utils
|
|
7
|
+
|
|
8
|
+
from gx import __version__
|
|
9
|
+
from gx.commands import clean, done, feat, pull, push, status
|
|
10
|
+
from gx.lib.console import set_verbosity
|
|
11
|
+
from gx.lib.git import check_git_installed, git
|
|
12
|
+
from gx.lib.options import VERBOSE_OPTION
|
|
13
|
+
|
|
14
|
+
rich_utils.STYLE_HELPTEXT = "" # ty:ignore[invalid-assignment]
|
|
15
|
+
CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
app = typer.Typer(rich_markup_mode="rich", context_settings=CONTEXT_SETTINGS)
|
|
19
|
+
app.add_typer(push.app, name="push")
|
|
20
|
+
app.add_typer(pull.app, name="pull")
|
|
21
|
+
app.add_typer(feat.app, name="feat")
|
|
22
|
+
app.add_typer(clean.app, name="clean")
|
|
23
|
+
app.add_typer(done.app, name="done")
|
|
24
|
+
app.add_typer(status.app, name="status")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _version_callback(value: bool) -> None: # noqa: FBT001
|
|
28
|
+
"""Print version and exit."""
|
|
29
|
+
if value:
|
|
30
|
+
typer.echo(f"gx {__version__}")
|
|
31
|
+
raise typer.Exit
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _is_git_repo() -> bool:
|
|
35
|
+
"""Return True if the current directory is inside a git repository."""
|
|
36
|
+
result = git("rev-parse", "--is-inside-work-tree")
|
|
37
|
+
return result.success
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@app.callback(invoke_without_command=True)
|
|
41
|
+
def callback(
|
|
42
|
+
ctx: typer.Context,
|
|
43
|
+
verbose: int = VERBOSE_OPTION,
|
|
44
|
+
_version: bool | None = typer.Option( # noqa: FBT001
|
|
45
|
+
None,
|
|
46
|
+
"--version",
|
|
47
|
+
"-V",
|
|
48
|
+
callback=_version_callback,
|
|
49
|
+
is_flag=True,
|
|
50
|
+
expose_value=False,
|
|
51
|
+
is_eager=True,
|
|
52
|
+
help="Show version and exit.",
|
|
53
|
+
),
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Streamline your git workflow.
|
|
56
|
+
|
|
57
|
+
GX wraps common git operations with sensible defaults, safety guards, and helpful summaries. Every command supports --dry-run and --verbose flags.
|
|
58
|
+
|
|
59
|
+
Run [bold]gx COMMAND -h[/bold] for details on a specific command.
|
|
60
|
+
|
|
61
|
+
[bold]Common workflows:[/bold]
|
|
62
|
+
|
|
63
|
+
Start a feature branch: gx feat
|
|
64
|
+
Push your work: gx push
|
|
65
|
+
Pull latest changes: gx pull
|
|
66
|
+
Clean stale branches: gx clean
|
|
67
|
+
Finish a merged PR: gx done
|
|
68
|
+
View status dashboard: gx status
|
|
69
|
+
|
|
70
|
+
Configure defaults in ~/.config/gx/config.toml
|
|
71
|
+
"""
|
|
72
|
+
set_verbosity(verbose)
|
|
73
|
+
check_git_installed()
|
|
74
|
+
if ctx.invoked_subcommand is None:
|
|
75
|
+
if _is_git_repo():
|
|
76
|
+
status_cmd = getattr(ctx.command, "commands", {}).get("status")
|
|
77
|
+
if status_cmd:
|
|
78
|
+
ctx.invoke(status_cmd)
|
|
79
|
+
else:
|
|
80
|
+
typer.echo(ctx.get_help())
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def main() -> None:
|
|
84
|
+
"""Entry point for the gx CLI."""
|
|
85
|
+
app()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Subcommand modules for gx."""
|