Git
Git Code Completion Exercises
Complete focused Git command forms drawn from the Git learning lessons.
Interactive exercises that help you practice, check your understanding, and find what to revisit.
Start here
Complete the Git commands: Git Mailbox Patch Series: Apply with git am
Create a disposable source repository with a base commit followed by two ordinary commits. Generate a mailbox series with `git format-patch -o patches BASE..HEAD`. In a separate target repository at the same base, run `git am patches/*.patch` and prove that two commits were created. Compare the resulting author and committer identities and commit messages with the source commits. Then repeat using `git am < series.mbox`. Create a deliberately conflicting target commit and apply a patch that cannot apply cleanly. Use `git am --show-current-patch=diff`, resolve the conflict, stage the resolution, and finish with `git am --continue`. In a second run, use `git am --skip` to omit one patch from a multi-patch series. In a final run, start an `am` operation and use `git am --abort`; prove that the branch and working tree return to their pre-operation state. Explain why `git apply` is not the correct command for reconstructing commits from the mailbox.
Explore the collection
Exercises
Browse the complete collection and choose the resource that matches what you want to practice or understand.
-
ExercisesAdvanced
Complete the Git commands: Git Mailbox Patch Series: Apply with git am
Create a disposable source repository with a base commit followed by two ordinary commits. Generate a mailbox series with `git format-patch -o patches BASE..HEAD`. In a separate target repository at the same base, run `git am patches/*.patch` and prove that two commits were created. Compare the resulting author and committer identities and commit messages with the source commits. Then repeat using `git am < series.mbox`. Create a deliberately conflicting target commit and apply a patch that cannot apply cleanly. Use `git am --show-current-patch=diff`, resolve the conflict, stage the resolution, and finish with `git am --continue`. In a second run, use `git am --skip` to omit one patch from a multi-patch series. In a final run, start an `am` operation and use `git am --abort`; prove that the branch and working tree return to their pre-operation state. Explain why `git apply` is not the correct command for reconstructing commits from the mailbox.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Archive
Create a disposable repository with at least three commits and a release tag. Add a development-only directory and a release metadata file, then use `.gitattributes` with `export-ignore` and `export-subst`. Produce a tar archive from the release tag with a versioned `--prefix`, and produce a second archive containing only one selected path. Extract both into clean directories. Verify that the ignored path is absent, that the substituted metadata contains the expected commit ID, and that `git get-tar-commit-id` returns the commit recorded for the tag archive. Finally, compare that ID with `git rev-parse <tag>^{}`. Do not use `--worktree-attributes` unless you intentionally demonstrate its effect.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Blame: Trace Line-Level Provenance
Create a disposable repository with at least four commits affecting one file, including a whitespace-only change and a substantial copy of lines into another file. Run ordinary blame and a focused `-L` blame. Demonstrate `-M`/`-C` for moved or copied lines. Use `--ignore-rev` and `--ignore-revs-file` for the mechanical revision. Run `--porcelain` and `--line-porcelain`, then demonstrate `--reverse` across a range in which a line is later removed. Finally compare ordinary blame with `--first-parent` on a repository containing a merge. Record what investigative question each invocation answers and verify that none of the commands changes repository history.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Branch Management: List, Create, Rename, Copy, Move, and Delete Branches
Build a disposable repository with at least three commits on main. Create a topic branch without switching, record both tips, then advance the topic branch using a real switch workflow. Create a second branch at an explicit start point, inspect branches with `-v`, rename one branch, copy another, and prove the resulting names resolve to the expected commit IDs. Create an unmerged branch and demonstrate that normal deletion refuses it; then, only in the disposable repository, demonstrate the forced deletion form. Finally, use `git branch -f` on a non-current branch and prove that only that branch ref moved. Capture command output and object IDs so every mutation is explained as a reference-state change. Success requires state evidence, not merely successful exit codes: record the branch list before and after each lifecycle operation, verify current-branch identity, record resolved commit IDs for every affected branch, and explain why rename, copy, delete, and force-move affect refs rather than creating alternative commit objects. Use disposable repositories for destructive operations.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Bundle: Move Git History Offline
Create a disposable repository with three commits on `main` and create a tag named `cutoff` at the first commit. Create and verify a self-contained `backup.bundle`, list its advertised refs, and clone from it into a fresh directory. Then create `update.bundle` from `cutoff..main`. Verify it successfully in a recipient that already has the prerequisite history and demonstrate that verification fails in an unrelated empty repository. Fetch `update.bundle` into the prepared recipient by explicitly mapping its advertised `HEAD` to a remote-tracking ref, then prove that the destination ref points to the expected tip. Finally, run `git bundle unbundle` against a self-contained bundle in a disposable bare repository and record which objects become available and which refs are merely reported rather than created. Explain why this is different from `git fetch`.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git cat-file: Inspect Repository Objects Precisely
Create a disposable repository with at least one commit containing a text file. Record the IDs for the commit, its tree, and the file blob. Use `git cat-file -t` and `-s` to identify each object, `-e` to check the commit, `-p` to inspect the commit and tree, and the typed form to read the commit and blob. Then feed the three object names through `--batch-check`, repeat with a custom `%(objectname) %(objecttype) %(objectsize)` format, and finally run `--batch` for the commit and blob. Finish with `--batch-check --batch-all-objects` and explain why its output is an inventory rather than a reachability report. Do not use a mock Git implementation. Success requires evidence from the actual repository: record the object type and size for the commit, tree, and blob; capture the batch metadata; and explain the difference between object inspection (`cat-file`) and repository integrity/connectivity checking (`fsck`). This lesson uses only official Git project documentation for command semantics, with `git-cat-file`, the Git 2.56.0 command index, and the object model documentation as the primary sources. No third-party Git documentation is used for command semantics.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Cherry-Pick: Applying Selected Commits to Another History
Create a disposable repository with main and maintenance branches. Make two independent commits on main, switch to maintenance, cherry-pick only one selected commit, record source and destination IDs, then use `--no-commit` for another change and create your own combined commit. In a separate disposable repository, create a conflict, inspect `CHERRY_PICK_HEAD`, and abort.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Clean: Safely Remove Untracked Working-Tree Content
Create a disposable repository with one committed file, one modified tracked file, one ordinary untracked file, one untracked directory, and one ignored file. Capture `git status --short --untracked-files=all`. Then use dry runs to demonstrate: default cleanup, directory cleanup with `-d`, ignored-only cleanup with `-X`, and combined ignored-plus-untracked cleanup with `-x`. Finally, safely delete exactly one disposable untracked file with a pathspec and `-f`. Your evidence must show that the modified tracked file remains untouched.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Commit-Graph: Accelerating Commit History Walks
Create a disposable repository with several commits and at least one changed path. First inspect the history with `git log --oneline --graph --all`. Write a reachable commit-graph and verify it. Then write it again with `--reachable --changed-paths` and verify it. Remove the single-file graph in the disposable repository, create a split graph with `--reachable --split --changed-paths`, list the resulting `commit-graphs` directory, and verify the chain. Finally run `git fsck --full` and explain why its responsibility differs from `git commit-graph verify`. Record which commands changed derived graph data and which commands only inspected it. This lesson uses only official Git project documentation for command semantics: `git-commit-graph`, the commit-graph design documentation, `commit-graph-format`, `git-fsck`, and the Git 2.56.0 command index. No third-party Git documentation is used for command semantics.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Describe: Ref-Based Human-Readable Names
Create a disposable repository with an annotated tag and a lightweight tag, then add commits after the tags. Capture the output of `git describe HEAD`, `git describe --tags HEAD`, and `git describe --all HEAD`. Check out the annotated tag and demonstrate the exact-tag result. Return to the branch, modify a tracked file, and demonstrate `git describe --tags --dirty`. Explain why the candidate set changes between the three forms and confirm that none of the commands changes the repository refs.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Detached HEAD: Inspecting Commits Without Moving a Branch
In a disposable repository, create at least two commits on `main` and record the tip object ID. Use `git switch --detach HEAD~1` to detach at a known earlier commit. Record `git rev-parse HEAD`, `git rev-parse --symbolic-full-name HEAD`, `git rev-parse --abbrev-ref HEAD`, `git status --short --branch`, and the unchanged `main` tip. Then make one controlled empty commit while detached and prove that HEAD advances while `main` remains unchanged. Next, create a second disposable repository and run `git switch --detach main` at the branch tip; prove that equal object IDs do not make HEAD attached. Finally, run `git switch main` and prove that HEAD is symbolic again. Do not use reflogs or recovery techniques in this exercise; those belong to the later investigation/recovery phase. Success requires state evidence rather than only command output: identify the selected commit, prove that HEAD is detached, prove that the named branch tip did not move during the detached commit, and then prove reattachment by observing `refs/heads/main` as HEAD’s symbolic target. Use disposable repositories whenever an experiment creates commits that you do not intend to keep on a named branch.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Diff: Comparing Repository States
Create a disposable repository with one committed file. Make one change and inspect it with plain `git diff`. Stage that change and inspect it with `git diff --cached`. Edit the file again without staging the second change, then run both commands and explain why they show different content. Finally, run `git diff HEAD` and `git diff HEAD~1 HEAD` after making a second commit, and use `--name-status` with a path restriction to inspect only the file you changed.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Patch Workflows: Format and Apply Patch Text
Create a disposable repository with a base commit and at least two later commits. Run `git format-patch -o patches BASE..HEAD` and inspect that one mailbox-style file is created per selected commit. Also run `git format-patch --stdout BASE..HEAD > series.mbox` and `git format-patch --numbered-files -o numbered BASE..HEAD`; explain how the outputs differ. Create a separate raw diff with `git diff BASE HEAD > change.patch`. In a clean checkout at BASE, run `git apply --check change.patch`, then apply it and prove that no commit was created. Repeat with `--cached` and prove only the index changed, then with `--index` and prove both index and worktree changed. Exercise `--stat`, `--numstat`, `--summary`, `--reverse`, `--include` or `--exclude`, `-p1`, and `--whitespace=error`. Finally, explain in your own words why the mailbox produced by `format-patch` belongs to the `git am` workflow rather than being fed directly to `git apply`.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Foundations: Working Tree, Index, and Commits
In a new disposable directory, reproduce the workflow without copying the lesson’s exact command sequence. Your final repository must contain one commit on `main`, and that commit must contain a file named `notes.txt` whose contents are `Git state matters.` followed by a newline. Before committing, use a status or diff command to verify what is staged. After committing, use `git log` or `git show` to verify the resulting history.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Repository Integrity: Verify with git fsck
Create a disposable repository with two normal commits and a tag. Run `git fsck`, `--full`, `--connectivity-only`, `--root`, `--tags`, and `--name-objects`, and explain what each adds or changes. Then create a third commit on a temporary branch, delete that branch, and compare `git fsck --unreachable` with `git fsck --unreachable --no-reflogs`. Identify the orphaned commit and explain why its supporting tree/blob objects may also appear. Finally, run `git fsck --full --strict --no-dangling` and record the clean result. Do not delete or rewrite any objects as part of the exercise.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Repository Maintenance: gc and maintenance
Create a disposable repository with several commits. Record its object-storage report with `git count-objects -v -H`. Run `git gc --no-prune`, then inspect the storage report again. Next run `git maintenance run --task=gc` and `git maintenance run --auto`. Create a disposable configuration file, register the repository with `git maintenance register --config-file`, verify that the repository was recorded in that file, and unregister it. Finally run `git fsck --full` and explain why a successful maintenance run is not an integrity certificate.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Grep: Repository-Aware Search
Create a disposable repository with tracked files under `src/` and `docs/`. Commit content containing the same search term in multiple files. Then create a staged change, an untracked file, and an ignored file containing distinguishable variants of the term. Demonstrate with captured output: a normal working-tree search; a line-numbered, case-insensitive search limited to `src/`; an index search with `--cached`; a committed-tree search against `HEAD`; an untracked-file search with `--untracked`; and a filename-only search with `-l`. Your evidence must show which repository state each command searched and must not rely on an external recursive `grep` command.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git HEAD: The Current Branch and Symbolic References
In a disposable repository, create at least two commits on main and a second branch pointing at the first commit. Record `git symbolic-ref HEAD`, `git rev-parse --symbolic-full-name HEAD`, `git rev-parse HEAD^{commit}`, and `git rev-parse main^{commit}`. Advance main with a new commit and repeat the observations. Then create a fresh repository with `git init -b main` and demonstrate the unborn-branch case without inventing a commit. Your evidence must show that attached HEAD names a branch, that the branch tip can move while HEAD remains attached to that branch, and that an unborn branch can exist before its first commit. Do not use detached HEAD; that belongs to the next lesson. Success requires comparing the symbolic target and resolved object ID before and after the branch advances. Treat failed commit resolution in the unborn repository as expected evidence of an unborn branch, not as a test failure.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Ignore Files and Check Ignore
Create a disposable repository containing `src.txt`, `debug.log`, and `notes.log`. Track and commit `src.txt` and `notes.log` first. Then create a `.gitignore` containing `*.log`. Verify that already tracked `notes.log` remains tracked, while a newly created `trace.log` is ignored. Use `git check-ignore --verbose` to identify the rule responsible for `trace.log`. Finally, verify that no ignored file was deleted.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git index-pack: Build and Verify Pack Indexes
Create a disposable repository with at least three commits and run `git repack -ad`. Locate its `.pack` file. Copy that pack outside the repository, remove the copied `.idx` and `.rev` files if present, and use `git index-pack -o` to build a new index. Repeat with `--rev-index` and prove that the `.rev` file is created. Then use `--strict` on the repository's pack. Finally send the pack through `--stdin` into a separate output pack with `--keep`, inspect the resulting `.keep`, `.idx`, and `.rev` files, and explain why `git index-pack` is not a replacement for `git repack` or `git verify-pack`. Do not modify or delete objects in the original repository. Command semantics in this lesson use only the official Git project documentation for `git-index-pack` and the Git 2.56.0 command index. No third-party Git documentation is used.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Interactive Rebase: Editing and Reshaping Commit History
Create a disposable repository with a base commit and a topic branch containing three commits. Run an interactive rebase that (1) reorders two commits, (2) squashes two adjacent commits, and (3) rewords the resulting message. Record the original commit IDs before the operation. Afterward, verify the resulting commit count, order, messages, and new object IDs. Then create a `--fixup` commit and run `git rebase -i --autosquash` to verify that Git rearranges it next to its target. Do not publish the rewritten history.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git ls-tree: Inspect Tree Objects
In a disposable repository, create files in at least two directory levels and commit them. Use `git ls-tree` to inspect the root, then use `-r`, `-r -t`, `-d`, a path argument, `--name-only`, `--object-only`, `-l`, `--abbrev=12`, `-z`, and a custom `--format`. Run at least one inspection from inside a subdirectory and compare `--full-name` with `--full-tree`. Finally, pipe the non-recursive tree listing into `git mktree` and confirm that inspection and construction are separate operations.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Merge Conflict Resolution: From Unmerged Paths to a Resolved Index
Create a disposable repository with a common base and two branches that conflict on at least two tracked paths. Start the merge and record the unresolved state. Resolve exactly one path first. Prove with `git ls-files --unmerged` that the other path is still unresolved. Then resolve the remaining path, stage both, and verify that the unmerged query is empty. Use `git diff --cached` to record the exact snapshot you have prepared. Finally verify that `HEAD` has not moved and `MERGE_HEAD` still identifies the incoming commit. Do not run the final merge-completion command; that belongs to V440. Success requires evidence at three checkpoints: (1) both paths are initially unmerged, (2) exactly one remains after the first `git add`, and (3) none remain after the second resolution. Your final evidence must also show that resolving the index did not itself move HEAD or delete MERGE_HEAD.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Merge Conflicts: Detecting and Inspecting the Unresolved State
Create a disposable repository with one common base commit, then create two branches that make incompatible edits to the same tracked file. Attempt the merge and collect evidence before changing anything. Record: the non-zero exit status; `git status --short --branch`; the current `HEAD`; `git rev-parse MERGE_HEAD`; every `git ls-files --unmerged` stage for the conflicting path; the working-tree conflict markers; and the parent list of `HEAD`. Your evidence must demonstrate that the branch tip did not become a new merge commit even though Git entered merge state. Do not resolve, continue, or abort the merge until all observations are recorded. Success requires a state table with separate columns for command result, working tree, index, HEAD, and merge metadata. The table must explicitly distinguish “conflict exists,” “merge is in progress,” and “merge commit does not yet exist.”
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Merge Continue and Abort: Completing or Abandoning an In-Progress Merge
Create a disposable repository with two diverging branches that conflict on one tracked path. Record the receiving and incoming tips before the merge. Start the merge and inspect the interrupted state. Then perform one complete laboratory run by resolving the conflict, staging the result, configuring a deterministic editor for the merge message, and running `git merge --continue`; prove the new HEAD has two parents and that the merge-in-progress state is gone. Perform a second independent run in which you start the conflict and run `git merge --abort`; prove the receiving branch returned to its pre-merge tip and the working tree matches that tip. Do not treat exit status alone as proof; inspect repository state and history. Success requires two independent proofs: the continuation run must produce a new two-parent merge commit and clear the interrupted merge state; the abort run must restore the receiving branch's pre-merge tip and clear the interrupted merge state without creating a merge commit.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Fast-Forward vs True Merge: Commit-Graph Outcomes
Create three disposable repositories. In the first, construct a descendant relationship and perform a default fast-forward. Record the old and new `main` object IDs and prove that the new tip already existed before the merge. In the second, construct divergent histories and perform a true merge; record the new merge commit and its parents. In the third, construct a fast-forwardable history, compare default merge with `--no-ff`, then construct a divergent history and prove that `--ff-only` refuses it. Your evidence must include exit statuses, object IDs, and parent lists. Do not infer the outcome solely from file content. Success requires a written state table for each experiment: pre-merge branch tips, ancestry relationship, command, exit status, post-merge branch tip, and parent list. The table must distinguish “new commit created” from “existing commit adopted.”
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Merge vs Rebase vs Cherry-Pick: Comparing History Transformations
Build three small disposable repositories. In the first, create divergent branches and merge them; record both merge parents. In the second, create a topic sequence and a new main base, then rebase the topic; record old and new commit IDs and verify ancestry. In the third, create two source commits, switch to another destination history, and cherry-pick only the first; prove that the second source change was not selected.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git mktag: Construct Tag Objects with Validation
In a disposable repository, create one commit and record its object ID. Construct a valid annotated-tag object input containing that commit as the target, a tag name, tagger identity, timestamp, and message. Run `git mktag` and capture the printed object ID. Verify with `git cat-file -t` that it is a tag object and with `git cat-file -p` that the stored contents match your input. Then change only the annotation message, run `git mktag` again, and demonstrate that the object ID changes. Finally, deliberately use a nonexistent target object and show that `git mktag` rejects the input. Do not create a tag reference with `git tag`; the exercise is specifically about the tag-object layer.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git mktree: Build Tree Objects from ls-tree Data
In a disposable repository, create at least two files and commit them. Obtain the commit's tree with `git rev-parse HEAD^{tree}`. Pipe its non-recursive `git ls-tree` output into `git mktree` and prove that the resulting object ID matches the original tree. Repeat with `-z`, then create a tree input that references a deliberately missing object and demonstrate the difference between default behavior and `--missing`. Finally, build two trees with one `--batch` invocation.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Multi-Pack Index: Indexing Multiple Packfiles
Create a disposable repository with enough commits to produce a set of objects, create multiple packfiles in the lab, and run `git count-objects -v` before creating a MIDX. Run `git multi-pack-index write`, confirm the MIDX exists, and run `git multi-pack-index verify`. Then run `git multi-pack-index repack --batch-size=1m`, followed by `git multi-pack-index expire` and another `git multi-pack-index verify`. Record the number of packfiles before and after the MIDX operations and explain why creating a MIDX is different from merging all objects into one pack. Finish with `git fsck --full` and explain why MIDX verification and object-integrity checking answer different questions. This lesson uses only official Git project documentation for command semantics: `git-multi-pack-index`, `gitformat-pack`, `git-repack`, `git-fsck`, and the Git 2.56.0 command index. No third-party Git documentation is used for command semantics.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git name-rev: Find Symbolic Names for Commits
Create a disposable repository with at least three commits, a branch, and a tag. Then run the following workflow. Record which symbolic name Git chooses for `HEAD~1`, compare branch-based naming with `--tags`, restrict the candidates with `--refs`, and finally annotate a line containing the current commit ID. Expected learning outcome: you can explain why the reported name depends on the candidate refs, and you can distinguish a symbolic description from an actual ref stored in the repository.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Non-Fast-Forward Rejection: Safe Integration Before Publishing
Create one bare remote and two disposable clones. From clone A, create a local commit without pushing it. From clone B, create and push a different commit. Attempt the stale push from clone A and record the rejection plus the unchanged remote tip. Fetch from A and record the updated `origin/main`. In one disposable copy, merge `origin/main` into A and verify that the resulting merge commit has both histories as ancestors before pushing. In another disposable copy, re-create the divergence, rebase A onto `origin/main`, verify that the local commit identity changed and the remote tip became an ancestor, then push. Finally, create an intentionally rewritten local tip and demonstrate that an explicit `--force-with-lease=<ref>:<expected>` succeeds only when the remote ref still equals the recorded expected object ID.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Notes: Attach Metadata to Git Objects
Create a disposable repository with at least two commits. Add a review note to the first commit, inspect it with `show` and `list`, append a second observation, and copy the resulting note to the second commit. Create a separate `refs/notes/review` annotation set and verify that its note is independent of the default notes ref. Remove one note and verify that it is gone. Run `git notes prune -n` and explain why note history is stored separately from the annotated commit. Finally, create two notes refs and perform a non-conflicting notes merge. Record which refs changed and why none of these operations changed the object ID of the commits.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Object Storage: Count, Repack, and Verify Packfiles
Create a disposable repository with at least two commits. Run `git count-objects -v -H` and record the loose-object count. Run `git repack -ad`, run the count again, and identify the changes in `count`, `in-pack`, `packs`, and `size-pack`. Locate the resulting `.idx` file under `.git/objects/pack/` and verify it with `git verify-pack`, then repeat with `-s` and `-v`. Finally run `git fsck --full` and explain why successful pack verification and successful object-integrity checking answer different questions. This lesson uses only official Git project documentation for command semantics: `git-count-objects`, `git-repack`, `git-verify-pack`, `git-fsck`, and the Git 2.56.0 command index. No third-party Git documentation is used for command semantics.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git pack-objects: Construct Pack Archives
In a disposable repository with several commits and an annotated tag, produce a pack from an explicit object list with `--stdout`. Produce another using `--revs`, then compare the resulting packs with `git index-pack` or the existing pack-inspection lessons. Repeat with `--all`, `--unpacked`, and `--incremental`; record which object sets are selected. Finally, generate a tuned pack using `--window`, `--depth`, and `--delta-base-offset`, and explain how `pack-objects` differs from `repack`.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git pack-refs: Pack References for Efficient Storage
Create a disposable repository with at least two commits, three branches, and three tags. Inspect `.git/refs` before packing. Run `git pack-refs --all`, then prove that the branch and tag names still resolve even though their loose files have been removed. Create or update a branch and observe that an active branch can become loose again. Run `git pack-refs --all --no-prune` and explain what remains on disk. In a fresh disposable repository, use `--include 'refs/heads/feature/*'` to pack only feature branches, then use `--all --exclude 'refs/heads/feature/*'` and explain why an exclusion does not unpack refs that were already packed. Finally run `git pack-refs --auto` and explain why its behavior is policy-driven. Finish by explaining why `git pack-refs` and `git repack` must not be treated as interchangeable storage operations. This lesson uses only official Git project documentation for command semantics: `git-pack-refs`, the Git 2.56.0 command index, and the official ref-storage documentation referenced by those manuals. No third-party Git documentation is used for command semantics.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git prune-packed: Remove Loose Copies of Packed Objects
In a disposable repository, create at least one commit, create a pack containing the repository's reachable objects without deleting the loose objects, and confirm that both representations exist. Run `git prune-packed --dry-run` and record the loose object paths it proposes to remove. Then run `git prune-packed` and verify that those loose files are gone while the packed object remains readable. Finally, explain why this operation does not depend on whether the commit is reachable.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git prune: Remove Unreachable Objects Safely
In a disposable repository, create a commit on a temporary branch, delete that branch, and expire the test repository's reflogs so the commit becomes genuinely unreachable for the experiment. Use `git fsck --unreachable --no-reflogs` to identify the unreachable commit and supporting objects. Run `git prune -n -v --expire=now` and record the candidates without changing the repository. Confirm the unreachable commit still exists. Then run `git prune --expire=now` and verify that the commit can no longer be resolved as an object. Repeat the experiment with an additional `<head>` pointing to the temporary commit and prove with a dry run that objects reachable from that head are protected. Finally explain why the same operation would be risky in a real repository and why `git gc` is normally preferred for routine maintenance. This lesson uses only official Git project documentation for command semantics: `git-prune`, the Git 2.56.0 command index, and the official `git-fsck` and `git-gc` documentation for command boundaries. No third-party Git documentation is used for command semantics.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Pull: Fetching and Integrating Remote History
Create a disposable bare repository and two working clones. Publish an initial commit, then create a new remote commit from the second clone. In the first clone, record HEAD and the remote-tracking tip, run an explicit `git pull --ff-only origin main`, and verify that the local branch advances to the fetched commit without creating a merge commit. Then create independent local and remote commits from a common base and compare `git pull --ff-only origin main` with `git pull --no-rebase origin main` in separate disposable repositories. Record which state changes occur in each case and verify that no pull command publishes local commits back to the remote.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Push: Publishing Local Refs to a Remote Repository
Create a disposable bare repository and a working clone. Make a local commit, record the local `HEAD`, the remote `refs/heads/main`, and the local `refs/remotes/origin/main`, then run `git push origin main`. Verify the remote ref and transferred commit object, verify the local HEAD did not move, and observe the local remote-tracking ref after the successful push. Next, create a local `topic` branch and push it with `git push origin topic`; verify that the remote topic ref exists. Finally, create genuine divergence in two clones and verify that an ordinary push is rejected without changing the remote tip. Repeat the publication with `--dry-run` first and confirm that the remote remains unchanged until the real push.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Range-Diff: Compare Commit Series
Create a disposable repository with a base commit and an old topic series of at least three commits. Record the old tip. Create a revised topic series from the same base in which one patch is rewritten, one patch is retained with a changed commit message or patch, and one new patch is added. Run the two explicit-range form, the three-revision form, and the symmetric form. Identify each `=`, `!`, `<`, and `>` relationship in the output. Then run `--creation-factor`, `--left-only`, `--right-only`, `--no-dual-color`, and a path-restricted comparison. Explain which review question each command answers. Do not rebase, reset, or otherwise rewrite the series as part of the exercise.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git read-tree: Read and Merge Trees into the Index
In a disposable repository, create a base commit and a second commit. Use an alternate `GIT_INDEX_FILE` to read the base tree, prove the index entries with `git ls-files --stage`, empty the alternate index, and read the tree again under a prefix. Then use `--index-output` to produce a separate index file. Verify `--dry-run` leaves the index unchanged. Finally, create two divergent commits from a common ancestor and use `git read-tree -m BASE OURS THEIRS` with an alternate index; inspect the resulting stage 1/2/3 entries with `git ls-files --stage`. Repeat a non-conflicting two-tree merge and, separately, a `-u` operation in a disposable worktree to observe the working-tree boundary.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Rebase Model: Transplanting Commits onto a New Base
Create a disposable repository with a base commit. Create a topic branch and commit topic work. Advance main with a separate commit. Return to topic and run `git rebase main`. Record the topic tip before and after, prove the IDs differ, prove the new topic tip descends from main, and verify that main itself did not move. Do not use interactive rebase or resolve a conflict here; those are later ownership boundaries.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Rebase: Running, Controlling, and Verifying History Rewrites
In a disposable repository, create a base commit, branch to `topic`, make a topic commit, advance `main` independently, then rebase `topic` onto `main`. Record the topic tip before and after. Prove the IDs differ, prove the new topic tip descends from main, and verify that main did not move. Then repeat the experiment with `--onto` using a three-branch topology. Do not use interactive rebase; do not publish the rewritten branch.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git References and Branch Tips: Commits Are Objects, Branches Are Names
Build a disposable repository with at least two commits. Record the object IDs that `main` resolves to. Create a second branch without switching, prove that both branch names resolve to the same commit, and prove that the working tree is still on the original branch. Then advance the second branch with one real commit. Record both branch tips and show that only the second branch moved. Finally, compare the repository's object-ID/type inventory immediately before and after creating a branch and explain why the result demonstrates a reference change rather than commit creation. Do not use a mock Git implementation. Success requires evidence about references and objects, not only command exit codes. Capture the full ref name, the commit IDs before and after each branch movement, the current branch name, and the object inventory comparison. The key conclusion should be stated in Git's own terms: branch refs move; commit objects are identified by object names and are not duplicated merely because another ref points at them.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Reflog: Reading the Local History of References
Create a disposable repository with three commits on `main`. Create a feature branch and switch between the branches so the HEAD reflog records checkouts. Then switch back to main, move `main` backward with `git reset --hard HEAD~1`, and inspect both the branch reflog and HEAD reflog. Resolve `main@{1}` and prove that it names the pre-reset commit. Do not recover or delete anything yet; the next lesson owns recovery workflows.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Remote Management: Configuring and Inspecting Remotes
Create a disposable repository with a local commit, add a bare repository as `origin`, inspect its URL, add a second remote named `upstream`, change the upstream URL, rename it to `backup`, and finally remove `backup`. At each step, verify the local configuration with `git remote`, `git remote -v`, and `git config --get-regexp '^remote\.'`. Confirm at the end that the current branch and HEAD object ID are unchanged by the remote administration operations.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Remote Names and Remote-Tracking References
Create two disposable repositories: a source repository and a bare remote. Add the remote to the source with a non-default name such as upstream, publish main, and create a separate clone. In the clone, inspect the remote name and fetch refspec, verify refs/remotes/origin/main, then advance the source main branch and fetch again. Prove with object IDs that origin/main advances while the clone’s current local branch and working tree remain unchanged. Finally create a new topic branch in the source, publish it, fetch in the clone, and prove that refs/remotes/origin/topic exists while refs/heads/topic does not. Record the exact refs and object IDs you observed.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Removing and Moving Tracked Files
Build a disposable repository containing `keep.txt`, `remove-me.txt`, and `notes.txt`. Commit them. Then: (1) remove `remove-me.txt` with `git rm`; (2) rename `notes.txt` to `documentation.txt` with `git mv`; (3) verify both changes are staged; (4) confirm `keep.txt` is still tracked and present; (5) commit the changes; and (6) verify the final commit contains the new path and no longer contains the removed path.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Repository Creation and Cloning
Build a complete local laboratory without using a network service. First create an ordinary repository named `source-repo` with initial branch `main`, add and commit one file, then create a bare repository named `shared.git` and push `main` to it. Finally clone `shared.git` into `clone-repo`. Verify the clone rather than trusting the command's success message: prove that the current branch is `main`, the working tree is clean, `origin` points to `shared.git`, and the local HEAD matches `origin/main`. Then create a non-empty directory and confirm that attempting to clone into it fails without deleting its existing file.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Reset: Moving HEAD and Controlling Local State
Create a disposable repository with at least two commits. First make a new edit and stage it, then use the path form of `git reset` to unstage it without losing the working-tree edit. Next create a second commit, record its object ID, and experiment in separate disposable copies with `--soft`, `--mixed`, and `--hard` resets to its parent. For each mode, record the resulting `HEAD`, index, working-tree content, `ORIG_HEAD`, and `git status --short`. Explain exactly which states changed and which did not. Success means you can predict the state transition before running each command and then verify the prediction with `git rev-parse`, `git diff`, `git diff --cached`, file contents, and `git status --short`. Never use `--hard` on work you need to keep.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Restore, Reset, and Revert: Choosing the Operation by State
Build four disposable repositories or reset one disposable repository between trials. Trial 1: create a tracked working-tree edit while keeping the index at the desired version, then use the operation that changes only the working tree. Trial 2: stage an edit and then unstage it without losing the working-tree change. Trial 3: make a prior commit's version of one path the current index and working-tree version without moving `HEAD`. Trial 4: create a history with a change that should remain in the graph but whose effect must be reversed by a new commit. Before each command, write down the state that must change; after it, verify `HEAD`, `git status --short`, `git diff --cached`, `git diff`, and `git log --oneline --decorate --graph`. Success means you can justify the command from the desired state transition, predict which of the working tree, index, branch tip, and commit history will change, and verify those predictions with real Git. Include at least one case where two commands could make a file look similar but only one satisfies the required history outcome.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Restore: Correcting Working Tree and Index State
Create a disposable repository with one committed file. Make one edit and stage it, then make a second working-tree edit. Use `git diff --cached` and `git diff` to observe the two states. Use `git restore --staged` to remove the first change from the index without discarding the working-tree content. Then use `git restore` to make the working tree match the index. Finally, verify that `HEAD` and the branch tip never changed during the restore operations. Success means you can identify which local state each command changed, explain the source used by default, and demonstrate that no commit was rewritten or created.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Revert: Recording a History-Preserving Reversal
Create a disposable repository with at least three commits whose effects are easy to distinguish. Revert the newest commit, then inspect the resulting history and patch. In a separate disposable repository, revert an earlier commit while preserving a later commit. Finally, construct a controlled conflict during a revert, resolve it, and complete the operation. Record the original target commit ID, the new revert commit ID, `HEAD`, `git show`, `git diff`, and `git status --short` before and after each operation. Success means you can predict that revert adds history rather than moving the branch tip, can identify the commit whose patch is being reversed, and can explain what happens when the inverse patch conflicts. Never substitute `reset --hard` for a history-preserving revert when the original commit must remain in the branch history.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Revision Selection and Ancestry Queries
Create a disposable repository with a base commit, two commits on a topic branch, two commits on `main`, and a non-fast-forward merge. Record the merge's first and second parent IDs. Use `MERGE^`, `MERGE^2`, `MERGE~2`, `MERGE^@`, `MERGE^!`, and `MERGE^-2` and explain each result. Then create two divergent branch tips and prove `A..B`, `^A B`, and `A...B` produce the documented commit sets. Finally, build a commit containing a deliberately chosen message and file, resolve it with `:/message` and `R:path`, and compare `git log A...B` with `git diff A...B` so the history-selection and endpoint-comparison semantics are explicit.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Safe Recovery Laboratories: ORIG_HEAD, Backup References, and Abortable Operations
Run all four laboratories in disposable repositories. For Lab 1, record the original `HEAD`, perform a hard reset to an earlier commit, prove that `ORIG_HEAD` names the original tip, and recover to it. For Lab 2, create a named safety branch before moving `HEAD`, then use that reference to recover. For Lab 3, construct a real conflicting revert, inspect the in-progress state, and use `git revert --abort`; verify that `HEAD`, tracked files, and status return to their pre-sequence values. For Lab 4, write a short recovery record naming the state that changed, the recovery anchor, the command used, and the post-recovery evidence. Success requires state evidence, not just a zero exit status. For every laboratory, capture the commit IDs before and after the destructive or abortable operation, verify working-tree and index state, and explain why the chosen recovery anchor was valid for that specific operation.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git show-index: Inspect Pack Indexes
Create a disposable repository containing at least two commits. Generate or obtain a pack containing its objects, locate the corresponding `.idx` file, and use `git show-index` to inspect it. Record the first few lines and identify the three fields on a version-2 index line. Then run the same inspection with `--object-format=sha1` and compare the object IDs. Finally, use `git verify-pack -v` on the same index and explain one concrete difference between the two commands.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Show: Reading Committed Snapshots
Create a disposable repository with two commits. Inspect the second commit with `git show`, then inspect the first commit explicitly. Use `--no-patch` to inspect commit information without the patch, and use a path restriction to inspect only the file changed by the second commit. Finally compare the two snapshots with `git diff` and explain why the outputs answer different questions.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git show-ref: Inspect Local References
Create a disposable repository with two branches, one lightweight tag, and one annotated tag. Use `git show-ref` to produce: (1) the complete ref listing, (2) branches only, (3) tags only, (4) exact verification of one branch, (5) an existence test for an existing and a missing ref, (6) an annotated-tag dereference, (7) hash-only output, and (8) an `--exclude-existing` filter fed by standard input. Record the exit status of the existence and verification checks. Then run `git pack-refs --all` and repeat the inspection to demonstrate that logical refs remain visible even when their loose files have been packed. Do not manually read `.git/packed-refs` as a substitute for the command's output.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Sparse-Checkout: Work with a Selected Working-Tree Subset
Create a disposable repository containing at least three directories with tracked files. Initialize sparse-checkout in cone mode, select two directories, verify the selected and omitted paths, add another directory, and inspect the selection with `list`. Then disable sparse-checkout and confirm the full working tree is restored. Reinitialize in non-cone mode, select files with patterns, and repeat the inspection. Deliberately create a temporary divergence by restoring or creating a tracked file outside the sparse selection, run `reapply`, and observe the result. Finally, repeat the setup once with `--sparse-index`. Explain why none of these operations delete the omitted files from Git history and why sparse-checkout is different from partial clone.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Stash Inspection, Application, and Recovery
Create a disposable repository with at least two stash entries. Use `git stash list` to identify them, inspect each with `git stash show` and one with `--patch`, then apply one without deleting it. Verify that the stash remains listed. Create a second disposable branch scenario in which the original branch has moved after the stash was created; use `git stash branch` to recover the stash on a branch based on its original commit. Finally, demonstrate the difference between `drop` and `clear` in separate disposable repositories. Record the exact command, selected stash revision, pre-operation status, post-operation status, and whether the stash entry remains.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Stashing
Create a disposable repository with one committed tracked file. Make one staged change and one separate unstaged change, then create a stash with `--keep-index`. Verify that the staged change remains staged and the other change is no longer present in the working tree. Next, create an untracked file and use `--include-untracked` to stash it. Finally, create an ignored file and use `--all` to stash it. Record the exact `git status --short` state before and after each operation, and explain which option caused each file class to be included or excluded.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Switch: Changing the Current Branch Safely
Create a disposable repository with at least two divergent local branches. Make one branch current, introduce a controlled local modification, and attempt to switch to the other branch. Record whether Git permits or rejects the switch and why. Then repeat with a non-conflicting local modification, use `git switch -` to return to the previous branch, create a new branch with `git switch -c` from an explicit start point, and verify every resulting branch tip with `git rev-parse --verify <name>^{commit}`. In a separate disposable repository, demonstrate the difference between `--discard-changes` and `--merge`; if `--merge` produces a conflict, inspect the index state rather than hiding it. Do not use detached HEAD in this exercise; that belongs to the next lesson. Success requires evidence of the state transition: current branch before and after, target branch tip object IDs, working-tree/index status, and the exact reason a switch was accepted or refused. Destructive flags must be demonstrated only in disposable repositories.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Tag Inspection and Deletion
Create a disposable repository with at least three commits and create both lightweight and annotated tags using the V457 creation rules. List all tags, then use a pattern to isolate one naming family. Use `--points-at` on a commit that has both tag forms and use `--contains`, `--merged`, and `--no-merged` against chosen commits so that you can explain the different questions each filter answers. Inspect an annotated tag with `git show` and its listing annotation with `git tag -n`. Finally, delete one lightweight tag and one annotated tag. Before each deletion, save the resolved reference value; after deletion, verify that `refs/tags/<name>` no longer resolves. For an annotated tag, optionally inspect the saved tag-object ID immediately after deletion to demonstrate why deleting a reference is not the same as deleting an object. Perform all deletion work in the disposable repository. Success requires a written state record for each filter and deletion: the tags present before the operation, the exact question the command answers, the reference state after deletion, and—if tested—the immediate object type of a saved tag-object ID. Do not use the Foxipa source repository for destructive demonstrations.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Tag Verification
Create a disposable repository and make at least two commits. Create one unsigned annotated tag and one cryptographically signed tag using a temporary verification setup appropriate to your platform. Record the exact tag object IDs, run `git show` on both tags, and then run `git tag -v` on the signed tag. Also run verification on the unsigned tag and preserve the diagnostic. If your environment cannot create or verify a signature, document the missing dependency rather than claiming success. Finally, run `git verify-tag` against the signed tag and compare its result with `git tag -v`. Success requires a written state record distinguishing: (1) annotated versus signed, (2) signature validity versus key/trust availability, (3) `git tag -v` versus `git verify-tag`, and (4) cryptographic verification versus your project’s separate provenance or authorization rules. Perform all signing experiments in the disposable repository.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git unpack-objects: Expand Pack Archives into Loose Objects
Create two disposable repositories. Put at least two commits in the source repository and use Git plumbing to produce a pack containing the source objects. In a clean receiver repository, run `git unpack-objects --dry-run` first, then unpack the pack. Capture evidence with `git count-objects -v` and `git cat-file -t <object-id>` showing that the receiver now has loose objects. Repeat the unpack to demonstrate that already-present objects are skipped. Finally, explain when `--strict`, `-q`, and `-r` would be appropriate and why `git unpack-objects` is the inverse storage direction of `git pack-objects`.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Upstream and Tracking Configuration: Connecting Local Branches to Remote Branches
Create a disposable bare remote and clone it. Verify that the cloned main branch has an upstream. Create a local topic branch from origin/main with --no-track and verify that topic@{upstream} fails. Then set its upstream explicitly with git branch --set-upstream-to=origin/main topic and verify the relationship with git branch -vv and topic@{upstream}. Change the tracking target to another remote-tracking branch without moving topic, then remove the upstream with --unset-upstream. Finally, create another branch with --track=direct and observe that the branch tip and its upstream relationship are separate pieces of state.
~6 min read Start exercise -
ExercisesBeginner
Complete the Git commands: Git Version, Help, and Configuration
Create a disposable repository named `git-config-practice`. Verify the Git version, obtain command help, configure a repository-local practice identity, and prove that the values can be read back from local configuration. Do not change global Git configuration. Success means the repository exists, the version command reports a Git version, help commands execute, both local identity values can be read back, and the configuration listing identifies the repository configuration source. The exercise does not require any commit.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git Worktree Lifecycle: Lock, Move, Repair, Prune, and Remove
In a disposable repository, create one linked worktree for a branch and prove it appears in `git worktree list`. Lock it with a reason, inspect the locked annotation, and demonstrate that normal removal is refused. Unlock it, move it with `git worktree move`, and verify the new path. Create a second linked worktree, move that directory manually, observe the stale administrative state, repair it with `git worktree repair`, and then remove it cleanly. Finally create a detached worktree, delete its directory manually, run `git worktree prune -n -v` to preview cleanup, then run `git worktree prune -v` and verify the stale entry is gone. Separately, create an unclean disposable worktree and demonstrate that normal removal refuses it before using `--force` only to discard that disposable worktree.
~6 min read Start exercise -
ExercisesIntermediate
Complete the Git commands: Git Multiple Worktrees
Create a repository with one initial commit. Then create (1) a linked worktree on a new branch named `feature-a`, (2) another linked worktree on a new branch named `feature-b`, and (3) a detached worktree at the initial commit. Use `git worktree list --porcelain` to verify all worktrees. Finally, make a distinct uncommitted edit in each linked worktree and confirm that each directory has its own working-tree state. Success criteria: the main worktree and all three linked worktrees appear in the listing; feature-a and feature-b are named branches; the experiment worktree is detached; and an edit made in one worktree does not silently appear as an uncommitted edit in another worktree.
~6 min read Start exercise -
ExercisesAdvanced
Complete the Git commands: Git write-tree: Turn the Index into Tree Objects
Create a disposable repository containing a nested directory and at least two files. Stage the files, run `git write-tree`, and prove that the returned object is a tree. Then run `git write-tree --prefix=<your-directory>/` and inspect the resulting subtree with `git ls-tree`. Finally, create an unresolved merge in a disposable repository and demonstrate that `git write-tree` refuses the unmerged index; resolve the conflict, stage the result, and show that write-tree succeeds.
~6 min read Start exercise