Git for Security Work: Version Control Beyond Just Downloading Code
Updated July 28, 2026 · Written by PWNMI — see About.
Every lab on this site starts with git clone, and that's most people's entire relationship with git — a command you run once to download something, then forget about. That's enough to get through a lab, but git shows up constantly beyond that: tracking your own notes and scripts through an engagement, contributing to or forking someone else's tool, and — a genuinely offensive use case, not just developer housekeeping — a .git directory accidentally left exposed on a web server is a real, common way to reconstruct an application's full source and history. This covers the workflow, not just the one command.
Repos, commits, and remotes
A repository ("repo") is a project's full history, tracked file by file, living in a hidden .git folder at its root. A commit is a saved snapshot — a deliberate checkpoint you create, with a message describing what changed and why, not an automatic autosave. A remote is a copy of the repo hosted somewhere else — GitHub, GitLab, or any server running git — that your local copy can sync with. git clone does two things at once: downloads a repo's full history, and automatically sets the source as a remote named origin, ready for later git pulls to catch up on new changes.
Installation
# Debian/Ubuntu (already present on Kali)
sudo apt install git
# macOS
brew install git
First-time setup, so your commits are attributed correctly:
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
Cloning and pulling
git clone https://github.com/pwnmihq/resources
This is every lab's first step on this site — downloading the full repo, history included, into a new folder named after it. Once you have a local copy, git pull fetches and merges any new commits from the remote — useful if a lab's resources repo gets updated after you first cloned it, or you're tracking a PoC repo that's still actively developed.
The basic local workflow
Git tracks changes in three stages, and understanding the middle one is what actually unblocks most beginners:
git status
Shows what's changed since your last commit — modified files, new untracked files, anything staged and ready to commit. Run this constantly; it's the least destructive, most informative command in git.
git add <file>
# or: git add .
Staging. This doesn't commit anything yet — it marks specific changes as "included in the next commit," which is what lets you commit a subset of your changes instead of all-or-nothing.
git commit -m "Add initial recon notes"
Saves everything currently staged as a new snapshot, with a message. Commit messages that explain why, not just what changed, are what make a project's history actually useful six months later — "fix bug" tells you nothing; "fix race condition in session token generation" does.
git push
Uploads your local commits to the remote, so they're backed up and visible to anyone else with access.
Branches
A branch is an independent line of development — the default is usually called main. Creating a new branch (git checkout -b feature-name) lets you make changes without touching the working version until you're ready, then merge them back in. This matters even solo: it's the difference between "I broke my only copy mid-experiment" and "I broke a throwaway branch, main is untouched."
Common mistakes
- Committing secrets. An API key or password committed once is in the project's history forever, even if you delete it in a later commit — git history doesn't forget. Use a
.gitignorefile to keep credential files,.env, and similar out of version control from the start. - Treating
git statusas optional. Running it before everyadd/commitcatches accidental inclusions (a stray debug file, an entirenode_modules/folder) before they're permanently in your history. - Cloning into a directory that already exists with content.
git cloneexpects to create a fresh folder; cloning into a populated one either fails or produces confusing results depending on what's already there. - Force-pushing without understanding what it overwrites.
git push --forcecan silently discard someone else's commits (or your own, from another machine) — a normalgit pushrefusing to go through is usually git protecting you from exactly that.
Next step
For the command-level quick reference, see the Git Cheat Sheet — including how an exposed .git directory on a live web server becomes a real recon and source-reconstruction technique. To see git clone used as step one of an actual lab, start with CVE-2026-62183 (Apache Syncope).
Get new write-ups in your inbox
New roadmaps, tool walkthroughs, and lab write-ups. No spam. Unsubscribe anytime.