gitgym 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gitgym-0.1.0/.gitignore +29 -0
- gitgym-0.1.0/LICENSE +21 -0
- gitgym-0.1.0/PKG-INFO +140 -0
- gitgym-0.1.0/README.md +110 -0
- gitgym-0.1.0/exercises/.gitkeep +0 -0
- gitgym-0.1.0/exercises/01_basics/01_init/exercise.toml +20 -0
- gitgym-0.1.0/exercises/01_basics/01_init/setup.sh +15 -0
- gitgym-0.1.0/exercises/01_basics/01_init/verify.sh +15 -0
- gitgym-0.1.0/exercises/01_basics/02_staging/exercise.toml +21 -0
- gitgym-0.1.0/exercises/01_basics/02_staging/setup.sh +18 -0
- gitgym-0.1.0/exercises/01_basics/02_staging/verify.sh +15 -0
- gitgym-0.1.0/exercises/01_basics/03_status/exercise.toml +28 -0
- gitgym-0.1.0/exercises/01_basics/03_status/setup.sh +27 -0
- gitgym-0.1.0/exercises/01_basics/03_status/verify.sh +35 -0
- gitgym-0.1.0/exercises/01_basics/04_first_commit/exercise.toml +22 -0
- gitgym-0.1.0/exercises/01_basics/04_first_commit/setup.sh +19 -0
- gitgym-0.1.0/exercises/01_basics/04_first_commit/verify.sh +24 -0
- gitgym-0.1.0/exercises/01_basics/05_gitignore/exercise.toml +30 -0
- gitgym-0.1.0/exercises/01_basics/05_gitignore/setup.sh +25 -0
- gitgym-0.1.0/exercises/01_basics/05_gitignore/verify.sh +35 -0
- gitgym-0.1.0/exercises/02_committing/01_amend/exercise.toml +22 -0
- gitgym-0.1.0/exercises/02_committing/01_amend/setup.sh +19 -0
- gitgym-0.1.0/exercises/02_committing/01_amend/verify.sh +28 -0
- gitgym-0.1.0/exercises/02_committing/02_multi_commit/exercise.toml +25 -0
- gitgym-0.1.0/exercises/02_committing/02_multi_commit/setup.sh +19 -0
- gitgym-0.1.0/exercises/02_committing/02_multi_commit/verify.sh +24 -0
- gitgym-0.1.0/exercises/02_committing/03_diff/exercise.toml +24 -0
- gitgym-0.1.0/exercises/02_committing/03_diff/setup.sh +29 -0
- gitgym-0.1.0/exercises/02_committing/03_diff/verify.sh +38 -0
- gitgym-0.1.0/exercises/03_branching/01_create_branch/exercise.toml +21 -0
- gitgym-0.1.0/exercises/03_branching/01_create_branch/setup.sh +20 -0
- gitgym-0.1.0/exercises/03_branching/01_create_branch/verify.sh +23 -0
- gitgym-0.1.0/exercises/03_branching/02_switch_branches/exercise.toml +21 -0
- gitgym-0.1.0/exercises/03_branching/02_switch_branches/setup.sh +23 -0
- gitgym-0.1.0/exercises/03_branching/02_switch_branches/verify.sh +16 -0
- gitgym-0.1.0/exercises/03_branching/03_delete_branch/exercise.toml +21 -0
- gitgym-0.1.0/exercises/03_branching/03_delete_branch/setup.sh +30 -0
- gitgym-0.1.0/exercises/03_branching/03_delete_branch/verify.sh +22 -0
- gitgym-0.1.0/exercises/04_merging/01_fast_forward/exercise.toml +25 -0
- gitgym-0.1.0/exercises/04_merging/01_fast_forward/setup.sh +33 -0
- gitgym-0.1.0/exercises/04_merging/01_fast_forward/verify.sh +42 -0
- gitgym-0.1.0/exercises/04_merging/02_three_way_merge/exercise.toml +24 -0
- gitgym-0.1.0/exercises/04_merging/02_three_way_merge/setup.sh +34 -0
- gitgym-0.1.0/exercises/04_merging/02_three_way_merge/verify.sh +39 -0
- gitgym-0.1.0/exercises/04_merging/03_merge_conflict/exercise.toml +25 -0
- gitgym-0.1.0/exercises/04_merging/03_merge_conflict/setup.sh +34 -0
- gitgym-0.1.0/exercises/04_merging/03_merge_conflict/verify.sh +46 -0
- gitgym-0.1.0/exercises/05_history/01_log_basics/exercise.toml +27 -0
- gitgym-0.1.0/exercises/05_history/01_log_basics/setup.sh +59 -0
- gitgym-0.1.0/exercises/05_history/01_log_basics/verify.sh +47 -0
- gitgym-0.1.0/exercises/05_history/02_log_graph/exercise.toml +28 -0
- gitgym-0.1.0/exercises/05_history/02_log_graph/setup.sh +44 -0
- gitgym-0.1.0/exercises/05_history/02_log_graph/verify.sh +35 -0
- gitgym-0.1.0/exercises/05_history/03_blame/exercise.toml +25 -0
- gitgym-0.1.0/exercises/05_history/03_blame/setup.sh +46 -0
- gitgym-0.1.0/exercises/05_history/03_blame/verify.sh +48 -0
- gitgym-0.1.0/exercises/05_history/04_show/exercise.toml +28 -0
- gitgym-0.1.0/exercises/05_history/04_show/setup.sh +35 -0
- gitgym-0.1.0/exercises/05_history/04_show/verify.sh +43 -0
- gitgym-0.1.0/exercises/06_undoing/01_restore_file/exercise.toml +27 -0
- gitgym-0.1.0/exercises/06_undoing/01_restore_file/setup.sh +32 -0
- gitgym-0.1.0/exercises/06_undoing/01_restore_file/verify.sh +33 -0
- gitgym-0.1.0/exercises/06_undoing/02_unstage/exercise.toml +24 -0
- gitgym-0.1.0/exercises/06_undoing/02_unstage/setup.sh +27 -0
- gitgym-0.1.0/exercises/06_undoing/02_unstage/verify.sh +30 -0
- gitgym-0.1.0/exercises/06_undoing/03_revert/exercise.toml +27 -0
- gitgym-0.1.0/exercises/06_undoing/03_revert/setup.sh +41 -0
- gitgym-0.1.0/exercises/06_undoing/03_revert/verify.sh +41 -0
- gitgym-0.1.0/exercises/06_undoing/04_reset_soft/exercise.toml +26 -0
- gitgym-0.1.0/exercises/06_undoing/04_reset_soft/setup.sh +28 -0
- gitgym-0.1.0/exercises/06_undoing/04_reset_soft/verify.sh +33 -0
- gitgym-0.1.0/exercises/06_undoing/05_reset_mixed/exercise.toml +27 -0
- gitgym-0.1.0/exercises/06_undoing/05_reset_mixed/setup.sh +32 -0
- gitgym-0.1.0/exercises/06_undoing/05_reset_mixed/verify.sh +33 -0
- gitgym-0.1.0/exercises/07_rebase/01_basic_rebase/exercise.toml +25 -0
- gitgym-0.1.0/exercises/07_rebase/01_basic_rebase/setup.sh +41 -0
- gitgym-0.1.0/exercises/07_rebase/01_basic_rebase/verify.sh +43 -0
- gitgym-0.1.0/exercises/07_rebase/02_interactive_rebase/exercise.toml +32 -0
- gitgym-0.1.0/exercises/07_rebase/02_interactive_rebase/setup.sh +37 -0
- gitgym-0.1.0/exercises/07_rebase/02_interactive_rebase/verify.sh +40 -0
- gitgym-0.1.0/exercises/07_rebase/03_rebase_conflict/exercise.toml +28 -0
- gitgym-0.1.0/exercises/07_rebase/03_rebase_conflict/setup.sh +35 -0
- gitgym-0.1.0/exercises/07_rebase/03_rebase_conflict/verify.sh +41 -0
- gitgym-0.1.0/exercises/08_stashing/01_stash_basics/exercise.toml +26 -0
- gitgym-0.1.0/exercises/08_stashing/01_stash_basics/setup.sh +33 -0
- gitgym-0.1.0/exercises/08_stashing/01_stash_basics/verify.sh +31 -0
- gitgym-0.1.0/exercises/08_stashing/02_stash_pop_apply/exercise.toml +29 -0
- gitgym-0.1.0/exercises/08_stashing/02_stash_pop_apply/setup.sh +28 -0
- gitgym-0.1.0/exercises/08_stashing/02_stash_pop_apply/verify.sh +33 -0
- gitgym-0.1.0/exercises/09_advanced/01_cherry_pick/exercise.toml +31 -0
- gitgym-0.1.0/exercises/09_advanced/01_cherry_pick/setup.sh +43 -0
- gitgym-0.1.0/exercises/09_advanced/01_cherry_pick/verify.sh +39 -0
- gitgym-0.1.0/exercises/09_advanced/02_bisect/exercise.toml +38 -0
- gitgym-0.1.0/exercises/09_advanced/02_bisect/setup.sh +73 -0
- gitgym-0.1.0/exercises/09_advanced/02_bisect/verify.sh +48 -0
- gitgym-0.1.0/exercises/09_advanced/03_tags/exercise.toml +31 -0
- gitgym-0.1.0/exercises/09_advanced/03_tags/setup.sh +30 -0
- gitgym-0.1.0/exercises/09_advanced/03_tags/verify.sh +65 -0
- gitgym-0.1.0/exercises/09_advanced/04_aliases/exercise.toml +33 -0
- gitgym-0.1.0/exercises/09_advanced/04_aliases/setup.sh +19 -0
- gitgym-0.1.0/exercises/09_advanced/04_aliases/verify.sh +27 -0
- gitgym-0.1.0/pyproject.toml +63 -0
- gitgym-0.1.0/src/gitgym/__init__.py +6 -0
- gitgym-0.1.0/src/gitgym/__main__.py +3 -0
- gitgym-0.1.0/src/gitgym/cli.py +441 -0
- gitgym-0.1.0/src/gitgym/config.py +9 -0
- gitgym-0.1.0/src/gitgym/display.py +95 -0
- gitgym-0.1.0/src/gitgym/exercise.py +45 -0
- gitgym-0.1.0/src/gitgym/progress.py +86 -0
- gitgym-0.1.0/src/gitgym/runner.py +105 -0
- gitgym-0.1.0/src/gitgym/watcher.py +188 -0
gitgym-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
.venv/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
|
|
9
|
+
# Node.js
|
|
10
|
+
node_modules/
|
|
11
|
+
dist/
|
|
12
|
+
*.tsbuildinfo
|
|
13
|
+
|
|
14
|
+
# Test coverage
|
|
15
|
+
coverage/
|
|
16
|
+
|
|
17
|
+
# Environment
|
|
18
|
+
.env
|
|
19
|
+
.env.local
|
|
20
|
+
|
|
21
|
+
# OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# Claude Code workflow
|
|
26
|
+
prompt.md
|
|
27
|
+
specs/
|
|
28
|
+
run_loop.sh
|
|
29
|
+
logs/
|
gitgym-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aaron Marquez
|
|
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.
|
gitgym-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gitgym
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An interactive CLI platform for learning git, inspired by Rustlings.
|
|
5
|
+
Project-URL: Homepage, https://github.com/enerrio/git-gym
|
|
6
|
+
Project-URL: Repository, https://github.com/enerrio/git-gym
|
|
7
|
+
Project-URL: Issues, https://github.com/enerrio/git-gym/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/enerrio/git-gym/blob/main/CHANGELOG.md
|
|
9
|
+
Author: enerrio
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cli,education,exercises,git,learning,tutorial
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Education
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: MacOS
|
|
19
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Education
|
|
24
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Requires-Dist: click
|
|
27
|
+
Provides-Extra: watch
|
|
28
|
+
Requires-Dist: watchdog; extra == 'watch'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# gitgym
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/gitgym/)
|
|
34
|
+
[](https://pypi.org/project/gitgym/)
|
|
35
|
+
[](https://github.com/enerrio/gitgym/actions/workflows/ci.yml)
|
|
36
|
+
|
|
37
|
+
An interactive CLI for learning git through hands-on exercises, inspired by [Rustlings](https://github.com/rust-lang/rustlings). No quizzes — you practice real git commands in real repositories inside a safe, sandboxed workspace.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install gitgym
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or with [uv](https://docs.astral.sh/uv/):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv tool install gitgym
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Requirements:** Python 3.12+ and git. macOS and Linux only (Windows is not currently supported).
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
gitgym list # see all 32 exercises
|
|
57
|
+
gitgym next # set up the next exercise
|
|
58
|
+
cd ~/.gitgym/exercises/01_basics/01_init # cd into the printed path
|
|
59
|
+
# ... run git commands to solve the exercise ...
|
|
60
|
+
gitgym verify # check your work
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Recommended Setup
|
|
64
|
+
|
|
65
|
+
Open **two terminal windows** (or tabs) side by side:
|
|
66
|
+
|
|
67
|
+
| Terminal 1 (work) | Terminal 2 (control) |
|
|
68
|
+
| --------------------------------------- | --------------------------------- |
|
|
69
|
+
| `cd` into the exercise directory | `gitgym describe` — read the goal |
|
|
70
|
+
| Run git commands to solve the exercise | `gitgym hint` — get a hint |
|
|
71
|
+
| Edit files, stage, commit, branch, etc. | `gitgym verify` — check your work |
|
|
72
|
+
| | `gitgym watch` — live feedback |
|
|
73
|
+
|
|
74
|
+
This keeps your git work separate from the gitgym CLI, just like a real workflow.
|
|
75
|
+
|
|
76
|
+
## Commands
|
|
77
|
+
|
|
78
|
+
| Command | Description |
|
|
79
|
+
| ------------------------- | ---------------------------------------------------------- |
|
|
80
|
+
| `gitgym list` | List all exercises grouped by topic with completion status |
|
|
81
|
+
| `gitgym start [exercise]` | Set up an exercise (defaults to next incomplete) |
|
|
82
|
+
| `gitgym next` | Alias for `gitgym start` with no argument |
|
|
83
|
+
| `gitgym describe` | Print the current exercise's description and goal |
|
|
84
|
+
| `gitgym verify` | Check if the current exercise's goal state is met |
|
|
85
|
+
| `gitgym watch` | Auto re-verify on changes (Ctrl+C to stop) |
|
|
86
|
+
| `gitgym hint` | Show the next progressive hint |
|
|
87
|
+
| `gitgym reset [exercise]` | Reset an exercise to its initial state |
|
|
88
|
+
| `gitgym reset --all` | Reset all exercises and clear progress |
|
|
89
|
+
| `gitgym progress` | Show overall progress summary |
|
|
90
|
+
| `gitgym clean` | Remove all gitgym data from your system |
|
|
91
|
+
|
|
92
|
+
Exercise names are shown in `gitgym list` (e.g. `init`, `staging`, `amend`). Use these names with `gitgym start` and `gitgym reset`.
|
|
93
|
+
|
|
94
|
+
## Exercises
|
|
95
|
+
|
|
96
|
+
32 exercises across 9 topics, from beginner to advanced:
|
|
97
|
+
|
|
98
|
+
| Topic | Exercises |
|
|
99
|
+
| ------------------- | ------------------------------------------------------ |
|
|
100
|
+
| **Basics** | init, staging, status, first_commit, gitignore |
|
|
101
|
+
| **Committing** | amend, multi_commit, diff |
|
|
102
|
+
| **Branching** | create_branch, switch_branches, delete_branch |
|
|
103
|
+
| **Merging** | fast_forward, three_way_merge, merge_conflict |
|
|
104
|
+
| **History** | log_basics, log_graph, blame, show |
|
|
105
|
+
| **Undoing Changes** | restore_file, unstage, revert, reset_soft, reset_mixed |
|
|
106
|
+
| **Rebase** | basic_rebase, interactive_rebase, rebase_conflict |
|
|
107
|
+
| **Stashing** | stash_basics, stash_pop_apply |
|
|
108
|
+
| **Advanced** | cherry_pick, bisect, tags, aliases |
|
|
109
|
+
|
|
110
|
+
## Features
|
|
111
|
+
|
|
112
|
+
**Progressive hints** — Each exercise has multiple hints revealed one at a time:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
$ gitgym hint
|
|
116
|
+
Hint 1/3: Look at the `git init` command.
|
|
117
|
+
|
|
118
|
+
$ gitgym hint
|
|
119
|
+
Hint 2/3: Run `git init` inside the exercise directory.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Watch mode** — `gitgym watch` polls the exercise directory and re-verifies automatically whenever you make changes. No need to switch terminals to run verify.
|
|
123
|
+
|
|
124
|
+
**Progress tracking** — Your progress is saved locally in `~/.gitgym/progress.json`. Close your terminal and pick up where you left off. Run `gitgym progress` to see a per-topic breakdown.
|
|
125
|
+
|
|
126
|
+
**Cleanup** — When you're done, run `gitgym clean` to remove all exercise data from your system.
|
|
127
|
+
|
|
128
|
+
## Platform Support
|
|
129
|
+
|
|
130
|
+
gitgym works on **macOS** and **Linux**. Windows is not currently supported because exercises use bash shell scripts. Windows users can use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) as a workaround.
|
|
131
|
+
|
|
132
|
+
## Contributing
|
|
133
|
+
|
|
134
|
+
Exercises follow a simple structure — each one is a directory with three files:
|
|
135
|
+
|
|
136
|
+
- `exercise.toml` — metadata, description, goal, and hints
|
|
137
|
+
- `setup.sh` — creates the initial repo state
|
|
138
|
+
- `verify.sh` — checks if the goal is met (exit 0 = pass)
|
|
139
|
+
|
|
140
|
+
See the `exercises/` directory for examples.
|
gitgym-0.1.0/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# gitgym
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/gitgym/)
|
|
4
|
+
[](https://pypi.org/project/gitgym/)
|
|
5
|
+
[](https://github.com/enerrio/gitgym/actions/workflows/ci.yml)
|
|
6
|
+
|
|
7
|
+
An interactive CLI for learning git through hands-on exercises, inspired by [Rustlings](https://github.com/rust-lang/rustlings). No quizzes — you practice real git commands in real repositories inside a safe, sandboxed workspace.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install gitgym
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or with [uv](https://docs.astral.sh/uv/):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv tool install gitgym
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Requirements:** Python 3.12+ and git. macOS and Linux only (Windows is not currently supported).
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
gitgym list # see all 32 exercises
|
|
27
|
+
gitgym next # set up the next exercise
|
|
28
|
+
cd ~/.gitgym/exercises/01_basics/01_init # cd into the printed path
|
|
29
|
+
# ... run git commands to solve the exercise ...
|
|
30
|
+
gitgym verify # check your work
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Recommended Setup
|
|
34
|
+
|
|
35
|
+
Open **two terminal windows** (or tabs) side by side:
|
|
36
|
+
|
|
37
|
+
| Terminal 1 (work) | Terminal 2 (control) |
|
|
38
|
+
| --------------------------------------- | --------------------------------- |
|
|
39
|
+
| `cd` into the exercise directory | `gitgym describe` — read the goal |
|
|
40
|
+
| Run git commands to solve the exercise | `gitgym hint` — get a hint |
|
|
41
|
+
| Edit files, stage, commit, branch, etc. | `gitgym verify` — check your work |
|
|
42
|
+
| | `gitgym watch` — live feedback |
|
|
43
|
+
|
|
44
|
+
This keeps your git work separate from the gitgym CLI, just like a real workflow.
|
|
45
|
+
|
|
46
|
+
## Commands
|
|
47
|
+
|
|
48
|
+
| Command | Description |
|
|
49
|
+
| ------------------------- | ---------------------------------------------------------- |
|
|
50
|
+
| `gitgym list` | List all exercises grouped by topic with completion status |
|
|
51
|
+
| `gitgym start [exercise]` | Set up an exercise (defaults to next incomplete) |
|
|
52
|
+
| `gitgym next` | Alias for `gitgym start` with no argument |
|
|
53
|
+
| `gitgym describe` | Print the current exercise's description and goal |
|
|
54
|
+
| `gitgym verify` | Check if the current exercise's goal state is met |
|
|
55
|
+
| `gitgym watch` | Auto re-verify on changes (Ctrl+C to stop) |
|
|
56
|
+
| `gitgym hint` | Show the next progressive hint |
|
|
57
|
+
| `gitgym reset [exercise]` | Reset an exercise to its initial state |
|
|
58
|
+
| `gitgym reset --all` | Reset all exercises and clear progress |
|
|
59
|
+
| `gitgym progress` | Show overall progress summary |
|
|
60
|
+
| `gitgym clean` | Remove all gitgym data from your system |
|
|
61
|
+
|
|
62
|
+
Exercise names are shown in `gitgym list` (e.g. `init`, `staging`, `amend`). Use these names with `gitgym start` and `gitgym reset`.
|
|
63
|
+
|
|
64
|
+
## Exercises
|
|
65
|
+
|
|
66
|
+
32 exercises across 9 topics, from beginner to advanced:
|
|
67
|
+
|
|
68
|
+
| Topic | Exercises |
|
|
69
|
+
| ------------------- | ------------------------------------------------------ |
|
|
70
|
+
| **Basics** | init, staging, status, first_commit, gitignore |
|
|
71
|
+
| **Committing** | amend, multi_commit, diff |
|
|
72
|
+
| **Branching** | create_branch, switch_branches, delete_branch |
|
|
73
|
+
| **Merging** | fast_forward, three_way_merge, merge_conflict |
|
|
74
|
+
| **History** | log_basics, log_graph, blame, show |
|
|
75
|
+
| **Undoing Changes** | restore_file, unstage, revert, reset_soft, reset_mixed |
|
|
76
|
+
| **Rebase** | basic_rebase, interactive_rebase, rebase_conflict |
|
|
77
|
+
| **Stashing** | stash_basics, stash_pop_apply |
|
|
78
|
+
| **Advanced** | cherry_pick, bisect, tags, aliases |
|
|
79
|
+
|
|
80
|
+
## Features
|
|
81
|
+
|
|
82
|
+
**Progressive hints** — Each exercise has multiple hints revealed one at a time:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
$ gitgym hint
|
|
86
|
+
Hint 1/3: Look at the `git init` command.
|
|
87
|
+
|
|
88
|
+
$ gitgym hint
|
|
89
|
+
Hint 2/3: Run `git init` inside the exercise directory.
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Watch mode** — `gitgym watch` polls the exercise directory and re-verifies automatically whenever you make changes. No need to switch terminals to run verify.
|
|
93
|
+
|
|
94
|
+
**Progress tracking** — Your progress is saved locally in `~/.gitgym/progress.json`. Close your terminal and pick up where you left off. Run `gitgym progress` to see a per-topic breakdown.
|
|
95
|
+
|
|
96
|
+
**Cleanup** — When you're done, run `gitgym clean` to remove all exercise data from your system.
|
|
97
|
+
|
|
98
|
+
## Platform Support
|
|
99
|
+
|
|
100
|
+
gitgym works on **macOS** and **Linux**. Windows is not currently supported because exercises use bash shell scripts. Windows users can use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) as a workaround.
|
|
101
|
+
|
|
102
|
+
## Contributing
|
|
103
|
+
|
|
104
|
+
Exercises follow a simple structure — each one is a directory with three files:
|
|
105
|
+
|
|
106
|
+
- `exercise.toml` — metadata, description, goal, and hints
|
|
107
|
+
- `setup.sh` — creates the initial repo state
|
|
108
|
+
- `verify.sh` — checks if the goal is met (exit 0 = pass)
|
|
109
|
+
|
|
110
|
+
See the `exercises/` directory for examples.
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[exercise]
|
|
2
|
+
name = "init"
|
|
3
|
+
topic = "Basics"
|
|
4
|
+
title = "Initialize a Repository"
|
|
5
|
+
description = """
|
|
6
|
+
Every git journey starts with `git init`. In this exercise, you'll \
|
|
7
|
+
initialize a new git repository in the provided directory.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
[goal]
|
|
11
|
+
summary = "The directory should be a valid git repository."
|
|
12
|
+
|
|
13
|
+
[[hints]]
|
|
14
|
+
text = "Look at the `git init` command."
|
|
15
|
+
|
|
16
|
+
[[hints]]
|
|
17
|
+
text = "Run `git init` inside the exercise directory."
|
|
18
|
+
|
|
19
|
+
[[hints]]
|
|
20
|
+
text = "Just run: git init"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
mkdir -p "$EXERCISE_DIR"
|
|
6
|
+
cd "$EXERCISE_DIR"
|
|
7
|
+
|
|
8
|
+
# Remove any existing git repo so the exercise is in the expected initial state
|
|
9
|
+
# (idempotent: running setup.sh a second time resets back to pre-init state)
|
|
10
|
+
if [ -d ".git" ]; then
|
|
11
|
+
rm -rf .git
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
# Create a file for the user to work with
|
|
15
|
+
echo "Hello, git!" >hello.txt
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
cd "$EXERCISE_DIR"
|
|
6
|
+
|
|
7
|
+
# Check that it's a git repo
|
|
8
|
+
if [ ! -d ".git" ]; then
|
|
9
|
+
echo "This directory is not a git repository yet."
|
|
10
|
+
echo "Use 'git init' to initialize it."
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
echo "Great job! You initialized a git repository."
|
|
15
|
+
exit 0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[exercise]
|
|
2
|
+
name = "staging"
|
|
3
|
+
topic = "Basics"
|
|
4
|
+
title = "Stage a File"
|
|
5
|
+
description = """
|
|
6
|
+
Before git can track a file, you need to stage it with `git add`. \
|
|
7
|
+
In this exercise, a file called `hello.txt` exists in the repo but \
|
|
8
|
+
has not been staged yet. Stage it so git will include it in the next commit.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
[goal]
|
|
12
|
+
summary = "hello.txt should be staged (in the index)."
|
|
13
|
+
|
|
14
|
+
[[hints]]
|
|
15
|
+
text = "Use `git status` to see the current state of the repository."
|
|
16
|
+
|
|
17
|
+
[[hints]]
|
|
18
|
+
text = "Look at the `git add` command."
|
|
19
|
+
|
|
20
|
+
[[hints]]
|
|
21
|
+
text = "Run `git add hello.txt` to stage the file."
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
mkdir -p "$EXERCISE_DIR"
|
|
6
|
+
cd "$EXERCISE_DIR"
|
|
7
|
+
|
|
8
|
+
# Initialize a fresh git repo (idempotent: remove any existing one first)
|
|
9
|
+
if [ -d ".git" ]; then
|
|
10
|
+
rm -rf .git
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
git init --initial-branch=main
|
|
14
|
+
git config user.email "gitgym@example.com"
|
|
15
|
+
git config user.name "Git Gym"
|
|
16
|
+
|
|
17
|
+
# Create an untracked file for the user to stage
|
|
18
|
+
echo "Hello, git!" >hello.txt
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
cd "$EXERCISE_DIR"
|
|
6
|
+
|
|
7
|
+
# Check that hello.txt is staged (present in the index)
|
|
8
|
+
if ! git diff --cached --name-only | grep -qx "hello.txt"; then
|
|
9
|
+
echo "hello.txt is not staged yet."
|
|
10
|
+
echo "Use 'git add hello.txt' to stage it."
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
echo "Great job! hello.txt is staged and ready to commit."
|
|
15
|
+
exit 0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[exercise]
|
|
2
|
+
name = "status"
|
|
3
|
+
topic = "Basics"
|
|
4
|
+
title = "Interpret git status"
|
|
5
|
+
description = """
|
|
6
|
+
`git status` shows you exactly what is staged, what is modified, and what \
|
|
7
|
+
is untracked. In this exercise the repo already has some files committed. \
|
|
8
|
+
Three new files have been added:
|
|
9
|
+
|
|
10
|
+
- `feature.txt` — a new file that should be staged
|
|
11
|
+
- `notes.txt` — a new file that should NOT be staged
|
|
12
|
+
- `readme.txt` — a previously committed file that has been modified
|
|
13
|
+
|
|
14
|
+
Your task: stage `feature.txt` and the modified `readme.txt`, but leave \
|
|
15
|
+
`notes.txt` untracked.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
[goal]
|
|
19
|
+
summary = "feature.txt and the modified readme.txt are staged; notes.txt is not."
|
|
20
|
+
|
|
21
|
+
[[hints]]
|
|
22
|
+
text = "Run `git status` to see the current state of all files."
|
|
23
|
+
|
|
24
|
+
[[hints]]
|
|
25
|
+
text = "Use `git add <file>` to stage a specific file."
|
|
26
|
+
|
|
27
|
+
[[hints]]
|
|
28
|
+
text = "Stage both files: `git add feature.txt readme.txt`"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
mkdir -p "$EXERCISE_DIR"
|
|
6
|
+
cd "$EXERCISE_DIR"
|
|
7
|
+
|
|
8
|
+
# Start fresh (idempotent)
|
|
9
|
+
if [ -d ".git" ]; then
|
|
10
|
+
rm -rf .git
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
git init --initial-branch=main
|
|
14
|
+
git config user.email "gitgym@example.com"
|
|
15
|
+
git config user.name "Git Gym"
|
|
16
|
+
|
|
17
|
+
# Create and commit an initial file so readme.txt has a committed version
|
|
18
|
+
echo "# My Project" >readme.txt
|
|
19
|
+
git add readme.txt
|
|
20
|
+
git commit -m "Initial commit"
|
|
21
|
+
|
|
22
|
+
# Modify the committed file (now it appears as "modified" in git status)
|
|
23
|
+
echo "# My Project (updated)" >readme.txt
|
|
24
|
+
|
|
25
|
+
# Add two new untracked files — user must stage only feature.txt
|
|
26
|
+
echo "New feature content" >feature.txt
|
|
27
|
+
echo "Private notes" >notes.txt
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
cd "$EXERCISE_DIR"
|
|
6
|
+
|
|
7
|
+
STAGED=$(git diff --cached --name-only)
|
|
8
|
+
PASS=1
|
|
9
|
+
|
|
10
|
+
# feature.txt must be staged
|
|
11
|
+
if ! echo "$STAGED" | grep -qx "feature.txt"; then
|
|
12
|
+
echo "feature.txt is not staged yet."
|
|
13
|
+
echo "Use 'git add feature.txt' to stage it."
|
|
14
|
+
PASS=0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# readme.txt (modified) must be staged
|
|
18
|
+
if ! echo "$STAGED" | grep -qx "readme.txt"; then
|
|
19
|
+
echo "readme.txt is not staged yet."
|
|
20
|
+
echo "Use 'git add readme.txt' to stage the modified version."
|
|
21
|
+
PASS=0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# notes.txt must NOT be staged
|
|
25
|
+
if echo "$STAGED" | grep -qx "notes.txt"; then
|
|
26
|
+
echo "notes.txt should not be staged — leave it untracked."
|
|
27
|
+
PASS=0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [ "$PASS" -eq 0 ]; then
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo "Great job! You staged the right files and left notes.txt untracked."
|
|
35
|
+
exit 0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[exercise]
|
|
2
|
+
name = "first_commit"
|
|
3
|
+
topic = "Basics"
|
|
4
|
+
title = "Create Your First Commit"
|
|
5
|
+
description = """
|
|
6
|
+
Staging a file is only half the story — you still need to save those \
|
|
7
|
+
changes permanently with `git commit`. In this exercise, `hello.txt` is \
|
|
8
|
+
already staged and ready to go. Create a commit with a meaningful message \
|
|
9
|
+
to record the snapshot.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
[goal]
|
|
13
|
+
summary = "The repo has at least one commit with a non-empty message."
|
|
14
|
+
|
|
15
|
+
[[hints]]
|
|
16
|
+
text = "Use `git log` to see the commit history (it will be empty until you commit)."
|
|
17
|
+
|
|
18
|
+
[[hints]]
|
|
19
|
+
text = "Look at the `git commit` command."
|
|
20
|
+
|
|
21
|
+
[[hints]]
|
|
22
|
+
text = "Run `git commit -m \"Your message here\"` to create the commit."
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
mkdir -p "$EXERCISE_DIR"
|
|
6
|
+
cd "$EXERCISE_DIR"
|
|
7
|
+
|
|
8
|
+
# Start fresh (idempotent)
|
|
9
|
+
if [ -d ".git" ]; then
|
|
10
|
+
rm -rf .git
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
git init --initial-branch=main
|
|
14
|
+
git config user.email "gitgym@example.com"
|
|
15
|
+
git config user.name "Git Gym"
|
|
16
|
+
|
|
17
|
+
# Stage a file so the user only needs to commit
|
|
18
|
+
echo "Hello, git!" >hello.txt
|
|
19
|
+
git add hello.txt
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
cd "$EXERCISE_DIR"
|
|
6
|
+
|
|
7
|
+
# Check there is at least one commit
|
|
8
|
+
COMMIT_COUNT=$(git rev-list --count HEAD 2>/dev/null || echo 0)
|
|
9
|
+
if [ "$COMMIT_COUNT" -eq 0 ]; then
|
|
10
|
+
echo "No commits found yet."
|
|
11
|
+
echo "Use 'git commit -m \"your message\"' to create your first commit."
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Check the commit message is non-empty
|
|
16
|
+
COMMIT_MSG=$(git log -1 --format="%s")
|
|
17
|
+
if [ -z "$COMMIT_MSG" ]; then
|
|
18
|
+
echo "Your commit has an empty message."
|
|
19
|
+
echo "A good commit message describes what changed and why."
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
echo "Great job! You made your first commit: \"$COMMIT_MSG\""
|
|
24
|
+
exit 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[exercise]
|
|
2
|
+
name = "gitignore"
|
|
3
|
+
topic = "Basics"
|
|
4
|
+
title = "Ignore Files with .gitignore"
|
|
5
|
+
description = """
|
|
6
|
+
Not every file belongs in version control. Build artifacts, log files, \
|
|
7
|
+
and editor configs should usually be excluded. Git reads a `.gitignore` \
|
|
8
|
+
file to know which files to skip.
|
|
9
|
+
|
|
10
|
+
In this exercise, the repo contains two files that should never be \
|
|
11
|
+
committed:
|
|
12
|
+
|
|
13
|
+
- `build.log` — a log file generated during builds
|
|
14
|
+
- `secret.key` — a sensitive file that must not be tracked
|
|
15
|
+
|
|
16
|
+
Create a `.gitignore` that excludes both of these files. The rest of \
|
|
17
|
+
the repo should remain unaffected.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
[goal]
|
|
21
|
+
summary = ".gitignore exists and causes build.log and secret.key to be ignored by git."
|
|
22
|
+
|
|
23
|
+
[[hints]]
|
|
24
|
+
text = "Create a file named `.gitignore` in the exercise directory."
|
|
25
|
+
|
|
26
|
+
[[hints]]
|
|
27
|
+
text = "Each line in `.gitignore` is a pattern. Add `build.log` and `secret.key` on separate lines."
|
|
28
|
+
|
|
29
|
+
[[hints]]
|
|
30
|
+
text = "Run `git status` after creating .gitignore — the two files should no longer appear as untracked."
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
mkdir -p "$EXERCISE_DIR"
|
|
6
|
+
cd "$EXERCISE_DIR"
|
|
7
|
+
|
|
8
|
+
# Start fresh (idempotent)
|
|
9
|
+
if [ -d ".git" ]; then
|
|
10
|
+
rm -rf .git
|
|
11
|
+
fi
|
|
12
|
+
rm -f .gitignore
|
|
13
|
+
|
|
14
|
+
git init --initial-branch=main
|
|
15
|
+
git config user.email "gitgym@example.com"
|
|
16
|
+
git config user.name "Git Gym"
|
|
17
|
+
|
|
18
|
+
# Commit a normal source file to give the repo some history
|
|
19
|
+
echo "print('hello')" >main.py
|
|
20
|
+
git add main.py
|
|
21
|
+
git commit -m "Initial commit"
|
|
22
|
+
|
|
23
|
+
# Create files that the user should ignore (do NOT stage or commit them)
|
|
24
|
+
echo "build output log" >build.log
|
|
25
|
+
echo "super-secret-value" >secret.key
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXERCISE_DIR="$1"
|
|
5
|
+
cd "$EXERCISE_DIR"
|
|
6
|
+
|
|
7
|
+
PASS=1
|
|
8
|
+
|
|
9
|
+
# .gitignore must exist
|
|
10
|
+
if [ ! -f ".gitignore" ]; then
|
|
11
|
+
echo ".gitignore does not exist yet."
|
|
12
|
+
echo "Create a .gitignore file in the exercise directory."
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# build.log must be ignored
|
|
17
|
+
if ! git check-ignore -q build.log 2>/dev/null; then
|
|
18
|
+
echo "build.log is not being ignored by git."
|
|
19
|
+
echo "Add 'build.log' to your .gitignore."
|
|
20
|
+
PASS=0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# secret.key must be ignored
|
|
24
|
+
if ! git check-ignore -q secret.key 2>/dev/null; then
|
|
25
|
+
echo "secret.key is not being ignored by git."
|
|
26
|
+
echo "Add 'secret.key' to your .gitignore."
|
|
27
|
+
PASS=0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [ "$PASS" -eq 0 ]; then
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo "Great job! Your .gitignore correctly excludes build.log and secret.key."
|
|
35
|
+
exit 0
|