A plain-English handbook that takes you from your first commit to team workflows. Every idea comes with a diagram, a worked example and a practice task.
parts6levelbeginner to advancedlanguageEnglishchapters44
You need no prior knowledge. If you can open a terminal and type a command, you can follow along.
Read the parts in order the first time, because each one builds on the last. Every chapter follows the same seven steps, so you always know where to look.
ConceptThe idea in two or three sentences
AnalogyA real-life comparison
DiagramWhat happens inside the repo
CommandsEach command with sample output
MistakesWhat goes wrong and the fix
RecapA one-minute revision list
ExerciseA task to try yourself
Conventions
In a dark code block, a line that starts with $ is a command you type. The $ itself is not part of the command. Lines without it are the output Git prints back. The copy button copies only the commands.
$ git status
On branch main
nothing to commit, working tree clean
Words in <angle brackets> are placeholders. In git checkout <branch-name> you type the real branch name and leave out the brackets. Tick the boxes in each exercise to track progress; the sidebar shows a check beside a finished chapter.
If you are taking the Git Learning Portal
This handbook is the textbook. The stage guide is the worksheet: what to click, what to paste, and what to do when a check fails.
Readthe chapters listed on the stage page
Startan issue, choose the stage, wait for the bot comment
Workin GitHub Codespaces in the browser. You do not install Git for stage 1
Submita second issue. If a box is empty, follow the fail link in the reply
Important
Create git-lab from the template in the browser. Do not run git init or mkdir on your computer for the course. Chapter 6 still teaches git init so you know what a repository is. If a screen looks wrong, open Screens you will see.
To get a PDF copy, use the Save as PDF button. If your browser blocks it inside a preview, open this page in its own tab, press Ctrl+P (Cmd+P on a Mac) and choose Save as PDF. The print layout is set up for A4.
1
Foundation
Why version control exists, where Git came from, how Git and GitHub relate, and a working Git setup on your machine.
Part 1Chapter 16 min read
What is version control?
Version control records every change made to a set of files, so you can go back to any earlier state, see who changed what, and work with other people without overwriting their work.
Nobody knows which file is the latest, what changed between them, or who changed it. Now imagine 500 source files and 10 developers. Three things go wrong:
Lost work. Someone overwrites a file and the earlier version is gone for good.
No history. A bug appears and nobody can tell which change caused it.
Collision. Two people edit the same file and one person's changes silently disappear.
A version control system (VCS) fixes all three. It keeps every version, labels each change with its author, date and reason, and helps merge work from many people.
1.2 Analogy: a time machine for your project
Think of a video game with save points. Before a hard level you save. If you fail, you reload and try again. Each commit in Git is a save point with a name and a note, and you can return to any save point whenever you like.
Each commit is a save point. HEAD marks the one you are standing on.
It is also like Google Docs version history, with more control: you decide when a save point is made, you write a note for it, and it works for any type of file.
1.3 Three generations of version control
Generation
How it works
Examples
Main weakness
Local
The version database lives on one computer
RCS
No collaboration; a disk failure loses everything
Centralized
One server holds the history; developers check files out of it
CVS, Subversion
If the server is down nobody can save history; it is a single point of failure
Distributed
Every developer has a full copy of the whole history
Git, Mercurial
A few more concepts to learn at the start
If the central server dies, the project is at risk. In Git, any developer's copy can restore it.
When you clone a Git repository you get the complete history, not just the latest files. That is why most Git commands work without internet.
1.4 Key terms you will see everywhere
Term
Plain meaning
Repository (repo)
A project folder together with its full recorded history
Commit
One saved snapshot of your project, with a message and an author
Branch
A separate line of work that does not disturb the main line
Merge
Combining the work of two branches into one
Remote
A copy of the repository on another machine or server, such as GitHub
Clone
Downloading a full copy of a remote repository
Note
Do not memorise these now. Each term gets its own chapter with examples.
1.5 Why teams cannot work without it
Collaboration: ten people can work on one codebase at the same time.
Traceability: every line can be traced to a person, a date and a reason.
Safety: a mistake is never permanent, because you can return to an earlier commit.
Experimentation: try a risky idea on a branch and throw it away if it fails.
Release management: mark exactly which version went to production.
1.6 Common mistakes
Warning
Copying folders as backups. A folder copy has no notes, no author and no way to merge. Use commits instead.
Thinking version control is only for code. It works for documents, configuration and notebooks too. Very large binary files need extra tools (Git LFS, in Part 5).
Waiting too long to start. Track the project from day one, not after a problem appears.
Quick recap
Version control records every change to your files.
Local, centralized and distributed are the three generations.
Git is distributed: every clone holds the full history.
A commit is a save point; a repository is the project plus its history.
Practice exercise
Think of a project from the past year and write down three moments when you wished you could go back.
For each moment, note what information (who, when, why) would have helped most.
Keep the list. Git records exactly that information, as Part 2 shows.
Git was created in April 2005 by Linus Torvalds, the creator of Linux, because the Linux kernel project suddenly lost the version control tool it depended on.
2.1 Before Git: the BitKeeper story
The Linux kernel is one of the largest collaborative software projects in the world. Until 2002 its changes were passed around as patches and archives, which did not scale. From 2002 the kernel team used a proprietary distributed tool called BitKeeper, offered to the project free of charge.
In April 2005 the relationship broke down. The company behind BitKeeper withdrew the free licence after a dispute about reverse-engineering its protocol. The kernel developers now had thousands of contributors and no tool.
2.2 A tool built in days
Torvalds found no existing tool that met his goals, so he wrote a new one. His requirements:
Speed: applying a patch should take seconds, even for a huge project.
Simple design: a small, understandable core.
Parallel work: thousands of branches that merge easily.
Fully distributed: no single central server required.
Integrity: impossible to change history without anyone noticing.
A first version worked within days, and the kernel began using Git for its own development within about two weeks. The name "git" is British slang for an unpleasant person; Torvalds has joked that he names his projects after himself. Junio Hamano has maintained Git since July 2005, and Git 1.0 was released in December 2005.
2.3 Timeline of milestones
Grey: earlier tools. Orange: Git. Purple: hosting platforms. Green: the move from master to main. Early years are approximate.
2.4 Why Git won
It is fast, because most operations happen on your own machine.
Branching is cheap, so teams create branches freely.
It stores snapshots and checks content with hashes, so history is trustworthy.
It is free and open source.
Websites like GitHub made sharing and reviewing code easy for everyone.
2.5 Common mistakes
Confusing Git with GitHub. They are different things; Chapter 3 explains how.
Assuming Git is only for Linux. It runs on Windows, macOS and Linux.
Quick recap
Git was created in 2005 by Linus Torvalds after BitKeeper's free licence ended.
Its goals: speed, simple design, parallel work, distribution and integrity.
Junio Hamano has maintained Git since 2005.
GitHub arrived in 2008 and made Git popular far beyond the Linux community.
Practice exercise
Open a terminal and run git --version.
Write down what you see. "command not found" is fine; you install Git in Chapter 4.
Git is the tool that tracks your changes on your computer. GitHub, GitLab and Bitbucket are websites that host Git repositories online so teams can share and review them.
3.1 Analogy: the engine and the garage
Git is a car engine: it does the real work and runs on your own machine. GitHub is a big garage and showroom. It stores your car (the repository), lets other people see it, lets mechanics suggest improvements, and offers extra services such as testing. You can run the engine without the garage, but the garage is useless without the engine. Likewise, email is a technology and Gmail is a service built on it.
Git works on the left with no internet. The hosting service on the right is where you share.
3.2 What Git does and what GitHub adds
Question
Git
GitHub
What is it?
A free, open-source command-line tool
A website and cloud service owned by Microsoft
Where does it run?
On your own computer
On the internet
Needs internet?
Not for most commands
Yes
Main job
Track changes, branch, merge
Host repositories, enable collaboration and review
All three use Git underneath, so the commands you learn work on each of them. This handbook uses GitHub for online examples because it is the most widely used and has a generous free plan.
Platform
Known for
Typical users
GitHub
Largest community, open source, GitHub Actions, Pages
Individuals, open-source projects, companies of all sizes
GitLab
Built-in CI/CD and DevOps tools; can be self-hosted
Teams that want one integrated DevOps platform
Bitbucket
Tight integration with Jira and other Atlassian tools
Teams already using Atlassian products
3.4 Common mistakes
Saying "I pushed to Git". You push to a remote such as GitHub. Git is the tool, GitHub is the place.
Treating GitHub as a guaranteed backup. It is hosting. Keep important repositories cloned in more than one place.
Caution
Check the visibility setting every time you create a repository. A private project made public by accident is hard to take back.
Quick recap
Git is a local version control tool; GitHub is an online service built around it.
GitLab and Bitbucket are alternatives and also use Git.
Git commands are the same on every platform.
Practice exercise
Create a free account at github.com with a professional username. It appears in your profile address and every contribution.
Installing Git takes about five minutes on any operating system, and you can confirm it worked by running git --version.
Course
If you are taking the Git Learning Portal, skip this chapter for stage 1. Work in GitHub Codespaces from the Start comment. Git is already there and signed in. Come back here only if you want Git on your own computer. Stage 1 guide ยท Open stage 1
4.1 Pick your operating system
Tip
The tabs below try to open on your own operating system. Choose another tab if you set up a different machine.
Download the installer from the official website, git-scm.com.
Run it. The defaults are fine for beginners, but check these screens:
Default editor: choose Visual Studio Code if you have it.
Initial branch name: choose "Override the default branch name" and enter main.
PATH environment: keep "Git from the command line and also from 3rd-party software".
Finish, then open Git Bash from the Start menu. Git CMD is the same Git program in a different window, not a second course.
Or use one command in PowerShell:
$ winget install --id Git.Git -e --source winget
Let macOS install the command line tools. If Git is missing, a window offers to install it.
Your version number will probably differ, and that is fine. Any reasonably recent version works with this handbook.
4.3 Common mistakes
Terminal opened before installing. It does not see new programs until you close it and open a new one.
"git is not recognized" on Windows. The PATH option was skipped. Run the installer again and choose the recommended PATH option.
Opening Git CMD because the Start issue appeared. The issue page is waiting for a bot comment. Use Codespaces from that comment. You do not need Git CMD for the course.
Important
Download Git only from git-scm.com or your system's package manager, never from an unofficial site.
Quick recap
Windows: installer from git-scm.com, or winget.
macOS: git --version triggers installation, or use Homebrew.
Linux: install the git package.
Always confirm with git --version.
Practice exercise
If you use Codespaces, open a terminal there and run git --version. If you use your own computer, install Git first, then run it.
Run git help and read the list of common commands. You meet most of them in Part 2.
Before your first commit you must tell Git who you are, because every commit records its author's name and email.
Course
Codespaces already uses your GitHub name and email. Skip the commands in this chapter until you install Git on your own computer. If a check says your commits are not counted, the email on the commit is not verified on GitHub. Fix: commits are not counted
5.1 The three configuration levels
Git reads settings from three places. A more specific level overrides a more general one.
Level
Flag
Applies to
Typical use
System
--system
Every user on the computer
Rarely changed
Global
--global
All repositories of your user account
Your name, email and editor
Local
--local (default)
One repository only
A different email for one project
5.2 Build your setup commands
Fill in your details and the commands below update as you type. Nothing is sent anywhere; this runs in your browser. Copy the result into your terminal.
Setup command builder
Important
Use the same email address that you registered on GitHub. If the emails differ, GitHub does not link your commits to your profile and they do not appear in your contribution graph.
Default branch. Older Git versions named the first branch master. GitHub and most teams now use main; the command above makes every new repository start on it.
Line endings. Windows ends lines with two hidden characters, macOS and Linux use one. The core.autocrlf setting stops this from creating noisy differences in mixed teams.
5.3 Check your settings
$ git config --list
user.name=Your Name
user.email=you@example.com
init.defaultbranch=main
core.editor=code --wait# read a single value$ git config user.name
Your Name
Settings live in a plain text file called .gitconfig in your home folder. You can open it in an editor and change values by hand.
5.4 Time-saving aliases
An alias is a short name for a longer command. Add these later, once you are comfortable.
Skipping the identity step. Git may refuse to commit, or guess a wrong name and email from your computer.
Forgetting quotes around a name with a space, such as "Your Name".
Warning
Commit emails are public in a public repository. If you do not want your real address exposed, use the private no-reply address from your GitHub email settings.
Quick recap
user.name and user.email are mandatory; set them once with --global.
Set init.defaultBranch main so new repositories start on main.
Configure an editor and line endings for your operating system.
git config --list shows everything Git currently knows about you.
Practice exercise
Set your name and email, matching your GitHub account.
Set the default branch to main and choose your editor.
Run git config --list and check every value.
Create the git lg alias. You will use it in Part 2 to view history.
Part 1 complete
Next up is Part 2: Git Basics, where you create your first repository and make your first commits.
2
Git Basics
Track, save, inspect and undo changes in a local project.
Part 2Chapter 64 min read
Your first repository
Creating a repository turns an ordinary folder into a project that Git can track.
Course
For the Git Learning Portal, do not run git init or mkdir on your computer. Create git-lab from the template in the browser, then open it in Codespaces. This chapter still uses git init so you can see what a repository is. Stage 1 guide ยท I already have GitHub. Do I still make a folder?
6.1 Creating a repository with git init
A repository is a folder that Git is watching. Any folder can become one with a single command.
$ mkdir hello-git
$ cd hello-git
$ git init
Initialized empty Git repository in /home/you/hello-git/.git/
That is all. Git created a hidden folder called .git inside your project. Everything Git knows about your project lives in that folder: the history, the settings and the branches. Your own files stay where they are.
Warning
Never delete the .git folder by accident. If you delete it, the project becomes an ordinary folder again and its entire history is gone.
6.2 What is inside the .git folder
You do not need to edit these files, but it helps to know what they are.
Item
What it holds
HEAD
A pointer to the branch or commit you are currently on
config
Settings for this repository only (the local level from Chapter 5)
objects/
Every file version and commit Git has saved, stored by hash
refs/
Pointers to branch tips and tags
index
The staging area (explained in the next chapter)
$ ls -a
. .. .git$ ls .git
HEAD config description hooks info objects refs
6.3 Checking the state with git status
git status is the command you will type most often. It tells you which branch you are on and what has changed.
$ git status
On branch mainNo commits yetnothing to commit (create/copy files and use "git add" to track)
Now create a file and ask again.
$ echo "# Hello Git" > README.md
$ git status
On branch mainNo commits yetUntracked files: (use "git add <file>..." to include in what will be committed) README.mdnothing added to commit but untracked files present (use "git add" to track)
Untracked means Git can see the file but is not recording it yet. You will fix that in the next chapter.
The life of a file in Git
6.4 Cloning an existing repository
Often you start from a project that already exists online. git clone downloads the whole repository, history included.
$ git clone https://github.com/git/git-scm.com.git
Cloning into 'git-scm.com'...$ cd git-scm.com
Clone creates a new folder named after the repository. Cloning from GitHub is covered fully in Part 4.
6.5 Common mistakes
Running git init inside another repository. You then have a repository inside a repository, which confuses Git. Check with git status first; if it says "not a git repository" you are safe to initialise.
Initialising your home folder. Always create a project folder first. Running git init in ~ makes Git try to track everything you own.
Running Git commands outside a repository. The error fatal: not a git repository simply means you are in the wrong folder.
Making a folder on your computer for the course. A folder is not a GitHub repository. For stage 1, use the template link in the Start comment, then Codespaces.
Opening the yellow Compare & pull request banners on git-lab.greeting-a and greeting-b come with the template. Ignore them in every stage, and do not merge them. Stage 3 makes its own greeting branches in Codespaces. Yellow banners
Tip
Your terminal prompt can show the current branch. Git Bash does this by default, and tools such as Oh My Zsh add it on macOS and Linux.
Quick recap
git init turns a folder into a repository by creating a hidden .git folder.
.git holds all history and settings; never delete it.
git status shows your branch and which files are untracked, modified or staged.
git clone copies an existing repository with its full history.
Check yourself
1. Which command turns an ordinary folder into a Git repository?
git init creates the hidden .git folder that stores the history.
2. A file Git can see but is not recording is called...
Untracked files must be added with git add before Git records them.
Practice exercise
Create a folder named hello-git and run git init inside it.
Run ls -a (or dir /a on Windows) and find the .git folder.
Create README.md, then run git status and read the output.
Git moves your work through three areas, and understanding them explains almost every Git command.
Course
The stage 1 command block does this three times: edit a file (working directory), git add (staging area), git commit (repository). Paste the whole block so you get three commits. Stage 1 guide
7.1 The idea: a photo studio
Imagine a photo studio. The working directory is the stage where you arrange people and props. The staging area is the shelf where you place exactly the items you want in the next photo. The repository is the album where finished photos are stored forever. You can rearrange the stage as much as you like. Only what you place on the shelf gets photographed, and once a photo is in the album it stays there.
The three areas and the commands that move work between them
7.2 Why a staging area exists
Many beginners ask why Git does not just save everything you changed. The staging area lets you build clean commits. If you fixed a bug and also reformatted another file, you can stage and commit them separately. Each commit then tells one story, which makes history easy to read and easy to undo.
7.3 Adding files with git add
$ git add README.md
$ git status
On branch mainNo commits yetChanges to be committed: (use "git rm --cached <file>..." to unstage) new file: README.md
The file is now staged. Useful variations:
Command
What it stages
git add file.txt
One file
git add file1.txt file2.txt
Several named files
git add src/
Everything inside a folder
git add *.py
All Python files in the current folder
git add .
Everything new or changed from here downward
git add -A
Everything new, changed or deleted in the whole project
git add -p
Pick individual pieces of a file, one at a time
7.4 A file can be in two states at once
Edit a file after staging it and Git remembers both versions: the one you staged, and the newer one in your working directory.
$ echo "More text" >> README.md
$ git status
Changes to be committed: new file: README.mdChanges not staged for commit: modified: README.md
Only the staged version goes into the next commit. Run git add README.md again to include the newer edit.
7.5 Staging part of a file
git add -p shows each changed chunk (called a hunk) and asks what to do.
$ git add -p
@@ -1,2 +1,3 @@ # Hello Git+More textStage this hunk [y,n,q,a,d,s,e,?]? y
Key
Meaning
y
Stage this hunk
n
Do not stage this hunk
s
Split into smaller hunks
q
Quit
7.6 Common mistakes
Using git add . blindly. It can stage secrets, build files or large data. Always run git status before committing.
Forgetting to stage again after editing. The commit contains the staged version, not your latest edit.
Thinking git add saves your work. Staging is only a preparation step. Nothing is saved to history until you commit.
Quick recap
Working directory, staging area and repository are the three areas.
git add moves changes into the staging area; git commit moves them into the repository.
The staging area lets you build small, focused commits.
git add -p stages part of a file.
Check yourself
1. What does git add do?
Staging prepares a commit. The commit itself is a separate step.
2. You edit a file after staging it. Which version goes into the next commit?
Git commits what is staged. Stage again to include the newer edit.
Practice exercise
Create two files, a.txt and b.txt, and stage only a.txt. Check with git status.
Edit a.txt after staging it and run git status to see the file listed twice.
Practise git add -p on a file with two separate edits.
A commit is a permanent snapshot of your staged changes together with a message explaining why you made them.
Course
Stage 1 needs three commits after your Start issue: the code file, the README line, and the *.log ignore rule. Your name goes in data/profile.json and is committed with the README. It does not replace those three commits. If Submit says you only have two commits, paste the rest of the Start block. Fix: only two commits ยท Fix: challenge code file
Never amend a commit that you have already pushed to a shared branch. Amending creates a different commit, and teammates who already have the old one will run into conflicts.
8.6 Common mistakes
Committing with no message or a vague one. Future you will not remember what "stuff" meant.
Huge commits that mix unrelated changes. Use git add -p to split them.
Committing secrets such as passwords or API keys. Removing them later is painful. Part 5 covers prevention.
Skipping the Start command block. The portal counts only commits after Start that are pushed to GitHub. Paste every line, then git push.
Quick recap
A commit is a snapshot plus author, date, message and a link to its parent.
git commit -m "message" saves what is staged.
Write short, imperative messages that explain why.
--amend fixes the last commit, but only before you share it.
Check yourself
1. Which part of your work does a commit save?
A commit records the staged snapshot.
2. Which message follows good practice?
Short, imperative and specific.
Practice exercise
Make your first commit with a clear message.
Change a file, stage it, commit it, then fix the message with git commit --amend.
Write one commit whose message explains why, not only what.
Git remembers everything, and a handful of commands let you read that memory in many different ways.
Course
The last line of the stage 1 block is git log --oneline. You should see at least three commits you just made. If you see only one or two, you skipped part of the block. Fix: only two commits
9.1 git log: the history list
$ git log
commit 9a1c4e7d2b8f0c3a5e6d7f8a9b0c1d2e3f4a5b6c (HEAD -> main)Author: Your Name <you@example.com>Date: Mon Oct 5 10:12:03 2026 +0530 Fix typo in READMEcommit 3f2a9c1e5b7d4a8c9d21f6e5a4b3c2d1e0f9a8b7Author: Your Name <you@example.com>Date: Mon Oct 5 09:40:11 2026 +0530 Add README
Newest commits come first. Press q to leave the scrolling view.
A .gitignore file tells Git which files to leave alone, such as secrets, build output and temporary files.
Course
Stage 1 needs a line that is exactly *.log in .gitignore, committed after Start. The Start block adds it. If Submit says the log rule is missing, you skipped those lines. Fix: gitignore is missing the log rule
10.1 Why some files must not be tracked
Some files should never enter your history:
Passwords, API keys and .env files.
Dependencies such as node_modules/ or venv/, which can be downloaded again.
Build output, logs and cache files.
Editor or operating system files such as .DS_Store or .vscode/.
Ignoring them keeps the repository small, clean and safe.
10.2 Creating a .gitignore
Create a plain text file named .gitignore in the project root. Each line is a pattern.
Commit the .gitignore itself so everyone on the team shares the same rules.
10.3 Pattern rules
Pattern
Matches
*.log
Any file ending in .log, in any folder
build/
A folder named build
/todo.txt
Only todo.txt in the project root
docs/*.pdf
PDF files directly inside docs
**/temp
A folder named temp at any depth
!important.log
Exception: do not ignore this file
# comment
A comment line
Note
The order matters. A later ! rule can re-include something an earlier rule ignored, but only if the parent folder itself is not ignored.
10.4 When a file is already tracked
.gitignore only affects files Git is not yet tracking. If you committed .env before ignoring it, Git keeps tracking it. Tell Git to stop, but keep the file on disk:
GitHub maintains a collection of .gitignore templates for most languages and frameworks. Start from the one that matches your stack, then adjust it. GitHub also offers to add one when you create a new repository.
10.8 Common mistakes
Ignoring after committing. Remember git rm --cached.
Ignoring a folder, then expecting an exception inside it to work. Ignore files, or use folder/* style patterns.
Ignoring everything with *. This hides real work. Be specific.
Quick recap
.gitignore lists patterns of files Git should not track.
Commit it so your whole team shares it.
Already tracked files need git rm --cached first.
Secrets in old history stay leaked until you rotate them.
Check yourself
1. You committed a file, then added it to .gitignore. What happens?
Ignore rules only apply to untracked files.
2. Which pattern ignores every .log file?
Practice exercise
Create a .gitignore that ignores *.log and a folder named build/.
Create debug.log and check that git status does not list it.
Use git check-ignore -v debug.log to see which rule matched.
Use --soft to redo a commit: your changes stay staged, ready to commit again.
Use --mixed to unstage and rethink.
Use --hard only when you truly want to throw the work away.
Caution
git reset --hard destroys uncommitted work. Check git status first.
11.5 git revert: undo safely in shared history
Reset rewrites history, which is dangerous once others have your commits. Revert adds a new commit that cancels an old one, so history is never rewritten.
Git tracks file moves and deletions too, and the right commands keep the history clean.
12.1 Deleting a tracked file
$ git rm old-notes.txt
rm 'old-notes.txt'$ git status
Changes to be committed: deleted: old-notes.txt$ git commit -m "Remove old notes"
git rm deletes the file from disk and stages the deletion in one step. If you delete a file in your file explorer instead, Git notices it as an unstaged deletion. Stage it with git add -A or git rm <file>.
12.2 Stop tracking but keep the file
$ git rm --cached config.local
This removes the file from Git's tracking but leaves it on disk. Pair it with .gitignore (Chapter 10).
12.3 Renaming and moving
$ git mv draft.txt final.txt
$ git status
Changes to be committed: renamed: draft.txt -> final.txt
git mv is the same as renaming the file yourself and then running git add on both names. Git does not store renames explicitly. It detects them by comparing contents, so a rename plus tiny edits is still recognised.
12.4 Restoring a deleted file
Deleted something by mistake? It is still in history.
Git is a database of snapshots identified by hashes, and seeing its structure makes the commands feel logical.
13.1 Snapshots, not differences
Older tools stored a list of changes for every file. Git instead stores a snapshot of the whole project at each commit. Unchanged files are not copied again; the new snapshot simply points to the version that already exists. This is why branching and switching are so fast.
13.2 The four object types
Git stores everything as objects inside .git/objects, each named by a hash.
Object
Stores
Analogy
Blob
The contents of one file
A page of text
Tree
A folder: a list of names pointing to blobs and other trees
A table of contents
Commit
A pointer to a tree, plus author, date, message and parent
A dated photo with a caption
Tag
A named pointer, often to a commit
A bookmark
How one commit points to your files
13.3 Hashes: the fingerprint of content
Every object is named by a hash of its contents. By default Git uses SHA-1, a 40-character hexadecimal code. Identical content always gives an identical hash, and any tiny change produces a completely different one.
This is why Git history is trustworthy: a commit's hash depends on its content and its parent's hash, so changing an old commit changes every hash after it. Git also has experimental support for SHA-256 as a stronger option.
13.4 Looking inside
$ git cat-file -t 3f2a9c1
commit$ git cat-file -p 3f2a9c1
tree 8c3a1b9d0e7f2a4b6c5d8e9f0a1b2c3d4e5f6a7bauthor Your Name <you@example.com> 1759640000 +0530committer Your Name <you@example.com> 1759640000 +0530Add README$ git cat-file -p 8c3a1b9
100644 blob 2f8d6a1b7c9e0a3b5d4c6e8f9a0b1c2d3e4f5a6b README.md
-t shows the type and -p pretty-prints the content. The hashes above are examples; yours will differ.
13.5 Branches and HEAD are just pointers
A branch is a tiny file that holds one commit hash. HEAD is a file that says which branch you are on.
Work on several things in parallel and combine the results safely.
Part 3Chapter 145 min read
Branches and HEAD
A branch is a separate line of work, so you can try ideas without disturbing the stable version of your project.
14.1 The idea: parallel drafts
Writing a report, you might copy the document to try a risky rewrite. If it works you keep it, and if not you throw the copy away. A branch gives you that without copying any files. Your stable work stays on main, while the experiment lives on its own branch.
Two lines of work that share a starting point
Because a branch is only a pointer to a commit (Chapter 13), creating one is instant and costs almost no space.
14.2 Creating and listing branches
$ git branch feature-login
$ git branch
feature-login* main
The * marks the branch you are on. Creating a branch does not move you onto it.
14.3 Switching branches
$ git switch feature-login
Switched to branch 'feature-login'$ git switch -c feature-search
Switched to a new branch 'feature-search'
git switch -c <name> creates and switches in one step, which is the form you will use most.
Note
Older tutorials use git checkout <branch> and git checkout -b <branch>. They still work. switch was added in 2019 to give branch changes a clearer name.
When you switch, Git rewrites the files in your working directory to match that branch. Work you committed on another branch seems to vanish and returns when you switch back.
14.4 HEAD: where you are
HEAD points to the branch you are on, and that branch points to its latest commit. Each new commit moves the branch forward, and HEAD follows.
HEAD is on feature-login, so the next commit extends that branch
14.5 Everyday branch commands
Command
What it does
git branch
List local branches
git branch -a
List local and remote branches
git branch -m old new
Rename a branch
git branch -d name
Delete a merged branch
git branch -D name
Force-delete a branch, even if unmerged
git switch -
Jump back to the previous branch
git branch -vv
Show each branch's latest commit and upstream
14.6 Detached HEAD
If you check out a commit hash or a tag, HEAD points straight at a commit, not a branch. Git calls this a detached HEAD.
$ git switch --detach 3f2a9c1
HEAD is now at 3f2a9c1 Add README
You can look around safely. But a commit made here belongs to no branch and can be lost. To keep the work, give it a branch:
$ git switch -c rescue-work
To leave without keeping anything, run git switch main.
14.7 Naming branches
Use short lowercase names with hyphens: fix-login-bug.
Group by purpose with a prefix: feature/search, bugfix/crash, docs/readme.
Avoid spaces and personal names.
14.8 Uncommitted changes when switching
If your edits do not touch files that differ between the branches, they travel with you. If they would be overwritten, Git refuses to switch and asks you to commit or stash first (Chapter 19).
14.9 Common mistakes
Committing on main by accident. Check git branch before you start.
Forgetting which branch you are on. Use git status or a prompt that shows it.
Deleting an unmerged branch with -D. You may lose the only copy of that work.
Quick recap
A branch is a movable pointer to a commit; creating one is instant.
git switch -c name creates and enters a branch.
HEAD points to your current branch; commits move it forward.
Detached HEAD means HEAD points at a commit; create a branch to keep work done there.
Check yourself
1. What does git switch -c login do?
The -c flag means create.
2. You made commits in a detached HEAD state. How do you keep them?
Practice exercise
Create a branch named feature-hello and switch to it.
Make a commit, switch back to main, and confirm the file disappears. Switch again and it returns.
Run git log --oneline --graph --all to see both branches.
If both branches have new commits, a simple slide is impossible. Git compares three snapshots: the common ancestor and the two branch tips. It then creates a merge commit with two parents.
Before: main and the feature have both moved onAfter: the merge commit M joins both lines
$ git merge feature-login
Merge made by the 'ort' strategy. login.py | 12 ++++++++++++ 1 file changed, 12 insertions(+)
Git opens your editor with a default message such as "Merge branch 'feature-login'". Save and close to finish.
15.4 Controlling the style of merge
Option
Effect
--no-ff
Always create a merge commit, even when a fast-forward is possible
--ff-only
Refuse to merge unless a fast-forward is possible
--squash
Combine all branch changes into one staged change, then you commit it yourself
--abort
Cancel a merge that is in progress
Teams choose between them. --no-ff keeps a visible record of each feature. --squash keeps main tidy with one commit per feature. GitHub offers the same three choices on pull requests (Chapter 25).
A conflict happens when two branches change the same lines, and Git asks you to decide what the result should be.
16.1 Why conflicts happen
Git merges automatically when branches change different parts of a file. When both branches edit the same lines, Git cannot know which version you want. It stops and asks you. That is not an error, only a question that needs a human answer.
16.2 What a conflict looks like
$ git merge feature-login
Auto-merging app.pyCONFLICT (content): Merge conflict in app.pyAutomatic merge failed; fix conflicts and then commit the result.
Open the file. Git has marked the conflicting area.
Visual Studio Code shows buttons above each conflict: Accept Current, Accept Incoming, Accept Both. Many people find this easier than editing markers by hand.
16.5 Making conflicts less likely
Pull the latest main into your branch often.
Keep branches short-lived and focused.
Talk to teammates before large refactors or file moves.
Keep functions and files small so edits rarely overlap.
16.6 Seeing more context
By default Git shows only two sides. The zdiff3 style also shows the common ancestor, which often makes the right choice obvious.
$ git config --global merge.conflictStyle zdiff3
16.7 Common mistakes
Leaving markers in the file. Search for <<<<<<< before you commit. A project with markers will not run.
Accepting one side without reading. You may silently throw away a teammate's work.
Panicking.git merge --abort always takes you back to safety.
Quick recap
Conflicts occur when both branches change the same lines.
Markers show your version, a divider and the incoming version.
Edit, remove markers, git add, then commit.
git merge --abort cancels the merge.
Check yourself
1. What must you do after fixing a conflicted file?
Staging the file tells Git the conflict is resolved.
2. Which command cancels a merge in progress?
Practice exercise
Create a conflict on purpose: edit the same line differently on two branches and merge.
Resolve it by combining both versions and finish the merge.
Repeat, then cancel the second attempt with git merge --abort.
Rebase replays your commits on top of another branch, which gives a straight, easy-to-read history.
17.1 The idea
Merging joins two histories with a merge commit. Rebasing takes your branch's commits and re-applies them one by one on top of the latest main, as if you had started your work from there.
Before: feature branched from C2, but main has moved on to C5
$ git switch feature
$ git rebase main
Successfully rebased and updated refs/heads/feature.
After: the feature commits are replayed on top of C5 (as new commits C3' and C4')
The history is now a straight line. The feature commits have the same changes but new hashes, because their parent changed.
17.2 Merge or rebase
Merge
Rebase
History
Keeps the true branching shape
Gives a straight line
Extra commits
Adds a merge commit
Adds none
Rewrites commits
No
Yes (new hashes)
Safe on shared branches
Yes
No
Typical use
Joining finished work
Updating your own branch before a pull request
17.3 Rebase conflicts
Conflicts can appear while commits are replayed. Resolve them as in Chapter 16, then continue.
$ git add app.py
$ git rebase --continue
$ git rebase --abort
$ git rebase --skip
--continue carries on after you fixed the files.
--abort returns to how things were before the rebase.
--skip drops the commit that is causing trouble.
17.4 The golden rule of rebase
Caution
Never rebase commits that other people already have. Rebase creates new commits with new hashes. If teammates built on the old ones, their history and yours no longer match, and fixing it is painful.
Rebase your own local or personal-branch commits freely. Do not rebase main or any shared branch.
17.5 Pull with rebase
A normal git pull can add merge commits. This keeps your local history linear instead:
Cherry-pick copies a single commit from one branch onto another without merging everything else.
18.1 When you need it
Imagine a bug fix was committed on a feature branch, and the same fix is needed urgently on main, but the rest of the feature is not ready. Cherry-pick takes just that one commit.
Cherry-picking commit F2 onto main creates a copy, F2'
Good for hotfixes that must reach a release branch quickly.
Good for rescuing one useful commit from an abandoned branch.
Poor as a routine way to move work. Duplicate commits make history confusing, and you may see the same change twice when the branches are merged later.
18.5 Common mistakes
Cherry-picking too much. If you need many commits, merge or rebase instead.
Forgetting the original still exists. The source branch keeps its own copy.
Quick recap
Cherry-pick copies selected commits to the current branch.
The copies get new hashes.
Use -x to record the source commit.
Resolve conflicts, then --continue.
Check yourself
1. What does cherry-pick create?
Practice exercise
On a branch, make two commits. Cherry-pick only the second one onto main.
Check that the hashes differ with git log --oneline --all.
Stash puts your unfinished work on a shelf so you can change branches with a clean working directory.
19.1 The problem
You are halfway through a change when a colleague asks for an urgent fix on another branch. Your work is not ready to commit, and Git refuses to switch because it would overwrite your edits. Stash solves exactly this.
19.2 Basic use
$ git stash
Saved working directory and index state WIP on feature: 8e4c1b7 Add form$ git switch main
Your working directory is now clean. Do the urgent work, then come back.
$ git switch feature
$ git stash pop
pop re-applies the stashed changes and removes them from the stash.
19.3 Commands you will use
Command
What it does
git stash push -m "message"
Stash with a helpful name
git stash list
Show all stashes
git stash pop
Re-apply the newest stash and delete it
git stash apply
Re-apply but keep it in the list
git stash drop
Delete the newest stash
git stash show -p
Show what the stash contains
git stash -u
Include untracked files
git stash branch name
Create a branch from a stash
$ git stash list
stash@{0}: On feature: half-finished formstash@{1}: WIP on main: a3b9d21 Fix typo$ git stash apply stash@{1}
19.4 What stash does not include
By default stash saves changes to tracked files only. New files that Git has never seen stay behind unless you add -u. Ignored files need -a.
19.5 Conflicts when applying
If the branch changed since you stashed, pop can conflict. Resolve it like any merge conflict. After a conflict, Git keeps the stash, so nothing is lost.
19.6 Common mistakes
Stashing and forgetting. Stashes are easy to lose track of. Give each a message and clear them regularly.
Expecting new files to be stashed. Use -u.
Using stash as long-term storage. For work you want to keep, make a branch and commit.
Quick recap
git stash shelves uncommitted work; git stash pop brings it back.
Use -m to label stashes and -u for untracked files.
apply keeps the stash; pop removes it.
For long-lived work, commit on a branch instead.
Check yourself
1. Which command re-applies a stash and removes it from the list?
Practice exercise
Edit a file without committing, then stash it and check that git status is clean.
Switch branches, come back, and run git stash pop.
Share your code, review other people's work and contribute to projects around the world.
Part 4Chapter 213 min read
Remotes and cloning
A remote is a copy of your repository stored somewhere else, usually on GitHub, and cloning is how you get one onto your machine.
21.1 What a remote is
So far everything lived on your computer. A remote is another copy of the repository on a server. Teammates push their work to it and pull each other's work from it. By tradition the main remote is called origin.
Your local repository and its remote on GitHub
21.2 Creating a repository on GitHub
Sign in at github.com and choose New repository.
Enter a name such as hello-git and an optional description.
Choose Public (anyone can see it) or Private (only people you invite).
If you already have a local project, leave the options to add a README, .gitignore and licence unchecked, so the two do not clash.
Select Create repository. GitHub then shows the commands you need.
Caution
Check the visibility before you create the repository. Anything pushed to a public repository can be seen and copied by anyone.
21.3 Connecting an existing local project
$ git remote add origin https://github.com/your-username/hello-git.git
$ git branch -M main
$ git push -u origin main
The first line registers the remote, the second makes sure your branch is called main, and the third uploads your history. The -u flag remembers the link, so later you can type only git push.
21.4 Cloning an existing repository
$ git clone https://github.com/your-username/hello-git.git
Cloning into 'hello-git'...$ cd hello-git
Cloning downloads the full history, sets origin automatically and checks out the default branch. To use another folder name add it at the end: git clone <url> my-folder.
21.5 Inspecting remotes
Command
Shows or does
git remote -v
List remotes and their URLs
git remote add name url
Add a remote
git remote rename old new
Rename a remote
git remote remove name
Remove a remote
git remote set-url origin <url>
Change a remote's address
git branch -vv
Show which remote branch each local branch follows
These three commands move commits between your computer and GitHub, and knowing how they differ avoids most beginner surprises.
22.1 Push: send your commits
$ git push
Enumerating objects: 5, done.To https://github.com/your-username/hello-git.git 3f2a9c1..9a1c4e7 main -> main
Push uploads commits that the remote does not have yet. After the first git push -u, a plain git push is enough.
22.2 Fetch: look without touching
$ git fetch
From https://github.com/your-username/hello-git 9a1c4e7..b81e6d2 main -> origin/main
Fetch downloads new commits but does not change your working files or your branch. They are stored in a remote-tracking branch named origin/main. You can inspect them before deciding what to do.
Use git pull --rebase if you prefer a straight history (Chapter 17).
22.4 When a push is rejected
$ git push
! [rejected] main -> main (fetch first)error: failed to push some refshint: Updates were rejected because the remote contains work thathint: you do not have locally.
Someone else pushed before you. The fix is always the same: bring in their work first.
Fixing a rejected push
22.5 Force pushing
$ git push --force-with-lease
A force push overwrites the remote history. You may need one after a rebase or an amend. Use --force-with-lease, which stops if someone else pushed in the meantime. Never force-push to main or any branch your team shares.
22.6 Other useful push commands
Command
Purpose
git push origin feature
Push a named branch
git push -u origin feature
Push a new branch and link it
git push origin --delete feature
Delete a remote branch
git fetch --prune
Remove references to deleted remote branches
git pull --ff-only
Pull only if no merge is needed
22.7 Common mistakes
Pushing without pulling first. Make git pull a habit before you start work.
Using --force instead of --force-with-lease. The first can erase a teammate's commits.
Thinking fetch changes your files. It does not; only merge, rebase or pull do.
Quick recap
git push sends your commits; git fetch downloads without changing your files.
git pull is fetch plus merge (or rebase).
A rejected push means you must pull the remote work first.
Prefer --force-with-lease and never force-push shared branches.
Check yourself
1. Which command downloads new commits without changing your working files?
Practice exercise
In two clones of the same repository, push a commit from one and git fetch in the other.
Run git log main..origin/main to see the incoming commit, then git pull.
Create a rejected push on purpose and fix it with a pull.
GitHub needs to know who you are before it accepts a push, and there are two safe ways to prove it: SSH keys and access tokens.
23.1 Why passwords no longer work
Since 2021 GitHub does not accept your account password for Git operations over HTTPS. You use either a personal access token or an SSH key. Both are safer, because they can be limited and revoked without changing your password.
23.2 Option 1: SSH keys
An SSH key is a pair of files. The private key stays secret on your computer. The public key is given to GitHub. GitHub uses the pair to check that you hold the private key, without the secret ever travelling.
Setting up an SSH key
$ ssh-keygen -t ed25519 -C "you@example.com"
Generating public/private ed25519 key pair.Enter file in which to save the key (/home/you/.ssh/id_ed25519):Enter passphrase (empty for no passphrase):$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_ed25519
$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3Nz...example you@example.com
Copy the whole line that starts with ssh-ed25519. On GitHub open Settings, then SSH and GPG keys, choose New SSH key, paste it and save.
$ ssh -T git@github.com
Hi your-username! You've successfully authenticated, but GitHub does not provide shell access.
Share only the file ending in .pub. The file without .pub is your private key. Never email it, paste it in chat or commit it.
Add a passphrase when asked. It protects the key if your laptop is stolen.
23.3 Option 2: Personal access tokens
A token is a long random string that acts like a password with limited powers. GitHub offers two kinds. Fine-grained tokens can be limited to selected repositories and permissions, and GitHub recommends them where possible. Classic tokens use broad scopes such as repo.
Open Settings, then Developer settings, then Personal access tokens.
Create a token, choose the repositories and permissions, and set an expiry date.
Copy it immediately; GitHub shows it only once.
When Git asks for a password over HTTPS, paste the token.
Git Credential Manager, which comes with Git for Windows and is available on other systems, stores the token safely so you do not paste it every time.
23.4 Choosing between them
SSH key
Access token (HTTPS)
Setup effort
A few commands once
Create and store a token
Day-to-day
No prompts
Credential manager remembers it
Revoke
Delete the key on GitHub
Delete or expire the token
Good for
Your own laptop
Scripts, CI and restricted access
23.5 GitHub CLI shortcut
The GitHub command-line tool gh can handle the login for you.
$ gh auth login
23.6 Common mistakes
Committing a token or private key. Treat it as leaked and revoke it at once.
Using a token with far too many permissions. Give the minimum.
Mixing HTTPS and SSH remotes. An SSH key does nothing if your remote is an https:// address.
Quick recap
Use SSH keys or personal access tokens; passwords are no longer accepted.
Keep the private key secret and give GitHub only the .pub file.
Fine-grained tokens with expiry dates are safest.
ssh -T git@github.com tests an SSH setup.
Check yourself
1. Which file do you upload to GitHub for SSH access?
The .pub file is the public half; the other is your secret.
Practice exercise
Generate an ed25519 key with a passphrase and add the public key to GitHub.
Run ssh -T git@github.com and confirm the greeting.
Switch your remote to the SSH address and push a commit.
A fork is your own copy of someone else's repository on GitHub, which lets you propose changes to a project you do not own.
24.1 Why forks exist
You cannot push to a stranger's repository. A fork copies it into your account, where you have full control. You change your copy, then ask the owner to take your changes through a pull request (Chapter 25).
The fork workflow
24.2 Fork versus clone
Fork
Clone
Where it happens
On GitHub, copy to your account
On your computer
Needs permission
No, if the repository is public
No, if you can read it
Purpose
Get your own writable copy online
Get files and history locally
You usually do both: fork on GitHub, then clone your fork.
A pull request asks the project to take your branch, and it is where discussion, review and automatic checks happen before merging.
25.1 What a pull request is
A pull request (PR) is a proposal to merge one branch into another. It is a page on GitHub that shows the changes, hosts the discussion and runs automatic checks. Even on your own project, PRs are a good habit because they record why a change was made.
The life of a pull request
25.2 Opening a pull request
$ git switch -c fix-typo
$ git add README.md
$ git commit -m "Fix typo in installation steps"
$ git push -u origin fix-typo
remote: Create a pull request for 'fix-typo' on GitHub by visiting:remote: https://github.com/your-username/hello-git/pull/new/fix-typo
GitHub shows a Compare and pull request button. On the form, write:
A clear title, like a good commit message.
A description of what changed and why, with screenshots if the change is visual.
Closing keywords such as Closes #12, which automatically close issue 12 when the PR merges.
Reviewers and assignees, if your team uses them.
25.3 Draft pull requests
Choose Create draft pull request when the work is not ready. A draft cannot be merged. It tells people you want early feedback, not a final review.
25.4 Reading a pull request page
Tab
Shows
Conversation
Description, comments, review status and checks
Commits
Every commit in the branch
Checks
Results of automatic tests and workflows
Files changed
The diff, where reviewers leave line comments
25.5 Three ways to merge
Option
Result
Best for
Merge commit
Keeps all commits and adds a merge commit
Preserving full history
Squash and merge
Combines the branch into one commit
Tidy main with one commit per change
Rebase and merge
Replays commits on top, no merge commit
A straight history with every commit kept
Repository owners can allow only the styles they prefer.
Review is where teammates catch bugs, share knowledge and keep quality high, and a kind, clear style makes it work.
26.1 Why review code
Another pair of eyes finds problems the author no longer sees. Review also spreads knowledge across the team, so no part of the project is understood by only one person.
26.2 How a review works on GitHub
Open Files changed. Hover over a line, select the + button and leave a comment. When you finish, choose Review changes and pick one of three options.
Choice
Meaning
Comment
General feedback without approving or blocking
Approve
The change is good to merge
Request changes
The author must address issues before merging
26.3 Suggested changes
Inside a comment, choose the Add a suggestion button. GitHub inserts a special block that you edit to hold the replacement text, for example:
total = price * quantity
The author accepts it with Commit suggestion, and GitHub makes the commit for them. Suggestions are ideal for typos and small fixes.
26.4 What to look for
Area
Questions
Correctness
Does it do what the description says? Are edge cases handled?
Tests
Are there tests, and do they pass?
Readability
Are names clear? Is anything too complicated?
Security
Any secrets, unsafe input or risky permissions?
Scope
Is the change focused, or did unrelated edits slip in?
26.5 Writing helpful comments
Comment on the code, not the person: "This loop can skip the last item" rather than "You made a mistake".
Explain why, and offer an alternative when you can.
Label small preferences, for example "nit:" for style points that need not block merging.
Praise good work. It is part of a review too.
26.6 Receiving feedback
Assume good intent. Reviewers want the project to succeed.
Reply to every comment, even if only to say "Done".
Disagree politely with reasons, and ask questions if something is unclear.
Push fixes as new commits, then click Resolve conversation once addressed.
26.7 Requesting a review
Use the Reviewers panel on the right of the pull request. A file called CODEOWNERS (Chapter 33) can add the right reviewers automatically.
26.8 Common mistakes
Approving without reading. A rubber stamp helps nobody.
Rewriting the author's design in comments. Discuss big design questions before the code is written.
Waiting days to review. Slow feedback blocks the team. Aim to respond within a working day.
Quick recap
Reviews catch bugs and share knowledge.
Choose Comment, Approve or Request changes when you finish.
Be specific, kind and focused on the code.
Reply to comments and resolve conversations as you fix them.
Check yourself
1. Which review choice blocks a pull request until the author responds?
Practice exercise
Open a pull request in a practice repository with a friend, and leave three comments, one of them a suggestion.
Apply a suggestion as the author and resolve the conversation.
Issues record bugs and ideas, and projects arrange them into a board, so the whole team can see what is happening.
27.1 What an issue is
An issue is a discussion thread about a bug, a task or a feature request. It has a title, a description, comments, an assignee and labels. Issues are numbered, such as #12, and you can refer to them from commits and pull requests.
27.2 Writing a good issue
A good bug report lets someone reproduce the problem without asking questions.
Title: Login button does nothing on Safari
Steps to reproduce
1. Open the login page in Safari 17
2. Enter a valid email and password
3. Click "Log in"
Expected: the dashboard opens
Actual: nothing happens and no error appears
Environment: macOS 14, Safari 17.2
27.3 Labels, assignees and milestones
Feature
Purpose
Example
Labels
Categorise issues
bug, enhancement, documentation, good first issue
Assignees
Say who is working on it
@priya
Milestones
Group issues for a release or deadline
v1.2.0
Projects
Show issues on a board
To do, In progress, Done
GitHub provides default labels, and you can add your own. The good first issue label is a convention that helps newcomers find easy tasks.
27.4 Linking issues and code
Mention an issue anywhere with #12. Use closing keywords in a pull request description or a commit message:
Fix login on Safari
Closes #12
The keywords close, fix and resolve, in their various forms, close the issue when the change reaches the default branch.
27.5 Mentions and references
@username notifies a person.
#12 links to an issue or pull request.
A full commit hash links to that commit.
27.6 GitHub Projects
A project is a board or table that collects issues and pull requests from one or more repositories. You can arrange them into columns such as Backlog, In progress, In review and Done, and add fields like priority or sprint.
A simple project board flow
27.7 Issue templates
Files inside .github/ISSUE_TEMPLATE/ give reporters a form to fill in, which makes reports more complete.
27.8 Discussions
For open-ended questions and ideas that are not tasks, repositories can turn on Discussions, a forum-style space separate from issues.
27.9 Common mistakes
Vague issues like "it is broken". Add steps, expected and actual results.
Duplicates. Search before you open a new issue.
Issues without owners. Assign someone, or label them as available.
Quick recap
Issues track bugs, tasks and ideas; they are numbered and searchable.
Labels, assignees and milestones organise them.
Closes #n links a pull request to an issue and closes it on merge.
Projects show work as a board.
Check yourself
1. How do you link a pull request so that it closes issue 12 when merged?
Practice exercise
Open an issue in your practice repository using the bug-report layout above.
Add two labels, assign yourself, and create a three-column project board.
Reference the issue from a pull request with Closes #<number>.
GitHub Pages turns a repository into a free website, with no server to manage.
28.1 What Pages gives you
GitHub Pages hosts static sites made of HTML, CSS and JavaScript files. It is free, serves over HTTPS and updates every time you push. It is ideal for portfolios, project documentation and landing pages. It cannot run server-side code or a database, which is why this handbook's interactive parts all run in the browser.
28.2 Two kinds of site
Type
Repository name
Address
User site
your-username.github.io
https://your-username.github.io/
Project site
any name, such as hello-git
https://your-username.github.io/hello-git/
28.3 Publishing your first page
Publish a page in five steps
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>My first page</title></head>
<body>
<h1>Hello from GitHub Pages</h1>
</body>
</html>
$ git add index.html
$ git commit -m "Add home page"
$ git push origin main
In the repository, open Settings, choose Pages, and under Build and deployment pick Deploy from a branch. Select main and the / (root) folder, then save. GitHub shows the address when the deployment finishes.
28.4 Using GitHub Actions to deploy
If your site needs a build step, for example a React app or a static site generator, choose GitHub Actions as the source and add a workflow that builds the site and publishes the output. Chapter 31 introduces workflows.
28.5 Custom domains
In Settings, then Pages, you can enter your own domain. Create the matching DNS record at your domain registrar, and GitHub provides the HTTPS certificate.
28.6 Handy tips
Put images and styles in folders and link them with relative paths.
A file named 404.html becomes your custom error page.
A .nojekyll file turns off the default Jekyll processing, which helps with plain HTML projects that have folders starting with an underscore.
28.7 Common mistakes
Missing index.html. Pages shows a 404 without it.
Wrong path casing. File names are case sensitive on GitHub: Logo.png is not logo.png.
Expecting instant updates. A deployment can take a minute or two.
Putting secrets in a Pages repository. Everything published is public.
Quick recap
Pages hosts free static sites straight from a repository.
User sites live at username.github.io; project sites in a sub-path.
Enable it under Settings, then Pages.
Use Actions to build sites that need a build step.
Check yourself
1. What kind of site can GitHub Pages host?
Practice exercise
Publish a one-page site from a new repository and open its address.
Edit the heading, push, and watch the page update.
A well-kept repository welcomes newcomers and protects the project with a clear README, licence and contribution rules.
29.1 The README: the front door
The README is the first thing visitors see. GitHub displays it under the file list. A good README answers four questions in under a minute: What is this? How do I run it? How do I use it? How do I help?
# Project name
One sentence about what it does.
## Install
## Usage
## Contributing
## Licence
Add a screenshot, a short code example and badges for build status if you have them. Write in Markdown.
29.2 Markdown essentials
You type
You get
# Heading
A large heading (## and ### make smaller ones)
**bold** and *italic*
bold and italic text
- item
A bullet list
[text](https://example.com)
A link

An image
Three backticks
A code block
29.3 Choosing a licence
Without a licence, nobody has permission to reuse your code, even if it is public. Add a LICENSE file. GitHub can create one from a template when you add a file.
Licence
In short
MIT
Very permissive. Reuse for anything, keep the copyright notice
Apache 2.0
Permissive, with an explicit patent grant
GPL 3.0
Reusers must also release their changes under the GPL
This is a simplified summary, not legal advice. For serious projects read the licence text or ask someone qualified.
29.4 Community files
File
Purpose
CONTRIBUTING.md
How to report bugs, set up the project and send changes
CODE_OF_CONDUCT.md
How people are expected to behave
SECURITY.md
How to report a vulnerability privately
.github/ISSUE_TEMPLATE/
Forms for new issues
.github/pull_request_template.md
Checklist for new PRs
GitHub shows these in the Community standards page of a repository and links them from the issue and PR screens.
29.5 Description, topics and social image
Fill in the one-line About description and add topics such as python or git-tutorial. Topics help people find your project in search.
29.6 A profile README
Create a public repository whose name is exactly your username, and add a README.md. GitHub shows it at the top of your profile, which is a good place for a short introduction and links to your best work.
29.7 Common mistakes
No licence. People cannot legally reuse the code.
A README that only says the project name. Explain installation and usage.
Committing generated files and secrets. Use .gitignore.
Quick recap
The README should explain what, how to run, how to use and how to contribute.
Add a LICENSE; MIT, Apache 2.0 and GPL 3.0 are common.
CONTRIBUTING.md, templates and a code of conduct welcome contributors.
A repository named after you becomes your profile README.
Check yourself
1. What happens if a public repository has no licence?
Practice exercise
Write a README for one of your repositories using the four-question outline.
Add an MIT LICENSE file from GitHub's template and a short CONTRIBUTING.md.
Open source runs on small contributions from strangers, and the workflow you have learned is exactly how they are made.
30.1 Why contribute
Contributing to open source teaches you to read real code, work with reviewers and communicate in public. Your commits and merged pull requests also become a visible record of your skills on your GitHub profile.
30.2 Finding a first project
Search GitHub issues with the label good first issue or help wanted.
Start with tools you already use, and look at their documentation for typos or gaps.
Check that the project is alive: recent commits, answered issues, and a CONTRIBUTING.md.
You do not need to write a new feature. These are all welcome and valuable:
Correcting documentation, typos and broken links.
Adding a missing example.
Reproducing a bug and adding details to the issue.
Improving tests.
Translating text.
30.5 Good etiquette
Read the existing issues and pull requests first, so you do not duplicate work.
Ask before starting a large change.
Keep pull requests small and on topic.
Be patient. Maintainers are often volunteers.
Say thank you, and accept feedback gracefully.
30.6 When your pull request is not accepted
It happens, and it is not personal. The project may have a different direction. Ask what would make the change acceptable, learn from it, and try another issue.
30.7 Becoming a maintainer
Contributors who help regularly are sometimes invited to review and merge. As a maintainer, you will triage issues, review pull requests and keep documentation current, which makes your GitHub activity richer and more visible.
30.8 Common mistakes
Opening a pull request without reading the contribution guide. It may be closed quickly.
One giant pull request. Split it up.
Working on main in your fork. Always use a branch.
Quick recap
Look for good first issue labels and active projects.
Fork, clone, branch, change, push and open a pull request.
Documentation and test improvements are real contributions.
Be patient and respectful with maintainers.
Check yourself
1. Where should you make your changes in a fork?
Practice exercise
Find an open-source project with a good first issue label that interests you.
Read its CONTRIBUTING.md and write down the steps it asks for.
Automate checks, recover lost work, tidy history and keep your repositories secure.
Part 5Chapter 313 min read
GitHub Actions
Actions runs your tests and scripts automatically whenever something happens in your repository, and it works as the engine of your whole workflow.
31.1 What Actions does
GitHub Actions is a built-in automation service. When an event occurs, such as a push or a pull request, GitHub starts a virtual machine and runs the commands you describe. The most common use is continuous integration (CI): every change is built and tested automatically, so mistakes show up in minutes.
How a workflow runs
31.2 The vocabulary
Term
Meaning
Workflow
An automated process defined in one YAML file
Event
What starts the workflow: a push, a pull request, a timer
Job
A group of steps that run on one machine; jobs can run in parallel
Step
One command, or one reusable action
Runner
The machine that runs the job (for example ubuntu-latest)
Action
A reusable building block written by you or others
31.3 Your first workflow
Create the file .github/workflows/ci.yml in your repository.
Reading it from top to bottom: run on every push to main and on every pull request; start an Ubuntu machine; fetch the code; install Python; install dependencies; run the tests.
Note
Action versions such as @v4 change over time. Check each action's page on the GitHub Marketplace for the newest major version before you copy a workflow.
Open the Actions tab of your repository. Each run shows a green tick or a red cross. Click a run, then a job, to read the log of every step. On a pull request, the same result appears at the bottom as a check. A failed step stops the job. Fix the code, push again, and the workflow re-runs. You can also choose Re-run jobs on a run page.
31.5 YAML in two minutes
YAML uses spaces for nesting, never tabs. Two spaces is standard.
A line starting with - is a list item.
key: value pairs make a dictionary.
Quote values that could be misread, like "3.12", which would otherwise be read as the number 3.12.
31.6 Common mistakes
Wrong folder. Workflows must be in .github/workflows/.
Tab characters. They break YAML. Use spaces.
Forgetting actions/checkout. Without it, the runner has none of your files.
Never write passwords or API keys in a workflow file. Store them under Settings, then Secrets and variables, then Actions, and read them in the workflow.
The permissions block gives the workflow's built-in token only what it needs. Combined with the official Pages actions, it publishes your site after each push to main.
32.7 Writing safe workflows
Give the token the least permissions with a permissions: block.
Treat pull requests from forks as untrusted. Do not run their code with access to secrets.
Trust actions from verified creators, and pin important third-party actions to a full commit hash.
32.8 Common mistakes
Printing secrets in logs. Even masked values can leak when transformed.
Using pull_request_target carelessly. It can expose secrets to untrusted code.
Very slow workflows. Cache, run jobs in parallel and test only what changed.
Quick recap
A matrix repeats a job across versions or systems.
Store credentials as secrets, never in the YAML.
Triggers include push, pull request, schedule and manual runs.
Limit permissions and treat fork pull requests as untrusted.
Check yourself
1. Where should an API key used by a workflow be stored?
Practice exercise
Turn your CI workflow into a matrix across two language versions.
Add workflow_dispatch and run the workflow manually.
Add a secret and print only whether it exists, never its value.
Protection rules make sure nothing reaches the main branch without review and passing checks, even by accident.
33.1 The problem
Rules in a document are easy to forget. Branch protection turns them into settings that GitHub enforces for everyone, including administrators if you choose.
33.2 Common rules
Open Settings, then Branches (or Rules, then Rulesets), and add a rule for main.
Rule
What it enforces
Require a pull request
No direct pushes to main
Require approvals
At least one or two people must approve
Dismiss stale approvals
New commits cancel old approvals
Require status checks
Chosen workflows must pass first
Require conversation resolution
All review threads must be resolved
Require linear history
Only squash or rebase merges are allowed
Block force pushes
History cannot be rewritten
Block deletions
The branch cannot be deleted
Require signed commits
Commits must carry a verified signature
What a protected main branch demands
33.3 Rulesets
Rulesets are the newer, more flexible way to do this. They can target several branches or tags with patterns, can apply across an organisation, and can run in an evaluate mode first, which reports what would have been blocked without enforcing anything. Use them when you have more than a couple of rules.
33.4 CODEOWNERS
A file named CODEOWNERS assigns people or teams to parts of the codebase. When a pull request touches those files, GitHub asks the owners to review.
# Everything
* @your-username
# Documentation
/docs/ @docs-team
# Workflows need an admin's eye
/.github/workflows/ @your-username
Place the file in .github/, the root or docs/. With the rule Require review from Code Owners, no one can merge changes to those files without an owner's approval.
33.5 A sensible starting setup
Require a pull request with one approval.
Require the CI check to pass.
Block force pushes and deletions.
Add a CODEOWNERS file for sensitive paths.
33.6 Common mistakes
Requiring checks that never run. The pull request will wait forever. Make sure the check name matches your workflow job.
Too strict for a small team. Five required reviewers slows everyone. Match the rules to your team size.
Letting admins bypass rules routinely. The exception becomes the habit.
Quick recap
Protection rules enforce reviews and checks on important branches.
Require pull requests, approvals, status checks and no force pushes.
Rulesets are the modern, flexible alternative.
CODEOWNERS routes reviews to the right people.
Check yourself
1. What does requiring status checks do?
Practice exercise
Protect main in a practice repository so a pull request and a passing check are required.
Try to push directly to main and read the rejection message.
Hooks are small scripts that Git runs automatically at key moments, so mistakes are stopped before they reach the team.
34.1 What a hook is
A hook is a script that Git starts when something happens, such as just before a commit. If the script exits with an error, Git cancels the action. Hooks live in .git/hooks/.
$ ls .git/hooks
applypatch-msg.sample pre-commit.sample pre-push.sample commit-msg.sample
Git ships samples ending in .sample. Remove the suffix to activate one.
34.2 Useful hooks
Hook
Runs
Typical use
pre-commit
Before a commit is made
Lint code, block secrets, run fast tests
commit-msg
After you write a message
Check message format
pre-push
Before a push
Run the full test suite
post-merge
After a merge
Reinstall dependencies
34.3 A simple pre-commit hook
This script refuses any commit that includes a file called .env.
#!/bin/sh
if git diff --cached --name-only | grep -q '^\.env$'; then
echo "Blocked: .env must not be committed."
exit 1
fi
Save it as .git/hooks/pre-commit and make it executable.
$ chmod +x .git/hooks/pre-commit
$ git add .env
$ git commit -m "Add settings"
Blocked: .env must not be committed.
34.4 Hooks are not shared
Everything inside .git stays on your machine, so teammates do not receive your hooks. Two common ways to share them:
Keep hooks in a folder in the repository and point Git at it: git config core.hooksPath .githooks.
Use a hook manager such as the pre-commit framework (Python), Husky (JavaScript) or lefthook.
Git rarely loses committed work, and the reflog is the diary that lets you find it again.
35.1 The safety net
You ran git reset --hard and your commits disappeared from git log. They are not truly gone. Git keeps a private diary, the reflog, of every place HEAD has pointed. As long as a commit appears in it, you can bring it back.
Creating a branch at the old hash is the safest form, because it adds a label to the commit without moving anything else. You can also reset the current branch back to the entry shown by the reflog:
$ git reset --hard HEAD@{1}
35.3 Typical rescue stories
Situation
What to do
Reset too far
Find the old tip in git reflog and reset to it
Deleted a branch by mistake
Find its last commit in the reflog and git switch -c name <hash>
Bad rebase
Find the entry before the rebase started and reset to it
Commits made in a detached HEAD
The reflog lists them; create a branch at the hash
35.4 Per-branch reflog
$ git reflog show feature
Each branch also has its own history of movements.
35.5 How long entries last
Reflog entries are kept for a limited time, by default about 90 days for reachable entries and 30 days for unreachable ones, after which Git's clean-up can remove them. Recover work soon rather than later.
35.6 When the reflog is not enough
Work that was never committed is not in the reflog. A file you edited and discarded with git restore cannot be recovered by Git. Commit early and often to give Git something to remember. For objects not reachable even from the reflog, git fsck --lost-found can list dangling commits, which is a last resort.
35.7 Common mistakes
Panicking and running more commands. Stop and read the reflog first.
Expecting to recover uncommitted work. Git cannot restore what it never saw.
Waiting weeks. Old unreachable entries are eventually cleaned up.
Quick recap
The reflog records where HEAD and branches pointed.
Find the lost commit hash in git reflog, then create a branch at it.
Entries expire after a while, so act soon.
Uncommitted work cannot be recovered from the reflog.
Check yourself
1. You ran git reset --hard and lost commits. What should you try first?
Practice exercise
Make three commits, reset hard back two, then recover them using git reflog.
Bisect finds the commit that introduced a bug, and interactive rebase lets you polish your commits before you share them.
36.1 git bisect: find the bad commit
Suppose a bug appeared somewhere in the last 200 commits. Checking each would take hours. Bisect performs a binary search, halving the range each time, so about eight checks are enough for 200 commits.
Bisecting for a bug
$ git bisect start
$ git bisect bad
$ git bisect good v1.2.0
Bisecting: 97 revisions left to test after this (roughly 7 steps)$ git bisect good
$ git bisect bad
b81e6d2 is the first bad commit$ git bisect reset
36.2 Automating bisect
If you have a test that fails on the bug, let Git run it.
$ git bisect start HEAD v1.2.0
$ git bisect run pytest tests/test_checkout.py
Git repeats the test at each step and reports the first failing commit by itself.
36.3 Interactive rebase: polish history
Before opening a pull request you may want to merge small "fix typo" commits or reword unclear messages. Interactive rebase does that on your own, unshared commits.
--fixup records a correction aimed at an earlier commit, and --autosquash arranges the list for you.
36.5 Safety
Caution
Interactive rebase rewrites history, so use it only on commits that nobody else has. If something goes wrong, run git rebase --abort, or recover through the reflog (Chapter 35).
36.6 Common mistakes
Rebasing shared commits. The golden rule from Chapter 17 applies.
Marking the first line as squash. There is no earlier commit to merge into.
Not knowing a good commit for bisect. Use a release tag or an older commit that you know worked.
Quick recap
git bisect binary-searches history to find the commit that introduced a bug.
git bisect run <command> automates the search.
git rebase -i lets you reword, squash, fixup, drop and reorder commits.
Only rewrite history that is still private.
Check yourself
1. Roughly how many tests does bisect need to search 1,000 commits?
Each step halves the range, so the number is about log2(1000).
Practice exercise
Make four small commits, then squash the middle two with git rebase -i.
Introduce a bug on purpose and find it with git bisect.
Big projects need tools for sharing code between repositories and storing large files, and each has trade-offs.
37.1 Submodules: a repository inside a repository
A submodule pins another repository at an exact commit inside yours. It is useful when you depend on a shared library that lives in its own repository.
$ git submodule add https://github.com/example/shared-lib.git libs/shared-lib
$ git commit -m "Add shared-lib as a submodule"
Git records the submodule's address in .gitmodules and the commit it points to. Cloning needs one more flag, or the folder will be empty.
Often a package manager (pip, npm, Maven) is simpler for libraries. Consider submodules when you need the source and its history together.
37.2 Monorepo or many repositories
Monorepo
Many repositories
Structure
All projects in one repository
One repository per project
Sharing code
Easy, one commit can change several parts
Needs published packages
Permissions
Harder to restrict one part
Per-repository access
Scale
Needs tooling as it grows
Many repositories to manage
Used by
Large engineering teams with strong tooling
Most small teams
Very large repositories can use sparse checkout, which downloads only some folders, and partial clone, which downloads file contents on demand.
$ git clone --filter=blob:none https://github.com/example/big-repo.git
$ git sparse-checkout set services/api
37.3 Large files and Git LFS
Git stores every version of every file forever, so large binary files such as videos, datasets and design files make a repository huge. GitHub warns about files over 50 MB and blocks files over 100 MB. Git LFS (Large File Storage) keeps lightweight pointer files in Git and stores the real content on a separate server.
Tracking rules are saved in .gitattributes, which you commit.
Warning
Track files with LFS before committing them. Files already in history stay there and keep the repository large. Moving them later needs git lfs migrate, which rewrites history.
GitHub gives each account a limited LFS storage and bandwidth allowance, so check the current limits for your plan. Datasets that change often are better kept in dedicated tools for data versioning or in cloud storage.
37.4 Common mistakes
Cloning a repository with submodules and getting empty folders. Use --recurse-submodules.
Committing large datasets to normal Git. Use LFS or external storage.
Moving to a monorepo without build tooling. Builds and tests become slow.
Quick recap
Submodules pin another repository at a specific commit.
Clone with --recurse-submodules.
Monorepos suit big teams with tooling; separate repositories suit most others.
Use Git LFS for large binary files, and set it up before committing them.
Check yourself
1. Why is the folder of a submodule empty after a normal clone?
Practice exercise
Add a small public repository as a submodule and clone your project with --recurse-submodules.
Track *.zip files with LFS in a practice repository.
One leaked password can undo a project, so build safe habits and let GitHub's automatic tools watch for you.
38.1 Rule one: secrets do not belong in Git
Passwords, API keys, tokens and private keys must never be committed. Even if you delete the file later, the secret remains in the history, and public repositories are scanned by bots within minutes.
38.2 If you leak a secret
Respond in this order
Revoking comes first because anyone may already have copied the secret. Cleaning history is a second, optional step.
38.3 Keeping secrets out
Put them in a .env file that is listed in .gitignore, and commit a .env.example with fake values.
Use your platform's secret store: GitHub Actions secrets, a cloud key vault or a password manager.
Turn on secret scanning and push protection in your repository's security settings, which block pushes that contain recognised secrets.
Add a pre-commit hook that scans for secrets (Chapter 34).
38.4 Dependabot
Dependabot watches your dependencies.
Feature
What it does
Dependabot alerts
Warns when a dependency has a known vulnerability
Dependabot security updates
Opens a pull request with the fix
Dependabot version updates
Opens regular pull requests to keep dependencies current
Code scanning with CodeQL analyses your source for common vulnerability patterns and reports them on pull requests. It is free for public repositories and available for private ones on some plans.
38.6 Protect your account
Use a strong, unique password in a password manager.
Turn on two-factor authentication. GitHub requires it for people who contribute code.
Consider passkeys or hardware security keys.
Review Settings, then Sessions and SSH keys now and then, and remove what you do not recognise.
38.7 Signed commits
Anyone can set any name and email in Git, so a commit's author is not proof of identity. A signature proves that the commit came from the holder of a key. GitHub marks such commits Verified. Git can sign with an SSH key, which you may already have from Chapter 23.
Add the same public key in GitHub under SSH and GPG keys, choosing the type Signing key.
38.8 Report vulnerabilities properly
A SECURITY.md file tells visitors how to report a problem privately. GitHub also offers private vulnerability reporting, so researchers do not post exploits in public issues.
38.9 Common mistakes
Thinking a private repository is safe for secrets. Access changes, repositories get forked and leaked.
Deleting the file and calling it fixed. The history still has it.
Ignoring Dependabot pull requests. Old, vulnerable dependencies are a common way in.
Quick recap
Never commit secrets; use .gitignore, secret stores and push protection.
If one leaks, revoke it first, then clean history.
Dependabot and code scanning find vulnerable dependencies and code.
Use two-factor authentication and sign your commits.
Check yourself
1. You pushed an API key by mistake. What is the FIRST step?
The key may already be copied, so make it useless immediately.
Practice exercise
Create .env.example and add .env to .gitignore in a practice project.
Add a dependabot.yml file and enable secret scanning in the repository settings.
Set up SSH commit signing and look for the Verified badge on GitHub.
Choose a team workflow, avoid common mistakes, and keep a cheat sheet, interview questions and glossary close at hand.
Part 6Chapter 394 min read
Branching strategies
A branching strategy is the team's agreement on how branches are named, used and merged, and the right one depends on how often you release.
39.1 Why you need one
Git lets you branch in any way you like, which is exactly the problem. Without an agreement, every developer does something different, and merging becomes painful. A strategy gives everyone the same map.
39.2 GitHub Flow
The simplest model, used by many web teams. main is always ready to deploy. All work happens on short-lived branches that are merged by pull request.
GitHub Flow: short branches that merge back into main quicklyGitHub Flow in five steps
39.3 Git Flow
A heavier model for software with versions and scheduled releases, such as mobile apps and libraries. It has permanent branches (main and develop) and temporary ones (feature, release, hotfix).
Git Flow: features merge into develop, and releases flow into main
Branch
Purpose
main
Released code only, tagged with versions
develop
Integration of finished features
feature/*
One feature each, branched from and merged back into develop
release/*
Final fixes before a release
hotfix/*
Urgent fixes branched from main
39.4 Trunk-based development
Everyone works on one shared branch, the trunk (main). Changes are tiny and merged at least daily, directly or through very short-lived branches. Unfinished features are hidden behind feature flags, switches that turn code on or off without a new deployment. It is the style used by many large teams that deploy continuously, and it relies on strong automated tests.
39.5 Comparing them
GitHub Flow
Git Flow
Trunk-based
Long-lived branches
main
main, develop
main
Branch lifetime
Days
Days to weeks
Hours to a day
Release style
Continuous
Scheduled versions
Continuous
Complexity
Low
High
Low, but needs discipline
Needs strong CI
Yes
Helpful
Essential
Best for
Web apps and small teams
Packaged software with versions
Mature teams that deploy often
39.6 How to choose
Deploy several times a week? Start with GitHub Flow.
Ship numbered versions and support older ones? Consider Git Flow, or a simplified release-branch version of it.
Have excellent tests and an experienced team? Try trunk-based development.
39.7 Common mistakes
Choosing Git Flow because it looks professional. It adds overhead that small teams do not need.
Long-lived feature branches. The longer a branch lives, the worse the conflicts.
Having a strategy nobody wrote down. Put it in CONTRIBUTING.md.
Quick recap
A branching strategy is a shared agreement on how work flows.
GitHub Flow: short branches and always-deployable main.
Git Flow: develop, release and hotfix branches for versioned software.
Trunk-based: tiny, frequent merges and feature flags.
Check yourself
1. Which strategy fits a web app that deploys several times a day?
Practice exercise
Write a short branching policy for your team and add it to CONTRIBUTING.md.
Practise one full GitHub Flow cycle: branch, commit, pull request, merge, delete.
Use the first when the merge is still in progress, the second right after a merge you have not pushed, and the third for a merge that is already pushed.
GitHub does not accept your SSH key. Check ssh -T git@github.com and ssh-add -l
Authentication failed
Use a personal access token or SSH; passwords are not accepted
refusing to merge unrelated histories
Two histories share no common commit. On stage 3, do not add --allow-unrelated-histories and do not merge origin/greeting-a or origin/greeting-b. Create both branches from main. Elsewhere, use the flag once, knowingly, only when you mean to join two separate repositories
src refspec main does not match any
There is no commit yet, or your branch has another name. Commit, then git branch -M main
Your local changes would be overwritten
Commit or git stash before switching or pulling
CONFLICT (content)
Fix the markers, git add, then commit or --continue (Chapter 16)
detached HEAD
You are on a commit, not a branch. git switch -c name keeps your work
warning: LF will be replaced by CRLF
Line-ending difference. Set core.autocrlf (Chapter 5)
remote: error: File is 120 MB
GitHub blocks files over 100 MB. Use Git LFS (Chapter 37)
41.7 I think I lost my work
Do not panic and do not run more commands blindly. Run git reflog, find the commit, and create a branch at it (Chapter 35). Uncommitted edits are the only thing Git cannot return.
41.8 Safety habits
Run git status before and after every important command.
Commit often so there is something to go back to.
Create a backup branch before a risky operation: git branch backup.
Use --dry-run or -n where a command offers it.
Prefer --force-with-lease to --force.
Quick recap
Most mistakes can be fixed with reset, revert, amend or the reflog.
Read error messages: they usually name the fix.
git status and a backup branch make risky work safe.
Uncommitted work cannot be recovered, so commit often.
Check yourself
1. You committed to main by mistake and have not pushed. What keeps your work safe?
Practice exercise
Practise the wrong-branch fix in a throwaway repository.
Cause a rejected push and a merge conflict on purpose, and fix both.
These questions come up again and again in developer interviews, with short answers you can explain in your own words.
Tap a question to see a model answer. Try answering aloud first.
43.1 Basics
What is the difference between Git and GitHub?
Git is a free, distributed version control tool that runs on your computer. GitHub is an online service that hosts Git repositories and adds collaboration features such as pull requests, issues and Actions.
What is a commit?
A commit is a snapshot of the staged project files with an author, date, message and a link to its parent commit. It is identified by a hash.
Explain the three areas in Git.
The working directory holds the files you edit, the staging area holds the changes chosen for the next commit, and the repository stores the committed history in the .git folder.
What is the difference between git fetch and git pull?
Fetch downloads new commits and updates remote-tracking branches without changing your files. Pull runs fetch and then merges (or rebases) the result into your current branch.
What does git clone do compared with git fork?
Clone copies a repository, with its history, to your computer. A fork is a copy of a repository in your own GitHub account, used to propose changes to projects you do not own.
What is HEAD?
HEAD is a pointer to your current position, usually the branch you are on, which in turn points to its latest commit.
43.2 Branching and merging
What is a branch, and why are branches cheap in Git?
A branch is a movable pointer to a commit. Creating one only writes a tiny file with a hash, so it is instant and uses almost no space.
What is the difference between merge and rebase?
Merge joins two histories with a merge commit and keeps the real branching shape. Rebase replays your commits on top of another branch, creating new commits and a linear history.
When should you not rebase?
Never rebase commits that are already shared with others, because it rewrites history and breaks their copies.
What is a fast-forward merge?
When the target branch has no new commits, Git just moves its pointer forward to the other branch's tip, without creating a merge commit.
How do you resolve a merge conflict?
Open the conflicted files, choose or combine the correct content, remove the conflict markers, git add the files, then commit or run the continue command.
What does git cherry-pick do?
It copies one or more specific commits onto the current branch, creating new commits with new hashes.
What is git stash used for?
It temporarily shelves uncommitted changes so you can switch tasks with a clean working directory, and you bring them back later with git stash pop.
43.3 Undoing and recovery
What is the difference between git reset and git revert?
Reset moves the branch pointer back and rewrites history, so it is for local commits. Revert adds a new commit that cancels an earlier one, so it is safe for shared history.
Explain reset --soft, --mixed and --hard.
All three move the branch. Soft keeps changes staged, mixed (the default) unstages them but keeps the files, and hard also discards the file changes.
How would you recover a commit you lost after a hard reset?
Run git reflog to find the commit hash, then create a branch at that hash or reset to it.
How do you undo the last commit but keep the work?
git reset --soft HEAD~1 if it is not pushed. If it is pushed, use git revert instead.
You committed a password. What do you do?
Revoke and replace the password immediately. Then remove it from history with a tool such as git filter-repo, force-push, and ask collaborators to re-clone.
43.4 GitHub and teamwork
What is a pull request, and why use one?
A pull request proposes merging one branch into another and hosts review, discussion and automated checks before the code reaches the main branch.
What are the merge options on a pull request?
Merge commit keeps all commits plus a merge commit, squash and merge combines the branch into one commit, and rebase and merge replays the commits without a merge commit.
What is GitHub Actions?
A built-in automation service that runs workflows, defined in YAML files, in response to events such as pushes and pull requests, commonly for continuous integration and deployment.
What is branch protection?
Repository rules that stop risky actions on important branches, such as pushing directly, force-pushing or merging without approvals and passing checks.
Describe a good Git workflow for a team.
Branch from main for each change, commit small and clearly, open a pull request, get a review and passing checks, merge, delete the branch. Protect main and keep branches short-lived.
How do SSH keys work with GitHub?
You keep a private key on your computer and give GitHub the public key. GitHub verifies that you hold the private key without it ever being sent.
What is .gitignore?
A file listing patterns of files Git should not track, such as secrets, dependencies and build output.
What are tags used for?
Tags permanently name a commit, usually to mark a release such as v1.0.0.
Quick recap
Be ready to explain the three areas, merge versus rebase and reset versus revert.
Describe a team workflow from branch to pull request to merge.
Know how to recover lost work with the reflog.
Practice exercise
Answer five questions aloud without looking, then check the model answers.
Plain-language definitions of the terms used in this handbook.
44.1 Terms
Term
Meaning
Action
A reusable step used in a GitHub Actions workflow
Amend
To replace the most recent commit with a new one
Artifact
A file saved from a workflow run, such as a report
Bisect
Binary search through history to find the commit that introduced a bug
Blame
A view showing who last changed each line of a file
Branch
A movable pointer to a commit; a line of work
Checkout
To switch branches or restore files (older command, now partly switch and restore)
Cherry-pick
Copy a single commit onto another branch
CI/CD
Continuous integration and delivery: automatic building, testing and deploying
Clone
A full copy of a repository, including history
Commit
A saved snapshot with message, author and parent
Conflict
A merge problem where both sides changed the same lines
Dependabot
GitHub's tool for dependency alerts and update pull requests
Detached HEAD
HEAD pointing straight at a commit instead of a branch
Diff
A comparison showing added and removed lines
Fast-forward
A merge that only moves a pointer, with no new commit
Fetch
Download remote commits without changing your files
Fork
Your own GitHub copy of someone else's repository
HEAD
Pointer to your current branch or commit
Hook
A script Git runs at a set moment, such as before a commit
Index
Another name for the staging area
Issue
A tracked bug, task or request on GitHub
LFS
Large File Storage, for big binary files
Main
The conventional name of the default branch
Merge
Combine the work of two branches
Monorepo
One repository holding many projects
Origin
The default name of the main remote
Pull
Fetch and merge in one step
Pull request
A proposal to merge a branch, with review and checks
Push
Upload commits to a remote
Rebase
Replay commits on top of another base
Reflog
Local diary of where HEAD and branches have pointed
Remote
A repository copy hosted elsewhere
Repository
A project and its complete history
Revert
Create a commit that cancels an earlier one
Squash
Combine several commits into one
Staging area
Where changes wait to be included in the next commit
Stash
A shelf for unfinished changes
Submodule
A repository nested inside another at a fixed commit
Tag
A permanent name for a commit
Upstream
The original repository you forked, or the remote branch a local branch follows
Working directory
The files you see and edit
Note
You have reached the end of the handbook. The best next step is practice. Build a small project, open a pull request, review someone else's, and contribute to an open-source project you like.