Guides

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 .gitignore file to keep credential files, .env, and similar out of version control from the start.
  • Treating git status as optional. Running it before every add/commit catches accidental inclusions (a stray debug file, an entire node_modules/ folder) before they're permanently in your history.
  • Cloning into a directory that already exists with content. git clone expects 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 --force can silently discard someone else's commits (or your own, from another machine) — a normal git push refusing 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).