Learning web development: Version control via Git and GitHub

[2025-09-18] dev, javascript, learning web dev
(Ad, please don’t block)

This blog post is part of the series “Learning web development” – which teaches people who have never programmed how to create web apps with JavaScript.

To download the projects, go to the GitHub repository learning-web-dev-code and follow the instructions there.

I’m interested in feedback! If there is something you don’t understand, please write a comment at the end of this page.


In this chapter, we learn how to use the version control system Git and a useful companion website, GitHub. Both are important tools when programming in teams but even help programmers who work on their own.

What is Git?  

Git is a version control system (a.k.a. source control system). It manages the different versions of the files in a repository (think directory) throughout their history. Git works best with text files such as the web development artifacts .html, .css and .js. But it can also handle binary files.

Installing Git  

Use your native package manager to install the git shell command – e.g.:

  • WinGet: Git.Git
  • Homebrew: git
    • MacOS already comes git preinstalled, however that tends to be an older version, which is why I recommend to always install it via Homebrew.
  • Other ways of installing Git

After installing, run the following command:

git config --global init.defaultBranch main

This is a common setting and disables a tip message that is shown when using git init (which we’ll use later).

Using Git locally  

In this section, we explore how we can turn a directory with files into a Git repository and how to use such a repository. You can follow along on your own computer. We’ll use the files when we explore GitHub, so don’t delete them after this section.

Creating a local directory with files  

We’ll create the following files:

git-demo/
  README.md
  LICENSE
  js/
    main.js

README.md  

The readme file of a repository describes what’s in it. Its name is often spelled like this: The all-caps are intended to draw attention to it and Markdown has become the most popular format for writing it. Let’s use the following content:

# Git demo

* This Git repository is for practicing Git.
* [More information on Git.](https://git-scm.com/doc)

LICENSE  

In the web development community, a lot of code is public. That helps people learn and collaborate. If you publish your code or share it with someone else, you should add a license because it makes it clear what others are allowed to do with your code and what not. Two popular licenses are:

  • MIT License lets other do whatever they want with your artifacts, as long as they mention you as a contributor.
  • The Unlicense makes your artifacts completely public domain – with no strings attached.

The website “Choose an open source license” can help you find good licenses for your projects.

Once you find a good license for this demo project, copy its text into the plain text file LICENSE.

js/main.js  

Create a file with one or more lines of JavaScript code.

Turning the directory into a Git repository  

Now it’s time to turn our directory into a Git repository:

% cd git-demo 
% git init
Initialized empty Git repository in /tmp/git-demo/.git/

When using Git via a shell, we always invoke the git command. That command has many subcommands – such as git init. This command adds the directory git-demo/.git/ which is where Git stores the data it needs to manage this repository.

The subcommand git status provides us with information about the state of our repository:

% git status
On branch main

No commits yet

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

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

Currently, Git does not track any of the files in our directory. Unless we explicitly add a file to the repository, Git ignores it.

Committing to the repository  

Committing (saving) changes to a Git repository happens in two steps:

  • First we stage the changes: We git add untracked files and/or changed tracked files.
  • Then we commit the staged changes to the repository, along with a commit message that describes the changes.

Let’s commit README.md to our repository. First we stage it:

% git add README.md

% git status
On branch main

No commits yet

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

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

Then we commit the staged changes (option -m specifies the commit message):

% git commit -m "First commit"
[main (root-commit) ddb66b4] First commit
 1 file changed, 4 insertions(+)
 create mode 100644 README.md

% git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	LICENSE
	js/

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

git log shows the history of our commits to the repository:

% git log
commit ddb66b4383713c4ddedc032a2b75d44c2db0e227 (HEAD -> main)
Author: Axel Rauschmayer <axel@rauschma.de>
Date:   Wed Sep 17 16:57:10 2025 +0200

    First commit

Notes:

  • We can always go back to old versions. The commit messages help us find relevant versions.
  • Normally, we would have included all files in the initial commit.

Which files not to track  

Most repositories contain files that we never want to track. We can tell Git to pretend those don’t exist, by creating the file .gitignore in the top level of a repository. In JavaScript repositories, this file may look like this:

.DS_Store
node_modules
  • .DS_Store is a file created by the macOS Finder that is only need by the local machine. It should never be committed to a repository.
  • The node_modules/ directory also usually not committed to repositories: It tends to be large and package-lock.json records what’s in it so that we can always re-create it precisely.

Git saves increments  

When saving a commit, Git does not store the complete new file, it detects what has changed between the old version and the new version and only stores the difference. That’s why it works so well for text files.

You can explore how computing the differences (diffing) works:

  • PowerShell: compare-object (get-content one.txt) (get-content two.txt)
  • Unix: diff one.txt two.txt

Remote Git repositories  

Git is not just a version control system; it is a distributed version control system. Let’s explore what that means: Each local repository can have zero or more remote repositories that can be stored anywhere: Locally on the machine or remotely, on a server. Each of these remote repositories has a unique name, but it’s most common that repositories have exactly one remote repository with the default name origin. That default name has the benefit that we don’t need to mention it when we use shell commands.

What is the purpose of a remote repository? We want to keep our local data in sync with the remote data. Two common scenarios are:

  • A single user that syncs their local repository with a remote repository on a server (e.g. on GitHub, as explained later in this chapter). That means they always have a backup in the cloud.

  • A team of people that works on a shared repository on a server. Each team member has a local repository whose remote repository is that shared repository. They constantly sync their changes to the shared files.

In the latter scenario, there are no editing conflicts as long as people work on different files. Git handles that case well because it records changes incrementally. Should more than one person change the same file, there may be conflicting edits. Git has strategies for handling those, but that is beyond the scope of this series.

We’ll soon experiment with a remote repository. The following terms are important:

  • Pulling means fetching changes from a remote repository and merging them into the local repository.
  • Pushing means sending changes to a remote repository and merging them into it.
  • Cloning means copying a remote repository and turning it into a local repository – e.g.: If you want to collaborate on a shared remote repository, you can start by cloning it.

Using Git via Visual Studio Code  

Visual Studio Code has an excellent graphical user interface for Git. Here is how to use it:

  • Open a Git repository with Visual Studio Code.
  • Switch the sidebar on the left to the Source Control View by clicking on the “Source Control” icon:
  • You can now easily stage and commit changes.

My recommendation is to use the shell for creating or cloning repositories and to otherwise use Visual Studio Code as much as possible. Its documentation has more information on its source control functionality.

Exercise: Use Visual Studio Code to commit a file to git-demo/.

Using GitHub  

GitHub is a web site that hosts Git repositories and provides various services around those repositories. Per repository, you can determine if it should be private (accessible only to you) or public (readable to everyone). You can also configure which other GitHub users can access a given repository.

Let’s try out GitHub!

Creating a GitHub account and logging into GitHub  

We chose HTTPS to access GitHub, another option is SSH (more information).

What does logging into GitHub give us? It means we can push changes to a GitHub repo without having to log in each time we do so. The user-wide login should work for Visual Studio Code, too (however, I only tested this on macOS).

More useful gh commands  

We have already seen one useful gh command. The only other two I use, are:

  • Information on how we are logged in:
    gh auth status
    
  • Cloning a repo:
    gh repo clone <user-name>/<repo-name>
    

To learn more about the remaining functionality of the GitHub CLI, you can check out the official manual.

Creating a GitHub repository  

  • Got to github.com and create a new repository. It should be relatively easy to figure out via the website’s user interface. If not, you can do a web search to find help.
  • Use gh repo clone to clone the repository to your drive.
  • Move the files from git-demo/ into the new directory.
  • Use Visual Studio Code to commit all files to the local repository and push this new commit to the remote repository.
  • Check out your remote repository online at github.com: You should see the file you committed.

Why use Git?  

  • Single users get automatic backup with infinite undo. That means you never have to worry about accidentally losing code: You can always to back to how things were in the past!

  • Teams get the aforementioned benefits plus the benefit that Git makes working on a shared set of artifacts much easier.

Further reading  

We have barely scratched the surface of how Git works, but the basics you have learned should get you relatively far. For more information on Git, see: