git-game
Learn git by playing. You work in your own terminal with real git commands on a real remote repository, while the game shows the rules of the current level next to it. Rules appear one at a time and are re-checked every second, so a green rule can turn red again. A Gremlin works on the same repository and breaks things along the way: you fix them with git.
| Level | You learn | The Gremlin... |
|---|---|---|
| 0 · Foundations | branch, commit, push, merge requests, conflicts | just watches |
| 1 · Cherry-pick | cherry-pick, ranges, reset | commits R8 on your R6 branch, moves your branch after the push, publishes docs with a debug print hidden among them |
| 2 · Interactive rebase | reword, fixup, drop, autosquash, force-with-lease | "implements" R9 in 6 messy commits, adds a fixup!, pushes on your branch right before your force-push |
| 3 · Reflog | reflog, ORIG_HEAD | runs reset --hard, deletes a branch, drops a commit with a rebase |
| 4 · The leak | rotation, git filter-repo, cleaning remote refs |
spreads a token across files, messages, a tag and a backup branch |
Contributions are welcome: new levels, new tricks for the Gremlin, better hints, bug fixes.
Play
pipx install git-game # or: uv tool install git-game
git-game
- On your git server create a new, empty, private repository (without a README).
- Start the game and paste the repository's SSH URL. The game clones it into
./play(change it with--play). - Keep the game open next to a normal terminal and follow the rules.
| Key | Action |
|---|---|
h |
hint for the newest red rule; inside the hint, hold s to reveal the solution |
b |
the level's board: where you are, what's done, where the Gremlin strikes |
t |
switch the history panel between your clone and origin |
p |
panic: undoes the Gremlin's last trick on your local branches |
q |
quit; your progress is saved in ~/.gitgame |
git-game --new forgets the saved game and starts over. Level 4 needs git filter-repo (pipx install git-filter-repo).
Development setup
You need Python 3.12+, git and uv.
git clone https://git-softeng.polito.it/s331551/git-edu.git
cd git-edu
uv sync # creates .venv with the game and the dev tools (pytest, git-filter-repo)
uv run git-game # play your working copy
uv run pytest # run the tests
Project layout
| Path | What it holds |
|---|---|
gitgame/levels.py |
the levels: every rule with its text, hint, solution, check and Gremlin trick |
gitgame/engine.py |
the game state, the loop that runs checks and tricks, the git hooks installed in the player's clone |
gitgame/git.py |
small helpers around the git command line |
gitgame/app.py |
the terminal UI (Textual) |
gitgame/board.py |
the level board and the track drawn with text |
gitgame/baseline/ |
the starting code the game pushes to the player's empty repository |
tests/test_playthrough.py |
plays the whole game against a local bare repository |
tests/test_app.py |
UI tests |
How the game works
- Watching the player's clone. Every second the engine reads the clone. It also installs hooks in
.git/hooks(post-commit, post-checkout, post-merge, post-rewrite, pre-push) so it reacts right away and the Gremlin's messages show up in the player's terminal. It never overwrites the player's own hooks. - Reading the remote. The game keeps a private clone in
~/.gitgame/gremlin-clone, used to read the remote and to push the Gremlin's commits with the player's SSH key. - Safety. The Gremlin acts only on the clone whose
originis the game's repository. It waits while the working tree has uncommitted changes or an operation is in progress, saves the position of the branches before every trick, and never force-pushesmain. - Secrets. Level 4 never asks for a password: it asks for a throw-away access token, or a fake one.
Adding or changing a rule
A level is a Level(name, intro, rules) in gitgame/levels.py, and LEVELS lists them in order. Each rule is:
Rule(title, text, hint, solution, check, trick=None, latch=False)
text,hintandsolutionare Markdown, or functionsf(engine)that return it (useful to show real commit hashes).check(engine)returnsNonewhen the rule is satisfied, otherwise a short message telling the player what is still missing.trick(engine)is the Gremlin's move, run once when the rule unlocks. It returns the message shown to the player. RaiseWait("...")when the repository isn't in the state the trick needs yet.latch=Truekeeps the rule green once it has been satisfied, for rules about recovering something that later rules change again.
Store what a trick creates (hashes, names) in engine.data and call engine.save(), so checks and hints can find it after a restart.
Every rule and every trick must be covered by tests/test_playthrough.py: the scripted player has to turn the rule red with the trick and green again with the solution you give in solution.
Contributing
- Open an issue first for a new level or a big change, so we can agree on it.
- Create a branch, make your change, add or update the playthrough test.
- Run
uv run pytestand make sure it passes. - Open a merge request. The pipeline (
.gitlab-ci.yml) runs the tests on every merge request and onmain.
Releasing
Releases go to PyPI through the CI pipeline (.gitlab-ci.yml):
uv version --bump minor # or patch / major: updates pyproject.toml
git commit -am "Release v$(uv version --short)"
git tag "v$(uv version --short)"
git push && git push --tags
Pushing a v* tag runs the tests, checks that the tag matches the version in pyproject.toml, builds the package and publishes it with the PyPI API token stored in the PYPI_TOKEN CI/CD variable (masked and protected, only maintainers can see it).
License
Released under the GNU General Public License v3.0 or later.
Metadata
Release files for git-game 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| git_game-0.1.0.tar.gz | 53.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| git_game-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 107.6 kB
Release files / git_game-0.1.0.tar.gz
| Download URL | git_game-0.1.0.tar.gz |
|---|---|
| Size | 53.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
12070871290eb00e5b2e1fb3b6e627e4b80650fec8121bb65da11c591d6e9eaf
|
|
BLAKE2b-256 checksum How to use checksums |
8158fb51b428692b1441b710cc1f4b19a876f36fbbbd886199e70d40a4dfecd3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / git_game-0.1.0-py3-none-any.whl
| Download URL | git_game-0.1.0-py3-none-any.whl |
|---|---|
| Size | 53.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d23e813f8d5fae6f6f55f4584c5edbb798869a3bf32d4d626ef33f3662bc81c0
|
|
BLAKE2b-256 checksum How to use checksums |
c1835f6cf2ea5082357829f7c4ebb5330eb5f8191170268acb3804c20ce8d150
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|