Git Reference
Core concepts, everyday commands, merging vs rebasing, undoing mistakes, remotes, branching strategies, pull requests, and repository hygiene for data projects.
1. Core Concepts
Git is a distributed version control system: every clone holds the full history. A commit is a snapshot of the whole project plus a pointer to its parent(s), identified by a SHA hash. A branch is just a movable pointer to a commit, and HEAD points to the branch (or commit) you have checked out.
working tree ──git add──► staging area (index) ──git commit──► local repository ──git push──► remote
▲ │
└──────────────── git restore / git switch ◄────────────────────┘ ◄── git fetch / pull
| Area | What it holds |
|---|---|
| Working tree | The files you edit |
| Staging area (index) | The exact changes that will go into the next commit |
| Local repository | All commits, branches and tags, in .git/ |
| Remote (e.g. origin) | A shared copy on GitHub, GitLab, Bitbucket or Azure Repos |
2. Everyday Commands
git clone git@github.com:org/data-pipelines.git git status # what changed, what is staged git switch -c feature/scd2-merge # create and switch to a branch (older: git checkout -b) git add src/scd2.py # stage a file (git add -p to stage hunk by hunk) git commit -m "Add SCD2 merge for dim_customer" git push -u origin feature/scd2-merge git log --oneline --graph --decorate -20 git diff # unstaged changes git diff --staged # what will be committed git show HEAD # last commit's changes git blame src/scd2.py # who last changed each line
Write commit messages in the imperative, explaining why: "Fix duplicate rows when source has late updates" beats "fix bug". Keep commits small and focused so they are easy to review and revert.
3. Branching, Merging & Rebasing
| git merge main | git rebase main | |
|---|---|---|
| What it does | Creates a merge commit joining both histories | Replays your commits on top of the latest main |
| History | Preserves exactly what happened; can get noisy | Linear and clean |
| Rewrites commits? | No | Yes, with new hashes |
| Safe on shared branches? | Yes | Only on branches nobody else has based work on |
A fast-forward merge happens when main has not moved since you branched: Git just moves the pointer, with no merge commit. Pull-request platforms also offer squash merge, which turns a branch into one commit on main.
Resolving a conflict
git switch feature/scd2-merge git fetch origin git rebase origin/main # or: git merge origin/main # CONFLICT in src/scd2.py: edit the file, keep the right lines, remove the markers # <<<<<<< ... ======= ... >>>>>>> git add src/scd2.py git rebase --continue # (git merge --continue for a merge) git rebase --abort # give up and return to the state before the rebase git push --force-with-lease # needed after rebasing a branch you already pushed
The golden rule: never rebase commits that others have already pulled. --force-with-lease refuses to overwrite the remote if someone else pushed in the meantime, unlike plain --force.
4. Undoing Things
| Situation | Command | Notes |
|---|---|---|
| Discard unstaged edits to a file | git restore file.py | Changes are lost |
| Unstage a file, keep the edits | git restore --staged file.py | |
| Fix the last commit's message or add a forgotten file | git commit --amend | Rewrites the commit: only before pushing |
| Undo commits, keep changes staged | git reset --soft HEAD~1 | |
| Undo commits, keep changes unstaged | git reset HEAD~1 (mixed, the default) | |
| Undo commits and throw away changes | git reset --hard HEAD~1 | Destructive; recoverable only via reflog |
| Undo a commit already pushed | git revert <sha> | Adds a new commit that reverses it; history is not rewritten |
| Recover a "lost" commit or branch | git reflog then git reset --hard <sha> or git branch rescue <sha> | Reflog keeps local HEAD history (about 90 days by default) |
Rule of thumb: reset for local, unpushed work; revert for anything others may have pulled.
5. Working with Remotes
git remote -v # list remotes git fetch origin # download new commits; does not touch your files git pull # fetch + merge into the current branch git pull --rebase # fetch + rebase (avoids needless merge commits) git push origin feature/x git push origin --delete feature/x # delete a remote branch git branch -vv # local branches and the upstream each one tracks git fetch --prune # drop references to branches deleted on the remote
git fetch is always safe: it only updates remote-tracking branches like origin/main. git pull also changes your current branch. Many teams set git config --global pull.rebase true.
6. Branching Strategies
| Strategy | How it works | Fits |
|---|---|---|
| GitHub flow | Short-lived feature branches off main → pull request → review + CI → merge → deploy | Most teams with continuous delivery |
| Trunk-based development | Everyone merges small changes to main at least daily; unfinished work hidden behind feature flags | High-frequency deployment, strong automated testing |
| Git flow | Long-lived main and develop, plus feature, release and hotfix branches | Scheduled, versioned releases (installed software) |
| Environment branches | dev / test / prod branches, promoted by merging | Common but risky: branches drift. Prefer promoting one build artifact through environments |
For data pipelines, GitHub flow plus CI/CD works well: main is always deployable, each PR runs unit tests and data-quality checks, and the same bundle or image is promoted from dev to prod with different configuration.
7. Pull Requests & Code Review
- Keep PRs small (a few hundred lines at most) with a description of what changed, why, and how it was tested.
- Protect main: require PRs, passing CI checks and at least one approval; disallow direct pushes and force pushes.
- Use
CODEOWNERSto auto-request reviews from the owners of specific paths (e.g./sql/finance/ @finance-data-team). - For data changes, reviewers should check: idempotency, partition and merge keys, NULL handling, backfill impact, and cost (full scans, shuffles).
- Link PRs to tickets; squash-merge to keep main's history readable.
8. Stash, Cherry-pick, Tags & Bisect
git stash push -m "wip: dq checks" # shelve uncommitted changes git stash list git stash pop # re-apply and drop the latest stash git cherry-pick <sha> # copy one commit onto the current branch (e.g. a hotfix) git tag -a v1.4.0 -m "Release 1.4.0" # annotated tag for a release git push origin v1.4.0 git bisect start # binary-search history for the commit that broke something git bisect bad # current commit is broken git bisect good v1.3.0 # this one worked # test each commit Git checks out, mark it good/bad, then: git bisect reset
9. Repository Hygiene for Data Projects
# .gitignore __pycache__/ *.pyc .venv/ .env # local secrets *.parquet *.csv data/ spark-warehouse/ metastore_db/ .ipynb_checkpoints/ .databricks/ *.log
- Never commit secrets (keys, tokens, passwords). If one is committed, rotate it immediately: removing it in a later commit does not remove it from history. Add secret scanning (GitHub secret scanning, gitleaks) in pre-commit hooks and CI.
- Do not commit data files. Keep data in object storage; use Git LFS or DVC only for small reference files or model artifacts.
- Add
pre-commithooks for formatting and linting (ruff, black, sqlfluff) so style is never a review comment. - Commit notebooks as source (
.py) or strip outputs before committing, so diffs stay readable and data does not leak.
10. Common Interview Questions
| Question | Short answer |
|---|---|
| git fetch vs git pull? | Fetch downloads commits only; pull = fetch + merge (or rebase) into the current branch |
| merge vs rebase? | Merge preserves history with a merge commit; rebase rewrites your commits onto the new base for a linear history. Do not rebase shared commits |
| reset vs revert? | Reset moves the branch pointer (rewrites history, local use); revert adds a new commit undoing an old one (safe for shared branches) |
| What is HEAD? Detached HEAD? | HEAD points to the current branch; detached HEAD means it points straight at a commit, so new commits belong to no branch unless you create one |
| How do you recover a deleted branch? | Find its last commit in git reflog and run git branch name <sha> |
| How do you remove a committed secret? | Rotate it first; then rewrite history (git filter-repo or BFG) and force-push if required, knowing clones may still contain it |
| What does squash merge do? | Combines all of a branch's commits into one commit on the target branch |