Git & GitHub β Version Control for DevOps
Before you start: basic command-line comfort (cd, ls, creating/editing a file) is the only real requirement β no prior version-control experience is assumed. See the Prerequisites tab if you want the full checklist.
Why Git for DevOps?
Every DevOps workflow starts with Git. Infrastructure-as-Code, application code, CI/CD pipeline definitions, Kubernetes manifests, Helm charts β all live in Git. GitOps (ArgoCD, FluxCD) uses Git as the single source of truth for cluster state.
Analogy β Think of Git like the "track changes" history in a shared document, but far more powerful: instead of one linear history everyone edits on top of, each person can branch off and work on their own version privately, then merge back in when it's ready. A commit is a saved checkpoint (like hitting "save" on a specific, named version); a branch is a separate copy of the document you can experiment on without touching the shared version; a merge is combining someone's branched edits back into the main document, automatically where possible and flagging a conflict only where two people genuinely edited the exact same spot differently.
Working Directory β git add β Staging Area β git commit β Local Repo β git push β Remote
(raw edits) (what's about (permanent (GitHub/
to be saved) checkpoint) GitLab)
Working Directory
Raw edits
β
Staging Area
git add β what's about to be saved
β
Local Repo
git commit β permanent checkpoint
β
Remote
git push β GitHub/GitLab
Core Concepts
Repository (Repo)
A directory tracked by Git. Contains:
β’Working tree: current files you see and edit
β’.git/ directory: history, refs, objects (the actual repo)
β’Remote: copy on GitHub/GitLab/Bitbucket
Three Areas
Working Directory β (git add) β Staging Area β (git commit) β Local Repo β (git push) β Remote
Branches
Lightweight, movable pointer to a commit. Default: main (formerly master).
Commits
Snapshots of staged changes + metadata (author, timestamp, message, parent commit).
Essential Commands
bash
# Setup (first time)
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
# Start
git init # new repo
git clone https://github.com/org/repo.git # clone existing
# Daily workflow
git status # what changed?
git add file.txt # stage specific file
git add . # stage all changes
git add -p # interactive stage (hunk by hunk)
git commit -m "feat: add search feature"
git push origin main
git pull origin main # fetch + merge
# Branching
git branch feature/search # create branch
git checkout feature/search # switch to it
git checkout -b feature/search # create + switch (shorthand)
git switch -c feature/search # modern syntax
# Merging
git merge feature/search # merge into current branch
git rebase main # rebase current branch onto main
git cherry-pick abc1234 # apply single commit
# Viewing history
git log --oneline --graph --all # visual branch graph
git log -p # show diffs
git diff main...feature/search # three dots: only what changed ON the branch since it diverged
# from main (excludes anything landed on main afterward) --
# different from `git diff main..feature/search` (two dots),
# which is a flat diff between the two branch tips directly
git blame app.py # who changed each line?
# Undoing
git restore file.py # discard working dir changes
git restore --staged file.py # unstage (keep changes)
git revert abc1234 # create new commit that undoes abc1234
git reset --soft HEAD~1 # undo last commit, keep staged
git reset --hard HEAD~1 # undo last commit AND changes (careful!)
# Stashing
git stash # save current changes temporarily
git stash pop # restore most recent stash
git stash list # list all stashes
# Tags (for versioning)
git tag v1.2.3 # lightweight tag
git tag -a v1.2.3 -m "Release v1.2.3" # annotated tag
git push origin v1.2.3 # push specific tag
git push origin --tags # push all tags
Branching Strategies
Git Flow
main/develop/feature/release/hotfix branches. Structured, more overhead
GitHub Flow
main + short-lived feature branches. Simpler, works well with CI/CD
Trunk-Based
Everyone commits to main daily. Needs strong CI/CD + feature flags
Git Flow
β’main β production-ready
β’develop β integration branch
β’feature/* β new features (from develop)
β’release/* β release candidates (from develop β main)
β’hotfix/* β urgent fixes (from main)
GitHub Flow (simpler, recommended for CI/CD)
β’main β always deployable
β’feature/* β short-lived feature branches
β’PR β review β merge β auto-deploy
β’Works well with CI/CD pipelines
Trunk-Based Development (for DevOps teams)
β’Everyone commits to main daily (short-lived branches, max 1β2 days)
β’Feature flags for incomplete features
β’Requires strong CI/CD and automated testing
Git for DevOps β Key Patterns
Conventional Commits
feat: add user authentication
fix: resolve database connection timeout
docs: update deployment guide
ci: add GitHub Actions workflow
refactor: extract payment service
test: add unit tests for order processing
chore: update dependencies
.gitignore for Infrastructure Repos
# Terraform
*.tfvars # may contain secrets
*.tfstate # must be in remote state, not git
*.tfstate.backup
.terraform/ # local provider cache
# General
.env # environment files with secrets
*.key # private keys
*.pem
secrets/
__pycache__/
node_modules/
.DS_Store
Git Hooks for DevOps
bash
# .git/hooks/pre-commit β run checks before every commit
#!/bin/bash
# Run terraform fmt check (needs terraform installed)
terraform fmt -check -recursive
# Run shellcheck on scripts (needs shellcheck installed)
find . -name "*.sh" -exec shellcheck {} \;
# Detect secrets (needs detect-secrets installed; first run needs a
# baseline: detect-secrets scan > .secrets.baseline, then compare against it)
detect-secrets scan
After creating this file: chmod +x .git/hooks/pre-commit, or it silently never runs. And critically β .git/hooks/ is not version-controlled. Committing this script to the repo does not distribute it to teammates or CI; it lives only in your own local clone. For a hook the whole team actually runs, point core.hooksPath at a tracked directory (git config core.hooksPath .githooks, scripts committed under .githooks/), or use a framework built for this (Husky, the pre-commit framework) that installs hooks as part of normal project setup.
GitHub Actions Trigger Patterns
yaml
on:
push:
branches: [main] # trigger on push to main
paths:
- 'app/**' # only when app/ changes
- '!**.md' # ignore markdown changes
pull_request:
branches: [main]
workflow_dispatch: # manual trigger
schedule:
- cron: '0 2 * * *' # 2am daily
Common Issues & Fixes
|-------|---------|
| Wrong email in commit | `git commit --amend --author="Name <email>"` |
|---|
| Need to undo pushed commit | git revert HEAD && git push (safe) |
| Merge conflict | Edit file, git add, git commit |
| Detached HEAD | git checkout -b new-branch to save work |
| Pushed secret by accident | Rotate the secret immediately, then use BFG Repo Cleaner |
| Large file accidentally committed | git filter-repo or BFG Repo Cleaner |
Try It (2 Minutes)
1.In any empty directory, run:
`bash
git init
echo "hello" > file.txt
git add file.txt
git commit -m "first commit"
`
2.Now edit file.txt (change "hello" to "hello world") and run git status β notice it shows the file as modified, but git diff --staged shows nothing yet, because the new edit hasn't been staged.
3.Run git add file.txt again, then git diff --staged β now it shows the change. This is the exact "why didn't my commit include that change" trap covered in the Fundamentals tab: staging is a snapshot taken at the moment you run git add, not a live link to the file.