Post

Git Part 2 — Practical Exam (Week 2)

Git Part 2 — Practical Exam (Week 2)

Git Part 2 — Practical Exam (Week 2)

Format:Execute each step in your terminal or VSCode. Write the exact command you used and paste the output in the space provided.
Duration:~3.5 hours
Covers:VSCode Git integration, stash, rebase, interactive rebase, cherry-pick, reflog, pull requests, code review, team workflows, real-world troubleshooting
Prerequisite:Week 1 exam passed


Question 1 — VSCode Git Integration

Scenario:You want to perform your full Git workflow inside VSCode without touching the terminal.

Tasks — execute in order:

  1. Open a project folder in VSCode that has at least 3 commits in its history.
  2. Modify two different files in your editor.
  3. Open the Source Control panel(Ctrl+Shift+G / Cmd+Shift+G) — what do you see?
  4. Stage only one of the two files using the VSCode UI (click the + icon next to the file).
  5. Write a commit message in the input box at the top and commit using Ctrl+Enter / Cmd+Enter.
  6. Open a file, make a change, and view the inline diffby clicking on it in the Source Control panel.
  7. Use the Source Control panel’s “More Actions” (…)menu to find and click “Undo Last Commit”. What happened?
  8. Recommit the same change.
  9. Open the Timeline viewfor one of your files (bottom of the Explorer panel). What does it show?

Questions to answer:

  • What do the letters M, U, A, and D mean next to file names in the VSCode Explorer?
  • In VSCode, what is the difference between the “Sync Changes” button and the “Push” action?
  • How do you stage a specific PART of a file (not the whole file) using VSCode?

Answer — Question 1

Source Control panel (Ctrl+Shift+G) shows:

  • Changessection: modified/untracked files (not yet staged)
  • Staged Changessection: files ready to commit
  • Input box at top for commit message
  • Action buttons: Commit, Sync Changes, More Actions (…)

File status letters in Explorer:

  • MModified: file tracked by Git and has been changed
  • UUntracked: new file Git has never seen
  • AAdded: file staged for the first time (new file in staging area)
  • DDeleted: file was deleted from disk

“Sync Changes” vs “Push”:

  • Sync Changes= git pull followed by git push in one click. It first brings in any remote commits, then pushes yours.
  • Push= git push only. Does not pull first. Use “Sync Changes” when working with teammates. Use “Push” only when you are certain you are the only one pushing.

Stage a specific part of a file:In the inline diff view (click the file in Changes section), right-click on the specific lines you want to stage and choose “Stage Selected Ranges”. This stages only those lines, not the entire file.

“Undo Last Commit”:Equivalent to git reset --soft HEAD~1. The commit disappears from history but all changes come back staged in the Source Control panel, ready to recommit.


Question 2 — Branching and Conflict Resolution in VSCode

Scenario:You create a feature branch and trigger a merge conflict, then resolve it using VSCode’s merge editor.

Tasks — execute in order:

  1. Click the branch namein the VSCode status bar (bottom-left corner). What options appear?
  2. Create a new branch called feature-vscode-test from the status bar.
  3. Edit index.html (or any file) on this branch: change line 1 to "Version A from feature branch". Commit.
  4. Switch back to main via the status bar.
  5. Edit the same file, same line 1 to "Version B from main". Commit.
  6. From the status bar, switch to feature-vscode-test.
  7. Open Command Palette (Ctrl+Shift+P), search “Git: Merge Branch” and merge main into feature-vscode-test. A conflict occurs.
  8. Open the conflicted file — describe what you see in VSCode (buttons, colors, layout).
  9. Use the “Accept Current Change”button to resolve in favor of the feature branch version.
  10. Stage the resolved file and commit the merge using the Source Control panel.

Questions to answer:

  • In VSCode’s conflict view, what does “Current Change” refer to? What does “Incoming Change” refer to?
  • What is the 3-way merge editor in VSCode? How do you open it?
  • After resolving a conflict in VSCode, what terminal command would you run to verify the merge is complete?

Answer — Question 2

Status bar branch options:A Quick Pick dropdown appears showing: current branches to switch to, option to create new branch, option to create branch from a specific commit.

VSCode conflict view:The conflicted file opens with color-coded sections:

  • Current Change(green, top half): your version — the branch you are currently on
  • Incoming Change(blue, bottom half): the version coming from the branch being merged
  • Action buttonsabove each section: Accept Current Change, Accept Incoming Change, Accept Both Changes, Compare Changes

“Current Change”= the version on the branch you have checked out (HEAD).
“Incoming Change”= the version from the branch being merged in.

3-way merge editor:A more powerful conflict resolution UI showing three panels — incoming (left), current (right), and result (bottom, fully editable). Open it by clicking the “Resolve in Merge Editor”button that appears in the conflict file header. Available in VSCode 1.69+.

Verify merge is complete:

1
2
3
4
git status
# Should show: nothing to commit, working tree clean
git log --oneline --graph
# Should show the merge commit connecting both branches

Question 3 — Git Stash

Scenario:You are halfway through building a new feature when you are interrupted by an urgent bug fix needed on main.

Tasks — execute in order:

  1. Create a new branch feature-payment.
  2. Create payment.py and write some code (any content). Do NOT commit yet.
  3. Also modify an existing file (e.g., add a line to index.html). Do NOT commit.
  4. Run git status — confirm you have unstaged changes.
  5. Stash your work with a description: git stash save "WIP: payment form — 50% done".
  6. Run git status — what happened?
  7. Run git stash list — what do you see?
  8. Switch to main and fix a bug: create bugfix.txt with “Fixed critical crash” and commit.
  9. Switch back to feature-payment.
  10. Run git stash pop to restore your work.
  11. Run git status — confirm your changes are back.
  12. Create a SECOND stash: git stash save "WIP: second experiment".
  13. Run git stash list — how many stashes? What are their indices?
  14. Apply the FIRST (older) stash using git stash apply stash@{1}.
  15. Clean up: git stash clear.

Questions to answer:

  • What is the difference between git stash pop and git stash apply?
  • By default, does git stash save untracked files (new files Git has never seen)? If not, how do you include them?
  • Describe a real-world scenario where stash is useful. When would you use it instead of making a WIP commit?

Answer — Question 3

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
git checkout -b feature-payment

echo "def process_payment(): pass" > payment.py
echo "<!-- payment link -->" >> index.html

git status

git stash save "WIP: payment form — 50% done"
git status
git stash list

git checkout main
echo "Fixed critical crash" > bugfix.txt
git add bugfix.txt
git commit -m "Fix critical crash bug"

git checkout feature-payment
git stash pop
git status

git stash save "WIP: second experiment"
git stash list

git stash apply stash@{1}

git stash clear

After stash:git status shows “nothing to commit, working tree clean”. Changes are saved.

git stash list with two stashes:

1
2
stash@{0}: On feature-payment: WIP: second experiment
stash@{1}: On feature-payment: WIP: payment form — 50% done

stash@{0} is always the most recent.

git stash pop vs git stash apply:

  • pop — applies the stash AND removes it from the stash list. Use when you are done with it.
  • apply — applies the stash but KEEPS it in the list. Use when you want to apply the same stash to multiple branches, or want to keep it as a backup.

Untracked files:Not included by default. Use git stash --include-untracked to also stash new files Git has never seen.

Real-world scenario:You are building a new dashboard feature (not ready to commit — it is broken). Your manager says “there is a payment crash on production, fix it now.” Stash your dashboard work, switch to main, fix the crash, push. Switch back, pop stash, continue dashboard. The stash lets you context-switch cleanly without a half-done commit in history.

Stash vs WIP commit:Use stash for very short interruptions (minutes to hours). Use a WIP commit for longer breaks (end of day) or when you want the work in history “just in case.” You can always git reset --soft HEAD~1 to undo a WIP commit later.


Question 4 — Git Rebase (Basic)

Scenario:Your feature branch is behind main (teammates have committed while you worked). You want to update your branch cleanly.

Tasks — execute in order:

  1. Ensure you are on main. Create and commit a file main_update_1.txt (simulating a teammate’s commit).
  2. Create a file main_update_2.txt and commit it too.
  3. You now have a feature branch feature-payment that was created BEFORE these two commits. Switch to it.
  4. Run git log --oneline --graph --all — you can see main is 2 commits ahead of feature-payment.
  5. Rebase feature-payment onto main:
    1
    
    git rebase main
    
  6. Run git log --oneline --graph --all again — what changed?
  7. Now merge feature-payment into main. What type of merge happens?

Now practice rebase conflicts:

  1. On main, create shared.txt with content "Line A from main" and commit.
  2. Create branch feature-rebase-test. Change shared.txt to "Line B from feature" and commit.
  3. Switch to main and change shared.txt to "Line C from main update" and commit.
  4. Switch back to feature-rebase-test and run git rebase main.
  5. A conflict appears. Resolve it, then run git rebase --continue.
  6. Verify with git log --oneline --graph.

Questions to answer:

  • What is the difference between git merge and git rebase? Draw the history shape each produces.
  • What is the “golden rule” of rebase? Why is it dangerous to break it?
  • When should you use git rebase --abort?

Answer — Question 4

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git checkout main
echo "teammate commit 1" > main_update_1.txt
git add main_update_1.txt && git commit -m "Main update 1"

echo "teammate commit 2" > main_update_2.txt
git add main_update_2.txt && git commit -m "Main update 2"

git checkout feature-payment
git log --oneline --graph --all

git rebase main
git log --oneline --graph --all

git checkout main
git merge feature-payment

After rebase — git log --oneline --graph --all:

1
2
3
4
* f8a3c1b (HEAD -> feature-payment) WIP: payment form
* e2d1c0a (main) Main update 2
* d1c0b9a Main update 1
...

Feature commits are now replayed on TOP of the latest main commits. Linear history.

Merge type:Fast-forward — because feature-payment is now directly ahead of main, no divergence.

Rebase conflict resolution:

1
2
3
4
# Resolve shared.txt conflict in editor, remove all conflict markers
git add shared.txt
git rebase --continue
# Repeat if more conflicts

Merge vs Rebase history:

1
2
3
4
git merge result:                    git rebase result:
A  B  C  M (merge commit)        A  B  C  D'  E'
                                    (D and E replayed on top)
    D  E

Merge preserves the full “what happened when” history. Rebase creates a clean linear history as if you had started from the latest commit.

The golden rule:Never rebase commits that have already been pushed to a branch others are using. Rebase rewrites commit hashes. If you rebase a pushed commit, everyone else who has that commit now has a different version — their history diverges from yours and causes chaos when they try to push or pull.

When to git rebase --abort:When a conflict occurs during rebase and you are not sure how to resolve it, or you realize you need to rethink the approach. --abort returns you to the exact state before the rebase started.


Question 5 — Interactive Rebase

Scenario:Before submitting a pull request, your commit history has messy WIP commits that need cleaning up.

Tasks — execute in order:

  1. Create a fresh branch feature-cleanup.
  2. Create and commit 5 commits with these messages:
    • “Add login page structure”
    • “WIP saving”
    • “fix typo in login”
    • “more fixes”
    • “actually working now”
  3. Run git log --oneline — these 5 commits look messy.
  4. Run git rebase -i HEAD~5 to open the interactive rebase editor.
  5. Squash commits 2-5 into commit 1 (change pick to squash for commits 2-5).
  6. Save and close the editor. A new editor opens for the combined commit message — write a clean final message: "Add login page with validation".
  7. Save and close.
  8. Run git log --oneline — you should see just 1 clean commit instead of 5.

Now practice reword:

  1. Create 3 more commits: “oops typo in mesage”, “fix the bug”, “add test”.
  2. Use git rebase -i HEAD~3 and change the first commit’s action to reword.
  3. Correct the commit message to "Fix typo in login page message".
  4. Run git log --oneline to verify.

Questions to answer:

  • What are the 4 most commonly used interactive rebase actions? Describe each.
  • What does squashing commits mean? Why would you do it before a PR?
  • After interactive rebase, the commit hashes change. Why is this a problem if you have already pushed?

Answer — Question 5

Commands:

1
2
3
4
5
6
7
8
9
10
11
git checkout -b feature-cleanup

echo "login html" > login.html && git add login.html && git commit -m "Add login page structure"
echo "partial work" >> login.html && git add login.html && git commit -m "WIP saving"
echo "fixed typo" >> login.html && git add login.html && git commit -m "fix typo in login"
echo "more" >> login.html && git add login.html && git commit -m "more fixes"
echo "done" >> login.html && git add login.html && git commit -m "actually working now"

git log --oneline

git rebase -i HEAD~5

In the interactive rebase editor:

1
2
3
4
5
pick a1b2c3d Add login page structure
squash b2c3d4e WIP saving
squash c3d4e5f fix typo in login
squash d4e5f6a more fixes
squash e5f6a7b actually working now

Save :wq. Next editor — write combined message: "Add login page with validation". Save :wq.

After rebase:

1
2
3
git log --oneline
# f8a9b0c (HEAD -> feature-cleanup) Add login page with validation
# [earlier commits...]

4 interactive rebase actions:

  • pick — keep the commit exactly as it is
  • squash — combine this commit with the one ABOVE it (merges content and opens an editor to write a new combined message)
  • reword — keep the commit’s changes but open an editor to fix the commit message
  • drop — completely remove this commit from history (the changes are gone)

Why squash before a PR:“WIP saving”, “fix typo”, “more fixes” are meaningless to a code reviewer or to someone reading history 6 months later. Squashing produces one clean commit with a meaningful message — easier to review, revert, and understand.

Problem with squashing after push:Squashing rewrites commit hashes. If you push 5 commits and then squash them to 1 locally, your local branch and the remote branch have different histories. A regular git push will be rejected. You would need git push --force, which overwrites the remote — dangerous if others have already pulled those commits. Only interactive-rebase local commits you have NOT yet pushed.


Question 6 — Git Reflog

Scenario:You accidentally ran git reset --hard and your commits appear to be gone. You need to recover them.

Tasks — execute in order:

  1. On branch feature-cleanup, run git log --oneline and note the most recent commit hash.
  2. Create one more commit: echo "extra work" >> login.html && git add login.html && git commit -m "Add extra login feature".
  3. Note the hash of this new commit.
  4. Now simulate an accident: git reset --hard HEAD~2 — this removes 2 commits.
  5. Run git log --oneline — confirm the commits are gone.
  6. Run git reflog — find the hash of the commit you lost.
  7. Recover by running git reset --hard <lost-commit-hash>.
  8. Run git log --oneline — your commits should be back.

Now practice recovering a deleted branch:

  1. Create a branch important-work. Add a commit: create critical.txt with “Critical business logic” and commit.
  2. Note the commit hash: git log --oneline -1.
  3. Switch to main. Force-delete the branch: git branch -D important-work.
  4. The branch is gone. Run git reflog to find the commit hash.
  5. Recreate the branch: git checkout -b important-work <commit-hash>.
  6. Verify the commit is there: git log --oneline.

Questions to answer:

  • What is git reflog? How is it different from git log?
  • How long does reflog keep entries?
  • Is reflog available on the remote (GitHub)? If not, what does that mean for recovery?

Answer — Question 6

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git log --oneline

echo "extra work" >> login.html
git add login.html
git commit -m "Add extra login feature"

git reset --hard HEAD~2
git log --oneline    #commits gone!

git reflog
# Look for the line: "commit: Add extra login feature"
# Note the hash, e.g. d4e5f6a

git reset --hard d4e5f6a
git log --oneline    #commits restored

Deleted branch recovery:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
git checkout -b important-work
echo "Critical business logic" > critical.txt
git add critical.txt
git commit -m "Add critical business logic"
git log --oneline -1    #note hash, e.g. c3d4e5f

git checkout main
git branch -D important-work

git reflog
# Find: HEAD@{N}: commit: Add critical business logic

git checkout -b important-work c3d4e5f
git log --oneline    #branch and commit are back

git reflog vs git log:

  • git log shows the commit history of the current branch — only commits that are part of the current lineage.
  • git reflog shows every HEAD movement on this machine — checkouts, merges, resets, rebases, commits. It captures things that are no longer in any branch’s history.

How long reflog keeps entries:By default, 90 days for reachable commits and 30 days for unreachable (orphaned) commits. Configurable via git config gc.reflogExpire.

Is reflog on the remote?No. Reflog is local only. If you push commits and then hard-reset locally, you can recover from reflog. But if you never committed the work, or if reflog itself expired, recovery is not possible. This is why you should commit often— committed work can almost always be recovered.


Question 7 — Git Cherry-Pick

Scenario:You fixed a critical bug on a feature branch. The fix needs to go to main immediately without merging the entire unfinished feature.

Tasks — execute in order:

  1. Create branch feature-api. Make 3 commits:
    • Commit 1: "Start API refactor" — create api.py with content “API skeleton”
    • Commit 2: "Fix null pointer crash in user lookup" — add "null check fix" to api.py
    • Commit 3: "Add rate limiting (incomplete)" — add "rate limit WIP" to api.py
  2. Run git log --oneline on feature-api. Note the hash of Commit 2 (the bug fix).
  3. Switch to main.
  4. Cherry-pick just the bug fix commit: git cherry-pick <commit-2-hash>.
  5. Run git log --oneline on main — does the bug fix commit appear?
  6. Run git log --oneline on feature-api — is it still there too?
  7. Check cat api.py on main — does it contain only the “null check fix” line, or the other lines too?

Now practice a cherry-pick conflict:

  1. On main, create base.txt with "Line from main" and commit.
  2. On feature-api, create base.txt with "Different line" and commit.
  3. Cherry-pick that feature-api commit onto main.
  4. Resolve the conflict, then run git cherry-pick --continue.

Questions to answer:

  • What does cherry-pick do? How is it different from merge?
  • Does cherry-pick create a new commit on the target branch or reuse the original hash?
  • Name two real-world situations where cherry-pick is the right tool.

Answer — Question 7

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
git checkout -b feature-api

echo "API skeleton" > api.py
git add api.py && git commit -m "Start API refactor"

echo "null check fix" >> api.py
git add api.py && git commit -m "Fix null pointer crash in user lookup"

echo "rate limit WIP" >> api.py
git add api.py && git commit -m "Add rate limiting (incomplete)"

git log --oneline
# Note hash of "Fix null pointer crash..." e.g. b4c5d6e

git checkout main
git cherry-pick b4c5d6e

git log --oneline
# Bug fix commit appears on main

git checkout feature-api
git log --oneline
# Bug fix commit ALSO still on feature-api

cat api.py on main:

1
2
API skeleton
null check fix

Only the changes from that specific commit — not the rate limiting line (which was a different commit).

Cherry-pick conflict resolution:

1
2
3
# Edit base.txt to resolve conflict, remove conflict markers
git add base.txt
git cherry-pick --continue

What cherry-pick does:Applies the changes from one specific commit to the current branch as a new commit. Unlike merge (which brings the entire branch history), cherry-pick takes only the diff of one commit and replays it.

New commit or same hash:Cherry-pick creates a BRAND NEW commit with a different hash. The commit message is the same but the hash changes because the parent commit is different.

Two real-world situations:

  1. Hotfix backport: Bug was fixed on the development branch. You cherry-pick the fix commit to the release branch so it goes out immediately without shipping unfinished features.
  2. Selective feature port: A useful utility function was written on a long-running feature branch. You cherry-pick just that function commit to main so other teams can use it now.

Question 8 — Pull Request Workflow

Scenario:You complete a feature and submit it for code review via a proper pull request.

Tasks — execute in order:

  1. On your project repository, pull latest main: git checkout main && git pull.
  2. Create a feature branch: git checkout -b feature-user-profile.
  3. Create profile.html with meaningful HTML content and commit it.
  4. Add a profile.css file and commit it.
  5. Push the branch: git push -u origin feature-user-profile.
  6. Go to GitHub — click the “Compare & pull request” banner.
  7. Write a PR description with these sections:
    • What this PR does(one sentence)
    • How to test it(2-3 steps)
    • Notes(any warnings or dependencies)
  8. Assign yourself as reviewer and submit the PR.
  9. On GitHub, leave a review comment on one line of profile.html.
  10. Back in your terminal, respond to the comment: make a small change to profile.html, commit it, and push.
  11. Observe on GitHub that the PR automatically updated with your new commit.
  12. Approve the PR and merge using “Squash and merge”.
  13. Back in terminal: git checkout main && git pull to get the merged commit.
  14. Delete the local feature branch: git branch -d feature-user-profile.

Questions to answer:

  • What happens to a PR when you push additional commits to the same branch?
  • Explain “Squash and merge” vs “Merge commit” vs “Rebase and merge” as merge strategies.
  • What does it mean when a PR has “conflicts” and how do you resolve them?

Answer — Question 8

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
git checkout main && git pull
git checkout -b feature-user-profile

cat > profile.html << 'EOF'
<html><body>
  <h1>User Profile</h1>
  <p class="bio">About the user.</p>
</body></html>
EOF
git add profile.html
git commit -m "Add user profile HTML structure"

echo "h1 { font-size: 2rem; } .bio { color: #555; }" > profile.css
git add profile.css
git commit -m "Add profile page styles"

git push -u origin feature-user-profile

PR description example:

1
2
3
4
5
6
7
8
9
10
## What this PR does
Adds a user profile page with HTML structure and basic CSS styling.

## How to test it
1. Open profile.html in a browser
2. Verify the heading and bio text appear
3. Verify the h1 is larger than body text and bio text is grey

## Notes
This is the first step — JS interactivity will be added in the next PR.

After reviewer leaves a comment and you respond:

1
2
3
4
5
6
# Make the requested change
sed -i 's/About the user/Add your bio here/g' profile.html
git add profile.html
git commit -m "Update bio placeholder text per review feedback"
git push
# PR on GitHub automatically shows the new commit — no new PR needed

What happens when you push more commits:The PR automatically updates. Reviewers can see the new commit in the PR timeline. No need to close and re-open the PR.

Three merge strategies:

  • Squash and merge: All PR commits are squashed into one single commit on main. Clean history — one PR = one commit. Recommended for most teams.
  • Merge commit: All PR commits preserved, plus a new merge commit. History is complete but can get noisy with many small commits.
  • Rebase and merge: PR commits are replayed individually on top of main — no merge commit. Linear history with full commit detail. Requires clean, meaningful commit history.

PR conflicts:When main has new commits that touch the same files as your PR, GitHub shows “This branch has conflicts.” Resolve by:

1
2
3
4
5
git checkout main && git pull
git checkout feature-user-profile
git merge main        #or: git rebase main
# Resolve conflicts, commit
git push

The PR then shows as conflict-free.


Question 9 — Code Review Skills

Scenario:You are reviewing a teammate’s pull request. Write review comments for the code snippets below.

Part A — Read the following diff and write review comments:

1
2
3
+def get_user(id):
+    user = db.query("SELECT * FROM users WHERE id=" + id)
+    return user

Write at least 2 review comments for this code. Be specific and constructive.

Part B — Read this comment and rewrite it professionally: Original comment: “This is terrible. Why would you do this?”

Rewrite the comment explaining the specific problem and what to do instead.

Part C — As PR author, respond to this review comment: Reviewer says: “We always use const for variables that are not reassigned. Can you update this?”

1
let maxRetries = 3;

Write: (a) your professional response, and (b) the corrected code.

Questions to answer:

  • As a reviewer, what are the 4 things you should always check in a PR?
  • What is the difference between “Request Changes” and “Comment” on a GitHub review?
  • As a PR author, when is it appropriate to push back on a reviewer’s suggestion?

Answer — Question 9

Part A — Review comments for the SQL code: Comment 1 (Critical — Security):“This query is vulnerable to SQL injection because it directly concatenates user input into the SQL string. If id contains '; DROP TABLE users; --, it will execute malicious SQL. Please use parameterized queries instead:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
user = db.query("SELECT * FROM users WHERE id = ?", (id,))
```"

**Comment 2 (Correctness — Type safety):**"The parameter `id` is likely an integer in the database, but if `get_user()` is called with a string from a URL parameter, the concatenation will still work but may return unexpected results. Consider adding type validation: `if not isinstance(id, int): raise ValueError(...)` or let the parameterized query handle it."

**Part B — Rewritten review comment:**
Original: "This is terrible. Why would you do this?"

Rewritten: "This approach uses string concatenation to build the SQL query, which creates a SQL injection vulnerability  an attacker could pass malicious input as `id` and execute arbitrary SQL against the database. Please use parameterized queries instead, which separate the SQL structure from the data. Here is the safe pattern: `db.query('SELECT * FROM users WHERE id = ?', (id,))`. Let me know if you want me to pair on this."

**Part C — Author response:**
Response: "Good catch, thank you! You are right  `maxRetries` is never reassigned so `const` is clearer and safer. Updated."

Corrected code:
```js
const maxRetries = 3;

4 things to always check in a PR:

  1. Correctness— does the code do what the PR description says? Are there edge cases or error conditions not handled?
  2. Security— SQL injection, XSS, unvalidated inputs, secrets accidentally committed?
  3. Readability— can you understand this code in 6 months without the author present? Are names clear?
  4. Tests— are there tests for the new behavior? Do they cover the edge cases?

“Request Changes” vs “Comment”:

  • Comment: General feedback that does not block the merge. The PR can still be approved and merged.
  • Request Changes: Blocks the PR from being merged until you explicitly approve it. Use for issues that MUST be fixed before this code goes to production.

When to push back on a reviewer:When you have a good technical reason — explain it clearly and politely. For example: “I understand the preference for const, but in this case the value IS reassigned later in the function on line 47 — that is why I used let.” Always back your pushback with a reason. If the reviewer insists and you still disagree, escalate to a third person for a tie-break. Never just ignore feedback silently.


Question 10 — Team Workflow and Real-World Scenarios

Scenario:Solve each real-world problem that your team encounters.

Problem A: “I committed to main instead of a feature branch!” You made 2 commits directly on main that were meant to be on feature-dashboard. Fix it.

Tasks:

  1. Note the hash of the last good commit on main (before your 2 mistakes).
  2. Create feature-dashboard at the current state: git checkout -b feature-dashboard.
  3. Switch back to main.
  4. Reset main to the last good commit: git reset --hard <good-hash>.
  5. Your 2 commits are now only on feature-dashboard. Verify with git log --oneline on both branches.

Problem B: “My PR has conflicts because someone merged first” You open your PR and see “This branch has conflicts with the base branch.”

Tasks:

  1. Pull latest main to your local machine.
  2. On your feature branch, merge main (or rebase — your choice).
  3. Resolve any conflicts.
  4. Push to update the PR.
  5. Verify on GitHub that conflicts are resolved.

Problem C: “I forgot to pull before starting work” You created feature-blog a week ago. Meanwhile, main has 15 new commits. Your PR shows a huge diff because it is based on old code.

Tasks:

  1. On feature-blog, run git fetch to see how far behind you are.
  2. Run git log --oneline main..origin/main to see the 15 new commits.
  3. Update your branch: git rebase origin/main.
  4. Push the updated branch: git push --force-with-lease origin feature-blog. (Why --force-with-lease and not --force?)

Questions to answer:

  • In Problem A, why did you need to create the branch BEFORE resetting main?
  • What is --force-with-lease? How is it safer than --force?
  • Name the GitHub Flow steps in order (the simplest professional workflow).

Answer — Question 10

Problem A commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
git log --oneline
# a3b4c5d last mistake commit
# b4c5d6e first mistake commit
# c5d6e7f last good commit  note this hash

git checkout -b feature-dashboard   #create branch at current (mistaken) state

git checkout main
git reset --hard c5d6e7f             #move main back to last good commit

git log --oneline                    #main: ends at c5d6e7f
git checkout feature-dashboard
git log --oneline                    #feature-dashboard: has both mistake commits

Why create branch BEFORE resetting:If you reset main first, those commits become unreachable (orphaned). By creating feature-dashboard at the current state, the commits are preserved — the branch pointer keeps them alive.

Problem B commands:

1
2
3
4
5
6
7
git checkout main && git pull
git checkout feature-my-branch
git merge main                   #or: git rebase main
# resolve conflicts
git add .
git commit
git push

Problem C commands:

1
2
3
4
5
6
7
git fetch
git log --oneline main..origin/main   #see 15 new commits

git rebase origin/main               #replay feature commits on top of latest main
# resolve any conflicts

git push --force-with-lease origin feature-blog

--force-with-lease vs --force:

  • --force overwrites the remote branch unconditionally. If a teammate pushed to that branch between your last fetch and now, their commits are silently lost.
  • --force-with-lease checks: “does the remote look exactly like I last fetched it?” If someone else has pushed in the meantime, it refuses and tells you — protecting their work. Always prefer --force-with-lease.

GitHub Flow steps (in order):

  1. git checkout main && git pull — start from latest main
  2. git checkout -b feature-name — create a feature branch
  3. Code, commit, repeat — make commits on the branch
  4. git push -u origin feature-name — push the branch
  5. Open a Pull Request on GitHub — request review
  6. Address review comments — push more commits to the same branch
  7. Get approval — reviewer approves
  8. Merge the PR — squash and merge into main
  9. git pull on main — sync locally
  10. git branch -d feature-name — delete the local branch

Bonus Challenge — Full Week 2 Workflow

Without any hints, complete the following from scratch. Write your exact commands.

Scenario:You are a developer on a small team building a task management app. Demonstrate all Week 2 skills.

  1. Clone the my_project repository to a fresh folder team_project_clone.
  2. Create branch feature-notifications. Make 4 commits with messy messages like “wip”, “fix”, “more”, “done”.
  3. Use interactive rebase to squash all 4 into one clean commit: "Add email notification system".
  4. Meanwhile, simulate a teammate’s commit on main by editing a file via the GitHub web UI.
  5. Fetch and rebase your branch onto the latest main.
  6. Push feature-notifications to the remote.
  7. Open a PR with a proper description (what, how to test, notes).
  8. Create another branch feature-reporting. Make 1 commit: “Add monthly report generator”.
  9. Your manager says: “The report generator is needed on main TODAY — but feature-reporting is not ready.” Cherry-pick just the report commit to main and push.
  10. Back on feature-notifications, simulate getting interrupted: make an unstaged change, stash it.
  11. Switch to main, pull latest, switch back, pop the stash.
  12. Merge feature-notifications into main. If there are conflicts, resolve them.
  13. Use git reflog to find when you stashed and verify the operations in your history.
  14. Final: git log --oneline --graph on main should tell a clear story.

Answer — Bonus Challenge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 1. Clone
git clone <remote-url> team_project_clone
cd team_project_clone

# 2. Create branch with messy commits
git checkout -b feature-notifications

echo "notif v1" > notifications.py && git add notifications.py && git commit -m "wip"
echo "notif v2" >> notifications.py && git add notifications.py && git commit -m "fix"
echo "notif v3" >> notifications.py && git add notifications.py && git commit -m "more"
echo "notif v4" >> notifications.py && git add notifications.py && git commit -m "done"

# 3. Interactive rebase — squash 4 commits to 1
git rebase -i HEAD~4
# Change 3x "pick" to "squash", keep first as "pick"
# Write new message: "Add email notification system"

# 4. Teammate commits on main — done via GitHub web UI

# 5. Fetch and rebase
git fetch origin
git rebase origin/main
# Resolve any conflicts: git add . && git rebase --continue

# 6. Push feature branch
git push -u origin feature-notifications

# 7. Open PR on GitHub — write full description

# 8. Create reporting branch
git checkout main && git pull
git checkout -b feature-reporting
echo "def monthly_report(): pass" > report.py
git add report.py && git commit -m "Add monthly report generator"
REPORT_HASH=$(git log --oneline -1 | cut -d' ' -f1)

# 9. Cherry-pick report commit to main
git checkout main
git cherry-pick $REPORT_HASH
git push

# 10. Stash on feature-notifications
git checkout feature-notifications
echo "interrupted work" >> notifications.py
git stash save "WIP: interrupted while adding retry logic"

# 11. Switch to main, pull, switch back, pop
git checkout main && git pull
git checkout feature-notifications
git stash pop

# 12. Merge feature-notifications
git checkout main
git merge feature-notifications
# Resolve any conflicts: git add . && git commit

# 13. View reflog
git reflog
# You will see: stash, pop, cherry-pick, rebase, squash all recorded

# 14. View final history
git log --oneline --graph

Expected git log --oneline --graph on main (roughly):

1
2
3
4
5
6
7
*   Merge branch 'feature-notifications'
|\
| * Add email notification system
|/
* Add monthly report generator
* [teammate's commit from GitHub UI]
* [earlier commits...]

Good luck! Mastering these skills means you are ready for professional team development.

This post is licensed under CC BY 4.0 by the author.