Skip to main content
Blog|
Learning center

Git Version Control: A Practical Guide

|
Feb 28, 2026|9 min read
LEARNING CENTERGit Version Control: APractical GuideHOSTNEYhostney.comOctober 11, 2023

Git tracks every change you make to your code, lets you undo mistakes, and makes it possible for multiple people to work on the same project without stepping on each other’s toes. If you write code for a living, or plan to, you’ll use Git nearly every day.

Linus Torvalds built Git in 2005 because he needed a version control system for the Linux kernel that was fast, distributed, and didn’t require a central server to function. Two decades later, it’s the standard. GitHub alone hosts over 200 million repositories. Whether you’re a solo developer or part of a large team, Git is the foundation that everything else sits on.

This guide covers Git from installation through advanced workflows, with commands for both Linux and Windows.

What Git actually does#

At its core, Git is a system that takes snapshots of your project. Every time you commit, Git records the state of every file in your repository at that moment. You can go back to any snapshot, compare changes between snapshots, or branch off into a separate line of development and merge it back later.

A few things make Git different from older version control systems:

It’s distributed. Every developer has a complete copy of the repository, including the full history. You can commit, branch, and view history without any network connection. This also means there’s no single point of failure. If the server goes down, every developer’s local copy is a full backup.

It’s fast. Because most operations happen locally, things like viewing history, creating branches, and diffing files are nearly instantaneous. Git was designed to handle the Linux kernel, which has millions of lines of code and thousands of contributors. Your project is probably smaller.

Branching is cheap. Creating a branch in Git takes milliseconds and costs almost nothing in terms of storage. This makes it practical to create branches for every feature, bug fix, or experiment, something that was painful with older tools like SVN.

Core concepts#

Before touching the command line, it helps to understand three things:

Repository. A repository (repo) is your project directory plus a hidden .git folder that contains the entire history. There are local repos on your machine and remote repos on servers like GitHub or GitLab.

Commit. A commit is a snapshot of your project at a specific point in time. Each commit has a unique hash (like a1b2c3d ), an author, a timestamp, and a message describing what changed. Commits are the atoms of Git. Everything else is built on top of them.

Branch. A branch is a movable pointer to a commit. When you create a branch, you’re just creating a new pointer. When you commit on that branch, the pointer moves forward. The default branch is called main (or master in older repos).

Installing Git#

Linux

Git is available in every major Linux distribution’s package manager.

Debian / Ubuntu:

sudo apt update
sudo apt install git

RHEL / Rocky Linux / CentOS:

sudo dnf install git

On older RHEL/CentOS versions that don’t have dnf :

sudo yum install git

Verify the installation:

git --version

Windows

Download the installer from git-scm.com and run it. The default options work fine for most setups. Once installed, you get Git Bash (a Unix-like terminal) and Git integrates with PowerShell and Command Prompt.

If you use Windows Package Manager:

winget install Git.Git

First-time setup#

Before you start using Git, set your name and email. These are attached to every commit you make.

git config --global user.name "Your Name"
git config --global user.email "you@example.com"

The --global flag applies these settings to all repositories on your machine. If you need different settings for a specific project (like a different email for work vs personal), run the same commands without --global inside that project directory.

A few other settings worth configuring upfront:

# Set the default branch name for new repos
git config --global init.defaultBranch main

# Use a cleaner output for git log
git config --global log.oneline true

Creating and cloning repositories#

Start a new project:

mkdir my-project
cd my-project
git init

This creates a .git directory inside your project. Nothing is tracked yet; you need to add files and make your first commit.

Clone an existing project:

git clone https://github.com/username/repo.git

This downloads the entire repository, including its full history, and sets up a remote called origin pointing back to the source.

The basic workflow#

This is the sequence you’ll repeat hundreds of times:

1. Make changes to your files.

2. Check what changed:

git status

This shows which files are modified, which are staged, and which are untracked.

3. Stage the changes you want to commit:

git add filename.js

Or stage everything:

git add .

Staging is Git’s way of letting you choose exactly which changes go into the next commit. You might have modified five files but only want to commit changes to two of them.

4. Commit the staged changes:

git commit -m "Add user login validation"

Write commit messages that explain why, not what. The diff shows what changed. The message should explain the reasoning. “Fix bug” tells your teammates nothing. “Prevent duplicate form submission on slow connections” tells them exactly what problem was solved.

5. View the history:

git log --oneline

This shows a compact list of commits with their hashes and messages.

Undoing things#

Everyone makes mistakes. Git gives you several ways to recover.

Discard uncommitted changes to a file:

git checkout -- filename.js

This restores the file to its last committed state. The changes are gone permanently.

Unstage a file (keep the changes, just remove from staging):

git restore --staged filename.js

Amend the last commit (fix a typo in the message or add a forgotten file):

git add forgotten-file.js
git commit --amend

Revert a commit (create a new commit that undoes a previous one):

git revert a1b2c3d

This is the safe way to undo changes that have already been pushed. It doesn’t rewrite history, it adds a new commit on top.

Reset to a previous commit (destructive, rewrites history):

git reset --hard a1b2c3d

Use this carefully and only on commits that haven’t been pushed to a shared repository. Rewriting shared history causes real problems for your team.

Branching#

Branching is where Git earns its reputation. A branch lets you work on something without affecting anyone else’s work.

Create and switch to a new branch:

git checkout -b feature/user-auth

Or using the newer syntax:

git switch -c feature/user-auth

Switch between existing branches:

git checkout main

List all branches:

git branch

Delete a branch after it’s been merged:

git branch -d feature/user-auth

A practical branching pattern

Most teams follow some variation of this:

  1. main  holds production-ready code
  2. Create a branch from  main  for each feature or fix
  3. Work on the branch, committing as you go
  4. Open a pull request when the work is ready for review
  5. Merge the branch into  main  after approval
  6. Delete the branch

This keeps main stable and gives everyone a clean workspace for their own changes.

Merging#

When your feature branch is ready, you merge it back into main :

git checkout main
git merge feature/user-auth

If nobody else has changed the same files you did, Git handles the merge automatically. If there are conflicting changes, Git marks the conflicts in the affected files and asks you to resolve them manually.

A conflict looks like this in the file:

<<<<<<< HEAD
const timeout = 3000;
=======
const timeout = 5000;
>>>>>>> feature/user-auth

You decide which version to keep (or combine them), remove the conflict markers, and commit the result. Conflicts sound scary, but they’re usually straightforward. They just mean two people changed the same line, and Git wants a human to make the call.

Working with remote repositories#

Most teams use a hosting service like GitHub or GitLab as their central remote repository.

Push your branch to the remote:

git push origin feature/user-auth

The first time you push a new branch, add the -u flag to set up tracking:

git push -u origin feature/user-auth

After that, git push and git pull work without specifying the remote and branch name.

Pull the latest changes from the remote:

git pull origin main

See which remotes are configured:

git remote -v

Pull requests

On GitHub and GitLab, you don’t merge branches directly on the command line (though you can). Instead, you push your branch and open a pull request (called a “merge request” on GitLab). This gives your team a place to review the code, leave comments, and run automated tests before the changes land in main .

Pull requests are where code review happens, and code review is one of the most effective ways to catch bugs, share knowledge, and maintain code quality across a team.

Advanced workflows#

Rebasing

Rebasing replays your branch’s commits on top of another branch, as if you’d started your work from the latest version of that branch. It produces a cleaner, linear history compared to merge commits.

git checkout feature/user-auth
git rebase main

The golden rule: never rebase commits that have already been pushed to a shared branch. Rebasing rewrites commit hashes, which causes problems for anyone who has based work on those commits. Use it for cleaning up your local branch before opening a pull request.

Tagging releases

Tags mark specific points in history, typically used for version releases:

git tag -a v1.0.0 -m "First stable release"
git push --tags

Tags are permanent markers, unlike branches, which move forward with each commit.

Cherry-picking

Sometimes you need a single commit from one branch applied to another without merging the entire branch:

git cherry-pick a1b2c3d

This creates a new commit on your current branch with the same changes. Useful for backporting a bug fix from a development branch to a release branch.

Git hooks

Hooks are scripts that run automatically at certain points in the Git workflow. Common uses:

  • pre-commit: Run linters or formatters before allowing a commit
  • pre-push: Run tests before pushing to the remote
  • commit-msg: Enforce commit message formatting

Hooks live in .git/hooks/ and are just executable scripts. Many teams use tools like Husky (for JavaScript projects) or pre-commit (Python) to manage hooks across the team.

Keeping things clean#

.gitignore tells Git which files to ignore. Every project should have one. Common entries:

node_modules/
.env
*.log
dist/
.DS_Store

Without a .gitignore , you’ll end up with dependency folders, environment files with secrets, and OS-generated junk cluttering your repository.

Delete merged branches. After a branch is merged, delete it. Stale branches accumulate and make it harder to find the ones that matter. Most teams configure GitHub/GitLab to auto-delete branches after merge.

Write meaningful commit messages. A good commit history is documentation that writes itself. Six months from now, git log should tell the story of how your project evolved. That only works if the messages are useful.

Git hosting: GitHub vs GitLab#

Both are excellent platforms for hosting Git repositories. GitHub is the larger ecosystem with more open-source projects and a wider community. GitLab offers a more integrated DevOps platform with built-in CI/CD pipelines, container registries, and issue tracking in a single product. Many self-hosted teams prefer GitLab because you can run your own instance.

For most projects, either works well. The important thing is that you’re using one of them as your remote, so your code is backed up, your team can collaborate through pull requests, and you have a web interface for browsing code and reviewing changes.

Check out the official Git documentation for the complete reference.

Host your projects with Hostney#

Every Hostney web hosting plan supports Git-based deployment workflows. Push your code, and your site is live. Combined with our built-in nginx caching, free SSL certificates, and server-side performance optimizations, you get a hosting environment built for developers who care about their workflow.

Try our web hosting free for 14 days with no obligation.