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
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
| Command | What it does | Safe on public history? |
|---|---|---|
git revert abc1234 | Creates a NEW commit that undoes abc1234 | Yes |
git reset --soft HEAD~1 | Moves HEAD back 1 commit; changes stay staged | Local only |
git reset --mixed HEAD~1 | Moves HEAD; changes unstaged but kept | Local only |
git reset --hard HEAD~1 | Moves HEAD AND discards changes | Destructive — local only |
git restore file.txt | Discard working tree changes for that file | Local only |
git restore --staged file.txt | Unstage but keep working tree changes | Local 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.