مبتدئ → تطبيقي · 10 دروس

Git و GitHub — من الحفظ المحلي إلى العمل الجماعي

تتعلّم حفظ نسخ مشروعك بذكاء، التعاون مع فريق، وفتح Pull Request — وتخرج بمستودع GitHub جاهز كمحفظة تعرض شغلك للعالم.

كيف تدرس هذا المسار؟

  1. اقرأ المفهوم — Git «آلة زمن» لمشروعك.
  2. انسخ أوامر Terminal وجرّبها في مجلد تجريبي.
  3. نفّذ المهمة وسجّل commit حقيقي إن أمكن.
  4. أكمل الاختبار — التقدّم يُحفظ على جهازك.

مناسب من سن 13 فما فوق، وفي آخر كل درس روابط لمراجعه الرسمية.

1. لماذا Git؟ — «آلة الزمن» لمشاريعك

الهدف: تفهم فكرة التحكم بالنسخ (Version Control) والفرق بين Git و GitHub قبل كتابة أي أمر.

قبل أن تكتب أي أمر، تأمّل هذا الموقف. ثلاثة طلاب في كلية الحاسبات بجامعة أسيوط يبنون موقع مشروع التخرج. كل واحد منهم يعدّل الملفات على جهازه، ثم يرسلها إلى الآخرين في مجموعة واتساب بأسماء مثل index_final.html ثم index_final2.html ثم index_final_ahmed.html. بعد أسبوعين يكتشفون أن صفحة الدخول التي كانت تعمل اختفت، ولا أحد يعرف أي نسخة تحتوي التعديل الصحيح، ولا متى ضاع، ولا من غيّره. هذه المشكلة بالضبط هي ما صُممت أنظمة التحكم بالنسخ (Version Control Systems) لحلها.

Git هو نظام التحكم بالنسخ الأوسع انتشاراً اليوم. بدل أن تنسخ الملفات يدوياً، تطلب من Git أن يحفظ «لقطة» (Snapshot) لحالة المشروع كلها في لحظة معينة، وتسمى هذه اللقطة commit. كل commit يحمل أربعة أشياء: معرّفاً فريداً يسمى hash يُحسب من محتوى اللقطة نفسها، واسم كاتبه وبريده وتاريخ الحفظ، ورسالة قصيرة تشرح ما الذي تغيّر، وإشارة إلى الـ commit الذي سبقه. هذه الإشارة الأخيرة هي ما يربط اللقطات في سلسلة واحدة نسميها تاريخ المشروع.

هكذا يبدو هذا التاريخ عندما تطلب ملخصه بالأمر git log --oneline، والأحدث في الأعلى:

e4f5a6b أصلحت زر الدخول في صفحة login
9c1d2e3 أضفت صفحة الأسعار
3f2a1c9 أول commit: الصفحة الرئيسية

الأحرف في أول كل سطر هي بداية المعرّف الكامل، وتكفي عادة لتمييز الـ commit داخل المشروع. لو طبّق الطلاب الثلاثة هذا الأسلوب منذ البداية، لعرفوا متى تغيّرت صفحة الدخول ومن غيّرها، ولأعادوا الأسطر المحذوفة دون أن يمسوا بقية العمل.

ميزة أساسية في Git أنه نظام موزّع (Distributed). كل نسخة من المستودع على أي جهاز تحمل التاريخ الكامل، وليس آخر نسخة من الملفات فقط. يترتب على ذلك أمران عمليان: تستطيع أن تحفظ commits وتتصفح التاريخ وتقارن النسخ دون اتصال بالإنترنت، والاتصال يلزم فقط عند المزامنة مع نسخة أخرى. وإن تعطل خادم مركزي، يبقى التاريخ موجوداً عند كل من يملك نسخة.

يخلط المبتدئون كثيراً بين Git و GitHub، والفرق بسيط حين تراه بوضوح. Git برنامج يعمل على جهازك، يدير اللقطات والفروع، ويعمل وحده تماماً. أما GitHub فخدمة على الإنترنت تستضيف نسخة من مستودعك، وتضيف فوقه أدوات التعاون: مراجعة التغييرات قبل دمجها، وقوائم المهام، ونشر المواقع، وصفحة شخصية تعرض أعمالك. توجد خدمات مشابهة مثل GitLab و Bitbucket و Codeberg، وكلها تتعامل مع المستودعات نفسها لأن المحرك واحد. يمكنك إذن أن تستخدم Git لسنوات دون حساب على أي منصة، لكنك لا تستطيع إدارة كودك على GitHub دون Git.

تصحيح لفكرة شائعة: وجود Git على جهازك لا يعني أن لديك نسخة احتياطية. المجلد المخفي الذي يحفظ فيه Git التاريخ موجود على القرص نفسه، فإن تعطل القرص أو سُرق الحاسوب ضاع المستودع معه. النسخة الاحتياطية الحقيقية تبدأ حين ترفع المستودع إلى مكان آخر، مثل GitHub أو قرص خارجي.

ومتى لا يناسبك Git كما هو؟ صُمم للملفات النصية مثل الكود وملفات الإعداد والتوثيق، لأنه يستطيع مقارنتها سطراً بسطر وعرض الفرق بدقة. الملفات الثنائية الكبيرة، كالفيديو وملفات التصميم الضخمة، لا يستطيع مقارنتها بهذه الطريقة، وكل تعديل عليها يضيف نسخة كاملة إلى التاريخ فيتضخم المستودع بسرعة. لهذه الحالة يوجد امتداد اسمه Git LFS يخزّن الملفات الكبيرة خارج التاريخ ويترك في المستودع إشارة صغيرة إليها.

خلاصة عملية قبل الانتقال إلى التثبيت: Git يجيب عن ثلاثة أسئلة لا تجيب عنها المجلدات العادية، وهي ماذا تغيّر، ومتى، ولماذا. والسؤال الثالث يتوقف عليك أنت، لأن رسالة كل commit تكتبها بيدك، وستعود هذه الفكرة في كل درس قادم.

  • Git يحفظ لقطات (commits) مترابطة تكوّن تاريخ المشروع، وكل لقطة لها معرّف وكاتب ورسالة.
  • Git موزّع: كل نسخة تحمل التاريخ كاملاً، فتعمل دون إنترنت وتزامن عند الحاجة.
  • Git هو المحرك على جهازك، و GitHub خدمة استضافة وتعاون مبنية عليه.
  • Git وحده ليس نسخة احتياطية، والملفات الثنائية الضخمة تحتاج Git LFS.

مخطط

ملفات مشروعك
Git commits
v1v2v3
GitHub remote

مختبر: قبل وبعد Git

اكتب 3 مشاكل حصلت لك (فقدان ملف، نسخة قديمة، تعارض مع زميل) — وبجانب كل مشكلة: أي ميزة في Git كانت ستحلها؟

مهمة عملية

اكتب أربع جمل تشرح لزميل الفرق بين Git و GitHub، واذكر فيها لماذا لا يكفي Git وحده كنسخة احتياطية.

اختبر فهمك (1/3)

Git في المقام الأول هو...

اختبر فهمك (2/3)

تعطل قرص جهازك ولم تكن قد رفعت مستودعك إلى أي مكان. ماذا حدث لتاريخ المشروع؟

اختبر فهمك (3/3)

زميلك يريد تتبع ملفات فيديو ضخمة في المستودع. ما النصيحة الأنسب؟

للاستزادة: Pro Git: عن التحكم بالنسخ · Pro Git: ما هو Git؟ · GitHub Docs: ما هو GitHub؟ · Git LFS — الموقع الرسمي

2. تثبيت Git وإعداد هويتك

الهدف: تثبّت Git وتضبط اسمك وبريدك واسم الفرع الافتراضي، وتعرف كيف تصلح أشهر أعطال الإعداد.

يتكون الإعداد من خطوتين تنفّذهما مرة واحدة لكل جهاز: تثبيت البرنامج، ثم تعريف Git بهويتك. تستغرق العملية دقائق، لكن أخطاءها تظهر لاحقاً في أماكن غير متوقعة، كأن تظهر commits على GitHub بلا صورتك أو باسم غريب، لذلك نمشي فيها خطوة خطوة.

التثبيت حسب نظام التشغيل:
• Windows: نزّل المثبّت الرسمي Git for Windows من صفحة التنزيل في git-scm.com، أو نفّذ في PowerShell الأمر winget install --id Git.Git -e --source winget. يضيف المثبّت معه طرفية Git Bash، وأداة Git Credential Manager التي تحفظ تسجيل دخولك إلى GitHub. الخيارات الافتراضية في المثبّت مناسبة للمبتدئ.
• macOS: نفّذ git --version في الطرفية؛ إن لم يكن Git مثبتاً سيعرض النظام تثبيت أدوات سطر الأوامر من Apple. والبديل brew install git إن كنت تستخدم Homebrew.
• Ubuntu و Debian: sudo apt install git، وفي Fedora: sudo dnf install git.

بعد التثبيت أغلق الطرفية وافتح نافذة جديدة، ثم نفّذ git --version. يجب أن يطبع سطراً يبدأ بعبارة git version يليها رقم الإصدار، وفي Windows ينتهي الرقم بلاحقة windows. إن ظهر هذا السطر فالتثبيت سليم، ولا يهم أن يطابق رقمك أرقام الشروح التي تقرؤها ما دام إصداراً حديثاً.

الآن عرّف Git بنفسك. هذه الأوامر الثلاثة تُكتب مرة واحدة على كل جهاز:

git config --global user.name "Sara Ahmed"
git config --global user.email "sara@example.com"
git config --global init.defaultBranch main

كل commit تصنعه بعد ذلك يُختم بالاسم والبريد اللذين ضبطتهما. يستخدم GitHub البريد تحديداً ليربط كل commit بحسابك ويعرض صورتك بجانبه، لذلك اختر بريداً مضافاً إلى حسابك هناك. أما السطر الثالث فيخص اسم الفرع الأول: إصدارات Git الحالية ما زالت تسميه master افتراضياً ما لم تضبط هذا الخيار، بينما يسمي GitHub الفرع الافتراضي main في المستودعات الجديدة. توحيد الاسم من البداية يجنبك ارتباكاً عند ربط المستودعين.

انتبه للخصوصية: المستودعات العامة تكشف بريد كل commit لأي زائر يفتح التاريخ. إن لم ترد نشر بريدك الشخصي، يوفر GitHub في إعدادات البريد عنواناً خاصاً ينتهي بـ users.noreply.github.com، تضعه في user.email فتُربط commits بحسابك دون كشف بريدك.

للإعداد ثلاثة مستويات، والأقرب إلى المشروع يتغلب على الأبعد. المستوى المحلي داخل مستودع بعينه (بلا خيار إضافي) يتغلب على المستوى العام --global الخاص بحسابك على الجهاز، وهذا بدوره يتغلب على مستوى --system الخاص بكل مستخدمي الجهاز. لترى كل قيمة ومن أي ملف جاءت، نفّذ git config --list --show-origin، ولقراءة قيمة واحدة نفّذ مثلاً git config user.email.

بعض أوامر Git تفتح محرراً نصياً لتكتب رسالة، كأن تنفّذ commit دون الخيار -m. إن فُتح لك محرر vim ولم تعرف الخروج منه، فاضغط Esc ثم اكتب :q! واضغط Enter. ولتجعل VS Code المحرر الافتراضي نفّذ git config --global core.editor "code --wait"، والخيار wait يجعل Git ينتظر حتى تغلق تبويب الرسالة.

سجل أعطال شائعة بعد التثبيت:
• الطرفية تقول إن git أمر غير معروف: النافذة فُتحت قبل التثبيت ولم تقرأ المسار الجديد. افتح نافذة جديدة، وفي Windows تأكد أن خيار إضافة Git إلى المسار (PATH) كان مفعّلاً في المثبّت.
• ظهرت رسالة Author identity unknown تتبعها عبارة Please tell me who you are عند أول commit: لم تضبط الاسم والبريد بعد. نفّذ أمري user.name و user.email ثم أعد الأمر، ولن يضيع شيء من ملفاتك.
• commits تظهر على GitHub بلا صورتك ولا رابط لحسابك: البريد في user.email غير مضاف إلى حسابك. أضفه في إعدادات البريد على GitHub أو غيّر user.email إلى بريد مسجّل.
• مشروع العمل يحمل بريدك الشخصي: نفّذ git config user.email "work@company.com" داخل مستودع العمل دون --global، فيسري على هذا المستودع وحده.

  • git --version في نافذة طرفية جديدة هو اختبار التثبيت.
  • user.name و user.email يُختم بهما كل commit، والبريد يربط commits بحسابك على GitHub.
  • init.defaultBranch main يوحّد اسم الفرع الأول مع ما يستخدمه GitHub.
  • الإعداد المحلي يتغلب على --global، و --show-origin يكشف مصدر كل قيمة.

مخطط

git config
name · email
كل commit

مختبر: تحقق من الإعداد

نفّذ git config --list --show-origin ثم ابحث عن user.name و user.email: من أي ملف جاءت كل قيمة؟

مهمة عملية

ثبّت Git واضبط الاسم والبريد واسم الفرع الافتراضي، ثم انسخ مخرجات git --version و git config user.email في ملف ملاحظاتك.

اختبر فهمك (1/3)

لماذا نضبط user.email في Git؟

اختبر فهمك (2/3)

ظهرت رسالة Author identity unknown عند محاولة commit. ما الحل؟

اختبر فهمك (3/3)

تريد بريد العمل في مستودع واحد وبريدك الشخصي في بقية المشاريع. كيف؟

للاستزادة: Pro Git: الإعداد لأول مرة · Git: مرجع git config · GitHub Docs: ضبط بريد commits · Git: صفحة التنزيل الرسمية

3. أول مستودع — init و add و commit

الهدف: تنشئ مشروعاً محلياً، وتقرأ مخرجات Git في كل خطوة، وتحفظ أول commit وتتراجع بأمان.

في هذا الدرس تنفّذ الدورة الكاملة على مشروع صغير، وتقرأ ما يطبعه Git بعد كل خطوة. الهدف أن تتعود على قراءة مخرجات الطرفية بدل التخمين، لأن Git يخبرك دائماً بحالة ملفاتك، وغالباً يقترح عليك الأمر التالي. نفّذ الأوامر في Git Bash على Windows أو في الطرفية على macOS و Linux؛ فالأمر echo في Windows PowerShell 5.1 يحفظ الملفات بترميز UTF-16، فيعاملها Git كملفات ثنائية ولا يعرض الفروق فيها بشكل مقروء.

الخطوة الأولى: أنشئ مجلداً وملفين، ثم حوّل المجلد إلى مستودع.

mkdir mesue-demo
cd mesue-demo
echo "# مشروع تجريبي" > README.md
echo "print(1 + 1)" > hello.py
git init
Initialized empty Git repository in /c/Users/sara/mesue-demo/.git/

أنشأ git init مجلداً مخفياً اسمه .git داخل المشروع. هذا المجلد هو المستودع نفسه: فيه كل commits والفروع والإعدادات المحلية. إن حذفته اختفى تاريخ المشروع كله وبقيت الملفات الحالية فقط. تنفّذ init مرة واحدة لكل مشروع، وداخل مجلد المشروع تحديداً، لا في مجلدك الرئيسي ولا على سطح المكتب كله.

الخطوة الثانية: اسأل Git عن الحالة. في المخرجات الحقيقية أسطر فارغة بين الأقسام حُذفت هنا للاختصار:

git status
On branch main
No commits yet
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README.md
        hello.py
nothing added to commit but untracked files present (use "git add" to track)

لتفهم هذه الرسالة تحتاج صورة المناطق الثلاث. مجلد العمل (Working Directory) هو الملفات كما تراها وتعدّلها. منطقة التجهيز (Staging Area) هي ما اخترته ليدخل في الـ commit القادم. والمستودع هو commits المحفوظة في التاريخ. الملفان الآن في مجلد العمل فقط، ولم يتتبعهما Git بعد، لذلك وصفهما بأنهما Untracked.

الخطوة الثالثة: جهّز الملفين واحفظ اللقطة.

git add README.md hello.py
git commit -m "أول commit: README وملف hello.py"
[main (root-commit) 3f2a1c9] أول commit: README وملف hello.py
 2 files changed, 2 insertions(+)
 create mode 100644 README.md
 create mode 100644 hello.py

السطر الأول يخبرك بالفرع، وبأن هذا أول commit في التاريخ (root-commit)، وبمعرّفه المختصر. والسطر الثاني يلخص حجم التغيير. فائدة الفصل بين add و commit تظهر حين تعدّل خمسة ملفات وثلاثة منها فقط تخص ميزة واحدة: تضيف الثلاثة وتحفظها في commit برسالة واضحة، ثم الباقي في commit آخر.

الخطوة الرابعة: عدّل وراجع قبل الحفظ. أضف سطراً بالأمر echo "print(2 * 3)" >> hello.py، ثم نفّذ git diff. سترى السطر الجديد مسبوقاً بعلامة + تحت عنوان الملف. الأمر git diff وحده يعرض التعديلات التي لم تُجهّز بعد، أما git diff --staged فيعرض ما سيدخل الـ commit فعلاً بعد add. عادة مراجعة الفرق قبل كل commit تمنعك من حفظ سطر اختبار نسيته أو كلمة سر كتبتها مؤقتاً.

أثناء العمل ستقابل ثلاث عبارات في git status، ومعنى كل منها محدد:
• Untracked files: ملفات جديدة لم يتتبعها Git بعد.
• Changes not staged for commit: ملفات متتبعة عدّلتها ولم تضفها إلى التجهيز.
• Changes to be committed: تعديلات جاهزة ستدخل الـ commit القادم.

وللتراجع أمران من عائلة restore. git restore --staged hello.py يخرج الملف من منطقة التجهيز ويبقي تعديلك كما هو في مجلد العمل، وهو آمن تماماً. أما git restore hello.py فيعيد الملف إلى حالته في آخر commit، أي أنه يمسح تعديلاتك غير المحفوظة في ذلك الملف نهائياً ولا يوجد أمر يستعيدها. اقرأ اسم الملف مرتين قبل تنفيذه.

أخيراً، رسالة الـ commit تُكتب للقارئ بعد شهر، وقد تكون أنت. اكتبها بفعل يصف التغيير مثل «أضفت صفحة About» أو «أصلحت رابط التواصل المكسور». رسائل مثل «تعديل» أو «asdf» لا تساعد أحداً حين يبحث في التاريخ عن سبب تغيير ما. والدورة اليومية التي ستكررها كثيراً: عدّل، ثم status، ثم diff، ثم add، ثم commit.

  • git init يُنفّذ مرة واحدة داخل مجلد المشروع، وينشئ المجلد المخفي .git.
  • add ينقل التعديل إلى منطقة التجهيز، و commit يحفظ ما في التجهيز كلقطة.
  • git diff يعرض ما لم يُجهّز، و git diff --staged يعرض ما سيُحفظ فعلاً.
  • git restore --staged آمن، أما git restore وحده فيمسح التعديل غير المحفوظ.

مخطط

workingstagedcommitted
تعديلadd
commit
git status

مختبر: 3 commits

أضف ملف notes.txt بثلاثة أسطر ثم commit. عدّل README وراجع git diff قبل commit ثانٍ. ثم أضف سطراً ثالثاً. نفّذ git log --oneline: هل ترى 3 commits برسائل مفهومة؟

مهمة عملية

أنشئ مجلد mesue-git-lab بملفين وثلاثة commits على الأقل، وانسخ مخرجات git log --oneline التي تُظهر رسائل واضحة.

اختبر فهمك (1/3)

بعد تعديل ملف متتبّع، ما الترتيب الصحيح لحفظه في التاريخ؟

اختبر فهمك (2/3)

عدّلت 5 ملفات، ثلاثة منها فقط تخص ميزة واحدة. ما الأسلوب الأفضل؟

اختبر فهمك (3/3)

أي أمر يمسح تعديلاتك غير المحفوظة في hello.py نهائياً بلا تراجع؟

للاستزادة: Pro Git: تسجيل التغييرات في المستودع · Git: مرجع git restore · Git: مرجع git status

4. الفروع — branch للتجربة بأمان

الهدف: تنشئ فرعاً وتعمل عليه وتدمجه في main، وتفهم الفرق بين الدمج (merge) وإعادة التأسيس (rebase).

الفرع (branch) في Git ليس نسخة من الملفات كما يظن كثيرون، بل مؤشر خفيف يشير إلى commit معيّن، ويتقدم تلقائياً مع كل commit جديد تصنعه عليه. لذلك إنشاء فرع فوري ولا يستهلك مساحة تذكر. وبجانبه مؤشر خاص اسمه HEAD يدل على موقعك الحالي، أي الفرع الذي تعمل عليه حالياً.

لنأخذ موقع مكتبة صغيرة في المنصورة يعمل على main، والمطلوب إضافة نموذج تواصل دون أن ينكسر الموقع أثناء العمل. هذه هي الدورة الكاملة مع ما يطبعه Git:

git switch -c feature/contact-form
Switched to a new branch 'feature/contact-form'
git add contact.html
git commit -m "أضفت نموذج التواصل"
git switch main
Switched to branch 'main'
git merge feature/contact-form
Updating 3f2a1c9..e4f5a6b
Fast-forward
 contact.html | 12 ++++++++++++
 1 file changed, 12 insertions(+)
git branch -d feature/contact-form
Deleted branch feature/contact-form (was e4f5a6b).

الأمر git switch مخصص للتنقل بين الفروع، والخيار -c ينشئ الفرع وينتقل إليه معاً. ستجد في شروح أقدم git checkout لأداء المهمة نفسها، وهو ما زال يعمل، لكنه أمر متعدد الوظائف يستطيع أيضاً استبدال محتوى الملفات، فأضاف Git الأمرين switch و restore ليفصل المهمتين ويقلل الأخطاء. الفكرة العملية أن main يبقى مستقراً وقابلاً للتشغيل دائماً، وكل ميزة أو إصلاح في فرع باسم يصفه مثل feature/contact-form أو fix/mobile-menu.

لاحظ كلمة Fast-forward في المخرجات. حين لا يتغير main منذ أنشأت الفرع، يكتفي Git بتحريك مؤشر main إلى الأمام حتى آخر commit في الفرع، ولا يصنع شيئاً جديداً. أما إن تقدّم الفرعان كلاهما، كأن يحدّث زميلك الأسعار على main بينما تعمل أنت على القائمة، فينشئ Git commit دمج (merge commit) له أبوان يجمع الخطين. ترى ذلك بوضوح بالأمر git log --oneline --graph --all:

*   7b8c9d0 Merge branch 'fix/mobile-menu'
|\
| * 5e6f7a8 أصلحت قائمة الموبايل
* | 2c3d4e5 حدّثت الأسعار
|/
* 3f2a1c9 أول commit

هناك طريقة ثانية لجمع عمل فرعين اسمها إعادة التأسيس (rebase). بدل صنع commit دمج، يأخذ Git commits فرعك ويعيد تطبيقها واحداً تلو الآخر فوق آخر نقطة في main، فيبدو التاريخ خطاً مستقيماً كأنك بدأت العمل اليوم:

git switch fix/mobile-menu
git rebase main
Successfully rebased and updated refs/heads/fix/mobile-menu.

المقارنة بين الطريقتين عملية لا عقائدية. الدمج يحفظ التاريخ كما حدث فعلاً ولا يغيّر أي commit موجود، لذلك هو الخيار الآمن دائماً. إعادة التأسيس تعطي تاريخاً أنظف، لكنها تصنع commits جديدة بمعرّفات جديدة بدل القديمة. ولهذا يضع كتاب Pro Git قاعدة ذهبية: لا تُعد تأسيس commits موجودة خارج مستودعك وربما بنى عليها غيرك. قاعدة القرار للمبتدئ إذن: استخدم merge افتراضياً، واستخدم rebase فقط لتحديث فرع محلي لم ترفعه بعد أو لا يعمل عليه أحد غيرك. وإن ظهر تعارض أثناء rebase فحلّه، ثم نفّذ git add للملف و git rebase --continue، أو تراجع عن العملية كلها بـ git rebase --abort.

قبل أن تقرر في حالة حقيقية، اسأل نفسك سؤالين: هل رفعت هذا الفرع إلى GitHub وسحبه أحد؟ وهل يهم فريقك أن يظهر التاريخ خطاً مستقيماً؟ إن كانت إجابة السؤال الأول نعم، فالدمج هو الجواب مهما كانت إجابة الثاني. وإن كان الفرع على جهازك وحدك وفريقك يفضّل تاريخاً خطياً، فإعادة التأسيس على main قبل فتح طلب الدمج تجعل مراجعة عملك أسهل على زملائك.

خطأ ستقابله عند التنقل: رسالة تبدأ بعبارة Your local changes to the following files would be overwritten by checkout. معناها أن لديك تعديلات غير محفوظة سيمسحها التنقل. الحل إما حفظها في commit على الفرع الحالي، أو وضعها جانباً مؤقتاً بـ git stash ثم استعادتها لاحقاً بـ git stash pop.

وعند الحذف، يرفض git branch -d حذف فرع لم يُدمج بعد ويخبرك أنه not fully merged، وهذه حماية من فقدان العمل. الخيار -D بالحرف الكبير يحذف بالقوة، فاستعمله فقط حين تريد فعلاً التخلص من تجربة فاشلة.

  • الفرع مؤشر خفيف على commit، و HEAD يشير إلى الفرع الذي تعمل عليه الآن.
  • git switch -c ينشئ فرعاً وينتقل إليه، و git merge يدمج الفرع في الفرع الحالي.
  • Fast-forward يحرّك المؤشر فقط، و merge commit يجمع خطين تقدّما معاً.
  • merge آمن دائماً، و rebase يعيد كتابة commits فلا تستخدمه على عمل مشترك.

مخطط الفروع

 main:    A---B---C---M
              \     /
 feature:      D---E

 rebase:  A---B---C---D'---E'

مختبر: دمج ثم إعادة تأسيس

أنشئ فرعين من main: add-style و fix-typo، وعدّل في كل منهما ملفاً مختلفاً. ادمج الأول بـ merge، ثم في الثاني نفّذ git rebase main قبل دمجه. قارن الرسم في git log --graph.

مهمة عملية

أنشئ فرع learn-day1، أضف ملف goals.txt واحفظه في commit، ثم ادمجه في main واحذف الفرع، وانسخ مخرجات git log --oneline --graph --all.

اختبر فهمك (1/3)

لماذا نستخدم الفروع (branches)؟

اختبر فهمك (2/3)

متى تتجنب git rebase وفق القاعدة الذهبية في Pro Git؟

اختبر فهمك (3/3)

ظهر عند التنقل: Your local changes to the following files would be overwritten. ما الحل الصحيح؟

للاستزادة: Pro Git: التفريع والدمج · Pro Git: إعادة التأسيس ومخاطرها · Git: مرجع git switch · Git: مرجع git stash

5. GitHub — حساب ومستودع على السحابة

الهدف: تفتح حساباً وتنشئ مستودعاً على GitHub، وتختار طريقة البداية الصحيحة وطريقة تسجيل الدخول المناسبة.

ابدأ بحساب مجاني على github.com، وفعّل التحقق بخطوتين (2FA) من إعدادات الحساب قبل أي شيء آخر. هذا الحساب سيحمل أعمالك وسمعتك المهنية، وكلمة مرور وحدها حماية ضعيفة لشيء بهذه الأهمية. اختر اسم مستخدم مهنياً قصيراً، لأنه سيظهر في كل رابط لمشاريعك.

عند إنشاء مستودع جديد من زر New repository، يسألك GitHub عن الاسم، وعن الظهور: Public يراه أي زائر وهو المناسب للمحفظة والمشاريع التعليمية، و Private لا يراه إلا أنت ومن تدعوهم، وهو المناسب لمشروع عميل أو عمل لم يكتمل. ثم يعرض إضافة README وملف .gitignore وترخيص. قرارك هنا يتوقف على مكان بداية المشروع، وهذه أهم نقطة في الدرس.

إن كان المشروع موجوداً على جهازك وفيه commits، فأنشئ المستودع على GitHub فارغاً تماماً بلا أي إضافة. كل إضافة من الثلاث تصنع commit على GitHub لا يعرفه مستودعك المحلي، فيصبح لديك تاريخان بلا أصل مشترك، ويُرفض أول رفع برسالة تطلب سحب التغييرات أولاً. بعد الإنشاء اربط المستودعين:

git remote add origin https://github.com/USER/mesue-portfolio.git
git branch -M main
git remote -v
origin  https://github.com/USER/mesue-portfolio.git (fetch)
origin  https://github.com/USER/mesue-portfolio.git (push)

الاسم origin مجرد اسم مستعار اصطلاحي للمستودع البعيد الأساسي، حتى لا تكتب الرابط الكامل في كل أمر، ويمكنك نظرياً تسميته بأي اسم. الأمر git remote -v يعرض الرابط مرتين، مرة للجلب (fetch) ومرة للرفع (push)، وغالباً يكونان متطابقين. أما git branch -M main فيعيد تسمية فرعك الحالي إلى main إن كان اسمه master من إعداد قديم.

وإن كنت تبدأ من الصفر، فالعكس أسهل: أنشئ المستودع على GitHub مع README وقالب .gitignore المناسب للغتك، ثم انسخه إلى جهازك. الأمر clone ينشئ المجلد، وينزّل التاريخ كاملاً، ويضبط origin وتتبع الفرع تلقائياً:

git clone https://github.com/USER/mesue-portfolio.git
Cloning into 'mesue-portfolio'...
cd mesue-portfolio

تبقى مسألة تسجيل الدخول. لا يقبل GitHub كلمة مرور حسابك في عمليات Git عبر HTTPS منذ أغسطس 2021، وهذا يربك كثيراً من المبتدئين الذين يكتبون كلمة المرور الصحيحة فتُرفض. أمامك ثلاثة بدائل. على Windows تتولى أداة Git Credential Manager الأمر: تفتح نافذة متصفح لتسجيل الدخول مرة واحدة، وتحفظ الإذن بأمان. في البيئات الأخرى يمكنك إنشاء رمز وصول شخصي (Personal Access Token) من إعدادات المطورين، بصلاحيات محدودة وتاريخ انتهاء، وتلصقه مكان كلمة المرور. عامل هذا الرمز معاملة كلمة السر تماماً، ولا تضعه في أي ملف داخل المشروع.

البديل الثالث هو SSH، حيث تنشئ زوج مفاتيح وتضيف المفتاح العام في إعدادات GitHub، ثم تستخدم روابط من الشكل git@github.com:USER/repo.git فلا تُسأل عن كلمة مرور بعدها:

ssh-keygen -t ed25519 -C "sara@example.com"
ssh -T git@github.com
Hi USER! You've successfully authenticated, but GitHub does not provide shell access.

كيف تختار؟ HTTPS مع Credential Manager أسهل بداية، ويعمل في الشبكات التي تحجب منافذ غير الويب كبعض شبكات الجامعات والشركات. SSH مريح لمن يعمل يومياً على عدة مستودعات، ولا يحتاج تجديد رموز. كلاهما آمن إن حافظت على المفتاح أو الرمز.

رابط أي مستودع تجده في زر Code الأخضر في صفحته، وفيه تبويبان: HTTPS و SSH. انسخ الرابط الذي يطابق طريقة الدخول التي اخترتها، لأن ربط المستودع برابط HTTPS ثم انتظار أن يستخدم Git مفتاح SSH من أكثر أسباب الارتباك شيوعاً. وإن غيّرت رأيك لاحقاً، فالأمر set-url الذي ستراه بعد قليل ينقلك من نوع إلى آخر دون أي أثر على التاريخ.

خطآن ستقابلهما عند الربط. الأول error: remote origin already exists، ومعناه أنك أضفت origin من قبل، والحل تغيير رابطه بالأمر git remote set-url origin متبوعاً بالرابط الجديد. والثاني Repository not found، وأسبابه المعتادة خطأ إملائي في الرابط، أو مستودع خاص لم تُدعَ إليه، أو أن جهازك محفوظ عليه تسجيل دخول لحساب آخر غير الذي يملك المستودع.

  • مشروع محلي فيه commits يُربط بمستودع GitHub فارغ تماماً، بلا README ولا ترخيص.
  • مشروع جديد من الصفر يُنشأ على GitHub أولاً ثم يُنسخ بـ git clone.
  • origin اسم مستعار للمستودع البعيد، و git remote -v يعرض روابطه.
  • كلمة مرور الحساب لا تعمل مع Git؛ استخدم Credential Manager أو رمز وصول أو SSH.

مخطط

جهازك
GitHub
mesue-portfolioorigin/main

مختبر: مستودع فارغ وربطه

أنشئ repo باسم mesue-learn-git-test — Public — دون README. اربطه بمجلد mesue-git-lab من الدرس الثالث، وتأكد أن git remote -v يعرض السطرين.

مهمة عملية

افتح حساب GitHub وفعّل التحقق بخطوتين، ثم أنشئ مستودعاً فارغاً واربطه بمشروعك المحلي، وانسخ مخرجات git remote -v.

اختبر فهمك (1/3)

ماذا يعني origin في Git؟

اختبر فهمك (2/3)

أنشأت مستودعاً على GitHub مع README، ثم ربطت به مستودعاً محلياً فيه commits، فرُفض أول push. لماذا؟

اختبر فهمك (3/3)

طُلبت كلمة مرور عند push عبر HTTPS فكتبت كلمة مرور حسابك الصحيحة فرُفضت. لماذا؟

للاستزادة: GitHub Docs: إنشاء مستودع جديد · GitHub Docs: إدارة المستودعات البعيدة · GitHub Docs: رموز الوصول الشخصية · GitHub Docs: الاتصال عبر SSH · GitHub Blog: إيقاف كلمات المرور في عمليات Git

6. push و pull — مزامنة جهازك مع GitHub

الهدف: ترفع commits وتسحب تحديثات الفريق، وتقرأ رسائل الرفض وتعالجها دون أن تمسح عمل أحد.

أول ما يجب أن يستقر في ذهنك: الأمر git push يرسل commits ولا يرسل ملفات. أي تعديل لم تصنع له commit لن يصل إلى GitHub مهما كررت الرفع. في أول رفع لفرع جديد استخدم الخيار -u، فهو يربط فرعك المحلي بنظيره البعيد، وهذا الربط يسمى upstream:

git push -u origin main
To https://github.com/USER/mesue-portfolio.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

بعد هذا الربط يكفيك git push و git pull بلا أي إضافات، ويبدأ git status يخبرك بالعلاقة بين الفرعين. عبارة Your branch is ahead of origin/main by 2 commits تعني أن لديك commitين لم تُرفعا. وعبارة Your branch is behind تعني أن على GitHub عملاً لم تسحبه. انتبه إلى أن هذه المقارنة تتم مع آخر ما يعرفه جهازك عن GitHub، لا مع الحالة اللحظية، ولتحديث هذه المعرفة تنفّذ git fetch.

الفرق بين fetch و pull جوهري. الأمر git fetch ينزّل commits الجديدة ويحدّث origin/main دون أن يمس ملفاتك أو فرعك، فتستطيع مراجعة ما وصل بالأمر git log --oneline main..origin/main قبل أن تقرر. أما git pull فهو fetch يتبعه مباشرة دمج ما وصل في فرعك الحالي. حين تعمل وحدك فـ pull مريح، وحين يكون المستودع مزدحماً بعمل الآخرين فـ fetch ثم المراجعة عادة أهدأ.

لنرَ كيف تبدو هذه المفاهيم في الواقع. هذا سجل أعطال من أسبوع فريق صغير من ثلاثة أشخاص يبني متجراً إلكترونياً لمحل ملابس في طنطا.

العطل الأول، يوم الأحد: نفّذت هالة git push وظهرت رسالة Everything up-to-date، لكن تعديل صفحة المنتج غير موجود على GitHub. السبب أنها عدّلت الملف وحفظته في المحرر، ثم رفعت دون add ولا commit. الحل كان git status لرؤية التعديل تحت Changes not staged، ثم add و commit، ثم push.

العطل الثاني، يوم الثلاثاء: رفض الخادم رفع عمر بهذه الرسالة:

 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/USER/shop.git'
hint: Updates were rejected because the remote contains work that you do not

المعنى أن هالة رفعت commit قبله لا يملكه عمر، وقبول رفعه كان سيمسح عملها. الحل الصحيح git pull لدمج عملها مع عمله، ثم git push. ما لم يفعله عمر، ولحسن الحظ، هو نصيحة وجدها على الإنترنت بإضافة --force.

العطل الثالث، يوم الخميس: عند git pull ظهر لعمر سطر يبدأ بـ hint يقول You have divergent branches، ثم سطر fatal: Need to specify how to reconcile divergent branches. معناه أن الفرعين المحلي والبعيد تقدّما كلاهما، وإصدارات Git الحديثة تطلب منك أن تختار طريقة التوفيق بدل أن تختار عنك. الحل أن تحدد الطريقة مرة واحدة: git config --global pull.rebase false يجعل pull يدمج بـ merge، وهو الأوضح للمبتدئ، بينما pull.rebase true يعيد تأسيس عملك فوق القادم كما شرح الدرس الرابع. بعد الضبط أعاد عمر الأمر فنجح.

نعود إلى --force. الأمر git push --force يستبدل التاريخ على GitHub بتاريخك المحلي، فيمسح commits زملائك التي لم تسحبها، ولا يعرض أي تحذير. لا تستعمله أبداً على main المشترك. إن احتجته يوماً على فرعك الخاص بعد rebase، فاستعمل git push --force-with-lease، الذي يرفض الرفع إن وجد على البعيد عملاً لم تره بعد.

العادة التي منعت تكرار هذه الأعطال في ذلك الفريق بسيطة: ابدأ جلسة العمل بـ git pull، واحفظ commits صغيرة متكررة، وأنهِ الجلسة بـ git push. كلما قصرت المدة بين السحب والرفع، قلّت فرص التعارض وصغر حجمه.

  • push يرسل commits فقط، وأي تعديل بلا commit لن يصل إلى GitHub.
  • git push -u يربط الفرع المحلي بالبعيد، فيكفي بعدها push و pull وحدهما.
  • fetch ينزّل دون أن يمس ملفاتك، و pull يساوي fetch ثم دمجاً.
  • رفض الرفع يُحل بـ pull ثم push، و --force ممنوع على main المشترك.

مخطط

local mainGitHub
local mainGitHub
(مراجعة دون دمج)

مختبر: رفع وتعديل على الويب

بعد push، عدّل README من واجهة GitHub (زر Edit) واحفظه. محلياً نفّذ git fetch ثم git status: هل يقول behind؟ ثم git pull وتأكد أن التعديل ظهر.

مهمة عملية

ارفع مشروع mesue-git-lab إلى GitHub، ثم عدّل ملفاً من الويب واسحبه محلياً، وتأكد أن git status يعرض Your branch is up to date.

اختبر فهمك (1/3)

أول مرة ترفع فرع main إلى origin، أي أمر يرفع ويربط الفرعين معاً؟

اختبر فهمك (2/3)

نفّذت git push فلم يظهر تعديلك على GitHub. ما السبب الأرجح؟

اختبر فهمك (3/3)

رُفض push برسالة: Updates were rejected because the remote contains work that you do not have locally. ما التصرف الصحيح؟

للاستزادة: Git: مرجع git push · Git: مرجع git pull وطرق التوفيق · Git: مرجع git fetch · Pro Git: العمل مع المستودعات البعيدة

7. Pull Request — مراجعة قبل الدمج

الهدف: تفهم سير Pull Request في الفريق من الفرع إلى المراجعة إلى الدمج، وتختار طريقة الدمج المناسبة.

طلب السحب (Pull Request، ويُختصر PR) هو اقتراح رسمي تقول فيه: «على فرعي تغييرات، أقترح دمجها في main، فراجعوها أولاً». هو نقطة التفتيش بين العمل الفردي والنسخة المشتركة، فلا يرفع أحد مباشرة إلى main، وكل سطر يدخل الفرع الرئيسي تراه عين ثانية قبل أن يصل إلى المستخدمين.

خذ مثالاً من مشروع صغير: مطوّران يبنيان موقع قائمة طعام لمقهى في الإسكندرية. منى تضيف صفحة الحجز، وكريم يراجع. تبدأ منى على جهازها:

git switch -c feature/booking
git add booking.html
git commit -m "أضفت صفحة الحجز"
git push -u origin feature/booking

بعد الرفع تفتح منى صفحة المستودع، فتجد شريطاً أصفر فيه زر Compare & pull request. تضغطه فترى في الأعلى اتجاه الطلب: base هو main، أي الفرع الذي سيستقبل التغيير، و compare هو feature/booking. تكتب عنواناً يصف التغيير، ووصفاً يجيب عن ثلاثة أسئلة: ماذا تغيّر، ولماذا، وكيف اختبرته. ثم تختار كريم من قائمة Reviewers وتنشئ الطلب.

يفتح كريم تبويب Files changed، فيرى كل سطر أُضيف بالأخضر وكل سطر حُذف بالأحمر. يستطيع التعليق على سطر بعينه، فيكتب مثلاً: «حقل رقم الهاتف لا يتحقق من الطول، ماذا لو أدخل الزائر حرفاً؟». وعند إنهاء المراجعة يختار واحداً من ثلاثة: Comment لملاحظات عامة، أو Approve للموافقة، أو Request changes لطلب تعديل قبل الدمج. المراجعة تكتشف أخطاء لم ترها صاحبة الكود لأنها كانت منغمسة فيه، وتنقل المعرفة بين أعضاء الفريق، فيفهم كريم صفحة الحجز قبل أن يحتاج يوماً إلى إصلاحها.

طلب كريم تعديلاً، فماذا تفعل منى؟ لا تغلق الطلب ولا تفتح طلباً جديداً. تعدّل على الفرع نفسه، وتصنع commit، وتنفّذ git push، فيتحدّث الـ PR المفتوح تلقائياً، ويرى كريم التعديل الجديد ويعيد المراجعة على السطور التي تغيّرت فقط.

بعد الموافقة يأتي زر الدمج، وله ثلاثة خيارات تعرفها من الدرس الرابع. Create a merge commit يحفظ كل commits الفرع ومعها commit دمج. Squash and merge يضغط كل commits الفرع في commit واحد على main، فيبقى تاريخ main نظيفاً حتى لو كانت رسائل الفرع من نوع «إصلاح صغير» مكررة. Rebase and merge يضع commits الفرع فوق main واحداً تلو الآخر دون commit دمج. للمشاريع الصغيرة والمبتدئين، Squash غالباً الأوضح، وصاحب المستودع يستطيع من الإعدادات تعطيل الخيارات التي لا يريدها.

بعد الدمج تنظّف منى جهازها:

git switch main
git pull
git branch -d feature/booking

ملاحظة دقيقة: إن تم الدمج بطريقة Squash، فقد يرفض git branch -d الحذف بحجة أن الفرع غير مدموج، لأن commits الفرع نفسها لم تدخل main وإنما دخل commit جديد يجمعها. تأكد على GitHub أن الطلب في حالة Merged، ثم احذف الفرع بـ -D مطمئناً.

ميزتان تجعلان الطلبات أكثر تنظيماً. الأولى ربط الطلب بمهمة: إن كتبت في الوصف Closes #12 تُغلق المهمة رقم 12 تلقائياً عند دمج الطلب في الفرع الافتراضي، وتعمل كذلك كلمات مثل Fixes و Resolves. والثانية الطلب المسودّة (Draft pull request)، تفتحه حين تريد رأياً مبكراً في عمل لم يكتمل، فلا يمكن دمجه حتى تعلن جاهزيته.

وإن أردت أن تصبح المراجعة قاعدة لا نية حسنة، ففي إعدادات المستودع قواعد حماية الفروع (Branch protection)، تمنع الرفع المباشر إلى main وتشترط موافقة مراجع واحد على الأقل قبل أي دمج. وتبقى نصيحة الواقع الأهم: الطلب الصغير يُراجع بسرعة وبعناية، أما طلب من آلاف الأسطر فيحصل غالباً على موافقة سطحية لم يقرأ صاحبها نصف التغييرات.

  • PR يقترح دمج فرع في main بعد مراجعة، و base هو الفرع المستقبِل.
  • المراجع يعلّق على السطور ثم يختار Comment أو Approve أو Request changes.
  • تعديلات المراجعة تُرفع إلى الفرع نفسه فيتحدث الطلب تلقائياً.
  • Squash and merge يجمع commits الفرع في commit واحد، و Closes #12 يغلق المهمة عند الدمج.

مخطط

 feature --push--> GitHub
                    |
                    v
                 [Open PR]
                    |
                    v
              Review → Merge → main

مختبر: PR تجريبي

على repo تجريبي: فرع docs-update، عدّل README، push، افتح PR بالقالب، ثم اترك تعليقاً على سطر بنفسك وادمج بـ Squash and merge.

مهمة عملية

افتح PR واحداً على مستودعك التجريبي بوصف عربي يجيب عن ماذا ولماذا وكيف اختبرت، وادمجه، ثم احذف الفرع محلياً وعلى GitHub.

اختبر فهمك (1/3)

Pull Request يُستخدم أساساً لـ...

اختبر فهمك (2/3)

طلب المراجع تعديلاً على PR مفتوح. ماذا تفعل؟

اختبر فهمك (3/3)

تريد أن تُغلق المهمة رقم 12 تلقائياً عند دمج PR في الفرع الافتراضي. ماذا تكتب في الوصف؟

للاستزادة: GitHub Docs: عن طلبات السحب · GitHub Docs: مراجعة طلبات السحب · GitHub Docs: طرق الدمج · GitHub Docs: ربط PR بمهمة · GitHub Docs: الفروع المحمية

8. .gitignore والأسرار — لا ترفع كلمات السر

الهدف: تستثني الملفات الحساسة والمولّدة، وتنقل الأسرار من الكود إلى متغيرات البيئة، وتعرف خطة الطوارئ إن تسرّب مفتاح.

ملف .gitignore في جذر المستودع يخبر Git بقائمة ملفات لا يتتبعها ولا يعرضها في status. يُستعمل لنوعين من الملفات: الحساسة كالأسرار والمفاتيح، والمولّدة تلقائياً كالمكتبات المنزّلة والسجلات وملفات البناء، التي يستطيع أي شخص إعادة توليدها فلا داعي لتضخيم التاريخ بها.

لنبدأ بمثال قبل وبعد. طالب يبني بوت تيليجرام بلغة Python لمتجر صغير، وكتب في ملف settings.py السطر API_KEY = "abc123-real-key"، ثم رفع المشروع إلى مستودع عام. أي شخص يفتح الملف يستطيع استخدام المفتاح باسمه. بعد التصحيح أصبح الكود يقرأ المفتاح من متغيرات البيئة، والقيمة الحقيقية في ملف .env لا يدخل Git أبداً:

# settings.py — يُرفع
import os
API_KEY = os.environ["API_KEY"]
# .env — لا يُرفع
API_KEY=abc123-real-key
# .env.example — يُرفع بقيم وهمية
API_KEY=put-your-key-here

الملف .env.example يخبر زملاءك بأسماء المتغيرات المطلوبة دون قيمها، فينسخه كل شخص إلى .env ويضع مفاتيحه محلياً. ولتحميل .env إلى متغيرات البيئة تلقائياً عند التشغيل تستخدم مكتبة مثل python-dotenv في Python، ولكل لغة شائعة مكتبة مشابهة.

هذا ملف .gitignore نموذجي يغطي الحالتين:

.env
*.log
node_modules/
__pycache__/
credentials/
/build
!keep.log

اقرأ الأنماط بدقة. *.log يطابق أي ملف ينتهي بـ .log في أي مجلد. node_modules/ بشرطة في آخره يطابق مجلداً بهذا الاسم أينما وُجد. /build بشرطة في أوله يطابق build في جذر المستودع فقط. و !keep.log بعلامة التعجب يستثني ملفاً من قاعدة سابقة، فيُتتبع رغم قاعدة *.log. ولا تكتب القائمة من الصفر: مستودع github/gitignore يضم قوالب جاهزة لمعظم اللغات والأطر، ويعرضها GitHub عند إنشاء أي مستودع.

ملفات مثل .DS_Store التي ينشئها macOS و Thumbs.db التي ينشئها Windows تخص جهازك لا المشروع. بدل تكرارها في كل مستودع، يمكنك وضعها في ملف تجاهل عام لحسابك وتعريفه مرة واحدة بالأمر git config --global core.excludesFile ~/.gitignore_global. أما ما يخص المشروع نفسه، كمجلد البناء أو ملف .env، فمكانه .gitignore داخل المستودع، حتى تسري القاعدة على كل زملائك لا على جهازك وحدك.

الفخ الأكبر: .gitignore لا يؤثر على ملف يتتبعه Git أصلاً. إن صنعت commit لملف .env ثم أضفته إلى القائمة، سيظل Git يتتبعه ويرفع تعديلاته. عليك إخراجه من التتبع صراحة:

git rm --cached .env
rm '.env'
git commit -m "توقف عن تتبع .env"
git check-ignore -v .env
.gitignore:1:.env	.env

الخيار --cached يزيل الملف من المستودع ويبقيه على جهازك. والأمر git check-ignore -v يطبع الملف والسطر والقاعدة المسؤولة عن تجاهل مسار ما، فإن لم يطبع شيئاً فالملف غير متجاهَل وعليك مراجعة القاعدة.

خطة الطوارئ إن رفعت سراً بالخطأ، بهذا الترتيب تحديداً:
1) أبطل السر فوراً من لوحة الخدمة وأصدر بديلاً. وثائق GitHub نفسها تجعل هذه الخطوة الأولى، لأن الرابط العام قد يكون نُسخ في دقائق، وبعد الإبطال يصبح المفتاح المسرّب بلا قيمة.
2) أخرج الملف من التتبع بـ git rm --cached، وأضفه إلى .gitignore، وانقل القيمة الجديدة إلى .env.
3) تذكّر أن الـ commit القديم ما زال في التاريخ، وحذف الملف الآن لا يمحوه. تنظيف التاريخ يحتاج أداة مثل git filter-repo وتنسيقاً مع كل من نسخ المستودع، والنسخ المتفرعة (forks) قد تحتفظ به رغم ذلك. لهذا لا تغني هذه الخطوة أبداً عن الأولى.

يوفّر GitHub شبكة أمان إضافية. في المستودعات العامة يعمل فحص الأسرار (Secret scanning) تلقائياً ومجاناً، فيبحث عن صيغ مفاتيح معروفة لمزودي خدمات كثيرين وينبّه عند اكتشافها. وقد يوقف خاصية حماية الدفع (Push protection) رفعك قبل وصوله إن احتوى سراً بصيغة مدعومة. هاتان أداتان مفيدتان، لكنهما لا تعرفان كل الصيغ، وكلمة سر قاعدة بيانات عادية قد تمر دون أن يلاحظها أحد.

القاعدة التي لا استثناء لها: مجلد credentials/ وملف .env خارج Git دائماً، وأفضل وقت لإنشاء .gitignore هو قبل أول commit، لا بعد أول حادثة.

  • الأسرار تعيش في .env المستثنى، و .env.example يُرفع بأسماء المتغيرات وقيم وهمية.
  • .gitignore لا يؤثر على ملف متتبّع؛ أخرجه أولاً بـ git rm --cached.
  • git check-ignore -v يكشف القاعدة التي تتجاهل مساراً ما.
  • إن تسرّب سر فأبطله أولاً، فحذفه من الملفات لا يمحوه من التاريخ.

مخطط

ملفات
.gitignoreGit يتتبع
.env✗ ممنوع
.env.example✓ قيم وهمية

مختبر: ignore و check-ignore

أنشئ .env و app.log و .env.example. أضف القواعد إلى .gitignore ثم نفّذ git status: يجب أن يظهر .env.example وحده. جرّب git check-ignore -v .env.

مهمة عملية

أضف .gitignore مناسباً لمشروعك التجريبي مع ملف .env.example، وتأكد بـ git status و git check-ignore -v أن .env لا يُتتبع.

اختبر فهمك (1/3)

أين تضع مفتاح API السري في مشروعك؟

اختبر فهمك (2/3)

رفعت .env في commit سابق ثم أضفته إلى .gitignore، وما زال Git يتتبعه. ما الحل؟

اختبر فهمك (3/3)

اكتشفت أنك رفعت مفتاح API إلى مستودع عام. ما الخطوة الأولى؟

للاستزادة: Git: مرجع أنماط gitignore · GitHub Docs: تجاهل الملفات · github/gitignore: قوالب جاهزة · GitHub Docs: فحص الأسرار · GitHub Docs: حماية الدفع · GitHub Docs: إزالة البيانات الحساسة

9. التعاون — Issues و Fork و تعارض بسيط

الهدف: تكتب Issue مفيداً، وتساهم في مشروع لا تملكه عبر Fork، وتحل تعارض دمج خطوة بخطوة.

المهام (Issues) هي قائمة عمل المشروع وسجل أخطائه على GitHub. الفرق بين Issue مفيد وآخر مهمل يظهر من العنوان. قارن «الموقع خربان» بعنوان مثل «زر الإرسال في صفحة التواصل لا يستجيب على Chrome في Android». الثاني يخبر المطوّر أين يبحث قبل أن يفتح التفاصيل. وفي المتن اكتب خطوات إعادة الخطأ مرقمة، ثم السلوك المتوقع، ثم ما حدث فعلاً، وأرفق لقطة شاشة إن أمكن.

لتنظيم المهام أداتان أساسيتان. التصنيفات (Labels) مثل bug و enhancement و documentation تفرز القائمة، والتصنيف good first issue يدل المساهمين الجدد على مهام مناسبة لبدايتهم. والمكلّفون (Assignees) يحددون من يعمل على كل مهمة، فلا يقضي شخصان يوماً كاملاً في إصلاح الخطأ نفسه دون أن يعلم أحدهما بالآخر.

ننتقل إلى سؤال يتكرر كثيراً: ما الفرق بين Fork و Clone؟ الاستنساخ (Clone) ينسخ مستودعاً إلى جهازك، لكنه لا يمنحك صلاحية الرفع إليه إن لم تكن من أصحابه. أما التفريع (Fork) فينسخ مستودع شخص آخر إلى حسابك أنت على GitHub، فتملك النسخة وترفع إليها بحرية، ثم تقترح تغييراتك على الأصل عبر PR. مسار المساهمة في مشروع مفتوح المصدر إذن: Fork، ثم clone لنسختك، ثم فرع جديد، ثم commits و push إلى نسختك، ثم PR نحو المستودع الأصلي.

ولأن المستودع الأصلي يستمر في التقدم، تحتاج أن تحدّث نسختك بين حين وآخر. يوفّر GitHub زر Sync fork في صفحة نسختك، ومن الطرفية تضيف الأصل باسم upstream:

git remote add upstream https://github.com/ORIGINAL/repo.git
git fetch upstream
git switch main
git merge upstream/main

الآن إلى التعارض (Merge Conflict). يحدث حين يعدّل فرعان السطر نفسه بطريقتين مختلفتين، فلا يعرف Git أيهما الصحيح ويطلب منك القرار. لنتابع مثالاً خطوة بخطوة: على main غيّر زميل عنوان الصفحة الرئيسية إلى «مرحباً بكم»، وعلى فرعك feature/title غيّرته إلى «أهلاً وسهلاً». عند الدمج يتوقف Git:

git merge feature/title
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

لا داعي للقلق، فلم يضع شيء. نفّذ git status، وستجد الملف تحت عنوان Unmerged paths مع وصف both modified. افتح index.html فتجد أن Git كتب النسختين معاً بين علامات خاصة:

<<<<<<< HEAD
<h1>مرحباً بكم</h1>
=======
<h1>أهلاً وسهلاً</h1>
>>>>>>> feature/title

ما بين <<<<<<< HEAD و ======= هو نسخة الفرع الذي تقف عليه (main هنا)، وما بعدها حتى >>>>>>> هو القادم من الفرع الذي تدمجه. قرارك واحد من ثلاثة: تبقي نسخة الفرع الحالي، أو القادمة، أو تكتب مزيجاً منهما. بعد القرار احذف أسطر العلامات الثلاثة كلها، فإن نسيت واحداً منها سيظهر حرفياً في صفحة الموقع.

ثم أخبر Git أنك انتهيت: git add index.html يعلّم الملف كمحلول، ثم git commit ينشئ commit الدمج، ويفتح المحرر برسالة جاهزة من نوع Merge branch، ويمكنك قبولها مباشرة بـ git commit --no-edit. في VS Code تظهر فوق كل تعارض أزرار مثل Accept Current Change و Accept Incoming Change و Accept Both Changes تختصر التحرير اليدوي. وإن ارتبكت تماماً في منتصف الطريق، فـ git merge --abort يعيدك إلى ما قبل الدمج بلا أي خسارة.

التعارضات جزء طبيعي من العمل الجماعي وليست دليلاً على خطأ أحد، لكن تقليلها ممكن بثلاث عادات: فروع قصيرة العمر تُدمج خلال أيام لا أسابيع، وسحب متكرر من main إلى فرعك، وكلمة في محادثة الفريق قبل إعادة تنسيق ملف كبير يعمل عليه غيرك.

  • Issue جيد له عنوان محدد وخطوات إعادة ونتيجة متوقعة وفعلية.
  • Fork ينسخ المستودع إلى حسابك للمساهمة عبر PR، و Clone ينسخه إلى جهازك.
  • التعارض يُحل باختيار النسخة وحذف العلامات ثم git add و git commit.
  • git merge --abort يلغي الدمج ويعيدك إلى ما قبله بلا خسارة.

مخطط

 Issue → branch → PR → merge
 Fork → clone → PR upstream

 <<<<<<< HEAD
 نسختك
 =======
 القادمة
 >>>>>>> feature

مختبر: صنع تعارض وحله

في مستودع تجريبي: عدّل السطر الأول من README على main واحفظه، ثم أنشئ فرعاً من commit سابق وعدّل السطر نفسه بطريقة أخرى. ادمج، واقرأ git status، وحل التعارض يدوياً.

مهمة عملية

أنشئ Issue بالقالب على مستودعك التجريبي، ثم اصنع تعارضاً متعمداً وحله، وانسخ مخرجات git log --oneline --graph التي تُظهر commit الدمج.

اختبر فهمك (1/3)

علامات <<<<<<< و ======= و >>>>>>> داخل ملف تعني...

اختبر فهمك (2/3)

تريد المساهمة في مشروع مفتوح المصدر لا تملك صلاحية الكتابة فيه. ما المسار الصحيح؟

اختبر فهمك (3/3)

اخترت النسخة الصحيحة داخل الملف المتعارض. ما الذي بقي لإنهاء الدمج؟

للاستزادة: GitHub Docs: عن المهام (Issues) · GitHub Docs: تفريع مستودع · GitHub Docs: مزامنة النسخة المتفرعة · GitHub Docs: حل التعارض من سطر الأوامر · Git: مرجع git merge

10. المشروع النهائي — محفظة GitHub

الهدف: تسلّم مستودع محفظة عاماً بـ README عربي واضح، وتاريخ نظيف، وفحص أمان قبل النشر.

المطلوب في هذا المشروع مستودع عام اسمه mesue-portfolio أو اسم مشابه، يعمل كواجهة تعرض من أنت وما صنعت. من يزور حسابك، سواء كان صاحب عمل أو زميلاً أو لجنة منحة، يقرر خلال دقيقة هل يكمل القراءة، والمستودع الجيد يسهّل عليه هذا القرار.

قائمة التسليم:
• ملف README.md عربي يعرّف بك ويعرض من 3 إلى 5 مشاريع بروابطها.
• ملف .gitignore مناسب، ولا أسرار في الملفات ولا في التاريخ.
• خمسة commits على الأقل برسائل واضحة تحكي تطور العمل.
• اختيارياً: موقع منشور عبر GitHub Pages وملف ترخيص LICENSE.

ملف README هو الصفحة الأولى فعلياً، لأن GitHub يعرضه تلقائياً أسفل قائمة الملفات، ومعظم الزوار لن يفتحوا ملفاً غيره. يُكتب بصيغة Markdown: العناوين بعلامة #، والقوائم بشرطة، والروابط بالشكل [النص](الرابط)، والصور بمسار نسبي داخل المستودع مثل ![لقطة](images/demo.png). لكل مشروع يكفي سطران: ماذا يفعل، وبأي تقنيات بُني، ثم رابط للكود ورابط للنسخة الحية إن وجدت. هذا مقتطف من README لطالبة تبدأ مسيرتها:

# سارة أحمد — مطوّرة ويب مبتدئة
مهتمة بواجهات الويب العربية وسهولة الوصول.
## المشاريع
- [قائمة مقهى](https://github.com/USER/cafe-menu): موقع ثابت بـ HTML و CSS يدعم الاتجاه من اليمين لليسار.
- [حاسبة المصاريف](https://github.com/USER/expenses): سكربت Python يقرأ ملف CSV ويطبع مجموع كل فئة.
## تواصل
- GitHub: @USER

لاحظ أن كل سطر مشروع يجيب عن سؤال القارئ مباشرة: ما هذا، ولماذا يستحق النقر. قارنه بقائمة أسماء مستودعات بلا وصف، يضطر فيها الزائر إلى فتح كل واحد ليفهم محتواه، وغالباً لن يفعل.

ميزة يغفل عنها كثيرون هي مستودع الملف الشخصي. وفق وثائق GitHub، إن أنشأت مستودعاً عاماً اسمه مطابق تماماً لاسم مستخدمك، وفي جذره ملف README.md فيه أي محتوى، يعرضه GitHub في أعلى صفحة حسابك. هذا المكان المثالي لتعريف قصير من ثلاثة أسطر مع رابط المحفظة.

ولتحويل المحفظة إلى موقع حقيقي استخدم GitHub Pages: من Settings ثم Pages اختر Deploy from a branch، وحدد الفرع main والمجلد الجذر، فيُنشر الموقع على رابط من الشكل USER.github.io/mesue-portfolio. يكفي ملف index.html بسيط، وكل push لاحق يحدّث الموقع تلقائياً بعد دقائق.

التاريخ يروي قصة أيضاً. commits منتظمة برسائل مثل «أضفت قسم المشاريع» و «حسّنت عرض الصفحة على الموبايل» تُظهر طريقة عملك، بينما commit واحد ضخم بعنوان «رفع كل شيء» لا يقول شيئاً. ولا تصنع commits فارغة لتملأ مربعات النشاط الخضراء، فأي مراجع خبير يفتح التاريخ يلاحظ ذلك فوراً ويخصم من ثقته.

فحص الأمان قبل جعل المستودع عاماً، لأن المستودع العام يكشف التاريخ كله لا الملفات الحالية فقط:

git grep -n -i "password"
git log -p -S "API_KEY"

الأمر الأول يبحث في الملفات الحالية المتتبعة ويطبع رقم السطر. والثاني يبحث في التاريخ عن كل commit أضاف النص أو حذفه ويعرض التغيير كاملاً. أي نتيجة تشير إلى سر حقيقي تعني العودة إلى خطة الطوارئ في الدرس الثامن قبل النشر: أبطل السر أولاً، ثم نظّف.

ملف الترخيص يحدد ما يحق للآخرين فعله بكودك. من دونه يبقى الكود محمياً بحقوق النشر الافتراضية حتى لو كان المستودع عاماً، فلا يحق لغيرك قانونياً إعادة استخدامه أو تعديله. إن أردت السماح بذلك، يقترح GitHub قوالب لتراخيص شائعة مثل MIT عند إضافة ملف باسم LICENSE، ويشرح موقع choosealicense.com الفروق بينها بلغة بسيطة.

اللمسات الأخيرة: ثبّت أفضل ستة مستودعات في صفحة حسابك عبر Customize your pins، وأضف وصفاً قصيراً لكل مستودع في خانة About. ومعيار النجاح النهائي أربعة شروط: الرابط يفتح لأي زائر دون تسجيل دخول، والزائر يفهم README خلال ثلاثين ثانية، والفحص لم يجد أسراراً، وآخر commit رسالته «Portfolio ready for review».

  • README هو واجهة المحفظة: لكل مشروع سطران ورابط للكود وللنسخة الحية.
  • مستودع عام باسم مستخدمك وفيه README يظهر أعلى صفحة حسابك.
  • GitHub Pages ينشر موقعاً ثابتاً من فرع main مباشرة.
  • افحص الملفات والتاريخ بـ git grep و git log -S قبل جعل المستودع عاماً.

مخطط

READMEمشاريعروابط
GitHub Profile
فرص / تعلّم / عمل

مختبر: العرض النهائي

شارك رابط المستودع مع صديق لم يره من قبل، واطلب منه أن يشرح لك مشاريعك بعد 30 ثانية من README. ما لم يفهمه هو ما يجب إعادة كتابته.

مهمة عملية

انشر mesue-portfolio عاماً بـ README عربي وخمسة commits على الأقل، ونفّذ أمري الفحص بلا نتائج حساسة، ثم ضع رابطه في سيرتك الذاتية أو حساباتك المهنية.

اختبر فهمك (1/3)

أفضل عنصر في محفظة GitHub للمبتدئ؟

اختبر فهمك (2/3)

حذفت ملف كلمات السر من مستودعك قبل جعله عاماً. هل يكفي؟

اختبر فهمك (3/3)

تريد أن يظهر تعريفك في أعلى صفحة حسابك على GitHub. ماذا تفعل؟

للاستزادة: GitHub Docs: README الملف الشخصي · GitHub Docs: إنشاء موقع GitHub Pages · GitHub Docs: أساسيات Markdown · GitHub Docs: تثبيت المستودعات في الملف الشخصي

مراجع رسمية لهذا المسار

محفظتك على GitHub جاهزة للانطلاق

اربط مسارات Learn في README، ثم جرّب مسار Flutter أو السحابة لنشر مشروعك.