Skip to content

Portal | Level: L0: Entry | Topics: Git | Domain: DevOps & Tooling

Git for DevOps Engineers - Primer

Why This Matters

Git is the version control system behind virtually every modern software and infrastructure project. As a DevOps engineer, you'll manage Terraform modules, Ansible playbooks, CI/CD pipelines, Dockerfiles, and Kubernetes manifests - all in Git. You need to go beyond git add; git commit; git push and understand branching strategies, conflict resolution, and recovery techniques.

Name origin: Git was created by Linus Torvalds in April 2005 — built in just two weeks after the Linux kernel project lost access to the proprietary BitKeeper VCS. The name "git" is British slang for a difficult person; Torvalds joked "I name all my projects after myself."

Fun fact: Git's design goals were speed, data integrity, and support for distributed workflows. Every Git repository is a full copy of the history — there is no single point of failure. This distributed model was revolutionary compared to centralized systems like SVN and CVS.

Core Concepts

The Three Areas

Git has three local areas where changes live:

Working Directory  -->  Staging Area (Index)  -->  Local Repository
   (your files)         (git add)                  (git commit)
                                                        |
                                                        v
                                                   Remote Repository
                                                    (git push)
  • Working directory: your actual files on disk
  • Staging area: changes queued for the next commit (git add)
  • Repository: committed snapshots (.git/ directory)

Every commit is a snapshot of the entire project, identified by a SHA-1 hash. Commits form a directed acyclic graph (DAG) - each commit points to its parent(s).

Under the hood: Git stores objects in .git/objects/ using content-addressed storage. There are four object types: blobs (file content), trees (directories), commits (snapshots + metadata), and tags (annotated pointers). Identical files across commits share the same blob — Git never duplicates content. A branch is just a 41-byte file containing a commit SHA.

Branching Models

Feature branch workflow (most common):

main ─────────────────────────────────
       \                    /
        feature/add-auth ──
- main is always deployable - Features developed on branches, merged via pull request - Branch naming: feature/, bugfix/, hotfix/, release/

Git Flow (more formal): - main - production releases - develop - integration branch - feature/* - new features - release/* - release prep - hotfix/* - production fixes

Trunk-based development (modern, CI/CD-friendly): - Everyone commits to main (or short-lived branches merged within hours) - Feature flags instead of long-lived branches - Requires strong CI/CD and test coverage

Merge vs Rebase

Merge creates a merge commit joining two histories:

git checkout main
git merge feature/add-auth
# Creates a merge commit with two parents
- Preserves complete history - Non-destructive (doesn't rewrite commits) - Can create a cluttered history with many merge commits

Rebase replays your commits on top of another branch:

git checkout feature/add-auth
git rebase main
# Moves your commits to the tip of main
- Creates a linear history (cleaner git log) - Rewrites commit hashes (never rebase commits that others have pulled) - Use for local cleanup before merging

Golden rule: Never rebase commits that have been pushed to a shared branch.

Remember: Merge vs Rebase decision: "Rebase for cleanup, merge for history." Rebase your local commits before pushing to get a clean linear history. Merge shared branches to preserve the record of parallel development. If in doubt, merge is always safe — rebase rewrites history and can cause problems for collaborators.

Under the hood: When you rebase, Git replays each commit on the new base by creating new commit objects with new SHA-1 hashes. The original commits still exist in the reflog for 90 days (default gc.reflogExpire). This is why git reflog can save you after a bad rebase — the original commits are not truly gone until garbage collection runs.

Conflict Resolution

Debug clue: The most common conflict scenario in DevOps: two people modify the same Terraform resource block or the same Helm values file. Prevention is better than cure — split infrastructure files by concern (one file per resource type, one values file per environment) to minimize the chance of two people editing the same lines.

Conflicts happen when two branches modify the same lines. Git marks them:

<<<<<<< HEAD
your changes
=======
their changes
>>>>>>> feature/other-branch

Resolution workflow:

git merge feature/other-branch
# CONFLICT in file.txt
# 1. Open file.txt, find conflict markers
# 2. Edit to keep what you want (remove markers)
# 3. Stage the resolved file:
git add file.txt
# 4. Complete the merge:
git commit

.gitignore

Gotcha: .gitignore only affects untracked files. If a file is already committed to the repo, adding it to .gitignore does nothing — Git continues tracking it. To stop tracking a committed file: git rm --cached <file>, add it to .gitignore, then commit. This is the #1 reason secrets leak into Git history: the file was committed before the ignore rule was added.

Tells Git which files to ignore. Critical for DevOps:

# Terraform
*.tfstate
*.tfstate.backup
.terraform/
*.tfvars          # Contains secrets

# Ansible
*.retry

# Environment
.env
*.pem
*.key

# IDE
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db

Place .gitignore in the repo root. For personal ignores that shouldn't be in the repo, use ~/.gitignore_global or .git/info/exclude.

Tags

Remember: Two kinds of tags: lightweight (just a pointer, like a branch that does not move) and annotated (a full Git object with author, date, message, and optional GPG signature). Always use annotated tags (git tag -a) for releases — they show up in git describe, carry metadata, and can be verified with GPG. Lightweight tags are fine for temporary local bookmarks.

Tags mark specific commits (releases, milestones):

# Lightweight tag (just a pointer)
git tag v1.0.0

# Annotated tag (includes metadata - preferred)
git tag -a v1.0.0 -m "Release 1.0.0"

# List tags
git tag -l "v1.*"

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

Useful Everyday Commands

# See what changed
git status                 # Current state
git diff                   # Unstaged changes
git diff --staged          # Staged changes
git log --oneline -20      # Recent history
git log --oneline --graph --all   # Visual branch graph

# Stash (temporarily shelve changes)
git stash                  # Save work-in-progress
git stash list             # See stashed items
git stash pop              # Restore most recent stash
git stash drop             # Discard most recent stash

# Undo operations
git checkout -- file.txt   # Discard unstaged changes to a file
git restore file.txt       # Same (modern syntax)
git reset HEAD file.txt    # Unstage a file
git restore --staged file.txt  # Same (modern syntax)

# Check who changed what
git blame file.txt         # Line-by-line authorship
git log -p file.txt        # Commit history for a specific file

What Experienced People Know

  • Write meaningful commit messages. "fix bug" tells you nothing in 6 months. "Fix race condition in session cleanup causing 500 errors under load" does.
  • Commit early, commit often on your branch. Squash before merging if the history is noisy.
  • Never commit secrets (API keys, passwords, certificates). Even if you remove them in the next commit, they're in the history forever. Use git-secrets or pre-commit hooks to prevent this.
  • git reflog is your safety net. Almost nothing in Git is truly lost if you haven't garbage collected.
  • Learn git rebase -i (interactive rebase). It's the most powerful tool for cleaning up your branch before a PR.
  • Pull requests aren't a Git feature - they're a platform feature (GitHub, GitLab). But they're where code review happens, so treat them seriously.

Wiki Navigation

Next Steps