Git & GitHub

The bar: you can branch, commit clean atomic history, resolve a merge conflict without panic, and get a pull request reviewed and merged on a real team repo without anyone holding your hand.

Every company that ships software runs on version control, and Git is the standard the industry settled on. It is not a nice-to-have skill you pick up later. If you cannot move confidently through a repository's history, you cannot collaborate, you cannot recover from mistakes, and you cannot get your work merged. This module is where you stop treating Git as a scary black box and start treating it as the tool that lets a team of strangers build one thing together.

The payoff is concrete: the same commands you drill here are the ones you will use to ship real code to a production platform that serves thousands of veterans. Learn them until they are muscle memory.

3.1 Git Fundamentals

Objective: understand how Git tracks changes and manages the history of a project.

Git has three places your work lives: the working directory (the files you edit), the staging area (the changes you have marked to include in the next snapshot), and the repository (the permanent history stored in the .git directory). Almost every confusing moment in Git traces back to losing track of which of those three you are looking at. Get that model straight and the rest follows.

A commit is an atomic snapshot with a message explaining why the change exists. Under the hood Git stores everything as objects: blobs (file contents), trees (directory structure), and commit objects that point at a tree plus their parents. ReferencesHEAD, branches, and tags — are just movable labels pointing at commits. Branches are cheap because they are nothing more than a pointer.

The common self-taught mistake is writing giant commits with messages like "fixes" or "stuff". A commit is a unit of thought, not a save button. Small, well-described commits are what make history readable and bugs findable later.

Drill:

  • Inspect the .git directory and identify where objects and refs live
  • Trace a single commit to its tree and parent using git cat-file -p
  • Explain out loud what HEAD points at right now
  • Write three commits with messages that state why, not what

3.2 Essential Commands

Objective: execute the everyday Git operations confidently and without lookup.

Start a project with git init, or copy an existing one with git clone. Stage work with git add, and when a file has several unrelated changes, use git add -p to stage them hunk by hunk — this is how you keep commits atomic. Commit with git commit, and fix the most recent commit (message or contents) with git commit --amend before you have pushed it.

Read history with git log, and make it legible with git log --oneline --graph. Check where you stand with git status and see exactly what changed with git diff. These two are your constant orientation tools — run them constantly, not just when something breaks.

Undoing is where beginners freeze. Learn the three distinct tools: git restore discards working-directory changes, git reset moves your branch pointer (and optionally the staging area), and git revert creates a new commit that undoes an old one safely on shared history. Reaching for reset --hard on shared branches is the classic way to lose someone else's work.

Drill:

  • git init a repo and git clone an existing one
  • Stage a partial file with git add -p
  • Amend a commit message with git commit --amend
  • Undo three ways: git restore a file, git reset a staged change, git revert a commit

3.3 Branching Strategies

Objective: use branches to develop features in parallel without stepping on other people.

Create branches with git branch, or create and switch in one move with git checkout -b or the newer git switch -c. A branch isolates your work so main stays shippable while you build. The feature branch workflow is the backbone of nearly every team: branch off main, do your work, open a pull request, merge back.

Naming matters more than beginners expect. A convention like feat/login-form or fix/broken-nav tells reviewers what a branch is for at a glance. Sloppy names like test2 or johns-branch slow everyone down.

Teams organize around one of two patterns. GitFlow uses long-lived develop, release, and hotfix branches — heavier, suited to versioned releases. Trunk-based development keeps short-lived branches that merge into main fast and often, which is what most web teams prefer today. The judgment call underneath both: branch when work will take more than one commit or needs review; commit directly only on throwaway solo repos.

Drill:

  • Create branches with both git switch -c and git checkout -b
  • Name three branches with a type/short-description convention
  • Run a full feature-branch cycle: branch, commit, switch back to main
  • State when your project should branch versus commit straight to main

3.4 Merging and Rebasing

Objective: integrate changes from one branch into another cleanly.

There are two shapes of merge. A fast-forward merge happens when main has not moved since you branched — Git just slides the pointer forward, no merge commit. A three-way merge happens when both branches have new commits; Git combines them and records a merge commit that has two parents, preserving the true history of what happened.

Rebasing replays your commits on top of another branch, producing a straight, linear history as if you had branched from the latest main. Interactive rebase (git rebase -i) lets you squash, reorder, reword, and drop commits before you share them — this is how you turn a messy work-in-progress into a clean story for reviewers.

The rule that keeps you out of trouble: rebase private branches to tidy them, merge to combine shared history, and never rebase commits other people have already pulled. Rewriting shared history is the fastest way to make a teammate hate you.

Conflicts are normal, not failure. When two branches change the same lines, Git marks the conflict and waits. Open the file, decide what the correct result is, remove the markers, stage, and continue. A merge tool helps, but reading the markers by hand is a skill you must own.

Drill:

  • Trigger a fast-forward merge, then a three-way merge, and inspect both in git log --graph
  • Clean up three messy commits with git rebase -i (squash and reword)
  • Manufacture a conflict and resolve it by editing the markers, then git add and continue
  • Explain in one sentence when you would merge versus rebase

3.5 GitHub Collaboration

Objective: collaborate on shared codebases through GitHub the way a real team does.

Forking copies someone else's repo to your account so you can propose changes without write access; cloning pulls a repo down to your machine. On an open-source contribution you will juggle two remotes: origin (your fork) and upstream (the original). Keep upstream in sync so your branches start from current code. git push sends your commits up; git pull brings others' down.

The pull request is the unit of collaboration. You open one to propose a change, teammates review it, and once approved it merges into main. A good PR is small, has a clear description of what and why, and passes its checks before you ask for eyes on it. Good code review is a two-way skill: give specific, kind, actionable feedback, and receive it without defensiveness.

Issues and project boards are how teams track what needs doing and who owns it. Link your PR to the issue it closes so the work stays traceable.

Drill:

  • Fork a repo, clone it, and add both origin and upstream remotes
  • Sync your fork from upstream before starting work
  • Open a pull request with a title and description a reviewer can act on
  • Leave one specific, constructive review comment on a teammate's PR
  • Open an issue and reference it from a PR

3.6 Advanced Git

Objective: reach for the right advanced tool when you land in a complex situation.

git stash shelves uncommitted work so you can switch context fast; git stash pop brings it back. Cherry-picking copies a single commit from one branch onto another when you need just that one change, not the whole branch. Bisecting (git bisect) does a binary search through history to pinpoint the exact commit that introduced a bug — a superpower when a regression appears and you have no idea when.

The reflog is your safety net. Git records where HEAD has been, so even a "lost" commit after a bad reset is usually recoverable with git reflog. Knowing this exists is the difference between calm and panic.

Git hooks run scripts automatically on events like commit or push — teams use them to lint and test before code ever leaves a laptop. Submodules and subtrees let one repo embed another; powerful, occasionally necessary, and a known source of confusion, so use them deliberately.

Drill:

  • Stash work, switch branches, and git stash pop it back
  • git cherry-pick a single commit onto another branch
  • Run git bisect to find a planted bad commit
  • Recover a commit you deliberately "lost" using git reflog
  • Wire up a pre-commit hook that runs your linter

3.7 Git Configuration

Objective: configure Git so it works for you consistently across every project.

Set your identity and tools once with global config: git config --global user.name, user.email, your editor, and a diff tool. This is what puts the right name on every commit you make. Aliases turn long commands you type fifty times a day into two letters — a git co for checkout or a one-line pretty log saves real time.

A .gitignore file keeps generated and secret files out of version control — node_modules, build output, .env. Committing those is a rookie mistake that pollutes the repo and can leak credentials. .gitattributes controls how Git handles specific files, such as normalizing line endings so Windows and Mac teammates stop fighting over invisible changes.

Finally, set up credential management so you authenticate to GitHub once instead of typing a token on every push. A credential helper or SSH key handles this cleanly.

Drill:

  • Set global user.name, user.email, and editor
  • Create two aliases for commands you run constantly
  • Write a .gitignore that excludes dependencies, build output, and .env
  • Add a .gitattributes line to normalize line endings
  • Configure a credential helper or SSH key so pushes stop asking for a password

3.8 Hands-On Lab: Team Workflow Simulation

Objective: execute a complete team collaboration workflow end to end, the way you will on the job.

What you ship

A full round-trip contribution on a shared repository, demonstrating every core skill in this module:

  • Fork a repository and set up origin and upstream remotes
  • Create feature branches with a clear naming convention and make changes across a few atomic commits
  • Deliberately create and resolve a merge conflict between two branches
  • Open a pull request, review a teammate's PR with specific feedback, and merge an approved one
  • Use interactive rebase to clean up your commit history before the final merge

You clear the lab when your change is merged into main with readable history and no leftover conflict markers.

On the VWC codebase

This is the exact workflow you use to contribute to the Vets Who Code app. Every change starts on a branch off master, follows Conventional Commit messages, and lands through a reviewed pull request — direct commits to the main branch are never allowed. Pre-commit hooks run Biome for lint and format, so clean, focused commits are not optional. The habits you build in this lab are the ones that get your first real PR merged.