Everything from the map above, on one page. Dashed topics are optional. The hours are rough estimates for reading plus hands-on practice.
Stage 1 · Beginner Git on your own
Learn Git in a folder on your own computer first, with no GitHub and no teammates. Once commits, the staging area and undoing mistakes feel natural, everything else is easier.
Version control and setting up Git
Version control records every change to your files, who made it and why, so you can go back to any earlier version, compare versions and work on several things at once without losing anything. Git is the version control system almost everyone uses today. It is distributed: every copy of a project (a repository) holds the full history, so you can commit, browse history and create branches offline.
Git and GitHub are different things. Git is the free program on your computer. GitHub is a website that hosts Git repositories and adds collaboration features such as pull requests, issues and Actions. GitLab and Bitbucket are similar services.
What to learn
- What version control solves, and Git vs GitHub
- Install Git and check it with
git --version
- Set your name and email:
git config --global user.name / user.email
- Make
main the default branch name: git config --global init.defaultBranch main
- Choose an editor for commit messages (
core.editor) and know where the config lives (git config --list --show-origin)
Free resources
Practice
Install Git, set your name, email and default branch, then run git config --global --list and check that all three are there.
Repositories, the staging area and commits
git init turns a folder into a repository. Git then sees three places: your working directory (the files you edit), the staging area or index (the changes you've picked for the next commit, with git add) and the history (the commits). A commit is a snapshot of the whole project with a message, an author and a unique ID: a SHA-1 hash such as 3f9c2a1.
The staging area lets you split work into small, focused commits: fix a bug and rename a variable in the same session, but commit them separately. git status tells you where every change is; git diff shows unstaged changes and git diff --staged what is about to be committed. Write messages that say why as well as what: a short summary line of about 50 characters, a blank line, then details.
What to learn
git init, git status, git add (including git add -p for parts of a file) and git commit
- The working directory, staging area and repository, and how a file moves between them
git diff vs git diff --staged, and git log --oneline --graph
git rm and git mv, and what Git does when you delete or rename a file yourself
- Good commit messages: a short imperative summary ("Fix login timeout"), then the why
Free resources
Practice
Put a small project (or your notes) under Git. Make at least ten commits over a few days, using git add -p at least once to commit only part of a file. Read your history back with git log --oneline.
.gitignore: keeping files out of Git
Some files must never be committed: dependencies such as node_modules/, build output, editor settings, log files and, above all, secrets such as .env files and private keys. A .gitignore file lists patterns Git should ignore. Once a secret is pushed it is in the history for good and must be treated as leaked: rotate it, don't just delete the file.
.gitignore only affects files that aren't tracked yet. If a file is already committed, stop tracking it with git rm --cached. A global ignore file (core.excludesFile) is the right place for things specific to your own machine, such as .DS_Store.
What to learn
- Patterns:
*.log, build/, /config.local, **/temp and negation with !
- Stop tracking a committed file with
git rm --cached
- A global ignore file for your OS and editor
- Debug a rule with
git check-ignore -v
- Why a pushed secret must be rotated, not just removed
Free resources
Practice
Add a .gitignore to your project from the github/gitignore template for its language, plus a line for .env. Create a .env file and confirm git status doesn't show it.
Undoing things: restore, reset, revert and reflog
Almost nothing in Git is truly lost once it has been committed. Which command undoes a mistake depends on where the mistake is. git restore file throws away uncommitted edits (this one is permanent). git restore --staged file unstages. git commit --amend fixes the last commit's message or adds a forgotten file. git reset moves your branch back to an earlier commit: --soft keeps the changes staged, --mixed (the default) keeps them unstaged and --hard throws them away.
Rewriting history is fine on commits you haven't shared. For commits that are already pushed, use git revert, which adds a new commit that undoes an old one without rewriting anything. And if you reset too far, git reflog lists everywhere your branch has pointed recently, so you can get back to it.
What to learn
git restore and git restore --staged (the modern versions of git checkout -- file and git reset HEAD file)
git commit --amend for the last commit
git reset --soft / --mixed / --hard, and when each is safe
git revert for commits that are already shared
- Recover "lost" commits with
git reflog
Free resources
Practice
In a throwaway repository, make three commits, run git reset --hard HEAD~2, then bring the two "lost" commits back using git reflog. Then undo a commit with git revert and compare the two histories.
Am I ready? Stage 1 quiz
You edited app.js and ran git add app.js. Which command shows exactly what will go into the next commit?
git diffgit diff --stagedgit log -pgit status --short
Plain git diff shows changes that are not staged yet. --staged compares the staging area with the last commit.
You committed config.local last week and have now added it to .gitignore, but Git still tracks it. Why?
- .gitignore only works after a push
- The pattern needs a leading
** - .gitignore must be in your home folder
- .gitignore only affects untracked files; run
git rm --cached config.local
Ignore rules never apply to files Git already tracks. Untrack the file first and it will stay ignored from then on.
A commit with a bug is already pushed and teammates have pulled it. What is the safe way to undo it?
git revert <commit>git reset --hard HEAD~1 and force-pushgit commit --amend- Delete the repository and clone it again
Revert adds a new commit that undoes the old one, so nobody else's history is rewritten.
Which of these really cannot be undone with Git?
git reset --hard to an older commit- Deleting a branch that was merged
git restore file on edits that were never committed or stagedgit commit --amend
Committed work can be found again with the reflog, but Git never saw edits that were not committed or staged, so it can't bring them back.
What does git reset --soft HEAD~1 do?
- Deletes the last commit and its changes
- Undoes the last commit but keeps its changes staged
- Creates a revert commit
- Unstages every file
--soft moves the branch back one commit and leaves everything staged, which is handy for redoing a commit.
Stage 2 · Branches and history Branches, merging and rebasing
Branches are what make Git powerful: try ideas, fix bugs and build features side by side, then combine them. Use the visual tutorial at Learn Git Branching alongside this stage.
Branches and HEAD
A branch is just a movable label that points at a commit, which is why creating one is instant. HEAD is the branch you are on. When you commit, the current branch moves forward to the new commit and the others stay where they are. Work on a new feature in its own branch so main always stays working.
git switch -c feature-login creates a branch and moves to it, and git switch main goes back. (git checkout does both too, but it also restores files, which is why the clearer switch and restore were added in Git 2.23.) If you check out a commit instead of a branch you are in "detached HEAD" state: fine for looking around, but create a branch before committing.
What to learn
- A branch is a pointer to a commit; HEAD points to the current branch
git branch, git switch, git switch -c and git branch -d / -D
- Detached HEAD: what it is and how to keep work done there
- Branch naming conventions such as
feature/…, fix/…
Free resources
Practice
Finish the "Introduction Sequence" levels of Learn Git Branching. Then, in your own repository, build a small feature on a branch while fixing a typo on main, switching between them.
Merging and resolving conflicts
git merge feature brings another branch's work into the current one. If your branch hasn't moved since the feature started, Git simply moves it forward: a fast-forward. Otherwise it creates a merge commit with two parents. When both branches changed the same lines, you get a merge conflict: Git marks the clash with <<<<<<<, ======= and >>>>>>> and waits for you to decide.
Conflicts are normal, not an error. Read both sides, edit the file into the version you want, remove the markers, git add it and finish with git commit (or git merge --continue). If it goes wrong, git merge --abort puts everything back. Small, frequent merges mean small conflicts.
What to learn
- Fast-forward merges vs merge commits (and
--no-ff)
- Read conflict markers and resolve a conflict by hand
git merge --abort and git merge --continue
- Use a merge tool or your editor's conflict view
- Why long-lived branches cause big conflicts
Free resources
Practice
Create a conflict on purpose: change the same line differently on two branches, merge them, resolve it, and look at the result with git log --oneline --graph.
Rebasing and interactive rebase
git rebase main takes the commits on your branch and replays them on top of the latest main, as if you had started your work today. The result is a straight, easy-to-read history with no merge commit. Interactive rebase (git rebase -i HEAD~5) goes further: reorder, reword, squash or drop commits to tidy a branch before others review it.
Rebasing creates new commits with new IDs. The golden rule: don't rebase commits that other people have already based work on, such as a shared main. Rebasing your own feature branch is fine; afterwards you need git push --force-with-lease, which refuses to overwrite work you haven't seen. To avoid surprise merge commits when pulling, many people set pull.rebase = true.
What to learn
- Rebase vs merge: what each does to history, and when to use which
- Resolve conflicts during a rebase (
--continue, --skip, --abort)
- Interactive rebase:
pick, reword, squash, fixup, drop
- The golden rule, and
--force-with-lease instead of --force
git pull --rebase and the pull.rebase setting
Free resources
Practice
Make five messy commits on a branch ("wip", "fix typo", "really fix"), then use git rebase -i to turn them into two clean commits with good messages and rebase the branch onto main.
Stash, cherry-pick and worktrees
Halfway through a change and need to fix something urgent on another branch? git stash puts your uncommitted work aside and git stash pop brings it back. git cherry-pick <commit> copies a single commit onto the current branch, for example to bring one bug fix to a release branch without merging everything.
git worktree add checks out a second branch in another folder from the same repository, so you can review a pull request or run a long build without stashing at all.
What to learn
git stash, stash list, stash pop and stash apply, including untracked files (-u)
git cherry-pick one commit or a range, and handling its conflicts
git worktree add / list / remove
git clean -n before git clean -f to remove untracked files
Free resources
Practice
Start a change, stash it, make a fix on main, then pop the stash. Then cherry-pick that fix onto another branch.
Searching history: log, blame and bisect
Git history answers questions such as "why is this line here?" and "when did this break?". git log filters by author, date, file or message, and git log -S "text" finds the commit that added or removed some text. git blame shows who last changed each line and in which commit, so you can read the reason in the commit message.
git bisect finds the commit that introduced a bug by binary search: mark one good and one bad commit, test the commit Git picks, and repeat. Even across 1,000 commits it takes about ten steps, and git bisect run can automate the test.
What to learn
git log filters: --author, --since, -- path, --grep, -S
git show for one commit, and git log -p
git blame (and ignoring formatting commits with --ignore-rev)
git bisect start / good / bad / reset and git bisect run
git grep to search the code at any commit
Free resources
Practice
Clone a popular open-source project, pick a strange-looking line and use git blame and git show to find out why it was written. Then use git bisect to find a commit you broke on purpose in your own repository.
Am I ready? Stage 2 quiz
What is a Git branch, technically?
- A full copy of the project files
- A separate repository
- A movable pointer to a commit
- A folder inside
.git with the changed files
A branch is just a reference to a commit, which is why creating and switching branches is instant.
You merge feature into main, and main has no new commits since feature was created. What happens?
- A fast-forward:
main just moves to the feature's last commit - A merge commit is always created
- Git asks you to rebase first
- A conflict, because both branches exist
With nothing to combine, Git moves the branch pointer forward. Use --no-ff if you want a merge commit anyway.
Which branch should you not rebase?
- Your own feature branch nobody else uses
- A local branch you haven't pushed
- A branch you want to tidy before review
- The shared
main branch other people pull from
Rebasing rewrites commits with new IDs. Doing that to a shared branch breaks everyone else's copy.
After rebasing your already-pushed feature branch, the push is rejected. What's the safest fix?
git push --forcegit push --force-with-lease- Merge
origin/feature back in - Delete the remote branch first
--force-with-lease only overwrites the remote branch if it hasn't changed since you last fetched, so it can't wipe out a teammate's new commits.
A bug appeared somewhere in the last 400 commits. Which tool finds the culprit fastest?
git blamegit log -pgit bisectgit stash
Bisect halves the range each step, so 400 commits need only about nine tests.
Stage 3 · Intermediate Collaborating on GitHub
Share your repository, work with other people and get your changes reviewed. This is the day-to-day workflow in almost every software team.
Remotes, push and pull, SSH keys and tokens
A remote is another copy of the repository, usually on GitHub, named origin by default. git clone copies a repository and sets up origin. git fetch downloads new commits without changing your files, git pull fetches and then merges (or rebases), and git push uploads your commits. Branches such as origin/main are your local record of where the remote's branches were at the last fetch.
GitHub doesn't accept your account password for Git. Connect with an SSH key (create one with ssh-keygen -t ed25519 and add the .pub file to your account) or over HTTPS with a personal access token. Prefer fine-grained tokens, limited to specific repositories and permissions with an expiry date, or let the GitHub CLI (gh auth login) handle it.
What to learn
git clone, git remote -v and git remote add
git fetch vs git pull, and remote-tracking branches such as origin/main
git push -u origin branch and upstream branches
- Create an ed25519 SSH key, add it to GitHub and test with
ssh -T git@github.com
- Fine-grained personal access tokens and the GitHub CLI
Free resources
Practice
Set up an SSH key, create an empty repository on GitHub and push your Stage 1 project to it. Clone it into a second folder, commit there, push, and git pull the change back into the first folder.
GitHub repositories, issues, Markdown and Projects
A GitHub repository is more than code. A README.md is the front page that explains what the project is and how to use it. Issues track bugs and ideas, with labels, assignees and milestones, and a commit or pull request that says "Fixes #12" closes issue 12 when it is merged. Projects turn issues into a board or table for planning, and Discussions is a forum for questions.
Almost everything on GitHub is written in Markdown, including task lists, tables, code blocks and mentions. Public repositories also need a licence to be reusable by others, and many add CONTRIBUTING and CODE_OF_CONDUCT files.
What to learn
- Write a useful README in Markdown (headings, lists, code blocks, links, images)
- Issues: templates, labels, assignees, milestones, and closing keywords such as
Fixes #12
- GitHub Projects boards and tables
- Public vs private repositories, licences, forks, stars and watching
Free resources
Practice
Give your project a README with a screenshot and setup steps, a licence, and three issues on a Projects board. Close one of them from a commit message.
Pull requests and code review
A pull request (PR) asks to merge one branch into another, and is where a team discusses and reviews a change before it lands. Push a branch, open a PR with a clear description of what and why, and reviewers comment on lines, suggest edits, approve or request changes. In open-source projects you fork the repository first, push to your fork and open the PR from there.
GitHub offers three ways to merge a PR: a merge commit (keeps every commit plus a merge commit), squash and merge (one tidy commit per PR) and rebase and merge (replays the commits with no merge commit). Small PRs get faster and better reviews. Mark unfinished work as a draft.
What to learn
- Open a PR: branch, push, description, linked issue, reviewers
- Review a PR: line comments, suggested changes, approve vs request changes
- Update a PR after feedback, and keep it up to date with
main
- Merge commit vs squash and merge vs rebase and merge
- Fork-based contributions to open source, and draft PRs
Free resources
Practice
Make a first open-source contribution: fix a typo in the docs of a project you use, through a fork and a pull request. Or ask a friend to review a PR on your own project and address their comments.
Branch protection, rulesets, CODEOWNERS and signed commits
On a team, nobody should be able to push straight to main or force-push over it by accident. Rulesets (and the older branch protection rules) can require a pull request, a number of approvals, passing status checks such as tests, a linear history or signed commits before anything is merged. A CODEOWNERS file automatically requests reviews from the right people for each part of the code.
Git lets anyone write any name and email into a commit, so GitHub can show a Verified badge only for commits signed with a GPG, SSH or S/MIME key that belongs to the account. Signing with your existing SSH key is the simplest: git config gpg.format ssh plus commit.gpgsign true.
What to learn
- Create a ruleset for
main: require a PR, one approval and passing checks; block force-pushes and deletion
- Rulesets vs classic branch protection rules
- A
CODEOWNERS file and required code owner reviews
- Sign commits with an SSH key and get the Verified badge
Free resources
Practice
Protect main on your repository with a ruleset, then try to push to it directly and watch it get rejected. Set up SSH commit signing and check that your next commit shows as Verified.
Team workflows: GitHub flow, trunk-based and Git flow
A branching strategy is a team agreement about where work happens and how it reaches production. GitHub flow is the common default: main is always deployable, every change is a short-lived branch and a PR, and merging means deploying. Trunk-based development goes further, with very small changes merged at least daily and unfinished features hidden behind feature flags, and is what continuous delivery teams tend to use.
Git flow adds long-lived develop, release and hotfix branches. It suits software shipped in numbered versions, such as mobile apps or libraries, but is heavy for web services that deploy many times a day. Whichever you pick, write it down in CONTRIBUTING.md.
What to learn
- GitHub flow step by step
- Trunk-based development and feature flags
- Git flow: develop, release and hotfix branches, and when it fits
- Keeping branches short-lived to avoid painful merges
Free resources
Practice
Write a short CONTRIBUTING.md for your project that describes its workflow: branch names, PR rules, how to merge and how releases happen.
Am I ready? Stage 3 quiz
What is the difference between git fetch and git pull?
- There is none
fetch only downloads new commits; pull also merges or rebases them into your branchfetch uploads, pull downloadspull only works over SSH
Fetch updates the remote-tracking branches such as origin/main without touching your work. Pull is fetch plus a merge or rebase.
Git asks for a password when you push over HTTPS to GitHub. What should you enter?
- Your GitHub account password
- Your SSH private key
- Your computer's password
- A personal access token (or sign in through the GitHub CLI or a credential manager)
GitHub doesn't accept account passwords for Git operations. Use a token, preferably fine-grained with an expiry date.
Which merge method puts all of a pull request's commits into one single commit on main?
- Squash and merge
- Create a merge commit
- Rebase and merge
- Fast-forward only
Squash and merge combines the PR into one commit, which keeps main's history to one tidy commit per change.
How do you stop people from pushing directly to main and require one approval and passing tests?
- A
.gitignore rule - A
CODEOWNERS file alone - A ruleset (or branch protection rule) on
main - A pre-commit hook
Rulesets are enforced by GitHub for everyone. Local hooks can be skipped, and CODEOWNERS only requests reviewers.
Which workflow keeps main always deployable, with every change made in a short-lived branch and merged through a pull request?
- Git flow
- GitHub flow
- Forking only
- Release trains
GitHub flow is the simple default for web projects: branch, PR, review, merge, deploy.
Stage 4 · Advanced Automation and advanced Git
Let GitHub test, build and release your code for you, keep the repository secure, and understand what Git does under the hood. This is where Git meets DevOps: the DevOps roadmap continues from here.
GitHub Actions: CI on every push
GitHub Actions runs automated jobs when something happens in your repository: a push, a pull request, a schedule or a button press. A workflow is a YAML file in .github/workflows/. It contains jobs, which run on fresh virtual machines (runners), and each job is a list of steps: shell commands or reusable actions such as actions/checkout. The usual first workflow is continuous integration: install, lint, build and test every PR, and require it to pass before merging.
Store passwords and API keys as encrypted secrets, never in the YAML. Give the built-in GITHUB_TOKEN only the permissions a job needs, and pin third-party actions to a version (or a commit SHA for maximum safety). Public repositories get Actions for free on standard runners; private repositories get a monthly allowance of minutes.
What to learn
- Workflow syntax:
on, jobs, runs-on, steps, uses and run
- Triggers:
push, pull_request, schedule (cron) and workflow_dispatch
- A version matrix, caching dependencies and uploading artifacts
- Secrets, variables and least-privilege
permissions for GITHUB_TOKEN
- Make the CI check required in your ruleset
Free resources
Practice
Add a CI workflow to your project that runs its tests on every pull request, add a status badge to the README, and make the check required on main.
Tags, releases and semantic versioning
A tag gives a permanent name to a commit, usually a version such as v1.4.0. Use annotated tags (git tag -a v1.4.0 -m "…") for releases, and remember that git push doesn't send tags unless you ask (git push origin v1.4.0). A GitHub release adds release notes and downloadable files to a tag.
Semantic versioning (MAJOR.MINOR.PATCH) tells users what an update means: PATCH for bug fixes, MINOR for new features that don't break anything, MAJOR for breaking changes. Conventional Commits messages (feat:, fix:, feat!:) let tools choose the next version and write the changelog automatically.
What to learn
- Lightweight vs annotated tags, and pushing and deleting tags
- Semantic versioning rules, including pre-releases (
2.0.0-beta.1)
- GitHub releases and automatically generated release notes
- Conventional Commits and a CHANGELOG
Free resources
Practice
Tag your project v1.0.0, publish a GitHub release with generated notes, then make one fix: and one feat: commit and decide what the next version number should be.
Repository security: secrets, Dependabot and scanning
Leaked secrets are one of the most common security incidents, and bots scan public repositories for keys within minutes of a push. GitHub's secret scanning and push protection block pushes that contain known token formats. Dependabot warns about dependencies with known vulnerabilities and opens PRs to update them, and code scanning finds security bugs in your own code.
Turn on two-factor authentication for your account, review who has access to each repository, and add a SECURITY.md that says how to report vulnerabilities privately. If a secret does leak, revoke it first, then clean up.
What to learn
- Two-factor authentication and access levels (read, triage, write, maintain, admin)
- Secret scanning and push protection
- Dependabot alerts, security updates and version updates (
dependabot.yml)
- Code scanning and SECURITY.md
- What to do when a secret has been pushed
Free resources
Practice
Turn on Dependabot alerts and version updates, secret scanning and push protection for your repository, then try to push a fake key in the format of a real token and watch it get blocked.
Large repositories: LFS, submodules and monorepos
Git stores every version of every file, so big binary files such as videos, design files or datasets make a repository slow to clone. Git LFS keeps them on a separate server and stores small pointer files in Git instead. Submodules embed one repository inside another at a fixed commit, which is useful but easy to get wrong, so many teams prefer package managers.
In a monorepo many projects live in one repository. Shallow clones (--depth 1), partial clones and git sparse-checkout keep huge repositories manageable.
What to learn
- Git LFS:
git lfs track, .gitattributes, and storage quotas
- Submodules: add, clone with
--recurse-submodules, update
- Monorepo vs many repositories: the trade-offs
- Shallow clones, partial clones and sparse checkout
Free resources
Practice
Create a repository with an image tracked by Git LFS and check what is actually stored in Git with git show HEAD:image.png. Then add a submodule and clone the repository fresh with its submodule.
Git internals: objects, refs and the .git folder
Under the hood Git is a simple key-value store. Every file version is a blob, every folder a tree, and every commit an object pointing to a tree and its parent commits, each named by the hash of its content. Branches and tags are just files in .git/refs that hold a commit hash. Once you see this, commands like reset, rebase and cherry-pick stop being magic.
What to learn
- Blobs, trees, commits and annotated tag objects
- Explore objects with
git cat-file -p and git ls-tree
- Refs, HEAD and packed refs inside
.git
- Packfiles and garbage collection (
git gc)
- Git's move from SHA-1 towards SHA-256 object names
Free resources
Practice
Make one commit in a new repository, then follow it by hand with git cat-file -p: from HEAD to the branch file, to the commit, to its tree, to a blob.
Git hooks and pre-commit checks
Hooks are scripts Git runs at certain moments, such as pre-commit before a commit is created or commit-msg to check the message. They are handy for running a formatter, a linter or a secret scanner before code ever leaves your machine. Hooks aren't copied when someone clones, so teams share them with a tool such as the pre-commit framework or Husky, and still run the same checks in CI, because local hooks can be skipped.
What to learn
- Client-side hooks in
.git/hooks: pre-commit, commit-msg, pre-push
- Share hooks with the pre-commit framework (or Husky for JavaScript projects)
- Run a formatter and a secret scanner before each commit
- Why CI must repeat the checks (
--no-verify skips hooks)
Free resources
Practice
Add a pre-commit configuration that trims trailing whitespace and checks YAML files, and a commit-msg hook that rejects messages shorter than ten characters.
Am I ready? Stage 4 quiz
Where do GitHub Actions workflow files live?
- In the repository settings, not in Git
.git/workflows/.github/workflows/ as YAML files- A file called
Actionsfile in the root
Workflows are YAML files committed in .github/workflows/, so they are versioned and reviewed like any other code.
Your workflow needs an API key to deploy. Where should it go?
- In an encrypted repository or environment secret, read as
${{ secrets.API_KEY }} - In the workflow YAML
- In a committed
.env file - In the README, for the team to see
Secrets are encrypted and hidden in logs. Anything committed is visible to everyone who can read the repository and stays in its history.
Under semantic versioning, your library is at 2.4.1 and you remove a public function. What is the next version?
2.4.22.5.02.4.1-13.0.0
Removing something users depend on is a breaking change, so the MAJOR number goes up.
You pushed a commit and forgot to push its tag v1.2.0. Why isn't the tag on GitHub?
- Tags are only for private repositories
git push doesn't send tags unless you push them explicitly (for example git push origin v1.2.0)- Tags must be created on GitHub
- The tag needs to be signed
Tags are pushed separately, one at a time or with --tags / --follow-tags.
An AWS key was pushed to a public repository an hour ago and then deleted in a new commit. What should you do first?
- Nothing, it's deleted now
- Make the repository private
- Revoke and replace the key, then clean the history if needed
- Force-push to hide the commit
The key is still in the history and has probably already been scraped. Revoking it is the only thing that makes it harmless.