A free, open-source distributed version-control system that tracks changes in any collection of files.

Table of Contents#

  1. Overview
  2. Installation
  3. Configuration
  4. Essential Commands
  5. Branching and Merging
  6. Synchronizing With Remote
  7. Stashing
  8. Interactive Rebase
  9. Cherry-pick
  10. Reflog
  11. GPG Commit Signing
  12. Git Hooks
  13. .gitignore
  14. .gitattributes
  15. Git LFS
  16. Aliases
  17. Troubleshooting
  18. See Also
  19. Sources

1. Overview#

Git is a distributed version-control system originally authored by Linus Torvalds in 2005 for Linux kernel development. Every Git working directory contains the full history and version-tracking capabilities, independent of network access or a central server. Git tracks content by storing snapshots, not diffs, which makes branching and merging extremely fast.

Key concepts:

ConceptDescription
Working treeThe files you see and edit on disk
Staging area (index)A buffer between the working tree and the repository; holds the next commit
Repository (.git)The full history stored in the .git directory
CommitAn immutable snapshot of the staging area with metadata (author, date, message, parent)
BranchA movable pointer to a commit
HEADA pointer to the current branch tip (or a detached commit)
RemoteA reference to another copy of the repository (e.g., origin)

2. Installation#

Linux#

# Debian/Ubuntu
sudo apt install git

# Arch Linux
sudo pacman -S git

# RHEL/Fedora
sudo dnf install git

macOS#

brew install git

Windows#

winget install git

For alternative packages and binary installers see https://git-scm.com/downloads.

3. Configuration#

User Identity#

git config --global user.name  "Your Name"
git config --global user.email "you@example.com"

Credential Storage#

HelperDescriptionExample
storeSaves credentials unencrypted to ~/.git-credentialsgit config --local credential.helper store
cacheHolds credentials in memory for N seconds (default 900)git config --global credential.helper 'cache --timeout=3600'
osxkeychainUses macOS Keychaingit config --global credential.helper osxkeychain
manager / manager-coreCross-platform GUI helper supporting PATs, OAuthgit config --global credential.helper manager-core

Personal Access Tokens#

Modern Git hosting services deprecate password authentication over HTTPS. Options:

  1. Generate a PAT: GitHub (Settings, Developer settings, Personal access tokens), GitLab (User settings, Access tokens), Bitbucket (Personal settings, App passwords). Grant only required scopes (e.g., repo, write:packages).

  2. Use the token when cloning/pushing:

    git clone https://<token>@github.com/<user>/<repo>.git

    Or input the token when prompted by your credential helper.

  3. Store the token with one of the helpers above, or keep it ephemeral with cache.

  4. Alternative: switch to SSH keys (ssh-keygen -t ed25519) and add the public key in your host profile.

4. Essential Commands#

CommandPurpose
git initCreate a repository in the current directory
git clone <url>Copy a remote repository locally
git statusShow unstaged and staged changes
git add <file>Stage file for next commit
git rm <file>Delete file and stage removal
git mv <old> <new>Rename/move file and stage the change
git commit -m "msg"Record staged snapshot
git commitOpen editor for detailed message
git diffShow unstaged changes
git diff --stagedShow staged changes (what will be committed)
git logShow commit history
git log --oneline --graphCompact history with branch visualization
git show <commit>Show a specific commit's diff and metadata

5. Branching and Merging#

# List branches (* marks current)
git branch

# Create and switch to a new branch
git switch -c <branch>

# Switch to an existing branch
git switch <branch>

# Fast-forward merge
git merge --ff-only <branch>

# Merge with a merge commit (no fast-forward)
git merge --no-ff <branch>

# Rebase current branch on top of another
git rebase <branch>

# Rebase instead of merge on pull
git pull --rebase origin main

# Delete a local branch (safe, refuses if unmerged)
git branch -d <branch>

# Delete a remote branch
git push origin --delete <branch>

# Rename current branch
git branch -m <new-name>

Merge Conflict Resolution#

# After a conflict, edit the files, then:
git add <resolved-file>
git merge --continue

# Or abort the merge entirely
git merge --abort

6. Synchronizing With Remote#

# Track a newly created branch
git push -u origin <branch>

# Fetch without merging
git fetch --all --prune

# Push all local branches
git push --all origin

# Push tags
git push --tags

# Set upstream for current branch
git branch --set-upstream-to=origin/<branch>

# Show remote URLs
git remote -v

# Add a new remote
git remote add <name> <url>

7. Stashing#

Stash saves uncommitted changes (both staged and unstaged) and reverts the working tree to HEAD:

# Stash current changes
git stash

# Stash with a descriptive message
git stash push -m "work in progress on feature X"

# Stash including untracked files
git stash push --include-untracked

# Stash only staged changes
git stash push --staged

# List all stashes
git stash list

# Apply the most recent stash (keep stash in list)
git stash apply

# Apply and remove the most recent stash
git stash pop

# Apply a specific stash by index
git stash apply stash@{2}

# Show what a stash contains
git stash show -p stash@{0}

# Drop a specific stash
git stash drop stash@{1}

# Clear all stashes
git stash clear

# Create a branch from a stash
git stash branch <new-branch> stash@{0}

8. Interactive Rebase#

Interactive rebase rewrites history by reordering, editing, squashing, or dropping commits:

# Rebase the last 5 commits interactively
git rebase -i HEAD~5

# Rebase onto a branch interactively
git rebase -i main

The editor opens with a list of commits. Each line starts with a command:

CommandShortDescription
pickpKeep the commit as-is
rewordrKeep the commit but edit its message
editePause at this commit for amending
squashsMeld into the previous commit, combine messages
fixupfMeld into the previous commit, discard this message
dropdRemove the commit entirely
execxRun a shell command after this commit

Example workflow for squashing:

# Start interactive rebase for last 3 commits
git rebase -i HEAD~3

# In the editor, change "pick" to "squash" on commits to combine:
# pick abc1234 Add feature
# squash def5678 Fix typo in feature
# squash ghi9012 Another fix
# Save and close, then edit the combined commit message

Warning: never rebase commits that have been pushed to a shared branch. This rewrites history and will cause problems for collaborators.

9. Cherry-pick#

Cherry-pick applies a specific commit from one branch onto the current branch:

# Apply a single commit
git cherry-pick <commit-hash>

# Apply multiple commits
git cherry-pick <hash1> <hash2> <hash3>

# Apply a range of commits (exclusive start, inclusive end)
git cherry-pick <start-hash>..<end-hash>

# Cherry-pick without committing (stage changes only)
git cherry-pick --no-commit <commit-hash>

# Abort a cherry-pick in progress
git cherry-pick --abort

# Continue after resolving conflicts
git cherry-pick --continue

10. Reflog#

The reflog records every change to HEAD, including commits, rebases, resets, and checkouts. It is the safety net for recovering "lost" commits:

# Show reflog for HEAD
git reflog

# Show reflog for a specific branch
git reflog show <branch>

# Recover a commit after a bad reset
git reflog
# Find the commit hash, then:
git reset --hard <commit-hash>

# Recover a deleted branch
git reflog
# Find the last commit of the branch, then:
git branch <branch-name> <commit-hash>

# Show reflog with timestamps
git reflog --date=relative

Reflog entries expire after 90 days (30 days for unreachable commits) by default. Adjust with:

git config gc.reflogExpire 180.days
git config gc.reflogExpireUnreachable 90.days

11. GPG Commit Signing#

Signed commits provide cryptographic proof of authorship:

# List your GPG keys
gpg --list-secret-keys --keyid-format=long

# Configure Git to use your key
git config --global user.signingkey <key-id>

# Sign a single commit
git commit -S -m "signed commit"

# Sign all commits by default
git config --global commit.gpgsign true

# Sign tags
git tag -s v1.0 -m "signed release"

# Verify a commit signature
git verify-commit <commit-hash>

# Verify a tag signature
git verify-tag v1.0

# Show signatures in log
git log --show-signature

For SSH signing (Git 2.34+):

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub

12. Git Hooks#

Hooks are scripts that run automatically at specific points in the Git workflow. They live in .git/hooks/ (local) or can be shared via a configured hooks directory.

Common Hooks#

HookTriggerUse case
pre-commitBefore commit is createdLinting, formatting, running tests
prepare-commit-msgAfter default message, before editor opensAuto-populate commit message templates
commit-msgAfter message is enteredEnforce commit message format
post-commitAfter commit completesNotifications, CI triggers
pre-pushBefore push to remoteRun full test suite, prevent force-push
pre-rebaseBefore rebase startsPrevent rebasing published branches
post-mergeAfter merge completesReinstall dependencies, rebuild
post-checkoutAfter checkout/switchUpdate submodules, regenerate files

Creating a Hook#

# Create a pre-commit hook that runs a linter
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
echo "Running linter..."
npm run lint
if [ $? -ne 0 ]; then
    echo "Lint failed. Commit aborted."
    exit 1
fi
EOF
chmod +x .git/hooks/pre-commit

Sharing Hooks#

# Store hooks in the repository and configure the path
mkdir -p .githooks
# Move hooks into .githooks/
git config core.hooksPath .githooks

13. .gitignore#

Create a .gitignore file to exclude generated and ephemeral files:

# Node
node_modules/
dist/

# Python
__pycache__/
*.py[cod]
.venv/

# IDE
.idea/
.vscode/
*.swp

# OS
.DS_Store
Thumbs.db

# Secrets (never commit these)
.env
*.pem

Useful patterns:

PatternMeaning
*.logIgnore all .log files
!important.logException: track this specific file
build/Ignore entire directory
**/tempIgnore "temp" directory at any depth
doc/*.txtIgnore .txt in doc/ only (not subdirectories)
doc/**/*.txtIgnore .txt in doc/ and all its subdirectories
# Check why a file is ignored
git check-ignore -v <file>

# List all ignored files
git status --ignored

14. .gitattributes#

.gitattributes controls per-path settings for line endings, diff behavior, merge strategies, and LFS tracking:

# Normalize line endings (LF in repo, native on checkout)
* text=auto

# Force LF for shell scripts
*.sh text eol=lf

# Force CRLF for Windows batch files
*.bat text eol=crlf

# Binary files (no diff, no merge)
*.png binary
*.jpg binary
*.pdf binary

# Custom diff driver for specific files
*.csv diff=csv

# LFS tracking
*.psd filter=lfs diff=lfs merge=lfs -text

# Linguist overrides (for GitHub language stats)
docs/** linguist-documentation
vendor/** linguist-vendored

Common use cases:

# Apply .gitattributes retroactively to normalize line endings
git add --renormalize .
git commit -m "Normalize line endings"

15. Git LFS#

Git LFS (Large File Storage) handles large assets outside the main repository:

# Install LFS (once per machine)
git lfs install

# Track file types
git lfs track "*.psd"
git lfs track "*.zip"

# Verify tracking (shows tracked files)
git lfs ls-files

# Show LFS configuration
cat .gitattributes

# Fetch LFS objects for current branch
git lfs pull

# Migrate existing files to LFS
git lfs migrate import --include="*.psd" --everything

Commit and push as usual; large files upload via the LFS endpoint.

16. Aliases#

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.last "log -1 HEAD"
git config --global alias.unstage "reset HEAD --"
git config --global alias.amend "commit --amend --no-edit"

Troubleshooting#

IssueCauseSolution
"Permission denied" on git pushInvalid PAT scopes or SSH key not loadedVerify PAT scopes or run ssh-add; clear credential cache with git credential-cache exit
Wrong user in commitsGlobal config overrides local identitySet per-repo: git config user.email "correct@email.com", or amend: git commit --amend --author="Name <mail>"
Merge conflictsDivergent changes to the same linesEdit conflicted files, git add, then git merge --continue
"detached HEAD" stateChecked out a tag or specific commitCreate a branch: git switch -c <branch-name>
Accidentally committed to wrong branchCommits on wrong branchgit stash, git switch <correct-branch>, git stash pop or use cherry-pick
Need to undo last commit (keep changes)Premature commitgit reset --soft HEAD~1 (keeps changes staged)
Large repo clone is slowFull history is largeUse git clone --depth 1 for shallow clone, git clone --filter=blob:none for blobless
.gitignore not working for tracked filesFile was tracked before the ignore rule was addedgit rm --cached <file>, commit, then the ignore rule applies
Reflog is emptyFreshly cloned repo has no local historyReflog only records local operations; it will populate as you work

See Also#

Sources#