Git by Example
Git by Example
Introduction
.Preface
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5.
Concepts
1. Basics
.Init
. . .repo
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7.
. . . . file
Add . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8.
. . . . file
Edit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9.
. . . . . . . . file
Rename . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10
..
. . . . . . file
Delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .11
..
. . . . . .current
Show . . . . . . status
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12
..
. . . . . .commit
Show . . . . . . .log
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13
..
. . . . . .specific
Show . . . . . . .commit
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15
..
. . . . . . .repo
Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .16
..
TABLE OF CONTENTS 2
4. Undo
.Amend
. . . . . . commit
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .33
..
. . . . . .uncommitted
Undo . . . . . . . . . . . .changes
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .35
..
. . . . . .local
Undo . . . . commit
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .37
..
. . . . . .remote
Undo . . . . . . commit
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .39
..
. . . . . . . history
Rewind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41
..
. . . . . .changes
Stash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .43
..
5. Advanced
.Log
. . . summary
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45
..
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .47
Worktree ..
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .49
Bisect ..
. . . . . . .checkout
Partial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .52
..
. . . . . . .clone
Partial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .54
..
. . . . . thoughts
Final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .55
..
TABLE OF CONTENTS 3
Preface
Git is the distributed version control system used in software development to‐
day. It’s very powerful, but also known for its not-so-obvious syntax.
I got tired of googling the same Git commands over and over again. So I cre‐
ated this step-by-step guide to Git operations, from basic to advanced. You
can read it from start to finish to (hopefully) learn more about Git, or jump to a
specific use case that interests you.
PREFACE 4
Concepts
This is the only piece of theory in the guide. I’ll keep it short and simplified to
the π == 3 level. Please don’t judge me if you’re a Git master.
A working tree is the slice of the project at any given moment (usually it’s the
current moment). When you add or edit code, you change the working tree.
A staging area is where you stage the changes from the working tree before
making them permanent.
When you make a change in the staging area permanent, it is removed from
the staging area and committed to the local repo. A commit is the permanent
record of that change. The repo contains all the commits that have been
made.
CONCEPTS 5
When you checkout a specific commit, the working tree is updated to reflect
the project state at the time of that commit.
Local and remote repos are frequently synchronized so that all repos contain
all commits from all developers.
A tag is a named state of the project. Typically, tags are created on the main
branch for important milestones such as releases.
Now that the boring stuff is out of the way, let’s get to the recipes!
CONCEPTS 6
1. Basics
Let’s start with basic Git operations on a local repo.
Init repo
Create an empty repo:
git init
Set user name and email for the repo (they are required):
Use the global flag to set the name and email at the OS user level instead
of the repo level.
f le:/sandbox/.gitconf g user.email=sandbox@example.com
f le:/sandbox/.gitconf g user.name=sandbox
f le:/sandbox/.gitconf g init.defaultbranch=main
f le:.git/conf g core.repositoryformatversion=0
f le:.git/conf g core.f lemode=true
f le:.git/conf g core.bare=false
f le:.git/conf g core.logallrefupdates=true
f le:.git/conf g user.email=alice@example.com
f le:.git/conf g user.name=Alice Zakas
BASICS 7
Add file
Create a file and add it to the staging area:
BASICS 8
Edit file
Edit the previously committed file:
git diff
Note that a does not add new files, only changes to the already committed
files.
BASICS 9
Rename file
Rename the previously committed file:
The change is already in the staging area, so git diff won’t show it. Use
cached :
BASICS 10
Delete file
Delete the previously committed file:
git rm message.txt
rm 'message.txt'
The change is already in the staging area, so git diff won’t show it. Use
cached :
BASICS 11
Show current status
Edit the previously committed file and add the changes to the staging area:
git status
On branch main
Changes to be committed:
(use "git restore staged <f le> " to unstage)
modif ed: message.txt
Untracked f les:
(use "git add <f le> " to include in what will be committed)
praise.txt
BASICS 12
Show commit log
Show commits:
git log
commit ecdeb79aad4565d8d7d725678ffadc48b3cdec52
Author: sandbox <sandbox@example.com>
Date: Thu Mar 14 15 00 00 2024 +0000
edit message
commit 3a2bd8f0929c605193120bd1ad12732f49457e99
Author: sandbox <sandbox@example.com>
Date: Thu Mar 14 15 00 00 2024 +0000
add message
BASICS 13
Show commits as an ASCII graph:
* commit ecdeb79aad4565d8d7d725678ffadc48b3cdec52
| Author: sandbox <sandbox@example.com>
| Date: Thu Mar 14 15 00 00 2024 +0000
|
| edit message
|
* commit 3a2bd8f0929c605193120bd1ad12732f49457e99
Author: sandbox <sandbox@example.com>
Date: Thu Mar 14 15 00 00 2024 +0000
add message
BASICS 14
Show specific commit
Show the last commit contents:
commit ecdeb79aad4565d8d7d725678ffadc48b3cdec52
Author: sandbox <sandbox@example.com>
Date: Thu Mar 14 15 00 00 2024 +0000
edit message
commit 3a2bd8f0929c605193120bd1ad12732f49457e99
Author: sandbox <sandbox@example.com>
Date: Thu Mar 14 15 00 00 2024 +0000
add message
Use HEAD~n to show the nth-before-last commit or use the specific commit
hash instead of HEAD~n .
BASICS 15
Search repo
There are 3 commits, each adding a new line to message.txt :
cc5b883 no debates
2774a8b is great
31abe57 is awesome
cat message.txt
git is awesome
git is great
there is nothing to debate
HEAD~1:message.txt:git is great
BASICS 16
2. Branch and merge
Let’s dive into the wondrous world of merging.
Branch
Show branches (there is only main now):
git branch
* main
git branch
main
* ohmypy
a715138 ohmy.py
We are now on the main branch, let’s merge the ohmypy branch back into
main:
There are no conflicts, so git commits automatically. Show the new commit
history:
We are now on the main branch, let’s rebase the ohmypy branch back into
main:
Rebasing (1/1)
Successfully rebased and updated refs/heads/main.
Rebasing rewrites history. So it’s better not to rebase branches that have al‐
ready been pushed to remote.
If we do git merge ohmypy to merge the ohmypy branch into main , the main
branch will receive all three commits from ohmypy.
Sometimes we prefer to “squash” all the branch commits into a single com‐
mit, and then merge it into main. Let’s do it.
Combine all ohmypy changes into a single commit in the working directory:
* 008dce6 ohmy[py,sh,lua]
|\
| * 4f2a17f ohmy[py,sh,lua]
| * b9a7d0f ohmy.lua
| * 5ca4d55 ohmy.sh
| * a715138 ohmy.py
* | ecdeb79 edit message
|/
* 3a2bd8f add message
cat message.txt
git is awesom
I’m not ready to merge the entire ohmypy branch, so I will cherry-pick the
commit:
cat message.txt
git is awesome
Push
Alice wants to clone our repo and make some changes.
cd /tmp/alice
git conf g user.email alice@example.com
git conf g user.name "Alice Zakas"
git push
git pull
git pull
cat message.txt
<<<<<<< HEAD
git is awesome
Git is awesome!
>>>>>>> b9714f2c59c7dbd1205cf20e0a99939b7a686d97
main
* ohmypy
remotes/origin/main
remotes/origin/ohmypy
git fetch
Remote has the ohmypy branch, but it’s not checked out locally:
git branch
* main
Show branches:
git branch
main
* ohmypy
Show tags:
git tag l
0.1.0
0.1.0-alpha
Delete tag:
Amend commit
Edit a file and commit:
Show commits:
UNDO 33
Git has replaced the last commit:
To change the commit message for one of the last n commits, use
git rebase i HEAD~n (interactive) and follow the instructions on the
screen.
Amend only works if the commit has not yet been pushed to the remote repo!
UNDO 34
Undo uncommitted changes
Edit the previously committed file and add the changes to the staging area:
git status
On branch main
Changes to be committed:
(use "git restore staged <f le> " to unstage)
modif ed: message.txt
The local file is still modified, but it’s not staged for commit:
git status
On branch main
Changes not staged for commit:
(use "git add <f le> " to update what will be committed)
(use "git restore <f le> " to discard changes in work dir)
modif ed: message.txt
no changes added to commit (use "git add" and/or "git commit a")
UNDO 35
Now let’s discard the changes altogether:
cat message.txt
git is awesom
UNDO 36
Undo local commit
I changed my mind about the last commit and I want to undo it.
Show commits:
git status
On branch main
Changes to be committed:
(use "git restore staged <f le> " to unstage)
modif ed: message.txt
UNDO 37
To remove both the commit and the local changes, use hard instead of
soft :
Reset only works if the commit has not yet been pushed to the remote repo!
UNDO 38
Undo remote commit
I changed my mind about the last commit and I want to undo it, but the com‐
mit is already pushed to the remote repo.
Show commits:
You can revert to nth-before-last commit by using HEAD~n or use the specific
commit hash instead of HEAD~n .
Since the commit has already been pushed, git can’t delete it. Instead it cre‐
ates an “undo” commit:
UNDO 39
Push the “undo” commit to the remote:
git push
UNDO 40
Rewind history
Show commits:
git reflog
UNDO 41
This resets the entire repo and the working tree to the moment of HEAD@{3} :
* a715138 ohmy.py
* 3a2bd8f add message
UNDO 42
Stash changes
Edit the previously committed file:
Let’s say we need to switch to another branch, but we don’t want to commit
the changes yet.
git stash
UNDO 43
Now we can switch to another branch and do something:
Switch back to the main branch and re-apply the latest changes from the
stash:
On branch main
Changes not staged for commit:
(use "git add <f le> " to update what will be committed)
(use "git restore <f le> " to discard changes in work dir)
modif ed: message.txt
no changes added to commit (use "git add" and/or "git commit a")
Dropped refs/stash@{0} (96af1a51462f29d7b947a7563938847efd5d5aeb)
pop returns changes from the stack in “last in, first out” order.
UNDO 44
5. Advanced stuff
While git gurus probably know all about these features, most developers have
never heard of them. Let’s fix that.
Log summary
Since the 1.0 release (tag v1.0 ), we have 6 commits from 3 contributors:
%h commit hash
%an author
%s message
%d decoration (e.g. branch name or tag)
ADVANCED STUFF 45
alice (2)
go is great
no debates
bob (3)
ohmy.py
ohmy.sh
ohmy.lua
sandbox (1)
edit message
List contributors along with the number of commits they have authored:
3 bob
2 alice
1 sandbox
ADVANCED STUFF 46
Worktree
I’m in the middle of something important in the ohmypy branch:
pwd
/tmp/repo
branches
main
* ohmypy
status
On branch ohmypy
Changes to be committed:
(use "git restore staged <f le> " to unstage)
new f le: ohmy.py
Suddenly I need to fix an annoying typo in the main branch. I can stash the lo‐
cal changes with git stash , or I can checkout multiple branches at the same
time with git worktree .
cd /tmp/hotf x
echo "git is awesome" > message.txt
git commit am "f x typo"
ADVANCED STUFF 47
[hotf x c3485cd] f x typo
1 f le changed, 1 insertion(+), 1 deletion(-)
Now I can return to /tmp/repo and continue working on the ohmypy branch.
ADVANCED STUFF 48
Bisect
I have 5 poorly named commits:
2f568eb main.sh
31ed915 main.sh
f8b2baf main.sh
5e0cf35 main.sh
8f0f1e4 main.sh
51c34ff test.sh
sh test.sh
I will use the bisection algorithm to find the commit that introduced the bug:
ADVANCED STUFF 49
The current state is obviously buggy, but I’m pretty sure the first “main.sh”
commit was good:
Git has automatically checked out the middle commit. Let’s test it:
sh test.sh
PASS
Git has automatically checked out the middle commit. Let’s test it:
sh test.sh
ADVANCED STUFF 50
The test fails. Show the commit details:
git show
commit 31ed915660c42a00aa30b51520a16e3f48201299
Author: sandbox <sandbox@example.com>
Date: Thu Mar 14 15 00 00 2024 +0000
main.sh
This is the commit that introduced the bug (subtraction instead of addition)!
ADVANCED STUFF 51
Partial checkout
The remote repo looks like this:
.
├── go.mod
├── main.go
├── products
│ └── products.go
└── users
└── users.go
ADVANCED STUFF 52
Only the root and users directories are checked out:
tree
.
├── go.mod
├── main.go
└── users
└── users.go
1 directories, 3 f les
ADVANCED STUFF 53
Partial clone
The partial checkout approach we tried earlier still clones the entire repo. So if
the repo itself is huge (which is often the case if it has a long history or large
binary files), the clone step can be slow and traffic-intensive.
To reduce the amount of data downloaded during cloning, use partial clone
with one of the following commands:
In both cases, git will lazily fetch the missing data later when needed.
Note that for this to work, the remote server should support partial cloning
(GitHub does).
ADVANCED STUFF 54
Final thoughts
We’ve covered important Git operations, from basic editing to branching and
merging, remote syncing, undoing changes, and performing some moderate
magic. I hope Git incantations make a little more sense to you now than they
did before.
If you find this book useful — please spread the word and subscribe to my oth‐
er projects at antonz.org/subscribe.
Now go and put your knowledge to good use. And may Git be with you!
FINAL THOUGHTS 55