Git

Git is a distributed version control system. It tracks changes in source code and manages project history. The terms Version Control System (VCS) and Source Code Management (SCM) are often used interchangeably.

History

BitMover Inc. created BitKeeper, a proprietary version control system. The Linux kernel community adopted it in 2002.

BitKeeper initially offered a free community edition. Later, that option was removed and the product became fully commercial. In response, Linus Torvalds and the Linux community built Git.

BitKeeper was released as open source in 2016.

Scope

Git works best with plain text files, especially source code. It is not well suited to binary formats such as:

  • PDF
  • Word documents
  • PSD files

Those file types usually require separate tooling for versioning and collaboration.

This article focuses on using Git effectively in programming projects.

Installation

Install Git before continuing.

On Windows, ensure the Git executable is available in your system PATH.


Git Configuration

Git can store settings at three levels.

Project

Stored in .git/config. Applies only to the current repository.

git config user.name "John Doe"

Global

Stored in ~/.gitconfig. Applies to all repositories for the current user.

git config --global user.name "John Doe"

System

Stored in /etc/gitconfig. Applies to all users and repositories on the machine.

git config --system user.name "John Doe"

Precedence

Git reads configuration in this order:

environment variables > project > global > system

Relevant environment variables:

  • GIT_AUTHOR_NAME
  • GIT_AUTHOR_EMAIL

Practical Guidance

  • Use global configuration as a default.
  • Override at the project level when a repository needs different identity settings.
  • In Docker or Vagrant environments, project-level configuration is often simpler than duplicating settings across host and guest systems.

Set Name and Email

At the project level:

git config user.name "John Doe"
git config user.email "john.doe@domain.com"

Inspect Configuration

git config --list

Optional Settings

Set the editor:

git config core.editor "nano"

Enable colored output:

git config color.ui true

Getting Help

List Git commands:

git help

Open help for a specific command:

git help commit

Creating a Repository

A repository is a project directory managed by Git.

Initialize a repository:

git init

This creates a .git directory. It stores:

  • repository metadata
  • commit history
  • index data
  • project-level configuration

To stop tracking the directory with Git, delete .git.

Do not edit files inside .git unless you know exactly why. The main exception is .git/config for repository-specific settings.


Making the First Commit

Create a repository and add a file:

cd ~/project
git init
echo "this is a demo text file" > foo.txt

Check repository status:

git status

Git will report foo.txt as untracked.

Stage the file:

git add foo.txt

Commit it:

git commit -m "Add foo.txt"

Check status again:

git status

The repository should now be clean.

View commit history:

git log

Core Concepts

The Three Trees

Git operates across three states:

  • Working tree: your current files on disk
  • Staging index: the set of changes selected for the next commit
  • Repository: committed project history

Typical flow:

working tree -> staging index -> repository

Why the Staging Index Exists

The staging index lets you split unrelated changes into separate commits.

Example:

  • You modify 5 files.
  • 2 files fix bug A.
  • 3 files fix bug B.

With Git, you can stage and commit them separately. That produces smaller, clearer commits.

Basic Workflow

Stage a file:

git add foo.txt

Commit staged changes:

git commit -m "Fix bug X"

Discard local changes in a file and restore from the latest commit:

git checkout -- foo.txt

Commit Structure

A commit contains:

  • a SHA-based identifier
  • a pointer to its parent commit
  • author metadata
  • a commit message
  • a snapshot of tracked content at that point in time

The working directory is what other programs see. The repository is Git’s internal history.

Commit Discipline

A commit should represent one logical change.

Do not combine unrelated fixes in one commit. Stage only the changes tied to a single goal, commit them, then repeat for the next goal.


Branches

A branch is a line of development.

A new repository starts with one branch, traditionally called master in older Git setups. Many modern repositories use main instead.

HEAD points to the current commit on the active branch. Git stores that reference in .git/HEAD.

Use branches to isolate work such as:

  • feature development
  • bug fixes
  • experiments
  • release preparation

Branches can start from any existing branch.

Merging

When work on a branch is complete and validated, merge it back into the target branch.


Reading git status

git status reports:

  • current branch
  • untracked files
  • modified files
  • staged changes
  • whether the working tree is clean

Clean Working Tree

git status

Example output:

On branch master
nothing to commit, working tree clean

This means the working tree, staging index, and repository are aligned.

Untracked File

Create a new file:

echo "demo" > foo.txt

Status:

Untracked files:
  foo.txt

Git does not track new files until you add them.

Modified but Not Staged

Changes not staged for commit:
  modified: foo.txt

Staged for Commit

git add foo.txt

Status:

Changes to be committed:
  modified: foo.txt

Viewing Changes

Show unstaged changes:

git diff

Show staged changes:

git diff --staged

Deleting, Moving, and Renaming Files

Delete via the Operating System

rm foo.txt
git add foo.txt
git commit -m "Remove foo.txt"

Delete via Git

git rm foo.txt
git commit -m "Remove foo.txt"

Using Git commands for deletion, movement, and renaming stages the change automatically.


Commit Shortcut

You can combine staging and commit for tracked file modifications:

git commit -a -m "Update tracked files"

Limitations:

  • stages modified tracked files
  • stages deletions of tracked files
  • does not include new untracked files

Use it only when that behavior is acceptable.


Viewing History

Show commit history:

git log

Common filters:

git log --grep="Init"
git log --since=2012-06-12
git log --until=2012-06-12
git log -n 3

Undoing Changes

Discard Working Tree Changes

Restore a file from the latest commit:

git checkout -- page.html

Use -- to make it explicit that the target is a file path, not a branch name.

Unstage a File

Remove a file from the staging index without losing local edits:

git reset HEAD resources.html

Amend the Last Commit

Use this when the latest commit needs a small correction or message change.

git commit --amend -m "New commit message"

This rewrites the most recent commit instead of creating a new one.


Retrieving an Older Version of a File

Find the target commit:

git log

Restore a file from that commit:

git checkout <commit-hash> -- resources.html

You can use the full hash or a unique prefix.

This places the retrieved version into your current state for further inspection or commit.


Reverting a Commit

Create a new commit that reverses an earlier commit:

git revert <commit-hash>

To apply the revert without committing immediately:

git revert -n <commit-hash>

Revert is safer than rewriting history when commits are already shared.


Resetting History

git reset moves HEAD to another commit. It can also affect the staging index and working tree.

Use it carefully.

Before a destructive reset, save recent commit hashes somewhere outside the repository.

Soft Reset

git reset --soft <commit-hash>

Effect:

  • moves HEAD
  • keeps staging index intact
  • keeps working tree intact

Mixed Reset

git reset --mixed <commit-hash>

or simply:

git reset <commit-hash>

Effect:

  • moves HEAD
  • resets staging index
  • keeps working tree intact

This is the default mode.

Hard Reset

git reset --hard <commit-hash>

Effect:

  • moves HEAD
  • resets staging index
  • resets working tree

This discards local changes.

After a hard reset, the working tree matches the target commit exactly.


Removing Untracked Files

Preview what would be removed:

git clean -n

Delete untracked files:

git clean -f

Use this carefully. It deletes files Git is not tracking.


Ignoring Files

Use .gitignore to exclude files and directories from tracking.

Common examples:

  • compiled artifacts
  • package directories
  • archives such as .zip and .gz
  • logs
  • databases
  • operating system metadata files
  • generated media and uploaded assets

Example Rules

Ignore all HTML files except index.html:

*.html
!index.html

Ignore a directory:

source/media/

Comments start with #.

Blank lines are ignored.

Global Ignore File

Apply ignore rules across all repositories for one user:

git config --global core.excludesfile ~/.gitignore_global

Ignoring Already Tracked Files

.gitignore only affects untracked files.

If a file is already tracked, remove it from the index first.

Remove and delete the file:

git rm file.txt

Stop tracking but keep the file locally:

git rm --cached file.txt

After that, add the rule to .gitignore.


Tracking Empty Directories

Git does not track empty directories.

To preserve one, add a placeholder file such as .gitkeep inside it:

empty-dir/
  .gitkeep

Summary

Git is built around a few core ideas:

  • track source code as text
  • separate working changes from staged changes
  • make small, focused commits
  • isolate work in branches
  • use history tools carefully
  • ignore generated or irrelevant files

The practical skill is not memorizing every command. It is understanding how the working tree, staging index, and repository interact.