Skip to main content

Git Reference

Volume 1 — Core Developer Skills

Git is the version control system behind every Traxs repo. This chapter covers the mental model, the day-to-day commands, and the branch/PR workflow used across RoundTrip, Waypoint, and Relay. Read it top to bottom once, then use it as a lookup afterward.

1. The Three-State Model

Everything in Git makes more sense once you internalize that a file moves through three states:

  • Working directory — the files on disk as you're editing them.
  • Staging area (the "index") — a holding area where you build up exactly what the next commit will contain.
  • Repository — the committed history stored in .git.

git add doesn't save anything permanently — it just marks a change as "include this in the next commit." This is why you can git add half a file's changes and leave the rest for a later commit.

2. Configuring Git

git config --global user.name "Pete Carroll"
git config --global user.email "pete@traxsgroup.com"
git config --global init.defaultBranch main
git config --global core.editor "code --wait"
git config --list # show all effective settings
git config --list --show-origin # show which file each setting came from

--global applies to every repo on the machine; drop it (just git config user.email "...") to override for a single repo — useful if a repo needs a different identity.

3. Starting a Repository

git init # start a new repo in the current directory
git clone https://github.com/Traxs-dev/RoundTripAPI.git # copy an existing remote repo
git clone git@github.com:Traxs-dev/RoundTripAPI.git # same, over SSH instead of HTTPS

HTTPS clones prompt for credentials (or a token) on push; SSH clones use your SSH key (see the SSH & Key Management chapter) and don't prompt at all once your key is set up.

4. The Basic Workflow

git status # what's changed, what's staged, what branch you're on
git add file1.cs file2.cs # stage specific files
git add . # stage everything changed in and below the current directory
git add -p # interactively choose which hunks to stage — useful when one file has two unrelated changes
git commit -m "Add leave request validation"
git push # push the current branch to its remote
git pull # fetch and merge the latest from the remote

git status is the command you should run before and after almost every other Git command until the three-state model is second nature — it always tells you exactly where you stand.

5. Branching

git branch # list local branches, * marks the current one
git branch -a # list local and remote branches
git branch feature/WAY-41-example # create a branch without switching to it
git switch feature/WAY-41-example # switch to it (modern syntax)
git checkout feature/WAY-41-example # switch to it (classic syntax, still everywhere)
git switch -c feature/WAY-41-example # create and switch in one step (modern)
git checkout -b feature/WAY-41-example # create and switch in one step (classic)
git branch -d feature/WAY-41-example # delete a branch (only if merged)
git branch -D feature/WAY-41-example # force-delete a branch (even if unmerged)

switch and restore are newer, narrower replacements for the overloaded checkout command — both do the same job, use whichever your muscle memory prefers. This handbook uses both interchangeably since you'll see both in the wild.

6. Merging

git switch main
git merge feature/WAY-41-example

Two outcomes:

  • Fast-forward — if main hasn't moved since the branch was created, Git just moves the pointer forward. No merge commit, linear history.
  • Three-way merge — if both branches have diverged, Git creates a merge commit with two parents.

Resolving a merge conflict

git merge feature/WAY-41-example
# CONFLICT (content): Merge conflict in LeaveRequestHandler.cs

Open the file — Git marks the conflicting section:

<<<<<<< HEAD
current code from the branch you're on
=======
incoming code from the branch being merged
>>>>>>> feature/WAY-41-example

Edit the file to the correct final version, remove the <<<<<<</=======/>>>>>>> markers, then:

git add LeaveRequestHandler.cs
git commit

git status during a conflict always lists exactly which files still need resolving — trust it over trying to remember.

7. Rebasing

Rebasing replays your branch's commits on top of another branch instead of merging the two histories together, producing a linear history with no merge commit.

git switch feature/WAY-41-example
git rebase main

If a commit conflicts during rebase:

# fix the conflicting file
git add file.cs
git rebase --continue
# or, to back out entirely:
git rebase --abort

Merge vs. rebase — when to use which:

SituationUse
Bringing your feature branch up to date with main before opening a PRrebase — keeps history linear and easy to read
Combining a completed feature branch back into mainmerge (often via a PR's "squash and merge" or "merge commit" button)
The branch has already been pushed and others are working off itmerge, never rebase — rebasing rewrites commit hashes and breaks anyone who's already pulled

Rule of thumb: never rebase a branch that's already been pushed and shared. Rebase freely on a branch that's still local-only or that only you are working on.

Interactive rebase (cleaning up commits before a PR)

git rebase -i HEAD~3 # interactively edit the last 3 commits

Opens an editor listing the commits with an action per line (pick, reword, squash, drop, ...). Reordering lines reorders commits; changing pick to squash folds a commit into the one above it — useful for turning a messy string of "wip", "fix typo", "actually fix it" commits into one clean commit before a PR.

8. Remotes

git remote -v # list remotes and their URLs
git remote add origin git@github.com:Traxs-dev/RoundTripAPI.git
git remote set-url origin git@github.com:Traxs-dev/RoundTripAPI.git # change an existing remote's URL
git fetch # download remote changes without merging them
git fetch origin # fetch from a specific remote
git pull # fetch + merge in one step
git push -u origin feature/WAY-41-example # push and set the upstream tracking branch (only needed the first time)
git push # after -u has been set once, plain push/pull work without arguments

fetch is the safe one — it updates your local view of the remote without touching your working files. pull is fetch + merge, so it does touch your working files. If you want to look before you leap, fetch then git log main..origin/main to see what's incoming before merging.

9. Viewing History

git log # full commit history
git log --oneline # one line per commit
git log --oneline --graph --all # visual branch graph, all branches
git log -p file.cs # history of a specific file, with diffs
git show <commit-hash> # full details of one commit
git diff # unstaged changes vs. the last commit
git diff --staged # staged changes vs. the last commit
git diff main..feature/WAY-41-example # everything different between two branches
git blame file.cs # who last changed each line, and in which commit

git blame is not about assigning fault — it's the fastest way to find the commit (and its message/ticket) that introduced a specific line, so you can go read the reasoning behind it.

10. Undoing Changes

This is the section worth bookmarking — it covers the difference between commands that are safe and commands that discard work.

git restore file.cs # discard uncommitted changes to a file (safe: only affects working directory)
git restore --staged file.cs # unstage a file, keep the edits (safe)
git checkout -- file.cs # classic-syntax equivalent of git restore file.cs

git commit --amend -m "Corrected message" # rewrite the most recent commit's message or contents
git commit --amend --no-edit # add currently staged changes to the last commit without changing its message

git reset --soft HEAD~1 # undo the last commit, keep changes staged
git reset --mixed HEAD~1 # undo the last commit, keep changes unstaged (default mode)
git reset --hard HEAD~1 # undo the last commit AND discard the changes entirely

git revert <commit-hash> # create a NEW commit that undoes a previous commit — safe for shared/pushed history

reset vs. revert: reset --hard rewrites history and destroys the commits it removes — only use it on local, unpushed commits. revert adds a new commit on top that undoes an earlier one, leaving history intact — this is the correct tool once something has already been pushed or merged into main.

11. Stashing

Stashing sets aside uncommitted work temporarily, without committing it — useful when you need to switch branches but aren't ready to commit.

git stash # stash all uncommitted changes
git stash push -m "WIP: leave balance calc" # stash with a descriptive label
git stash list # see all stashes
git stash pop # reapply the most recent stash and remove it from the stash list
git stash apply # reapply the most recent stash but keep it in the stash list
git stash drop # delete a stash without applying it

12. Tags

git tag v1.4.0 # lightweight tag on the current commit
git tag -a v1.4.0 -m "M14 billing release" # annotated tag — stores author, date, and message; preferred for releases
git push origin v1.4.0 # tags don't push automatically — push them explicitly
git push origin --tags # push all tags at once
git tag -l "v1.4.*" # list tags matching a pattern

Use annotated tags (-a) for anything marking an actual release — they carry metadata a lightweight tag doesn't.

13. .gitignore

.gitignore tells Git which files to never track — build output, secrets, local settings.

bin/
obj/
node_modules/
.env
appsettings.Development.json
*.user
.DS_Store

If a file is already tracked, adding it to .gitignore won't stop tracking it — untrack it explicitly first:

git rm --cached appsettings.Development.json

Then commit that removal. The file stays on disk; Git just stops watching it.

14. Useful Aliases

Add these to ~/.gitconfig (or run each as a git config --global alias.X "..." command) to shorten commands you'll type constantly:

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"
git config --global alias.last "log -1 HEAD"
git config --global alias.unstage "restore --staged"

After this, git lg gives you the full visual branch graph without typing the flags every time.

15. The Traxs Branch and PR Workflow

This is the actual pattern used across RoundTrip and Waypoint:

git switch main
git pull
git switch -c feature/WAY-41-endpoint-authorization

# ... make changes, commit as you go ...

git push -u origin feature/WAY-41-endpoint-authorization

Then open a PR into main. A few rules that keep this workflow clean:

  • Branch naming: feature/WAY-NNN-short-description for Waypoint work, feature/TRA-NNN-short-description for RoundTrip or cross-product work — the ticket ID makes it trivial to trace a branch back to Linear.
  • One PR at a time: PRs are merged sequentially, not in parallel, to avoid stacking conflicting changes against a moving main.
  • Rebase before opening the PR, not after — if main has moved since you branched, git rebase main on your feature branch keeps the diff clean and avoids a noisy merge commit.
  • After merge: the pipeline deploys from main automatically. Mark the Linear ticket Done only after production verification, not just after the merge lands.

16. Troubleshooting Playbook

SymptomLikely causeWhat to do
fatal: not a git repositoryYou're not inside a repo directory, or .git got deletedcd into the correct directory; confirm with git status
Your branch is ahead/behind 'origin/main' by N commitsLocal and remote have divergedgit pull to bring them together; git push if you're only ahead
Merge conflict markers left in a file after resolvingForgot to remove <<<<<<</=======/>>>>>>> before committingSearch the file for <<<<<<< before staging; re-edit and re-add
Accidentally committed to main directlyWorking on the wrong branchIf unpushed: git branch feature/xyz then git reset --hard origin/main on main to move the commit onto the new branch. If pushed: coordinate before rewriting shared history
git push rejected: "fetch first"Remote has commits you don't have locallygit pull (or git fetch + git merge/rebase) before pushing again
Rebase conflict, unsure how to proceedMid-rebase state is unfamiliargit rebase --abort returns you to the pre-rebase state with zero risk; then retry more carefully
Committed a secret (API key, connection string)Staged and committed before noticingRotate the credential immediately — removing it from a later commit does not remove it from history. Treat any committed secret as compromised.
Detached HEAD stateChecked out a commit hash or tag directly instead of a branchgit switch -c temp-branch-name to save your place, or git switch main to abandon it if you made no changes
Large file won't push, remote rejects itFile exceeds the host's size limitRemove it from the commit; use Git LFS or external storage (Azure Blob) for large binaries instead

17. Quick Reference

CategoryCommandWhat it does
Statusgit statusWhat's changed and staged
Staginggit add -pStage selected hunks interactively
Commitgit commit --amend --no-editAdd staged changes to the last commit
Branchinggit switch -c nameCreate and switch to a new branch
Merginggit merge branchMerge a branch into the current one
Rebasinggit rebase mainReplay current branch on top of main
Rebasinggit rebase -i HEAD~3Interactively edit the last 3 commits
Remotesgit fetchDownload remote changes without merging
Remotesgit push -u origin branchPush and set upstream tracking
Historygit log --oneline --graph --allVisual history of all branches
Historygit blame fileWho last changed each line
Undogit restore fileDiscard uncommitted changes (safe)
Undogit reset --hard HEAD~1Discard the last commit entirely (local only)
Undogit revert <hash>Safely undo a pushed commit with a new commit
Stashgit stash popReapply and remove the latest stash
Tagsgit tag -a v1.0 -m "msg"Create an annotated release tag

Part of the Traxs Engineering Handbook — Volume 1: Core Developer Skills. Companion chapters in this volume: Bash Reference, Git Troubleshooting, SSH & Key Management.