Setting Up Git: SSH Keys and Global Configuration

TT
Emily Ross
Setting Up Git: SSH Keys and Global Configuration

Setting Up Git: SSH Keys and Global Configuration

A properly configured Git setup saves hours every week. SSH key authentication means no more typing passwords; a good global .gitconfig enforces consistent commit identity, enables useful aliases, and configures sensible defaults. This guide covers everything from generating a key pair to managing multiple GitHub accounts on the same machine.


Why SSH Over HTTPS

HTTPS authentication for Git requires a Personal Access Token (PAT) instead of your password-GitHub removed password auth in 2021. PATs are fine but expire, and you need to store them somewhere or retype them. SSH keys are:

  • More convenient: authenticate once with ssh-agent, then never type a password
  • More secure: private key never leaves your machine (vs. PAT which can be stolen if stored)
  • Revocable per device: each machine has its own key, so revoking one device doesn't affect others

Generating an SSH Key Pair

Ed25519 is the recommended algorithm-smaller, faster, and more secure than RSA 4096.

bash
# Generate an Ed25519 key pair
ssh-keygen -t ed25519 -C "your-email@example.com"

# You'll be prompted:
# Enter file in which to save the key (/home/you/.ssh/id_ed25519): [Enter for default]
# Enter passphrase (empty for no passphrase): [use a strong passphrase]
# Enter same passphrase again: [repeat]

# Result:
# Private key: ~/.ssh/id_ed25519         ← never share this
# Public key:  ~/.ssh/id_ed25519.pub     ← this goes to GitHub

Always use a passphrase. It encrypts the private key on disk. If your machine is stolen, the attacker cannot use the key without the passphrase.

bash
# View your public key (this is safe to share)
cat ~/.ssh/id_ed25519.pub
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... your-email@example.com

If you're on a machine that doesn't support Ed25519 (rare):

bash
# RSA fallback (use 4096 bits minimum)
ssh-keygen -t rsa -b 4096 -C "your-email@example.com"

Adding the Key to ssh-agent

ssh-agent holds your decrypted private key in memory so you only enter the passphrase once per session.

bash
# Start ssh-agent if not running
eval "$(ssh-agent -s)"
# Agent pid 1234

# Add your key to the agent
ssh-add ~/.ssh/id_ed25519
# Enter passphrase for /home/you/.ssh/id_ed25519: ***
# Identity added: /home/you/.ssh/id_ed25519

# List keys in agent
ssh-add -l
# 256 SHA256:abc123... your-email@example.com (ED25519)

Persist ssh-agent Across Sessions

On Linux (add to ~/.bashrc or ~/.zshrc):

bash
# Auto-start ssh-agent and load keys
if [ -z "$SSH_AUTH_SOCK" ]; then
  eval "$(ssh-agent -s)" > /dev/null
  ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi

On macOS (uses system Keychain to persist passphrase):

bash
# Add to ~/.ssh/config
Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519

# Add key once (macOS Keychain stores passphrase)
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

On Windows (PowerShell, enable ssh-agent service):

powershell
# Enable and start the OpenSSH Authentication Agent service
Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent

# Add key
ssh-add $env:USERPROFILE\.ssh\id_ed25519

Adding Your Key to GitHub

bash
# Copy public key to clipboard
# macOS:
pbcopy < ~/.ssh/id_ed25519.pub

# Linux (requires xclip):
xclip -selection clipboard < ~/.ssh/id_ed25519.pub

# Windows PowerShell:
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | Set-Clipboard

# Or just print and copy manually:
cat ~/.ssh/id_ed25519.pub

On GitHub:

  1. Settings -> SSH and GPG keys -> New SSH key
  2. Title: descriptive name for this device (e.g., "MacBook Pro Work 2025")
  3. Key type: Authentication Key
  4. Paste your public key
  5. Add SSH key

Verify it works:

bash
ssh -T git@github.com
# Hi yourusername! You've successfully authenticated, but GitHub does not provide shell access.

Global Git Configuration

bash
# Identity (required - appears in every commit)
git config --global user.name "Your Name"
git config --global user.email "your-email@example.com"

# Default branch name (avoid "master")
git config --global init.defaultBranch main

# Default editor (use your preferred editor)
git config --global core.editor "code --wait"  # VS Code
git config --global core.editor "vim"           # Vim
git config --global core.editor "nano"          # Nano

# Merge strategy: use "merge" or "rebase" when pulling
git config --global pull.rebase false  # merge (default)
# git config --global pull.rebase true   # rebase (preferred by many teams)

# Push: only push the current branch, not all matching branches
git config --global push.default current

# Diff and merge tool
git config --global diff.tool vscode
git config --global merge.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'

# Credential helper (cache credentials for 1 hour)
git config --global credential.helper cache
git config --global credential.helper 'cache --timeout=3600'

# On macOS, use Keychain for HTTPS credentials
git config --global credential.helper osxkeychain

# On Windows, use Git Credential Manager
git config --global credential.helper manager

Useful Aliases

bash
# Short status
git config --global alias.st "status -sb"

# Pretty one-line log graph
git config --global alias.lg "log --oneline --graph --decorate --all"

# Last commit details
git config --global alias.last "log -1 HEAD --stat"

# Undo last commit (keep changes staged)
git config --global alias.undo "reset --soft HEAD~1"

# List branches sorted by last commit
git config --global alias.recent "branch --sort=-committerdate"

# Amend without editing message
git config --global alias.amend "commit --amend --no-edit"

# Show changes in last commit
git config --global alias.show-last "diff HEAD~1 HEAD"

# Usage after aliases:
# git st    -> git status -sb
# git lg    -> pretty log graph
# git undo  -> undo last commit

Your full .gitconfig lives at ~/.gitconfig:

ini
[user]
    name = Your Name
    email = your-email@example.com

[init]
    defaultBranch = main

[core]
    editor = code --wait
    autocrlf = input     # LF on commit, keep as-is on checkout (Linux/macOS)
    # autocrlf = true    # Use on Windows: CRLF on checkout, LF on commit

[pull]
    rebase = false

[push]
    default = current

[alias]
    st = status -sb
    lg = log --oneline --graph --decorate --all
    last = log -1 HEAD --stat
    undo = reset --soft HEAD~1
    recent = branch --sort=-committerdate

[credential]
    helper = osxkeychain  # or: manager (Windows), cache (Linux)

Multiple GitHub Accounts on One Machine

Common scenario: personal GitHub account and work GitHub account on the same laptop.

Step 1: Generate Separate Keys

bash
# Personal key
ssh-keygen -t ed25519 -C "personal@email.com" -f ~/.ssh/id_ed25519_personal

# Work key
ssh-keygen -t ed25519 -C "work@company.com" -f ~/.ssh/id_ed25519_work

Add both public keys to their respective GitHub accounts.

Step 2: Configure SSH Aliases

bash
# ~/.ssh/config
Host github-personal
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_personal
    AddKeysToAgent yes

Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_work
    AddKeysToAgent yes

Step 3: Use the Right Host When Cloning

bash
# Personal repo: use "github-personal" instead of "github.com"
git clone git@github-personal:yourusername/personal-project.git

# Work repo: use "github-work"
git clone git@github-work:company/work-project.git

# Fix an existing repo's remote
git remote set-url origin git@github-work:company/repo.git

Step 4: Per-Directory Git Identity

Use conditional includes to automatically apply work identity in work directories:

ini
# ~/.gitconfig
[user]
    name = Your Name
    email = personal@email.com

[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work
ini
# ~/.gitconfig-work
[user]
    email = work@company.com
    name = Your Name

Now any repo under ~/work/ automatically uses the work email in commits.


Signing Commits with GPG

Signed commits prove that commits actually came from you, not someone who got access to your machine.

bash
# Generate a GPG key (if you don't have one)
gpg --full-generate-key
# Choose: ECC and ECC (Ed25519), key does not expire, your name and email

# List your GPG keys
gpg --list-secret-keys --keyid-format=long
# sec   ed25519/ABC123DEF456 2024-01-01 [SC]

# Export public key and add to GitHub (Settings -> GPG keys)
gpg --armor --export ABC123DEF456

# Configure Git to use your key
git config --global user.signingkey ABC123DEF456
git config --global commit.gpgsign true    # sign all commits
git config --global tag.gpgsign true       # sign all tags

Verify a signed commit:

bash
git verify-commit HEAD
# gpg: Good signature from "Your Name <email@example.com>"

Frequently Asked Questions

Q: My ssh -T git@github.com says "Permission denied (publickey)". How do I debug?

Run ssh -vT git@github.com for verbose output. Common causes: (1) The key is not added to ssh-agent - run ssh-add ~/.ssh/id_ed25519. (2) The public key wasn't added to GitHub, or was added to the wrong account. (3) The ~/.ssh/config file has wrong permissions - it must be 600: chmod 600 ~/.ssh/config. (4) You're using the HTTPS URL instead of SSH - verify with git remote -v and change with git remote set-url origin git@github.com:user/repo.git.

Q: Should I use the same SSH key on every machine or generate one per machine?

Generate one key per machine. This follows the principle of least privilege: if one machine is compromised, you revoke only that key without affecting others. It also gives you an audit trail on GitHub-you can see which device was used for authentication. Name your keys descriptively in GitHub: "MacBook Pro Work 2025", "Home Desktop", "CI Server".

Q: What is core.autocrlf and should I set it?

Windows uses CRLF (\r\n) line endings; Linux/macOS use LF (\n). autocrlf = true (Windows) converts LF->CRLF on checkout and CRLF->LF on commit, keeping your editor happy while keeping the repo LF. autocrlf = input (Linux/macOS) converts CRLF->LF on commit only. Teams with mixed OS contributors should also add a .gitattributes file to enforce LF for all text files regardless of OS: * text=auto eol=lf.

Q: I accidentally committed with the wrong email address. How do I fix it?

For the most recent commit: git commit --amend --reset-author (updates the author to match your current config). For multiple commits before pushing: git rebase -i HEAD~N then git commit --amend --reset-author for each. For commits already pushed to a shared branch: this requires a force-push and coordination with your team since it rewrites history. Going forward, use includeIf in .gitconfig to prevent this automatically.