Skip to content
GitTree

Lesson 31 · Recovery

Keep detached work and start an orphan history

Name a commit made on a detached HEAD before you leave it, then start an orphan branch in a separate copy and see what it removes and what it keeps.

Progress saved in this browser.

Playing loads the video from YouTube (privacy-enhanced mode).

English narration · English and Arabic captions

Open the video pageWatch on YouTube

What you will learn

  • Tell attached, detached and unborn HEAD apart.
  • Put a branch on a detached commit so it stays reachable.
  • Predict what an orphan branch does to tracked files, untracked files and the old branch.
  • Read a checkout refusal caused by local changes.

Before you start

Two disposable copies of lesson 18's result M. In the first, you check out the commit M itself instead of the branch, make a small stylesheet change there, and want to keep it. In the second, you want a brand-new, unrelated history for a documentation-only branch without touching main.

Everything is local and on the Free plan (GitTree asks you to sign in): no hosting account, no provider, no internet. Every identity is fictional (example.com). Never run this lesson in a real project.

Make the changes in GitTree or with the native Git commands, not both in the same copy. Reading the result is always safe. The menu labels come from GitTree 2.0's own strings and the native commands were run with Git 2.43 in disposable copies. The GitTree steps were not run on macOS, Windows or Linux.

Concept and state effects

HEAD normally names a branch, and committing moves that branch. When HEAD names a commit directly it is detached: a new commit is real, but nothing names it, so switching away leaves it reachable only through the reflog. Putting a branch on it fixes that. Creating a branch does not switch to it unless you choose to.

An orphan branch is different. HEAD names a branch that has no commit yet (an unborn branch). Its first commit has no parent, so it shares no history with main. Git removes the tracked files from the working folder; untracked and ignored files stay. If the old .gitignore was tracked, its rules go with it, and files it ignored can now show as untracked. Your old branch keeps every commit.

Both states are normal, not damage. A checkout is refused when it would overwrite local changes; nothing is lost.

Prepare the exact project state

Start from two separate ordinary copies of lesson 18's result and leave your original untouched. The bodies below are the baseline stylesheet (BASE_CSS), the widened one (LAYOUT), a dirty edit (DIRTY_LAYOUT), the orphan's readme (ORPHAN_README), a local note (LOCAL_NOTE) and an ignored note (IGNORED_NOTE).

Complete file inputs

Save each full UTF-8/LF body only in its stated phase, with a final newline and without Markdown fences. BASE_CSS is the inherited 48rem reference; LAYOUT is the 52rem commit; DIRTY_LAYOUT is a separate unstaged 56rem replay. The two note files belong only to the survivor variant. Unchanged main files inherit the complete L018/M input.

styles.css — BASE_CSS

File excerpt · css
:root {
  color-scheme: light;
  --page: #f7f8fa;
  --surface: #ffffff;
  --text: #17212e;
  --link: #154ab5;
  --line: #bac4d0;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--text);
  font: 1rem/1.6 system-ui, sans-serif;
}

a {
  color: var(--link);
}

.site-header,
main,
footer {
  width: min(68rem, 100% - 2rem);
  margin-inline: auto;
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1rem;
}

.hero {
  max-width: 48rem;
  padding-block: 3rem;
}

h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: 1.2;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
}

.feature-card {
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--surface);
}

section {
  margin-block-end: 2rem;
}

button,
input {
  font: inherit;
}

button,
input {
  padding: 0.5rem 0.75rem;
}

:focus-visible {
  outline: 3px solid var(--link);
  outline-offset: 3px;
}

form {
  display: grid;
  max-width: 28rem;
  gap: 0.5rem;
}

styles.css — LAYOUT

File excerpt · css
:root {
  color-scheme: light;
  --page: #f7f8fa;
  --surface: #ffffff;
  --text: #17212e;
  --link: #154ab5;
  --line: #bac4d0;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--text);
  font: 1rem/1.6 system-ui, sans-serif;
}

a {
  color: var(--link);
}

.site-header,
main,
footer {
  width: min(68rem, 100% - 2rem);
  margin-inline: auto;
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1rem;
}

.hero {
  max-width: 52rem;
  padding-block: 3rem;
}

h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: 1.2;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
}

.feature-card {
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--surface);
}

section {
  margin-block-end: 2rem;
}

button,
input {
  font: inherit;
}

button,
input {
  padding: 0.5rem 0.75rem;
}

:focus-visible {
  outline: 3px solid var(--link);
  outline-offset: 3px;
}

form {
  display: grid;
  max-width: 28rem;
  gap: 0.5rem;
}

styles.css — DIRTY_LAYOUT

File excerpt · css
:root {
  color-scheme: light;
  --page: #f7f8fa;
  --surface: #ffffff;
  --text: #17212e;
  --link: #154ab5;
  --line: #bac4d0;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--text);
  font: 1rem/1.6 system-ui, sans-serif;
}

a {
  color: var(--link);
}

.site-header,
main,
footer {
  width: min(68rem, 100% - 2rem);
  margin-inline: auto;
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1rem;
}

.hero {
  max-width: 56rem;
  padding-block: 3rem;
}

h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: 1.2;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
}

.feature-card {
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--surface);
}

section {
  margin-block-end: 2rem;
}

button,
input {
  font: inherit;
}

button,
input {
  padding: 0.5rem 0.75rem;
}

:focus-visible {
  outline: 3px solid var(--link);
  outline-offset: 3px;
}

form {
  display: grid;
  max-width: 28rem;
  gap: 0.5rem;
}

README.md — ORPHAN_README

Output
# GitTree Features documentation

This branch contains a documentation-only history for the fictional course project.
The landing page remains on main; no form submits information to a server.
Keep useful detached experiments under a branch name before switching away.

local-review.txt — LOCAL_NOTE

Output
Local rehearsal note; preserve this file.

reports/preview.txt — IGNORED_NOTE

Output
Ignored rehearsal preview; preserve these bytes.

In GitTree

Open the palette with Command+Shift+P (Ctrl+Shift+P on Ubuntu).

  1. In the first copy, right-click the commit M in the graph and choose Check Out This Commit. The branch picker reads "Detached at …" and the graph shows HEAD, detached.
  2. Save LAYOUT as styles.css, stage that file and commit it with the Summary style: widen the hero reading measure. The panel counts the change "on a detached HEAD" while you work.
  3. Right-click the new commit and choose Create Branch Here…. Name it experiment/layout, leave the option to check it out switched off and choose Create Branch. The notice says "Created experiment/layout." and HEAD is still detached.
  4. Right-click the experiment/layout label and choose Check Out Branch. HEAD is attached again, styles.css is LAYOUT and main still points at M.
  5. Dirty checkout. In another copy, detach at experiment/layout, edit styles.css to DIRTY_LAYOUT without committing and try to check out main. The dialog "Local changes are in the way of main" lists the file and offers Commit First, Stash and Switch and Discard and Switch. Choose Cancel; discarding keeps a copy under refs/ogt/trash but is not needed here.
  6. Orphan. In the second copy, with a clean work tree, run Create Orphan Branch… from the palette. In "Create an orphan branch in …?" type orphan-docs. The dialog states how many tracked files leave the working folder, that untracked and ignored files stay, and that the old commit is kept under refs/ogt/trash. Cancel has the focus. Choose Create Orphan Branch.
  7. The notice says "Switched to the new orphan branch orphan-docs. Undo switches back." The folder has no tracked files and the graph shows no commit on the new branch. Create README.md from ORPHAN_README, stage it and commit it with the Summary docs: start the separate project guide. The new commit has no parent.
  8. Compare: main still has every commit; the orphan branch is a separate root. Do not push either copy.

For every ordinary commit in the course: Summary is the stated subject, Description is empty and the options Amend Previous Commit, Skip Hooks, Add Signed-off-by, Different Author, Push After Commit and Allow Empty Commit stay off.

In the terminal

In the first copy:

Command · bash
BASE_SHA="$(git rev-parse main)"
git switch --detach "$BASE_SHA"
git symbolic-ref --quiet --short HEAD; echo $?

The last command prints 1: HEAD is not a branch. Save LAYOUT as styles.css, then:

Command · bash
git commit --quiet -am "style: widen the hero reading measure"
git branch experiment/layout
git symbolic-ref --quiet --short HEAD; echo $?
git rev-parse HEAD^ main
git switch --quiet experiment/layout
git symbolic-ref --short HEAD
git status --short
Output
1
experiment/layout

The creating command does not attach HEAD (the second query still prints 1), the two IDs from rev-parse are equal (M), and the status is empty. In another copy detached at experiment/layout, edit styles.css and run git switch main: Git refuses with "Your local changes to the following files would be overwritten by checkout" and the file is untouched.

In the second copy, with a clean tree:

Command · bash
git switch --orphan orphan-docs
git symbolic-ref --short HEAD
git rev-parse --verify --quiet HEAD; echo $?
git ls-files
Output
orphan-docs
1

The branch exists but has no commit (rev-parse exits 1) and ls-files prints nothing. Tracked files are gone from the folder, while local-review.txt and reports/ stay and now show as untracked. Create README.md, then:

Command · bash
git add -- README.md
git commit --quiet -m "docs: start the separate project guide"
git ls-tree -r --name-only HEAD
git rev-list --max-parents=0 --count HEAD
git merge-base --all main orphan-docs; echo $?
Output
README.md
1
1

The new branch has one root commit and no common ancestor with main (merge-base prints nothing and exits 1).

Result

Where you end: the detached commit is named experiment/layout and checked out, main still points at M and the tree is clean. The orphan copy has orphan-docs with one root commit that has no parent, the old tracked files are gone from the folder but kept by main, and nothing was pushed.

Common mistakes

  • Committing on a detached HEAD and switching away without a branch, which leaves the commit reachable only through the reflog.
  • Expecting Create Branch Here… to switch to the new branch.
  • Starting an orphan branch with uncommitted tracked changes, or thinking it deletes main.
  • Assuming a file that was ignored stays ignored after its .gitignore leaves the folder.

If something goes wrong

If you left a detached commit without a name, open the reflog (lesson 29) and put a branch on it. If an orphan branch was a mistake, Undo switches back while the new branch has no commit; otherwise check out main again. A refused checkout changed nothing.

Check your understanding

Answer each question, then read why. Questions marked “Required” count toward completing the lesson.

  1. Question 1 of 6

    What is a detached HEAD?

    Choose one answer.

  2. Question 2 of 6

    Which of these are true about an orphan branch? (multiple choice)

    Choose every answer that applies.

  3. Question 3 of 6
    Required

    You commit on a detached HEAD, create experiment/layout there without checking it out, and look at HEAD. What is it?

    Choose one answer.

  4. Question 4 of 6

    Change the input: the work tree has an uncommitted edit to styles.css when you try to check out main. What happens?

    Choose one answer.

  5. Question 5 of 6
    Required

    git ls-files prints nothing right after git switch --orphan orphan-docs. Is something wrong?

    Choose one answer.

  6. Question 6 of 6
    Required

    After the first commit on orphan-docs, which readbacks show it is a separate root? (multiple choice)

    Choose every answer that applies.

0 of 6 correct

Answer the required questions correctly to complete the lesson.

Checked in this browser.

Practice

Report where HEAD points in each state (branch, detached, unborn), the ID of the detached commit and the branch on it, and what git ls-files printed on the orphan branch. Then predict what happens to an ignored file when the .gitignore is gone.

Your own confirmation: the site cannot see your computer.

To complete the lesson, answer the required questions correctly and confirm the practical exercise.

Recap

  • A detached commit needs a branch to stay reachable.
  • An orphan branch starts a separate root and removes tracked files from the folder.
  • A refused checkout protects your edit.

Glossary terms introduced

Detached HEAD — HEAD منفصل; orphan branch — فرع يتيم; unborn branch — فرع غير مولود; root commit — إيداع جذري.

Video transcript

The narration of the video, cue by cue.

Show the transcript
  1. Where is HEAD? Three states.
  2. HEAD can name a branch, a commit, or a branch with no commit yet.
  3. A commit made while detached is real, but nothing names it.
  4. A branch on that commit keeps it reachable.
  5. Right-click the commit M, and choose Check Out This Commit.
  6. Change the stylesheet, stage it, and commit once.
  7. Right-click the new commit, and create a branch here, without checking it out.
  8. Now check out the branch. HEAD is attached again.
  9. With an unsaved edit, a checkout is refused. Cancel keeps everything.
  10. Commit first, stash and switch, or discard. Discard is the one to avoid.
  11. For a new history, run Create Orphan Branch from the palette.
  12. It counts the tracked files that leave. Untracked and ignored ones stay.
  13. Confirm. The folder has no tracked files.
  14. Add a readme, and commit. This commit has no parent.
  15. In the terminal, symbolic-ref exits 1 when HEAD is detached.
  16. On an unborn branch, rev-parse of HEAD fails.
  17. After the commit, merge-base finds no common ancestor.
  18. If you left without a branch, the reflog still has it.
  19. Practice: name HEAD in each state, then predict an ignored file.
  20. Next, recover from mistakes after pushing.