Understanding Distributed Version Control & Git's Core Architecture
What is Git?
Git is a modern, high-performance Distributed Version Control System (DVCS) created in 2005 by Linus Torvalds to manage the development of the Linux operating system kernel.
Before Git, version control was dominated by centralized systems like Subversion (SVN), CVS, and Perforce. In centralized systems, every operation (viewing history, diffing changes, committing) required connecting to a central server over a network. If the server crashed or went offline, developers were completely blocked from collaborating or committing code.
Centralized VCS vs. Distributed VCS
In Git, every developer's local machine contains a full-fledged clone of the entire repository—including the complete commit history, all branches, and all tags. Network connectivity is only required when synchronizing (pushing or fetching) with remote servers like ProGit or GitHub.
Snapshots, Not Differences (Deltas)
The fundamental conceptual difference between Git and traditional VCS is how data is stored. Most legacy systems store changes as a base version followed by a sequence of delta differences over time. Git, however, conceives of its data as a stream of complete filesystem snapshots.
Every time you commit in Git, it takes an instantaneous snapshot of what all files in your project look like at that moment and records a reference to that snapshot. To keep storage ultra-efficient, if a file has not changed between commits, Git does not duplicate the file—it simply creates an internal cryptographic pointer to the identical file already saved in the object database.
Figure 1.1: Git's Content-Addressable Object Model (Commits → Trees → Blobs).
Content-Addressable Object Database
Everything in Git is addressed by the 40-character SHA-1 (or 64-character SHA-256) hash of its contents and header:
Blob (Binary Large Object): Holds pure file content compressed with zlib. Blobs store no file names, folder paths, or timestamps—only raw content.
Tree: Represents a directory or folder. Associates file names and file permissions with specific blob or sub-tree hashes.
Commit: Represents a point-in-time state. Points to a top-level Tree object, records author and committer names, timestamps, parent commit hash(es), and commit log message.
Annotated Tag: A permanent cryptographic reference to a commit, including tagger details, date, and optional GPG signature.
Installing Git on macOS, Linux, and Windows Platforms
Installing Git Across Operating Systems
Git is universally supported on macOS, Linux, and Windows. Follow the instructions for your platform:
1. macOS Installation
# Option A: Install via Homebrew (Recommended)
brew install git
# Option B: Install Apple Command Line Developer Tools
xcode-select --install
2. Linux Installation
# Ubuntu / Debian
sudo apt update && sudo apt install -y git
# Red Hat Enterprise Linux / CentOS / Fedora / AlmaLinux
sudo dnf install -y git
# Arch Linux / Manjaro
sudo pacman -S git
# Alpine Linux (Minimal Container Image)
apk add --no-cache git
3. Windows Installation
Download the official installer from git-scm.com/download/win. This provides Git Bash (a complete bash emulation environment) and installs native Git command-line tools into Windows PowerShell.
4. Verifying Your Installation
Confirm Git is installed and check your version in terminal:
git --version
# Expected output: git version 2.44.0 (or newer)
which git
# Expected output: /usr/local/bin/git or /usr/bin/git
Configuring Identity, Default Branch, Editors, and Environment Settings
Configuration Scopes in Git
Git utilizes three distinct levels of configuration with ascending precedence:
--system: Applied to all users on the operating system (/etc/gitconfig).
--global: Applied to your specific OS user account (~/.gitconfig or ~/.config/git/config).
--local: Applied only to the current repository directory (.git/config). Overrides global settings.
Setting Up Developer Identity
Every Git commit embeds the author's name and email. Set these globally right after installation:
# Set full legal name and professional email
git config --global user.name "Sanjeev Senapati"
git config --global user.email "sanjeev@prodevans.com"
# Set default primary branch name to 'main'
git config --global init.defaultBranch main
# Set preferred command line text editor (e.g. VS Code, nano, or vim)
git config --global core.editor "vim"
# Configure pull behavior to rebase by default (keeps history linear)
git config --global pull.rebase true
Line Ending Configuration (CRLF vs LF)
Windows uses Carriage Return + Line Feed (\r\n), while macOS and Linux use Line Feed (\n). To prevent dirty diffs across team members:
# On macOS / Linux:
git config --global core.autocrlf input
# On Windows:
git config --global core.autocrlf true
Inspecting Active Configuration
# List all configured keys and which configuration file they originate from
git config --list --show-origin
# Read a specific key value
git config user.email
Initializing Repositories, Cloning Remotes, and Anatomy of the .git Directory
Initializing a New Repository
To turn any folder into a tracked Git repository, run git init inside that directory:
# Create project directory and enter it
mkdir my-service && cd my-service
# Initialize Git repository
git init
# Initialized empty Git repository in /Users/sanjeev/my-service/.git/
Cloning an Existing Remote Repository
To download a project and its entire history from ProGit or GitHub, use git clone:
# Clone via Smart HTTPS
git clone https://progit.co.in/prodevans/backend.git
# Clone via SSH on custom port 2222
git clone ssh://git@progit.co.in:2222/prodevans/backend.git
# Clone a specific branch directly
git clone -b develop https://progit.co.in/prodevans/backend.git
Anatomy of the Hidden .git Directory
The .git directory contains all internal bookkeeping. Here are its critical contents:
Path
Role & Function
HEAD
File pointing to the currently checked-out branch ref (e.g., ref: refs/heads/main).
objects/
Content-addressable object store holding all compressed blobs, trees, and commits.
refs/heads/
Files storing 40-character commit hashes for each local branch.
refs/remotes/
Commit hashes for remote tracking branches (e.g. origin/main).
refs/tags/
Pointers to specific release tag commit hashes.
index
Binary staging area file caching filenames, file modes, and staged blob SHA-1s.
File Lifecycles, Tracking Status, and Creating Your First Files
The File Lifecycle in Git
Files in your workspace directory transition through specific lifecycle phases:
Status
Description
Untracked
File exists on disk in your folder, but Git has no record of it in the previous commit snapshot or staging index.
Tracked (Unmodified)
File content exactly matches the snapshot in the last commit (HEAD).
Tracked (Modified)
File has been changed in your workspace since the last commit, but has not yet been staged.
Staged
A modified or new file whose exact changes have been marked to go into the next commit snapshot.
Creating New Files and Inspecting Status
# Create new project files
echo "# Payment Microservice" > README.md
echo "package main" > main.go
# Check repository status
git status
# Output:
# On branch main
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
# README.md
# main.go
Short Status Output
For a compact overview during rapid development, use git status -s:
git status -s
# ?? README.md (?? = Untracked file)
# ?? main.go (?? = Untracked file)
# M config.yaml ( M = Tracked file modified in working tree, not staged)
# M api.go (M = Tracked file staged in index, ready to commit)
The Staging Area (Index), Selective Staging, and Interactive Hunks
What is the Staging Area (Index)?
The Staging Area (often called the Index) is an intermediate staging buffer where you curate and assemble your next commit snapshot. Unlike other version control systems that force you to commit all modified files simultaneously, Git allows you to craft small, atomic, logical commits by selectively staging specific files—or even specific lines of code.
Basic Staging Commands
# Stage a single specific file
git add README.md
# Stage multiple files
git add main.go internal/api.go
# Stage all modified, created, and deleted files in the entire project
git add -A
# Stage all files in current directory and subdirectories
git add .
Interactive Hunk Staging (git add -p)
When you have made multiple changes in a single file but want to split them into separate commits, use patch staging:
git add -p server.go
Git will display individual change diffs (hunks) and prompt you with options:
y: Stage this hunk for the next commit.
n: Do not stage this hunk (keep changes in working tree).
s: Split this hunk into smaller sub-hunks.
e: Manually edit the hunk with your text editor.
q: Quit interactive staging.
Un-staging Files (Keeping Worktree Edits)
If you staged a file accidentally, un-stage it without losing your code:
# Modern Git (2.23+):
git restore --staged secret.env
# Traditional Git:
git reset HEAD secret.env
Each commit should represent a single complete, logical unit of work. If a commit breaks unit tests or contains unrelated refactorings alongside a bugfix, split it. Atomic commits make reviewing code, running git bisect, and reverting bugs effortless.
Lightweight vs Annotated Tags, Release Management, and Cryptographic Verifications
What are Git Tags?
Tags are permanent, human-readable references pointing to specific commits. They are universally used to mark release milestones (e.g. v1.0.0, v2.4.1-rc1).
Lightweight vs. Annotated Tags
Lightweight Tag: A simple pointer bookmark to a commit hash. Contains no additional metadata or signature.
Annotated Tag (Recommended for Releases): Stored as a full object in the Git object database. Contains tagger name, email, timestamp, release notes, and optional GPG signature.
Creating Tags
# 1. Create an Annotated Tag with release notes (Recommended)
git tag -a v1.2.0 -m "Release v1.2.0: Adds SQLite WAL mode and SSH authentication"
# 2. Create a Lightweight Tag
git tag v1.2.0-beta
# 3. Tag a past commit in history
git tag -a v1.1.0 4a3f12e -m "Release v1.1.0 backport"
Listing and Inspecting Tags
# List all tags
git tag
# Filter tags by semantic version pattern
git tag -l "v1.*"
# Inspect tag author, timestamp, and commit diff
git show v1.2.0
Pushing Tags to Remote Repositories
By default, git push does not transmit tags to remote servers. You must push them explicitly:
# Push a single tag
git push origin v1.2.0
# Push all local tags simultaneously
git push origin --tags
# Delete a tag locally and remotely
git tag -d v1.2.0
git push origin --delete v1.2.0
Temporarily Shelving Uncommitted Work, Inspecting Stashes, and Restoring State
Why Use Git Stash?
Often you are in the middle of writing a feature when an urgent production bug requires your immediate attention. You cannot commit unfinished or broken code, but you cannot switch branches with dirty files. Git Stash takes your dirty working tree and staged index, records them on an internal stack, and restores a clean working directory matching HEAD.
Stashing Work
# Quick stash of all tracked modifications
git stash
# Stash with a descriptive message (Recommended)
git stash push -m "WIP: integrating OAuth2 Google provider"
# Include untracked files in the stash
git stash -u
Viewing and Inspecting Stashes
# List all stashed changes
git stash list
# Output:
# stash@{0}: On main: WIP: integrating OAuth2 Google provider
# stash@{1}: On develop: WIP: navbar redesign
# Inspect diff of the most recent stash
git stash show -p stash@{0}
Restoring Stashed Work
# Pop the latest stash (applies changes and deletes it from stash list)
git stash pop
# Apply a specific stash without deleting it
git stash apply stash@{1}
# Create a new branch directly from a stash and switch to it
git stash branch feature/oauth2 stash@{0}
# Drop a specific stash
git stash drop stash@{0}
# Clear all stashes permanently
git stash clear
Navigating Commit Logs, Graphical Tree Views, Filtering, and Diff Inspection
Inspecting the Commit History (git log)
Git offers granular inspection of repository history:
# Basic sequential commit log
git log
# Display one commit per line with short hash
git log --oneline
# Graphical ASCII branch tree visualization
git log --graph --oneline --decorate --all
# Limit number of commits displayed
git log -n 5 --oneline
Filtering History by Author, Date, and Message
# Filter commits by author name or email
git log --author="Sanjeev" --oneline
# Filter commits within a specific timeframe
git log --since="2026-10-01" --until="2026-11-01" --oneline
# Search commit messages for keywords (grep)
git log --grep="CVE-" --oneline
# View history of modifications to a single file
git log -p internal/auth/jwt.go
Viewing File Change Statistics (--stat)
To see which files were touched and how many lines were inserted or deleted per commit:
Accessing Built-in Manuals, Command Syntax, and Subcommand Discovery
Accessing Built-in Git Help
Git ships with complete documentation embedded directly in the binary. You never need an active internet connection to read full manual pages:
# View comprehensive manual page in terminal pager
git help commit
git commit --help
man git-commit
# View quick flag reference without leaving terminal
git commit -h
git branch -h
Discovering Available Subcommands and Guides
# List all standard Git subcommands
git --help
# List all available subcommands including third-party extensions
git help -a
# Read Git conceptual guides (e.g., gitworkflows, giteveryday, gitglossary)
git help workflows
git help everyday
git help glossary
Branch Mechanics, 41-Byte Pointers, Switching, and Branch Deletion
What is a Branch in Git?
In Git, a branch is not an expensive copy of your project directory. A branch is simply a lightweight, movable pointer to a 40-character commit hash stored in a 41-byte text file inside .git/refs/heads/<branch-name>. Because creating a branch writes only 41 bytes to disk, branch operations in Git are instantaneous regardless of repository size.
Figure 1.2: Branching Mechanics in Git (Pointers to Commits).
Creating and Switching Branches
# Modern Git (Recommended):
# 1. Create a new branch
git branch feature/payment-gateway
# 2. Switch to an existing branch
git switch feature/payment-gateway
# 3. Create AND switch in a single command
git switch -c feature/payment-gateway
# Legacy Git command (still functional):
git checkout -b feature/payment-gateway
Listing Branches
# List local branches (* marks current branch)
git branch
# List both local and remote-tracking branches
git branch -a
# Show commit hash and commit subject for each branch
git branch -v
Deleting and Renaming Branches
# Safe delete local branch (fails if branch has unmerged work)
git branch -d feature/payment-gateway
# Force delete local branch (discards unmerged commits)
git branch -D feature/payment-gateway
# Delete branch on remote repository
git push origin --delete feature/payment-gateway
# Rename current active branch
git branch -m new-branch-name
Fast-Forward vs True Three-Way Merges, Aborting Merges, and Merge Commits
What Happens During a Merge?
Merging integrates changes from another branch into your currently checked-out branch. Git analyzes the commit history graph to find the best common ancestor (merge base) between the two branch tips.
1. Fast-Forward Merge (--ff)
If no new commits were made on the base branch (e.g. main) while your feature branch was developed, Git performs a Fast-Forward merge. It simply moves the main branch pointer forward to point directly at the tip commit of the feature branch. No new merge commit is created.
git switch main
git merge feature/docs
2. True Three-Way Merge (--no-ff)
When both main and the feature branch have diverged (each contains commits the other lacks), Git computes a three-way merge using:
Snapshot of Common Ancestor (Merge Base).
Snapshot of Current Branch (HEAD).
Snapshot of Incoming Branch.
Git applies non-overlapping changes automatically and creates a brand-new Merge Commit with two parent commit hashes.
# Enforce creating a merge commit even if fast-forward is possible
git merge --no-ff feature/billing -m "merge: integrate billing microservice"
3. Squash Merge (--squash)
Takes all commits on the feature branch, combines their collective diffs into staged changes in the index, and allows you to make a single clean commit on main:
GitFlow vs GitHub Flow vs Trunk-Based Development Comparison
Industry Branching Strategies
Engineering teams choose branching workflows based on team size, release cadence, and deployment automation:
Strategy
Core Structure
Ideal Use Case
GitHub Flow (Modern Agile)
main is always deployable.
Short-lived feature/* branches.
Pull Requests for peer review & CI gates.
Merge to main triggers automated deploy.
Web applications, SaaS products, microservices, continuous deployment environments.
Trunk-Based Development (DevOps / DORA)
Developers commit directly to main daily (or via 1-day PRs).
Incomplete features hidden behind Feature Toggles.
Extremely heavy automated testing suite.
High-performing engineering teams (FAANG, DevOps leaders), continuous deployment.
GitFlow (Traditional Enterprise)
main (production releases). develop (integration branch). feature/*, release/*, hotfix/*.
Mobile apps (App Store review cycles), on-premise boxed software releases.
The Recommended ProGit Flow
# 1. Update your local main branch
git switch main && git pull --rebase
# 2. Branch off main for your task
git switch -c feature/user-settings
# 3. Code, test, and commit atomically
git commit -m "feat(settings): add dark mode preference toggle"
# 4. Push to ProGit and open a Pull Request
git push -u origin feature/user-settings
Engineering Standards, Hygiene, Commit Hygiene, and Security Protocols
10 Golden Rules for Git Engineering
Make Commits Atomic: Each commit should solve one problem or implement one isolated feature. Never bundle formatting cleanups into bugfixes.
Write Imperative Commit Messages: Write "fix: prevent SQL injection in login handler", not "fixed bug".
Never Commit Secrets or Credentials: Keep .env, private keys (id_rsa, *.pem), passwords, and API tokens out of Git. Use a strict .gitignore.
Always Pull with Rebase: Configure git config --global pull.rebase true to avoid superfluous merge commit bubbles in your history.
Never Rewrite Shared Remote History: Do not force push (git push -f) to main or shared branches. If you must force-push a private feature branch, always use git push --force-with-lease.
Test Before Committing: Ensure code compiles and all unit tests pass before committing. Use pre-commit hooks to automate linting.
Keep Long-Lived Branches Synced: Frequently rebase your feature branches onto main to resolve conflicts early.
Review Your Own Diff Before Committing: Run git diff --staged to verify exactly what changes you are about to record.
Use Descriptive Branch Names: Use structured prefixes: feat/<name>, fix/<issue-id>, refactor/<scope>.
Sign Release Tags Cryptographically: Always use annotated, GPG/SSH-signed tags (git tag -s v1.0.0) for production releases.
Safe Non-Destructive Inversion of Public Commits and Merge Reversals
What is git revert?
git revert is the safest way to undo changes that have already been pushed to a remote repository (such as main on ProGit or GitHub). Unlike git reset, which erases commits from the history graph, git revert records a brand-new commit that applies the exact mathematical inverse diff of the target commit.
Why Revert is Safe for Teams
Because git revert appends a new commit forward in time, it does not rewrite project history. Other team members can pull normally without encountering divergent history or merge conflicts.
Reverting a Single Commit
# Revert the most recent commit on current branch
git revert HEAD
# Revert a specific commit by hash
git revert 4a8f9c2
# Revert without opening text editor (use default generated commit message)
git revert --no-edit 4a8f9c2
Reverting Multiple Commits Without Multiple Commit Messages
# Revert a range of commits and leave changes staged in index (no auto-commit)
git revert -n HEAD~3..HEAD
# Create a single cohesive revert commit
git commit -m "revert: rollback payment v2 integration due to gateway timeout"
Reverting a Merge Commit (The -m Flag)
A merge commit has two parent commits. When you revert a merge commit, Git does not know which branch's changes should be kept. You must specify the mainline parent with -m 1:
Understanding the Three Reset Modes: Soft, Mixed, and Hard
The Three Modes of git reset
git reset moves the current branch pointer (HEAD) backwards in time to a specified commit. How it treats the Staging Area (Index) and your Working Tree depends entirely on which flag you provide:
Command Flag
HEAD Pointer
Staging Area (Index)
Working Tree (Files on Disk)
git reset --soft <commit>
Moved back to <commit>
Untouched! Changes remain staged.
Untouched! All your work remains safe.
git reset --mixed <commit> (Default if no flag given)
Moved back to <commit>
Reset to match <commit> (un-stages changes).
Untouched! Changes remain in working tree as un-staged edits.
git reset --hard <commit>
Moved back to <commit>
Reset to match <commit>.
DESTRUCTIVE! All modified and un-staged changes are permanently erased to match <commit>.
Ideal when you committed too early and want to add another file or change the commit message:
# Undo last commit, keep changes staged in index
git reset --soft HEAD~1
# Add missing file and commit properly
git add forgotten_test.go
git commit -m "feat(api): complete endpoint with unit test coverage"
Scenario 2: Unstage Everything (--mixed)
# Unstage files without losing any code on disk
git reset HEAD~1
Scenario 3: Complete Discard to Match Previous Commit (--hard)
Warning: Destructive Command
git reset --hard permanently destroys uncommitted changes in your working tree. Ensure you have no unsaved work, or run git stash first.
# Discard all changes and restore workspace to exact state of origin/main
git reset --hard origin/main
Modifying the Most Recent Commit, Adding Forgotten Files, and Fixing Messages
What is git commit --amend?
git commit --amend takes whatever is currently staged in your index and combines it with the previous commit, creating a revised commit snapshot that replaces the previous commit entirely.
Use Case 1: Fixing a Typo in the Commit Message
# Open text editor to edit commit message of the last commit
git commit --amend
# Or set new message directly without opening text editor:
git commit --amend -m "fix(auth): correct token expiry calculation in JWT validator"
Use Case 2: Adding a Forgotten File to the Last Commit
# Stage the missed file
git add config/settings.yaml
# Amend commit while preserving the original commit message
git commit --amend --no-edit
The Golden Rule of Amend
Because amending produces a new commit SHA-1 hash, you should never amend a commit that has already been pushed to a shared branch on ProGit or GitHub. Doing so will cause your local branch and the remote branch to diverge.
Linear Commit Graphs, Rebasing onto Main, and Interactive Rebase (rebase -i)
What is Rebasing?
In Git, there are two primary ways to integrate changes from one branch into another: merging and rebasing.
While a merge creates a new three-way merge commit that ties two divergent histories together, rebasing takes the commits from your feature branch and replays them one-by-one on top of the newest commit of the base branch. The result is a perfectly linear, sequential project history without branching noise.
Rebasing a Feature Branch onto main
# 1. Fetch latest changes from remote
git fetch origin
# 2. Switch to your feature branch
git switch feature/payments
# 3. Rebase your feature commits onto the tip of origin/main
git rebase origin/main
Interactive Rebase (git rebase -i)
Interactive rebase is one of Git's most potent features. It allows you to rewrite, squash, reorder, or drop your local unpushed commits before opening a pull request:
# Interactively inspect and edit the last 4 commits
git rebase -i HEAD~4
Git opens your text editor with a list of commits prefixed by command words:
pick a1b2c3d feat(user): add database migration for users table
pick e4f5g6h fix typo in migration
pick 7i8j9k0 feat(user): add repository query methods
pick 1l2m3n4 wip test debugging
Interactive Rebase Commands
Command
Effect
pick (p)
Keep the commit as-is.
reword (r)
Keep the commit, but pause to edit the commit message.
edit (e)
Pause rebase at this commit to amend code or add files.
squash (s)
Melt this commit into the previous commit and concatenate their commit messages.
fixup (f)
Melt this commit into the previous commit and discard this commit's message.
drop (d)
Delete this commit entirely from history.
Resolving Conflicts During a Rebase
# 1. Edit conflicting files and resolve conflict markers
# 2. Stage resolved files:
git add internal/user/repo.go
# 3. Continue the rebase:
git rebase --continue
# Or abort and restore original state:
git rebase --abort
The Ultimate Safety Net: Tracking HEAD Movements and Recovering History
What is the Reference Log (reflog)?
Git maintains a local ledger called the Reference Log (reflog) in .git/logs/HEAD. Every single time HEAD or a local branch pointer moves—whether due to a commit, checkout, switch, rebase, merge, reset, or pull—Git appends an immutable entry to the reflog.
Even if you run git reset --hard and believe you erased your work, the commits still exist in the Git object database and are tracked in the reflog for up to 90 days before garbage collection (git gc) runs.
Viewing the Reflog
git reflog
# Output:
# 4a8f9c2 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
# b7e3d11 HEAD@{1}: commit: feat(api): add health check probe
# 9f2a4b8 HEAD@{2}: commit: feat(auth): implement jwt token validator
# 1c3e5d7 HEAD@{3}: checkout: moving from feature/auth to main
Time-Traveling with HEAD@{n}
Each reflog entry has a selector representing steps back in time:
# View commit diff of where HEAD was 2 actions ago
git show HEAD@{2}
# View reflog entries for a specific branch
git reflog show feature/payments
# View where HEAD was 2 hours ago
git reflog show --date=relative
Disaster Recovery Recipes: Undoing Hard Resets, Restoring Deleted Branches, and Rescuing Work
Emergency Disaster Recovery Recipes
Here are step-by-step solutions to the 4 most common Git emergencies:
Scenario 1: You Ran git reset --hard by Accident
# 1. Run reflog to find the commit hash immediately before your reset
git reflog
# 2. Look for the action before "reset: moving to...":
# b7e3d11 HEAD@{1}: commit: feat(api): add health check probe
# 3. Reset hard to that reflog selector:
git reset --hard HEAD@{1}
# Working directory and all commits are 100% restored!
Scenario 2: You Deleted an Unmerged Branch by Mistake
# You ran: git branch -D feature/payments
# 1. Find the commit hash that was at the tip of the deleted branch:
git reflog
# Look for: 9f2a4b8 HEAD@{4}: commit: feat(pay): integrate stripe api
# 2. Re-create the branch pointing directly at that commit hash:
git branch feature/payments 9f2a4b8
# Branch and all unmerged commits are completely restored!
Scenario 3: You Committed to the Wrong Branch (e.g. main instead of a feature branch)
# 1. Create a new feature branch from your current position (preserves commit):
git branch feature/new-analytics
# 2. Reset main back one commit:
git reset --hard HEAD~1
# 3. Switch to your feature branch:
git switch feature/new-analytics
# main is clean, and your work is safely on the new feature branch!
Scenario 4: Finding Dangling Objects with git fsck
If all else fails and reflog was cleared, scan the raw object database:
Ignoring Build Artifacts, OS Junk, Secrets, Glob Patterns, and Cache Removal
What is .gitignore?
A .gitignore text file placed in your project root instructs Git to deliberately ignore untracked files that should never be committed into the repository—such as compiled binaries, dependency folders, logs, OS metadata, and sensitive credentials.
Pattern Matching & Glob Syntax Rules
Pattern
Behavior / Match Rule
# Comment
Lines starting with # are comments and ignored.
*.log
Matches any file ending in .log anywhere in the project tree.
build/
Matches and ignores any directory named build and all its children.
/dist
Leading slash anchors the match to the project root (ignores root dist/, but not src/dist/).
**/temp
** matches directories recursively at any depth.
!important.log
Exclamation point negates an ignore pattern (forces file to be tracked).
Un-tracking a File That Was Committed Accidentally
If a file was committed before being added to .gitignore, Git continues tracking it. To remove it from Git while keeping the file on your local disk:
# Remove from Git index without deleting the file on disk
git rm --cached .env
# Commit the removal
git commit -m "chore: stop tracking .env secret file"
Line-Ending Normalization, Binary File Flags, Merge Drivers, and Export Controls
What is .gitattributes?
The .gitattributes file defines path-specific settings across your repository. It ensures consistent line endings between developers on Windows and Linux/macOS, specifies which files are binary, customizes merge drivers, and controls what is excluded from release archives.
1. Cross-Platform Line-Ending Normalization
Prevent noisy diffs where every line is marked modified due to Carriage Return differences:
# Set default behavior: auto-normalize text files to LF on commit
* text=auto
# Explicitly declare text files that must ALWAYS have LF line endings
*.go text eol=lf
*.sh text eol=lf
*.js text eol=lf
*.py text eol=lf
*.md text eol=lf
# Windows batch files must retain CRLF
*.bat text eol=crlf
*.cmd text eol=crlf
2. Declaring Binary Files
Ensure Git does not attempt to calculate text diffs or normalize line endings on binaries:
Chapter 3: GIT ADVANCED/Git Large File Storage (LFS)
25 / 54 (46%)
Git Large File Storage (LFS)
Handling Multi-Megabyte Binaries Without Bloating Repository Objects
Why Git Struggles with Large Binary Files
In standard Git, every modification to a file stores a complete new compressed object in .git/objects/. If a designer commits five 200 MB Photoshop files or machine learning weights, every developer who clones the repository must download all 1 GB of historical binaries—even if they only need the latest code.
Git Large File Storage (Git LFS) solves this by replacing large files inside your Git repository with tiny pointer text files (around 130 bytes). The actual binary payloads are stored on dedicated LFS storage servers (such as ProGit's built-in LFS or S3 buckets).
Anatomy of an LFS Pointer File
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a2846a4d773f3e389f4b00f4f71ec16d87fa923c52c2c00882d16177aa3e0
size 124589201
Installing and Using Git LFS
# 1. Install Git LFS hooks on your machine (one-time setup)
git lfs install
# 2. Tell LFS which file extensions to track
git lfs track "*.psd"
git lfs track "*.bin"
git lfs track "models/*.onnx"
# 3. Commit the generated .gitattributes file
git add .gitattributes
git commit -m "chore: track binary models with Git LFS"
# 4. Add, commit, and push normally
git add models/weights.onnx
git commit -m "feat(ai): add initial quantized transformer weights"
git push origin main
Managing Local LFS Disk Cache
# Fetch LFS objects for current checkout only
git lfs pull
# Delete old cached LFS files from local machine to free up disk space
git lfs prune
Cryptographic Proof of Identity Using GPG and Modern SSH Signatures
Why Cryptographically Sign Commits?
In Git, the author and committer names and emails are arbitrary strings defined in git config. Anyone can easily spoof commits claiming to be Linus Torvalds or your enterprise CEO.
Cryptographic Signing generates a mathematical signature embedded into each commit or tag using your private key. Remote platforms like ProGit and GitHub verify this against your registered public key and display a green "Verified" badge.
Option A: Modern SSH Signature Signing (Git 2.34+ - Recommended)
Starting with Git 2.34, you no longer need complex GPG tools; you can sign commits directly with your existing Ed25519 SSH key:
# 1. Tell Git to use SSH for signing
git config --global gpg.format ssh
# 2. Specify your SSH public key path
git config --global user.signingkey ~/.ssh/id_ed25519.pub
# 3. Enable automatic commit signing for all commits
git config --global commit.gpgsign true
# 4. Enable automatic tag signing
git config --global tag.gpgsign true
Option B: Traditional GPG Signing
# 1. Generate GPG Key Pair
gpg --full-generate-key
# Choose RSA (4096-bit) or Ed25519, enter name and email
# 2. Get your GPG Key ID
gpg --list-secret-keys --keyid-format=long
# Example output: sec rsa4096/3AA5C34371567BD2 ...
# 3. Configure Git with your key ID
git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true
Verifying Signed Commits in Terminal
git log --show-signature -n 1
# Output:
# commit 9e2b10a (HEAD -> main)
# gpg: Signature made Sun Nov 8 11:15:20 2026 IST
# gpg: using ED25519 key 4A8F9C2...
# gpg: Good signature from "Sanjeev Senapati <sanjeev@prodevans.com>" [ultimate]
Porting Specific Commits Across Branches, Generating Email Patches, and Applying Diffs
What is git cherry-pick?
git cherry-pick allows you to select a specific commit from any branch and apply its exact changes onto your currently checked-out branch as a brand-new commit.
This is invaluable when a critical bugfix was made on a develop branch and needs to be backported immediately to a production main or release/v1.0 branch without merging unrelated unfinished work.
Basic Cherry-Picking
# 1. Switch to target branch where you want the fix
git switch main
# 2. Apply a specific commit from develop branch
git cherry-pick 7a4e1b9
# 3. Cherry-pick without auto-committing (leaves changes staged)
git cherry-pick -n 7a4e1b9
Cherry-Picking a Range of Commits
# Cherry-pick all commits from A up to B (inclusive)
git cherry-pick A^..B
Generating and Applying Patches
Before modern pull requests, developers collaborated by exchanging email patches:
# Generate a formatted Unix mailbox patch for the latest commit
git format-patch -1 HEAD
# Creates 0001-feat-add-sqlite-wal.patch
# Inspect what the patch will change
git apply --stat 0001-feat-add-sqlite-wal.patch
# Check if patch applies cleanly without errors
git apply --check 0001-feat-add-sqlite-wal.patch
# Apply patch and record commit with original author, timestamp, and message
git am 0001-feat-add-sqlite-wal.patch
Conflict Marker Anatomy, Three-Way Resolution Walkthrough, and Verification
Why Do Merge Conflicts Occur?
A merge conflict occurs when two different branches have modified the exact same lines of a file since their common ancestor, or when one branch modified a file while the other branch deleted it. Git refuses to guess which version is correct and pauses the merge so a human engineer can review.
Anatomy of Conflict Markers
Git edits the conflicting file directly and inserts visual delimiter markers:
<<<<<<< HEAD
// Changes on your current branch (e.g. main)
const databaseURL = "sqlite://data/progit.db?cache=shared&mode=wal";
=======
// Incoming changes from the branch being merged (e.g. feature/db)
const databaseURL = process.env.DATABASE_URL || "postgres://localhost:5432/progit";
>>>>>>> feature/postgres-support
Step-by-Step Resolution Process
Locate Conflicted Files:
git status
# Conflicted files show as "both modified: database/connection.js"
Open and Edit Files:
Open the file in your editor, decide which logic to preserve (or combine them), and delete all marker lines (<<<<<<<, =======, >>>>>>>).
How Git Events Drive Automated Testing, Webhook Triggers, and Status Gating
The Convergence of Git and CI/CD
Modern Continuous Integration and Continuous Deployment (CI/CD) is entirely driven by Git repository events. Rather than manual deployments by system administrators, Git acts as the single source of truth (GitOps).
Figure 3.1: Git Event-Driven CI/CD Architecture.
Key Git Triggers for Automation
Push to Feature Branch: Triggers automated unit testing, static code analysis (SonarQube/golangci-lint), and builds preview containers.
Pull Request Created / Synchronized: Executes end-to-end integration tests and reports pass/fail status back to the PR merge button.
Merge to main: Automatically compiles release binaries, builds Docker container images, pushes to registry, and rolls out to staging/production.
Tag Created (v*): Triggers release pipelines, generates GitHub/ProGit release notes, and deploys release packages.
Automating Checks with Pre-Commit, Commit-Msg, Pre-Push, and Server Hooks
What are Git Hooks?
Git hooks are custom executable scripts that Git fires automatically before or after key actions (like committing, pushing, or receiving code). They live inside .git/hooks/ as sample scripts ending in .sample.
Client-Side vs. Server-Side Hooks
Hook Name
Scope
When It Executes & Use Case
pre-commit
Client
Runs before commit message is written. Validates code formatting, runs linters, scans for leaked API keys. Exit code ≠ 0 cancels commit.
commit-msg
Client
Validates the commit message text (enforces Conventional Commits formatting).
pre-push
Client
Runs right before git push transmits objects. Runs fast unit tests.
pre-receive
Server
Runs on Git server (ProGit). Enforces branch protection, verifies signed commits, prevents pushes with files > 50 MB.
post-receive
Server
Runs on Git server after push is accepted. Triggers webhooks and sends notifications.
Writing a Custom pre-commit Hook
Create an executable script at .git/hooks/pre-commit:
#!/bin/bash
# .git/hooks/pre-commit
echo "==> Running automated pre-commit quality checks..."
# Check 1: Scan for accidental AWS / Private key commits
if git diff --staged | grep -E "AKIA[0-9A-Z]{16}|BEGIN PRIVATE KEY"; then
echo "ERROR: Leaked credentials or private keys detected in staged files!"
exit 1
fi
# Check 2: Run Go unit tests
go test -v ./...
if [ $? -ne 0 ]; then
echo "ERROR: Go unit tests failed! Fix tests before committing."
exit 1
fi
echo "All pre-commit checks passed cleanly."
exit 0
Make the script executable:
chmod +x .git/hooks/pre-commit
# Version controlling hooks across your entire team:
git config core.hooksPath .githooks
Embedding External Repositories, Recursive Cloning, and Submodule Synchronization
What is a Git Submodule?
A submodule allows you to keep an external Git repository as a subdirectory of another Git repository. The parent repository does not track the external repository's files directly; instead, it records a specific commit SHA-1 pointer in the parent tree and maintains remote repository metadata in a file named .gitmodules.
Adding a Submodule to Your Project
# Add external library repository into libs/proto
git submodule add https://progit.co.in/prodevans/proto.git libs/proto
# Commit the new submodule and .gitmodules file
git add .gitmodules libs/proto
git commit -m "chore: embed proto contract submodule"
Cloning a Repository That Contains Submodules
When you run normal git clone, submodule directories will be empty! To download everything automatically:
# Clone repo AND all embedded submodules recursively
git clone --recurse-submodules https://progit.co.in/prodevans/app.git
# If you already cloned normally, initialize and fetch submodules:
git submodule update --init --recursive
Updating Submodule Commit Pointers
# Pull latest upstream commits inside the submodule
cd libs/proto
git switch main && git pull origin main
# Switch back to parent repo and record the new submodule pointer
cd ../..
git add libs/proto
git commit -m "chore: bump proto submodule to latest commit"
Multiple Remotes, Refspecs, Remote Pruning, and Safe Force-Pushing
Managing Multiple Remotes
In open-source and enterprise workflows, you often track both your own fork (origin) and the shared company upstream repository (upstream):
# View current remotes and URLs
git remote -v
# Add upstream remote pointing to central organization repo
git remote add upstream https://progit.co.in/prodevans/core-service.git
# Sync your local main with upstream main
git fetch upstream
git switch main
git merge upstream/main
Pruning Deleted Remote Tracking Branches
When a colleague deletes a merged branch on the remote server, your local Git still keeps obsolete origin/feature-xyz references. Clean them up with pruning:
# Fetch and prune deleted branches in one command
git fetch --prune
# Or prune a specific remote
git remote prune origin
Understanding Refspecs
Inside .git/config, Git defines how remote branches map to local tracking branches via refspecs:
Chapter 4: PROGIT TUTORIAL/ProGit Architecture & Zero CGO
33 / 54 (61%)
ProGit Architecture & Zero CGO
Pure Go Engine, Sub-50ms Cold Boots, < 40 MB RAM Footprint, and High-Concurrency SQLite WAL
What is ProGit?
ProGit is an ultra-fast, high-concurrency self-hosted Git collaboration platform engineered entirely in pure Go with zero external database dependencies and an embedded modern React 19 single-page application.
ProGit provides development teams and enterprises with a sovereign, sovereign-controlled alternative to public cloud services like GitHub and GitLab, as well as an upgraded modern successor to legacy platforms like Gogs and Gitea.
Zero CGO & Pure Go Compilation
Unlike traditional Git platforms that link against native C libraries (libgit2, SQLite via GCC), ProGit compiles with CGO_ENABLED=0. It produces a completely static binary that runs on Linux, macOS, and minimal Alpine containers without external shared library dependencies.
Architectural Highlights
Memory Footprint: Consumes under 40 MB of RAM under typical production workloads (compared to GitLab's 4 GB to 8 GB minimum).
Sub-50ms Cold Starts: Boots from disk and starts serving HTTP and SSH Git traffic in under 50 milliseconds.
High-Concurrency SQLite WAL: Uses modernc.org/sqlite with Write-Ahead Logging (WAL) and automated checkpoints for outstanding concurrent read/write throughput without needing PostgreSQL or MySQL.
Built-in SSH Server (Port 2222): Pure-Go SSH implementation isolated from host system accounts.
Embedded React 19 UI: Single binary embeds all compiled web assets, templates, and localization files.
# Start container in background
docker compose up -d
# Verify container is running and healthy
curl -I http://localhost:3000/
# Output: HTTP/1.1 200 OK
Initial Configuration Wizard, Super Administrator Setup, and Disabling the Install URL
Initial Setup via Web Installation Wizard
When you access ProGit for the very first time at http://localhost:3000/install, you are presented with the graphical setup wizard:
Database Settings: Select SQLite3 (path defaults to /data/progit.db).
Application Name: Set your organization or company title (e.g. ProGit Enterprise).
Domain & Port Settings: Domain progit.co.in, HTTP Port 3000, SSH Port 2222.
Administrator Account: Create the initial Super Administrator username, password, and email.
Click Install ProGit to initialize the database schema.
Hardening Security: Disabling the /install Path
Preventing Unauthorized Re-Installation
Once the installation wizard finishes and the initial admin user is created, ProGit automatically sets INSTALL_LOCK = true in app.ini. This permanently disables the /install endpoint in the browser to prevent malicious attackers from reconfiguring the database.
Creating Subsequent Administrators via CLI
For ongoing operations, the ops team can create additional admin accounts directly via the CLI without exposing any browser install endpoints:
# Create new admin user directly via CLI binary
progit admin user create --name alice --password "SuperSecretPass123!" --email alice@prodevans.com --admin=true
Creating Repositories, Pushing Existing Projects, and Enforcing Branch Protection Rules
Repository Lifecycle on ProGit
Repositories on ProGit can be created via the web interface or pushed directly from an existing local project:
Pushing an Existing Local Codebase to ProGit
# 1. In terminal, navigate to your local codebase
cd /path/to/my-project
# 2. Add your ProGit repository as the 'origin' remote
git remote add origin https://progit.co.in/prodevans/my-project.git
# 3. Push all commits and set upstream branch tracking
git push -u origin main
Branch Protection Rules
To protect critical branches (e.g., main, release/*) from regressions or accidental force-pushes, ProGit allows repository administrators to configure branch protection:
Require Pull Request Before Merging: Direct pushes to the protected branch are blocked. All code must pass peer review.
Require Status Checks to Pass: Automated CI/CD pipelines (unit tests, security scanners) must report success before the merge button unlocks.
Restrict Who Can Merge: Limit merge approval authority to designated tech leads or repo owners.
Prevent Force Pushes & Deletion: Disallows git push --force and branch deletion.
Chapter 4: PROGIT TUTORIAL/Online Web IDE & 8 Syntax Themes
38 / 54 (70%)
Online Web IDE & 8 Syntax Themes
In-Browser Code Editing, Folder Uploads, Prettier Formatting, and Syntax Highlighters
The ProGit Built-In Web IDE
ProGit features an integrated, full-featured in-browser code editor that allows developers to review code, create files, edit multi-line source code, and format on the fly without launching a local IDE.
Key Web IDE Capabilities
Online File Creation & Editing: Add new files, modify source code, and commit directly from your web browser with custom commit messages.
Recursive Folder Uploads: Drag-and-drop entire directory trees into the browser to upload assets and source bundles.
Prettier Integration: One-click automated code formatting for JavaScript, TypeScript, JSON, Markdown, and YAML.
8 Curated Syntax Highlighter Themes
Developers can customize their editing workspace with 8 beautiful built-in color schemes:
Theme Name
Aesthetic Profile
Dracula
Rich dark purple with vibrant pink and cyan accents.
Nord
Arctic, north-bluish clean minimalist palette.
Monokai
Classic hacker dark theme with bright yellow and magenta highlights.
GitHub Dark
The familiar, elegant dark theme from GitHub.
Synthwave '84
Neon retro 80s aesthetic with glowing purples and magentas.
Public Visibility, Member-Only Merge Controls, and Three Native Merge Strategies
Pull Requests in ProGit
ProGit implements full GitHub-class Pull Request workflows for collaborative code review and quality gating:
1. Public Visibility for Open-Source Collaboration
In public repositories, any user—even unauthenticated visitors—can explore code diffs, review discussions, and inspect commits. This enables seamless community open-source participation.
2. Restricted Member-Only Merge Controls
Security Scoped Merge Permissions
While anyone can read public pull requests, the Merge Pull Request action is strictly restricted to authenticated users who possess write permissions on the target repository or the original repository owner.
3. Three Native Merge Strategies
When merging a pull request, ProGit offers three configurable merge strategies:
Create a Merge Commit (--no-ff): Preserves all historical commits from the branch and adds a merge commit.
Squash and Merge: Combines all commits from the PR into a single clean commit on the target branch. Keeps history compact.
Rebase and Merge: Replays commits linearly on top of the base branch without creating a merge commit.
Dual-Interface Documentation via Web Markdown Editor and Offline Bare Git Clone
The Git-Backed Wiki Engine
Documentation is a first-class citizen in ProGit. Every repository has an optional built-in Wiki powered by a dedicated Git repository under the hood.
Dual-Interface Documentation Management
Web UI Markdown Editor: Create, edit, and organize documentation pages directly in the browser with live Markdown preview. If no wiki exists, users see a GitHub-style "Create your first Wiki page" prompt.
Offline Git Bare Clone: Every wiki is stored as a Git repository named <repo-name>.wiki.git. You can clone it locally, edit markdown in your favorite editor, and push changes:
# Clone repository wiki locally to your workstation
git clone https://progit.co.in/prodevans/backend.wiki.git
# Enter wiki folder and edit markdown pages
cd backend.wiki
echo "# Architecture Overview" > Architecture.md
# Commit and push documentation updates
git add Architecture.md
git commit -m "docs: add system architecture diagram"
git push origin main
Repository Wiki Settings
Repository administrators can enable or disable the Wiki module at any time via Repository Settings → Collaboration Features → Enable Wiki.
Zero-Configuration Web Hosting from Git Branches and Custom Domains
What is ProGit Pages?
ProGit Pages is a built-in static site hosting service directly integrated into ProGit. It allows engineering teams to host documentation portals, project showcases, and single-page apps directly from their repository branches with zero external web servers.
Supported Branch Sources
You can configure ProGit Pages to publish from:
The docs branch (serving from root).
The gh-pages branch (standard GitHub Pages convention).
The main branch (serving from the /docs directory).
Custom Domain (CNAME) Support
To bind a custom domain (such as progit.co.in), simply commit a CNAME text file to the publishing branch root:
# CNAME file content:
progit.co.in
ProGit Pages automatically configures HTTP routing and serves assets with optimal caching headers.
Chapter 4: PROGIT TUTORIAL/Developer Direct Messaging
42 / 54 (78%)
Developer Direct Messaging
User Search, Connection Request Acceptance Protocols, and Server Enable Flags
Developer Direct Messaging
ProGit provides a secure internal direct messaging feature allowing developers to communicate directly inside the platform without needing third-party chat tools.
Privacy-Preserving User Discovery
No Public User Scraping: To protect developers from spam, username harvesting, and misuse, ProGit does not display an unrestricted public directory of all registered users.
Search by Username or Email: Users must actively search for a teammate's username or email ID to initiate contact.
Connection Acceptance Protocol: When a user sends a message to a new contact, the recipient receives a connection prompt. Messages must be accepted before a conversation begins. If the recipient rejects the request, the sender is respectfully notified.
Server-Wide Configuration Flag
System administrators can enable or disable the direct messaging capability across the entire server via app.ini:
[feature]
ENABLE_MESSAGES = true # Set to false to disable messaging platform-wide
Visual Sprint Columns, Issue Tracking, Milestones, and Story Assignment
Kanban Project Boards in ProGit
Every repository and organization on ProGit features visual Kanban boards for agile sprint planning and issue tracking:
Custom Sprint Columns: Organize tasks into Backlog, In Progress, Code Review, and Done.
Drag-and-Drop Workflow: Move cards between columns with automated status synchronization.
Issue & PR Integration: Link issues directly to Pull Requests; merging a PR automatically moves associated cards to Done.
Team Assignment & Milestones: Assign assignees, label priorities (e.g. bug, p0-blocker), and group tasks into release Milestones with burndown tracking.
Chapter 4: PROGIT TUTORIAL/Danger Zone & Administration
45 / 54 (83%)
Danger Zone & Administration
Repository Renaming, Ownership Transfer, Archiving, and Deletion Security
Repository Danger Zone Operations
Located at the bottom of Repository Settings → Danger Zone, these actions permanently alter or remove repository resources and require explicit confirmation:
1. Transferring Repository Ownership
Transfer a repository to another user or an organization (e.g. from sanjeev/backend to prodevans/backend). ProGit automatically updates internal Git paths and sets up HTTP redirect stubs so existing Git remotes continue working.
2. Archiving a Repository (Read-Only Mode)
Places the repository in immutable read-only mode. All commits, issues, and wikis remain readable, but all pushes, pull request creations, and edits are blocked.
3. Deleting a Repository
Irreversible Deletion Protection
Deleting a repository purges all Git objects, wiki pages, issues, and pull request histories from disk. ProGit requires the user to manually type the full repository name (e.g. prodevans/core-api) and verify their password before proceeding.
Multi-Architecture Container Images (amd64, arm64) with Layer Caching
Production Multi-Architecture Container Build
Save this as .github/workflows/docker-publish.yml. It builds production images for both Intel/AMD (x86_64) and ARM64 (Apple Silicon / AWS Graviton) using Docker Buildx and pushes to GitHub Container Registry (GHCR):
Chapter 5: CI/CD & GITHUB ACTIONS/Pipeline 3: Deploy via SSH
50 / 54 (93%)
Pipeline 3: Deploy via SSH
Zero-Downtime Remote Production Deployment via SSH and Docker Compose
Production SSH Deployment Pipeline
Save this as .github/workflows/deploy.yml. It securely connects to your production Linux server over SSH, pulls the newest container image, and performs a zero-downtime rolling reload:
name: Deploy to Production Server
on:
workflow_run:
workflows: [ "Build and Publish Container Image" ]
types: [ completed ]
branches: [ main ]
jobs:
deploy:
name: Remote SSH Deployment
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Execute Remote Deployment Script via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.PROD_SERVER_HOST }}
username: ${{ secrets.PROD_SERVER_USER }}
key: ${{ secrets.PROD_SSH_PRIVATE_KEY }}
port: 22
script: |
echo "==> Deploying newest ProGit release..."
cd /opt/progit
# Pull latest container image
podman pull ghcr.io/prodevans/progit:main
# Restart service container with zero downtime
podman-compose down && podman-compose up -d
# Health check probe
sleep 3
curl -f http://127.0.0.1:3000/api/v1/version || exit 1
echo "==> Deployment successfully verified!"
Never echo environment secrets to the console. GitHub Actions masks known repository secrets, but nested variables or curl payloads can be accidentally leaked. Use ::add-mask:: if your script dynamically fetches sensitive values: