Metadata-Version: 2.5
Name: git-game
Version: 0.1.0
Summary: Learn git by playing: rules to satisfy on a real repository while a Gremlin breaks things
Project-URL: Repository, https://git-softeng.polito.it/s331551/git-edu
Project-URL: Issues, https://git-softeng.polito.it/s331551/git-edu/-/issues
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Classifier: Environment :: Console
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Education
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.12
Requires-Dist: textual>=0.80
Description-Content-Type: text/markdown

# 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

```sh
pipx install git-game        # or: uv tool install git-game
git-game
```

1. On your git server create a **new, empty, private** repository (without a README).
2. Start the game and paste the repository's SSH URL. The game clones it into `./play` (change it with `--play`).
3. 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`](https://github.com/newren/git-filter-repo) (`pipx install git-filter-repo`).

## Development setup

You need Python 3.12+, git and [uv](https://docs.astral.sh/uv/).

```sh
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](https://textual.textualize.io/)) |
| `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 `origin` is 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-pushes `main`.
- **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:

```python
Rule(title, text, hint, solution, check, trick=None, latch=False)
```

- `text`, `hint` and `solution` are Markdown, or functions `f(engine)` that return it (useful to show real commit hashes).
- `check(engine)` returns `None` when 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. Raise `Wait("...")` when the repository isn't in the state the trick needs yet.
- `latch=True` keeps 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

1. Open an issue first for a new level or a big change, so we can agree on it.
2. Create a branch, make your change, add or update the playthrough test.
3. Run `uv run pytest` and make sure it passes.
4. Open a merge request. The pipeline (`.gitlab-ci.yml`) runs the tests on every merge request and on `main`.

## Releasing

Releases go to [PyPI](https://pypi.org/p/git-game) through the CI pipeline (`.gitlab-ci.yml`):

```sh
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](LICENSE).
