Version Control & Workflow Read time: 8 min Updated: October 2026

Git Fundamentals: Safe Commits, Diff Inspection, and Code Recovery

In modern software engineering, managing revisions by manually saving copies like project_v2_final or final_fixed quickly leads to confusion, broken dependencies, and lost work. Git provides a distributed version control system that tracks granular code changes, records verifiable commit histories, and allows safe state rollbacks.[1]

Rather than memorizing arbitrary command sequences, this tutorial walks through the foundational three-tree mental model of Git, safe commit inspection with git diff, protecting credentials with .gitignore, and understanding the critical distinction between unstaging files and discarding work with git restore.[1][2][3]

1. The Core Architecture: Working Tree, Index, and HEAD

Git maintains three distinct state areas on your local machine:

  • Working Directory (Working Tree): The actual files on your filesystem that you edit with your code editor.
  • Staging Area (Index): An intermediate preparation zone where changes are selected and verified before being committed.
  • Repository History (HEAD): The permanent cryptographic chain of committed snapshots recorded inside the .git database.[1]

Understanding this tripartite division prevents accidental commits and clarifies why running git add does not immediately save a commit.

2. Initializing a Repository and Configuring Identity

Verify that Git is installed on your system by checking its version in your terminal:[1]

git --version

Create an isolated practice directory and initialize a clean repository:

mkdir git-practice
cd git-practice
git init
git config user.name "Your Name"
git config user.email "you@example.com"

Configuring user.name and user.email establishes the author identity attached to every subsequent commit. Never place passwords or authentication tokens in author metadata; commit author details are stored in plain text across project history.[1]

3. Staging and Your First Commit

Create an introductory project file named README.md:

echo "# Git Practice Project" > README.md
git status

The output from git status indicates that README.md is an untracked file. To stage the file, inspect what is staged, and commit it to the repository history, execute the following commands:[1]

# Stage the current state of README.md to the index
git add README.md

# Inspect the changes currently staged for the upcoming commit
git diff --cached

# Record the snapshot with a clear imperative message
git commit -m "Initialize project documentation with README"

A high-quality commit message should describe what changed and why. Avoid generic messages like "update" or "fix".

4. Diff Inspection: Staged vs Unstaged Changes

Now append a second line to README.md:

echo "Always inspect diffs before staging." >> README.md

Running git diff without flags compares your current Working Directory against the Staging Area, showing lines added or removed that have not yet been staged:[1]

git diff

Once you stage the file with git add README.md, running plain git diff produces no output because there are no differences between your working directory and the staging area. To view what is queued for commit, you must use:[1]

git diff --staged
# or equivalently: git diff --cached
Review Workflow: Always run git diff --staged before issuing git commit. This habit catches debug print statements, leftover scratch notes, and accidental file inclusions before they enter your team's permanent git history.

5. Protecting Secrets and Artifacts with .gitignore

Source code repositories must never contain credentials, environment files, build artifacts, or local dependency trees. The .gitignore file defines pattern rules that Git uses to intentionally ignore untracked files.[2]

Create a .gitignore file containing common sensitive and temporary patterns:

# Environment variables and secrets
.env
.env.*
*.pem
*.key

# Dependencies and caches
node_modules/
__pycache__/
*.pyc

# Local runtime logs
*.log

Commit the .gitignore file into your repository so that the ignore rules are shared with all collaborators:[2]

git add .gitignore
git commit -m "Configure gitignore rules for secrets and build caches"

Critical Note: .gitignore only prevents untracked files from being added. If a file was already committed to Git previously, adding it to .gitignore does not remove it from history. If you accidentally commit an API key, revoke and rotate the credential immediately, then rewrite the branch history.[2]

6. Safe Code Recovery: Unstaging vs Discarding

One of the most dangerous points of confusion for developers is the difference between removing a change from the staging area and discarding file edits entirely. Git modern CLI syntax provides the git restore command to handle both situations cleanly:[3]

Scenario A: Unstaging a file without losing your edits

Suppose you edited README.md and ran git add README.md, but then realized you want to make further edits before committing. To remove the file from the staging area while keeping your changes in the working directory:[3]

# Removes README.md from the index; working tree edits remain completely intact
git restore --staged README.md
git status

Scenario B: Discarding uncommitted working tree modifications

If you made exploratory changes in your working tree that you want to throw away completely, restoring the file to the last committed state:[3]

# WARNING: This overwrites local uncommitted edits with the last committed version
git restore README.md
Safety Warning: Unlike committed snapshots which can almost always be recovered via git reflog, changes discarded with git restore <file> that were never committed or stashed cannot be recovered by Git. Always verify with git diff before discarding.

7. Summary Checklist for Daily Workflows

  1. Check repository status frequently with git status.[1]
  2. Review unstaged modifications with git diff before staging.
  3. Review staged changes with git diff --staged before committing.
  4. Maintain strict .gitignore boundaries to prevent credential leakage.[2]
  5. Use git restore --staged <file> to safely unstage files without destroying work.[3]

Primary Sources & Documentation