Fundamentals 51 min read

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.

WeDoctor Frontend Technology
WeDoctor Frontend Technology
WeDoctor Frontend Technology
Master Git Internals: An Interactive Tutorial Beyond 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 .

git components
git components

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.git

The 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 clone

also creates a new folder named

git_training

in the directory where you run the command.

Cloning the remote repo
Cloning the remote repo

Add Some New Content

Someone has already added

Alice.txt

to 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 status

to 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.txt

is untracked;

git status

also suggests how to add it.

Run

git add Bob.txt

to 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 &lt;commit&gt;^!

. To compare any two commits, use

git diff &lt;from commit&gt; &lt;to commit&gt;

.

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 &lt;branch name&gt;

. For example, to create a branch named

change_alice

:

git branch change_alice

This 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_alice

to create and switch in one step).

State after switching branch
State after switching branch

Now you can add and commit changes to

Alice.txt

on this branch without affecting master . When ready, push the branch:

git push

If the remote does not have this branch, Git will suggest

git push --set-upstream origin change_alice

to 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_alice

into 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.

Before fast forward merge
Before fast forward merge

After the merge, master points to the same commit as

change_alice

:

After fast forward merge
After fast forward merge

Merge Conflict Branch

Now create a conflict: on master modify

Bob.txt

and commit; then switch to

change_alice

, modify

Alice.txt

and commit. The histories diverge.

Divergent commits
Divergent commits

When you try to merge, Git cannot fast‑forward and opens a merge‑commit editor for you to write a message.

Merging branches
Merging branches

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>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
Hi! I'm Bob. I've been here for a while now.
=======
Hi! I'm Bobby. I'm new here.
&gt;&gt;&gt;&gt;&gt;&gt;&gt; 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_patrick

that adds

Patrick.txt

and another branch where you added text to

Alice.txt

. Their histories diverge.

History before a rebase
History before a rebase

Switch to

add_patrick

and 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.

History after rebase
History after rebase

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 &lt;commit&gt;

to apply a specific commit from another branch onto the current branch.

Cherry‑picking a single commit
Cherry‑picking a single commit

You can also cherry‑pick a range with

git cherry-pick &lt;from&gt;..&lt;to&gt;

:

Cherry‑picking commit range
Cherry‑picking commit range

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~3

to 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

reword

to edit its message, and change the third line to

squash

to 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-lease

to ensure you do not overwrite others' work.

What happens in a push --force-with-lease
What happens in a push --force-with-lease

Never rewrite public history unless you are certain and use the safe

--force-with-lease

option.

Read History

Git keeps a reference log (reflog) that records every update to branch pointers, commits, rebases, etc.

Run

git reflog

to 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 0b22064

or

git reset HEAD@{4}

, to undo the rebase.

Alternatively, start another interactive rebase from the desired point to edit, squash, or drop commits again.

Reference logs
Reference logs
GitrebaseTutorialVersion ControlcommandsBranchingmerging
WeDoctor Frontend Technology
Written by

WeDoctor Frontend Technology

Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.