Master Git Internals: An Interactive Tutorial Beyond Commands
This interactive Git tutorial teaches you how Git works under the hood—covering repositories, cloning, adding, committing, pushing, branching, merging, rebasing, cherry‑picking, resolving conflicts, rewriting history, and using reflog—so you can understand the concepts instead of just memorising commands.
This interactive Git tutorial aims to teach you how Git works, not just which commands to run.
Do you want to learn Git? If you want to understand how it works rather than just execute commands, this tutorial is for you. Let's get started!
This tutorial is based on Rachel M. Carmena's blog "How to teach Git" and the Git Book and Reference page.
Read history
Overview
Create a remote repository
Add new content
Modify content
Branch
Merge
Fast‑forward merge
Merge conflict branch
Resolve conflict
Rebase
Update local environment from remote
Pick (cherry‑pick)
Rewrite history
Read history
Overview
As shown in the diagram, there are four modules. The isolated one is called a remote repository , and the three that are together are called the development environment .
The remote repository is where you push changes to share with others, and you can also pull changes from it. If you have used other version‑control systems, this concept is not new.
The development environment on your local machine consists of three parts: the working directory , the staging area , and the local repository . Before using Git, we will learn these concepts.
Select a location for your development environment—your home directory or any folder where you want the project; you don’t need to create a new folder for the environment.
Create a Remote Repository
Now we will create a remote repository and import its contents to your computer.
I recommend using this repository:
https://github.com/UnseenWizzard/git_training.git.
Use the following command to clone the repository: git clone https://github.com/UnseenWizzard/git_training.git Since this example repository does not allow others to push, fork it to your own GitHub account (the fork button is at the top right).
After forking, clone it to your local environment:
git clone https://github.com/{YOUR USERNAME}/git_training.gitThe remote repository is now copied to two locations: your working directory and your local repository .
You can see how Git performs distributed version control: the local repository is a copy of the remote repository, but it is not shared with anyone yet.
git clonealso creates a new folder named
git_trainingin the directory where you run the command.
Add Some New Content
Someone has already added
Alice.txtto the remote repository. To give Alice a companion, we will create
Bob.txt.
This step adds the file to your working directory .
Now your working directory contains two kinds of files: tracked files (known to Git) and untracked files (unknown to Git).
Run
git statusto see what is happening in your working directory, which branch you are on, whether your local repository differs from the remote, and the status of tracked and untracked files.
You will see that
Bob.txtis untracked;
git statusalso suggests how to add it.
Run
git add Bob.txtto stage the file. After adding all changes, you can commit them to the local repository.
When you commit, a text editor opens so you can write a commit message describing the significance of the change. After saving and closing, the commit is added to the local repository.
You can also commit directly with
git commit -m "Add Bob". For small changes, a one‑line message is enough.
To see the list of commits, run
git log. It shows each commit’s hash, author, date, and message.
<code>commit 87a4ad48d55e5280aa608cd79e8bce5e13f318dc (HEAD -> master)
Author: {YOU} <{YOUR EMAIL}>
Date: Sun Jan 27 14:02:48 2019 +0100
Add text to Bob
commit 8af2ff2a8f7c51e2e52402ecb7332aec39ed540e (origin/master, origin/HEAD)
Author: {YOU} <{YOUR EMAIL}>
Date: Sun Jan 27 13:35:41 2019 +0100
Add Bob
commit 71a6a9b299b21e68f9b0c61247379432a0b6007c
Author: UnseenWizzard <[email protected]>
Date: Fri Jan 25 20:06:57 2019 +0100
Add Alice
commit ddb869a0c154f6798f0caae567074aecdfa58c46
Author: Nico Riedmann <[email protected]>
Date: Fri Jan 25 19:25:23 2019 +0100
Add Tutorial Text
Changes to the tutorial are all squashed into this commit on master, to keep the log free of clutter that distracts from the tutorial
See the tutorial_wip branch for the actual commit history</code>Note the interesting points:
The first two commits were made by me.
In the remote repository, the initial commit of Bob was on the master branch.
The latest commit in the local repository is the one we just made.
Actual commit hashes may differ. To learn how Git generates these IDs, read the linked article.
To compare this commit with a previous one, run
git diff <commit>^!. To compare any two commits, use
git diff <from commit> <to commit>.
Branch
Branching is an essential part of Git and one of the reasons Git is powerful.
When you clone a remote repository, Git automatically creates a default branch called master .
Usually you work on your own branch until you are satisfied, then merge the changes into master .
Git hosting services like GitLab and GitHub can protect branches (e.g., the master branch is often protected).
You can create a new branch with
git branch <branch name>. For example, to create a branch named
change_alice:
git branch change_aliceThis adds a new branch pointer in your local repository. Your working directory and staging area are not tied to a branch, but commits always go to the current branch.
Switch to the new branch with
git checkout change_alice(or
git checkout -b change_aliceto create and switch in one step).
Now you can add and commit changes to
Alice.txton this branch without affecting master . When ready, push the branch:
git pushIf the remote does not have this branch, Git will suggest
git push --set-upstream origin change_aliceto create it and set the upstream.
You can also set the upstream of an existing remote branch with git branch --set-upstream-to=origin/change_alice .
Merge
Since you and others usually work on separate branches, you need to merge branches to bring changes together.
To merge
change_aliceinto master , first checkout master and then run
git merge change_alice.
Fast‑Forward Merge
If master has no new commits and the branch you are merging is ahead, Git can simply move the master pointer forward—this is a fast‑forward merge.
After the merge, master points to the same commit as
change_alice:
Merge Conflict Branch
Now create a conflict: on master modify
Bob.txtand commit; then switch to
change_alice, modify
Alice.txtand commit. The histories diverge.
When you try to merge, Git cannot fast‑forward and opens a merge‑commit editor for you to write a message.
The new merge commit has two parents: the last commit on master and the commit from
change_alice.
Resolve Conflict
Both branches edited the same line in
Bob.txt, causing a conflict.
<code><<<<<<< HEAD
Hi! I'm Bob. I've been here for a while now.
=======
Hi! I'm Bobby. I'm new here.
>>>>>>> bobby_branch</code>Manually edit the file to keep the desired content and remove the conflict markers, then stage and commit:
git add Bob.txt git commit(this creates a merge commit).
Rebase
Rebase is another way to integrate changes from one branch into another by moving the base of a branch.
Suppose you have a branch
add_patrickthat adds
Patrick.txtand another branch where you added text to
Alice.txt. Their histories diverge.
Switch to
add_patrickand run
git rebase master. Git rewinds the branch to the common ancestor, then re‑applies the commits on top of master :
<code>First, rewinding head to replay your work on top of it...
Applying: Add Patrick</code>After rebasing, the commit that used to point to the old base now points to the tip of master , giving a linear history.
Rebasing keeps a clean linear history, which is useful before fast‑forward merges.
Pick (Cherry‑Pick)
Congratulations! You have learned the typical Git commands and how they work. Now let's learn how to cherry‑pick commits.
Use
git cherry-pick <commit>to apply a specific commit from another branch onto the current branch.
You can also cherry‑pick a range with
git cherry-pick <from>..<to>:
Rewrite History
Do you remember rebase? If not, review the earlier sections because rewriting history relies on it.
Sometimes you need to fix a recent commit—add a missing file, improve the commit message, or remove bad code.
To modify the most recent commit, stage the missing changes and run
git commit --amend. The editor opens with the previous message, which you can edit. After saving, the commit hash changes, and the old commit disappears.
You can also change author or date with --author and --date options.
Interactive rebase (
git rebase -i) lets you squash, reorder, edit, or drop commits.
Example: you have three commits—"Add text to Alice", "Add Bob.txt", and "Add more text to Alice". Run
git rebase -i HEAD~3to open an editor:
<code>pick 9e06fca Add text to Alice
pick 062ef13 Add Bob.txt
pick 0b22064 Add more text to Alice
# ... instructions ...</code>Change the second line to
rewordto edit its message, and change the third line to
squashto combine it with the first commit. Also reorder lines so the squash follows the commit it should be merged into.
<code>pick 9e06fca Add text to Alice
squash 0b22064 Add more text to Alice
reword 062ef13 Add Bob.txt</code>After saving, Git will prompt you to edit the combined commit message for the squash and the message for the reworded commit. You can remove ".txt" from the Bob commit message and write a concise message for the combined Alice commit.
When the rebase finishes, the history now has two new commits replacing the original three:
<code>105177b (HEAD -> interactiveRebase) Add Bob
ed78fa1 Add a lot of very important text to Alice
df3ad1d (origin/master, origin/HEAD, master) Add Alice
800a947 Add Tutorial Text</code>Rewriting public history (commits that have been pushed) is risky. If you must rewrite a shared branch, use
git push --force-with-leaseto ensure you do not overwrite others' work.
Never rewrite public history unless you are certain and use the safe
--force-with-leaseoption.
Read History
Git keeps a reference log (reflog) that records every update to branch pointers, commits, rebases, etc.
Run
git reflogto see recent actions:
<code>105177b (HEAD -> interactiveRebase) HEAD@{0}: rebase -i (finish): returning to refs/heads/interactiveRebase
105177b HEAD@{1}: rebase -i (reword): Add Bob
ed78fa1 HEAD@{2}: rebase -i (squash): Add a lot very important text to Alice
9e06fca HEAD@{3}: rebase -i (start): checkout HEAD~3
0b22064 HEAD@{4}: commit: Add more text to Alice
062ef13 HEAD@{5}: commit: Add Bob.txt
9e06fca HEAD@{6}: commit: Add text to Alice
df3ad1d (origin/master, origin/HEAD, master) HEAD@{7}: checkout: moving from master to interactiveRebase</code>If you made a mistake, you can reset to a previous reflog entry, e.g.,
git reset 0b22064or
git reset HEAD@{4}, to undo the rebase.
Alternatively, start another interactive rebase from the desired point to edit, squash, or drop commits again.
WeDoctor Frontend Technology
Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.