diff --git a/astro-site/src/plugins/remark-rewrite-links.ts b/astro-site/src/plugins/remark-rewrite-links.ts index a9acf63..5fcbf6c 100644 --- a/astro-site/src/plugins/remark-rewrite-links.ts +++ b/astro-site/src/plugins/remark-rewrite-links.ts @@ -1,11 +1,28 @@ import { visit } from "unist-util-visit"; import type { Root, Link } from "mdast"; -const CHAPTER_LINK = /^(\d{2}-)(.+)\.md(#.*)?$/; +const CHAPTER_LINK = /^(?:\.\.\/)?(\d{2}-)(.+)\.md(#.*)?$/; +const SUBDIR_FILE_LINK = /^([\w-]+)\/([\w-]+)\.md(#.*)?$/; +const SIBLING_LINK = /^([a-z][\w-]*)\.md(#.*)?$/; export function remarkRewriteLinks() { return (tree: Root) => { visit(tree, "link", (node: Link) => { + // Links from top-level to subdir: playbook/index.md → ../playbook/ + // Links from top-level to subdir page: playbook/undoing-changes.md → ../playbook/undoing-changes/ + const subdirMatch = node.url.match(SUBDIR_FILE_LINK); + if (subdirMatch) { + const slug = subdirMatch[2] === "index" ? "" : `${subdirMatch[2]}/`; + node.url = `../${subdirMatch[1]}/${slug}${subdirMatch[3] || ""}`; + return; + } + // Links to sibling files in same directory: slug.md → slug/ + const siblingMatch = node.url.match(SIBLING_LINK); + if (siblingMatch) { + node.url = `${siblingMatch[1]}/${siblingMatch[2] || ""}`; + return; + } + // Links to numbered chapters: NN-slug.md or ../NN-slug.md → ../slug/ const match = node.url.match(CHAPTER_LINK); if (match) { node.url = `../${match[2]}/${match[3] || ""}`; diff --git a/chapters/07-playbook.md b/chapters/07-playbook.md deleted file mode 100644 index 1e19889..0000000 --- a/chapters/07-playbook.md +++ /dev/null @@ -1,420 +0,0 @@ ---- -title: "Playbook" -description: "Step-by-step recipes for common Git tasks — undoing changes, branching, merging, rebasing, stashing, tagging, and debugging." -section: "playbook" -order: 7 ---- - -## 1. Overview - -This chapter is a quick-reference collection of recipes for common -Git tasks. Each recipe shows the problem, the commands to solve it, -and what to watch out for. - -For command syntax, see [Appendix](08-appendix.md). For definitions, -see [Glossary](09-glossary.md). - -In this chapter you will learn: - -- How to undo changes at every level — unstage, reset, revert, and recover -- Branching recipes — create, delete, rename, and inspect branches -- Merging recipes — fast-forward, no-ff, squash, conflict resolution -- Rebasing — linearize history and squash commits interactively -- Remote operations — push, pull, force push safely, sync forks -- Cherry-picking — apply individual commits across branches -- Stashing — save and restore work in progress -- Tagging — create, push, and delete annotated tags -- Submodules — add, clone, update, and remove submodules -- Debugging — bisect, blame, and search commit history -- Configuration — identity, defaults, aliases, and diagnostics - -## 2. Undoing changes - -### Discard unstaged changes to a file - -```text -$ git restore -``` - -Reverts the file in your working tree to match the last commit. -Uncommitted edits are lost. - -### Unstage a file without losing changes - -```text -$ git restore --staged -``` - -Removes the file from the index but keeps your edits in the working -tree. - -### Undo the last commit but keep changes staged - -```text -$ git reset --soft HEAD~1 -``` - -The commit is removed but your changes stay in the index, ready to -recommit. - -### Undo the last commit and unstage changes - -```text -$ git reset HEAD~1 -``` - -The default (`--mixed`). Changes go back to the working tree as -unstaged edits. - -### Undo the last commit and discard all changes - -```text -$ git reset --hard HEAD~1 -``` - -The commit and all changes are gone. Recover with `git reflog` if -needed. - -### Revert a commit without rewriting history - -```text -$ git revert -``` - -Creates a new commit that undoes the changes. Safe to use on shared -branches. - -### Recover a lost commit - -```text -$ git reflog # find the hash -$ git branch recovered # or: git reset --hard -``` - -Works as long as the reflog entry has not expired (default: 30 days). - -## 3. Branching - -### Create a branch and switch to it - -```text -$ git switch -c feature/name -``` - -### Delete a branch (local and remote) - -```text -$ git branch -d feature/name # local (safe — only if merged) -$ git branch -D feature/name # local (force — even if unmerged) -$ git push origin --delete feature/name # remote -``` - -### Rename the current branch - -```text -$ git branch -m new-name -``` - -### See which branches are merged into main - -```text -$ git branch --merged main -``` - -Safe to delete any branch listed here (except `main` itself). - -## 4. Merging - -### Merge a feature branch into main - -```text -$ git switch main -$ git merge feature/name -``` - -### Force a merge commit (no fast-forward) - -```text -$ git merge --no-ff feature/name -``` - -### Squash a branch into a single commit - -```text -$ git merge --squash feature/name -$ git commit -m "Add feature" -``` - -### Abort a conflicted merge - -```text -$ git merge --abort -``` - -Returns everything to the pre-merge state. - -### Resolve a merge conflict - -```text -$ git status # identify conflicting files -# ... edit each file, remove markers ... -$ git add # stage resolved file -$ git commit # complete the merge -``` - -## 5. Rebasing - -### Rebase a feature branch onto main - -```text -$ git switch feature/name -$ git rebase main -``` - -### Squash commits with interactive rebase - -```text -$ git rebase -i HEAD~3 -# change "pick" to "squash" for commits to combine -``` - -### Abort a conflicted rebase - -```text -$ git rebase --abort -``` - -### Continue after resolving a rebase conflict - -```text -$ git add -$ git rebase --continue -``` - -## 6. Remote operations - -### Push a new branch and set up tracking - -```text -$ git push -u origin feature/name -``` - -### Pull with rebase (avoid merge commits) - -```text -$ git pull --rebase origin main -``` - -### Fix a rejected push - -```text -$ git pull origin main # fetch + merge -$ git push origin main # retry -``` - -### Force push safely (after rebase) - -```text -$ git push --force-with-lease origin feature/name -``` - -Never use `--force` on shared branches. - -### Sync a fork with upstream - -```text -$ git fetch upstream -$ git switch main -$ git merge upstream/main -$ git push origin main -``` - -### Fetch and inspect before merging - -```text -$ git fetch origin -$ git log main..origin/main --oneline # what's new on remote -$ git diff main origin/main # line-by-line changes -$ git merge origin/main # integrate when ready -``` - -## 7. Cherry-picking - -### Apply a single commit from another branch - -```text -$ git cherry-pick -``` - -### Cherry-pick without committing (stage only) - -```text -$ git cherry-pick --no-commit -``` - -Useful when you want to modify the change before committing. - -## 8. Stashing - -### Save work in progress - -```text -$ git stash push -m "description" -``` - -### Save including untracked files - -```text -$ git stash push -u -m "description" -``` - -### Restore the latest stash - -```text -$ git stash pop # restore and remove from stash -$ git stash apply # restore but keep in stash -``` - -### List and drop stash entries - -```text -$ git stash list # show all entries -$ git stash drop stash@{0} # delete a specific entry -$ git stash clear # delete all entries -``` - -## 9. Tagging - -### Create an annotated tag - -```text -$ git tag -a v1.0.0 -m "First release" -``` - -### Tag a past commit - -```text -$ git tag -a v0.9.0 -m "Beta release" -``` - -### Push tags to remote - -```text -$ git push origin v1.0.0 # single tag -$ git push origin --tags # all tags -``` - -### Delete a tag (local and remote) - -```text -$ git tag -d v1.0.0 # local -$ git push origin --delete v1.0.0 # remote -``` - -## 10. Submodules - -### Add a submodule - -```text -$ git submodule add -$ git commit -m "Add submodule" -``` - -### Clone a repo with submodules - -```text -$ git clone --recurse-submodules -``` - -Or after a regular clone: - -```text -$ git submodule update --init --recursive -``` - -### Update a submodule to latest - -```text -$ git submodule update --remote -$ git add -$ git commit -m "Update submodule" -``` - -### Remove a submodule - -```text -$ git submodule deinit -$ git rm -$ rm -rf .git/modules/ -$ git commit -m "Remove submodule" -``` - -## 11. Debugging - -### Find which commit introduced a bug - -```text -$ git bisect start -$ git bisect bad # current commit has the bug -$ git bisect good # this older commit was fine -# ... test each midpoint, mark good/bad ... -$ git bisect reset # done — return to original branch -``` - -### Automated bisect with a test script - -```text -$ git bisect start HEAD -$ git bisect run ./test.sh -``` - -### See who last changed each line - -```text -$ git blame -``` - -### Search commit messages - -```text -$ git log --grep="fix" --oneline -``` - -### Search file contents across history - -```text -$ git log -S "functionName" --oneline -``` - -## 12. Configuration - -### Set identity - -```text -$ git config --global user.name "Your Name" -$ git config --global user.email "you@example.com" -``` - -### Set default branch name - -```text -$ git config --global init.defaultBranch main -``` - -### Set default pull strategy to rebase - -```text -$ git config --global pull.rebase true -``` - -### Create an alias - -```text -$ git config --global alias.hist "log --oneline --graph --all" -``` - -### See where a setting comes from - -```text -$ git config --list --show-origin -``` diff --git a/chapters/08-appendix.md b/chapters/08-appendix.md index cd0bbc6..685095a 100644 --- a/chapters/08-appendix.md +++ b/chapters/08-appendix.md @@ -9,7 +9,7 @@ order: 8 This appendix contains reference material that supports the tutorial chapters — merge strategies, Git clients, external resources, and -notes. For step-by-step recipes, see the [Playbook](07-playbook.md). +notes. For step-by-step recipes, see the [Playbook](playbook/index.md). For command help, run `git help `. In this chapter you will learn: diff --git a/chapters/09-glossary.md b/chapters/09-glossary.md index 934ed30..d422c77 100644 --- a/chapters/09-glossary.md +++ b/chapters/09-glossary.md @@ -14,7 +14,7 @@ A reference of key Git terms used throughout this tutorial, with links to the ch | Annotated tag | A tag object with author, date, and message — stored in `.git/objects/` | [2](02-building-blocks.md) | | Bare repository | A repository with no working tree — only `.git/` internals | [2](02-building-blocks.md) | | Bisect | Binary search through commit history to find the commit that introduced a bug | [6](06-expert-topics.md) | -| Blame | Show which commit and author last modified each line of a file | [7](07-playbook.md) | +| Blame | Show which commit and author last modified each line of a file | [7](playbook/debugging.md) | | Blob | Object that stores the raw contents of a single file | [2](02-building-blocks.md) | | Branch | A movable pointer to a commit, stored in `.git/refs/heads/` | [2](02-building-blocks.md) | | Cherry-pick | Copy a single commit from one branch onto another, creating a new commit with a different hash | [3](03-branching-and-merging.md) | @@ -47,7 +47,7 @@ A reference of key Git terms used throughout this tutorial, with links to the ch | Remote-tracking branch | A read-only local reference that mirrors a remote branch (e.g. `origin/main`), updated by fetch and pull | [4](04-remote-repositories.md) | | Repository | The `.git/` directory containing all objects, references, and configuration for a project | [2](02-building-blocks.md) | | Reset | Move HEAD and optionally the branch tip to a different commit — `--soft`, `--mixed`, or `--hard` | [2](02-building-blocks.md) | -| Revert | Create a new commit that undoes a previous commit's changes without rewriting history | [7](07-playbook.md) | +| Revert | Create a new commit that undoes a previous commit's changes without rewriting history | [7](playbook/undoing-changes.md) | | Squash merge | Combine all commits from a branch into a single change set on the target branch — no merge commit | [3](03-branching-and-merging.md) | | Stash | Save uncommitted changes temporarily so you can switch branches with a clean working tree | [3](03-branching-and-merging.md) | | Submodule | A reference to a specific commit in another repository — stores URL and hash, not files | [5](05-nested-repositories.md) | diff --git a/chapters/playbook/branching.md b/chapters/playbook/branching.md new file mode 100644 index 0000000..76a663a --- /dev/null +++ b/chapters/playbook/branching.md @@ -0,0 +1,36 @@ +--- +title: "Branching" +description: "Git recipes for creating, deleting, renaming, and inspecting branches." +section: "playbook/branching" +order: 72 +--- + +## Branching + +### Create a branch and switch to it + +```text +$ git switch -c feature/name +``` + +### Delete a branch (local and remote) + +```text +$ git branch -d feature/name # local (safe — only if merged) +$ git branch -D feature/name # local (force — even if unmerged) +$ git push origin --delete feature/name # remote +``` + +### Rename the current branch + +```text +$ git branch -m new-name +``` + +### See which branches are merged into main + +```text +$ git branch --merged main +``` + +Safe to delete any branch listed here (except `main` itself). diff --git a/chapters/playbook/cherry-picking.md b/chapters/playbook/cherry-picking.md new file mode 100644 index 0000000..7165e2e --- /dev/null +++ b/chapters/playbook/cherry-picking.md @@ -0,0 +1,22 @@ +--- +title: "Cherry-Picking" +description: "Git recipes for applying individual commits from another branch — with or without committing." +section: "playbook/cherry-picking" +order: 76 +--- + +## Cherry-Picking + +### Apply a single commit from another branch + +```text +$ git cherry-pick +``` + +### Cherry-pick without committing (stage only) + +```text +$ git cherry-pick --no-commit +``` + +Useful when you want to modify the change before committing. diff --git a/chapters/playbook/configuration.md b/chapters/playbook/configuration.md new file mode 100644 index 0000000..ad2676e --- /dev/null +++ b/chapters/playbook/configuration.md @@ -0,0 +1,39 @@ +--- +title: "Configuration" +description: "Git recipes for setting identity, default branch name, pull strategy, aliases, and diagnosing configuration sources." +section: "playbook/configuration" +order: 81 +--- + +## Configuration + +### Set identity + +```text +$ git config --global user.name "Your Name" +$ git config --global user.email "you@example.com" +``` + +### Set default branch name + +```text +$ git config --global init.defaultBranch main +``` + +### Set default pull strategy to rebase + +```text +$ git config --global pull.rebase true +``` + +### Create an alias + +```text +$ git config --global alias.hist "log --oneline --graph --all" +``` + +### See where a setting comes from + +```text +$ git config --list --show-origin +``` diff --git a/chapters/playbook/debugging.md b/chapters/playbook/debugging.md new file mode 100644 index 0000000..0ad3963 --- /dev/null +++ b/chapters/playbook/debugging.md @@ -0,0 +1,43 @@ +--- +title: "Debugging" +description: "Git recipes for finding bugs with bisect, blaming lines, and searching commit messages and file contents across history." +section: "playbook/debugging" +order: 80 +--- + +## Debugging + +### Find which commit introduced a bug + +```text +$ git bisect start +$ git bisect bad # current commit has the bug +$ git bisect good # this older commit was fine +# ... test each midpoint, mark good/bad ... +$ git bisect reset # done — return to original branch +``` + +### Automated bisect with a test script + +```text +$ git bisect start HEAD +$ git bisect run ./test.sh +``` + +### See who last changed each line + +```text +$ git blame +``` + +### Search commit messages + +```text +$ git log --grep="fix" --oneline +``` + +### Search file contents across history + +```text +$ git log -S "functionName" --oneline +``` diff --git a/chapters/playbook/index.md b/chapters/playbook/index.md new file mode 100644 index 0000000..6a06504 --- /dev/null +++ b/chapters/playbook/index.md @@ -0,0 +1,31 @@ +--- +title: "Playbook" +description: "Step-by-step recipes for common Git tasks — undoing changes, branching, merging, rebasing, stashing, tagging, and debugging." +section: "playbook" +order: 7 +--- + +## Overview + +This chapter is a quick-reference collection of recipes for common +Git tasks. Each recipe shows the problem, the commands to solve it, +and what to watch out for. + +For command syntax, see [Appendix](../08-appendix.md). For definitions, +see [Glossary](../09-glossary.md). + +## Recipes + +| Recipe | What you'll find | +|--------|-----------------| +| [Undoing Changes](undoing-changes.md) | Discard, unstage, reset, revert, and recover lost commits | +| [Branching](branching.md) | Create, delete, rename, and inspect branches | +| [Merging](merging.md) | Fast-forward, no-ff, squash, conflict resolution | +| [Rebasing](rebasing.md) | Linearize history and squash commits interactively | +| [Remote Operations](remote-operations.md) | Push, pull, force push safely, sync forks | +| [Cherry-Picking](cherry-picking.md) | Apply individual commits across branches | +| [Stashing](stashing.md) | Save and restore work in progress | +| [Tagging](tagging.md) | Create, push, and delete annotated tags | +| [Submodules](submodules.md) | Add, clone, update, and remove submodules | +| [Debugging](debugging.md) | Bisect, blame, and search commit history | +| [Configuration](configuration.md) | Identity, defaults, aliases, and diagnostics | diff --git a/chapters/playbook/merging.md b/chapters/playbook/merging.md new file mode 100644 index 0000000..60e5e4a --- /dev/null +++ b/chapters/playbook/merging.md @@ -0,0 +1,45 @@ +--- +title: "Merging" +description: "Git recipes for merging branches — fast-forward, no-ff, squash merges, aborting, and resolving merge conflicts." +section: "playbook/merging" +order: 73 +--- + +## Merging + +### Merge a feature branch into main + +```text +$ git switch main +$ git merge feature/name +``` + +### Force a merge commit (no fast-forward) + +```text +$ git merge --no-ff feature/name +``` + +### Squash a branch into a single commit + +```text +$ git merge --squash feature/name +$ git commit -m "Add feature" +``` + +### Abort a conflicted merge + +```text +$ git merge --abort +``` + +Returns everything to the pre-merge state. + +### Resolve a merge conflict + +```text +$ git status # identify conflicting files +# ... edit each file, remove markers ... +$ git add # stage resolved file +$ git commit # complete the merge +``` diff --git a/chapters/playbook/rebasing.md b/chapters/playbook/rebasing.md new file mode 100644 index 0000000..79b69d5 --- /dev/null +++ b/chapters/playbook/rebasing.md @@ -0,0 +1,35 @@ +--- +title: "Rebasing" +description: "Git recipes for rebasing branches, squashing commits with interactive rebase, and handling rebase conflicts." +section: "playbook/rebasing" +order: 74 +--- + +## Rebasing + +### Rebase a feature branch onto main + +```text +$ git switch feature/name +$ git rebase main +``` + +### Squash commits with interactive rebase + +```text +$ git rebase -i HEAD~3 +# change "pick" to "squash" for commits to combine +``` + +### Abort a conflicted rebase + +```text +$ git rebase --abort +``` + +### Continue after resolving a rebase conflict + +```text +$ git add +$ git rebase --continue +``` diff --git a/chapters/playbook/remote-operations.md b/chapters/playbook/remote-operations.md new file mode 100644 index 0000000..df57db2 --- /dev/null +++ b/chapters/playbook/remote-operations.md @@ -0,0 +1,53 @@ +--- +title: "Remote Operations" +description: "Git recipes for pushing, pulling, force pushing safely, syncing forks, and inspecting remote changes before merging." +section: "playbook/remote-operations" +order: 75 +--- + +## Remote Operations + +### Push a new branch and set up tracking + +```text +$ git push -u origin feature/name +``` + +### Pull with rebase (avoid merge commits) + +```text +$ git pull --rebase origin main +``` + +### Fix a rejected push + +```text +$ git pull origin main # fetch + merge +$ git push origin main # retry +``` + +### Force push safely (after rebase) + +```text +$ git push --force-with-lease origin feature/name +``` + +Never use `--force` on shared branches. + +### Sync a fork with upstream + +```text +$ git fetch upstream +$ git switch main +$ git merge upstream/main +$ git push origin main +``` + +### Fetch and inspect before merging + +```text +$ git fetch origin +$ git log main..origin/main --oneline # what's new on remote +$ git diff main origin/main # line-by-line changes +$ git merge origin/main # integrate when ready +``` diff --git a/chapters/playbook/stashing.md b/chapters/playbook/stashing.md new file mode 100644 index 0000000..bef4635 --- /dev/null +++ b/chapters/playbook/stashing.md @@ -0,0 +1,35 @@ +--- +title: "Stashing" +description: "Git recipes for saving work in progress, stashing untracked files, restoring stashes, and managing stash entries." +section: "playbook/stashing" +order: 77 +--- + +## Stashing + +### Save work in progress + +```text +$ git stash push -m "description" +``` + +### Save including untracked files + +```text +$ git stash push -u -m "description" +``` + +### Restore the latest stash + +```text +$ git stash pop # restore and remove from stash +$ git stash apply # restore but keep in stash +``` + +### List and drop stash entries + +```text +$ git stash list # show all entries +$ git stash drop stash@{0} # delete a specific entry +$ git stash clear # delete all entries +``` diff --git a/chapters/playbook/submodules.md b/chapters/playbook/submodules.md new file mode 100644 index 0000000..d06f7bd --- /dev/null +++ b/chapters/playbook/submodules.md @@ -0,0 +1,44 @@ +--- +title: "Submodules" +description: "Git recipes for adding, cloning, updating, and removing submodules in a repository." +section: "playbook/submodules" +order: 79 +--- + +## Submodules + +### Add a submodule + +```text +$ git submodule add +$ git commit -m "Add submodule" +``` + +### Clone a repo with submodules + +```text +$ git clone --recurse-submodules +``` + +Or after a regular clone: + +```text +$ git submodule update --init --recursive +``` + +### Update a submodule to latest + +```text +$ git submodule update --remote +$ git add +$ git commit -m "Update submodule" +``` + +### Remove a submodule + +```text +$ git submodule deinit +$ git rm +$ rm -rf .git/modules/ +$ git commit -m "Remove submodule" +``` diff --git a/chapters/playbook/tagging.md b/chapters/playbook/tagging.md new file mode 100644 index 0000000..4d48dd1 --- /dev/null +++ b/chapters/playbook/tagging.md @@ -0,0 +1,34 @@ +--- +title: "Tagging" +description: "Git recipes for creating annotated tags, tagging past commits, pushing tags to remote, and deleting tags." +section: "playbook/tagging" +order: 78 +--- + +## Tagging + +### Create an annotated tag + +```text +$ git tag -a v1.0.0 -m "First release" +``` + +### Tag a past commit + +```text +$ git tag -a v0.9.0 -m "Beta release" +``` + +### Push tags to remote + +```text +$ git push origin v1.0.0 # single tag +$ git push origin --tags # all tags +``` + +### Delete a tag (local and remote) + +```text +$ git tag -d v1.0.0 # local +$ git push origin --delete v1.0.0 # remote +``` diff --git a/chapters/playbook/undoing-changes.md b/chapters/playbook/undoing-changes.md new file mode 100644 index 0000000..fd532aa --- /dev/null +++ b/chapters/playbook/undoing-changes.md @@ -0,0 +1,71 @@ +--- +title: "Undoing Changes" +description: "Git recipes for discarding edits, unstaging files, resetting commits, reverting safely, and recovering lost work with reflog." +section: "playbook/undoing-changes" +order: 71 +--- + +## Undoing Changes + +### Discard unstaged changes to a file + +```text +$ git restore +``` + +Reverts the file in your working tree to match the last commit. +Uncommitted edits are lost. + +### Unstage a file without losing changes + +```text +$ git restore --staged +``` + +Removes the file from the index but keeps your edits in the working +tree. + +### Undo the last commit but keep changes staged + +```text +$ git reset --soft HEAD~1 +``` + +The commit is removed but your changes stay in the index, ready to +recommit. + +### Undo the last commit and unstage changes + +```text +$ git reset HEAD~1 +``` + +The default (`--mixed`). Changes go back to the working tree as +unstaged edits. + +### Undo the last commit and discard all changes + +```text +$ git reset --hard HEAD~1 +``` + +The commit and all changes are gone. Recover with `git reflog` if +needed. + +### Revert a commit without rewriting history + +```text +$ git revert +``` + +Creates a new commit that undoes the changes. Safe to use on shared +branches. + +### Recover a lost commit + +```text +$ git reflog # find the hash +$ git branch recovered # or: git reset --hard +``` + +Works as long as the reflog entry has not expired (default: 30 days).