الدرس 31 · الاستعادة
احفظ العمل المنفصل وابدأ تاريخًا يتيمًا
سمِّ إيداعًا أُجري على HEAD منفصل قبل أن تغادره، ثم ابدأ فرعًا يتيمًا في نسخة منفصلة وانظر ما يزيله وما يُبقيه.
- المستوى: متوسط
- قراءة 9 دقائق
- فيديو 5 دقائق
- آخر تحديث
- قبل هذا الدرس: أنشئ فرع الميزة واحذف المرجع التجريبي وحده, العثور على إيداع ضائع بسجل المراجع (reflog)
حُفظ التقدّم في هذا المتصفح.
يحمّل التشغيل الفيديو من YouTube (وضع الخصوصية المحسّنة).
ما ستتعلمه
- أن تميّز HEAD المربوط والمنفصل وغير المولود.
- أن تضع فرعًا على إيداع منفصل ليبقى قابلًا للوصول.
- أن تتوقع ما يفعله الفرع اليتيم بالملفات المتتبَّعة وغير المتتبَّعة والفرع القديم.
- أن تقرأ رفض تبديل سببه تغييرات محلية.
قبل البدء
نسختان مؤقتتان من ناتج الدرس 18 (M). في الأولى تبدّل إلى الإيداع M نفسه بدل الفرع وتجري تغييرًا صغيرًا على ورقة الأنماط وتريد الاحتفاظ به. وفي الثانية تريد تاريخًا جديدًا غير مرتبط لفرع توثيق فقط دون المساس بـ main.
كل شيء هنا محلي وضمن الخطة المجانية (يطلب GitTree تسجيل الدخول): دون حساب استضافة أو مزوّد أو إنترنت. كل الهويات خيالية (example.com). لا تنفّذ الدرس في مشروع حقيقي.
نفّذ التغييرات في GitTree أو بأوامر Git الأصلية، لا بالطريقتين في النسخة نفسها. القراءة بعد التنفيذ آمنة دائمًا. تسميات القوائم مأخوذة من نصوص GitTree 2.0 نفسها، وشُغّلت الأوامر الأصلية بـ Git 2.43 في نسخ مؤقتة. لم تُشغَّل خطوات GitTree على macOS أو Windows أو Linux.
الفكرة وأثر الأفعال
يسمّي HEAD عادةً فرعًا، والإيداع يحرّك ذلك الفرع. وحين يسمّي HEAD إيداعًا مباشرة يكون منفصلًا: الإيداع الجديد حقيقي لكن لا شيء يسمّيه، فإن غادرته صار قابلًا للوصول عبر سجل المراجع فقط. ووضع فرع عليه يعالج ذلك. وإنشاء فرع لا يبدّل إليه ما لم تختر ذلك.
أما الفرع اليتيم فشيء آخر. يسمّي HEAD فرعًا لا إيداع فيه بعد (فرع غير مولود). وإيداعه الأول بلا أب، فلا يشارك main أي تاريخ. ويُخرج Git الملفات المتتبَّعة من مجلد العمل؛ وتبقى غير المتتبَّعة والمتجاهَلة. وإن كان .gitignore القديم متتبَّعًا فقواعده تذهب معه، وقد تظهر الملفات التي تجاهلها غير متتبَّعة الآن. ويحتفظ فرعك القديم بكل إيداعاته.
الحالتان طبيعيتان لا ضرر فيهما. ويُرفض التبديل حين يكتب فوق تغييرات محلية؛ ولا يضيع شيء.
إعداد حالة المشروع المحددة
ابدأ من نسختين عاديتين منفصلتين من ناتج الدرس 18 وأبقِ أصلك دون مساس. النصوص أدناه هي ورقة الأنماط الأساسية (BASE_CSS) والموسَّعة (LAYOUT) وتعديل غير مودَع (DIRTY_LAYOUT) وملف readme اليتيم (ORPHAN_README) وملاحظة محلية (LOCAL_NOTE) وملاحظة متجاهَلة (IGNORED_NOTE).
مدخلات الملفات كاملة
احفظ كل محتوى كامل بصيغة UTF-8 وLF وسطر نهائي، في مرحلته فقط ودون أسوار Markdown. BASE_CSS مرجع 48rem الموروث، وLAYOUT إيداع 52rem، وDIRTY_LAYOUT إعادة مستقلة غير مجهزة بعرض 56rem. يخص ملفا الملاحظات نسخة البقاء وحدها. ترث ملفات main الثابتة محتوى L018/M الكامل.
styles.css — BASE_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
: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
: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
# 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
Local rehearsal note; preserve this file.reports/preview.txt — IGNORED_NOTE
Ignored rehearsal preview; preserve these bytes.في GitTree
افتح لوحة الأوامر بـ Command+Shift+P (وCtrl+Shift+P على Ubuntu).
- في النسخة الأولى انقر الإيداع M في المخطط بزر الفأرة الأيمن واختر التبديل إلى هذا الإيداع (Check Out This Commit). يقرأ منتقي الفروع «منفصل عند …» ويعرض المخطط HEAD، منفصل.
- احفظ LAYOUT باسم
styles.cssوجهّز الملف وأودِعه بالملخّصstyle: widen the hero reading measure. تعدّ اللوحة التغيير «على HEAD منفصل» أثناء العمل. - انقر الإيداع الجديد بزر الفأرة الأيمن واختر إنشاء فرع هنا… (Create Branch Here…). سمِّه
experiment/layoutوأبقِ خيار التبديل إليه معطّلًا واختر إنشاء فرع (Create Branch). يقول الإشعار «أُنشئ experiment/layout.» ويبقى HEAD منفصلًا. - انقر وسم
experiment/layoutبزر الفأرة الأيمن واختر التبديل إلى الفرع (Check Out Branch). صار HEAD مربوطًا من جديد، وstyles.cssهو LAYOUT، وما زالmainعلى M. - تبديل بتغييرات غير مودَعة. في نسخة أخرى، افصل HEAD عند
experiment/layoutوعدّلstyles.cssإلى DIRTY_LAYOUT دون إيداع وحاول التبديل إلىmain. يسرد حوار «تغييرات محلية تعترض التبديل إلى main» الملف ويعرض الإيداع أولًا (Commit First) وتخبئة ثم تبديل (Stash and Switch) والتخلّص من التغييرات ثم التبديل (Discard and Switch). اختر الإلغاء؛ فالتخلّص يحفظ نسخة تحتrefs/ogt/trashلكن لا حاجة إليه هنا. - اليتيم. في النسخة الثانية ومجلد العمل نظيف، نفّذ إنشاء فرع يتيم… (Create Orphan Branch…) من لوحة الأوامر. في «إنشاء فرع يتيم في …؟» اكتب
orphan-docs. يذكر الحوار كم ملفًا متتبَّعًا يخرج من مجلد العمل، وأن غير المتتبَّعة والمتجاهَلة تبقى، وأن الإيداع القديم يُحفظ تحتrefs/ogt/trash. الإلغاء هو الذي عليه التركيز. اختر إنشاء الفرع اليتيم (Create Orphan Branch). - يقول الإشعار «بُدِّل إلى الفرع اليتيم الجديد orphan-docs. يعيدك التراجع.» ولا ملفات متتبَّعة في المجلد ولا إيداع على الفرع الجديد في المخطط. أنشئ
README.mdمن ORPHAN_README وجهّزه وأودِعه بالملخّصdocs: start the separate project guide. الإيداع الجديد بلا أب. - قارن: ما زال
mainيحمل كل إيداع؛ والفرع اليتيم جذر منفصل. لا تدفع أيًّا من النسختين.
لكل إيداع عادي في الدورة: الملخّص (Summary) هو العنوان المحدد، والوصف (Description) خالٍ، وتبقى الخيارات تعديل الإيداع الأخير وتخطّي الخطّافات وإضافة Signed-off-by ومؤلف مختلف والدفع بعد الإيداع والسماح بإيداع فارغ معطّلة.
في الطرفية
في النسخة الأولى:
BASE_SHA="$(git rev-parse main)"
git switch --detach "$BASE_SHA"
git symbolic-ref --quiet --short HEAD; echo $?يطبع الأمر الأخير 1: HEAD ليس فرعًا. احفظ LAYOUT باسم styles.css ثم:
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 --short1
experiment/layoutلا يربط أمر الإنشاء HEAD (ما زال الاستعلام الثاني يطبع 1)، والمعرّفان من rev-parse متساويان (M)، والحالة خالية. وفي نسخة أخرى منفصلة عند experiment/layout عدّل styles.css ونفّذ git switch main: يرفض Git بعبارة «Your local changes to the following files would be overwritten by checkout» ويبقى الملف كما هو.
في النسخة الثانية ومجلد العمل نظيف:
git switch --orphan orphan-docs
git symbolic-ref --short HEAD
git rev-parse --verify --quiet HEAD; echo $?
git ls-filesorphan-docs
1الفرع موجود لكن لا إيداع فيه (rev-parse ينتهي برمز 1) وls-files لا يطبع شيئًا. خرجت الملفات المتتبَّعة من المجلد، وبقي local-review.txt وreports/ وتظهر الآن غير متتبَّعة. أنشئ README.md ثم:
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 $?README.md
1
1للفرع الجديد إيداع جذري واحد ولا سلف مشترك مع main (لا يطبع merge-base شيئًا وينتهي برمز 1).
النتيجة
أين تنتهي: سُمّي الإيداع المنفصل experiment/layout وبُدِّل إليه، وما زال main على M والمجلد نظيف. وفي النسخة اليتيمة فرع orphan-docs بإيداع جذري واحد بلا أب، والملفات المتتبَّعة القديمة خرجت من المجلد ويحتفظ بها main، ولم يُدفع شيء.
أخطاء شائعة
- الإيداع على HEAD منفصل ثم التبديل بلا فرع، فيبقى الإيداع قابلًا للوصول عبر سجل المراجع فقط.
- توقع أن ينقلك إنشاء فرع هنا… إلى الفرع الجديد.
- بدء فرع يتيم وفي الملفات المتتبَّعة تغييرات غير مودَعة، أو ظن أنه يحذف
main. - افتراض أن الملف المتجاهَل يبقى متجاهَلًا بعد أن يخرج
.gitignoreمن المجلد.
إن حدث خطأ
إن غادرت إيداعًا منفصلًا بلا اسم فافتح سجل المراجع (الدرس 29) وضع عليه فرعًا. وإن كان الفرع اليتيم خطأً فإن تراجع يعيدك ما دام الفرع الجديد بلا إيداع؛ وإلا فبدّل إلى main من جديد. والتبديل المرفوض لم يغيّر شيئًا.
اختبر فهمك
أجب عن كل سؤال ثم اقرأ السبب. الأسئلة المعلَّمة «مطلوب» تُحتسب لإكمال الدرس.
0 من 6 صحيحة
أجب عن الأسئلة المطلوبة إجابة صحيحة لتكمل الدرس.
جرى التحقق في هذا المتصفح.
تدريب
اذكر أين يشير HEAD في كل حالة (فرع ومنفصل وغير مولود)، ومعرّف الإيداع المنفصل والفرع عليه، وما طبعه git ls-files على الفرع اليتيم. ثم توقّع ما يحدث لملف متجاهَل حين يغيب .gitignore.
تأكيد منك أنت: لا يستطيع الموقع رؤية جهازك.
لإكمال الدرس، أجب عن الأسئلة المطلوبة إجابة صحيحة وأكّد تنفيذ التمرين العملي.
خلاصة
- الإيداع المنفصل يحتاج فرعًا ليبقى قابلًا للوصول.
- الفرع اليتيم يبدأ جذرًا منفصلًا ويُخرج الملفات المتتبَّعة من المجلد.
- التبديل المرفوض يحمي تعديلك.
ذو صلة
المصطلحات الجديدة
Detached HEAD — HEAD منفصل; orphan branch — فرع يتيم; unborn branch — فرع غير مولود; root commit — إيداع جذري.
نص الفيديو
ترجمة التعليق الصوتي الإنجليزي للفيديو، مقطعًا بعد مقطع.
اعرض النص
- أين HEAD؟ ثلاث حالات.
- قد يسمّي HEAD فرعًا أو إيداعًا أو فرعًا بلا إيداع بعد.
- الإيداع على HEAD منفصل حقيقي لكن لا شيء يسمّيه.
- فرع على ذلك الإيداع يُبقيه قابلًا للوصول.
- انقر الإيداع M بزر الفأرة الأيمن واختر التبديل إلى هذا الإيداع.
- عدّل ورقة الأنماط وجهّزها وأودِع مرة واحدة.
- انقر الإيداع الجديد وأنشئ فرعًا هنا دون التبديل إليه.
- الآن بدّل إلى الفرع. صار HEAD مربوطًا من جديد.
- مع تعديل غير محفوظ يُرفض التبديل. والإلغاء يُبقي كل شيء.
- الإيداع أولًا أو التخبئة ثم التبديل أو التخلّص. والتخلّص هو ما يُتجنب.
- لتاريخ جديد نفّذ إنشاء فرع يتيم من لوحة الأوامر.
- يقول كم ملفًا متتبَّعًا يخرج، وأن غير المتتبَّعة والمتجاهَلة تبقى.
- أكِّد. لا ملفات متتبَّعة في المجلد.
- أضف ملف readme وأودِع. هذا الإيداع بلا أب.
- في الطرفية ينتهي symbolic-ref برمز 1 حين يكون HEAD منفصلًا.
- على فرع غير مولود يفشل rev-parse لـ HEAD.
- بعد الإيداع لا يجد merge-base سلفًا مشتركًا.
- إن غادرت بلا فرع فسجل المراجع ما زال يحمله.
- تدريب: سمِّ HEAD في كل حالة ثم توقّع ملفًا متجاهَلًا.
- التالي: الاستعادة من أخطاء بعد الدفع.