# LevelsCompare

Comparison source: `https://github.com/Gazler/githug/` at `a613718` (`release: 0.5.1`, 2024-08-11).

Android source compared from `app/src/main/java/solutions/tretter/githugandroid/levels/`.

This file records the upstream Ruby setup and validation intent beside the Android Kotlin setup and validation. The Android port uses a real native Git sandbox for runtime state, but it exposes validation through `RepoState`, so some checks are expressed as equivalent state predicates rather than direct Ruby/Grit calls.

## Setup Summary

| Level | Upstream setup intent | Android setup intent | Parity / reason for difference |
| --- | --- | --- | --- |
| `init` | Starts outside a Git repository. | Starts with empty `RepoState`. | Equivalent. |
| `config` | Initializes a repository. | Initializes a repository. | Equivalent. |
| `add` | Initializes repo, creates untracked `README`, renames branch to `master`. | Initializes repo with untracked `README` on `master`. | Equivalent. |
| `commit` | Initializes repo, creates and stages `README`. | Initializes repo with staged `README`. | Equivalent. |
| `clone` | No local repo setup. | Empty state. | Equivalent; validation is command/network simplified. |
| `clone_to_folder` | No local repo setup. | Empty state. | Equivalent; validation is command/network simplified. |
| `ignore` | Initializes repo, creates `README.swp`, clears global excludes in `.git/config`. | Initializes repo with `.gitignore` available for editing. | Differs slightly: Android does not need a swap file fixture because validation checks `.gitignore` content directly. |
| `include` | Initializes repo, creates `first.a`, `second.a`, `lib.a`, and clears excludes. | Initializes repo with `.gitignore`, `lib.a`, and `main.a`. | Same ignore-pattern lesson; Android uses a smaller fixture. |
| `status` | Initializes repo; stages `config.rb`, `README`, `setup.rb`, `deploy.rb`, `Guardfile`; leaves `database.yml` untracked. | Native setup now creates/stages the same five files and leaves `database.yml` untracked. | Equivalent. |
| `number_of_files_committed` | Initializes repo with committed modified files plus staged/untracked files, leaving exactly two files staged for commit. | Initializes equivalent file states with two staged files. | Equivalent. |
| `rm` | Initializes repo, commits `deleteme.rb`, then deletes it from the working tree. | Initializes repo with tracked deleted `deleteme.rb`. | Equivalent. |
| `rm_cached` | Initializes repo, stages `.gitignore` and `deleteme.rb`. | Initializes repo with both files staged. | Equivalent. |
| `stash` | Copies upstream fixture containing committed `lyrics.txt` plus local modifications. | Initializes repo with tracked modified `lyrics.txt` and matching native fixture. | Equivalent. |
| `rename` | Initializes repo, commits `oldfile.txt`. | Initializes repo with tracked `oldfile.txt` and commit. | Equivalent. |
| `restructure` | Initializes repo with tracked `about.html`, `contact.html`, `index.html`. | Same tracked HTML files. | Equivalent. |
| `log` | Initializes repo with one commit whose message identifies the target. | Same one modeled commit. | Equivalent. |
| `tag` | Initializes repo with one commit. | Initializes repo with one tracked file and commit. | Equivalent. |
| `push_tags` | Creates local and remote repos, tag `tag_to_be_pushed`, and origin remote. | Native setup creates local/remote repo and tag; model records tag and remote. | Equivalent state projection. |
| `commit_amend` | Commits `README`, then creates untracked `forgotten_file.rb`. | Same committed `README` plus untracked `forgotten_file.rb`. | Equivalent. |
| `commit_in_future` | Initializes repo with staged `README`. | Initializes repo with staged `README`. | Equivalent setup. |
| `reset` | Commits `README`; stages `to_commit_first.rb` and `to_commit_second.rb`. | Same committed `README` plus both staged files. | Equivalent. |
| `reset_soft` | Commits `README`, then commits `newfile.rb`. | Same two commits and tracked files. | Equivalent. |
| `checkout_file` | Commits initial `config.rb`, then modifies it. | Same tracked modified `config.rb`. | Equivalent. |
| `remote` | Initializes repo and adds `my_remote_repo` remote. | Same remote in initialized repo. | Equivalent. |
| `remote_url` | Initializes repo and adds `my_remote_repo` and `remote_location`. | Same two remotes. | Equivalent. |
| `pull` | Initializes repo and adds remote URL `https://github.com/pull-this/thing-to-pull`. | Native setup creates local synthetic remote with a remote commit. | Deliberate offline equivalent to upstream network remote. |
| `remote_add` | Initializes repo with no origin remote. | Same. | Equivalent. |
| `push` | Creates divergent local and remote master histories: local has `file3`, remote has `file4`. | Native setup creates synthetic divergent origin with the same file/commit shape. | Equivalent. |
| `diff` | Copies upstream fixture with committed `app.rb` and one changed line. | Native setup creates committed then modified `app.rb`. | Equivalent. |
| `blame` | Copies upstream fixture where password line is authored by Spider Man. | Native setup creates deterministic author history for `config.rb`. | Equivalent. |
| `branch` | Initializes repo with one `README` commit. | Same. | Equivalent. |
| `checkout` | Initializes repo with one `README` commit. | Same. | Equivalent. |
| `checkout_tag` | Creates five commits and tags `v1.0`, `v1.2`, `v1.5`. | Same commit/tag shape. | Equivalent. |
| `checkout_tag_over_branch` | Creates same tags and also a branch named `v1.2`. | Same tag-plus-branch ambiguity. | Equivalent. |
| `branch_at` | Creates three commits on `file1`. | Same three modeled commits and native setup. | Equivalent. |
| `delete_branch` | Copies fixture with `delete_me` branch. | Initializes repo with `delete_me` branch. | Equivalent. |
| `push_branch` | Creates local and remote repos with multiple branches and target `test_branch`. | Native setup creates synthetic remote and same branch intent. | Equivalent state projection. |
| `merge` | Copies fixture with `file1` on master and `file2` on feature. | Native setup creates master `file1` and feature `file2`. | Equivalent. |
| `fetch` | Creates local master tracking origin/master; remote later gains `new_branch` with `file1`. | Native setup creates the same synthetic remote branch scenario; model starts with one fetched head. | Equivalent after latest update. |
| `rebase` | Copies fixture with divergent `master` and `feature`. | Native setup creates matching divergent `master` and `feature`. | Equivalent. |
| `rebase_onto` | Creates `authors.md`, bad `wrong_branch`, and `readme-update` branch needing `--onto`. | Native setup creates the same branch graph and files. | Equivalent setup. |
| `repack` | Initializes repo with one committed `foo`. | Same. | Equivalent. |
| `cherry-pick` | Copies fixture with `master` and `new-feature` commits. | Native setup creates equivalent branches/files. | Equivalent. |
| `grep` | Copies fixture with four TODO entries. | Initializes `app.rb` with four TODO entries. | Equivalent. |
| `rename_commit` | Creates commits `Initial commit`, `First coommit`, `Second commit`. | Same typo commit sequence. | Equivalent. |
| `squash` | Creates initial hidden commit plus README and three squash-target README updates. | Same commit sequence. | Equivalent. |
| `merge_squash` | Creates master and `long-feature-branch` with multiple feature changes. | Native setup creates the same branch and `file3` feature effect. | Mostly equivalent; Android currently validates less detail. |
| `reorder` | Creates commits `Initial Setup`, `First commit`, `Third commit`, `Second commit`. | Same order. | Equivalent. |
| `bisect` | Copies upstream bisect fixture. | Initializes modeled bad hash `18ed2ac`. | Simplified setup; Android validates the intended answer directly. |
| `stage_lines` | Commits initial `feature.rb`, then leaves two unstaged feature lines. | Initializes tracked `feature.rb` containing both feature lines. | Setup is close, but Android does not yet model partial staged vs unstaged hunks. |
| `find_old_branch` | Copies fixture with recoverable `solve_world_hunger` branch. | Initializes branch map with `solve_world_hunger`. | Equivalent for visible exercise state. |
| `revert` | Creates commits `First commit`, `Bad commit`, `Second commit`. | Same commit messages. | Equivalent. |
| `restore` | Creates `file1`, `file2`, then creates and removes `file3` so it is recoverable from reflog/history. | Native setup creates matching history and removes `file3`; model starts without `file3`. | Equivalent. |
| `conflict` | Copies fixture with `master` and `mybranch` conflict in `poem.txt`. | Initializes equivalent branch/file conflict fixture. | Equivalent setup. |
| `submodule` | Initializes empty repo. | Same. | Equivalent; network submodule operation is modeled. |
| `contribute` | No local setup; solution clones upstream externally. | Empty state. | Deliberate simplification for mobile/offline final prompt. |

## Validation Summary

| Level | Upstream validation intent | Android validation intent | Parity / reason for difference |
| --- | --- | --- | --- |
| `init` | `repo.valid?` | `RepoState.initialized` | Equivalent. |
| `config` | Prompted name/email must match `user.name` and `user.email`. | Validates nonblank `user.name` and `user.email`. | Deliberate UI difference: Android validates repository config state, not repeated prompt text. |
| `add` | `README` exists in index and is not untracked. | `README` is staged. | Equivalent. |
| `commit` | Repository has at least one commit. | Commit exists and `README` became tracked/unstaged. | Stricter but same lesson outcome. |
| `clone` | `cloneme` repo contains known commit `157b2b6...`. | Accepts exact clone command. | Deliberate mobile/network simplification; no external GitHub clone is performed in-game. |
| `clone_to_folder` | `my_cloned_repo` contains known commit. | Accepts exact clone-to-folder command. | Deliberate mobile/network simplification. |
| `ignore` | `.gitignore` contains `*.swp`. | `.gitignore` has a line equal to `*.swp`. | Equivalent. |
| `include` | `.gitignore` contains `*.a` and `!lib.a`. | Same two entries are present. | Equivalent. |
| `status` | User answers `database.yml`; setup has five staged files and one untracked file. | User answers `database.yml`; setup now mirrors staged files plus untracked `database.yml`. | Updated for parity. |
| `number_of_files_committed` | Prompt answer is integer `2`. | Answer is `2`. | Equivalent. |
| `rm` | `deleteme.rb` no longer has an unstaged deletion. | File removed from model or deletion staged. | Equivalent. |
| `rm_cached` | `deleteme.rb` is not staged and still exists. | `deleteme.rb` is unstaged, untracked, and not deleted. | Equivalent. |
| `stash` | `stash@{0}` exists and `lyrics.txt` has no working-tree changes. | At least one stash and no staged files. | Slightly looser; Android does not model unstaged tracked dirt separately after stash. |
| `rename` | `oldfile.txt` deleted unstaged and `newfile.txt` added. | `newfile.txt` exists and live `oldfile.txt` is gone. | Equivalent for accepted solutions. |
| `restructure` | HTML files are deleted at root and added under `src/`. | All three `src/*.html` files exist and no live root HTML files remain. | Equivalent. |
| `log` | Prompt answer matches latest commit hash prefix. | Answer matches the modeled commit hash. | Equivalent within Android's deterministic commit model. |
| `tag` | First tag is `new_tag`. | `new_tag` exists. | Equivalent. |
| `push_tags` | Remote tag list contains `tag_to_be_pushed`. | `tag_to_be_pushed` is in `pushedTags`. | Equivalent state projection. |
| `commit_amend` | One commit and amended commit contains two files. | One commit and `forgotten_file.rb` is tracked. | Equivalent. |
| `commit_in_future` | Commit authored date is in the future. | Any commit exists. | Known gap: Android does not currently expose commit authored timestamps in `RepoState`. |
| `reset` | `to_commit_second.rb` exists but is unstaged; `to_commit_first.rb` remains staged. | Same staged/unstaged split with one commit. | Equivalent. |
| `reset_soft` | `newfile.rb` exists, is staged, and commit count is one. | Same. | Equivalent. |
| `checkout_file` | `config.rb` no longer modified and commit count remains one. | `config.rb` content equals initial content. | Equivalent. |
| `remote` | User answers `my_remote_repo`. | Answer is `my_remote_repo`. | Equivalent. |
| `remote_url` | User answers URL matching `https://github.com/githug/not_a_repo/?`. | Answer is exact URL without trailing slash. | Slightly stricter; can be relaxed if trailing slash should be accepted. |
| `pull` | Latest commit hash is `1797a7c`. | Remote file is present, `origin/master` fetched, and commits advanced. | Equivalent local synthetic remote outcome. |
| `remote_add` | `git remote -v` contains `https://github.com/githug/githug`. | `origin` remote equals that URL. | Equivalent and slightly stricter on remote name. |
| `push` | Local `master` and `origin/master` have four identical commits. | `origin/master` was pushed, four files are present, and commit count is at least four. | Equivalent user outcome; Android tracks pushed ref instead of comparing remote commit IDs. |
| `diff` | Answer is changed line number `26`. | Answer is `26`. | Equivalent. |
| `blame` | Answer equals author of known password commit (`Spider Man`). | Answer is `Spider Man`. | Equivalent with deterministic Android fixture. |
| `branch` | Branch `test_code` exists. | `test_code` exists and current branch remains `master`. | Slightly stricter to prevent solving by checkout side effects. |
| `checkout` | Current branch is `my_branch`. | Current branch is `my_branch` and branch exists. | Equivalent. |
| `checkout_tag` | HEAD subject is "Some more changes" with five commits. | HEAD is at tag `v1.2`. | Equivalent in Android tag model. |
| `checkout_tag_over_branch` | Same as `checkout_tag`; must choose tag, not branch. | HEAD is at tag `v1.2`. | Equivalent in Android tag model. |
| `branch_at` | `test_branch` exists and excludes "Updating file1 again". | `test_branch` points at commit index 2. | Equivalent. |
| `delete_branch` | `delete_me` branch no longer exists. | Same. | Equivalent. |
| `push_branch` | Remote has pushed `test_branch` but not all branches. | `origin/test_branch` pushed and master/other not pushed. | Equivalent. |
| `merge` | `file1` and `file2` exist. | Current branch is `master`, a merge action occurred, and `file2` is tracked. | Deliberately stricter than upstream to avoid `git switch feature` falsely solving on Android. |
| `fetch` | Local branch count is one and `.git/FETCH_HEAD` has two entries. | `branches.size == 1`, `fetchHeadCount == 2`, and no recorded `pull` action. | Mostly parity; Android adds the `pull` guard because real Git can leave two `FETCH_HEAD` lines after `git pull`, while the exercise wording explicitly says fetch without merging. |
| `rebase` | `feature` commit messages are `add feature`, `add content`, `init commit`, and old hash changed. | Current branch is `feature` and modeled commit messages match. | Equivalent, except Android does not compare the old hash. |
| `rebase_onto` | `readme-update` has four commits, excludes "Wrong changes", and preserves authors. | Current branch is `readme-update` and rebase-onto action occurred. | Known gap: Android validation is looser than upstream content/commit checks. |
| `repack` | `git count-objects -v` includes packed/pruned object evidence. | `repack` action recorded. | Equivalent action-level validation; Android does not model object database packing stats. |
| `cherry-pick` | Top commits are "Filled in README..." then "Added fancy branded output". | Same commit message order plus `README.md` tracked. | Equivalent. |
| `grep` | Answer is TODO count `4`. | Answer is `4`. | Equivalent. |
| `rename_commit` | Parent commit message is corrected to `First commit`. | No `coommit` remains and `First commit` exists. | Equivalent for intended reword. |
| `squash` | Commit count is two. | Commit count at most two and "Adding README" remains. | Slightly stricter on preserving the base README commit. |
| `merge_squash` | Commit count is three and all long-feature changes are included. | Squash action recorded and a commit exists. | Known gap: Android does not yet verify all squash file/content effects. |
| `reorder` | `git log` subject order matches `Third.*Second.*First.*Initial`. | Modeled commit order becomes First, Second, Third. | Equivalent relative to Android's oldest-first commit list. |
| `bisect` | Answer is hash prefix `18ed2ac`. | Answer is `18ed2ac`. | Equivalent. |
| `stage_lines` | Staged diff contains first feature line; unstaged diff contains second feature line. | `feature.rb` is staged. | Known gap: Android does not yet model partial hunk staging. |
| `find_old_branch` | Current branch is `solve_world_hunger`. | Same. | Equivalent. |
| `revert` | More than three commits and a revert of "Bad commit" exists. | A commit message starts with `Revert`. | Slightly looser; sufficient for current fixture. |
| `restore` | `file3` exists. | `file3` is tracked. | Equivalent. |
| `conflict` | On `master`, merge commit has two parents, conflict markers removed, both poem lines preserved. | `poem.txt` contains expected merged poem text. | Known gap: Android does not model merge-parent count for conflict resolution. |
| `submodule` | `githug-include-me` directory exists, has README, and is a gitlink/submodule. | `submodules` contains `githug-include-me` URL. | Equivalent state projection. |
| `contribute` | Clones upstream and checks for a commit authored by configured user. | Any nonblank command. | Deliberate mobile/offline simplification for the final contribution prompt. |

## Focused Source-To-Android Checks

### `fetch`

| Upstream Ruby (`levels/fetch.rb`) | Android Kotlin (`FetchLevel.kt`) |
| --- | --- |
| Counts local branches with `repo.branches.size`. | Uses `repo.branches.size`. |
| Counts `.git/FETCH_HEAD` lines after fetch; success requires `num_remote == 2`. | `GitRuntime` now reads `.git/FETCH_HEAD` into `RepoState.fetchHeadCount`; success requires `fetchHeadCount == 2`. |
| Success requires exactly one local branch and two fetched heads. | Success requires exactly one local branch and two fetched heads. Android also rejects a recorded `pull` action to preserve the exercise instruction "without merging" under native Git behavior. |

### `status`

| Upstream Ruby (`levels/status.rb`) | Android Kotlin (`StatusLevel.kt`) |
| --- | --- |
| Stages `config.rb`, `README`, `setup.rb`, `deploy.rb`, and `Guardfile`. | Native setup now creates and stages those same five files. |
| Leaves `database.yml` untracked. | Leaves `database.yml` untracked. |
| Validates answer `database.yml`. | Validates answer `database.yml`. |

### `merge`

| Upstream Ruby (`levels/merge.rb`) | Android Kotlin (`MergeLevel.kt`) |
| --- | --- |
| Validates `File.exists?("file1") && File.exists?("file2")`. | Validates `master`, a recorded merge action, and tracked `file2`. |
| Upstream is filesystem-only and loose. | Android is intentionally stricter because `git switch feature` otherwise produces the same visible file state and falsely solves the level. |

## Follow-Up Gaps

These are the remaining known non-parity items that need additional model support if exact upstream validation is required:

- `commit_in_future`: add authored timestamp inspection to `CommitNode`/`RepoState`.
- `stage_lines`: model partial staged vs unstaged hunks.
- `merge_squash`: verify the exact squashed file/content effects.
- `conflict`: model merge parent count or inspect merge commit parents.
- `rebase_onto`: verify final commit count/content and removal of "Wrong changes".
- `contribute`, `clone`, `clone_to_folder`: current Android behavior intentionally avoids real network-dependent validation.
