🔧 Git & GitHub FoundationHands-On · Part 6 of 7
📖
Concept First
Understand the why before you dive in?
Read the Concept Guide →

Running Project: my-notes

Starting state (from Part 5): five commits on main including one merge commit, annotated tag v1.0 on the latest commit, notes.txt (2 lines) and todos.txt.

This post has two sections: Section A covers rebase and fast-forward merge using a feature/daily-log branch. Section B covers branch deletion and reflog recovery.

Section A — Rebase a Branch onto Main

Step 1 — Create a Feature Branch

Why: You need a branch that will diverge from main. Create it now, before you make the diverging commit on main.

git switch -c feature/daily-log

Expected output:

Switched to a new branch 'feature/daily-log'

Self-check: git branch shows * feature/daily-log and main. Both point to the same commit right now.

Step 2 — Make Two Commits on the Feature Branch

Why: Two commits on the branch means the rebase will replay two commits — clearly demonstrating that the full commit sequence moves, not just the latest change.

Commit 1 — Create goals.txt: Create a new file in my-notes/:

Goal 1: Complete the Git & GitHub Foundation series.
Goal 2: Push my notes to GitHub in Part 7.

Stage and commit:

git add goals.txt
git commit -m "Add goals file for the learning project"

Commit 2 — Update todos.txt: Open todos.txt and replace its content with:

TODO: read Git & GitHub Part 4 ✓
TODO: read Git & GitHub Part 5 ✓
TODO: read Git & GitHub Part 6 (in progress)

Stage and commit:

git add todos.txt
git commit -m "Update todos: mark Parts 4 and 5 complete"

Self-check: git log --oneline on feature/daily-log shows 7 commits total (5 from main + 2 new).

Step 3 — Add a New Commit on Main (Create Divergence)

Why: For rebase to be meaningful, main must have a commit that the feature branch doesn't have. Without this, there's nothing to rebase onto — it would be a no-op. Switching to main and committing creates the genuine divergence rebase is designed to solve.

git switch main

Create a new file progress.txt in my-notes/:

Progress log started: 2026-08-07
Parts completed: 1 through 5

Stage and commit:

git add progress.txt
git commit -m "Add progress log to main"

State check: main is now one commit ahead of where feature/daily-log branched. The branches have diverged: main has progress.txt, the feature branch has goals.txt and the updated todos.txt. A direct merge would create a merge commit.

Self-check: git log --oneline --graph --all shows two separate branch lines diverging from the common ancestor at commit 5.

Step 4 — Rebase the Feature Branch

Why: With main one commit ahead, rebasing replays feature/daily-log's two commits on top of main's tip. After this, the feature branch will be directly ahead of main with no divergence — enabling a fast-forward merge.

git switch feature/daily-log
git rebase main

Expected output:

Successfully rebased and updated refs/heads/feature/daily-log.

Inspect the result:

git log --oneline --graph --all

Expected output:

* abc7890 (HEAD -> feature/daily-log) Update todos: mark Parts 4 and 5 complete
* def4567 Add goals file for the learning project
* ghi1234 (main) Add progress log to main
* jkl3456 (tag: v1.0) Add Day 2 note: diff, stash, and tags
*   mno2345 Merge feature/add-todos: combine Day 1 note edits
... (earlier commits)

Read the output: The two feature branch commits (abc7890 and def4567) now sit on top of main's latest commit (ghi1234). The graph shows no fork — a single straight line. These are new commit objects (new hashes) containing the same changes as before.

Self-check: No forked graph lines. Feature branch commits appear above main's tip in a straight chain.

Step 5 — Fast-Forward Merge into Main

Why: Because the feature branch is now directly ahead of main with no divergence, the merge is a fast-forward: Git simply moves the main pointer forward two commits. No merge commit. No fork in the graph.

git switch main
git merge feature/daily-log

Expected output:

Updating ghi1234..abc7890
Fast-forward
 goals.txt | 2 ++
 todos.txt | 3 ++-
 2 files changed, 4 insertions(+), 1 deletion(-)

The word "Fast-forward" in the output confirms no merge commit was created. Git just advanced the pointer.

Verify the log:

git log --oneline --graph

Expected output: A perfectly straight line of commits. No branching symbols. The feature branch work looks as if it was always committed directly on main.

State of my-notes after Section A: notes.txt, todos.txt, goals.txt, progress.txt — all on main. Eight commits total. Tag v1.0 on commit 5. Linear history.

Self-check: "Fast-forward" in merge output. git log --graph shows no fork. Four files in the working directory.

Section B — Delete a Branch and Recover It With Reflog

Step 6 — Create a Branch and Make a Commit

Why: You need an unmerged branch to delete. Creating a commit on it ensures there's real data to recover. The goal is to simulate an accident — deleting a branch before merging it — and then recovering the "lost" work.

git switch -c experiment

Create scratch.txt in my-notes/ with any content:

Scratch notes: testing reflog recovery.
This file was on a deleted branch.

Stage and commit:

git add scratch.txt
git commit -m "Add scratch notes on experiment branch"

Self-check: git log --oneline shows a new commit at the top with your message. scratch.txt exists in the working directory.

Step 7 — Switch Back to Main and Delete the Branch

Why: You must be on a different branch to delete the current one. The -D flag forces deletion even though the branch isn't merged into main — this is the "accident."

git switch main

Verify scratch.txt is gone:

cat scratch.txt     # Mac / Linux / Git Bash
type scratch.txt    # Windows CMD

Expected output: Error — the file doesn't exist on main. Switching branches restored main's working directory, which doesn't include scratch.txt.

Now force-delete the branch:

git branch -D experiment

Expected output:

Deleted branch experiment (was abc1234).

Git prints the hash of the deleted branch's tip. Note it — though reflog will find it too. The commit still exists in the object store. The branch pointer was removed. The commit is now orphaned: no branch, no tag, nothing pointing to it. Git will eventually garbage-collect it, but not for 90 days.

Self-check: git branch no longer lists experiment. scratch.txt absent from working directory.

Step 8 — Find the Orphaned Commit in Reflog

Why: Reflog records every HEAD position, including the HEAD position while you were on the experiment branch. The commit is still in the object store — you just need its hash.

git reflog

Expected output (excerpt):

abc7890 HEAD@{0}: checkout: moving from experiment to main
b3d9f12 HEAD@{1}: commit: Add scratch notes on experiment branch
abc7890 HEAD@{2}: checkout: moving from main to experiment
...

Read the reflog:

  • HEAD@{0} — the current position (main after the branch switch)
  • HEAD@{1} — where HEAD was one step ago — the commit on the experiment branch. This is the hash you need.
  • HEAD@{2} — the switch to experiment

The hash next to HEAD@{1} (e.g., b3d9f12 in this example) is the orphaned commit. Your actual hash will differ.

Self-check: You can see the "Add scratch notes on experiment branch" commit message in the reflog. Its hash is identifiable.

Step 9 — Recover the Commit

Why: git reset --hard <hash> moves the current branch's pointer to the specified commit and updates the working directory to match. Running this on main while pointing at the orphaned commit restores scratch.txt and brings that commit back into main's history.

git reset --hard b3d9f12

Replace b3d9f12 with the actual hash from your HEAD@{1} reflog entry.

Expected output:

HEAD is now at b3d9f12 Add scratch notes on experiment branch

Verify recovery:

cat scratch.txt     # Mac / Linux / Git Bash
type scratch.txt    # Windows CMD

Expected output:

Scratch notes: testing reflog recovery.
This file was on a deleted branch.

The file is back. The commit is back. The "accident" is undone.

Self-check: scratch.txt is present. git log --oneline shows "Add scratch notes on experiment branch" at the top of main's history.

Step 10 — Confirm the Recovery Is in Reflog

Why: Every operation — including the recovery itself — is recorded by reflog. Running it again shows the full sequence: branch creation, commit, branch switch, deletion, and reset-recovery. This closes the loop.

git reflog

Expected output (top entries):

b3d9f12 HEAD@{0}: reset: moving to b3d9f12
abc7890 HEAD@{1}: checkout: moving from experiment to main
b3d9f12 HEAD@{2}: commit: Add scratch notes on experiment branch
abc7890 HEAD@{3}: checkout: moving from main to experiment
...

The topmost entry is the reset that performed the recovery. The reflog now records its own recovery operation — meaning if you accidentally recovered to the wrong commit, you could undo the undo using the same technique.

Self-check: "reset: moving to b3d9f12" is the newest entry in your reflog.

State of my-notes at the End of This Post

my-notes/
├── notes.txt      ← 2 lines (Day 1 + Day 2)
├── todos.txt      ← 3 lines (Parts 4, 5 done, Part 6 in progress)
├── goals.txt      ← 2 goals from feature/daily-log
├── progress.txt   ← progress log from main
└── scratch.txt    ← recovered from deleted experiment branch

Git history: 9+ commits on main (linear after rebase; includes recovered commit)
Tag: v1.0 on commit 5 (Add Day 2 note)

Part 7 starts exactly here. You'll push this repository to GitHub, set the remote, and run your first git push.

🛠
Next Step
Continue the hands-on series.
Go to Part 7: Remote, Push, and Pull →