All topics
General · Learning hub

Git notes for developers

Master Git with a curated set of 6 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore General notes
Git

Branching & Merging

Git Branching & Merging Branching is one of Git's most powerful features. It enables parallel development, feature isolation, and safe experimentation. Understa

Git Branching & Merging

Branching is one of Git's most powerful features. It enables parallel development, feature isolation, and safe experimentation. Understanding merge strategies and rebase is critical for team workflows.

Branch Operations

# Create a branch
git branch feature-login
git checkout -b feature-login       # Create and switch (older syntax)
git switch -c feature-login          # Create and switch (modern syntax)
git switch -c feature-login main     # Branch off from main explicitly

# List branches
git branch                   # Local branches
git branch -a                # All branches (local + remote)
git branch -r                # Remote-tracking branches only
git branch -v                # Show last commit on each branch
git branch --merged          # Branches already merged into current
git branch --no-merged       # Branches not yet merged

# Switch branches
git checkout main
git switch main              # Modern syntax
git switch -                 # Switch to previously checked-out branch

# Rename a branch
git branch -m old-name new-name
git branch -M main           # Force rename to main (even if branch exists)

# Delete branches
git branch -d feature-login          # Safe delete (only if merged)
git branch -D feature-login          # Force delete (even if unmerged)
git push origin --delete feature-login  # Delete remote branch

Merging

Merging integrates changes from one branch into another. Git supports several merge strategies - fast-forward, recursive, and squash merges each have different use cases.

# Standard merge (creates merge commit if needed)
git checkout main
git merge feature-login

# Fast-forward only (fails if a merge commit would be needed)
git merge --ff-only feature-login

# No-fast-forward (always create merge commit, preserves branch history)
git merge --no-ff feature-login

# Squash merge (combine all feature branch commits into one staged change)
git merge --squash feature-login
git commit -m "Add login feature"   # Then commit the squashed result

# Abort a merge with conflicts
git merge --abort

# Resolving conflicts manually:
# 1. Edit conflicting files (look for <<<<<<, =======, >>>>>>>)
# 2. git add <resolved-files>
# 3. git commit

# Use a merge tool
git mergetool

Rebase

Rebase rewrites commit history by replaying commits on top of another base. It produces a cleaner, linear history but should never be used on shared/public branches.

# Rebase current branch onto main
git checkout feature-login
git rebase main

# Interactive rebase - edit, reorder, squash commits
git rebase -i HEAD~3          # Rebase last 3 commits
git rebase -i main            # Interactive rebase onto main

# In interactive rebase, commands:
# pick   - use commit as-is
# reword - use commit but edit message
# squash - meld into previous commit
# fixup  - like squash but discard commit message
# drop   - remove commit entirely

# Abort or continue rebase
git rebase --abort
git rebase --continue          # After resolving conflicts
git rebase --skip              # Skip current commit

# Cherry-pick - apply specific commits to current branch
git cherry-pick abc123
git cherry-pick abc123 def456  # Multiple commits
git cherry-pick main~2         # Relative reference
git cherry-pick --no-commit abc123  # Apply without committing
Git

Git Workflows

Git Workflows # Feature branch workflow git checkout -b feature/new-feature # Make changes git add . git commit -m "Add new feature" git push origin feature/new

Git Workflows

# Feature branch workflow
git checkout -b feature/new-feature
# Make changes
git add .
git commit -m "Add new feature"
git push origin feature/new-feature
# Create pull request
# After review and merge
git checkout main
git pull origin main
git branch -d feature/new-feature
Git

Interview Questions

Git Interview Questions Common Git questions asked in technical interviews - from fundamentals to advanced concepts. Understanding the "why" behind these answer

Git Interview Questions

Common Git questions asked in technical interviews - from fundamentals to advanced concepts. Understanding the "why" behind these answers sets you apart from candidates who only memorize commands.

1. What is Git and how does it differ from SVN?

Git is a distributed version control system (DVCS) where every developer has a full copy of the repository including complete history. SVN is centralized - there is one central server and developers only have working copies. Git enables offline work, faster operations (most commands are local), non-linear branching workflows, and greater resilience since there is no single point of failure.

2. What is the difference between git merge and git rebase?

Both integrate changes from one branch into another, but differently. git merge creates a new "merge commit" that has two parents, preserving the complete history of both branches. git rebase rewrites commits by replaying them on top of the target branch, producing a linear history as if the feature was developed after main. Rule of thumb: rebase local/private branches for clean history; never rebase shared/public branches because it rewrites SHAs and causes problems for collaborators.

3. What is the difference between git fetch and git pull?

git fetch downloads remote changes and updates remote-tracking branches (origin/main etc.) but does NOT modify your working directory or local branches. It is safe to run anytime. git pull is git fetch followed by git merge (or git rebase with --rebase flag) - it immediately integrates the remote changes into your current branch. Use git fetch when you want to review changes before merging; use git pull when you are ready to integrate.

4. Explain the three trees in Git (working directory, staging area, repository)

Git manages three "trees": (1) Working Directory - the actual files on disk that you edit. (2) Staging Area (Index) - a preview of the next commit; git add moves changes here. (3) Repository (HEAD) - the committed snapshot history stored in .git/. git status shows the difference between these three. git diff shows working directory vs staging area. git diff --staged shows staging area vs last commit.

5. What does git reset --soft vs --mixed vs --hard do?

All three move HEAD to a different commit but differ in how they handle the staging area and working directory. --soft: moves HEAD only; staged and working directory are untouched - your changes stay staged, ready for a new commit. --mixed (default): moves HEAD and clears the staging area; your changes are still in the working directory but unstaged. --hard: moves HEAD, clears staging area, and discards all working directory changes - effectively throws away all uncommitted work. Never use --hard carelessly; use the reflog to recover if needed.

6. What is a detached HEAD state?

Normally HEAD points to a branch name (like main), which in turn points to the latest commit. In detached HEAD state, HEAD points directly to a commit SHA instead of a branch. This happens when you checkout a specific commit, tag, or remote branch. You can make commits in detached HEAD, but they will be lost unless you create a branch before switching away (git checkout -b new-branch). It is useful for exploring history or testing an older version.

7. How do you resolve a merge conflict?

When Git cannot automatically merge changes, it marks conflicts in the file with conflict markers (<<<<<<, =======, >>>>>>>). To resolve: (1) Open conflicting files and edit them to the desired state, removing conflict markers. (2) Run git add on each resolved file to mark it resolved. (3) Run git commit to complete the merge. You can also use git mergetool to open a visual merge tool. To abort and return to pre-merge state, use git merge --abort.

8. What is git stash and when would you use it?

git stash temporarily shelves uncommitted changes so you can switch context without committing incomplete work. Common scenarios: you are mid-feature and an urgent bug comes in, you accidentally started work on the wrong branch, or you want to pull the latest remote changes but have local modifications that would conflict. Stashes are stored in a stack (LIFO). git stash pop restores the most recent stash and removes it; git stash apply restores without removing so you can apply the same changes to multiple branches.

9. What is the difference between git revert and git reset?

git revert creates a new commit that undoes the changes of a specific commit, preserving the original commit in history. It is safe for shared branches because it does not rewrite history. git reset moves the branch pointer backward, effectively removing commits from history - this rewrites history and should only be used on local commits that have not been pushed. If you need to undo a commit that is already on a shared remote branch, always use git revert.

10. What is git bisect and when is it useful?

git bisect is a debugging tool that uses binary search to find the exact commit that introduced a bug. You tell it a "good" commit (bug absent) and a "bad" commit (bug present). Git checks out the midpoint commit; you test it and mark it good or bad. Git repeats, halving the search space each time. For 1000 commits, it finds the culprit in about 10 steps. You can also automate it with git bisect run <test-script>, where the script exits 0 for good and non-zero for bad.

Git

Core Commands

Git Core Commands Git is a distributed version control system. These are the day-to-day commands you will use in every project - mastering them is essential for

Git Core Commands

Git is a distributed version control system. These are the day-to-day commands you will use in every project - mastering them is essential for any developer workflow.

Initializing & Staging

Every Git workflow starts with initializing a repository and staging changes. The staging area (index) lets you build up exactly the changes you want in your next commit.

# Initialize a new repository
git init
git init my-project   # Create new directory and init inside it

# Clone an existing repo
git clone https://github.com/user/repo.git
git clone https://github.com/user/repo.git my-folder  # Into specific folder
git clone --depth 1 https://github.com/user/repo.git  # Shallow clone (faster)

# Check working tree status
git status
git status -s          # Short/compact format

# Stage files
git add file.txt               # Stage specific file
git add src/                   # Stage entire directory
git add .                      # Stage all changes in current directory
git add -p                     # Interactively stage hunks (patch mode)
git add -u                     # Stage only tracked files (no new files)

# Unstage files
git restore --staged file.txt  # Unstage (modern syntax)
git reset HEAD file.txt        # Unstage (older syntax)

Committing Changes

Commits are snapshots of your project. A good commit message describes what changed and why. Use the imperative mood: "Add feature" not "Added feature".

# Commit staged changes
git commit -m "Add user authentication"

# Stage all tracked files and commit in one step
git commit -am "Fix typo in README"

# Open editor for a multi-line commit message
git commit

# Amend the last commit (before pushing!)
git commit --amend -m "Corrected commit message"
git commit --amend --no-edit   # Amend without changing the message

# Empty commit (useful for triggering CI)
git commit --allow-empty -m "Trigger CI build"

Viewing History & Differences

git log and git diff are your windows into the project history and current state of changes.

# View commit log
git log
git log --oneline          # Compact one-line format
git log --oneline --graph  # ASCII branch graph
git log --oneline -10      # Last 10 commits
git log --author="Alice"   # Filter by author
git log --since="2 weeks ago"
git log -- src/auth.ts     # History for a specific file

# View differences
git diff                   # Unstaged changes vs last commit
git diff --staged          # Staged changes vs last commit
git diff HEAD              # All uncommitted changes
git diff main..feature     # Compare two branches
git diff abc123 def456     # Compare two commits
git diff HEAD~1            # Compare with one commit ago

Undoing Changes

Git gives you multiple ways to undo mistakes. Choose the right tool depending on whether you have pushed changes or not.

# Discard changes in working directory
git restore file.txt        # Discard unstaged changes (modern)
git checkout -- file.txt    # Discard unstaged changes (older)

# Reset commits (do NOT use on pushed commits)
git reset --soft HEAD~1     # Undo last commit, keep changes staged
git reset --mixed HEAD~1    # Undo last commit, keep changes unstaged (default)
git reset --hard HEAD~1     # Undo last commit, discard all changes

# Revert a commit (safe - creates new commit to undo)
git revert abc123
git revert HEAD             # Revert last commit

# Stash uncommitted changes temporarily
git stash                   # Stash working changes
git stash push -m "WIP: auth feature"  # Named stash
git stash list              # List all stashes
git stash pop               # Apply most recent stash and remove it
git stash apply stash@{1}   # Apply specific stash without removing
git stash drop stash@{0}    # Delete specific stash
git stash clear             # Delete all stashes
Git

Remote Workflows

Git Remote Workflows Working with remote repositories is essential for team collaboration. Understanding how fetch, pull, and push interact with remote-tracking

Git Remote Workflows

Working with remote repositories is essential for team collaboration. Understanding how fetch, pull, and push interact with remote-tracking branches prevents data loss and merge conflicts.

Managing Remotes

# View remotes
git remote
git remote -v                    # Show URLs
git remote show origin           # Detailed info about a remote

# Add / rename / remove remotes
git remote add origin https://github.com/user/repo.git
git remote add upstream https://github.com/original/repo.git
git remote rename origin old-origin
git remote remove upstream

# Change remote URL
git remote set-url origin https://github.com/user/new-repo.git
git remote set-url origin git@github.com:user/repo.git  # Switch to SSH

Fetch, Pull & Push

git fetch downloads remote changes without merging them. git pull fetches and merges. git push uploads your local commits to the remote. Understanding the difference prevents accidental overwrites.

# Fetch - download remote changes, don't merge
git fetch origin
git fetch --all              # Fetch all remotes
git fetch origin main        # Fetch specific branch
git fetch --prune            # Remove stale remote-tracking branches

# Pull - fetch + merge (or rebase)
git pull
git pull origin main
git pull --rebase            # Rebase instead of merge
git pull --rebase=interactive
git pull --ff-only           # Fail if merge commit would be created

# Push
git push
git push origin main
git push -u origin feature-branch   # Set upstream tracking
git push --force-with-lease          # Safer force push (checks remote state)
git push origin --delete feature-branch  # Delete remote branch
git push --tags                          # Push all tags
git push origin v1.0.0                   # Push specific tag

# Tracking branches
git branch -u origin/main main           # Set upstream for existing branch
git branch --set-upstream-to=origin/feature feature

Forking & Upstream Sync

When contributing to open source, you fork the repo, make changes in your fork, then submit a pull request. Keeping your fork in sync with the original (upstream) requires a consistent workflow.

# Fork workflow - initial setup
git clone https://github.com/YOUR_USERNAME/repo.git
cd repo
git remote add upstream https://github.com/ORIGINAL_OWNER/repo.git
git remote -v   # Verify: origin = your fork, upstream = original

# Sync fork with upstream
git fetch upstream
git checkout main
git merge upstream/main
# Or in one step:
git pull upstream main

# Push synced main to your fork
git push origin main

# Work on a feature branch from up-to-date main
git checkout -b fix/issue-123
# ... make changes ...
git push origin fix/issue-123
# Then open pull request on GitHub

Tags & Releases

# List tags
git tag
git tag -l "v1.*"           # Filter by pattern

# Create tags
git tag v1.0.0              # Lightweight tag
git tag -a v1.0.0 -m "Release 1.0.0"  # Annotated tag (preferred)
git tag -a v1.0.0 abc123 -m "Tag old commit"  # Tag a specific commit

# Push tags
git push origin v1.0.0      # Push single tag
git push --tags             # Push all tags

# Delete tags
git tag -d v1.0.0                     # Delete local tag
git push origin --delete v1.0.0       # Delete remote tag
git push origin :refs/tags/v1.0.0     # Alternate remote delete syntax
Git

Advanced Git

Advanced Git Techniques Beyond the basics, Git has powerful tools for debugging history, automating workflows, managing large projects, and recovering from mist

Advanced Git Techniques

Beyond the basics, Git has powerful tools for debugging history, automating workflows, managing large projects, and recovering from mistakes. These techniques separate senior developers from juniors.

Reflog - Your Safety Net

The reflog records every time HEAD changes. Even after a hard reset or dropped stash, you can recover lost commits using the reflog. It is the ultimate undo tool.

# View reflog
git reflog
git reflog show HEAD        # Default - HEAD movements
git reflog show main        # Reflog for a specific branch

# Recover lost commit after hard reset
git reset --hard HEAD~3     # Oops - lost 3 commits
git reflog                  # Find the SHA before the reset
git reset --hard abc1234    # Restore to that commit

# Recover a dropped stash
git fsck --lost-found       # Find dangling objects
git stash apply <dangling-sha>

# Checkout any historical state
git checkout abc1234        # Detached HEAD at specific commit
git checkout -b recovered-branch abc1234  # Create branch from it

Bisect - Finding Bugs by Binary Search

git bisect uses binary search to find the exact commit that introduced a bug. It halves the search space at each step, making it extremely efficient even with thousands of commits.

# Start bisect session
git bisect start
git bisect bad                    # Current commit is broken
git bisect good v2.0.0            # This release was working

# Git checks out middle commit - test it, then mark:
git bisect good                   # This commit is fine
git bisect bad                    # This commit is broken
# Repeat until Git identifies the culprit

# Automate with a test script (exits 0 = good, non-zero = bad)
git bisect run npm test
git bisect run ./scripts/check-feature.sh

# End bisect (restores HEAD)
git bisect reset

Git Hooks

Git hooks are scripts that run automatically at key points in the Git lifecycle. Use them to enforce code standards, run tests, or prevent bad commits from reaching the remote.

# Hooks live in .git/hooks/ - create executable scripts there
# Common hooks:
#   pre-commit      - runs before commit is created
#   commit-msg      - validate commit message format
#   pre-push        - runs before push
#   post-merge      - runs after merge
#   post-checkout   - runs after checkout

# Example: pre-commit hook to run linter
# File: .git/hooks/pre-commit (must be executable: chmod +x)
#!/bin/sh
npm run lint
if [ $? -ne 0 ]; then
  echo "Linting failed - fix errors before committing"
  exit 1
fi

# Example: commit-msg hook to enforce format
#!/bin/sh
COMMIT_MSG=$(cat "$1")
if ! echo "$COMMIT_MSG" | grep -qE "^(feat|fix|docs|style|refactor|test|chore): .+";
then
  echo "Commit message must follow Conventional Commits format"
  exit 1
fi

# Share hooks with team using husky (Node.js projects)
npx husky init
# Then add hooks to .husky/ directory (version controlled)

Interactive Rebase & Cleaning History

# Squash last 4 commits into one before merging
git rebase -i HEAD~4
# In editor: change "pick" to "squash" (or "s") for commits to merge
# Then write a clean combined commit message

# Reorder commits
# In the rebase editor, just rearrange the lines

# Remove a specific file from all history (dangerous - rewrites all SHAs)
git filter-branch --tree-filter 'rm -f secrets.env' HEAD
# Modern alternative:
git filter-repo --path secrets.env --invert-paths

# Clean untracked files
git clean -n          # Dry run - shows what would be deleted
git clean -f          # Delete untracked files
git clean -fd         # Delete untracked files and directories
git clean -fX         # Delete only ignored files (.gitignore)
git clean -fdx        # Delete everything untracked + ignored

Submodules

Submodules allow you to embed one Git repository inside another. Useful for shared libraries or when you need to pin a dependency at a specific commit.

# Add a submodule
git submodule add https://github.com/user/lib.git vendor/lib
git submodule add --branch main https://github.com/user/lib.git vendor/lib

# Clone a repo with submodules
git clone --recurse-submodules https://github.com/user/repo.git
# Or initialize submodules after cloning
git submodule update --init --recursive

# Update all submodules to latest remote commit
git submodule update --remote
git submodule update --remote vendor/lib  # Specific submodule

# Run a command in all submodules
git submodule foreach git pull origin main

# Remove a submodule
git submodule deinit vendor/lib
git rm vendor/lib
git commit -m "Remove lib submodule"

Keep your Git knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever