Git for SDETs

25Git for SDETs

What you will master here

  • Mental model: working tree, index, HEAD, remote
  • Branches, commits, rebase vs merge
  • The PR workflow end-to-end
  • Stash, cherry-pick, revert, reset (and when which)
  • Bisect — find the commit that introduced a bug
  • Hooks (pre-commit, pre-push)
  • Common SDET-flavoured scenarios

25.1 Mental model

Working tree your files on disk Staging (index) git add Local repo (HEAD) git commit Remote git push Four areas. Each git command moves between adjacent areas.
Figure 17 — Working tree → staging → local commit → remote.

25.2 Daily workflow commands

# Status / inspection
git status              # what's changed / staged / untracked
git diff                # unstaged changes
git diff --staged       # staged changes
git log --oneline -10   # recent commits
git log --graph --oneline --all

# Start work
git pull --rebase                          # update from remote, rebasing local commits
git checkout -b feature/login-flake-fix    # new branch

# Commit
git add tests/login.spec.ts                # stage specific file
git add -p                                 # stage interactively per hunk
git commit -m "fix flaky login test"

# Push
git push -u origin feature/login-flake-fix

# Update
git fetch origin
git rebase origin/main                     # bring your branch up to date
# (or: git merge origin/main — see "rebase vs merge" below)

25.3 Rebase vs Merge

Merge
git checkout feature/x
git merge main
  • Creates a merge commit
  • Preserves exact branch history
  • History looks "messier" with merges
  • Safer — never rewrites commits
Rebase
git checkout feature/x
git rebase main
  • Replays your commits on top of main
  • Linear history — easier to read
  • Rewrites commits (new SHAs)
  • NEVER rebase a public/shared branch
Pragmatic ruleRebase your local feature branch onto main before pushing. Use merge for integrating completed PRs into main. Never git push --force to shared branches without coordinating.

25.4 The PR workflow

# 1. Update main + branch off
git checkout main && git pull
git checkout -b feature/locator-rewrite

# 2. Work, commit small, push
git add . && git commit -m "refactor: replace xpath with getByRole in LoginPage"
git push -u origin feature/locator-rewrite

# 3. Open PR (gh CLI or web)
gh pr create --title "Replace XPath with getByRole" --body "..."

# 4. Address review feedback
git add . && git commit --amend --no-edit
git push --force-with-lease    # safer than --force

# 5. Keep up-to-date with main
git fetch origin && git rebase origin/main
git push --force-with-lease

# 6. Squash on merge (or set repo policy)
# (done in GitHub UI)

25.5 Stash — temporary work-in-progress shelf

git stash             # shove changes aside
git stash list        # see all stashes
git stash pop         # apply latest stash + drop it
git stash apply 1     # apply specific stash, keep it
git stash drop 1      # drop without applying
git stash push -m "WIP login fix" -- tests/login.spec.ts    # partial stash

25.6 Cherry-pick — bring a commit to another branch

git checkout release/v2.3
git cherry-pick abc1234        # apply commit abc1234 to current branch
git cherry-pick abc1234..def5678   # range

25.7 Revert vs Reset vs Restore

CommandWhat it doesSafe on public history?
git revert abc1234Creates a NEW commit that undoes abc1234Yes
git reset --soft HEAD~1Moves HEAD back 1 commit; changes stay stagedLocal only
git reset --mixed HEAD~1Moves HEAD; changes unstaged but keptLocal only
git reset --hard HEAD~1Moves HEAD AND discards changesDestructive — local only
git restore file.txtDiscard working tree changes for that fileLocal only
git restore --staged file.txtUnstage but keep working tree changesLocal only
Never reset published commitsIf you've pushed, use revert (creates a new commit) — reset rewrites history and confuses collaborators.

25.8 Bisect — find the commit that broke a test

git bisect start
git bisect bad                           # current is broken
git bisect good v1.5.0                   # last known good
# git checks out a midpoint commit
npx playwright test tests/login.spec.ts  # is it broken here?
git bisect good     # or git bisect bad
# Repeat. Git narrows down via binary search.
git bisect reset                         # back to HEAD when done

# Or automate
git bisect run npx playwright test tests/login.spec.ts

25.9 Hooks

.git/hooks/pre-commit       # runs before each commit (lint, type-check, test)
.git/hooks/pre-push         # runs before push (full test suite, build)
.git/hooks/commit-msg       # validate message format (e.g. Conventional Commits)

Use Husky + lint-staged to manage hooks in JS projects:

npm i -D husky lint-staged
npx husky init
# .husky/pre-commit
npx lint-staged

# package.json
"lint-staged": {
  "*.{ts,tsx}": ["eslint --fix", "prettier --write"]
}

25.10 Common SDET scenarios

"I accidentally committed to main"

git branch feature/oops    # save current state to a branch
git reset --hard origin/main  # rewind main locally
git checkout feature/oops  # continue on the new branch

"I committed but forgot a file"

git add forgotten-file.ts
git commit --amend --no-edit
# If already pushed: git push --force-with-lease (only on YOUR branch)

"I want to undo only the last 2 commits but keep my changes"

git reset --soft HEAD~2     # commits gone, changes still staged

"I want to squash 5 commits into 1 before merging"

git rebase -i HEAD~5
# in the editor, change "pick" to "squash" (or "s") on lines 2..5
# save; git lets you edit the combined message

"Someone deleted my work after a force push"

git reflog            # shows every HEAD move including before the force push
git checkout abc1234  # the SHA before the push
git branch recover    # save it

25.11 .gitignore for SDET projects

node_modules/
dist/
build/

# Playwright outputs
playwright-report/
test-results/
blob-report/
allure-results/
.auth/
*.zip

# IDE
.vscode/
.idea/

# Env
.env
.env.local

# OS
.DS_Store
Thumbs.db

Module 41 — Git Q&A

Rebase vs merge — which do you use when?
Rebase your local feature branch onto main before pushing — keeps history linear. Use merge (or "squash-merge" via the UI) when integrating completed PRs into main. Never rebase a shared/public branch.
What's the difference between reset and revert?
Revert creates a NEW commit that undoes a previous one — safe on published history. Reset rewinds the branch pointer and rewrites history — only safe locally / on your own branch.
What's git stash for?
Temporarily shelf uncommitted changes so you can switch branches without losing them. Restore with git stash pop (apply + drop) or git stash apply (apply, keep stash).
How do you cherry-pick a commit?
git cherry-pick <sha> applies the change introduced by that commit to your current branch. Useful for backporting a fix from main to a release branch.
How do you find the commit that introduced a bug?
git bisect. Mark a known-good commit (git bisect good v1.5) and current bad (git bisect bad). Git binary-searches by checking out midpoints; you mark each as good or bad. With git bisect run <cmd> you automate it via a test command.
How do you safely force push?
Use git push --force-with-lease instead of --force. It refuses to push if someone else pushed to the same branch since your last fetch — protects against overwriting teammates' work.
What's a pre-commit hook good for?
Catching problems before they enter history — lint, type-check, format, run fast tests. Implemented via .git/hooks/pre-commit or Husky. Keep them fast (< 10s) or developers will bypass them.
How do you squash commits before merging?
git rebase -i HEAD~N — interactive rebase; mark commits as "squash" or "s" to combine. Or set the PR merge policy to "Squash and merge" on GitHub so it happens automatically.
You accidentally git reset --hard; can you recover?
Usually yes — git reflog shows every HEAD move. Find the SHA before your reset, git checkout <sha>, create a branch to save it. Reflog entries expire after ~90 days.
What's the difference between .gitignore and .gitattributes?
.gitignore: tells Git not to track listed paths. .gitattributes: per-path attributes — line endings, diff drivers, merge strategy, language for stats. Both matter for cross-OS dev (CRLF vs LF).
How do you set up Husky + lint-staged?
npm i -D husky lint-staged, npx husky init. Add npx lint-staged to the pre-commit hook. Configure lint-staged in package.json to run ESLint/Prettier on staged TS files. Pre-commit becomes self-installing for every contributor.
How do you handle merge conflicts?
Open the conflicted file; you'll see <<<<<<<, =======, >>>>>>> markers. Edit to the correct combined content, remove markers. git add resolved files, git rebase --continue (or git merge --continue). Abort with --abort if you want to start over.
What's a fast-forward merge?
When the target branch hasn't moved since you branched off, "merging" just advances the pointer — no merge commit needed. --no-ff forces a merge commit anyway, preserving branch history.
How do you find what changed between two branches?
git log main..feature shows commits on feature not on main. git diff main..feature shows file content diff. Add --stat for summary.