Freebie

Best practices

This page covers three real-world git workflows: managing a software application as part of a team, tracking system administration files, and configuring a new workstation from scratch. Each workflow builds on the commands covered in the other pages in this section.

Environment setup

Run these steps once on any new machine before working with git.

1. Install git and set your identity

sudo apt install git-all

git config --global user.name "Your Name"
git config --global user.email you@example.com
git config --global init.defaultBranch main
git config --global core.autocrlf input    # Linux/macOS
git config --global pull.rebase true       # keep history linear on pull
git config --global color.ui auto

2. Generate an SSH key and add it to your git host

SSH authentication is faster and more secure than HTTPS with a password. Generate a key pair and copy the public key to GitHub or GitLab:

# generate an ed25519 key (recommended over RSA)
ssh-keygen -t ed25519 -C "you@example.com"

# start the SSH agent and load the key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

# print the public key to copy into GitHub -> Settings -> SSH Keys
cat ~/.ssh/id_ed25519.pub

Test the connection after adding the key:

ssh -T git@github.com
# Hi username! You've successfully authenticated.

3. Set up a global .gitignore

A global .gitignore keeps editor files, OS artifacts, and secrets out of every repository without editing each project’s own .gitignore:

# create the file
touch ~/.gitignore_global

# tell git to use it
git config --global core.excludesfile ~/.gitignore_global

Common entries to add:

# macOS
.DS_Store

# Linux
*~

# editors
.vscode/
.idea/
*.swp

# secrets (never commit these)
.env
.env.local
*.pem
*.key

4. Add useful aliases

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.lg 'log --oneline --graph --all --decorate'
git config --global alias.last 'log -1 HEAD'

Managing a software application

This workflow, known as GitHub Flow, is the standard approach for teams working on a continuously deployed application. Every change moves through a feature branch and a pull request before reaching main.

main:    A---B-----------M---N      (protected, always deployable)
                  \     /
feature:           C---D            (your work in isolation)

Daily workflow

Start each session

Pull the latest changes from main before creating a branch. This reduces the chance of conflicts later:

git checkout main
git pull

Create a branch for each unit of work

Name branches so the purpose is clear from the name alone:

# feature
git checkout -b feature/user-authentication

# bug fix
git checkout -b fix/login-timeout

# documentation update
git checkout -b docs/api-reference

Commit early and often

Small, focused commits are easier to review and easier to revert if something breaks. Write the commit message as a short imperative sentence describing what the commit does, not what you did:

git add -p                          # stage only what belongs in this commit
git commit -m 'add JWT validation middleware'

A useful commit message structure:

<type>: <short summary under 72 characters>

<optional body explaining why, not what>

Common types: feat, fix, docs, refactor, test, chore.

Keep your branch up to date

If main has moved ahead while you were working, rebase your branch onto it rather than merging. This keeps the feature branch history linear and makes the eventual merge clean:

git fetch origin
git rebase origin/main

Push and open a pull request

git push -u origin feature/user-authentication

Then open a pull request on GitHub or GitLab. Keep pull requests small enough to review in one sitting. A good rule of thumb is under 400 lines changed.

After the pull request merges

Delete the local and remote branches to keep the repository clean:

git checkout main
git pull
git branch -d feature/user-authentication
git push origin --delete feature/user-authentication

Tracking system administration files

Sysadmins regularly edit files in /etc, ~/.config, and home directories. Tracking these in git gives you a history of every change, a way to roll back a misconfiguration, and a record of who changed what and when.

Option 1: Track a single config directory

If your changes are confined to one directory, initialize git inside it:

cd /etc/nginx
sudo git init
sudo git add nginx.conf sites-available/
sudo git commit -m 'initial nginx configuration'

Push to a private remote so the history is backed up and accessible from other machines:

sudo git remote add origin git@github.com:username/nginx-config.git
sudo git push -u origin main

Option 2: Track dotfiles with a bare repository

A bare repository is a git database without a working tree. This technique lets you track files anywhere in your home directory without nesting .git inside ~:

# initialize a bare repo in a hidden directory
git init --bare ~/.dotfiles

# create an alias so you do not have to type the flags every time
echo "alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'" \
  >> ~/.bashrc
source ~/.bashrc

# hide untracked files so git status is not noisy
dotfiles config --local status.showUntrackedFiles no

# add your configuration files
dotfiles add ~/.bashrc ~/.vimrc ~/.ssh/config
dotfiles commit -m 'initial dotfiles'
dotfiles remote add origin git@github.com:username/dotfiles.git
dotfiles push -u origin main

To restore your environment on a new machine:

git clone --bare git@github.com:username/dotfiles.git ~/.dotfiles
alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles checkout

Protecting secrets

Never commit passwords, API keys, or private keys. Add a global ignore pattern for common secret file names (see Environment setup above) and audit staged files before every commit:

git diff --staged    # review exactly what is going into the commit

If you accidentally commit a secret, treat it as compromised immediately: revoke and rotate the credential. Removing it from history with git filter-branch or git filter-repo does not protect it if the commit was ever pushed to a remote.

Recording changes to system files

When you edit a system file, commit with a message that explains why the change was made, not just what changed. Future you and your colleagues will thank you:

# vague: describes what, not why
git commit -m 'update sshd_config'

# clear: explains the reason
git commit -m 'disable password auth, require SSH keys per security audit'

Recovering from common mistakes

MistakeRecovery
Committed to main instead of a branchgit branch feature/oops, git reset --hard HEAD~1, switch to the new branch
Committed a secretRevoke the credential immediately, then remove with git filter-repo
Pushed a bad commit to a shared branchgit revert <hash> creates a new commit that undoes it without rewriting history
Lost commits after a resetgit reflog shows every HEAD movement; check out the commit to recover it
Merge conflict you cannot resolvegit merge --abort cancels the merge and restores the pre-merge state