Git & GitHub in Depth: Theory, Practice, and the Commands Power Users Reach For

Git is a distributed version-control system: every clone is a full history, and branches are cheap pointers you move as you work. GitHub is a collaboration platform on top—pull requests, reviews, automation, and governance. This post is a course-style reference: how Git stores data under the hood, the workflows teams actually use, and the lesser-known commands that save you when history gets messy.

In short

Commits form a DAG of snapshots; branches and tags are names pointing at commits; remotes sync copies between machines. Master everyday add/commit/branch/merge/push, then learn recovery (reflog, reset, revert), inspection (log, show, bisect), and GitHub’s PR + Actions model for team delivery.

Git is not GitHub (but they ship together)

Git runs on your laptop, in CI, and on servers. It does not require GitHub, GitLab, or Bitbucket. GitHub hosts Git repositories and adds social and operational layers: issues, pull requests, branch protection, Actions, security scanning, and org-wide policies.

In platform work you will touch both: Git for what changed and why, GitHub (or similar) for who may merge it and how it reaches production. GitOps later treats Git as the control plane for infrastructure—this course is the foundation that makes that model legible.

Theory: what Git actually stores

Git’s database is a content-addressed store keyed by SHA-1 hashes (Git is migrating toward SHA-256 on newer versions; the mental model is unchanged). Four object types matter:

ObjectContainsAnalogy
BlobFile contents (no filename)One version of a file’s bytes
TreeDirectory listing: names, modes, pointers to blobs/treesA folder snapshot
CommitTree + parent commit(s) + author/committer + messageA labeled snapshot with lineage
TagPointer to a commit (annotated tags add message/signature)Release marker

History is a directed acyclic graph (DAG): commits point to parent(s); merges have two parents. Branches are movable refs like refs/heads/main; HEAD usually points at a branch, which points at a commit. Detached HEAD means HEAD points directly at a commit—fine for inspection, risky for new work unless you create a branch.

Peek inside with plumbing commands (hidden gems #1):

git cat-file -t HEAD          # commit
git rev-parse HEAD            # full hash
git ls-tree -r HEAD --name-only | head
git show --stat HEAD

The three trees: working directory, index, repository

Git stages changes in layers:

  1. Working tree — files on disk you edit.
  2. Index (staging area) — what the next commit will record.
  3. Repository (HEAD) — committed snapshots.
# Edit files → stage → commit
git status
git add src/app.py docs/readme.md
git commit -m "feat: add health check endpoint"

# Modern restore (replaces much of checkout for paths)
git restore --staged src/app.py    # unstage
git restore src/app.py             # discard working-tree changes

.gitignore excludes untracked noise; .gitattributes controls line endings, diff drivers, and LFS filters. For secrets, ignore is not enough—use pre-commit hooks, git-secrets, or platform scanning; never rewrite public history to “remove” a leaked key without rotating the credential.

First repository: practical bootstrap

git init my-app
cd my-app
git branch -M main
echo "# My App" > README.md
git add README.md
git commit -m "chore: initial commit"

# Or clone existing work
git clone https://github.com/org/my-app.git
cd my-app
git remote -v

Clone copies the entire object database and checks out a branch. Fork (on GitHub) is a server-side clone under your account; you add upstream to track the original:

git remote add upstream https://github.com/org/my-app.git
git fetch upstream
git switch -c feature/sync main
git merge upstream/main

Branching and integration strategies

Branches are cheap. Teams pick a integration pattern:

ModelFlowBest when
Trunk-basedShort-lived branches; frequent merges to mainStrong CI, feature flags, platform teams
GitHub FlowBranch → PR → merge to main → deployWeb apps, continuous delivery
Git Flowdevelop, release, hotfix branchesScheduled releases, multiple versions in field
git switch -c feature/rate-limit
# ... commits ...
git switch main
git pull --ff-only origin main
git merge --no-ff feature/rate-limit -m "Merge feature/rate-limit"
git push origin main

Merge records a merge commit (or fast-forward if possible). Rebase replays your commits on top of another tip—linear history, but rewrites SHAs; never rebase commits already pushed to a shared branch unless the team agrees.

git switch feature/rate-limit
git fetch origin
git rebase origin/main
# resolve conflicts → git add ... → git rebase --continue
git push --force-with-lease origin feature/rate-limit

--force-with-lease is the safe force-push: it fails if someone else updated the remote branch since your last fetch.

Remotes, fetch, pull, push

git fetch origin                    # download objects; don't merge
git log --oneline main..origin/main # what's on remote
git pull origin main                # fetch + merge (or rebase with config)
git push -u origin feature/x        # publish branch, set upstream

# Push a tag (releases)
git tag -a v1.2.0 -m "Release 1.2.0"
git push origin v1.2.0

Set defaults once:

git config --global init.defaultBranch main
git config --global pull.rebase false   # or true for rebase pulls
git config --global push.autoSetupRemote true

Hidden and power-user Git commands

These are the commands tutorials skip until something breaks. Grouped by job.

Inspection and archaeology

git log --oneline --graph --decorate --all -20
git log -p -S "deleteUser" -- src/api/     # pickaxe: when string appeared/vanished
git log --follow -- path/old-name.py        # history across renames
git blame -L 40,60 src/app.py
git show HEAD~3:src/app.py                  # file at ancestor commit
git shortlog -sn --since="3 months ago"

Stash, worktrees, sparse checkout

git stash push -u -m "wip before hotfix"
git stash list
git stash pop

git worktree add ../my-app-hotfix hotfix/1.0
git worktree list

git sparse-checkout init --cone
git sparse-checkout set src docs

Worktrees let you check out two branches in separate directories simultaneously—ideal for urgent hotfixes without disturbing your feature branch.

Recovery: reflog, reset, revert, cherry-pick

git reflog                          # where HEAD has been (local safety net)
git reset --soft HEAD~1             # undo commit; keep staged
git reset --mixed HEAD~1            # undo commit; unstage (default)
git reset --hard origin/main        # match remote (destructive locally)

git revert abc1234                  # new commit that undoes abc1234 (safe on main)
git cherry-pick def5678             # apply one commit elsewhere

On shared branches, prefer revert over reset + force push. Reflog expires (~90 days default); it is not a backup service.

Interactive rebase and commit hygiene

git rebase -i origin/main
# pick / squash / fixup / drop commits in editor
git commit --amend --no-edit
git commit --fixup=abc1234 && git rebase -i --autosquash origin/main

Finding bugs: bisect

git bisect start
git bisect bad                    # current commit is broken
git bisect good v1.0.0            # known good tag
# test each step → git bisect good|bad until culprit found
git bisect reset

Automate with git bisect run ./scripts/test.sh.

Cleaning, repairing, and deep maintenance

git clean -fdn                    # dry-run: remove untracked
git fsck --full
git gc --prune=now
git count-objects -vH

# Submodule (legacy but everywhere)
git submodule update --init --recursive

# Rerere: reuse recorded conflict resolutions
git config --global rerere.enabled true

For history rewriting at scale (remove large file from all history), use git filter-repo (preferred over deprecated filter-branch).

Plumbing worth knowing

git update-ref refs/heads/main HEAD~1   # move branch (careful)
git symbolic-ref HEAD
git merge-base main feature/x
git diff main...feature/x               # changes on feature since diverged
git diff main..feature/x                # all differences between tips

# Debug transport
GIT_TRACE=1 GIT_CURL_VERBOSE=1 git fetch origin
GIT_SSH_COMMAND="ssh -v" git fetch origin

Useful aliases (team templates)

git config --global alias.lg "log --oneline --graph --decorate -20"
git config --global alias.last "log -1 HEAD --stat"
git config --global alias.unstage "restore --staged"
git config --global alias.please "push --force-with-lease"

Merge conflicts: read the markers

<<<<<<< HEAD
our change
=======
their change
>>>>>>> feature/rate-limit

Edit to the intended result, git add resolved files, then continue merge or rebase. Tools: git mergetool, VS Code, IntelliJ. For complex repeats, enable rerere.

Signing, hooks, and policy

  • SSH commit signinggit config gpg.format ssh and set user.signingkey; GitHub shows “Verified.”
  • Hookspre-commit (format/lint), commit-msg (conventional commits), pre-push (tests). Husky and pre-commit frameworks wrap these.
  • Conventional Commitsfeat:, fix:, chore: enable changelog automation and clearer history.

GitHub in depth: collaboration and delivery

GitHub’s core unit of review is the pull request (PR): propose merging a branch, discuss diffs, run checks, then merge with a strategy.

Merge optionResult
Create a merge commitPreserves branch topology; merge commit on base
Squash and mergeOne commit on base; good for noisy feature branches
Rebase and mergeLinear history; replays commits (no merge commit)

Branch protection enforces reviews, status checks, signed commits, and linear history. CODEOWNERS auto-requests reviewers by path. Environments gate deployments with required approvers and secrets scoped per environment.

Issues, projects, and labels

Issues track work; link PRs with Fixes #42 to auto-close. GitHub Projects (v2) are kanban/roadmap views across repos. Labels and milestones keep release trains visible.

GitHub Actions (CI/CD on events)

# .github/workflows/ci.yml (minimal pattern)
name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: npm ci && npm test

Secrets live in repo or org settings—never commit them. Use OIDC to assume cloud roles without long-lived keys. Reusable workflows and composite actions DRY pipelines across repos. For a full treatment—workflows, environments, matrices, GHCR, branch protection, and GitOps handoff—see GitHub CI/CD in depth.

Security and supply chain

  • Dependabot — dependency update PRs.
  • Code scanning — CodeQL static analysis on PRs.
  • Secret scanning — blocks or alerts on committed tokens.
  • Rulesets — org-wide branch and tag policies (successor patterns to legacy branch protections).

GitHub CLI (gh)

gh auth login
gh repo clone org/my-app
gh pr create --fill
gh pr checkout 123
gh pr merge 123 --squash --delete-branch
gh run list --workflow=ci.yml
gh api repos/{owner}/{repo}/pulls/123 --jq .title

The CLI mirrors the UI for scripting reviews and releases from your terminal—especially useful in tmux or remote dev boxes.

SSH keys, tokens, and authentication

Prefer SSH keys for Git transport ([email protected]:org/repo.git). For HTTPS and API access, use fine-grained personal access tokens with least privilege. In CI, use GitHub Apps or OIDC—not a personal PAT tied to a human who might leave the company.

# ~/.ssh/config
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_github
  IdentitiesOnly yes

Monorepos, submodules, and LFS

  • Monorepo — one repo, many services; path filters in CI run only affected jobs.
  • Submodules — pin another repo at a commit; powerful but easy to footgun (git submodule update --init).
  • Git LFS — stores large binaries outside the main object graph; costs and clone behavior differ—know your host’s LFS billing.

Course lab progression (suggested)

  1. Create a repo, make ten commits on main, practice git log --oneline --graph.
  2. Branch feature, open a PR on GitHub, squash merge, delete branch.
  3. Cause a merge conflict intentionally; resolve with git merge and again with git rebase.
  4. Use git stash and git worktree during a simulated hotfix.
  5. “Lose” a commit, recover with git reflog.
  6. Run git bisect on a small script with a known bad commit.
  7. Add a GitHub Actions workflow that runs tests on every PR.
  8. Enable branch protection requiring one review and green CI.
  9. Configure gh and merge a PR from the terminal.
  10. Capstone: fork an open-source repo, fix a doc typo, open PR upstream.

Common pitfalls (exam and production)

  • Committing secrets — rotate the credential; history rewrite is painful and public forks may retain copies.
  • Force push on shared branches — breaks teammates; use revert or coordinated maintenance windows.
  • Huge binaries in Git — bloats forever; use LFS or artifact storage.
  • Merge vs rebase religion — pick a team rule; document it in CONTRIBUTING.md.
  • git pull without understanding — surprise merge commits; prefer fetch + explicit merge/rebase.
  • Ignoring CI on green button day — branch protection exists because humans skip tests under pressure.
  • Submodule drift — CI checks out but forgets submodule update --init.

How this connects to platform work

Git history is audit evidence. GitHub (or GitLab or Bitbucket) is where change management meets automation: PR reviews map to change advisory boards; CI (GitHub Actions, GitLab CI/CD, or Bitbucket Pipelines) maps to build pipelines; protected main maps to production gates. When you adopt GitOps, the same skills apply—only the artifacts in the repo are manifests instead of application source.

For infrastructure-as-code mechanics, continue with Terraform & IaC for everyone. For calm incident response when a bad deploy lands, see Incidents & disaster response.

Further reading

  • Pro Git (Scott Chacon) — free online book; deep and clear
  • Git documentation — git help <command> and official reference
  • GitHub Docs — Actions, security, branch protection, rulesets
  • Atlassian Git tutorials — visual branching lessons
  • Conventional Commits — message format for automation

Blog index · GitHub CI/CD in depth · GitLab CI/CD in depth · GitOps principles · Terraform & IaC

Back to blog list