Git and Submission¶
This page explains how released exercises get from the course repo onto your
laptop, and how your work gets from your laptop into a repo your TA and
professor can read. You will want it the first time oslings update refuses to
run, and any time you are unsure whether what you typed today was actually
saved. First-time setup lives in Dev Setup; this is the
reference for what those commands do underneath.
Two remotes, one clone¶
You have one clone of one repository on your machine, with two remotes attached to it. Everything else follows from that.
| Remote | Points at | Your access | Used by |
|---|---|---|---|
course |
the class repo | read only (fetch) | oslings update |
origin |
your private oslings-<your-github-username> repo |
read and write | oslings submit |
flowchart LR
C["course\n(class repo, read-only)"] -- "oslings update\nfetch + merge" --> L["your clone\n(your laptop)"]
L -- "oslings submit\nadd + commit + push" --> O["origin\n(your private repo)"]
oslings init-repo <url> builds this arrangement, and you run it once. It
renames the clone's existing origin to course, adds your repo URL as the new
origin, and pushes your current branch with -u so it tracks (sync.rs:50).
Re-running it with a different URL is safe: it uses remote set-url when
origin already exists (sync.rs:59). If your URL does not contain
oslings-, it warns you — batch grading finds your repo by name.
Note what is not here: no per-exercise repo, no branch per exercise, no pull request. Exercises are gated by absence — one that has not been released exists in no commit you can fetch, so there is nothing to read ahead to.
What oslings update does¶
oslings update is a fetch and a merge, with two safety checks in front of it
(sync.rs:86). In order:
- Dirty check. Any uncommitted change — modified, staged, or untracked —
under a course-owned path aborts the command before anything is fetched
(
sync.rs:97,git.rs:78). - Fetch.
git fetch course. Nothing in your working tree changes yet. - Divergence check.
git diff --name-only course/main...HEADfiltered to course-owned paths (git.rs:88). This catches edits you already committed to course files — the only thing a merge can conflict on. - Merge.
git merge --no-edit course/main(sync.rs:129). Deliberately not--ff-only. - Report. It diffs
info.tomlbefore and after and prints the exercise names that are new (sync.rs:15). No new blocks meansAlready up to date — no new exercises. - Self-update. If the merge touched
oslings-cli/, it re-runscargo install --path oslings-cli --forcefor you (sync.rs:146).
oslings update --from <remote-or-path> pulls from somewhere other than
course, which you will only use if an instructor tells you to.
Why there is a merge commit every time¶
Between two updates you make your own commits (every oslings submit is one),
so your branch moves ahead while course/main moves ahead too. Two branches
that both moved cannot fast-forward; git has to join them, and that join is
the merge commit. Your history correctly looks like this:
* 4f1c2ab Merge remote-tracking branch 'course/main'
|\
| * 9b0e771 release: ex07_scheduler <- from course
* | 2d4a8e0 ex06_context_switch: submit (passing) <- yours
* | 77c1d90 ex06_context_switch: submit (in progress)
|/
* 1a9f004 Merge remote-tracking branch 'course/main'
A merge commit per release is normal, harmless, and expected — not a mistake, and not something to rebase away. The merge is safe precisely because the two sides never write to the same files; see the ownership table below.
What oslings submit commits¶
oslings submit stages exactly these paths, skipping any that do not exist
(sync.rs:183):
| Path | What it holds |
|---|---|
my-work/ |
your archived work, one directory per exercise |
submissions/ |
snapshots taken when an exercise passed, plus oslings-meta.toml |
.oslings/state.toml |
which exercise you are on, what you have passed, hints used, test runs |
rv6/src |
the kernel you are building |
warmup/src |
the Module 1 Rust exercises |
It then commits with the message <exercise>: submit (passing) or
<exercise>: submit (in progress) depending on whether that exercise is in your
completed list (sync.rs:32), and runs git push origin HEAD. If nothing is
staged it prints Nothing new to submit. and stops — no empty commits.
Two things worth knowing:
- Submit at the end of every session, red or green. All work happens in class, so this push is the record that you were here and working, and it is where you resume next time.
commands/src/binandasmlab/srcare not in that list. Work there reaches your repo when OSlings archives it intomy-work/<exercise>/, which happens whenever you move off the exercise (model.rs:711). To push an in-progress command file before moving on, stage it yourself first:git add commands/src/binand thenoslings submit— submit commits whatever is already in the index.
Ignored on purpose, so do not go looking for them in your repo: target/,
every Cargo.lock except the CLI's, .oslings/config.toml, and the
oslings ship output (rv6/src/userbin/, rv6/src/userbin.rs). Command
sources are committed; built images are not — see
ulib and Commands.
Who owns which files¶
The whole design rests on the two sides never touching the same files.
| Course-owned (guarded) | Student-owned |
|---|---|
exercises/ |
rv6/src |
info.toml |
warmup/src |
oslings-cli/ |
commands/src/bin |
setup.sh, SETUP.md, README.md |
asmlab/src |
my-work/, submissions/, .oslings/state.toml |
The guarded list is literally COURSE_PATHS in git.rs:15; the staging
directories are in model.rs:598. Everything else — the crate manifests
(rv6/Cargo.toml, warmup/Cargo.toml, commands/Cargo.toml, ulib/), the
linker scripts (rv6/kernel.ld, commands/user.ld), rust-toolchain.toml — is
course-maintained but not guarded. Editing those will not stop update from
starting, but if a later release changes the same file you get a real merge
conflict at step 4, which is much less pleasant than the guard. Treat them as
read-only.
When update refuses¶
This is the one failure mode worth understanding, and it has two forms.
"these course files have uncommitted changes"¶
You edited or created a file under a course-owned path and have not committed
it. Common causes: a scratch note dropped in exercises/, hand-editing
info.toml, an editor reformatting a file you had open. The error prints the
exact fix:
Caveat the message does not spell out: git checkout -- restores tracked
files only. If the offending path is a file you created, git checkout will
error with did not match any file(s) known to git — move it out of the repo
(or delete it) instead:
"you have committed changes to course files"¶
Same cause, but the edit is already in a commit, so discarding the working copy is not enough. Restore the course version from the upstream ref and commit that restoration:
Your own work is never at risk in either case: both guards run before the
merge and neither touches rv6/src, warmup/src, my-work/, or
submissions/. If the merge fails anyway, oslings tells you to stop — do
that, and ask a TA rather than experimenting with git reset.
Git for people who have not used it much¶
You can pass this course knowing five ideas.
- Commit — a saved snapshot of the whole project with a message attached. Snapshots are cheap and permanent; nothing in a commit is lost.
- Working tree — the files as they are right now on disk. Changes here are not saved until a commit.
- Staging area (index) — what
git addputs files into;git commitsaves exactly what is staged.oslings submitdoes both for you. - Remote — a copy of the repository living on GitHub.
pushsends your commits there;fetchbrings theirs down. - Merge — joining two lines of history. Normal here, once per release.
Useful, and safe to run any time:
| Command | Tells you |
|---|---|
git status |
what has changed and what is staged |
git log --oneline --graph -20 |
your recent history, merges and all |
git remote -v |
that origin is yours and course is the class repo |
git diff |
the exact lines you changed since your last commit |
Avoid git reset --hard, git rebase, git push --force, and git checkout
<branch> unless a TA is watching — those are the commands that can actually
destroy work, and none of them are needed in this course. Everything else is
recoverable.
Two habits close every loop: oslings update at the start of a session,
oslings submit at the end of it. See Using OSlings for the
rest of the CLI and the Integrity Policy for what your
commit history may contain.