Git-Mastery: Lessons

Tour 1: Recording the History of a Folder

Target Usage: To use Git to systematically record the history of files in a folder on your own computer. Specifically, to put a folder under Git's control, choose which file versions to include, and save snapshots of tracked files at chosen points in time.

Motivation: Recording the history of selected files in a folder (e.g., code files of a software project, case notes, files related to an article you are writing) can be useful when you need to refer to past versions.

Lesson plan:

Before learning about Git, let us first understand what revision control is.

   T1L1. Introduction to Revision Control covers that part.

Before you start learning Git, you need to install some tools on your computer.

   T1L2. Preparing to Use Git covers that part.

To be able to save snapshots of a folder using Git, you must first put the folder under Git's control by initializing a Git repository in that folder.

   T1L3. Putting a Folder Under Git's Control covers that part.

To save a snapshot, you start by specifying what to include in it, also called staging.

   T1L4. Specifying What to Include in a Snapshot covers that part.

After staging, you can save the snapshot by creating a commit.

   T1L5. Saving a Snapshot covers that part.

It is useful to visualize the commit timeline, called the revision graph.

   T1L6. Examining the Revision History covers that part.

Recommended: Watch this video!

Tour/lesson videos (such as the one below) are recommended viewing, as they help you build the right mental models about Git before diving into detailed steps covered in the lessons.

T1L1. Introduction to Revision Control


Before learning about Git, let us first understand what revision control is.

This lesson covers that part.

Revision control is the process of managing versions of as they evolve, such as tracking the versions of a project's files. You can do this by hand: each time you make some changes, you save the project folder under a new descriptive name (e.g., Project-Foo-v1.2 (after fixing memory leak)). But this is tedious and error-prone, especially when multiple people work on the same project.

Revision Control Software (RCS) automates revision control. Modern RCS tools can handle large teams working together on projects with thousands of files. RCS tools are also known as Version Control Software (VCS), and by .

Revision vs Version

  • Revision ("How it changed"): A discrete change made to an artifact at a specific point in time. For example, an edit that fixes a typo in the file README.md is a revision to that file.
  • Version ("What it is"): A specific state of an artifact, usually the result of one or more revisions. For example, after fixing that typo, you have a new version of README.md.

In everyday conversation, these two terms are often used interchangeably. We'll do the same in these lessons.

A revision control tool can:

  • track your project's history, recording who made each change, when, why, and what changed.
  • make collaboration easier, for example by helping you spot and resolve conflicting changes made around the same time.
  • help you recover from mistakes, letting you revert to an earlier version and even pinpoint when a problem was introduced.
  • let you work on multiple versions at once and manage the between them.

Git is the most widely used RCS today. It is a free and open-source tool created by Linus Torvalds in 2005 to manage the development of the Linux kernel. Other RCS tools include Mercurial, Subversion (SVN), Perforce, CVS (Concurrent Versions System), Bazaar, TFS (Team Foundation Server), and ClearCase.

GitHub is a web-based platform for hosting projects that use Git for revision control. Other similar services include GitLab, Bitbucket, and SourceForge.


T1L2. Preparing to Use Git


Before you start learning Git, you need to install some tools on your computer.

This lesson covers that part.

Installing Git

To use Git, you need to install Git on your computer.

PREPARATION: Install Git
Windows

Download the Git installer from the official Git website.
Run the installer and make sure to select the option to install Git Bash when prompted.

The screenshots below provide some guidance on the dialogs you might encounter when installing Git. In other cases, go with the default option.






When running Git commands, Windows users should use the Git Bash terminal that comes with Git. To open the Git Bash terminal, hit the key and type git-bash. Some commands might not work in other terminals such as PowerShell.

The installation might not have added a shortcut to the Start Menu. You can navigate to the directory where git-bash.exe is (most likely C:\Program Files\Git\git-bash.exe), and double-click git-bash.exe to open Git Bash.
You can also right-click it and choose Pin to Start or Pin to taskbar.

SIDEBAR: Git Bash Terminal

Git Bash is a terminal application that lets you use Git from the command line on Windows. Since Git was originally developed for Unix-like systems (like Linux and macOS), Windows does not come with a native shell that supports all the commands and utilities commonly used with Git.

Git Bash provides a Unix-like command-line environment on Windows. It includes:

  • A Bash shell (Bash stands for Bourne Again SHell), which is a widely used command-line interpreter on Linux and macOS.
  • Common Unix tools and commands (like ls, cat, ssh, etc.) that are useful when working with Git and scripting.

When pasting text into a Git Bash terminal, you will not be able to use the familiar Ctrl+V key combo to paste. Instead, use Shift+Insert, or right-click on the terminal and use the Paste menu option.

On Windows, you might need to close and open the terminal again for it to recognize changes made elsewhere on the computer (e.g., newly installed software, changes to system variables, etc.).

macOS

Install Homebrew if you don't already have it, and then run brew install git.

Linux

Use your Linux distribution's package manager to install Git. Examples:

  • Debian/Ubuntu: run sudo apt-get update and then sudo apt-get install git.
  • Fedora: run sudo dnf update and then sudo dnf install git.

Verify Git is installed by running the following command in a terminal.

git --version
git version 2._._

The output should display the version number.


Configuring user.name and user.email

Git needs to know who you are to record changes properly. When you save a snapshot of your work in Git, it records your name and email as the author of that change. This ensures everyone working on the project can see who made which changes. Accordingly, you should set the config settings user.name and user.email before you start using Git for revision control.

PREPARATION: Set user.name and user.email

To set the two config settings, run the following commands in your terminal window:

git config --global user.name "<your-name>"
git config --global user.email "<your-email@example.com>"

Example:

git config --global user.name "John Doe"
git config --global user.email "john.doe@example.com"

To check whether they are set as intended, you can use the following two commands:

git config --global user.name
git config --global user.email

Configuring init.defaultBranch

Git has a config property named init.defaultBranch that specifies the default branch name for new repositories (you'll learn more about Git branches in later lessons). Git uses master as the default value, but main is more common now. Git-Mastery uses main too. To make Git behave more consistently with our lessons, you should set this property to main, as described in the panel below:

PREPARATION: Set init.defaultBranch to main

To set the init.defaultBranch config property to main, run the following command in your terminal window:

git config --global init.defaultBranch main

To verify, run the following command:

git config --global init.defaultBranch
main

If you want to set this property back to master later, use git config --global init.defaultBranch master.


Interacting with Git: CLI vs GUI

Git is fundamentally a command-line tool. You primarily interact with it by typing commands in its . This gives you full control over its features and helps you understand what's really happening under the hood.

clients for Git also exist, such as Sourcetree, GitKraken, and the built-in Git support in editors like IntelliJ IDEA and VS Code. These tools provide a more visual way to perform some Git operations.

If you're new to Git, it's best to learn the CLI first. The CLI is universal, always available (even on servers), and helps you build a solid understanding of Git's concepts. You can use GUI clients as a supplement -- for example, to visualize complex history structures.

Mastering the CLI gives you confidence and flexibility, while GUI tools can serve as helpful companions.

PREPARATION: [Optional] Install a GUI client

Optionally, you can install a Git GUI client, such as Sourcetree (installation instructions).

Our Git lessons show how to perform Git operations using Git CLI and Sourcetree; Sourcetree is included only to illustrate how Git GUIs work. It is perfectly fine for you to learn the CLI only.


[image credit: https://www.sourcetreeapp.com]


Installing the Git-Mastery App

In these lessons, we will use Git-Mastery, a companion app we developed to help Git learners. In particular, it provides exercises that let you self-test your Git knowledge, and it verifies whether your solution is correct.

If you are new to Git, we strongly recommend that you install and use the Git-Mastery app.

1. Install the Git-Mastery App

Windows
  • Download the gitmastery.exe file from the latest release.
    Put it in a suitable location (ensure the file name remains gitmastery.exe).

    Do not run the gitmastery.exe file directly! If you do, it will only flash a terminal briefly and disappear.
    Reason: Git-Mastery is a app that you activate by issuing a command via a terminal, not running the executable directly (e.g., by double-clicking the file). More on how to use CLI apps on Windows

  • Add the folder containing gitmastery.exe to your Windows PATH system variable by following this guide.
    E.g., if the file location is C:\Users\Jane\Tools\gitmastery.exe, you should add C:\Users\Jane\Tools to your PATH.

  • Close and reopen the Git Bash terminal (for the updated PATH to take effect).

Windows Defender says gitmastery.exe is a virus?

In some cases, Windows Defender can incorrectly flag gitmastery.exe as a virus. The Git-Mastery team is working on getting the app allowlisted. In the meantime, it is safe to override the warning or block by choosing the Run anyway option (if given) or using the following steps.

  1. Open Windows SecurityVirus & threat protection.
  2. Click Protection history.
  3. Find the blocked gitmastery.exe and click it.
  4. Choose ActionsAllow on device (or Restore).
    After this step, you may need to re-download the file if it was removed previously.

Alternatively, refer to this page to see how to exclude a file from Windows virus scanner (look for the section named 'Exclusions').


macOS
brew tap git-mastery/gitmastery
brew install gitmastery
Linux

Ensure you are running libc version 2.38 or newer (you can use the ldd --version command to check the current version).

Then install the app by running the following commands:

echo "deb [trusted=yes] https://git-mastery.github.io/gitmastery-apt-repo any main" | \
  sudo tee /etc/apt/sources.list.d/gitmastery.list > /dev/null
sudo apt install software-properties-common
sudo add-apt-repository "deb https://git-mastery.github.io/gitmastery-apt-repo any main"
sudo apt update
sudo apt-get install gitmastery

Use an AUR helper to install gitmastery-bin. For example, use yay:

yay -S gitmastery-bin

Alternatively, you can build the PKGBUILD yourself following the instructions on the Arch wiki.


If you are using a Linux distribution that is not yet supported by Git-Mastery, please download the correct binary for your architecture from the latest release.

Install it to /usr/bin to access the binary. The following example uses version 3.3.0.

install -D -m 0755 gitmastery-3.3.0-linux-arm64 /usr/bin/gitmastery

2. To verify the installation, open a terminal, and run the gitmastery --help command from two different folders. Here is an example (IMPORTANT: change the cd command to match your folders):

gitmastery --help
cd ../my-projects  # navigate to a different folder
gitmastery --help

Explanation of cd ../my-projects command

The current version of the app takes about 3 to 5 seconds to respond to a command because it comes with a bundled Python runtime (so users don't need to install Python first) that must load before the command can be executed.

3. In a terminal, navigate to a suitable folder where you want Git-Mastery to place the files and folders it creates.

Do not use a folder controlled by OneDrive, Dropbox, GDrive, etc. for this! Git, and by extension Git-Mastery, can run into problems if Git repositories are placed inside folders controlled by file sync software such as OneDrive, Dropbox, GDrive, etc. 🤔 Why?

Example:

mkdir gitmastery-home
cd gitmastery-home

Explanation of mkdir gitmastery-home command

4. Trigger the initial setup by running the gitmastery setup command in that terminal.

gitmastery setup

The gitmastery setup command will perform the following tasks:

  • Verify that Git is installed.
  • Verify that user.name and user.email are set.
  • Prompt you to specify a name for the git-mastery exercises directory (sometimes called the git-mastery root directory).
    • Recommended: accept the default (i.e., gitmastery-exercises) by pressing Enter.
    • If you choose to specify a different name for that folder, remember to use that name instead whenever our instructions refer to the gitmastery-exercises folder.
    • Caution: do not rename or move this folder later, as doing so can affect the app's functionality.
  • Set up a mechanism to locally track the progress of your exercises.

Notes:

  • If the command fails because check (a) or (b) failed, fix the problem and run the command again.
  • If you want to check the Git setup again later, run the gitmastery check git command.

Git-Mastery App: Commands
Command Run from ... What it does
gitmastery --help anywhere Prints a brief message on how to use the app.
gitmastery <command> --help anywhere Prints a brief explanation of the <command>.
e.g., gitmastery download --help
gitmastery version anywhere Gets the current version of the Git-Mastery app on your machine.
gitmastery setup anywhere Sets up Git-Mastery for your local machine.
gitmastery check git anywhere Verifies that you have set up Git for Git-Mastery.
gitmastery check github anywhere Verifies that you have set up GitHub and GitHub CLI for Git-Mastery.
gitmastery download <exercise name> git-mastery root Sets up the sandbox for the specified exercise.
gitmastery download <hands-on-practical name> git-mastery root Sets up the specified hands-on practical on your computer.
gitmastery verify inside exercise Verifies your exercise attempt. Saves the progress made.
gitmastery progress reset exercise root Resets the progress of the current exercise.
gitmastery progress show git-mastery root Shows a summary of your exercise progress.
gitmastery progress sync on git-mastery root Enables remote progress tracking of exercises.
gitmastery progress sync off git-mastery root Disables remote progress tracking of exercises.

Explanation of 'Run from ...' options:

  • git-mastery root: Run the command from the directory where Git-Mastery exercises are located, aka the exercises directory (default folder name: gitmastery-exercises).
  • exercise root: Run the command in the sandbox folder containing the exercise.
  • inside exercise: Run the command from the sandbox folder containing the exercise, or any subfolder of it.

Updating the Git-Mastery App

Because the Git-Mastery app is under active development, it is likely to get updated frequently. When you run a gitmastery <command>, the output will warn you if there is a new version; update the app immediately by following the instructions in that message.

Windows

Replace your current gitmastery.exe with the latest version from the latest release and restart your terminal.

macOS
brew update
brew upgrade gitmastery
Linux
sudo apt-get update
sudo apt-get install --only-upgrade gitmastery

sudo pacman -S gitmastery-bin



T1L3. Putting a Folder Under Git's Control


To be able to save snapshots of a folder using Git, you must first put the folder under Git's control by initializing a Git repository in that folder.

This lesson covers that part.

Normally, we use Git to manage a revision history of a specific folder, which gives us the ability to revision-control any file in that folder and its subfolders.

To put a folder under Git's control, we initialize a (short name: repo) in that folder. This lets us create repos in different folders and revision-control different clusters of files independently of each other, e.g., files belonging to different projects.

You can follow the hands-on practical below to learn how to initialize a repo in a folder.

What is this? HANDS-ON panels contain hands-on activities you can do as you learn Git. If you are new to Git, we strongly recommend that you do them yourself (even if they appear straightforward), as hands-on usage will help you internalize the concepts and operations better.

HANDS-ON: Initialize a Git repo in a folder

Preparation Choose a folder to put under Git's control. The folder may or may not contain any files. For this practical, let us create a folder named things for this purpose.

You can use the Git-Mastery app to for this practical, or create the sandbox manually. The instructions below cover both options.

Option 1: Create a fresh sandbox using the Git-Mastery app

  • Navigate inside the gitmastery-exercises folder.
  • Run the gitmastery download hp-init-repo command.

The sandbox will be set up inside the gitmastery-exercises/hp-init-repo folder.


Option 2: Manually set up a sandbox

Assuming you have a folder named git-practicals for doing Git hands-on practicals, you can run the following commands.

cd git-practicals
mkdir things

Avoid putting Git repos inside cloud-synced (e.g., OneDrive, Dropbox) folders. Multiple tools trying to detect/sync changes in the same folder can cause conflicts and unexpected behavior.
If you want to access project files from multiple computers, use Git instead of cloud syncing tools.

1 Then, cd into it. What is cd? For example,

cd hp-init-repo/things

2 Run the git status command to check the status of the folder.

git status
fatal: not a git repository (or any of the parent directories): .git

Don't panic. The error message is expected. It confirms that the folder currently does not have a Git repo.

3 Now, initialize a repository in that folder.

 CLI

Use git init to initialize the repo.

git init
Initialized empty Git repository in <path-to-repo>/things/.git/

Note how the output mentions the repo being created in things/.git/ (not things/). More on that later.

Sourcetree on Windows

Click FileClone/New…, then click the + Create button on the top menu bar.

Enter the location of the directory and click Create.

To open an existing repo in Sourcetree, click FieOpen and select the folder location of the repo (i.e., the folder containing the hidden .git folder).

Sourcetree on macOS
  1. FileNew... to open the dialog for creating a new repo.
  2. In that dialog, click on the New... dropdown and choose Create Local Repository (or Create New Repository).
  3. Click the ... button to select the folder location for the repository. After selecting the folder location, click the Create button.

To open an existing repo in Sourcetree, click FieOpen... and select the folder location of the repo (i.e., the folder containing the hidden .git folder).

done!

Initializing a repo results in two things:

  • First, Git now recognizes this folder as a Git repository, which means it can now help you track the version history of files inside this folder.
HANDS-ON: Verifying a folder is a Git repo

To confirm, you can run the git status command. It should respond with something like the following:

git status
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

Don't worry if you don't understand the output (we will learn about these details later) or if your output is slightly different (e.g., master instead of main); what matters is that it no longer gives an error message as it did before.

done!

  • Second, Git created a hidden subfolder named .git inside the things folder. This folder will be used by Git to store metadata about this repository.

A Git-controlled folder is divided into two main parts:

  1. The hidden .git subfolder, which contains all the Git metadata related to the folder's revision history.
    How to see hidden folders in: Windows | macOS | Linux

  2. The working directory – everything else in that folder, where you create and edit files.

What is this? EXERCISE panels contain a Git-Mastery exercise that you can download using the Git-Mastery app, and you can use the same app to verify that your solution is correct.

EXERCISE: under-control

What is this? DETOUR panels contain related directions you can optionally explore. We recommend that you only skim them the first time you are going through a tour (i.e., just to know what each detour covers); you can revisit them later, to deepen your knowledge further, or when you encounter a use case related to the concepts covered by the detour.

DETOUR: Undoing a Repo Initialization

When Git initializes a repo in a folder, it does not touch any files in the folder except to create the .git folder and its contents. So, you can reverse the operation by deleting the newly created .git folder.

git status # run this to confirm a repo exists

rm -rf .git  # delete the .git folder

git status # this should give an error, as the repo no longer exists

Explanation of rm -rf .git command

EXERCISE: undo-init



T1L4. Specifying What to Include in a Snapshot


To save a snapshot, you start by specifying what to include in it, also called staging.

This lesson covers that part.

Git provides an internal space called the staging area, which it uses to build the next snapshot. Another name for the staging area is the index.

Git treats new files that you add to the working directory as 'untracked', i.e., Git is aware of them, but they are not yet under Git's control. The same applies to files that existed in the working directory at the time you initialized the repo.

We can stage an untracked file to tell Git that we want its current version to be included in the next snapshot (in Git terminology, such a snapshot is called a commit). When asked to stage a file, Git copies that file from the working directory to the staging area. Once you stage an untracked file, it becomes a 'tracked' (i.e., under Git's control) file thereafter.

In the example below, you can see how staging files changes the status of the repo as you go from (a) to (c).

Project Folder
.git Folder
other Git metadata ...
Staging area

[empty]

Working directory

├─ fruits.txt (untracked!)
└─ colours.txt (untracked!)


(a) State of the repo, just after initialization and creating two files. Both are untracked.
Project Folder
.git Folder
other Git metadata ...
Staging area

└─ fruits.txt

Working directory

├─ fruits.txt (tracked)
└─ colours.txt (untracked!)


(b) State after staging fruits.txt.
Project Folder
.git Folder
other Git metadata ...
Staging area

├─ fruits.txt
└─ colours.txt

Working directory

├─ fruits.txt (tracked)
└─ colours.txt (tracked)


(c) State after staging colours.txt.
HANDS-ON: Adding untracked files

Preparation

Option 1: Create a fresh sandbox using the Git-Mastery app

  • Navigate inside the gitmastery-exercises folder.
  • Run the gitmastery download hp-add-files command.

The sandbox will be set up inside the gitmastery-exercises/hp-add-files folder.


Option 2: Continue with the sandbox from the previous hands-on practical

1 Add a file (e.g., fruits.txt) to the things repo folder.

Here is an easy way to do that with a single terminal command.

Windows users: Use the Git Bash terminal to run the commands given in these lessons. Some of them might not work in other terminals such as PowerShell.

echo -e "apples\nbananas\ncherries" > fruits.txt

Explanation of the echo -e "apples\nbananas\ncherries" > fruits.txt command

things/fruits.txt
apples
bananas
cherries

To see the content of the file, you can use the cat command (or open it in your favorite text editor):

cat fruits.txt

2 Stage the new file.

 CLI

2.1 Check the status of the folder using the git status command.

git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

  fruits.txt
nothing added to commit but untracked files present (use "git add" to track)

Git commands targeting a specific repo should be run inside the repo folder. (we use the term repo folder to loosely refer to the folder that we initialized the repo in). For example, to check the status of the things repo, you need to navigate to the things folder in your terminal before you run the git status command.
Remember this for future Git commands too.

2.2 Use the git add <file> command to stage the file.

git add fruits.txt

You can replace add with stage (e.g., git stage fruits.txt) and the result is the same (they are synonyms). Git-Mastery usually uses add, but sometimes uses stage to remind you that both are correct.

Windows users: When using the echo command to write to text files from Git Bash, you might see a warning LF will be replaced by CRLF the next time Git touches it when Git interacts with such a file. This warning is caused by the way line endings are handled differently by Git and Windows. You can ignore it, or suppress it in the future by running the following command:

git config --global core.safecrlf false

2.3 Check the status again. You should see that the file is no longer 'untracked'.

git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

      new file:   fruits.txt

As before, don't worry if you don't understand the output yet (we'll unpack it in a later lesson). The key point is that the file is no longer listed as 'untracked'.

Sourcetree

2.1 Note how the file is shown as 'unstaged'. The question mark icon indicates the file is untracked.

If the newly added file does not appear in the Sourcetree UI, refresh the UI (: F5
| +R)

Sourcetree screenshots/instructions: vs

Note that the Sourcetree UI can vary slightly between Windows and macOS versions. Some screenshots in our lessons are from the Windows version while others are from the macOS version.

In some cases, we have specified how they differ.
In other cases, you may need to adapt if the given screenshots/instructions are slightly different from what you are seeing in your Sourcetree.

2.2 Stage the file:

Sourcetree on Windows

Select fruits.txt and click the Stage Selected button.

Sourcetree on macOS

You can stage the file using checkboxes or the ... menu next to the file.

2.3 Note how the file is now staged, i.e., fruits.txt appears in the Staged files panel.

If Sourcetree shows a \ No newline at the end of the file message below the staged lines (i.e., below the cherries line in the above screenshot), it means you did not press enter after entering the last line of the file, so Git is not sure if that line is complete. To fix this, move the cursor to the end of the last line in that file and press enter, as if you were adding a blank line below it. This new change will now appear as an 'unstaged' change. Stage it as well.

done!

If you modify a staged file, Git views it as 'modified', i.e., the file contains changes that are not present in the staged copy waiting to be included in the next snapshot. If you wish to include these new changes in the next snapshot, you need to stage the file again, which will overwrite the copy of the file that was previously in the staging area.
The example below shows how the status of a file changes when it is modified after it was staged.

Project Folder
.git Folder
other Git metadata ...
Staging area
names.txt
Alice
Working directory
names.txt
Alice

(a) The file names.txt is staged. The copy in the staging area is an exact match to the one in the working directory.
Project Folder
.git Folder
other Git metadata ...
Staging area
names.txt
Alice
Working directory
names.txt (modified)
Alice
Bob

(b) State after adding a line to the file. Git indicates it as 'modified' because it now differs from the version in the staging area.
Project Folder
.git Folder
other Git metadata ...
Staging area
names.txt
Alice
Bob
Working directory
names.txt
Alice
Bob

(c) After staging the file again, the staging area is updated with the latest copy of the file, and it is no longer marked as 'modified'.
HANDS-ON: Re-staging 'modified' files

Preparation

Option 1: Create a fresh sandbox using the Git-Mastery app

  • Navigate inside the gitmastery-exercises folder.
  • Run the gitmastery download hp-stage-modified command.

The sandbox will be set up inside the gitmastery-exercises/hp-stage-modified folder.


Option 2: Repurpose the sandbox from the previous hands-on practical

Start with the things repo from the previous hands-on practical, and add another line to fruits.txt, to make it 'modified'.

Here is a way to do that with a single terminal command.

echo "dragon fruits" >> fruits.txt
things/fruits.txt
apples
bananas
cherries
dragon fruits

1 Now, verify that Git sees that file as 'modified'.

 CLI

Use the git status command to check the status of the working directory.

git status
On branch main

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file:   fruits.txt

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified:   fruits.txt

Note how fruits.txt now appears twice, once as new file: ... (representing the version of the file we staged earlier, which had only three lines) and once as modified: ... (representing the latest version of the file which now has a fourth line).

Sourcetree

Note how fruits.txt appears in the Staged files panel as well as 'Unstaged files'.

2 Stage the file again, the same way you added/staged it earlier.

3 Verify that Git no longer sees it as 'modified', as in step 1.

done!

Staging applies regardless of whether a file is currently tracked.

  • Staging an untracked file will both begin tracking the file and include it in the next snapshot. Git creates a copy of the file in the working directory in the staging area.
  • Staging an already tracked file marks its current changes for inclusion in the next commit. Git overwrites the version of that file in the staging area with a copy of that file in the working directory.

Git also supports fine-grained selective staging, i.e., staging only specific changes within a file while leaving other changes to the same file unstaged. A later lesson covers this.

Git does not track empty folders. It tracks only folders that contain tracked files.
You can test this by adding an empty subfolder inside the things folder (e.g., things/more-things) and checking if it shows up as 'untracked' (it will not). If you add a file to that folder (e.g., things/more-things/food.txt) and then stage that file (e.g., git add more-things/food.txt), the file and its path will now be included in the next snapshot.

PRO-TIP: Applying a Git command to multiple files in one go

When a Git command expects a list of files or paths as a parameter (as the git add command does), these parameters are known as pathspecs — patterns that tell Git which files or directories to operate on. Pathspecs can be simple file names, directory names, or more complex patterns.

Here are some common ways to write them, shown with examples using the git add <pathspec> command:

  • Specify multiple files, separated by spaces:

    git add f1.txt f2.txt data/lists/f3.txt  # stages the specified three files
    
  • Use a glob pattern:

    git add '*.txt'  # stages all .txt files in the current directory
    

    When using glob patterns in Git commands, putting them inside quotes ('*.txt' instead of *.txt) is recommended, to avoid your shell expanding the pattern before Git sees it.

  • Use . to indicate 'all in the current directory and subdirectories':

    git add .  # stages all files in current directory and its subdirectories
    
  • Specify a directory to indicate 'this directory and its subdirectories':

    git add path/to/dir  # stages all files in path/to/dir and its subdirectories
    
  • Negated pathspecs, to indicate 'except these':

    git add . ':!*.log'  # stage everything except .log files
    

Git supports combining these features — for example, you could add all .txt files except those in a certain folder using:

git add '*.txt' ':!docs/*.txt'

Staged changes can be unstaged to indicate that we no longer want them to be included in the next snapshot.

EXERCISE: stage-fright

Related DETOUR: Unstaging Changes

Unstaging a staged file removes it from the staging area but keeps the changes in your working directory. This is useful if you later realize that you don't actually want to include a staged file in the next commit, perhaps because you staged it by mistake or want to include that change in a later commit.

That aspect is covered in the tour Unstaging Changes given in the lesson T1L5. Saving a Snapshot.


Related DETOUR: Staging File Deletions

When you delete a tracked file from your working directory, Git doesn't automatically assume you want that change to be part of your next commit. To tell Git you intend to record a file deletion in the repository's history, you need to stage the deletion explicitly.

That aspect is covered in the tour Staging File Deletions given in the lesson T1L5. Saving a Snapshot.



T1L5. Saving a Snapshot


After staging, you can save the snapshot by creating a commit.

This lesson covers that part.

Saving a snapshot of a repository is called committing, and the saved snapshot itself is called a commit.

Git constructs a commit based on the staging area. When you examine the staging area using a CLI command or a Git GUI, you are typically shown only a list of staged changes. This can mislead you into thinking that the staging area merely records changes you have selected for the commit. In reality, the staging area, which Git internally calls the index, is a complete record of the exact version of every tracked file that would be written into the next commit, not just a record of staged changes. This behavior aligns more closely with the name "index" than the name "staging area".

A Git commit is therefore a full snapshot of all tracked files. More precisely, it is a record of the exact state of all files in the staging area at that moment -- even the files that have not changed since the previous commit. This contrasts with the intuitive expectation that a commit stores only the since the previous commit. Consequently, a Git commit has all the information it needs to recreate the snapshot of the tracked files in the working directory at that point in time. In addition to the file contents, a commit also stores metadata such as the author, date, and an optional commit message describing the change.

Here is an example of how the three internal zones of Git look as a commit is followed by further changes to tracked files.

(a) Right after creating commit C1:

Project Folder
.git Folder
Committed history

C1 ← a commit

↑ ├── a snapshot of all tracked files │ ├── fruits.txt (snapshot) │ └── colours.txt (snapshot) └── other metadata about the commit
      e.g., author, date, commit message
[other Git metadata ...]
Staging area

[no changes to commit]

all tracked files ├── fruits.txt (same as C1) └── colours.txt (same as C1)
Working directory

├─ fruits.txt (tracked)
└─ colours.txt (tracked)

The staging area is empty of changes (i.e., nothing to commit), but it still contains a record of all tracked files. Tracked files in the last commit, staging area, and the working directory are identical.

(b) fruits.txt updated and staged:

Project Folder
.git Folder
Committed history

C1

↑ ├── a snapshot of all tracked files │ ├── fruits.txt (snapshot) │ └── colours.txt (snapshot) └── other metadata about the commit
      e.g., author, date, commit message
[other Git metadata ...]
Staging area

[ready to commit]

all tracked files ├── fruits.txt (some changes made) └── colours.txt (same as C1)
Working directory

├─ fruits.txt (tracked, some changes made)
└─ colours.txt (tracked)

The updated version of fruits.txt is also in the staging area. There are no changes to colours.txt in the working directory or the staging area. We can create a new commit at this point.

Given this, the staging area is not truly "empty" right after a commit; it is only empty of changes. It still contains a record of all tracked files, reflecting exactly the versions that were written into the previous commit.

A Git commit is a snapshot of all tracked files, not simply a delta of what changed since the last commit.

This is a good time to recap the three internal zones of a Git repo:

  1. Working directory The folder on your computer that contains the repo files. This is your workspace for editing files.
    Another name for this zone is working tree.
  2. Staging area (aka the index) The space that contains a copy of the exact versions of all tracked files (modified and unmodified) that will be written into the next commit. This resides inside the .git folder.
  3. Committed history Stores the commits and other metadata related to the revision history of the project. This also resides inside the .git folder.

Which area is the 'repository', exactly?

The term 'repository' generally refers to the disk area of the .git folder. However, it can sometimes also mean the 'committed history' area (which resides inside the .git folder` or even the entire project folder depending on the context.

Most Git operations are for transferring some information from one Git internal zone to another. For example, staging a file copies its current version from the working directory to the staging area, and committing saves the staged versions of all tracked files from the staging area to the commit history.

%%{init: {'sequence': {'mirrorActors': false}}}%%
sequenceDiagram
    participant WD as 📁 Working Directory
    participant SA as 📋 Staging Area
    participant CH@{ "type": "database" } as Committed History

    rect rgb(235, 245, 255)
    Note over WD,SA: Staging a file
    WD->>SA: copy current version of the file
    end

    rect rgb(235, 255, 235)
    Note over SA,CH: Committing
    SA->>CH: save staged versions of all tracked files
    end
HANDS-ON: Creating your first commit

Target To create a commit based on staged changes.

Preparation

Option 1: Create a fresh sandbox using the Git-Mastery app

  • Navigate inside the gitmastery-exercises folder.
  • Run the gitmastery download hp-first-commit command.

The sandbox will be set up inside the gitmastery-exercises/hp-first-commit folder.


Option 2: Continue with the sandbox from the previous hands-on practical

 CLI

1 First, do a sanity check using the git status command to confirm there are staged files.

git status
On branch main

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
  new file:   fruits.txt

2 Now, create a commit using the commit command. The -m switch is used to specify the commit message.

git commit -m "Add fruits.txt"
[main (root-commit) d5f91de] Add fruits.txt
 1 file changed, 4 insertions(+)
 create mode 100644 fruits.txt

3 Verify the staging area is empty using the git status command again.

git status
On branch main
nothing to commit, working tree clean

Note how the output says nothing to commit, which means there are no staged changes to commit.

Sourcetree

1 Ensure the new file fruits.txt has been staged.

2 Click the Commit button at the top of the window.

3 Enter a commit message (e.g., add fruits.txt) into the text box.

4 Click Commit.

done!

EXERCISE: grocery-shopping

DETOUR: Staging File Deletions

When you delete a tracked file from your working directory, Git doesn't automatically assume you want that change to be part of your next commit. To tell Git you intend to record a file deletion in the repository's history, you need to stage the deletion explicitly.

When you stage a deleted file, you're adding the file's removal to the staging area, just like you'd stage a modified or newly created file. After staging, the next commit will reflect that the file was removed from the project.

Note that staging a file deletion matters only if there is at least one commit in the repository. Before any commits are made, there is no file history, so deletions have no effect on the repository.

 CLI

To delete a file and stage the deletion in one go, you can use the git rm <pathspec> command. It removes the file from the working directory and stages the deletion at the same time.

git rm data/list.txt plan.txt

If you've already deleted the file manually (for example, using rm or deleting it in your file explorer), you can still stage the deletion using the stage command (or its synonym add). Even though the file no longer exists, staging records the deletion in the staging area.

git stage data/list.txt  # same as: git add data/list.txt

Unstaging file deletions is covered in the tour Unstaging Changes given in the lesson T1L5. Saving a Snapshot.

Staging a file deletion is done similarly to staging other changes.

Sourcetree

Staging a file deletion is done similarly to staging other changes.


DETOUR: Unstaging Changes

Unstaging a staged file removes it from the staging area but keeps the changes in your working directory. This is useful if you later realize that you don't actually want to include a staged file in the next commit, perhaps because you staged it by mistake or want to include that change in a later commit.

 CLI
  • To unstage a file you added or modified, run git restore --staged <pathspec>. This command removes the file from the staging area, leaving your working directory untouched.

    git restore --staged plan.txt budget.txt data/list.txt
    

    If your repo does not have any commits yet, git restore --staged will fail with the error fatal: could not resolve HEAD.
    The remedy is to use git reset <pathspec> instead.

    git reset plan.txt
    

    In fact, git reset is an alternative way of unstaging files, and it works regardless of whether you have any commits.

    Wait. Then why does git restore --staged exist at all, if it is longer and fails in some special cases?
    Answer: It is still considered the "modern" way of unstaging files (it was introduced more recently), because it is more intuitive and purpose-specific -- whereas git reset serves multiple purposes and, if used incorrectly, can cause unintended consequences.

    The restore command can accept multiple files or paths as input, which means you can use the notation for specifying multiple files. For example, to unstage all changes you've staged, you can use git restore --staged ..

  • To unstage a file deletion (staged using git rm), use the same command as above. It will unstage the deletion and restore the file in the staging area.
    If you also deleted the file from your working directory, you may need to recover it separately with git restore <file-name(s)>.

    git restore data/list.txt data/plan.txt
    
  • To 'nuke' all changes (i.e., get rid of all staged and unstaged changes to tracked files), you can add the --worktree flag to the git restore --staged <pathspec> command.

    git restore --staged --worktree .  # nuke all changes in current folder and subfolders
    
Sourcetree

To unstage a file, locate the file in the staged files section, click the ... in front of the file, and choose Unstage file:

EXERCISE: staging-intervention


Related DETOUR: Updating the Last Commit

Git allows you to amend the most recent commit. This is useful when you realize you need to change something, such as fixing a typo in the commit message or excluding an unintended change from the commit.

That aspect is covered in the tour Updating the Last Commit given in the lesson T5L3. Reorganizing Commits.


Related DETOUR: Resetting Uncommitted Changes

At times, you might need to get rid of uncommitted changes so that you have a fresh start for the next commit.

That aspect is covered in the tour Resetting Uncommitted Changes given in the lesson T4L5. Rewriting History to Start Over.


Related DETOUR: Undoing/Deleting Recent Commits

How do you undo or delete the last few commits if you realize they were incorrect, unnecessary, or done too soon?

That aspect is covered in the tour Undoing/Deleting Recent Commits given in the lesson T4L5. Rewriting History to Start Over.



T1L6. Examining the Revision History


It is useful to visualize the commit timeline, called the revision graph.

This lesson covers that part.

Git commits form a timeline, as each corresponds to a point in time when you asked Git to save a snapshot of the tracked files prepared in the staging area. Except for the initial commit, each commit links to at least one previous commit, forming a structure that we can traverse.
A timeline of commits can have a name. Such a named timeline is called a branch. By default, Git names the initial branch master -- though many now use main instead. You'll learn more about branches in future lessons. For now, just be aware that the commits you create in a new repo will be on a branch called main (or master) by default.

gitGraph
    %%{init: { 'theme': 'default', 'gitGraph': {'mainBranchName': 'main (or master)'}} }%%
    commit id: "Add fruits.txt"
    commit id: "Update fruits.txt"
    commit id: "Add colours.txt"
    commit id: "..."

Git can show you the list of commits in a repo's history.

HANDS-ON: Viewing the list of commits

Preparation

Option 1: Create a fresh sandbox using the Git-Mastery app

  • Navigate inside the gitmastery-exercises folder.
  • Run the gitmastery download hp-list-commits command.

The sandbox will be set up inside the gitmastery-exercises/hp-list-commits folder.


Option 2: Continue with the sandbox from the previous hands-on practical

1 View the list of commits, which should show only the commit you just created.

 CLI

Navigate into the repo folder and run the git log command to see the commit history.

cd hp-list-commits/things
git log
commit ... (HEAD -> main)
Author: ... <...@...>
Date:   ...

Add fruits.txt

Use the Q key to exit the output screen of the git log command.

Note how the output includes details about the commit you just created. You can ignore most of them for now, but notice that it also shows the commit message you provided.

Sourcetree

Expand the BRANCHES menu and click main to view the history graph, which contains only one node at the moment, representing the commit you just added. For now, ignore the label main attached to the commit.

2 Create a few more commits (i.e., a few rounds of add/edit files → stage → commit), and observe how the list of commits grows.

 CLI

Here is an example list of bash commands to add two commits while observing the list of commits after each commit.

echo "figs" >> fruits.txt  # add another line to fruits.txt
git add fruits.txt  # stage the updated file
git commit -m "Insert figs into fruits.txt"  # commit the changes
git log  # check commits list

echo "a file for colours" >> colours.txt  # add a colours.txt file
echo "a file for shapes" >> shapes.txt  # add a shapes.txt file
git add colours.txt shapes.txt  # stage both files in one go
git commit -m "Add colours.txt, shapes.txt"  # commit the changes
git log  # check commits list

You can copy-paste a list of commands (such as the commands above), including any comments, to the terminal. After that, press Enter to run them in sequence.

zsh users: If the terminal reports an error because of comments starting with #, you may need to enable the interactive comments feature in your terminal (e.g., add the line setopt INTERACTIVE_COMMENTS to your ~/.zshrc).

The output of the final git log should be something like this:

commit ... (HEAD -> main)
Author: ... <...@...>
Date:   ...

    Add colours.txt, shapes.txt

commit ...
Author: ... <...@...>
Date:   ...

    Insert figs into fruits.txt

commit ...
Author: ... <...@...>
Date:   ...

    Add fruits.txt

SIDEBAR: Working with the 'less' pager

Some Git commands -- such as git log -- may show their output through a pager. A pager is a program that lets you view long text one screen at a time, so you don't miss anything that scrolls off the top. For example, git log output will temporarily hide the current terminal content and enter a pager view that shows the output one screen at a time. When you exit the pager, the git log output will disappear from view, and the previous content of the terminal will reappear.

command 1
output 1

git log


commit f761ea63738a...
Author: ... <...@...>
Date:   Sat ...

    Add colours.txt

By default, Git uses a pager called less. Below are some useful commands for the less pager.

Command Description
q Quit less and return to the terminal
or j Move down one line
or k Move up one line
Space Move down one screen
b Move up one screen
G Go to the end of the content
g Go to the beginning of the content
/pattern Search forward for pattern (e.g., /fix)
n Repeat the last search (forward)
N Repeat the last search (backward)
h Show help screen with all less commands

If you'd rather see the output directly, without using a pager, you can add the --no-pager flag to the command, for example:

git --no-pager log

You can also configure Git not to use less, to use a different pager, or to fine-tune how less behaves. For example, you can reduce Git's use of the pager (recommended) by using the following command:

git config --global core.pager "less -FRX"

Explanation: -FRX is shorthand for combining the following three flags.

  • -F : Quits if the output fits on one screen (don't show pager unnecessarily)
  • -R : Shows raw control characters (for colored Git output)
  • -X : Keeps content visible after quitting the pager (so output stays on the terminal)
Sourcetree

To see the list of commits, click on the History item (listed under the WORKSPACE section) on the menu on the right edge of Sourcetree.

After adding two more commits, the list of commits should look something like this:

done!

The Git data model consists of two types of entities: objects and refs (short for references). In this lesson, you will encounter examples of both.

A Git commit graph (also called a revision graph) is a visualization of a repo's revision history, consisting of one or more branches. First, let us work with a simpler revision graph that has one branch, such as the one below.

C3
|
C2
|
C1

  • Nodes in the revision graph represent commits. A commit is one of four main types of Git objects. For completeness, the other three are:
    • blob (short for binary large object): stores the contents of a file
    • tree: represents a directory and records the hierarchy of its contents by referencing blobs and other trees
    • tag (specifically, annotated tag): a label-like object that can store additional metadata and point to a specific commit
  • A commit is identified by its SHA value. A SHA (Secure Hash Algorithm) value is a unique identifier generated by Git to represent each commit. In the usual Git repository format, it is produced by applying SHA-1 (i.e., one of the algorithms in the SHA family of cryptographic hash functions) to the contents of the commit object. It's a 40-character hexadecimal string (e.g., f761ea63738a67258628e9e54095b88ea67d95e2) that acts like a fingerprint, ensuring that every commit can be referenced unambiguously. That is, every commit has a unique hash value. Tell me more about the use of SHA
  • A commit is a full snapshot of all tracked files, based on the versions prepared in the staging area at commit time. That means each commit (except the initial commit) is based on another 'parent' commit. Some commits can have multiple parent commits -- we'll cover that later.

Because every commit has a unique hash, the commit hash values in our examples will differ from your own commit hash values when you follow our hands-on practicals.

Edges in the revision graph represent links between a commit and its parent commit(s). In some revision graph visualizations, you might see arrows (instead of lines) showing how each commit points to its parent commit.

C3
C2
C1

Git uses refs to name and keep track of various points in a repository's history. These refs are essentially named pointers that can serve as bookmarks to reach a certain point in the revision graph using the ref name.

C3 mainHEAD
|
C2
|
C1

In the revision graph above, there are two refs main and  HEAD.

  • main is a branch ref. A branch ref points to the latest commit on a branch. In this visualization, the commit shown alongside the ref is the one it points to, i.e., C3.
    When you create a new commit, the branch ref of the branch moves to the new commit.
    You'll be learning more about Git branches in a later lesson.
  • HEAD is a special ref that typically points to the current branch and moves along with that branch ref. In this example, it is pointing to the main branch.
    In certain cases, the HEAD may point directly to a specific commit instead of a branch. This situation is called a 'detached HEAD'; a later lesson covers it.
HANDS-ON: View the revision graph

Target Use Git to examine the revision graph of a simple repo.

Preparation

Option 1: Create a fresh sandbox using the Git-Mastery app

  • Navigate inside the gitmastery-exercises folder.
  • Run the gitmastery download hp-view-graph command.

The sandbox will be set up inside the gitmastery-exercises/hp-view-graph folder.


Option 2: Continue with the sandbox from the previous hands-on practical

 CLI

1 First, use a simple git log to view the list of commits.

git log
commit f761ea63738a... (HEAD -> main)
Author: ... <...@...>
Date:   Sat ...

    Add colours.txt, shapes.txt

commit 2bedace69990...
Author: ... <...@...>
Date:   Sat ...

    Insert figs into fruits.txt

commit d5f91de5f0b5...
Author: ... <...@...>
Date:   Fri ...

    Add fruits.txt

Below is the visual representation of the same revision graph. As you can see, the log output shows the refs slightly differently, but you can still match them to the same refs.

C3 mainHEADAdd colours.txt, shapes.txt
|
C2Insert figs into fruits.txt
|
C1Add fruits.txt

2 Use the --oneline flag to get a more concise view. Note how the commit SHA has been truncated to the first seven characters (the first seven characters of a commit SHA are enough for Git to identify a commit).

git log --oneline
f761ea6 (HEAD -> main) Add colours.txt, shapes.txt
2bedace Insert figs into fruits.txt
d5f91de Add fruits.txt

3 The --graph flag makes the result closer to a graphical revision graph. Note the * that indicates a node in a revision graph.

git log --oneline --graph
* f761ea6 (HEAD -> main) Add colours.txt, shapes.txt
* 2bedace Insert figs into fruits.txt
* d5f91de Add fruits.txt

The --graph option is more useful when examining a revision graph with multiple parallel branches (branches will be covered in a later lesson).

Sourcetree

Click History to see the revision graph.

  • In some versions of Sourcetree, the HEAD ref may not be shown -- it is implied that the HEAD ref points to the same commit as the current branch ref.

done!

EXERCISE: log-and-order


At this point: You should now be able to initialize a Git repository in a folder, stage file versions, and commit snapshots of tracked files at chosen points in time. So far, you have not learned how to use those snapshots (other than listing them and viewing a simple revision graph) -- we will do that in later tours.

What's next: Tour 2: Backing up a Repo on the Cloud