1. Introduction to Git and Version Control {#introduction}
Version control systems are the backbone of modern software development, and Git stands as the most widely adopted distributed version control system in the world. Created by Linus Torvalds in 2005 to manage the Linux kernel's source code, Git has evolved into an indispensable tool that transcends programming—writers, designers, data scientists, and countless other professionals rely on it daily.
What Makes Git Special?
Unlike centralized version control systems like SVN or CVS, Git is fundamentally distributed. Every developer's working copy is a complete repository with full history and version-tracking capabilities, independent of network access or a central server. This architecture provides several profound advantages:
Speed: Nearly all operations are local, making Git blazingly fast compared to systems that need constant server communication.
Redundancy: Every clone is a full backup of the entire project history.
Branching: Git's branching model is exceptionally lightweight and fast, encouraging experimental workflows.
Data Integrity: Git uses SHA-1 hashing to ensure data integrity—every file and commit is checksummed, making silent corruption nearly impossible.
Non-linear Development: Multiple parallel branches of development can proceed simultaneously without interfering with each other.
The Philosophy Behind Git
Git was designed with specific goals that shape how it works:
Content-addressable filesystem: Git is fundamentally a simple key-value data store where the content itself determines the key (via SHA-1 hashing).
Snapshots, not differences: Unlike systems that store file changes, Git takes snapshots of your entire project at each commit.
Branch-centric workflow: Branches are cheap and merging is encouraged.
Distributed is better: No single point of failure; everyone has the full history.
Understanding Git's Learning Curve
Git has a reputation for complexity, and this reputation isn't entirely undeserved. However, the complexity stems not from arbitrary design choices but from Git's powerful, flexible model. Once you understand Git's internal model—how it stores data, what commits really are, how branches work—the commands begin to make intuitive sense.
This guide will take you from absolute beginner to Git expert, explaining not just what commands do, but why they work the way they do, and when you should use them. We'll cover every significant Git command with unprecedented depth, including real-world examples, internal mechanics, and expert-level variations.
2. Git's Architecture and Internal Model {#architecture}
Before diving into commands, understanding Git's internal architecture is crucial. This knowledge transforms Git from a collection of mysterious commands into a logical, predictable system.
The Object Database
At its core, Git is a content-addressable filesystem with a version control interface built on top. Everything in Git—files, directories, commits—is stored as an object in the .git/objects directory.
There are four types of objects:
1. Blob (Binary Large Object)
- Stores file contents
- Contains no metadata—not even the filename
- Identified by SHA-1 hash of contents
- Immutable once created
2. Tree
- Represents a directory
- Contains pointers to blobs (files) and other trees (subdirectories)
- Stores filenames and permissions
- Like a snapshot of directory structure
3. Commit
- Points to a tree object (the project snapshot)
- Contains metadata: author, committer, timestamp, message
- Points to parent commit(s)
- Creates the history chain
4. Tag
- Points to a commit (usually)
- Contains annotation metadata
- Used for marking releases
How Git Stores Data
When you commit changes, Git:
- Creates blob objects for each modified file
- Creates tree objects representing directory structure
- Creates a commit object pointing to the root tree
- Updates the branch reference to point to the new commit
This creates an immutable, content-addressed history. Because objects are identified by content hash, identical files are stored only once (deduplication), and any corruption is immediately detectable.
References (Refs)
While objects are immutable and content-addressed, Git needs mutable pointers to track branches, tags, and other important commits. These are called references or refs, stored in .git/refs/.
Types of references:
-
refs/heads/*: Local branches -
refs/remotes/*: Remote-tracking branches -
refs/tags/*: Tags -
HEAD: Special reference pointing to current branch
The Index (Staging Area)
Git uniquely features a staging area (also called the index), sitting between your working directory and the repository. This intermediate step allows you to carefully craft commits, including only the changes you want.
The index is stored in .git/index as a binary file containing:
- List of files to be committed
- Their blob object hashes
- File metadata (permissions, timestamps)
The Three States
Every file in Git exists in one of three states:
- Modified: Changed in working directory but not staged
- Staged: Marked for inclusion in next commit
- Committed: Safely stored in repository
Understanding these states is fundamental to mastering Git's workflow.
3. Installation and Initial Configuration {#installation}
Installing Git
Git is available for all major platforms:
Linux:
# Debian/Ubuntu sudo apt-get update sudo apt-get install git # Fedora/RHEL sudo dnf install git # Arch Linux sudo pacman -S git macOS:
# Using Homebrew brew install git # Or download from git-scm.com # Xcode Command Line Tools also includes Git xcode-select --install Windows:
- Download Git for Windows from git-scm.com
- Includes Git BASH, a terminal emulator
- Integrates with Windows Terminal
Verify Installation:
git --version This displays the installed Git version, confirming successful installation.
4. git config - Configuration Management
Purpose
git config manages Git configuration at three hierarchical levels, controlling everything from user identity to default behaviors, aliases, and external tool integration.
Configuration Levels
Git configuration operates at three scopes, each overriding the previous:
1. System-level (--system)
- Location:
/etc/gitconfig(Linux/macOS) orC:\Program Files\Git\etc\gitconfig(Windows) - Applies to all users and repositories on the system
- Requires administrator privileges to modify
- Rarely used except in corporate environments
2. Global/User-level (--global)
- Location:
~/.gitconfigor~/.config/git/config - Applies to all repositories for the current user
- Most common configuration level
- Your identity and preferences go here
3. Repository-level (--local)
- Location:
.git/configin the repository - Applies only to the specific repository
- Overrides global and system settings
- Default when no scope specified
Essential Initial Configuration
After installing Git, you must configure your identity:
git config --global user.name "Your Full Name" git config --global user.email "your.email@example.com" Why this matters: Every commit includes author information. Without configuration, Git will guess (poorly) or refuse to commit. This identity is embedded in every commit you create and cannot be easily changed later without rewriting history.
Advanced Identity Configuration:
# Set different identity for a specific repository cd /path/to/work-project git config user.name "Your Name" git config user.email "work.email@company.com" # Use conditional includes for automatic identity switching # In ~/.gitconfig: [includeIf "gitdir:~/work/"] path = ~/.gitconfig-work [includeIf "gitdir:~/personal/"] path = ~/.gitconfig-personal This powerful feature automatically applies different configurations based on directory location.
Default Branch Name
Modern Git allows customizing the default branch name:
git config --global init.defaultBranch main This sets "main" as the default branch for new repositories instead of "master". Many projects and platforms have adopted this convention.
Editor Configuration
Git opens an editor for commit messages, interactive rebases, and other tasks:
# Use your preferred editor git config --global core.editor "vim" git config --global core.editor "nano" git config --global core.editor "code --wait" # VS Code git config --global core.editor "subl -n -w" # Sublime Text git config --global core.editor "emacs" The --wait flag (VS Code) tells Git to wait for you to close the file before continuing.
Viewing Configuration
# Show all configuration and their sources git config --list --show-origin # Show specific configuration value git config user.name # List all global configuration git config --global --list # List all local (repository) configuration git config --local --list Removing Configuration
# Remove specific setting git config --global --unset user.name # Remove entire section git config --global --remove-section user Advanced Configuration Options
Line Ending Configuration:
Different operating systems use different line ending conventions (CRLF on Windows, LF on Unix). Git can normalize these:
# Windows: Convert LF to CRLF on checkout, CRLF to LF on commit git config --global core.autocrlf true # macOS/Linux: Convert CRLF to LF on commit, no conversion on checkout git config --global core.autocrlf input # Disable line ending conversion entirely git config --global core.autocrlf false Whitespace Handling:
# Warn about whitespace errors (trailing whitespace, etc.) git config --global core.whitespace trailing-space,space-before-tab # Make Git diff highlight whitespace errors git config --global color.diff.whitespace "red reverse" Color Configuration:
# Enable colored output git config --global color.ui auto # Customize specific colors git config --global color.branch.current "yellow reverse" git config --global color.branch.local yellow git config --global color.branch.remote green git config --global color.status.added green git config --global color.status.changed yellow git config --global color.status.untracked red Diff and Merge Tools:
# Configure external diff tool git config --global diff.tool meld git config --global difftool.meld.path "/usr/bin/meld" git config --global difftool.prompt false # Configure external merge tool git config --global merge.tool kdiff3 git config --global mergetool.kdiff3.path "/usr/bin/kdiff3" git config --global mergetool.prompt false git config --global mergetool.keepBackup false Aliases for Efficiency:
Aliases are custom shortcuts for Git commands:
# Basic aliases git config --global alias.co checkout git config --global alias.br branch git config --global alias.ci commit git config --global alias.st status # Advanced aliases git config --global alias.unstage 'reset HEAD --' git config --global alias.last 'log -1 HEAD' git config --global alias.visual 'log --oneline --graph --decorate --all' git config --global alias.amend 'commit --amend --no-edit' # Aliases with arguments git config --global alias.show-branches '!git for-each-ref --sort=-committerdate --format="%(committerdate:short) %(refname:short)" refs/heads/' The ! prefix executes the command in the shell, allowing complex operations.
Credential Storage:
Avoid repeatedly entering passwords:
# Cache credentials in memory for 15 minutes (900 seconds) git config --global credential.helper cache git config --global credential.helper 'cache --timeout=3600' # Store credentials permanently (less secure) git config --global credential.helper store # Use OS-specific credential managers # macOS Keychain git config --global credential.helper osxkeychain # Windows Credential Manager git config --global credential.helper manager-core # Linux Secret Service git config --global credential.helper libsecret Push Behavior:
# Only push current branch to its upstream git config --global push.default simple # Push all matching branches git config --global push.default matching # Push current branch to branch of same name git config --global push.default current # Refuse to push if upstream differs git config --global push.default nothing Pull Behavior:
# Use rebase instead of merge when pulling git config --global pull.rebase true # Only allow fast-forward merges when pulling git config --global pull.ff only Performance Settings:
# Enable parallel index preload for faster operations git config --global core.preloadindex true # Enable filesystem monitor for large repositories git config --global core.fsmonitor true # Increase HTTP buffer size for large repositories git config --global http.postBuffer 524288000 Configuration File Format
Git configuration files use INI-style format:
[user] name = Your Name email = your.email@example.com [core] editor = vim autocrlf = input [alias] st = status co = checkout [color] ui = auto [push] default = simple You can edit these files directly with a text editor, though using git config is safer.
Real-World Configuration Example
Here's a comprehensive .gitconfig for professional development:
[user] name = Jane Developer email = jane@example.com signingkey = ABC123DEF456 [core] editor = code --wait autocrlf = input whitespace = trailing-space,space-before-tab pager = delta [init] defaultBranch = main [pull] rebase = true [push] default = simple autoSetupRemote = true [fetch] prune = true [rebase] autoStash = true [merge] conflictstyle = diff3 tool = vscode [diff] colorMoved = zebra tool = vscode [alias] st = status -sb co = checkout br = branch ci = commit unstage = reset HEAD -- last = log -1 HEAD lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit amend = commit --amend --no-edit undo = reset --soft HEAD^ stash-all = stash save --include-untracked [color] ui = auto [credential] helper = osxkeychain 5. Repository Initialization and Cloning {#repository-basics}
git init - Initialize a New Repository
Purpose: Creates a new Git repository, transforming an ordinary directory into a version-controlled project.
Basic Usage:
# Initialize in current directory git init # Initialize in a new directory git init my-project # Initialize with specific initial branch name git init --initial-branch=main my-project # Or shorter: git init -b main my-project What Actually Happens:
When you run git init, Git creates a .git subdirectory containing the entire repository structure:
.git/ ├── HEAD # Points to current branch ├── config # Repository configuration ├── description # Repository description (for GitWeb) ├── hooks/ # Hook scripts ├── info/ # Additional repository information │ └── exclude # Local .gitignore equivalent ├── objects/ # Object database │ ├── info/ │ └── pack/ └── refs/ # References (branches, tags) ├── heads/ # Local branches └── tags/ # Tags Bare Repositories:
A bare repository contains only Git data (no working directory) and is used as a central sharing point:
# Create bare repository git init --bare project.git # Convention: bare repositories end with .git Bare repositories are used for:
- Central servers (like GitHub's storage)
- Shared network repositories
- Backup repositories
When to Use git init:
- Starting a new project from scratch
- Converting existing project to Git
- Creating a local repository for experimentation
- Setting up a central bare repository
Real-World Example:
# Start a new web project mkdir awesome-website cd awesome-website git init -b main # Create initial structure mkdir src css js touch README.md src/index.html css/style.css js/app.js # Make first commit git add . git commit -m "Initial project structure" git clone - Clone a Repository
Purpose: Creates a complete local copy of a remote repository, including all history, branches, and tags.
Basic Usage:
# Clone a repository git clone https://github.com/user/repository.git # Clone into specific directory git clone https://github.com/user/repository.git my-local-name # Clone specific branch git clone -b develop https://github.com/user/repository.git # Shallow clone (limited history) git clone --depth 1 https://github.com/user/repository.git Protocol Options:
Git supports several protocols for cloning:
1. HTTPS:
git clone https://github.com/user/repo.git - Most common and universally supported
- Works through firewalls
- Requires authentication for private repos
- Can cache credentials
2. SSH:
git clone git@github.com:user/repo.git - Requires SSH key setup
- More secure than HTTPS with passwords
- No credential prompting once keys configured
- Preferred for frequent access
3. Git Protocol:
git clone git://github.com/user/repo.git - Fastest protocol
- No authentication (read-only for public repos)
- Often blocked by firewalls
- Rarely used today
4. Local Filesystem:
git clone /path/to/repository git clone file:///path/to/repository - Clone from local directories
- Useful for network shares
- Can hardlink objects for efficiency
What git clone Does:
- Creates a new directory with the repository name
- Initializes a
.gitdirectory inside it - Fetches all repository data from remote
- Checks out the default branch into working directory
- Sets up
originremote pointing to source - Creates remote-tracking branches for all remote branches
Advanced Cloning Options:
Shallow Clones:
For large repositories where you only need recent history:
# Clone only latest commit git clone --depth 1 https://github.com/user/large-repo.git # Clone with last 50 commits git clone --depth 50 https://github.com/user/repo.git # Shallow clone single branch git clone --depth 1 --single-branch --branch main https://github.com/user/repo.git Benefits:
- Faster cloning
- Less disk space
- Faster operations on huge repositories
Limitations:
- Cannot push to shallow clones
- Limited history access
- Cannot clone from shallow clones
Deepen Shallow Clones:
# Fetch more history git fetch --depth=100 # Convert to full clone git fetch --unshallow Partial Clones (Sparse Checkout):
Clone without immediately downloading all files:
# Clone with blob filtering (no file contents initially) git clone --filter=blob:none https://github.com/user/repo.git # Clone with tree filtering (fetch trees on-demand) git clone --filter=tree:0 https://github.com/user/repo.git Files are downloaded on-demand when checked out. Perfect for monorepos where you only work on specific areas.
Mirror Clones:
Create an exact mirror of the repository:
# Mirror clone (includes all refs and history) git clone --mirror https://github.com/user/repo.git # Useful for backups or creating exact replicas Submodule Handling:
# Clone repository and initialize submodules git clone --recursive https://github.com/user/repo.git # Or equivalently: git clone --recurse-submodules https://github.com/user/repo.git Configuration During Clone:
# Set configuration during clone git clone -c http.proxy=http://proxy.example.com:8080 https://github.com/user/repo.git # Set branch name git clone -c init.defaultBranch=main https://github.com/user/repo.git Clone and Checkout Specific Commit:
# Clone then checkout specific commit git clone https://github.com/user/repo.git cd repo git checkout abc123def456 Real-World Scenarios:
Scenario 1: Contributing to Open Source
# Clone the project git clone https://github.com/original/project.git cd project # Add your fork as remote git remote add myfork git@github.com:yourname/project.git # Create feature branch git checkout -b feature/awesome-addition # Work, commit, push to your fork git push -u myfork feature/awesome-addition Scenario 2: Large Repository (e.g., Linux Kernel)
# Shallow clone for quick start git clone --depth 1 --single-branch --branch master https://github.com/torvalds/linux.git # Later, if you need more history cd linux git fetch --unshallow Scenario 3: Backup Creation
# Create exact mirror for backup git clone --mirror https://github.com/company/critical-project.git backup.git # Update backup regularly cd backup.git git remote update 6. The Three Trees: Working Directory, Staging Area, and Repository {#three-trees}
Understanding Git's three-tree architecture is essential for mastering its workflow. These "trees" represent three different states of your project:
The Three Trees Explained
1. Working Directory (Working Tree)
- Your actual project files
- What you see in your file explorer
- Where you make changes
- Can contain untracked, modified, or clean files
2. Staging Area (Index)
- Proposed next commit
- Intermediate storage between working directory and repository
- Allows selective committing
- Stored in
.git/index
3. Repository (HEAD)
- Committed history
- Permanent storage of snapshots
- Immutable object database
- Organized as commits, trees, and blobs
The Git Workflow Cycle
Working Directory → (git add) → Staging Area → (git commit) → Repository ← (git checkout) ← ← (git reset) ← Visual Representation
┌─────────────────────┐ │ Working Directory │ ← Your files as you edit them │ - file1.txt │ │ - file2.txt │ └─────────────────────┘ │ │ git add ▼ ┌─────────────────────┐ │ Staging Area │ ← Files staged for commit │ - file1.txt │ └─────────────────────┘ │ │ git commit ▼ ┌─────────────────────┐ │ Repository (HEAD) │ ← Committed history │ - commit abc123 │ │ - commit def456 │ └─────────────────────┘ 7. Basic Git Workflow Commands {#basic-workflow}
git status - Check Repository Status
Purpose: Shows the state of the working directory and staging area. This is your primary diagnostic command.
Basic Usage:
# Full status git status # Short format git status -s git status --short # Show branch and tracking info git status -sb git status --short --branch Understanding Output:
Full Status Output:
$ git status On branch main Your branch is up to date with 'origin/main'. Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: file1.txt new file: file2.txt Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: file3.txt Untracked files: (use "git add <file>..." to include in what will be committed) file4.txt Section Breakdown:
- Current Branch: Which branch you're on
- Branch Status: Relationship to remote tracking branch
- Changes to be committed: Staged changes (green in color terminals)
- Changes not staged: Modified but not staged (red in color terminals)
- Untracked files: New files Git doesn't know about
Short Status Format:
$ git status -s M file1.txt # Modified and staged M file3.txt # Modified but not staged A file2.txt # Added (new file staged) ?? file4.txt # Untracked MM file5.txt # Modified, staged, then modified again Status Codes:
-
??- Untracked -
A- Added (staged) -
M- Modified -
D- Deleted -
R- Renamed -
C- Copied -
U- Updated but unmerged (conflict)
Advanced Usage:
# Show ignored files git status --ignored # Show untracked files in detail git status -u git status --untracked-files=all # Machine-readable format (for scripts) git status --porcelain # Show status with file stats git status -v git status --verbose Why Use git status Frequently:
Running git status before and after operations helps you:
- Understand current repository state
- Prevent accidental commits
- Verify operations completed successfully
- Catch uncommitted changes before switching branches
- Identify merge conflicts
Best Practice: Run git status constantly. It's fast, safe, and informative.
git add - Stage Changes
Purpose: Adds file contents to the staging area, preparing them for commit. This is how you tell Git which changes to include in the next commit.
Basic Usage:
# Stage specific file git add filename.txt # Stage multiple files git add file1.txt file2.txt file3.txt # Stage all changes in current directory and subdirectories git add . # Stage all changes in repository git add -A git add --all # Stage all modified and deleted files (not new files) git add -u git add --update Understanding the Differences:
| Command | New Files | Modified Files | Deleted Files | Scope |
|---|---|---|---|---|
git add . | ✓ | ✓ | ✓ | Current directory and below |
git add -A | ✓ | ✓ | ✓ | Entire repository |
git add -u | ✗ | ✓ | ✓ | Entire repository |
Interactive Staging:
The most powerful git add feature is interactive mode, allowing surgical precision in staging changes:
# Enter interactive mode git add -i git add --interactive Interactive Mode Menu:
*** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help Patch Mode (Most Useful):
# Stage parts of files interactively git add -p filename.txt git add --patch filename.txt # Patch mode for all files git add -p Patch Mode Commands:
When in patch mode, Git shows each change hunk and asks what to do:
-
y- Stage this hunk -
n- Don't stage this hunk -
q- Quit; don't stage this or remaining hunks -
a- Stage this and all remaining hunks in file -
d- Don't stage this or any remaining hunks in file -
s- Split the hunk into smaller hunks -
e- Manually edit the hunk -
?- Print help
Example Patch Mode Session:
$ git add -p example.py diff --git a/example.py b/example.py index 1234567..abcdefg 100644 --- a/example.py +++ b/example.py @@ -10,6 +10,7 @@ def process_data(data): result = [] for item in data: processed = transform(item) + validated = validate(processed) result.append(processed) return result Stage this hunk [y,n,q,a,d,s,e,?]? y Staging by File Type:
# Stage all Python files git add *.py # Stage all files in directory git add src/ # Stage all .txt files recursively git add '*.txt' Note: Quote wildcards to prevent shell expansion.
Force Adding Ignored Files:
# Add file even if in .gitignore git add -f ignored-file.txt git add --force ignored-file.txt Intent to Add:
# Register untracked file without staging content git add -N newfile.txt git add --intent-to-add newfile.txt This makes the file visible to git diff without staging it.
Dry Run:
# Show what would be added git add --dry-run . git add -n . Verbose Mode:
# Show added files git add -v file.txt git add --verbose file.txt Real-World Scenarios:
Scenario 1: Commit Related Changes Separately
You've fixed a bug and also refactored unrelated code. Commit them separately:
# Stage only bug fix git add src/buggy_module.py git commit -m "Fix: Resolve null pointer exception" # Stage refactoring git add src/refactored_module.py git commit -m "Refactor: Improve code readability" Scenario 2: Stage Part of a File
You've made multiple independent changes in one file. Stage them separately:
git add -p complex_file.py # Stage first logical change: y # Skip unrelated change: n git commit -m "Add validation function" # Now stage the second change git add -p complex_file.py # Stage second logical change: y git commit -m "Add logging" Scenario 3: Stage Everything Except One File
# Stage all changes git add -A # Unstage one file git restore --staged unwanted.txt Common Mistakes:
Mistake 1: Using git add . in wrong directory
# You're in src/ but meant to stage root files pwd # /home/user/project/src git add . # Only stages src/ contents! # Solution: Navigate to repository root or use -A cd .. git add -A Mistake 2: Forgetting to stage after modifications
git add file.txt # Edit file.txt more git commit -m "Update file" # Doesn't include latest edits! # Solution: Always check status before committing git status git add file.txt # Stage latest changes git commit -m "Update file" Mistake 3: Accidentally staging sensitive data
# Accidentally added secrets file git add config/secrets.yml # Solution: Unstage immediately git restore --staged config/secrets.yml # Add to .gitignore echo "config/secrets.yml" >> .gitignore git commit - Record Changes to Repository
Purpose: Creates a snapshot of staged changes in the repository, permanently recording them in project history with metadata (author, timestamp, message).
Basic Usage:
# Commit with inline message git commit -m "Add user authentication feature" # Open editor for commit message git commit # Commit with detailed message git commit -m "Add user authentication" -m "- Implement JWT tokens" -m "- Add login endpoint" -m "- Add tests" Commit Message Best Practices:
A good commit message has structure:
Short summary (50 characters or less) More detailed explanatory text, if necessary. Wrap it to about 72 characters. The blank line separating the summary from the body is critical; tools like git log --oneline will only show the summary. Explain the problem that this commit is solving. Focus on why you are making this change as opposed to how (the code explains that). Are there side effects or other unintuitive consequences of this change? Here's the place to explain them. - Bullet points are okay - Use a hyphen or asterisk for bullets If you use an issue tracker, put references to them at the bottom: Resolves: #123 See also: #456, #789 Conventional Commits Format:
Many teams use structured commit messages:
git commit -m "feat: add user authentication with JWT" git commit -m "fix: resolve null pointer in payment processing" git commit -m "docs: update API documentation" git commit -m "refactor: simplify database query logic" git commit -m "test: add unit tests for auth module" git commit -m "chore: update dependencies" Types:
-
feat: New feature -
fix: Bug fix -
docs: Documentation changes -
style: Code style changes (formatting, semicolons, etc.) -
refactor: Code refactoring -
perf: Performance improvements -
test: Adding or updating tests -
chore: Maintenance tasks -
ci: CI/CD changes -
build: Build system changes
Advanced Commit Options:
Skip Staging Area:
# Stage and commit modified tracked files in one step git commit -a -m "Update all tracked files" git commit -am "Update all tracked files" ⚠️ Warning: This doesn't include untracked files, only modified tracked files.
Amend Previous Commit:
# Modify the last commit git commit --amend # Amend without changing message git commit --amend --no-edit # Amend and change message git commit --amend -m "Corrected commit message" What --amend does:
- Replaces the last commit entirely
- Includes currently staged changes
- Allows message modification
- Changes commit SHA (rewrites history)
⚠️ Critical Warning: Never amend commits that have been pushed to shared branches. This rewrites history and causes problems for collaborators.
Use case for amend:
# Made a commit git commit -m "Add feature X" # Oops, forgot to include a file git add forgotten-file.txt git commit --amend --no-edit # Or fix typo in commit message git commit --amend -m "Add feature X (corrected)" Empty Commits:
# Create commit without changes (useful for triggering CI/CD) git commit --allow-empty -m "Trigger CI pipeline" Commit with Specific Date:
# Set custom commit date git commit --date="2024-01-15 10:30:00" -m "Backdated commit" # Use author date instead of commit date GIT_AUTHOR_DATE="2024-01-15 10:30:00" git commit -m "Message" Commit as Different Author:
# Commit with different author git commit --author="Jane Doe <jane@example.com>" -m "Fix bug" Useful when:
- Applying patches from others
- Committing on behalf of someone
- Maintaining proper attribution
Sign Commits (GPG):
# Sign commit with GPG key git commit -S -m "Signed commit" git commit --gpg-sign -m "Signed commit" # Configure automatic signing git config --global commit.gpgsign true Signed commits prove authenticity and are required by many organizations.
Verbose Mode:
# Show diff in commit message editor git commit -v git commit --verbose This displays the diff below the commit message template, helping you write accurate messages.
Template Messages:
# Use commit message template git config --global commit.template ~/.gitmessage.txt Example template (~/.gitmessage.txt):
# [Type] Short description (max 50 chars) # Detailed description (wrap at 72 chars) # Explain WHY this change is being made # Related issues # Resolves: # # See also: # Partial Commits with Interactive Staging:
# Stage specific changes, then commit git add -p file.txt git commit -m "Add first feature" git add -p file.txt git commit -m "Add second feature" Commit Only Specific Files:
# Commit specific files, bypassing staging area git commit file1.txt file2.txt -m "Update specific files" Reuse Previous Message:
# Reuse message from previous commit git commit -C HEAD -m "Same message as before" # Edit the reused message git commit -c HEAD Real-World Commit Strategies:
Atomic Commits:
Each commit should be a single logical unit:
# Bad: Everything in one commit git add . git commit -m "Various updates" # Good: Separate logical commits git add src/auth.js git commit -m "feat: add JWT authentication" git add src/validation.js git commit -m "feat: add input validation" git add tests/auth.test.js git commit -m "test: add authentication tests" git add README.md git commit -m "docs: update authentication section" Benefits:
- Easier code review
- Simpler to revert specific changes
- Clearer history
- Better bisecting for bugs
Commit Frequency:
# Too infrequent (bad) # Work all day, commit once with "Day's work" # Too frequent (bad) # Commit after every line change # Just right (good) # Commit after each logical, working change git commit -m "feat: add login form" git commit -m "feat: add form validation" git commit -m "feat: connect form to API" git commit -m "test: add login form tests" Work-in-Progress Commits:
For checkpoint commits you'll squash later:
# Quick checkpoint git commit -m "WIP: working on feature X" # More checkpoints git commit -m "WIP: partial implementation" # Before pushing, squash WIP commits git rebase -i HEAD~3 # Mark WIP commits with 'squash' or 'fixup' Commit Verification:
Before committing, verify:
# Check what's staged git diff --staged git diff --cached # Check status git status # Run tests npm test # or your test command # Then commit git commit -m "feat: add feature X" Commit Message Examples by Type:
Feature Addition:
git commit -m "feat: add password reset functionality Implement password reset workflow: - Add /forgot-password endpoint - Send email with reset token - Add token validation - Allow password update with valid token Closes #234" Bug Fix:
git commit -m "fix: resolve race condition in payment processing The payment confirmation was sometimes sent before database commit, causing confusion when users refreshed and saw 'pending' status. Now we ensure database commit completes before sending confirmation. Fixes #567" Refactoring:
git commit -m "refactor: extract user validation into separate module No functional changes. Improves code organization and makes validation logic reusable across auth and profile modules." Documentation:
git commit -m "docs: add API authentication guide Add comprehensive guide covering: - JWT token generation - Token refresh flow - Error handling - Code examples in multiple languages" git diff - Show Changes
Purpose: Shows differences between various Git states: working directory, staging area, commits, branches, and more. Essential for understanding what changed.
Basic Usage:
# Show unstaged changes (working directory vs staging area) git diff # Show staged changes (staging area vs last commit) git diff --staged git diff --cached # Show all changes (working directory vs last commit) git diff HEAD Understanding Diff Output:
$ git diff example.txt diff --git a/example.txt b/example.txt index 1234567..abcdefg 100644 --- a/example.txt +++ b/example.txt @@ -1,5 +1,6 @@ Line 1 Line 2 -Line 3 +Line 3 modified +Line 4 added Line 5 Output breakdown:
-
diff --git a/example.txt b/example.txt: Compared files -
index 1234567..abcdefg: Object hashes -
--- a/example.txt: Original file -
+++ b/example.txt: Modified file -
@@ -1,5 +1,6 @@: Hunk header (old range, new range) - Lines with
-: Removed - Lines with
+: Added - Lines with no prefix: Context (unchanged)
Comparing Commits:
# Compare two commits git diff commit1 commit2 # Compare with specific commit git diff abc123 # Compare with HEAD git diff HEAD git diff HEAD~1 # One commit before HEAD git diff HEAD~5 # Five commits before HEAD # Compare with branch git diff main git diff origin/main Comparing Branches:
# Show differences between branches git diff main..feature-branch # Show what's in feature-branch but not in main git diff main...feature-branch # Compare current branch with main git diff main Difference between .. and ...:
-
git diff main..feature: Changes between the tips of both branches -
git diff main...feature: Changes in feature since it diverged from main (more useful)
Specific File Diff:
# Diff specific file git diff filename.txt # Diff specific file between commits git diff commit1 commit2 filename.txt # Diff specific file on different branch git diff main:file.txt feature:file.txt Diff Statistics:
# Show summary statistics git diff --stat # Show statistics and brief diff git diff --shortstat # Show file name and statistics git diff --numstat # Show summary of changes git diff --summary Example output:
$ git diff --stat file1.txt | 10 +++++----- file2.txt | 5 +++++ file3.txt | 15 --------------- 3 files changed, 15 insertions(+), 20 deletions(-) Word-Level Diff:
# Show word-by-word differences (better for prose) git diff --word-diff # Color word diff git diff --word-diff=color # Show only changed words git diff --word-diff=plain Ignoring Whitespace:
# Ignore whitespace changes git diff -w git diff --ignore-all-space # Ignore whitespace at line end git diff --ignore-space-at-eol # Ignore whitespace amount changes git diff -b git diff --ignore-space-change Context Lines:
# Show 10 lines of context instead of default 3 git diff -U10 git diff --unified=10 # Show entire file git diff --unified=999999 # Show only changes, no context git diff -U0 Color and Highlighting:
# Force color output (useful for piping) git diff --color # Highlight moved code git diff --color-moved # Show whitespace errors git diff --check Diff Tools:
# Use external diff tool git difftool # Use specific tool git difftool --tool=meld # Compare directory git difftool --dir-diff Advanced Diff Options:
Function Context:
# Show function name in hunk headers git diff -p git diff --show-function # For Python/Java/C, shows which function changed Rename Detection:
# Detect renames (default) git diff -M git diff --find-renames # Detect renames with threshold (50% similarity) git diff -M50% # Detect copies as well git diff -C git diff --find-copies Binary Files:
# Show binary files changed git diff --binary # Don't show binary file differences git diff --no-binary Diff Algorithms:
# Use different diff algorithm git diff --minimal # Spend extra time to find smaller diff git diff --patience # Patience algorithm (better for complex changes) git diff --histogram # Histogram algorithm (fast and good) # Configure default algorithm git config --global diff.algorithm histogram Diff Filters:
# Show only added files git diff --diff-filter=A # Show only deleted files git diff --diff-filter=D # Show only modified files git diff --diff-filter=M # Show renamed files git diff --diff-filter=R # Combine filters (added or modified) git diff --diff-filter=AM Real-World Diff Scenarios:
Scenario 1: Pre-Commit Review
# Review what you're about to commit git diff --staged # If using multiple stages, review each step git diff # Unstaged changes git diff --staged # Staged changes git diff HEAD # All changes since last commit Scenario 2: Code Review
# Review all changes in feature branch git diff main...feature-branch # Review specific file changes git diff main...feature-branch -- src/ # Get statistics for pull request git diff main...feature-branch --stat Scenario 3: Finding When Bug Was Introduced
# Compare current broken state with last known good commit git diff known-good-commit current-broken-commit # Focus on specific problematic file git diff known-good-commit current-broken-commit -- src/buggy-file.js Scenario 4: Reviewing Others' Changes
# See what changed in latest pull git fetch git diff HEAD origin/main # Review changes before merging git diff main origin/feature-branch Scenario 5: Checking Whitespace Issues
# Find whitespace errors before committing git diff --check # See exactly what whitespace changed git diff --ws-error-highlight=all Creating Patches:
# Create patch file git diff > changes.patch # Create patch for specific commits git diff commit1 commit2 > feature.patch # Apply patch git apply changes.patch # Apply patch and stage changes git apply --index changes.patch Diff Output Formats:
# Default unified format git diff # Context format (old-style) git diff --context # Raw format (for scripts) git diff --raw # Name only git diff --name-only # Name and status git diff --name-status Performance Optimization:
# For large repositories, limit diff git diff --patience # More accurate but slower git diff --minimal # Spend extra time finding minimal diff git diff -U0 # No context (faster) # Skip binary files git diff --no-binary git restore - Restore Working Tree Files
Purpose: Modern command (Git 2.23+) for discarding changes in working directory or unstaging files. Replaces old git checkout and git reset usage for these purposes.
Basic Usage:
# Discard changes in working directory (restore from staging area or HEAD) git restore filename.txt # Discard all changes git restore . # Unstage file (restore staging area from HEAD) git restore --staged filename.txt # Unstage all files git restore --staged . Understanding git restore:
git restore operates on two main areas:
- Working directory: Discard local modifications
- Staging area: Unstage changes
Restore from Different Sources:
# Restore from HEAD (last commit) git restore --source=HEAD filename.txt # Restore from specific commit git restore --source=abc123 filename.txt # Restore from different branch git restore --source=main filename.txt # Restore from previous commit git restore --source=HEAD~1 filename.txt Restore Both Working Directory and Staging Area:
# Restore file in both working directory and staging area git restore --staged --worktree filename.txt # Short version git restore -SW filename.txt Interactive Restore:
# Interactively choose changes to discard git restore -p filename.txt git restore --patch filename.txt Similar to git add -p, this lets you selectively discard hunks of changes.
Restore Specific Paths:
# Restore entire directory git restore src/ # Restore by pattern git restore '*.txt' # Restore from specific source and path git restore --source=main -- src/ Advanced Options:
# Show what would be restored (dry run) git restore --dry-run filename.txt # Merge restored content (when conflicts) git restore --merge filename.txt # Keep unmerged entries (during conflict resolution) git restore --ours filename.txt git restore --theirs filename.txt Overlay vs No-Overlay Mode:
# Overlay mode (default): only restore specified files git restore --overlay filename.txt # No-overlay: remove files in target that aren't in source git restore --no-overlay --source=main src/ Real-World Scenarios:
Scenario 1: Undo Accidental Changes
# Oops, made unwanted changes to file git status # modified: important-file.txt # Discard changes git restore important-file.txt # Verify git status # nothing to commit, working tree clean Scenario 2: Unstage Accidentally Added File
# Added wrong file git add secrets.txt git status # Changes to be committed: secrets.txt # Unstage it git restore --staged secrets.txt git status # Untracked files: secrets.txt # Add to .gitignore echo "secrets.txt" >> .gitignore Scenario 3: Partially Discard Changes
# File has multiple changes, keep some, discard others git restore -p complex-file.js # Interactively choose: # y - discard this hunk # n - keep this hunk # q - quit # s - split into smaller hunks Scenario 4: Restore File from Different Branch
# Need version of file from main branch git restore --source=main -- config/settings.json # This brings main's version to working directory without switching branches Scenario 5: Completely Reset File
# Staged changes AND working directory changes git restore -SW buggy-file.py # File now matches HEAD exactly Comparison with Old Commands:
Before Git 2.23, you'd use:
# OLD WAY (still works but discouraged) git checkout -- filename.txt # Discard changes git reset HEAD filename.txt # Unstage # NEW WAY (clearer) git restore filename.txt # Discard changes git restore --staged filename.txt # Unstage The new git restore is clearer in intent and safer.
Safety Warnings:
⚠️ Critical: git restore permanently discards changes in working directory. There's no undo (unless you have editor backups or IDE history).
# DANGEROUS: Discards ALL local changes git restore . # SAFER: Check what you're about to discard git diff # See unstaged changes git status # See overall state git stash # Consider stashing instead # Then decide git restore filename.txt # Discard specific file git rm - Remove Files
Purpose: Remove files from working directory AND stage the removal for commit. Combines file system deletion with Git staging.
Basic Usage:
# Remove file from working directory and stage deletion git rm filename.txt # Remove multiple files git rm file1.txt file2.txt file3.txt # Remove all .log files git rm *.log # Remove directory recursively git rm -r directory/ What git rm Does:
- Deletes file from working directory
- Stages the deletion (ready for commit)
- After commit, file is removed from repository history going forward
Remove from Git Only (Keep File Locally):
# Stop tracking file but keep in working directory git rm --cached filename.txt # Remove from Git, keep locally (common for .env files) git rm --cached .env echo ".env" >> .gitignore git commit -m "Stop tracking .env file" This is crucial when you accidentally committed sensitive files.
Force Removal:
# Force remove (even if file has uncommitted changes) git rm -f filename.txt git rm --force filename.txt ⚠️ Warning: This discards uncommitted changes. Use cautiously.
Dry Run:
# See what would be removed git rm --dry-run -r directory/ git rm -n *.txt Remove Files Matching Pattern:
# Remove all .txt files in directory git rm directory/*.txt # Remove all .log files recursively git rm -r '*.log' Note: Quote wildcards to prevent shell expansion.
Handling Modified Files:
If file has uncommitted changes:
$ git rm modified-file.txt error: the following file has changes staged in the index: modified-file.txt # Options: # 1. Commit changes first git commit -am "Save changes before removing" git rm modified-file.txt # 2. Force remove (loses changes) git rm -f modified-file.txt # 3. Unstage and remove git restore --staged modified-file.txt git rm modified-file.txt Real-World Scenarios:
Scenario 1: Remove Accidentally Committed Secrets
# OH NO! Committed API keys git rm --cached config/secrets.yml echo "config/secrets.yml" >> .gitignore git commit -m "Remove secrets file from tracking" # Note: This doesn't remove from history! # File still exists in previous commits # For complete removal, need git filter-branch or BFG Repo-Cleaner Scenario 2: Cleanup Obsolete Files
# Remove entire deprecated module git rm -r src/old-module/ git commit -m "Remove deprecated authentication module" Scenario 3: Convert to Ignored Files
# Stop tracking node_modules but keep locally git rm -r --cached node_modules/ echo "node_modules/" >> .gitignore git commit -m "Stop tracking node_modules" Scenario 4: Mass File Removal
# Remove all temporary files git rm '*.tmp' '*.cache' '*.swp' git commit -m "Remove temporary files" Comparison with Regular File Deletion:
Method 1: Regular deletion (BAD for Git)
rm filename.txt # Delete file git add filename.txt # Stage deletion git commit -m "Remove file" Method 2: Git rm (GOOD)
git rm filename.txt # Delete AND stage in one step git commit -m "Remove file" git rm is cleaner and prevents forgotten staging.
Method 3: Delete then commit -a
rm filename.txt git commit -am "Remove file" # Works but less explicit Recovery from git rm:
If you haven't committed yet:
# Undo git rm before commit git restore --staged filename.txt # Unstage git restore filename.txt # Restore file # Or in one step (old command) git checkout HEAD filename.txt If you've committed:
# Restore file from previous commit git restore --source=HEAD~1 filename.txt # Or checkout from history git checkout HEAD~1 -- filename.txt git mv - Move or Rename Files
Purpose: Move or rename files while maintaining Git history tracking. Git automatically detects renames, but git mv makes it explicit and efficient.
Basic Usage:
# Rename file git mv oldname.txt newname.txt # Move file to directory git mv file.txt directory/ # Move and rename git mv old-name.txt new-directory/new-name.txt # Move directory git mv old-directory/ new-directory/ What git mv Does:
- Renames/moves file in working directory
- Stages the change (ready for commit)
- Git tracks it as a rename (not delete + add)
Internally, git mv is equivalent to:
mv oldname.txt newname.txt git rm oldname.txt git add newname.txt But git mv is cleaner and more explicit.
Git's Rename Detection:
Git doesn't actually store renames—it detects them by content similarity:
# These are equivalent: git mv file.txt renamed.txt # Same as: mv file.txt renamed.txt git add renamed.txt git rm file.txt Git sees the content is similar and marks it as a rename.
Checking Rename Detection:
# Status shows rename $ git mv readme.txt README.md $ git status Changes to be committed: renamed: readme.txt -> README.md # In log git log --follow README.md # Shows history through renames Force Move/Rename:
# Overwrite existing file (dangerous!) git mv -f source.txt destination.txt git mv --force source.txt destination.txt Dry Run:
# See what would happen git mv -n oldname.txt newname.txt git mv --dry-run oldname.txt newname.txt Handling Case-Sensitive Renames:
On case-insensitive filesystems (Windows, macOS), renaming file.txt to File.txt is tricky:
# This might not work on case-insensitive systems: git mv file.txt File.txt # Solution: use intermediate name git mv file.txt temp.txt git mv temp.txt File.txt git commit -m "Fix file name casing" Real-World Scenarios:
Scenario 1: Reorganize Project Structure
# Move related files into subdirectory mkdir utils git mv helper1.py utils/ git mv helper2.py utils/ git mv validator.py utils/ git commit -m "Organize utility functions into utils/ directory" Scenario 2: Rename for Conventions
# Rename to follow naming convention git mv myComponent.js MyComponent.js # React component git mv database_helper.py db_helper.py # Python convention git commit -m "Rename files to follow project conventions" Scenario 3: Mass Renaming
# Rename multiple files (using shell script) for file in *.html; do git mv "$file" "${file%.html}.htm" done git commit -m "Change file extension from .html to .htm" Scenario 4: Rename with History Preservation
# Rename file git mv old-implementation.js new-implementation.js git commit -m "Rename old-implementation to new-implementation" # View history following renames git log --follow new-implementation.js # Shows full history including when it was "old-implementation.js" Rename Detection Threshold:
Git detects renames based on content similarity:
# Configure similarity threshold (default 50%) git config diff.renameLimit 1000 git config diff.renames copies # Also detect copies # View renames in log with similarity percentage git log --stat -M # Show renames git log --stat -C # Show renames and copies Advanced Rename Options:
# Detect renames even if only 30% similar git log --find-renames=30% # Show exact rename percentage git log --stat -M --summary When Manual Rename Detection Fails:
If Git doesn't detect a rename automatically:
# After manual move mv oldname.txt newname.txt # Git shows as delete + add $ git status deleted: oldname.txt untracked: newname.txt # Fix it git rm oldname.txt git add newname.txt # Commit with both staged git commit -m "Rename oldname to newname" # Git will detect rename if content is similar enough 8. Branching and Branch Management {#branching}
Branches are one of Git's most powerful features, allowing parallel lines of development without affecting the main codebase. Understanding branching is essential for effective Git use.
What Is a Branch?
A branch is simply a movable pointer to a commit. Git's default branch is typically main or master. When you create a branch, you're creating a new pointer—nothing more. This makes branching incredibly lightweight and fast.
Branch Representation:
main → commit C feature → commit E A ← B ← C (main) ↖ D ← E (feature) git branch - List, Create, and Delete Branches
Purpose: Manage branches—list existing branches, create new ones, delete old ones, rename branches.
Basic Usage:
# List local branches git branch # List all branches (including remote) git branch -a git branch --all # List remote branches only git branch -r git branch --remotes # Create new branch (doesn't switch to it) git branch feature-x # Create branch from specific commit git branch feature-x abc123 # Create branch from another branch git branch feature-x develop Branch Listing with Details:
# Show last commit on each branch git branch -v git branch --verbose # Show tracking information git branch -vv # Show merged branches git branch --merged # Show unmerged branches git branch --no-merged Example output:
$ git branch -vv * main abc123 [origin/main] Latest commit message develop def456 [origin/develop: ahead 2] Work in progress feature ghi789 Feature implementation -
*indicates current branch -
[origin/main]shows tracking branch -
ahead 2means 2 commits not pushed yet
Deleting Branches:
# Delete branch (safe: prevents deleting unmerged branches) git branch -d feature-x git branch --delete feature-x # Force delete (even if unmerged) git branch -D feature-x git branch --delete --force feature-x # Delete remote branch git push origin --delete feature-x git push origin :feature-x # Old syntax Renaming Branches:
# Rename current branch git branch -m new-name git branch --move new-name # Rename different branch git branch -m old-name new-name # Force rename (overwrite existing) git branch -M old-name new-name Renaming and Updating Remote:
# Rename local branch git branch -m old-name new-name # Delete old remote branch git push origin --delete old-name # Push new branch and set upstream git push origin -u new-name Creating Branch and Switching:
While git branch creates branches, it doesn't switch to them:
# Create and switch (two commands) git branch feature-x git checkout feature-x # Better: create and switch in one command git checkout -b feature-x # Modern way (Git 2.23+) git switch -c feature-x Advanced Branch Options:
Copy Branches:
# Copy branch to new name git branch -c existing-branch new-branch git branch --copy existing-branch new-branch # Force copy (overwrite if exists) git branch -C existing-branch new-branch Set Upstream Tracking:
# Set upstream for existing branch git branch -u origin/feature-x git branch --set-upstream-to=origin/feature-x # Remove upstream tracking git branch --unset-upstream Branch with Specific Start Point:
# Create branch from tag git branch hotfix v1.2.3 # Create branch from remote branch git branch local-feature origin/remote-feature # Create branch from specific commit git branch bugfix abc123def List Branches by Pattern:
# List branches matching pattern git branch --list 'feature/*' git branch --list '*fix*' # Case-insensitive matching git branch -i --list 'FEATURE/*' Branch Sorting:
# Sort by commit date git branch --sort=-committerdate # Sort by author date git branch --sort=-authordate # Sort alphabetically (default) git branch --sort=refname Branch Coloring:
# Always use colors git branch --color=always # Never use colors git branch --color=never # Auto (default) git branch --color=auto Real-World Branch Strategies:
Feature Branch Workflow:
# Create feature branch from main git checkout main git pull git checkout -b feature/user-authentication # Work on feature git add . git commit -m "Implement login endpoint" # Push to remote git push -u origin feature/user-authentication # After review and merge, delete git checkout main git branch -d feature/user-authentication git push origin --delete feature/user-authentication Release Branch Workflow:
# Create release branch from develop git checkout develop git checkout -b release/v2.0 # Finalize release (bug fixes only) git commit -m "Bump version to 2.0" # Merge to main git checkout main git merge release/v2.0 git tag v2.0 # Merge back to develop git checkout develop git merge release/v2.0 # Delete release branch git branch -d release/v2.0 Hotfix Branch Workflow:
# Critical bug in production! git checkout main git checkout -b hotfix/critical-security-fix # Fix and test git commit -m "Fix SQL injection vulnerability" # Merge to main git checkout main git merge hotfix/critical-security-fix git tag v1.2.1 # Merge to develop git checkout develop git merge hotfix/critical-security-fix # Delete hotfix branch git branch -d hotfix/critical-security-fix git checkout - Switch Branches and Restore Files
Purpose: Historically did two things: switch branches and restore files. In Git 2.23+, split into git switch (branches) and git restore (files) for clarity, but git checkout still works.
Branch Switching:
# Switch to existing branch git checkout existing-branch # Create and switch to new branch git checkout -b new-branch # Create branch from specific commit and switch git checkout -b hotfix abc123 # Switch to previous branch git checkout - # Switch to remote branch (creates tracking branch) git checkout remote-branch What Happens During Checkout:
- Updates HEAD to point to the branch
- Updates staging area to match branch's commit
- Updates working directory to match branch's commit
- Fails if you have uncommitted changes that conflict
Handling Uncommitted Changes:
# If you have uncommitted changes $ git checkout other-branch error: Your local changes would be overwritten by checkout # Options: # 1. Commit changes git commit -am "WIP" # 2. Stash changes git stash git checkout other-branch git stash pop # 3. Force checkout (DANGEROUS - loses changes) git checkout -f other-branch Detached HEAD State:
Checking out a commit (not a branch) creates "detached HEAD":
# Checkout specific commit git checkout abc123 # You're now in detached HEAD state $ git status HEAD detached at abc123 # Any commits made here are orphaned unless you create a branch git checkout -b save-my-work Use Cases for Detached HEAD:
- Inspecting historical code
- Testing specific versions
- Building release artifacts
- Bisecting to find bugs
Checkout with Path:
# Restore file from HEAD git checkout -- filename.txt # Restore file from specific commit git checkout abc123 -- filename.txt # Restore file from different branch git checkout main -- config.yml # Restore entire directory git checkout main -- src/ Advanced Checkout Options:
Merge During Checkout:
# Attempt three-way merge when switching branches git checkout -m branch-name # If conflicts, resolves what it can and marks conflicts Orphan Branches:
# Create branch with no history (useful for gh-pages) git checkout --orphan gh-pages git rm -rf . # Add new content git add . git commit -m "Initial GitHub Pages commit" Track Remote Branches:
# Create tracking branch git checkout -t origin/feature-x git checkout --track origin/feature-x # Create with different name git checkout -b local-name origin/remote-name Checkout and Reset:
# Checkout and discard all local changes git checkout -f branch-name git checkout --force branch-name git switch - Switch Branches (Modern)
Purpose: Modern command (Git 2.23+) specifically for switching branches, clearer than git checkout.
Basic Usage:
# Switch to existing branch git switch branch-name # Create and switch to new branch git switch -c new-branch git switch --create new-branch # Switch to previous branch git switch - # Force switch (discard local changes) git switch -f branch-name git switch --force branch-name # Discard local changes with confirmation git switch --discard-changes branch-name Create Branch from Commit:
# Create branch from specific commit and switch git switch -c bugfix abc123 # Create from remote branch git switch -c local-feature origin/remote-feature Guess and Create Tracking Branch:
# If "feature" doesn't exist locally but "origin/feature" does git switch feature # Automatically creates local tracking branch Detached HEAD:
# Switch to specific commit (detached HEAD) git switch --detach abc123 # Create branch from detached HEAD git switch -c save-detached-work Advantages of git switch over git checkout:
- Clarity: Only switches branches, doesn't restore files
- Safety: Won't accidentally overwrite files
- Simplicity: Fewer options, easier to understand
- Modern: Part of Git's effort to improve user experience
Comparison:
# OLD WAY (still works) git checkout branch-name git checkout -b new-branch # NEW WAY (clearer) git switch branch-name git switch -c new-branch Branch Management Best Practices
Branch Naming Conventions:
# Feature branches feature/user-authentication feature/payment-integration feat/shopping-cart # Bug fix branches bugfix/login-error fix/memory-leak hotfix/security-vulnerability # Release branches release/v1.2.0 release/2024-q1 # Experimental branches experiment/new-architecture poc/graphql-migration Benefits of conventions:
- Easy to identify branch purpose
- Helps with filtering and organization
- Supports automated workflows
- Improves team communication
Branch Lifecycle:
# 1. Create from up-to-date main git checkout main git pull git checkout -b feature/new-feature # 2. Regular commits git commit -m "Progress on feature" # 3. Keep updated with main git checkout main git pull git checkout feature/new-feature git merge main # or rebase # 4. Push to remote git push -u origin feature/new-feature # 5. Create pull request (on GitHub/GitLab/etc.) # 6. After merge, clean up git checkout main git pull git branch -d feature/new-feature git push origin --delete feature/new-feature Cleaning Up Old Branches:
# List merged branches git branch --merged main # Delete all merged branches (except main) git branch --merged main | grep -v "\* main" | xargs -n 1 git branch -d # Delete remote-tracking branches that no longer exist git fetch --prune git fetch -p # Remove stale remote-tracking references git remote prune origin Branch Protection:
On remote repositories (GitHub, GitLab), protect important branches:
- Require pull request reviews
- Require status checks to pass
- Require signed commits
- Restrict who can push
- Prevent force pushes
- Prevent deletion
Long-Lived vs Short-Lived Branches:
Long-lived branches:
-
main/master: Production code -
develop: Integration branch -
staging: Pre-production testing
Short-lived branches:
- Feature branches: Deleted after merge
- Bug fix branches: Deleted after merge
- Hotfix branches: Deleted after merge
Recommended: Keep short-lived branches actually short (days to weeks, not months).
9. Merging Strategies and Conflict Resolution {#merging}
Merging combines independent lines of development. Understanding merge strategies is crucial for maintaining clean history and resolving conflicts.
git merge - Join Development Histories
Purpose: Integrate changes from one branch into another, creating a merge commit if necessary.
Basic Usage:
# Merge branch into current branch git merge feature-branch # Merge with commit message git merge feature-branch -m "Merge feature X implementation" # Merge without committing (stage changes) git merge --no-commit feature-branch # Abort merge git merge --abort How Merging Works:
Git finds the common ancestor (merge base) of the two branches and performs a three-way merge:
C---D (feature) / A---B---E---F (main) After merge: C---D / \ A---B---E---F---G (main, merge commit G) Types of Merges:
1. Fast-Forward Merge:
When the target branch hasn't diverged, Git simply moves the pointer forward:
# Before: A---B---C (main) \ D---E (feature) # After fast-forward merge: A---B---C---D---E (main, feature) # Fast-forward merge happens automatically git checkout main git merge feature # Fast-forward # Force merge commit even for fast-forward git merge --no-ff feature 2. Three-Way Merge:
When branches have diverged, Git creates a merge commit:
# Before: C---D (feature) / A---B---E (main) # After three-way merge: C---D / \ A---B---E---F (main, merge commit) 3. Squash Merge:
Combines all commits from feature branch into one commit on target:
git merge --squash feature-branch git commit -m "Implement feature X (squashed)" This creates clean history but loses individual commit information.
Merge Strategies:
Git supports different merge strategies for complex scenarios:
Recursive (Default):
# Explicit recursive strategy git merge -s recursive feature-branch git merge --strategy=recursive feature-branch # With strategy options git merge -s recursive -X ours feature-branch # Prefer our changes git merge -s recursive -X theirs feature-branch # Prefer their changes Ours (Take Our Version):
# Use our version entirely, discard theirs git merge -s ours obsolete-feature Use case: Record that a branch was merged, but don't actually include its changes.
Octopus (Multiple Branches):
# Merge multiple branches at once git merge feature1 feature2 feature3 Used for merging multiple branches simultaneously (rarely needed manually).
Subtree:
# Merge as subdirectory git merge -s subtree sub-project-branch Useful when one project is part of another.
Merge Strategy Options:
# Ignore whitespace changes git merge -Xignore-space-change feature # Ignore all whitespace git merge -Xignore-all-space feature # Be more patient (better conflict resolution) git merge -Xpatience feature # Use diff3 conflict style (shows common ancestor) git merge -Xdiff3 feature # Rename threshold (for rename detection) git merge -Xrename-threshold=50% feature Merge Verification:
# Verify merge would succeed before doing it git merge --no-commit --no-ff feature git diff --staged # Review changes git merge --abort # Back out if not happy # Or just check if branches can merge git merge-base main feature # Shows common ancestor Fast-Forward Control:
# Only allow fast-forward merges git merge --ff-only feature # Fails if fast-forward not possible # Always create merge commit (no fast-forward) git merge --no-ff feature # Allow fast-forward (default) git merge --ff feature When to use --no-ff:
- Preserve feature branch history
- Make it clear when feature was integrated
- Easier to revert entire feature later
Merge with Specific File Handling:
# Use theirs version for specific file git checkout --theirs conflicted-file.txt git add conflicted-file.txt # Use ours version git checkout --ours conflicted-file.txt git add conflicted-file.txt Conflict Resolution
What Is a Merge Conflict?
Occurs when Git can't automatically reconcile differences between commits. Requires manual intervention.
Conflict Markers:
def calculate_total(items): <<<<<<< HEAD (Current Change) total = sum(item.price for item in items) return total * 1.1 # Add 10% tax ======= total = sum(item.cost for item in items) return total * 1.15 # Add 15% tax >>>>>>> feature-branch (Incoming Change) Marker Explanation:
-
<<<<<<< HEAD: Start of your current branch's version -
=======: Separator -
>>>>>>> feature-branch: End of incoming branch's version
Step-by-Step Conflict Resolution:
1. Identify conflicts:
$ git merge feature-branch Auto-merging src/calculator.py CONFLICT (content): Merge conflict in src/calculator.py Automatic merge failed; fix conflicts and then commit the result. # Check status $ git status Unmerged paths: (use "git add <file>..." to mark resolution) both modified: src/calculator.py 2. Open conflicted file:
# Use your editor vim src/calculator.py code src/calculator.py nano src/calculator.py 3. Resolve conflict:
Choose which version to keep, or combine both:
# Option 1: Keep ours def calculate_total(items): total = sum(item.price for item in items) return total * 1.1 # Option 2: Keep theirs def calculate_total(items): total = sum(item.cost for item in items) return total * 1.15 # Option 3: Combine (often best) def calculate_total(items): # Use 'price' attribute (from HEAD) total = sum(item.price for item in items) # Use 15% tax rate (from feature-branch) return total * 1.15 Remove conflict markers completely.
4. Mark as resolved:
# Stage resolved file git add src/calculator.py # Check status git status # All conflicts fixed: run "git commit" 5. Complete merge:
# Commit merge git commit # Opens editor with merge commit message # Or provide message directly git commit -m "Merge feature-branch, resolve calculation conflicts" Tools for Conflict Resolution:
Using Diff Tools:
# Launch configured merge tool git mergetool # Use specific tool git mergetool --tool=meld git mergetool --tool=kdiff3 git mergetool --tool=vimdiff Popular merge tools:
- Meld: Visual diff and merge (Linux, macOS, Windows)
- KDiff3: Three-way merge tool
- P4Merge: Perforce's merge tool (free)
- Beyond Compare: Commercial but powerful
- VS Code: Built-in merge conflict UI
Configure Merge Tool:
# Set default merge tool git config --global merge.tool meld # Don't save backup files (.orig) git config --global mergetool.keepBackup false # Don't prompt before launching git config --global mergetool.prompt false Using Built-in Merge Conflict Resolution:
# Accept all "ours" changes git checkout --ours . # Accept all "theirs" changes git checkout --theirs . # For specific files git checkout --ours path/to/file git checkout --theirs path/to/file ⚠️ Warning: These commands automatically resolve ALL conflicts in favor of one side. Use cautiously.
Conflict Resolution Strategies:
Strategy 1: Diff3 Style (Recommended)
Shows common ancestor for better context:
# Configure diff3 style git config --global merge.conflictstyle diff3 # Conflict now shows: <<<<<<< HEAD current version ||||||| merged common ancestor original version ======= incoming version >>>>>>> branch The middle section shows the original (common ancestor), helping understand what each side changed.
Strategy 2: Always Use Specific Strategy:
# Configure default merge strategy git config --global pull.rebase false # merge (default) git config --global pull.rebase true # rebase # Always use ours for specific file types git config --global merge.ours.driver true # Then in .gitattributes: # *.config merge=ours Advanced Conflict Handling:
Restart Merge:
# Made a mistake? Abort and restart git merge --abort # Or reset to before merge git reset --hard HEAD Continue After Resolving:
# If merge was stopped (e.g., by pre-commit hook) git merge --continue Skip Specific Commits:
# During rebase, skip commit causing conflicts git rebase --skip # Or cherry-pick git cherry-pick --skip View Conflicts:
# List conflicted files git diff --name-only --diff-filter=U # Show conflicts for specific file git diff file.txt # Show changes from both sides git diff HEAD...MERGE_HEAD Real-World Conflict Scenarios:
Scenario 1: Simple Conflict Resolution
# Merge causes conflict $ git merge feature CONFLICT (content): Merge conflict in app.js # Edit app.js, resolve conflicts, remove markers # Stage resolved file git add app.js # Complete merge git commit Scenario 2: Multiple File Conflicts
# Multiple conflicts $ git merge feature CONFLICT in file1.js CONFLICT in file2.js CONFLICT in file3.js # Resolve each file vim file1.js # resolve git add file1.js vim file2.js # resolve git add file2.js vim file3.js # resolve git add file3.js # Commit when all resolved git commit Scenario 3: Using Merge Tool
# Launch merge tool for all conflicts git mergetool # Resolve in GUI # Tool stages files automatically # Clean up backup files rm *.orig # Commit git commit Scenario 4: Accept One Side Entirely
# Feature branch is definitely correct git checkout --theirs . git add . git commit -m "Merge feature, accepting all their changes" # Or main is definitely correct git checkout --ours . git add . git commit -m "Merge feature, keeping our changes" Scenario 5: Complex Three-Way Conflict
# Use diff3 to see ancestor git config merge.conflictstyle diff3 git merge feature # In file: <<<<<<< HEAD new implementation ||||||| merged common ancestor old implementation ======= alternative new implementation >>>>>>> feature # Now you understand: # - Old had X # - We changed X to Y # - They changed X to Z # - Need to combine Y and Z approaches Preventing Conflicts:
1. Frequent Integration:
# Regularly merge main into feature branch git checkout feature git merge main 2. Small, Focused Changes:
- Keep feature branches small
- Merge frequently
- Avoid long-lived branches
3. Communication:
- Coordinate with team on overlapping work
- Use feature flags for incomplete features
- Split work to minimize conflicts
4. Code Reviews:
- Review before merging
- Discuss potential conflicts
- Plan integration strategy
10. Rebasing: Rewriting History {#rebasing}
Rebasing is a powerful but potentially dangerous operation that rewrites commit history. It's an alternative to merging that creates a linear history.
git rebase - Reapply Commits on Top of Another Base
Purpose: Move or combine a sequence of commits to a new base commit, creating a linear history.
Basic Usage:
# Rebase current branch onto main git rebase main # Rebase feature branch onto main (while on feature) git checkout feature git rebase main # Rebase specific branch onto another git rebase main feature-branch # Interactive rebase git rebase -i HEAD~5 git rebase --interactive HEAD~5 How Rebase Works:
Before rebase: C---D (feature) / A---B---E---F (main) After git checkout feature; git rebase main: C'---D' (feature) / A---B---E---F (main) Git:
- Finds common ancestor (B)
- Saves commits C and D as patches
- Resets feature to main
- Applies patches C and D on top
Note: C' and D' are new commits with different SHAs (history rewritten).
Rebase vs Merge:
Merge:
- Preserves complete history
- Shows when branches were integrated
- Creates merge commits
- Safe for public branches
Rebase:
- Creates linear history
- Cleaner, easier to follow
- No merge commits
- Dangerous for public branches
The Golden Rule of Rebasing:
🚨 NEVER rebase commits that have been pushed to a shared/public branch 🚨
Why? Rebasing rewrites history. If others have based work on your original commits, rebasing causes massive conflicts and confusion.
Safe rebasing:
# Safe: Rebasing local, unpushed commits git checkout feature git rebase main Dangerous rebasing:
# DANGEROUS: Rebasing shared branch git checkout main # Public branch! git rebase some-branch # DON'T DO THIS Interactive Rebase
Interactive rebase (-i) is incredibly powerful for cleaning up commit history before sharing.
Basic Interactive Rebase:
# Rebase last 5 commits interactively git rebase -i HEAD~5 # Rebase all commits since main git rebase -i main # Rebase from specific commit git rebase -i abc123 Interactive Rebase Editor:
pick abc123 Add user authentication pick def456 Fix typo in authentication pick ghi789 Add password validation pick jkl012 Update documentation pick mno345 Fix bug in validation # Rebase abc123..mno345 onto previous commit # # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, meld into previous commit # f, fixup <commit> = like squash, discard commit message # x, exec <command> = run command # b, break = stop here (continue with 'git rebase --continue') # d, drop <commit> = remove commit # l, label <label> = label current HEAD # t, reset <label> = reset HEAD to label # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] Interactive Rebase Commands:
1. pick (p): Use commit as-is
pick abc123 Add feature 2. reword (r): Change commit message
reword def456 Fix typo # Will prompt for new message 3. edit (e): Stop at commit to make changes
edit ghi789 Add validation # Git stops here, you can: # - Amend commit: git commit --amend # - Split commit: git reset HEAD^, then make multiple commits # - Continue: git rebase --continue 4. squash (s): Combine with previous commit, edit message
pick abc123 Add feature squash def456 Add tests for feature # Git combines both, lets you edit combined message 5. fixup (f): Combine with previous, discard this message
pick abc123 Add feature fixup def456 Fix typo # Git combines, keeps only first message 6. drop (d): Remove commit entirely
drop ghi789 Experimental code 7. exec (x): Run shell command
pick abc123 Add feature exec npm test pick def456 Add another feature exec npm test # Runs tests after each commit Real-World Interactive Rebase Examples:
Example 1: Clean Up Messy History
# You have: abc123 WIP: start feature def456 WIP: continue ghi789 WIP: almost done jkl012 Feature complete mno345 Fix typo pqr678 Actually fix typo # Clean it up: git rebase -i HEAD~6 # Change to: pick abc123 WIP: start feature fixup def456 WIP: continue fixup ghi789 WIP: almost done reword jkl012 Feature complete fixup mno345 Fix typo fixup pqr678 Actually fix typo # Result: Two clean commits Example 2: Reorder Commits
# You have: abc123 Add feature A def456 Add feature B ghi789 Fix bug in feature A jkl012 Add tests for feature B # Reorder logically: git rebase -i HEAD~4 # Change to: pick abc123 Add feature A pick ghi789 Fix bug in feature A pick def456 Add feature B pick jkl012 Add tests for feature B Example 3: Split a Commit
# One commit does too much git rebase -i HEAD~3 # Mark commit with 'edit': edit abc123 Add many features pick def456 Other commit # When Git stops: git reset HEAD^ # Now make separate commits: git add feature1.js git commit -m "Add feature 1" git add feature2.js git commit -m "Add feature 2" # Continue rebase: git rebase --continue Example 4: Remove Sensitive Data
# Oops, committed secrets git rebase -i HEAD~10 # Find commit with secrets: drop ghi789 Add configuration (contains secrets!) # Or edit to remove just the sensitive file: edit ghi789 Add configuration git rm --cached secrets.yml git commit --amend git rebase --continue ⚠️ Note: This only removes from future history. Old commits still contain secrets. For complete removal, use git filter-branch or BFG Repo-Cleaner.
Handling Rebase Conflicts
Conflicts during rebase are common:
$ git rebase main ... CONFLICT (content): Merge conflict in file.txt error: could not apply abc123... Commit message hint: Resolve all conflicts manually, mark them as resolved with hint: "git add/rm <conflicted_files>", then run "git rebase --continue". Resolution Steps:
# 1. Fix conflicts in files vim file.txt # Resolve conflicts # 2. Stage resolved files git add file.txt # 3. Continue rebase git rebase --continue # If you want to skip this commit: git rebase --skip # If you want to abort entirely: git rebase --abort Common Rebase Scenarios:
Scenario 1: Update Feature Branch
# Keep feature branch up-to-date with main git checkout feature git rebase main # If conflicts, resolve and continue git add resolved-file.txt git rebase --continue # Force push (since history changed) git push --force-with-lease origin feature Scenario 2: Squash All Commits Before Merging
# Squash all feature commits into one git checkout feature git rebase -i main # Mark all except first as 'squash' or 'fixup' pick abc123 First commit squash def456 Second commit squash ghi789 Third commit # Edit combined message, then: git push --force-with-lease Scenario 3: Clean History Before Pull Request
# Fix commit messages, combine WIP commits git rebase -i HEAD~10 # Reword unclear messages # Fixup WIP commits # Reorder for logical flow # Force push git push --force-with-lease Advanced Rebase Options
Preserve Merges:
# Preserve merge commits during rebase git rebase --preserve-merges main git rebase -p main # Note: --preserve-merges is deprecated, use --rebase-merges git rebase --rebase-merges main Rebase Onto:
# Rebase onto different branch than fork point git rebase --onto new-base old-base branch # Example: Move feature from main to develop # Before: # main: A---B---C # feature: D---E # develop: A---B---F---G # # After git rebase --onto develop main feature: # main: A---B---C # develop: A---B---F---G # feature: D'---E' Autosquash:
# Create fixup commits git commit --fixup=abc123 git commit --squash=def456 # Rebase with autosquash git rebase -i --autosquash main # Configure as default git config --global rebase.autosquash true This automatically organizes fixup/squash commits in interactive rebase.
Autostash:
# Automatically stash and reapply during rebase git rebase --autostash main # Configure as default git config --global rebase.autostash true Force Push Safely:
# After rebase, force push required (history changed) git push --force-with-lease # This fails if someone else pushed to branch # Safer than --force which overwrites unconditionally When to Use Rebase
Good Use Cases:
- Update feature branch with latest main:
git checkout feature git rebase main - Clean up local commits before pushing:
git rebase -i HEAD~5 - Maintain linear history:
# Instead of merging main into feature repeatedly git rebase main # Creates linear history - Fix recent commits:
# Fix last commit git commit --amend # Fix earlier commits git rebase -i HEAD~3 Bad Use Cases:
- Public/shared branches:
# NEVER do this: git checkout main git rebase feature # Rewrites public history! - Commits already pushed and used by others:
# DANGEROUS: git rebase main # If others branched from your commits git push --force - When merge commits provide valuable context:
# Sometimes merge commits are informative git merge feature # Shows when feature was integrated Rebase Best Practices
1. Only Rebase Local Commits:
# Safe: Rebase commits not yet pushed git rebase main # Check if commits are pushed: git log origin/feature..feature # Shows unpushed commits 2. Use --force-with-lease Instead of --force:
# Safer force push git push --force-with-lease origin feature # This fails if remote has commits you don't have locally # Prevents accidentally overwriting others' work 3. Communicate with Team:
If you must rebase shared branches:
- Warn team members
- Coordinate timing
- Provide instructions for updating their local copies
4. Test After Rebase:
# After rebase, verify everything works git rebase main npm test # or your test command git push --force-with-lease 5. Keep Rebase Sessions Short:
# Don't rebase too many commits at once git rebase -i HEAD~5 # Manageable # Instead of: git rebase -i HEAD~50 # Too many, error-prone 6. Use Descriptive Messages During Interactive Rebase:
When rewording commits, make messages clear:
# Bad: "Fix stuff" # Good: "Fix null pointer exception in payment processing" Recovery from Rebase Disasters
If Rebase Goes Wrong:
1. Abort Ongoing Rebase:
# During rebase, if things are messy git rebase --abort # Returns to state before rebase started 2. Use Reflog to Recover:
The reflog tracks all HEAD movements, even rebase operations:
# View reflog git reflog # Output: abc123 HEAD@{0}: rebase: Add feature def456 HEAD@{1}: rebase: Fix bug ghi789 HEAD@{2}: rebase: Start jkl012 HEAD@{3}: commit: Original state (before rebase) # Reset to before rebase git reset --hard HEAD@{3} # or git reset --hard jkl012 3. Recover Specific Commits:
# Find lost commit in reflog git reflog | grep "lost commit message" # Cherry-pick it back git cherry-pick abc123 4. If Force Push Caused Problems:
# If you force pushed and broke things for others: # Option 1: Revert the force push (if possible) git reset --hard origin/feature@{1} # Previous state git push --force-with-lease # Option 2: Create revert commits git revert abc123..def456 git push 11. Remote Repositories and Collaboration {#remotes}
Remote repositories enable collaboration. Understanding remote operations is essential for team workflows.
git remote - Manage Remote Repositories
Purpose: Add, view, rename, and remove remote repository connections.
Basic Usage:
# List remotes git remote # List with URLs git remote -v git remote --verbose # Add remote git remote add origin https://github.com/user/repo.git # Add remote with different name git remote add upstream https://github.com/original/repo.git # Remove remote git remote remove origin git remote rm origin # Rename remote git remote rename old-name new-name # Change remote URL git remote set-url origin https://github.com/user/new-repo.git Viewing Remote Details:
# Show detailed information about remote git remote show origin # Output includes: # - Fetch/push URLs # - Remote branches # - Local branches configured for push/pull # - Stale branches Example output:
$ git remote show origin * remote origin Fetch URL: https://github.com/user/repo.git Push URL: https://github.com/user/repo.git HEAD branch: main Remote branches: main tracked develop tracked feature/new-feature tracked refs/remotes/origin/old-branch stale (use 'git remote prune' to remove) Local branch configured for 'git pull': main merges with remote main Local ref configured for 'git push': main pushes to main (up to date) Managing Multiple Remotes:
Common pattern: fork workflow
# Add original repository as 'upstream' git remote add upstream https://github.com/original/repo.git # Add your fork as 'origin' git remote add origin https://github.com/yourusername/repo.git # List remotes $ git remote -v origin https://github.com/yourusername/repo.git (fetch) origin https://github.com/yourusername/repo.git (push) upstream https://github.com/original/repo.git (fetch) upstream https://github.com/original/repo.git (push) # Fetch from upstream git fetch upstream # Merge upstream changes git merge upstream/main Pruning Stale Remote Branches:
# Remove stale remote-tracking branches git remote prune origin # Show what would be pruned git remote prune origin --dry-run # Prune during fetch git fetch --prune git fetch -p Changing Remote URLs:
# Switch from HTTPS to SSH git remote set-url origin git@github.com:user/repo.git # Add push URL different from fetch URL git remote set-url --add --push origin git@github.com:user/repo.git # View remote URLs git remote get-url origin git remote get-url --all origin git fetch - Download Objects and Refs from Remote
Purpose: Download commits, files, and refs from remote repository without merging. Updates remote-tracking branches.
Basic Usage:
# Fetch from default remote (origin) git fetch # Fetch from specific remote git fetch upstream # Fetch specific branch git fetch origin main # Fetch all remotes git fetch --all # Fetch and prune stale branches git fetch --prune git fetch -p What git fetch Does:
Remote repository: origin/main: A---B---C---D---E Local repository before fetch: origin/main: A---B---C main: A---B---C After git fetch: origin/main: A---B---C---D---E (updated) main: A---B---C (unchanged) Key Points:
- Downloads new commits from remote
- Updates remote-tracking branches (
origin/main, etc.) - Does NOT modify your working directory
- Does NOT merge anything
- Safe to run anytime
Fetch Specific Refs:
# Fetch specific branch git fetch origin feature-branch # Fetch specific tag git fetch origin tag v1.0.0 # Fetch all tags git fetch --tags # Fetch and create local branch git fetch origin feature-branch:feature-branch Fetch Depth:
# Shallow fetch (last N commits only) git fetch --depth=10 # Deepen existing shallow clone git fetch --deepen=50 # Convert shallow to complete git fetch --unshallow # Shallow fetch single branch git fetch --depth=1 origin main Fetch with Pruning:
# Remove remote-tracking branches that no longer exist on remote git fetch --prune # Aggressive pruning (also remove tags) git fetch --prune --prune-tags # Configure automatic pruning git config --global fetch.prune true Dry Run:
# See what would be fetched without actually fetching git fetch --dry-run git fetch -n Fetch and Display:
# Fetch and show what was fetched git fetch --verbose git fetch -v # Show what changed after fetch git fetch git log HEAD..origin/main # Commits on remote not on local git diff HEAD origin/main # Changes between local and remote Real-World Fetch Scenarios:
Scenario 1: Check for Updates Before Starting Work
# Start of day routine git fetch --all --prune # See what's new git log HEAD..origin/main --oneline # Update your branch git merge origin/main # or git rebase origin/main Scenario 2: Review Changes Before Merging
# Fetch updates git fetch origin # Review what's new without merging git log origin/main --not main --oneline git diff main origin/main # If satisfied, merge git merge origin/main Scenario 3: Update Multiple Remotes
# Fetch from all configured remotes git fetch --all # See status across remotes git branch -vv # Merge from upstream git merge upstream/main git pull - Fetch and Merge
Purpose: Fetch from remote and immediately merge into current branch. Combination of git fetch + git merge.
Basic Usage:
# Pull from tracking branch git pull # Pull from specific remote and branch git pull origin main # Pull with rebase instead of merge git pull --rebase git pull -r # Pull from specific remote git pull upstream How git pull Works:
# git pull is equivalent to: git fetch origin git merge origin/main # git pull --rebase is equivalent to: git fetch origin git rebase origin/main Pull Strategies:
1. Merge (Default):
git pull # Creates merge commit if branches diverged 2. Rebase:
git pull --rebase # Rebases your commits on top of remote commits # Creates linear history 3. Fast-forward Only:
git pull --ff-only # Only succeeds if fast-forward possible # Fails if branches diverged (safety measure) Configure Default Pull Behavior:
# Use merge (default) git config --global pull.rebase false # Use rebase git config --global pull.rebase true # Use fast-forward only git config --global pull.ff only Pull Specific Branch:
# Pull specific branch into current branch git pull origin feature-branch # Pull and create/update local branch git pull origin feature-branch:feature-branch Pull with Options:
# Pull without commit (stage changes) git pull --no-commit # Pull with verbose output git pull --verbose git pull -v # Pull all remotes git pull --all # Pull and prune git pull --prune Handling Pull Conflicts:
$ git pull Auto-merging file.txt CONFLICT (content): Merge conflict in file.txt Automatic merge failed; fix conflicts and then commit the result. # Resolve conflicts vim file.txt # Stage resolved files git add file.txt # Complete merge git commit # Or abort pull git merge --abort Pull vs Fetch:
Use git fetch when:
- Checking for updates without applying them
- Reviewing changes before integrating
- Need to inspect remote branches
- Working on multiple features
Use git pull when:
- Ready to integrate remote changes immediately
- Branch is up-to-date and simple fast-forward expected
- Quick synchronization needed
Best Practice: Many developers prefer git fetch + manual git merge/git rebase for more control:
# Explicit workflow (recommended) git fetch origin git log HEAD..origin/main # Review changes git merge origin/main # Merge when ready # Quick workflow (convenient but less control) git pull git push - Update Remote Refs
Purpose: Upload local commits to remote repository, updating remote branches.
Basic Usage:
# Push to tracking branch git push # Push to specific remote and branch git push origin main # Push and set upstream tracking git push -u origin feature-branch git push --set-upstream origin feature-branch # Push all branches git push --all # Push tags git push --tags git push --follow-tags # Push tags reachable from pushed commits How git push Works:
Local repository: main: A---B---C---D---E Remote repository before push: origin/main: A---B---C After git push origin main: origin/main: A---B---C---D---E (updated) Setting Upstream:
# First push of new branch, set tracking git push -u origin feature-branch # Future pushes just need: git push # Configure automatic upstream setup git config --global push.autoSetupRemote true Force Push:
# Force push (DANGEROUS - overwrites remote) git push --force # Safer force push (fails if remote has changes you don't have) git push --force-with-lease # Force push specific branch git push --force-with-lease origin feature-branch ⚠️ Critical Warning: Never force push to shared branches like main or develop. Only force push to your personal feature branches after rebasing.
Push Options:
Delete Remote Branch:
# Delete remote branch git push origin --delete feature-branch # Old syntax (still works) git push origin :feature-branch Push Specific Refs:
# Push local branch to different remote branch git push origin local-branch:remote-branch # Push tag git push origin v1.0.0 # Push all tags git push origin --tags # Delete remote tag git push origin --delete tag v1.0.0 Push with Options:
# Dry run (see what would be pushed) git push --dry-run git push -n # Verbose output git push --verbose git push -v # Verify before push (run pre-push hook) git push --verify # Skip pre-push hook git push --no-verify Push Behavior Configuration:
# Configure default push behavior # Simple: Push current branch to its upstream (default) git config --global push.default simple # Current: Push current branch to branch of same name git config --global push.default current # Upstream: Push current branch to its upstream branch git config --global push.default upstream # Matching: Push all matching branches git config --global push.default matching # Nothing: Don't push anything unless explicitly specified git config --global push.default nothing Recommended: Use simple (default) or current.
Handling Push Rejection:
$ git push To https://github.com/user/repo.git ! [rejected] main -> main (fetch first) error: failed to push some refs to 'https://github.com/user/repo.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. # Solution 1: Pull first (merge) git pull git push # Solution 2: Pull with rebase git pull --rebase git push # Solution 3: Force push (DANGEROUS - only for personal branches) git push --force-with-lease Real-World Push Scenarios:
Scenario 1: First Push of New Branch
# Create and switch to new branch git checkout -b feature/new-feature # Make commits git commit -am "Implement feature" # Push and set upstream git push -u origin feature/new-feature # Future pushes git push # Just works Scenario 2: Update Feature Branch After Rebase
# Rebase feature on latest main git fetch origin git rebase origin/main # Force push (history changed) git push --force-with-lease Scenario 3: Push Multiple Branches
# Push all branches git push --all origin # Push specific branches git push origin main develop feature-x Scenario 4: Cleaning Up Remote Branches
# Delete merged feature branch git branch -d feature-old git push origin --delete feature-old # Or in one command git push origin --delete feature-old Scenario 5: Push with CI/CD Trigger
# Push specific commit message to trigger deployment git commit -m "Deploy to production [deploy]" git push # Or use tags for releases git tag -a v1.2.0 -m "Release version 1.2.0" git push origin v1.2.0 Remote Branch Tracking
Understanding Remote-Tracking Branches:
Remote-tracking branches are references to the state of branches on remote repositories. They're named <remote>/<branch> (e.g., origin/main).
Viewing Tracking Information:
# Show all branches with tracking info git branch -vv # Output: * main abc123 [origin/main] Latest commit feature def456 [origin/feature: ahead 2, behind 1] Work in progress local ghi789 No tracking information # Ahead 2: You have 2 commits not pushed # Behind 1: Remote has 1 commit you don't have Setting Up Tracking:
# Set upstream for current branch git branch -u origin/main git branch --set-upstream-to=origin/main # Set upstream when pushing git push -u origin feature-branch # Create branch tracking remote branch git checkout -b local-branch origin/remote-branch # Or with git switch git switch -c local-branch origin/remote-branch # Automatic tracking (Git 2.37+) git config --global push.autoSetupRemote true Removing Tracking:
# Remove upstream tracking git branch --unset-upstream # Branch no longer tracks remote Synchronizing with Remote:
# Fetch updates git fetch origin # See differences git log HEAD..origin/main # What's on remote you don't have git log origin/main..HEAD # What you have that's not on remote # Pull updates git pull # Push updates git push Collaboration Workflows
Centralized Workflow:
Single central repository, everyone pushes to main:
# Clone repository git clone https://github.com/team/project.git cd project # Make changes git add . git commit -m "Add feature" # Pull latest changes git pull # Push changes git push Feature Branch Workflow:
Each feature developed in dedicated branch:
# Create feature branch git checkout -b feature/awesome-feature # Make commits git commit -am "Implement feature" # Push feature branch git push -u origin feature/awesome-feature # Create pull request on GitHub/GitLab # After review and approval, merge # Delete branch git branch -d feature/awesome-feature git push origin --delete feature/awesome-feature Gitflow Workflow:
Structured branching model with dedicated branches:
# Main branches: # - main: Production-ready code # - develop: Integration branch # Create feature branch from develop git checkout develop git checkout -b feature/new-feature # Develop feature git commit -am "Add feature" # Merge back to develop git checkout develop git merge feature/new-feature git branch -d feature/new-feature # Create release branch git checkout -b release/1.0 develop # Final testing and bug fixes # Merge to main and develop git checkout main git merge release/1.0 git tag v1.0 git checkout develop git merge release/1.0 git branch -d release/1.0 # Hotfix for production git checkout -b hotfix/critical-bug main # Fix bug git checkout main git merge hotfix/critical-bug git tag v1.0.1 git checkout develop git merge hotfix/critical-bug git branch -d hotfix/critical-bug Fork and Pull Request Workflow:
Common in open source:
# Fork repository on GitHub/GitLab # Clone your fork git clone https://github.com/yourname/project.git cd project # Add upstream remote git remote add upstream https://github.com/original/project.git # Create feature branch git checkout -b feature/contribution # Make changes and commit git commit -am "Add contribution" # Keep updated with upstream git fetch upstream git rebase upstream/main # Push to your fork git push -u origin feature/contribution # Create pull request from your fork to upstream # After merge, sync your fork git checkout main git fetch upstream git merge upstream/main git push origin main 12. Inspection, Logging, and History Navigation {#inspection}
Understanding your repository's history is crucial. Git provides powerful tools for inspecting commits, changes, and project evolution.
git log - Show Commit Logs
Purpose: Display commit history with various formatting and filtering options.
Basic Usage:
# Show commit history git log # One commit per line git log --oneline # Show last N commits git log -n 5 git log -5 # Show commits with diffs git log -p git log --patch # Show commit stats git log --stat # Show graph of branches git log --graph --oneline --all Understanding Log Output:
$ git log commit abc123def456... (HEAD -> main, origin/main) Author: John Doe <john@example.com> Date: Mon Dec 2 10:30:00 2024 -0800 Add user authentication feature Implemented JWT-based authentication with login and logout endpoints. Added middleware for protected routes. commit 789ghi012jkl... Author: Jane Smith <jane@example.com> Date: Sun Dec 1 15:20:00 2024 -0800 Fix bug in payment processing Formatting Options:
# Oneline format git log --oneline # Output: abc123 Commit message # Short format git log --pretty=short # Full format (includes committer) git log --pretty=full # Fuller format (includes dates) git log --pretty=fuller # Custom format git log --pretty=format:"%h - %an, %ar : %s" # Output: abc123 - John Doe, 2 days ago : Commit message Custom Format Placeholders:
| Placeholder | Description |
|---|---|
%H | Commit hash (full) |
%h | Commit hash (abbreviated) |
%T | Tree hash |
%t | Tree hash (abbreviated) |
%P | Parent hashes |
%p | Parent hashes (abbreviated) |
%an | Author name |
%ae | Author email |
%ad | Author date |
%ar | Author date, relative |
%cn | Committer name |
%ce | Committer email |
%cd | Committer date |
%cr | Committer date, relative |
%s | Subject (commit message) |
%b | Body (commit message) |
%Cred | Switch color to red |
%Cgreen | Switch color to green |
%Cblue | Switch color to blue |
%Creset | Reset color |
Beautiful Custom Format:
git log --pretty=format:"%C(yellow)%h%Creset %C(blue)%ad%Creset %C(green)%an%Creset %s" --date=short # Or create alias: git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative" # Use: git lg Graph Visualization:
# Show branch structure git log --graph # Compact graph git log --graph --oneline # All branches git log --graph --all --oneline # Decorated (show branch/tag names) git log --graph --decorate --oneline Example output:
* abc123 (HEAD -> main) Latest commit * def456 Previous commit | * ghi789 (feature) Feature commit | * jkl012 Another feature commit |/ * mno345 Common ancestor Filtering Commits:
By Author:
# Commits by specific author git log --author="John Doe" # Multiple authors (regex) git log --author="John\|Jane" # Case-insensitive git log --author="john" -i By Date:
# Commits after date git log --since="2024-01-01" git log --after="2 weeks ago" # Commits before date git log --until="2024-12-31" git log --before="1 month ago" # Date range git log --since="2024-01-01" --until="2024-12-31" # Relative dates git log --since="2 weeks ago" git log --since="yesterday" git log --until="3 days ago" By Message:
# Commits with message containing text git log --grep="bug fix" # Case-insensitive git log --grep="BUG" -i # Multiple patterns (OR) git log --grep="fix" --grep="bug" # Multiple patterns (AND) git log --grep="fix" --grep="bug" --all-match # Invert match (exclude) git log --grep="WIP" --invert-grep By File:
# Commits affecting specific file git log -- path/to/file.txt # Multiple files git log -- file1.txt file2.txt # Files in directory git log -- src/ # Follow file renames git log --follow -- file.txt By Content Changes:
# Commits adding or removing specific string git log -S "function_name" # With regex git log -G "regex_pattern" # Show patches with changes git log -S "function_name" -p This is incredibly powerful for finding when specific code was introduced or removed.
By Commit Range:
# Commits between two commits git log commit1..commit2 # Commits reachable from commit2 but not commit1 git log commit1...commit2 # Commits on branch not on main git log main..feature-branch # Commits on main not on feature git log feature-branch..main # Not yet merged to main git log --no-merges main..feature Limiting Output:
# First N commits git log -n 10 git log -10 # Skip first N commits git log --skip=5 # Maximum count git log --max-count=20 Merge and Non-Merge Commits:
# Only merge commits git log --merges # Exclude merge commits git log --no-merges # First parent only (simplifies merge history) git log --first-parent Statistics and Patches:
# Show files changed and stats git log --stat # Show shorter stat git log --shortstat # Show name and status only git log --name-status # Show filenames only git log --name-only # Show patches (diffs) git log -p # Show word diff in patches git log -p --word-diff Advanced Log Options:
# Show commits that changed number of lines git log --diff-filter=M --stat # Show only added files git log --diff-filter=A --name-only # Show only deleted files git log --diff-filter=D --name-only # Show renamed files git log --diff-filter=R --name-status # Complex filter (Added or Modified) git log --diff-filter=AM # Show commits touching specific function git log -L :function_name:path/to/file.c # Show commits changing lines 10-20 git log -L 10,20:path/to/file.txt Real-World Log Examples:
Example 1: Daily Standup Report
# What did I do yesterday? git log --author="Your Name" --since="yesterday" --oneline # What did the team do this week? git log --since="1 week ago" --pretty=format:"%an: %s" --no-merges Example 2: Release Notes
# Changes since last release git log v1.0.0..HEAD --oneline --no-merges # With categories git log v1.0.0..HEAD --pretty=format:"%s" --no-merges | grep "^feat:" git log v1.0.0..HEAD --pretty=format:"%s" --no-merges | grep "^fix:" Example 3: Find Bug Introduction
# When was this function added? git log -S "problematic_function" --oneline # Show the actual changes git log -S "problematic_function" -p # Who introduced this bug? git log -S "buggy_code" --pretty=format:"%an - %s" Example 4: Code Review Preparation
# All commits in feature branch git log main..feature-branch --oneline # With file changes git log main..feature-branch --name-status # With full diffs git log main..feature-branch -p git show - Show Various Types of Objects
Purpose: Display information about Git objects (commits, tags, trees, blobs).
Basic Usage:
# Show latest commit git show # Show specific commit git show abc123 # Show commit with specific formatting git show --oneline abc123 # Show specific file at specific commit git show abc123:path/to/file.txt # Show tag git show v1.0.0 Showing Commits:
# Show commit with diff git show HEAD # Show previous commit git show HEAD^ git show HEAD~1 # Show grandparent commit git show HEAD~2 # Show specific commit git show abc123def # Show abbreviated commit git show --abbrev-commit abc123 Showing Specific Files:
# Show file at specific commit git show HEAD:README.md # Show file from different branch git show main:src/app.js # Show file as it was 5 commits ago git show HEAD~5:config.yml Showing Tags:
# Show tag (displays tag object and commit) git show v1.0.0 # Show lightweight tag (just the commit) git show v1.0.1 Formatting Options:
# Show with stats git show --stat abc123 # Show with patch git show -p abc123 # Show abbreviated git show --oneline abc123 # Show with specific format git show --pretty=format:"%h - %s" abc123 Showing Trees:
# Show tree object (directory listing) git show abc123^{tree} # Show specific directory git show abc123:src/ Real-World Show Examples:
Example 1: Inspect Merge Commit
# Show what was merged git show merge-commit-hash # Show files changed in merge git show --stat merge-commit-hash Example 2: Compare File Versions
# Current version cat README.md # Version from 5 commits ago git show HEAD~5:README.md # Side-by-side comparison diff <(git show HEAD~5:README.md) README.md Example 3: Extract File from History
# Get old version of file git show abc123:old-file.txt > recovered-file.txt git diff - Show Differences (Covered Earlier)
We covered git diff extensively in the Basic Workflow section. Quick refresher on history-related diff usage:
# Diff between commits git diff commit1 commit2 # Diff with ancestor git diff HEAD~3 HEAD # Diff between branches git diff main feature-branch # Diff specific file between commits git diff abc123 def456 -- file.txt # Diff with three dots (since branches diverged) git diff main...feature-branch git reflog - Reference Logs
Purpose: Shows history of HEAD and branch references, even after commits are "lost" from normal history. Essential for recovery.
Basic Usage:
# Show reflog for HEAD git reflog # Show reflog for specific branch git reflog show main # Show with dates git reflog --date=iso # Show relative dates git reflog --date=relative Understanding Reflog Output:
$ git reflog abc123 HEAD@{0}: commit: Add feature X def456 HEAD@{1}: commit: Fix bug Y ghi789 HEAD@{2}: checkout: moving from main to feature jkl012 HEAD@{3}: commit: Update documentation mno345 HEAD@{4}: merge feature: Fast-forward pqr678 HEAD@{5}: commit: Initial commit Each entry shows:
- Commit hash
- HEAD position reference (HEAD@{N})
- Action performed
- Message
Using Reflog References:
# Checkout previous HEAD position git checkout HEAD@{1} # Reset to where HEAD was 3 moves ago git reset --hard HEAD@{3} # Show commit from reflog git show HEAD@{5} # Diff with previous position git diff HEAD HEAD@{1} Reflog with Time:
# HEAD position 1 hour ago git show HEAD@{1.hour.ago} # HEAD position yesterday git show HEAD@{yesterday} # HEAD position last week git show HEAD@{1.week.ago} # Branch position 2 days ago git show main@{2.days.ago} Filtering Reflog:
# Show only specific action git reflog --grep="commit" git reflog --grep="rebase" git reflog --grep="checkout" # Show reflog for specific file git reflog -- path/to/file.txt # Limit output git reflog -n 20 git reflog -10 Reflog for All References:
# Show reflog for all refs (branches, tags, etc.) git reflog show --all # Specific branch reflog git reflog show feature-branch # Remote branch reflog git reflog show origin/main Reflog Expiration:
Reflog entries expire after a certain time:
# Show reflog expiration configuration git config --get gc.reflogExpire # Default: 90 days git config --get gc.reflogExpireUnreachable # Default: 30 days # Change expiration git config gc.reflogExpire 180 # Keep for 180 days # Expire reflog manually git reflog expire --expire=30.days --all # Never expire (dangerous, can bloat repository) git config gc.reflogExpire never Real-World Reflog Usage:
Scenario 1: Recover from Bad Reset
# Oops, accidentally reset too far git reset --hard HEAD~10 # Check reflog git reflog # abc123 HEAD@{0}: reset: moving to HEAD~10 # def456 HEAD@{1}: commit: Important work # Recover git reset --hard HEAD@{1} # or git reset --hard def456 Scenario 2: Recover Deleted Branch
# Accidentally deleted branch git branch -D important-feature # Deleted branch important-feature (was abc123). # Find it in reflog git reflog show important-feature # or search all reflog git reflog | grep "important-feature" # Recreate branch git branch important-feature abc123 Scenario 3: Undo Rebase Disaster
# Rebase went wrong git rebase main # ... conflicts, mess ... git rebase --abort # If still in progress # Or if rebase completed but broke things git reflog # Find commit before rebase git reset --hard HEAD@{5} Scenario 4: Find Lost Commit
# Made commit, then somehow lost it git reflog --all | grep "commit message fragment" # Found it: abc123 git cherry-pick abc123 # or create branch git branch recovered-work abc123 git blame - Show What Revision Modified Each Line
Purpose: Show who last modified each line of a file and when. Essential for understanding code history and accountability.
Basic Usage:
# Blame entire file git blame filename.txt # Blame specific line range git blame -L 10,20 filename.txt # Blame with commit details git blame -c filename.txt # Show email instead of name git blame -e filename.txt Understanding Blame Output:
$ git blame example.py abc123de (John Doe 2024-11-15 10:30:00 -0800 1) def calculate_total(items): abc123de (John Doe 2024-11-15 10:30:00 -0800 2) total = 0 def456gh (Jane Smith 2024-11-20 14:15:00 -0800 3) for item in items: def456gh (Jane Smith 2024-11-20 14:15:00 -0800 4) total += item.price ghi789jk (Bob Lee 2024-12-01 09:00:00 -0800 5) return total * 1.1 # Add tax Columns:
- Commit hash
- Author name
- Date and time
- Line number
- Line content
Formatting Options:
# Show long format (full commit hash) git blame -l filename.txt # Show short format (abbreviated hash) git blame -s filename.txt # Suppress author name git blame -n filename.txt # Show line number git blame -n filename.txt # Show email addresses git blame -e filename.txt # Show relative dates git blame --date=relative filename.txt # Show custom date format git blame --date=short filename.txt git blame --date=iso filename.txt Line Range:
# Blame lines 10 to 20 git blame -L 10,20 filename.txt # Blame from line 10 to end git blame -L 10, filename.txt # Blame first 50 lines git blame -L 1,50 filename.txt # Blame function (if Git can detect) git blame -L :function_name: filename.c Following File Renames:
# Follow file renames git blame -C filename.txt # Follow with copy detection git blame -C -C filename.txt # Aggressive copy detection git blame -C -C -C filename.txt Ignoring Whitespace:
# Ignore whitespace changes git blame -w filename.txt # Ignore whitespace changes at line ends git blame --ignore-space-at-eol filename.txt # Ignore all space changes git blame --ignore-space-change filename.txt Blame Specific Revision:
# Blame as of specific commit git blame abc123 filename.txt # Blame as of tag git blame v1.0.0 filename.txt # Blame as of date git blame HEAD@{2.weeks.ago} filename.txt Ignoring Revisions:
Useful for ignoring bulk formatting commits:
# Create .git-blame-ignore-revs echo "abc123def456" >> .git-blame-ignore-revs # Formatting commit # Configure Git to use it git config blame.ignoreRevsFile .git-blame-ignore-revs # Now blame ignores that commit git blame filename.txt Interactive Blame:
# Use tig (if installed) for interactive blame tig blame filename.txt # Use Git GUI git gui blame filename.txt Real-World Blame Examples:
Example 1: Find Who Introduced Bug
# Find problematic line grep -n "buggy_code" src/app.js # Line 42 matches # Blame that line git blame -L 42,42 src/app.js # abc123 (John Doe 2024-11-15) buggy_code # Show full commit git show abc123 # Contact John about the bug Example 2: Understand Code History
# Why does this code exist? git blame -L 100,150 complex_file.py # See commits that modified these lines git log -p abc123 def456 ghi789 # Read commit messages for context Example 3: Code Review Context
# Reviewing someone's changes git blame new_file.py # See who wrote each part # Check if recent changes are from PR author # Review their overall contribution patterns Example 4: Track Feature Development
# When was this feature added? git blame -L :feature_function: src/features.js # See evolution git log -p abc123 # Initial commit git log -p def456 # Later modification git shortlog - Summarize Git Log
Purpose: Summarize commit history by author, useful for generating release notes and contributor lists.
Basic Usage:
# Summarize by author git shortlog # Count commits per author git shortlog -sn git shortlog --summary --numbered # Show email addresses git shortlog -sne git shortlog --summary --numbered --email Understanding Output:
$ git shortlog Jane Doe (10): Add user authentication Fix login bug Update documentation ... John Smith (5): Implement payment processing Add tests ... With -sn (summary, numbered):
$ git shortlog -sn 10 Jane Doe 5 John Smith 3 Bob Lee 1 Alice Johnson Filtering:
# Specific date range git shortlog --since="2024-01-01" --until="2024-12-31" # Specific author git shortlog --author="John" # Specific branch git shortlog main # Between commits/branches git shortlog v1.0..v2.0 # No merge commits git shortlog --no-merges Formatting:
# Group by committer instead of author git shortlog -c # Show email addresses git shortlog -e # Custom format git shortlog --format="%h %s" Real-World Shortlog Examples:
Example 1: Generate Release Notes
# Commits since last release git shortlog v1.0.0..HEAD --no-merges # Summarize contributions git shortlog v1.0.0..HEAD --no-merges -sn Example 2: Contributor Recognition
# All-time contributors git shortlog -sn # Contributors this year git shortlog --since="2024-01-01" -sn # New contributors this release git shortlog v1.0..HEAD -sn Example 3: Team Activity Report
# Activity last month git shortlog --since="1 month ago" -sn # Detailed activity git shortlog --since="1 week ago" git describe - Describe Commit Using Tags
Purpose: Give human-readable name to commit based on available tags.
Basic Usage:
# Describe current commit git describe # Describe specific commit git describe abc123 # Describe with tags git describe --tags # Always show long format git describe --long Understanding Output:
$ git describe v1.2.0-5-gabc123d # Breakdown: # v1.2.0: Most recent tag # 5: Number of commits since tag # g: Prefix indicating Git # abc123d: Abbreviated commit hash If on exact tag:
$ git describe v1.2.0 # Just the tag name Options:
# Abbreviated commit hash length git describe --abbrev=10 # Only show tag (no commit count) git describe --exact-match # Fails if not on tag # Always show tag-commits-hash git describe --long # Use all refs, not just tags git describe --all # Use any tag (annotated or lightweight) git describe --tags # Add dirty indicator for modified working directory git describe --dirty git describe --dirty=-modified # Output: v1.2.0-5-gabc123d-modified Real-World Describe Usage:
Example 1: Version Numbering
# Get version for build VERSION=$(git describe --tags --always --dirty) echo "Building version: $VERSION" # Use in code echo "#define VERSION \"$VERSION\"" > version.h Example 2: Release Identification
# What release is this commit from? git describe abc123 # Output: v1.2.0-3-gabc123 # It's 3 commits after v1.2.0 Example 3: CI/CD Versioning
# In CI/CD pipeline GIT_VERSION=$(git describe --tags --always --dirty) docker build -t myapp:$GIT_VERSION . Navigating History
Relative References:
Git provides convenient syntax for referring to commits relative to others:
Parent References:
# Parent of HEAD HEAD^ HEAD~1 # Grandparent HEAD^^ HEAD~2 # Great-grandparent HEAD^^^ HEAD~3 # Show specific parent commit git show HEAD^ git show HEAD~2 For Merge Commits (Multiple Parents):
# First parent (main branch) HEAD^1 HEAD^ # Second parent (merged branch) HEAD^2 # Grandparent through first parent HEAD^^1 HEAD~2 # Grandparent through second parent HEAD^2^ Combining References:
# Third commit before HEAD HEAD~3 # First parent of second parent of HEAD HEAD^2^1 # Show range HEAD~5..HEAD~2 Branch References:
# Latest commit on branch main origin/main feature/auth # Previous commit on branch main^ main~1 # Branch as of yesterday main@{yesterday} # Branch as of specific date main@{2024-11-15} Real-World Navigation:
# View previous commit git show HEAD^ # Compare current with 5 commits ago git diff HEAD~5 HEAD # Checkout previous version git checkout HEAD~3 # Cherry-pick parent's parent git cherry-pick HEAD~2 # Reset to 3 commits ago git reset --soft HEAD~3 13. Stashing: Temporary Storage {#stashing}
Stashing saves your work temporarily without committing, allowing you to switch contexts cleanly.
git stash - Stash Changes
Purpose: Temporarily save modified tracked files and staged changes, reverting working directory to clean state.
Basic Usage:
# Stash current changes git stash # Stash with message git stash push -m "Work in progress on feature X" # Stash including untracked files git stash -u git stash --include-untracked # Stash everything (including ignored files) git stash -a git stash --all # List stashes git stash list # Apply most recent stash git stash apply # Apply and remove most recent stash git stash pop # Remove stash git stash drop # Clear all stashes git stash clear How Stashing Works:
Before stash: Working Directory: Modified files Staging Area: Staged files Repository: Clean After git stash: Working Directory: Clean (matches HEAD) Staging Area: Clean Stash: Saved changes (both working directory and staging area) Stash List:
$ git stash list stash@{0}: WIP on main: abc123 Latest commit stash@{1}: On feature: Work in progress stash@{2}: WIP on develop: def456 Previous work Stashes are numbered: stash@{0} is most recent, stash@{1} is older, etc.
Stashing Options:
Stash with Message:
# Better than default "WIP on branch" git stash push -m "Implementing user authentication" # Or (older syntax, still works) git stash save "Implementing user authentication" Partial Stashing:
# Stash specific files git stash push path/to/file1.txt path/to/file2.txt # Stash with pattern git stash push -m "Config changes" -- '*.config' # Interactive stashing git stash push -p git stash push --patch # Choose which hunks to stash (like git add -p) Stash Untracked Files:
# Include untracked files git stash -u git stash --include-untracked # Include ignored files too git stash -a git stash --all Stash Keeping Index:
# Stash working directory changes but keep staging area git stash --keep-index # Use case: You staged changes for one commit, # but need to quickly switch branches Applying Stashes:
# Apply most recent stash (keeps stash in list) git stash apply # Apply specific stash git stash apply stash@{2} # Apply and remove (pop) git stash pop git stash pop stash@{1} # Apply only file changes, not staging information git stash apply --index Viewing Stashes:
# List stashes git stash list # Show stash contents git stash show # Show detailed diff git stash show -p git stash show --patch # Show specific stash git stash show stash@{1} -p Managing Stashes:
# Drop specific stash git stash drop stash@{1} # Clear all stashes (CAUTION: irreversible) git stash clear # Create branch from stash git stash branch new-branch-name git stash branch new-branch stash@{1} Creating a branch from stash applies the stash and drops it if successful.
Real-World Stash Scenarios:
Scenario 1: Quick Context Switch
# Working on feature, urgent bug reported git stash push -m "WIP: feature X implementation" # Switch to main, fix bug git checkout main git checkout -b hotfix/critical-bug # Fix bug, commit, push # Return to feature work git checkout feature-branch git stash pop Scenario 2: Clean Working Directory for Pull
# Have local changes, need to pull git stash -u # Pull updates git pull # Reapply changes git stash pop # If conflicts, resolve and drop manually git stash drop Scenario 3: Experiment Without Committing
# Current work is not ready to commit git stash # Try experimental approach # ... make changes ... # Doesn't work, discard git reset --hard # Restore original work git stash pop Scenario 4: Partial Stashing
# Have changes for two different features git stash push -p -m "Feature A changes" # Select only Feature A hunks # Commit Feature B git commit -am "Implement Feature B" # Restore Feature A work git stash pop Scenario 5: Transfer Work Between Branches
# Started work on wrong branch git stash # Switch to correct branch git checkout correct-branch # Apply work git stash pop Advanced Stash Techniques:
Stash and Create Branch:
# Stash causes conflicts when popping git stash pop # CONFLICT... # Instead, create branch from stash git stash branch fix-conflicts # Git creates branch, applies stash, drops it if successful # You're now on new branch with changes applied Recover Dropped Stash:
# Accidentally dropped important stash git stash drop stash@{0} # Oops! # Find it in reflog git fsck --unreachable | grep commit | cut -d ' ' -f3 | xargs git log --merges --no-walk --grep=WIP # Or search reflog git log --graph --oneline --decorate $(git fsck --no-reflog | awk '/dangling commit/ {print $3}') # Apply recovered stash git stash apply <commit-hash> Stash Part of Staging Area:
# Some files staged, some not git status # Changes to be committed: file1.txt # Changes not staged: file2.txt # Stash only unstaged changes git stash --keep-index # Now only staged changes remain Create Stash Without Changing Working Directory:
# Create stash but don't clean working directory git stash create # Returns stash commit hash # Useful for scripting Comparing Stashes:
# Diff between stash and current state git diff stash@{0} # Diff between two stashes git diff stash@{0} stash@{1} # Diff stash with specific commit git diff stash@{0} main 14. Tagging: Marking Important Points {#tagging}
Tags mark specific points in history as important, typically for releases.
git tag - Create, List, Delete Tags
Purpose: Create named references to specific commits, typically for marking releases.
Types of Tags:
1. Lightweight Tags:
- Simple pointer to commit
- Just a name
- Like a branch that doesn't move
2. Annotated Tags:
- Full Git objects
- Contain tagger name, email, date
- Can be signed and verified
- Can have message
- Recommended for releases
Basic Usage:
# List tags git tag # List tags with pattern git tag -l "v1.*" git tag --list "v1.*" # Create lightweight tag git tag v1.0.0 # Create annotated tag git tag -a v1.0.0 -m "Release version 1.0.0" git tag --annotate v1.0.0 -m "Release version 1.0.0" # Tag specific commit git tag v1.0.0 abc123 # Delete tag git tag -d v1.0.0 git tag --delete v1.0.0 # Show tag info git show v1.0.0 Creating Tags:
Lightweight Tag:
# Create lightweight tag on HEAD git tag v1.0.0 # Create on specific commit git tag v1.0.0 abc123 Annotated Tag (Recommended):
# Create annotated tag with message git tag -a v1.0.0 -m "Release version 1.0.0" # Create annotated tag and open editor for message git tag -a v1.0.0 # Tag specific commit git tag -a v1.0.0 abc123 -m "Release 1.0.0" Signed Tags:
# Create signed tag (requires GPG) git tag -s v1.0.0 -m "Signed release 1.0.0" git tag --sign v1.0.0 -m "Signed release 1.0.0" # Verify signed tag git tag -v v1.0.0 git tag --verify v1.0.0 Listing Tags:
# List all tags git tag # List with pattern git tag -l "v1.8.*" git tag -l "v2.*" # List annotated tags with messages git tag -n git tag -n9 # Show up to 9 lines of message # Sort tags git tag --sort=-version:refname # Descending version sort git tag --sort=version:refname # Ascending version sort git tag --sort=-committerdate # By date Showing Tag Information:
# Show tag and commit info git show v1.0.0 # Show only tag object git cat-file -p v1.0.0 # List tags with commit info git show-ref --tags Deleting Tags:
# Delete local tag git tag -d v1.0.0 # Delete remote tag git push origin --delete v1.0.0 git push origin :refs/tags/v1.0.0 # Old syntax Pushing Tags:
# Push specific tag git push origin v1.0.0 # Push all tags git push origin --tags # Push only annotated tags git push --follow-tags # Configure to always push annotated tags git config --global push.followTags true Checking Out Tags:
# Checkout tag (detached HEAD) git checkout v1.0.0 # Create branch from tag git checkout -b version-1.0 v1.0.0 # Switch to tag (Git 2.23+) git switch --detach v1.0.0 Real-World Tagging Scenarios:
Scenario 1: Release Workflow
# Finalize release commit git commit -m "Bump version to 1.0.0" # Create annotated tag git tag -a v1.0.0 -m "Release 1.0.0 Major features: - User authentication - Payment processing - Admin dashboard Breaking changes: - API endpoint /v1/users renamed to /v2/users" # Push commit and tag git push origin main git push origin v1.0.0 Scenario 2: Semantic Versioning
# Major release (breaking changes) git tag -a v2.0.0 -m "Major release with breaking changes" # Minor release (new features) git tag -a v2.1.0 -m "Add new features" # Patch release (bug fixes) git tag -a v2.1.1 -m "Fix critical bug" # Pre-release git tag -a v2.2.0-beta.1 -m "Beta release" Scenario 3: Hotfix Tagging
# Critical bug in production (v1.2.0) git checkout v1.2.0 git checkout -b hotfix/security-fix # Fix bug git commit -m "Fix security vulnerability" # Tag hotfix git tag -a v1.2.1 -m "Security hotfix" # Push git push origin hotfix/security-fix git push origin v1.2.1 Scenario 4: Build Versioning
# Tag nightly build DATE=$(date +%Y%m%d) git tag -a nightly-$DATE -m "Nightly build $DATE" # Tag release candidate git tag -a v1.0.0-rc.1 -m "Release candidate 1" git tag -a v1.0.0-rc.2 -m "Release candidate 2" Scenario 5: Comparing Releases
# Changes between releases git log v1.0.0..v2.0.0 --oneline # Diff between releases git diff v1.0.0 v2.0.0 # Files changed git diff --name-status v1.0.0 v2.0.0 # Generate changelog git log v1.0.0..v2.0.0 --no-merges --pretty=format:"- %s" Tag Naming Conventions:
Semantic Versioning:
v1.0.0 (or 1.0.0) vMAJOR.MINOR.PATCH Examples: v1.0.0 - Initial release v1.1.0 - New features v1.1.1 - Bug fixes v2.0.0 - Breaking changes Pre-releases: v1.0.0-alpha v1.0.0-beta.1 v1.0.0-rc.1 Date-Based:
release-2024.12.02 v2024.12 Build Numbers:
build-12345 v1.0.0-build.456 Best Practices:
- Use annotated tags for releases:
# Good git tag -a v1.0.0 -m "Release 1.0.0" # Avoid for releases git tag v1.0.0 - Be consistent with naming:
# Choose one format and stick to it v1.0.0, v1.1.0, v2.0.0 # Good v1.0.0, 1.1.0, version-2.0 # Inconsistent - Don't move tags:
# DON'T do this: git tag -d v1.0.0 git tag v1.0.0 different-commit # Tags should be permanent markers - Sign release tags:
# For important releases git tag -s v1.0.0 -m "Signed release" - Document releases in tag messages:
git tag -a v1.0.0 -m "Release 1.0.0 Features: - Feature A - Feature B Bug fixes: - Fix issue #123 Breaking changes: - API change in module X" 15. Advanced History Modification {#history-modification}
⚠️ Warning: These commands rewrite history. Never use on public/shared branches unless you understand the consequences.
git commit --amend (Covered Earlier)
Quick refresher:
# Modify last commit git commit --amend # Amend without changing message git commit --amend --no-edit # Amend and change author git commit --amend --author="New Author <email@example.com>" git reset - Reset Current HEAD
Purpose: Move HEAD (and optionally branch) to specified commit, with various effects on staging area and working directory.
Three Modes:
1. Soft Reset:
- Moves HEAD and branch pointer
- Keeps staging area unchanged
- Keeps working directory unchanged
- Commits become "uncommitted changes"
2. Mixed Reset (Default):
- Moves HEAD and branch pointer
- Resets staging area to match commit
- Keeps working directory unchanged
- Commits become "unstaged changes"
3. Hard Reset:
- Moves HEAD and branch pointer
- Resets staging area to match commit
- Resets working directory to match commit
- Destroys uncommitted changes
Basic Usage:
# Soft reset (uncommit, keep changes staged) git reset --soft HEAD~1 # Mixed reset (unstage changes) git reset HEAD~1 git reset --mixed HEAD~1 # Hard reset (discard everything) git reset --hard HEAD~1 # Reset to specific commit git reset --hard abc123 # Reset specific file git reset HEAD file.txt Visual Representation:
Initial state: A---B---C---D (HEAD, main) After git reset --soft HEAD~2: A---B---C---D ↑ (HEAD, main) Changes from C and D are staged After git reset --mixed HEAD~2: A---B---C---D ↑ (HEAD, main) Changes from C and D are unstaged After git reset --hard HEAD~2: A---B (HEAD, main) Commits C and D are "gone" (but recoverable via reflog) Reset Modes Comparison:
| Mode | HEAD | Staging Area | Working Directory |
|---|---|---|---|
--soft | Reset | Unchanged | Unchanged |
--mixed | Reset | Reset | Unchanged |
--hard | Reset | Reset | Reset |
Common Reset Scenarios:
Scenario 1: Undo Last Commit (Keep Changes)
# Undo commit, keep changes staged git reset --soft HEAD~1 # Now you can re-commit with different message or add more changes git commit -m "Better commit message" Scenario 2: Unstage Files
# 1Accidentally staged wrong file git add wrong-file.txt # Unstage it git reset HEAD wrong-file.txt # or (Git 2.23+) git restore --staged wrong-file.txt Scenario 3: Completely Undo Commits
# Discard last 3 commits entirely git reset --hard HEAD~3 # WARNING: This destroys commits and changes # Only do this on unpushed commits Scenario 4: Squash Commits Before Pushing
# You have 5 messy commits git log --oneline # e5 Fix typo # e4 Fix another typo # e3 Implement feature # e2 WIP # e1 Start feature # Reset to before commits (keep changes) git reset --soft HEAD~5 # Now create one clean commit git commit -m "Implement feature X" Scenario 5: Discard Local Changes
# Made a mess, want to start over git reset --hard HEAD # Or go back to specific commit git reset --hard origin/main Reset Specific Paths:
# Reset specific file to specific commit git reset abc123 -- path/to/file.txt # Reset multiple files git reset HEAD -- file1.txt file2.txt # Reset directory git reset HEAD -- src/ Recovery from Reset:
If you reset by mistake:
# View reflog git reflog # Find commit before reset # abc123 HEAD@{1}: commit: Important work # Reset back git reset --hard abc123 # or git reset --hard HEAD@{1} git revert - Revert Commits
Purpose: Create new commit that undoes changes from previous commit(s). Safe for public branches because it doesn't rewrite history.
Basic Usage:
# Revert last commit git revert HEAD # Revert specific commit git revert abc123 # Revert multiple commits git revert abc123 def456 ghi789 # Revert range git revert HEAD~3..HEAD # Revert without committing (stage changes) git revert -n abc123 git revert --no-commit abc123 How Revert Works:
Original: A---B---C---D (HEAD, main) After git revert C: A---B---C---D---E (HEAD, main) ↑ Revert C (undoes changes from C) Commit E contains the inverse of changes in commit C.
Revert vs Reset:
| Feature | git revert | git reset |
|---|---|---|
| History | Creates new commit | Moves HEAD pointer |
| Public branches | ✅ Safe | ❌ Dangerous |
| Undo method | Add inverse changes | Remove commits |
| History rewrite | No | Yes |
Revert Options:
Edit Commit Message:
# Revert and open editor for message git revert abc123 # Use default message git revert abc123 --no-edit # Custom message git revert abc123 -m "Revert problematic feature" Revert Merge Commits:
Merge commits have multiple parents, must specify which to revert to:
# Revert merge commit, keeping main history git revert -m 1 merge-commit-hash # -m 1: Keep changes from first parent (main) # -m 2: Keep changes from second parent (merged branch) Multiple Reverts:
# Revert multiple commits separately git revert abc123 def456 # Revert range (oldest..newest) git revert abc123..def456 # Revert without auto-commit (for manual adjustments) git revert -n abc123 def456 ghi789 git commit -m "Revert multiple changes" Abort Revert:
# If revert causes conflicts git revert --abort # Or if using -n flag git revert --quit Continue Revert:
# After resolving conflicts git add resolved-file.txt git revert --continue Real-World Revert Scenarios:
Scenario 1: Revert Bad Commit on Main
# Bad commit pushed to main git log --oneline # abc123 (HEAD -> main, origin/main) Bad feature # def456 Previous commit # Revert it (safe for public branch) git revert abc123 # Push revert git push origin main Scenario 2: Revert Merge
# Merged feature branch, but it broke production git log --oneline --graph # * abc123 (HEAD -> main) Merge branch 'feature' # |\ # | * def456 Feature commit # * | ghi789 Main commit # Revert merge git revert -m 1 abc123 # Push git push origin main Scenario 3: Revert Series of Commits
# Last 5 commits introduced bugs git revert --no-commit HEAD~4..HEAD # Review changes git diff --staged # Commit all reverts together git commit -m "Revert commits that introduced bugs" Scenario 4: Partial Revert
# Revert commit but stage only (no commit) git revert -n abc123 # Unstage specific files you want to keep git restore --staged file-to-keep.txt # Commit partial revert git commit -m "Partially revert abc123" git cherry-pick (Covered in Next Section)
Cherry-picking applies specific commits from one branch to another.
16. Cherry-Picking and Patching {#cherry-picking}
git cherry-pick - Apply Changes from Existing Commits
Purpose: Apply changes from specific commits onto current branch, creating new commits.
Basic Usage:
# Cherry-pick single commit git cherry-pick abc123 # Cherry-pick multiple commits git cherry-pick abc123 def456 ghi789 # Cherry-pick range git cherry-pick abc123..def456 # Cherry-pick without committing git cherry-pick -n abc123 git cherry-pick --no-commit abc123 How Cherry-Pick Works:
Source branch: A---B---C---D (feature) Target branch before: A---B---E---F (main, HEAD) After git cherry-pick C: A---B---E---F---C' (main, HEAD) C' is new commit with same changes as C but different hash Cherry-Pick Options:
Edit Commit Message:
# Cherry-pick and edit message git cherry-pick -e abc123 git cherry-pick --edit abc123 # Use original message git cherry-pick -x abc123 # Adds "cherry picked from" note Mainline Parent (for Merges):
# Cherry-pick merge commit git cherry-pick -m 1 merge-commit # -m 1: Use first parent # -m 2: Use second parent Sign Off:
# Add Signed-off-by line git cherry-pick -s abc123 git cherry-pick --signoff abc123 Continue, Abort, Quit:
# If conflicts occur git cherry-pick abc123 # CONFLICT... # Resolve conflicts git add resolved-file.txt # Continue git cherry-pick --continue # Or abort git cherry-pick --abort # Or quit (leave in current state) git cherry-pick --quit Multiple Cherry-Picks:
# Cherry-pick range (exclusive start, inclusive end) git cherry-pick abc123..def456 # Cherry-pick all commits from branch git cherry-pick feature-branch # Cherry-pick with strategy git cherry-pick -X theirs abc123 git cherry-pick -X ours abc123 Real-World Cherry-Pick Scenarios:
Scenario 1: Apply Hotfix to Multiple Branches
# Hotfix committed to main git checkout main git commit -m "Fix critical security bug" # Commit: abc123 # Apply to release branch git checkout release-2.0 git cherry-pick abc123 # Apply to release branch 1.0 git checkout release-1.0 git cherry-pick abc123 Scenario 2: Extract Commits from Feature Branch
# Feature branch has 10 commits # Only commits 3 and 7 are ready git checkout main git cherry-pick <commit-3-hash> git cherry-pick <commit-7-hash> # Leave rest of feature for later Scenario 3: Move Commit to Different Branch
# Accidentally committed to main instead of feature git log --oneline # abc123 (HEAD -> main) Feature work # Create feature branch git branch feature abc123 # Remove from main (if not pushed) git reset --hard HEAD~1 # Or revert if already pushed git revert abc123 Scenario 4: Apply Commits Between Forks
# Add fork as remote git remote add other-fork https://github.com/other/repo.git git fetch other-fork # Cherry-pick their commit git cherry-pick other-fork/main~2 Scenario 5: Selective Backporting
# Backport specific fixes to older version git checkout stable-1.0 git cherry-pick bug-fix-hash-1 git cherry-pick bug-fix-hash-2 git cherry-pick feature-hash # Skip if not compatible # Test and push git push origin stable-1.0 git apply - Apply Patch Files
Purpose: Apply changes from patch files (created with git diff or git format-patch).
Basic Usage:
# Apply patch git apply patch-file.patch # Apply and stage changes git apply --index patch-file.patch # Check if patch can be applied git apply --check patch-file.patch # Apply with statistics git apply --stat patch-file.patch # Reject conflicts to separate files git apply --reject patch-file.patch Creating Patches:
Method 1: Using git diff:
# Create patch from uncommitted changes git diff > changes.patch # Create patch from specific commits git diff abc123 def456 > feature.patch # Create patch for specific files git diff -- file1.txt file2.txt > files.patch Method 2: Using git format-patch:
# Create patch for last commit git format-patch -1 HEAD # Create patches for last 3 commits git format-patch -3 # Create patches since specific commit git format-patch abc123..HEAD # Create patch for specific commit git format-patch -1 abc123 # Output to specific directory git format-patch -o patches/ abc123..HEAD Applying Patches:
# Apply patch file git apply changes.patch # Apply and stage git apply --index changes.patch # Apply with three-way merge git apply -3 changes.patch # Apply formatted patches (preserves author, message) git am patches/0001-feature.patch # Apply multiple formatted patches git am patches/*.patch Patch Options:
Check Before Applying:
# Dry run git apply --check patch.patch # Show what would change git apply --stat patch.patch # Show actual changes git apply --summary patch.patch Handle Whitespace:
# Ignore whitespace changes git apply --ignore-whitespace patch.patch # Warn about whitespace errors git apply --whitespace=warn patch.patch # Fix whitespace automatically git apply --whitespace=fix patch.patch Reverse Apply:
# Unapply patch git apply --reverse patch.patch git apply -R patch.patch Partial Application:
# Apply specific files from patch git apply --include='*.txt' patch.patch # Exclude specific files git apply --exclude='test/*' patch.patch Real-World Patching Scenarios:
Scenario 1: Email-Based Workflow
# Create patches to email git format-patch -2 HEAD --stdout > feature.patch # Email feature.patch # Recipient applies git am feature.patch Scenario 2: Quick Fix Sharing
# Create quick fix git diff > quickfix.patch # Send to colleague # Colleague applies git apply quickfix.patch git commit -am "Apply quick fix" Scenario 3: Code Review via Patches
# Generate patches for review git format-patch origin/main # Output: 0001-commit1.patch, 0002-commit2.patch # Reviewer applies and tests git am 0001-commit1.patch # Test git am 0002-commit2.patch # Test Scenario 4: Resolve Conflicts in Patches
# Apply patch with conflicts git apply changes.patch # error: patch failed... # Apply with reject files git apply --reject changes.patch # Creates .rej files for failed hunks # Manually apply .rej files # Then stage git add . git commit git format-patch - Prepare Patches for Email
Purpose: Create properly formatted patch files suitable for email submission, preserving commit metadata.
Basic Usage:
# Create patch for last commit git format-patch -1 # Create patches for last N commits git format-patch -5 # Create patches since commit git format-patch abc123 # Create patches for range git format-patch abc123..def456 # Output to directory git format-patch -o output-dir/ abc123..HEAD Format-Patch Options:
# Include cover letter git format-patch --cover-letter -3 # Add threading headers for email git format-patch --thread -3 # Include signature git format-patch --signature="My Signature" -1 # Suppress diff output git format-patch --stdout abc123 > patch.txt # Include binary files git format-patch --binary -1 Real-World Format-Patch Usage:
Linux Kernel Style Submission:
# Create patch series with cover letter git format-patch --cover-letter -3 --thread -o patches/ # Edit cover letter vim patches/0000-cover-letter.patch # Send via email git send-email patches/* git am - Apply Mailbox Format Patches
Purpose: Apply patches from email (created with git format-patch), preserving commit information.
Basic Usage:
# Apply single patch git am 0001-feature.patch # Apply multiple patches git am patches/*.patch # Apply from stdin cat feature.patch | git am # Apply interactively git am -i patches/*.patch Handling Conflicts:
# Apply patch git am feature.patch # Conflict occurs # Resolve conflicts vim conflicted-file.txt git add conflicted-file.txt # Continue git am --continue # Skip this patch git am --skip # Abort git am --abort AM Options:
# Apply with three-way merge git am -3 patch.patch # Sign off patches git am -s patches/*.patch # Keep non-patch content in commit message git am -k patches/*.patch # Ignore whitespace git am --ignore-whitespace patches/*.patch This completes the comprehensive sections on Git fundamentals, branching, merging, rebasing, remotes, inspection, stashing, tagging, history modification, and cherry-picking/patching.
Top comments (0)