أساسيات الـ Git
السلام عليكم ورحمة الله وبركاته
الفهرس
- المقدمة
- ما هو الـ Git
- كيف يتعامل الـ Git مع مشروعك ؟
- كيف يتعامل Git مع ملفات المشروع ؟
- أهم أوامر الـ Git
- التعريف بنفسك
- git init
- git status
- git add file-name
- git commit -m nice-message
- git restore file-name
- git restore --staged file-name
- git rm file-name
- git rm --cached file-name
- git log
- git revert commit-hash
- git reflog
- الـ HEAD الكبير
- git reset commit-hash
- git reset --soft commit-hash
- git reset --mixed commit-hash
- git reset --hard commit-hash
- جدول لتلخيص الفروق بين soft و mixed و hard في git reset
- git checkout commit-hash
- ما هو الـ Stash ؟
- أهم أوامر الـ Stash
- خاتمة
المقدمة
اليوم لدينا مقالة دسمة إلى حد ما، سأتكلم في هذه المقالة عن الـ Git
وكيف يعمل وكيف نستفيد منه ونستخدمه وما أهم الأوامر الخاصة به
وكيف يفيدنا في تنظيم مشاريعنا وتنظيم طريق العمل ضمن فريق
في هذه المقالة سأركز على الـ Git
في بيئة عمل Local
أي على جهازك الشخصي
ثم في مقالة أخرى سنتكلم عن مفهوم الـ Branch
ويمكنكم قراءتها من هنا الـ Git Branch، آله السفر عبر الأبعاد
ثم في مقالة أخرى سنتطرق لجزء الـ Remote
الخاص بالـ Git
وكيف نتعامل مع GitHub
وكل تلك الأمور إن شاء الله
يمكنكم قراءتها من هنا الـ Git Remotes، وما علاقة الـ GitHub بالـ Git ؟
ما هو الـ Git
الـ Git
هو باختصار آلة السفر بين الزمن والأبعاد ... أقصد أنه أشهر Version Control System
بمعنى أنه نظام متكامل تم إنشاؤه لتنظيم وإدارة المشروعات بسلاسة ويسجل كل التغيرات والإضافات التي حدثت لمشروع على مر الزمن
بعض من أهم ما يقدمه:
- يتابع تاريخ المشروع بالكامل عبر الزمن، بما في ذلك على سبيل المثال الإضافات والتعديلات وما الذي تم حذفه
والإشارة إلى من قام بالتغير ومتى قام به - يمكن أيضا استخدام
Git
كآلة للسفر عبر الزمن، حيث يمكنك العودة إلى نسخة قديمة من مشروعك في أي وقت ومقارنة التعديلات السابقة بالحالية - من بين أهم مزاياه أيضا أنه يمكنك استنساخ المشروع لعدة نسخ، وكل نسخة تطور فيها وتعدل وتفعل ما تريده دون أن تأثر على نسخة المشروع الأساسية
- أو تقسم العمل ضمن الفريق حيث يمكن لكل عضو العمل على نسخته من الكود في فرع مختلف بشكل مستقل عن باقي الفريق
(... همس: قلت لك أنه جهاز سفر عبر الأبعاد)
سنتكلم عن كل هذه التفاصيل وأكثر
كيف يتعامل الـ Git مع مشروعك ؟
هناك عدة مراحل يتخذها الـ Git
للتنقل الحالات المختلفة لمشروعك والتعديلات
وهذه المراحل مهمة جدًا في أن تستوعبها وتفهمها جيدًا لأنها ستكون أساس كل شيء سنشرحه في باقي هذه المقالة
ملحوظة
: أول شيء عليك معرفته هو أن الـGit
ينشيء مجلد يدعى.git
داخل المشروع الخاص بك
وهو المنزل الخاص بـGit
الذي يضم كل شيء متعلق به
بما في ذلك الـLocal Repository
والـStaging Area
وStash
وكل شيء متعلق ويستخدمه الـGit
Working Directory
الـ Working Directory
وأيضًا يطلق عليه الـ Working Tree
هو المجلد الأساسي الذي يحتوي على ملفات المشروع على جهازك
Local Repository
الـ Local Repository
هو مستودع يستخدمه الـ Git
ليحتفظ ويتابع التعديلات الموجودة في الـ Working Directory
الخاص بك على جهازك
ويطلق عليه عدة مسميات مثل Git Repository
أو Git Directory
أو Git History
Upstream Repository
الـ Upstream Repository
أو الـ Remote Repository
هو كالـ Local Repository
لكنه نسخة رئيسية من المشروع يتم استضافتها في مكان ما على الانترنت مثل GitHub
أو GitLab
أو غيرهم كثير
ويسهل العمل مع فريق من أماكن مختلفة ويقدم مميزات كثيرة تساعد على ها وتحقيق اقصى استفادة ممكنة في العمل ضمن فريق
بمعنى أن كل عضو لديه نسخته من المشروع ويعدل في الـ Local Repository
الخاص به ثم في النهاية يرفع تلك التعديلات على الـ Remote Repository
ليتشاركها مع الجميع وكل الفريق يتابع آخر التعديلات التي قام بها كل عضو
Staging Area
الـ Staging Area
وتسمى أيضًا بالـ Index
أو Cache
هي مرحلة وسيطة ما بين الـ Working Directory
والـ Local Repository
تستخدم لوضع التعديلات التي قمنا بها في الـ Working Directory
قبل نقلها إلى الـ Local Repository
أو العكس لوضع التعديلات التي ترغب في التراجع عنها من الـ Local Repository
قبل إزالتها من الـ Working Directory
هذه المنطقة الوسيطة وجدت لتجنب التعديل والتغير المباشر ما بين الـ Working Directory
و الـ Local Repository
لكي يتم مراجعتها جيدًا والتأكد من كل شيء قبل تنفيذ العملية التي نريدها سواء إضافة أو تعديل أو حذف
ومن المهم دائمًا وجود مرحلة تأكيدية كتلك لتجنب الأخطاء الغير مقصودة أو الاستعجالية التي تحدث عندما نفذ التعديل بشكل مباشر
وأيضًا يحتفظ بالملفات الـ Tracked
ليتابعها ويقارنها مع النسخة التي في الـ Working Directory
ليتأكد هل تم تعديلها أم لا
Stash
الـ Stash
هو مكان يستخدم لحفظ التعديلات التي قمت بها بشكل مؤقت، ثم يمكنك العمل على شيء آخر أو أن تنتقل
إلى branch
آخر، ثم بعد انتهائك يمكنك أن تعود وتستحضر التعديلات المحفوظة التي قمت بها من الـ Stash
في أي وقت
كيف يتعامل Git مع ملفات المشروع ؟
نستطيع أن نقول أن Git
يصنف الملفات لعدة حالات ليسهل عليه التعامل معها ويتابعها ويراقب التغيرات
Untracked Files
الـ Untracked Files
هي الملفات التي لا يتم متابعتها من قبل Git
بمعنى أنها ملفات موجودة في الـ Working Directory
ولكن Git
لا يعرف شيئًا عنها ولم يتخذ أي إجراء تجاهها من قبل
أي أنها لم تُنقل إلى الـ Staging
من قبل أو تم حذفها من الـ Staging
والـ Git
لم يعد يتابعها
Tracked Files
الـ Tracked Files
هي الملفات التي أصبح Git
يدرك وجودها ويتابع أي تغير فيها
ويملك نسخة منها في الـ Staging
ليتابعها
Modified Files
هي الملفات التي كان Git
يتابعها أي أنها Tracked
بالفعل لكن حصل لها تعديل ولم تنقل إلى مرحلة الـ Staging
بعد
بمعنى أن نسختها التي في الـ Working Directory
مختلفة عن نسختها التي يتم متابعتها في الـ Staging
Staged Files
هي الملفات التي كان Git
يتابعها أي أنها Tracked
بالفعل وحصل لها تعديل ثم نُقلت إلى مرحلة الـ Staging
أي أن نسختها التي في الـ Staging
مختلفة عن نسختها التي في الـ Local Repository
أهم أوامر الـ Git
حسنًا سنتخذ مثال وهمي بسيط وسنشرح أهم الأوامر الخاصة بـ Git
عن طريق المثال
حسنًا لنتخيل أننا لدينا مشروع لمدونة جميلة وهذا المشروع حاليا فيه ملف واحد يمثل مقالة واحدة
> ls
article_1.txt
التعريف بنفسك
أول شيء نفعله وهو أن تعرف نفسك لـ Git
لذا نستخدم git config --global user.email "[email protected]"
و git config --global user.name "your name"
لتسجيل اسمك وبريدك الإلكتروني في Git
على مستوى جهازك الشخصي
أما اذا اردت على مستوى المشروع الحالي فيمكنك ازالة --global
> git config --global user.email "[email protected]"
> git config --global user.name "AhmedEl-Tabarani"
git init
نستخدم git init
لمرة واحدة فقط داخل المجلد الذي فيه المشروع أي الـ Working Directory
لنخبر الـ Git
أن يبدأ بإنشاء مجلد الـ .git
الذي يضم كل شيء متعلق به
بما في ذلك الـ Local Repository
والـ Staging Area
و Stash
يبدأ في مراقبة ومتابعة أي تعديلات في هذا الـ Working Directory
> git init
Initialized empty Git repository in blog/.git/
لاحظ أنه يخبرنا أنه أنشيء Git Repository
وهو مجلد يدعى .git
وهو المنزل الخاص بـ Git
الذي الذي يضم كل شيء متعلق به كما قلنا
وهو مجلد خفي لكن يمكنك ان تراه وتتأكد من وجوده عن طريق أمر بسيط
> ls --all
./ ../ .git/ article_1.txt
ملحوظة
: وعندما نقوم بعملgit init
لاول مرة لمشروعنا يبدأGit
بعملbranch
فارغ يدعىmain
لا يحتوي على أيcommit
بعد
git status
نستخدم git status
لمعرفة حالة المشروع والملفات التي لدينا
هل تم تعديل الملفات أم لا ؟ هل يوجد ملفات جديدةUntracked
أم لا
أو هل هناك ملفات نُقلت للـ Staging
أم لا ... إلخ
وأيضا دائمًا ما يساعدك على اقتراح لك بعض الأوامر التي يمكنك أن تقوم بها في الحالة التي أنت فيها
> git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
article_1.txt
nothing added to commit but untracked files present (use "git add" to track)
لاحظ أن git status
أخبرنا بالكثير من المعلومات، بما في ذلك:
- أننا في الـ
branch
الأساسيmain
الذي ينشئهGit
لنا ليضم كل الـcommit
الخاصة بمشروعنا - أننا لا نملك أي
commit
بعد وهذا منطقي لأننا لم نفعل شيء بعد - أن هناك ملف في الـ
Working Directory
يسمىarticle_1.txt
من ضمن الـUntracked Files
وGit
لا يعرف عنه أي شيء - أيضًا بعض النصائح حول كيفية التصرف، مثل استخدام
git add
لتحويل الملف إلىTracked
من قبلGit
وأيضًا لنقل الملف إلى مرحلة الـStaging
git add file-name
نستخدم git add
عندما نريد نقل التعديلات من الـ Working Directory
إلى مرحلة الـ Staging
ويحول الملفات من حالة Untracked
إلى Tracked
يمكنك git add
ثم اسم الملف أو اسم المجلد الذي تريد نقله إلى مرحلة الـ Staging
أو يمكنك القيام بكتابة git add
ثم .
أو *
لنقل كل التعديلات
> git add article_1.txt
الآن، بعد إضافة الملف article_1.txt
إلى مرحلة الـ Staging
، يمكننا أن نستخدم git status
مجددًا إذا اردنا لكي نرى حالة الملفات الآن
> git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: article_1.txt
لاحظ أنه لم يعد يقول لنا أن الملف article_1.txt
انه Untracked
بل يخبرنا أنه الآن في مرحلة الـ Staging
وأنه اصبح جاهزًا لكي نقوم بعمل commit
لننقله إلى الـ Local Repository
ويقول استخدم git rm --cached <file>
لك إن أردنا حذف الملف من مرحلة الـ Staging
وإرجاعه مجددا لحالة الـ Untracked
git commit -m nice-message
نستخدم git commit
عندما نريد نقل التعديلات من مرحلة الـ Staging
إلى الـ Local Repository
وعندما نقوم بعمل commit
فهكذا كأننا نقوم بحفظ نسخة جديدة من المشروع والتعديلات التي حصلت فيه
وقد تسمع أن commit
ينشيء snapshot
لمشروع أي نسخة من المشروع
ويمكننا القول أن الـ Local Repository
هو مجموعة من الـ snapshot
التي تم حفظها من المشروع
ولاحظ أن git commit
تقوم بنقل كل الملفات والتغيرات المتواجدة في مرحلة الـ Staging
كحزمة واحدة إلى الـ Local Repository
في commit
واحد فقط
بمعنى أن commit
واحد قد يضم أكثر من ملف وتعديل
ونستخدم -m
لكتابة رسالة توضح التغييرات التي تمت في هذا الـ commit
لتساعدك أنت والفريق الذي معك بتبع تاريخ كل التغيرات التي حصلت في المشروع فيما بعد
ويفضل أن تكون الرسالة مختصرة ومعبرة وغالبًا ما تتفق أنت وباقي الفريق على صيغة معينة لكتابة تلك الرسائل التوضحية
> git commit -m "add new article to the blog"
[main (root-commit) 4032898] add new article to the blog
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 article_1.txt
لاحظ أن Git
يخبرنا ببعض المعلومات الجميلة منها
- الـ
commit
هذا هو الـroot-commit
لفرع الرئيسي للمشروع وهوbranch
الـmain
- يخبرنا بعدد الملفات التي حدث فيها التغير، وفي حالتنا ملف واحد فقط
- يخبرنا بعدد الاسطر التي اضيفت والتي حذفت، وبما ان الملف الذي اضفناه كان فارغًا فلا يوجد اسطر
وأمر لم يخبرنا به وهو أنه أصبح لدينا مؤشر يدعى HEAD
ويؤشر على آخر commit
تم اضافته إلى الـ main
وسنتحدث عن هذا بالتفصيل لاحقًا في هذه المقالة
ملحوظة
: الـHEAD
يتواجد داخل الـLocal Repository
بالطبع، والـcommit
الذي يشاور عليه هو ما نراه في الـWorking Directory
وسنفهم معنى هذا لاحقًا
حسنًا لنقم بعمل git status
مجددًا لكي نرى حالة الملفات الآن
> git status
On branch main
nothing to commit, working tree clean
الآن، يخبرنا Git
أنه لا توجد تغييرات جديدة
وبالتالي لا توجد أي ملفات تحتاج إلى الانتقال إلى مرحلة الـ Staging
أو عمل لها commit
ونقلها إلى الـ Local Repository
ولذا يقول لك أن الـ Working Tree
أي الـ Working Directory
الخاص بالمشروع نظيف ولا يحتاج إلى أي شيء
git restore file-name
نستخدم git restore
ثم اسم الملف عندما يكون الملف حالته Tracked
بالنسبة للـ Git
لكن تم التعديل عليه
أي أن نسخته التي في الـ Working Directory
مختلفة عن نسخته التي في الـ Staging
ونريد التراجع عن هذا التعديل وإرجاع الملف لحالته الأصلية التي كان عليها
ما يقوم به git restore
حقًا هو أنه يأخذ نسخة الملف التي في الـ Staging
ويستبدلها مع الملف الذي في الـ Working Directory
الـ Staging
هو أيضًا يحتفظ بنسخة بالملفات التي يتابعها وعن طريقها يستطيع معرفة إن حدث تعديل أم
بالتالي إن كان هناك ملف تم تعديله في الـ Working Directory
فنستطيع عن طريق استخدام git restore
استبدالها لترجع وتصبح طبق الأصل لنسختها التي في الـ Staging
كما كانت
ونكون قد تخلصنا من التعديلات التي حدثت للملف في الـ Working Directory
لنرى مثال توضيحي، أولًا لنعدل في ملف المقالة article_1.txt
ونضيف أي شيء
> echo "In this article we will prove that git is a time machine!" > article_1.txt
بعد ما اضفنا سطر وعدلنا في article_1.txt
، لنقم بعمل git status
لكي نرى حالة الملف
> git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: article_1.txt
no changes added to commit (use "git add" and/or "git commit -a")
أنظر كيف يقوم الأمر git status
بتوضيح كل شيء لك ويساعدك على اتخاذ القرارات
يقول لك هنا:
- أنه لا يوجد ملفات حالتها
Untracked
لكن يوجد ملفTracked
تم التعديل عليه
بمعنى أن نسخته في الـWorking Directory
مختلفة عن نسخته في الـStaging
- ويقول لك أنك يمكنك أن تضيف هذا الملف إلى مرحلة الـ
Staging
عن طريقgit add
- أو يمكنك التراجع عن التعديل وجعل الملف يرجع لحالته الأصلية التي كان عليها عن طريق
git restore
لذا لكي نخبره أننا نريد التراجع عن التعديل فسنقول له git restore article_1.txt
> git restore article_1.txt
هكذا سيقوم Git
بالذهاب إلى الـ Staging
واحضار نسخة الملف article_1.txt
المتواجده فيه ويستبدلها
بالملف الذي في الـ Working Directory
git restore --staged file-name
هذا هو نفس الأمر السابق لكن مع زيادة --staged
والفرق هنا ان الملف الذي تم التعديل عليه دخل إلى مرحلة الـ Staging
بالفعل عن طريق git add
والآن نريد أن نخرجه فقط من مرحلة الـ Staging
دون أن نتراجع عن التعديلات التي في الملف في الـ Working Directory
لذا مع زيادة --staged
يقوم Git
بإحضار نسخة الملف من الـ HEAD
المتواجد في الـ Local Repository
ثم يستبدله بالملف الذي في الـ Staging
كهذا سيصبح الملف الذي في الـ Staging
مطابق لنسخة الملف الذي في الـ HEAD
المتواجد في الـ Local Repository
وهكذا تراجعنا عن التعديل واخرجناه من مرحلة الـ Staging
لنفقم برؤية هذا بشكل عملي
> git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: article_1.txt
no changes added to commit (use "git add" and/or "git commit -a")
لنفترض أننا وصلنا إلى هذه النقطة ثم قمنا بعمل git add
بهذا الشكل
> git add article_1.txt
الآن سنقوم بعمل git status
لنرى ما الذي حدث
> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: article_1.txt
لاحظ أن الملف تم نقله إلى مرحلة الـ Staging
ونستطيع أن نقوم بعمل commit
جديد في الـ Local Repository
يضم التعديلات التي قمنا بها
عن طريق git commit
لكن نحن نريد ان نتعلم كيف نتراجع عن هذا التعديل لذا Git
يساعدنا ويخبرنا كيف نفعل هذا
يقول لنا إن أردنا إخراج الملف فقط من مرحلة الـ Staging
دون ان نتراجع عن التعديلات التي في الملف نستعمل git restore --staged article_1.txt
> git restore --staged article_1.txt
هكذا سيقوم Git
بالذهاب إلى الـ HEAD
المتواجد في الـ Local Repository
واحضار نسخة الملف article_1.txt
ويستبدلها بالملف الذي في الـ Staging
بالتالي نسخة الملف التي في الـ Staging
ستكون مطابقة لنسخة الملف التي في الـ HEAD
بالتالي كأننا أخرجناه من حالة الـ Staging
لكن التعديلات التي في الملف مازالت موجودة
بمعنى أن نسخة الملف في الـ Working Directory
مختلفة عن نسختها التي في الـ HEAD
وتذكر أن الـ HEAD
يشير إلى آخر commit
قمنا بعمله والذي يضم آخر التعديلات التي قمنا بتسجيلها داخل الـ Local Repository
يمكننا عمل git status
ونتأكد
> git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: article_1.txt
no changes added to commit (use "git add" and/or "git commit -a")
أنظر رجعنا لحالتنا الأولى قبل قيامنا بعمل git add article_1.txt
بمعنى أن التعديلات التي في الملف مازالت موجودة في الـ Working Directory
لكن نحن فقط أخرجنا الملف من مرحلة الـ Staging
لذا نستطيع أن نقول أن git restore --staged
هو الأمر الذي يعكس ما يقوم به الأمر git add
ملحوظة
: تذكرgit restore
يقوم باسترجاع الملف من الـStaging
إلىWorking Directory
أماgit restore --staged
يقوم باسترجاع الملف من الـHEAD
المتواجد فيLocal Repository
إلىStaging
قبل أن نكمل لنقم بعمل git add
و commit
للملف article_1.txt
> git add article_1.txt
> git commit -m "edit article 1"
[main 6dc050b] edit article 1
1 file changed, 1 insertion(+)
git rm file-name
نستخدم git rm
عندما نريد حذف ملف من المشروع كليًا أي من الـ Working Directory
ومن الـ Staging
وما يفعله أنه يحذف الملف من الـ Working Directory
ثم يقوم بعمل git add
تلقائيًا للملف المحذوف دون أن تشعر
والـ git add
غرضها هنا وضع الملف المحذوف في مرحلة الـ Staging
لتأكيد على حذفه في الـ commit
جديد
بالتالي سيتم إنشاء نسخة جديدة من المشروع يكون الملف محذوفًا فيه
ملحوظة
: يمكننا القول أنgit rm
هو في الحقيقة مزيج بينrm
ثمgit add
وبما أنها تحذف الملف من الـ Staging
هكذا تم تحويل الملف إلى حالة Untracked
بالتالي Git
لن يتعرف عليه
لانه لم يعد Tracked File
بالنسبة له
لكن في هذه الحالة الأمر لن يكون مهمًا لأن الملف لم يعد موجودًا في الـ Working Directory
من الأساس
لنأخذ مثال عملي على ما قلناه
لنقم بإنشاء ملف جديد ثم نقوم بعمل git add
و commit
له
ثم نحاول حذفه من المشروع
> touch article_xyz.txt
> git add article_xyz.txt
> git commit -m "add xyz article"
[main c2d77c1] add xyz article
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 article_xyz.txt
> ls
article_1.txt article_xyz.txt
الآن أصبح لدينا مقالة جديدة تدعى article_xyz.txt
لكن للأسف الشديد هذه المقالة تم إنشاءها عن طريق الخطأ ونريد أن نحذفها
لذا سنستعمل الأمر الجميل git rm
> git rm article_xyz.txt
rm 'article_xyz.txt'
الآن هنا حصل أمرين:
- تم حذف الملف من الـ
Working Directory
- تم وضع الملف المحذوف في مرحلة الـ
Staging
للتجهيزه لعملcommit
جديد
لنقوم بعمل git status
للتحقق من ما قلناه
> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: article_xyz.txt
لاحظ أنه يقول لك Changes to be committed
أي أن هذه التعديلات التي في الـ Staging
جاهزة لعمل commit
وأهم شيء ستلاحظ أن الملف article_xyz.txt
يتم الإشارة إليه بأنه محذوف بمعنى انه سيتم حذفه في النسخة التي سينشئها الـ commit
لاحظ أنه أيضًا يقول لك بكيفية التراجع عن الحذف عن طريق git restore --staged article_xyz.txt
وهذا الأمر كما أوضحنا سيتم إرجاع نسخة الملف من الـ HEAD
إلى الـ Staging
هكذا سنستعيد الملف في الـ Staging
وسيصبح Tracked
كما كان
لكن الملف محذوف أيضًا من الـ Working Directory
لذا لكي تسترجعه بشكل نهائي ستحتاج لتنفيذ git restore article_xyz.txt
وهكذا سيتبدل الملف المحذوف من الـ Staging
ألى الـ Working Directory
وهكذا سنستعيد الملف ويعود المشروع إلى حالته الاصلية
يمكنك أختبار طريقة ارجاع الملف المحذوف بنفسك
نحن لا نريد أن نسترجع الملف المحذوف لذا سنقوم بعمل الـ commit
ليتم إنشاء نسخة من المشروع يكون الملف محذوفًا فيه
> git commit -m "delete xyz article"
[main 937637d] delete xyz article
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 article_xyz.txt
git rm --cached file-name
هذا هو نفس الأمر السابق لكن مع زيادة --cached
وهو في الحقيقة يشبه الأمر السابق لكنه لا يقوم بحذف الملف من الـ Working Directory
بل يكتفي بأن يحذفه فقط من الـ Staging
وطالما أن الملف حذف من الـ Staging
فقط فسيتم تحويل الملف إلى Untracked
بالتالي Git
لن يتعرف عليه لانه لم يعد Tracked File
بالنسبة للـ Git
وتذكر أن الملف لم يحذف من الـ Working Directory
بل حذف فقط من الـ Staging
سنعطي نفس المثال السابق بأننا سننشيء ملف ما ثم نحاول حذف
> touch article_xyz_again.txt
> git add article_xyz_again.txt
> git commit -m "add xyz article again"
[main cbbc411] add xyz article again
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 article_xyz_again.txt
الآن أصبح لدينا مقالة جديدة تدعى article_xyz_again.txt
ونريد أن نحذفها عن طريق الأمر git rm --cached
> git rm --cached article_xyz_again.txt
rm 'article_xyz_again.txt'
الآن ما حصل هنا هو مثل ما حصل الأمر السابق لكن مع اختلاف:
أن الملف حذف فقط من الـ Staging
وليس من الـ Working Directory
وبالتطبع تم وضع الملف المحذوف في مرحلة الـ Staging
للتجهيزه لعمل commit
جديد
لنقوم بعمل git status
للتحقق من ما قلناه مجددًا
> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: article_xyz_again.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
article_xyz_again.txt
حسنًا أريدك أن تركز جيدًا في ما سأشرحه الآن
الأمر git status
يخبرنا بمعلومتين مهمتين
الأولى أن الملف الذي حذفناه تم وضعه في مرحلة الـ Staging
مع الإشارة إليه بأنه محذوف بمعنى انه سيتم حذفه في النسخة التي سينشئها الـ commit
الثانية أن نفس الملف موجود بالفعل في الـ Working Directory
لكنه Untracked
أي أن Git
ينظر اليه كملف جديد لا يعرف عنه شيء
وهذا بسبب أن الأمر git rm --cached
لا يحذف الملف من الـ Working Directory
بل يحذفه فقط الـ Staging
وطالما أن الملف حذف من الـ Staging
فبالتبعية سيتم تحويل حالة الملف من Tracking
إلى Untracking
الآن لدينا شيء مميز هنا وأريدك أن تركز معي، إذا أردنا التراجع عن الحذف ما الذي سنفعله ؟
ستقول لي هذا سهل، فقط سنستخدم git restore --staged article_xyz_again.txt
لنخرجه من حالة الـ Staging
كما تعلمنا
صحيح، لكن ماذا سيحدث عندما نقوم بعمل git add article_xyz_again.txt
؟
سيتم وضع الملف article_xyz_again.txt
في مرحلة الـ Staging
وجعله Tracked
حسنًا وثم ؟ فكر وركز هنا جيدًا، ستتفاجيء ان الملف لم يعد مطالب حذفه
والمشروع عاد كمان كان إلي حالته الأصلية
بمعنى أن تنفيذ الأمر git add article_xyz_again.txt
كان مساويًا تماما للأمر it restore --staged article_xyz_again.txt
في هذه الحالة بالتحديد
هل تستطيع التفكير بالسبب ؟
السبب بسيط وهو أن الأمر git rm --cached
، حذف الملف من الـ Staging
فقط لا غير وأبقى الملف كما هو في الـ Working Directory
وهذا الملف الآن الموجود في الـ Working Directory
مازال مساويًا للنسخة الموجودة في الـ HEAD
داخل الـ Local Repository
كل ما في الأمر أنه ليس في الـ Staging
!
لذالك عندما نستعمل git add
سيتم إضافة الملف من الـ Working Directory
إلى الـ Staging
وعندما نستعمل git restore --staged
يتم نقل الملف من الـ HEAD
إلى الـ Staging
إذًا في كلتا الحالتين ستكون نسخة الملف في الـ Staging
متساوية مع النسخة التي في الـ Working Directory
ومتساوية مع النسخة الموجودة في الـ HEAD
المتواجد داخل الـ Local Repository
هكذا طالما أن كل النسخ متساوية، فكل شيء عاد كما كان قبل حذف الملف
والملف سيرجع Tracked
كمان كان لأنه في الـ Staging
لذا النتيجة ستكون ذاتها بأن اصبحت كل النسخ متساوية
أرجوا أن تكون الفكرة وصلت الأمر يحتاج فقط لفهم طبيعة عمل كلا الأمرين git add
وgit restore --staged
لنعود للأمر git status
> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: article_xyz_again.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
article_xyz_again.txt
ونقول أننا نريد عمل commit
لإنشاء نسخة من المشروع يكون الملف محذوفً فيه
> git add article_xyz_again.txt
> git commit -m "delete xyz article again"
[main be7a504] delete xyz article again
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 article_xyz_again.txt
الآن تم إنشاء نسخة جديدة لا يتواجد فيها الملف article_xyz_again.txt
لكن تذكر أن ملف لازال موجودًا في الـ Working Directory
وحالته Untracked
لنتأكد من هذا عن طريق git status
> git status
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
article_xyz_again.txt
nothing added to commit but untracked files present (use "git add" to track)
يمكنك أن تمسحه يدويًا الآن إن أردت أو أن تحتفظ به أو تضيف مجددًا، فلا يهم افعل ما تريده
أنا سأقوم بحذفه يدويًا لأنني لن احتاجه مجددًا
> rm article_xyz_again.txt
> ls
article_1.txt
git log
نستخدم git log
عندما نرغب في مشاهدة تاريخ المشروع بالكامل وكل التعديلات التي تمت عليه من بداية المشروع حتى اللحظة الحالية
وكذلك لمعرفة من قام بإجراء التعديل ومتى قام بها
لذا عندما تريد رؤية جميع التغييرات والـ commit
التي قمت بها في المشروع، فقط بضغطة زر تنفذ هذا الأمر البسيط git log
> git log
commit be7a50440aa044d331741b5af4758e23a190760d (HEAD -> main)
Author: AhmedEl-Tabarani <[email protected]>
Date: Fri Apr 12 03:17:02 2024 +0200
delete xyz article again
commit cbbc4119e47737ea5c96cd441b7bc69f51b16f1b
Author: AhmedEl-Tabarani <[email protected]>
Date: Fri Apr 12 01:51:45 2024 +0200
add xyz article again
commit 937637d020fbc80114f291c841d2f17f88f6178a
Author: AhmedEl-Tabarani <[email protected]>
Date: Fri Apr 12 01:02:54 2024 +0200
delete xyz article
commit c2d77c1cd3b94208816aefa916c8feb7f933f8ec
Author: AhmedEl-Tabarani <[email protected]>
Date: Thu Apr 11 23:55:25 2024 +0200
add xyz article
commit 6dc050b8d781d0247277fda29e8c330cd8b7f689
Author: AhmedEl-Tabarani <[email protected]>
Date: Thu Apr 11 05:50:45 2024 +0200
edit article 1
commit 4032898bcaf1dd2b5fbbaac2f15840e93097955f
Author: AhmedEl-Tabarani <[email protected]>
Date: Thu Apr 11 03:25:33 2024 +0200
add new article to the blog
وسيتم عرض لك جميع الـ commit
التي كانت تخزن في الـ Local Repository
بالإضافة إلى عدة أمور منها الـ hash
الخاص بالـ commit
واسم الشخص الذي قام بالتعديل والوقت الذي تم فيه التعديل والرسالة التي كتبها مع الـ commit
فكما ترى لدينا العديد من الـ commit
التي أنشأناها أثناء الشرح
لاختصار كمية المعلومات يمكننا ان ننفذ الأمر هكذا git log --oneline
> git log --oneline
be7a504 (HEAD -> main) delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article
6dc050b edit article 1
4032898 add new article to the blog
وكما ترى فأنه مفيد جدًا لرؤية آخر الـ commit
التي اجريناها في المشروع
قد تجدني استخدم بعض الخيارات الاضافية معه كهذا git log --all --decorate --oneline --graph
git revert commit-hash
نستخدم git revert
عندما نريد التراجع عن commit
معين
وما يقوم به أنه ينشيء commit
جديد يعكس ويتراجع عن التعديلات التي كانت في الـ commit
المعين الذي حددناه
بمعنى أنه يقوم بعكس التغيرات التي كانت في الـ commit
الذي نريد التراجع عنه
بالتالي ما أُضيف سيُحذف وما حُذف سيعاد اضافته مجددًا
ثم سيتم تطبيق التغيرات فورًا في commit
جديد دون وضعها في الـ Staging
بل سيتم إضافة هذا الـ commit
الجديد إلى الـ Local Repository
مباشرةً
لنتخيل أننا نريد أن نتراجع عن آخر commit
> git log --oneline
be7a504 (HEAD -> main) delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article
6dc050b edit article 1
4032898 add new article to the blog
وآخر commit
كان عندما حذفنا الملف article_xyz_again.txt
لذا عندما نقوم بعمل الأمر git revert
على آخر commit
سيتم عكس الـ commit
وسيتم ارجاع الملف article_xyz_again.txt
مجددًا
> git revert be7a504
[main cd9c14a] Revert "delete xyz article again"
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 article_xyz_again.txt
الآن إذا نظرنا إلى الملفات ستجد أن الملف article_xyz_again.txt
الذي حذفناه عاد إلينا
> ls
article_1.txt article_xyz_again.txt
ودعونا ننفذ الأمر git log --oneline
لرؤية الـ commit
التي قمنا بعملها
> git log --oneline
cd9c14a (HEAD -> main) Revert "delete xyz article again"
be7a504 delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article
6dc050b edit article 1
4032898 add new article to the blog
لاحظ أنه تم إنشاء commit
جديد ليعكس ويتراجع عن التعديلات التي كانت في الـ commit
السابق
git reflog
الـ git reflog
يشبه شقيقه git log
لكنه متخصص في تتبع كل حركات الـ HEAD
وكيف تحرك
ويستطيع تتبع عدة امور منها commit
، revert
، reset
و checkout
وغيرها من الأمور وحتى وان حذفت commit
يتم تسجيلها في الـ reflog
> git reflog
2d4a2f8 (HEAD -> main) HEAD@{0}: revert: Revert "delete xyz article again"
be7a504 HEAD@{1}: commit: delete xyz article again
cbbc411 HEAD@{2}: commit: add xyz article again
937637d HEAD@{3}: commit: delete xyz article
c2d77c1 HEAD@{4}: commit: add xyz article
6dc050b HEAD@{5}: commit: edit article 1
4032898 HEAD@{6}: commit (initial): add new article to the blog
سترى أنه يتم تتبع كل حركات الـ HEAD
هنا وسترى يسجل كل الـ commit
حتى الـ revert
سجله
وفيما سنتعرف على الـ reset
وسنرى انه يسجله أيضًا هنا
وسترى كيف سيساعدنا reflog
عندما نريد تحريك الـ HEAD
وتتبع خطواته
لكن لحظة واحدة ما هو الـ HEAD
؟
الـ HEAD الكبير
لنتكلم الآن عن الـ HEAD
لأننا اصبحنا نراه ونتكلم عليه كثيرًا ولا نعرف ما هو بالتحديد
الـ HEAD
هو مؤشر يشاور على commit
ما وغالبا ما يؤشر على آخر commit
حصل في الـ branch
لكن يمكننا تغير مكانه وجعله يؤشر على commit
قديم وهكذا وسنرى ذلك لاحقًا
مكان الـ HEAD
يعكس محتوى الملفات التي في الـ Working Directory
بمعنى إذا غيرنا مكان الـ HEAD
وجعلناه يشاور على commit
قديم
فإن محتوى الـ Working Directory
ستكون نفس حالة المشروع التي كان عليها في هذا الـ commit
القديم
بمعنى أن الـ HEAD
هو جهاز السفر عبر الزمن الذي تحدثنا عنه ويستطيع السفر إلى أي commit
ليرى كيف كان شكل المشروع في هذا الـ commit
فيستطيع جلب التعديلات التي كانت في commit
معين ويطبقها مباشرةً في الـ Working Directory
أويجعلها في الـ Staging
لرؤيتها ونراجعها قبل أن ننقلها للـ Working Directory
إن اردنا
كيف نغير الـ commit
الذي يشاور عليها الـ HEAD
؟
يوجد العديد من الأوامر التي نستطيع أن نستخدمها لتحريك الـ HEAD
ومن ضمنها git reset
, git checkout
, git switch
git reset commit-hash
نستطيع أن نغير مكان الـ HEAD
عن طريق أمر الـ git reset
وهو يأخذ الـ commit
الذي تريد أن تنقل الـ HEAD
إليه
هناك ثلاث خيارات مهمة في الـ git reset
وهم --soft
, --mixed
, --hard
ويتشاركوا في:
- جميعهم يحركوا الـ
branch
والـHEAD
في آن واحد وليس فقط الـHEAD
إلى الـcommit
الذي تم تحديده - عندما لا يتم تحديد
commit
يتم تحديد الـHEAD
كقيمة افتراضية - يمكن التراجع عن الأمر بالاستعانة بـ
reflog
وreset
للرجوع للـcommit
الأصلي الذي كان عليه الـHEAD
من قبل
git reset --soft commit-hash
الأمر git reset --soft
يحرك الـ HEAD
إلى commit
المعين
- لا يقوم بتعديل الـ
Working Directory
على الاطلاق بل يظل كما هو قبل الـgit reset
- لا يفعل أي شيء مع الملفات التي في مرحلة الـ
Staging
بل تظل كما هي في الـStaging
- التعديلات التي حدثت ما بين الـ
HEAD
وcommit
يتم نقلها إلى الـStaging
- إذا كان هناك تعديلات كنا نعمل عليها ولم تنقل إلى الـ
Staging
فسيتم نقلها تلقائيًا (كأنك قمت بعملgit add .
)
لنتخيل أننا قمنا بالعديد من الاضافات والتعديل وهذا هو شكل الـ git log
الخاص بنا الآن
> git log --all --oneline
290f0eb (HEAD -> main) add article 3 # new commit
9ebb273 add article 2 # new commit
0035199 edit article 1 # new commit
be7a504 delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article # <--- we will reset soft the HEAD to this commit here
6dc050b edit article 1
4032898 add new article to the blog
هنا قمنا بالتعديل على المقالة الاولى وقمنا بإضافة مقالات جديد المقالة الثانية والثالثة
> ls
article_1.txt article_2.txt article_3.txt
ثم قمنا بعمل git reset --soft c2d77c1
الآن ما حصل أن الـ Working Directory
لم يحدث له أي شيء
> ls
article_1.txt article_2.txt article_3.txt
نحن فقط ذهبنا إلى الـ commit
الـ c2d77c1
ونستطيع أن نتأكد من موقع الـ HEAD
عن طريق git log --oneline
> git log --all --oneline
c2d77c1 (HEAD -> main) add xyz article # <--- now the HEAD is here
6dc050b edit article 1
4032898 add new article to the blog
لترى أن HEAD
أصبح يشاور بالفعل على الـ commit
الذي حددناه
لكن ستلاحظ أن كل الـ commit
التي كانت بعده اختفت، لكنها لم تختفي تمامًا لا تقلق نستطيع الرجوع لها عن طريق الـ reflog
وسنرى ذلك لاحقًا
وعلى أي حال كل التعديلات التي كانت ما بين موقع الـ HEAD
السابق والموقع الجديد في الـ commit
تم نقلها إلى الـ Staging
ويمكننا التأكد من هذا عن طريق الأمر git status
> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: article_1.txt
new file: article_2.txt
new file: article_3.txt
deleted: article_xyz.txt
كما ترى برغم من أننا انتقلنا إلى commit
قديم إلا أنه احتفظ بالملفات والتعديلات التي كانت موجودة لدينا في الـ Working Directory
قبل ان نتنقل للـ commit
القديم
وقام بوضعهم في مرحلة الـ Staging
كما لو أنه قام بتنفيذ git add .
بعد أن قام بعمل git reset --soft
نحن كأننا رجعنا بالماضي باستخدام git reset
حسنًا لنقل أننا نريد ان نعود للـ commit
الأساسي الذي كنا فيه قبل قيامنا بتنفيذ الـ reset
بمعنى أننا نريد أن نرجع مجددًا إلى المستقبل حيث كنا ما الذي سنفعله ؟
نستطيع أن نستخدم git reflog
لنتتبع أين كان الـ HEAD
وكيف تحرك
> git reflog
c2d77c1 (HEAD -> main) HEAD@{0}: reset: moving to c2d77c1
290f0eb HEAD@{1}: commit: add article 3 # <--- we was here
9ebb273 HEAD@{2}: commit: add article 2
0035199 HEAD@{3}: commit: edit article 1
be7a504 HEAD@{4}: commit: delete xyz article again
cbbc411 HEAD@{5}: commit: add xyz article again
937637d HEAD@{6}: commit: delete xyz article
c2d77c1 (HEAD -> main) HEAD@{7}: commit: add xyz article
6dc050b HEAD@{8}: commit: edit article 1
4032898 HEAD@{9}: commit (initial): add new article to the blog
سترى أنه يتم تتبع كل حركات الـ HEAD
نحن فقط الـ commit
الذي كما فيه سابقًا قبل الـ reset
وهو كما نلاحظ الخطوة المشار إليها بـ HEAD@{1}
لذا يمكننا استخدام reset
مجددًا وتحريك الـ HEAD
إلى الأمام إلى المستقبل حيث كنا تحديدا إلى HEAD@{1}
> git reset HEAD@{1}
ثم سنرى أننا عدنا الى ما كنا عليه
> git log --all --oneline
290f0eb (HEAD -> main) add article 3
9ebb273 add article 2
0035199 edit article 1
be7a504 delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article
6dc050b edit article 1
4032898 add new article to the blog
وأيضًا تم تسجيل كل تحركات الـ HEAD
هنا في reflog
تم تسجيل ذهابه إلى commit
قديم ثم رجوعه مجددًا
> git reflog
290f0eb (HEAD -> main) HEAD@{0}: reset: moving to HEAD@{1}
c2d77c1 HEAD@{1}: reset: moving to c2d77c1
290f0eb (HEAD -> main) HEAD@{2}: commit: add article 3
9ebb273 HEAD@{3}: commit: add article 2
0035199 HEAD@{4}: commit: edit article 1
be7a504 HEAD@{5}: commit: delete xyz article again
cbbc411 HEAD@{6}: commit: add xyz article again
937637d HEAD@{7}: commit: delete xyz article
c2d77c1 HEAD@{8}: commit: add xyz article
6dc050b HEAD@{9}: commit: edit article 1
4032898 HEAD@{10}: commit (initial): add new article to the blog
git reset --mixed commit-hash
الأمر git reset --mixed
يقوم بكل الأشياء التي تحدثنا عنا سابقًا مع --soft
الفرق أنه لا يضع التعديلات في الـ Staged
- لا يقوم بتعديل الـ
Working Directory
على الاطلاق بل يظل كما هو قبل الـreset
- كل الملفات التي في مرحلة الـ
Staging
تخرج من مرحلة الـStaging
وتنتقل إلى الـWorking Directory
(كأنك قمت بعملgit restore --staged .
) - التعديلات التي حدثت ما بين الـ
HEAD
وcommit
لا يتم نقلها إلى الـStaging
بل تظل كما هي في الـWorking Directory
- الـ
--mixed
هو القيمة الافتراضية للـgit reset
يمكننا أن نقول أن --mixed
تساوي --soft
لكن بدون عمل git add
، ونقل كل التعديلات من الـ Staging
إلى الـ Working Directory
لنرى مثالًا عمليًا على هذا وسنرجع لشكل الـ git log
السابق
> git log --all --oneline
290f0eb (HEAD -> main) add article 3
9ebb273 add article 2
0035199 edit article 1
be7a504 delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article # <--- we will reset mixed the HEAD to this commit here
6dc050b edit article 1
4032898 add new article to the blog
لنرى الملفات
> ls
article_1.txt article_2.txt article_3.txt
الآن نقوم بعمل بعمل git reset --mixed c2d77c1
أو git reset c2d77c1
لأن --mixed
هي القيمة الافتراضية كما قلنا
> git reset --mixed c2d77c1
Unstaged changes after reset:
M article_1.txt
D article_xyz.txt
الآن ما حصل هو تمامًا ما حصل مع --soft
:
- الـ
Working Directory
لم يحدث له أي شيء - الـ
HEAD
أصبح يشاور بالفعل على الـcommit
الذي حددناه - وأن كل الـ
commit
التي كانت ما بين موقع الـHEAD
السابق والموقع الجديد في الـcommit
الذي حددناه اختفت - يمكننا استرجاعها عن طريق الـ
reflog
بالنفس الطريق التي فعلناها مع--soft
لكن الفرق ما بين --soft
و--mixed
هو أن في --mixed
كل التعديلات التي كانت ما بين موقع الـ HEAD
السابق والموقع الجديد في الـ commit
لا تنقل إلى الـ Staging
كما كان يحصل مع --soft
بل ان التعديلات تظل في الـ Working Directory
وأي شيء داخل مرحلة الـ Staging
يتم سحبه منها واخراجه إلى الـ Working Directory
ويمكننا التأكد من هذا عن طريق الأمر git status
> git status
On branch main
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: article_1.txt
deleted: article_xyz.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
article_2.txt
article_3.txt
كما ترى الملفات article_2.txt
وarticle_3.txt
لم يتم حذفهما بل بقيا كما هما في الـ Working Directory
لكن أصبحا Untracked
ولم يتم إضافتها للـ Staging
كما كان الأمر مع --soft
والتعديلات الخاصة بالملف article_1.txt
واجراء الحذف الخاص بالملف article_xyz.txt
بقيا كما هما في الـ Working Directory
ولكن لم يتم إضافتها للـ Staging
كما كان الأمر مع --soft
ملحوظة
: ما تراه في الـgit status
بعد قيامنا بتنفيذ--mixed
إذا قمنا بعملgit add .
فسنحصل على نفسgit status
إذا نفذنا--soft
لنتأكد من هذا نحن فقط سنقوم بعمل git add.
ونرى أن الأمر git status
تغير واصبح مطابق مع ما نراه مع --soft
> git add .
> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: article_1.txt
new file: article_2.txt
new file: article_3.txt
deleted: article_xyz.txt
git reset --hard commit-hash
الـ --hard
خطير ويجب الحذر منه لأنه:
- يحرك الـ
HEAD
إلى الـcommit
وينقل التعديلات إلى الـWorking Directory
مباشرةً - يتخلص من أي تعديلات كانت في الـ
Working Directory
دون رجعة (أي أنه يتخلص من الـModified Files
) - يتخلص من أي تعديلات كانت في مرحلة الـ
Staging
دون رجعة
ملحوظة
: أحذر من استخدام--hard
فهذا سيعدل الملفات المتواجدة في الـWorking Directory
وسيجعلها مطابقة للـHEAD
أو للـcommit
الذي نحدده بالتالي سيتخلص من أيModified Files
وكل التعديلات التي تقوم بها حاليًا في الـWorking Directory
وفي الـStaging
ستختفي دون رجعة
لذا قبل أن تستخدم git reset --hard
ويوجد ملفات يتم التعديل عليها حاليًا في الـ Working Directory
فيجب أن تنقل هذه التعديلات في مكان آخر قبل أن تقوم بعمل git reset --hard
وهنا يأتي دور الأمر git stash
، والذي نتكلم عنه بالتفصيل لاحقًا في هذه المقالة
لنرى مثالًا عمليًا على هذا وسنرجع لشكل الـ git log
السابق
> git log --all --oneline
290f0eb (HEAD -> main) add article 3
9ebb273 add article 2
0035199 edit article 1
be7a504 delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article # <--- we will reset hard the HEAD to this commit here
6dc050b edit article 1
4032898 add new article to the blog
لنرى الملفات
> ls
article_1.txt article_2.txt article_3.txt
الآن نقوم بعمل بعمل git reset --hard c2d77c1
> git reset --hard c2d77c1
HEAD is now at c2d77c1 add xyz article
لنرى الملفات
> ls
article_1.txt article_xyz.txt
لنرى git status
>git status
On branch main
nothing to commit, working tree clean
ما الذي حصل ؟
المشروع عاد كما كان في هذا الـ commit
وهو c2d77c1
وكل التعديلات التي قمنا بها بعد هذا الـ commit
تم التخلص منها
ولم يتم نقلها إلى الـ Staging
كما كان يحصل
واذا كنت تملك أي ملفات يتم التعديل عليها في الـ Working Directory
أو كانت في مرحلة الـ Staging
ولم يتم إنشاء أي commit
أو عمل لها stash
فهذه التعديلات تم التخلص منها نهائيًا دون رجعة
بالطبع يمكننا استرجاع أي commit
باستخدام الأمر git reflog
جدول لتلخيص الفروق بين soft و mixed و hard في git reset
الاختيار | الملفات التي في الـ Working Directory | التعديلات التي في مرحلة الـ Staging | التعديلات التي في الـ Working Directory ولم تُنقل إلى مرحلة الـ Staging بعد | التعديلات التي حدثت ما بين الـ HEAD والـ commit المحدد |
---|---|---|---|---|
soft | تظل كما هي | تظل كما هي | تُنقل إلى مرحلة الـ Staging |
تُنقل إلى مرحلة الـ Staging |
mixed | تظل كما هي | تُنقل إلى الـ Working Directory |
تظل كما هي في الـ Working Directory |
تظل كما هي في الـ Working Directory |
hard | يتم استبدالها اجباريًا لجعل الـ Working Directory يطابق النسخة التي كان عليها في الـ commit المحدد |
يتم التخلص منها | يتم التخلص منها | يتم التخلص منها |
git checkout commit-hash
الـ git checkout
لها عدة استخدامات منها:
- احضار ملف ما في
commit
معين إلىHEAD
ووضعه في الـStaging
- لتغير مكان الـ
HEAD
إلىcommit
محدد كما كان يفعلgit reset
مع بعض الاختلافات - التنقل بين الـ
branch
, لكن يفضل استخدامgit switch
لهذا الغرض
استعادة ملف من commit معين
لنرى بعض الأمثلة عليه أولًا لنرى الـ git log
المعتاد
> git log --all --oneline
290f0eb (HEAD -> main) add article 3
9ebb273 add article 2
0035199 edit article 1
be7a504 delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article
6dc050b edit article 1
4032898 add new article to the blog
والملفات كما هي
> ls
article_1.txt article_2.txt article_3.txt
الآن سنحاول احضار الملف article_xyz.txt
الذي كانت في الـ commit
الـ c2d77c1
> git checkout c2d77c1 article_xyz.txt
Updated 1 path from 7847fb1
الملف article_xyz.txt
الذي كان في الـ commit
الـ c2d77c1
أصبح في مرحلة الـ Staging
ويمكننا التأكد من هذا عن طريق git status
> git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: article_xyz.txt
نقل الـ HEAD إلى commit محدد (detached HEAD)
يختلف الـ git checkout
عن الأمر git reset
في طريقة تغير مكان الـ HEAD
إلى الـ commit
المحدد
الـ git reset
كان يغير مكان الـ HEAD
والـ Branch
في آن واحد إلى الـ commit
المحدد
وكان يخفي ويتخلص من كل الـ commit
التي كانت ما بين مكان الـ HEAD
والـ commit
المحدد
وكنا نستخدم طريقة ملتوية وهي الاستعانة بالأمر reflog
لكي نستطيع أن نرجع الـ commit
التي فقدت
أما مع git checkout
فالأمور مختلفة قليلًا
- يتم نقل الـ
HEAD
فقط إلى الـcommit
المحدد ولا يتم تغير موقع الـBranch
- التغيرات يتم تطبيقها فورًا في الـ
Working Directory
- إذا كنت تملك ملفات تقوم بالتعديل عليها في الـ
Working Directory
أو في الـStaging
فلن تستطيع تنفيذ الـgit checkout
والـGit
سينبهك بأنك لا يمكنك تحريك الـHEAD
وهناك ملفات يتم التعديل عليها
طالما أن الـcommit
الحالي ما بين مكان الـHEAD
مختلفين
لنرى مثالًا عمليًا، لدينا الـ git log
المعتاد
> git log --all --oneline
290f0eb (HEAD -> main) add article 3
9ebb273 add article 2
0035199 edit article 1
be7a504 delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 add xyz article
6dc050b edit article 1
4032898 add new article to the blog
والملفات كما هي
> ls
article_1.txt article_2.txt article_3.txt
الآن سنجعل الـ HEAD
يذهب إلى الـ commit
الـ c2d77c1
الأمر أشبه بقيامنا بعمل git reset --hard
لكن إليك الفروقات هنا
> git checkout c2d77c1
Note: switching to 'c2d77c1'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at c2d77c1 add xyz article
حسنًا سترى أنه كما تعودنا من الـ Git
أنه دائمًا ما يزودنا بالمعلومات وينصحنا ويشرح لنا ما الذي يجري بالتحديد
هنا هو يقول لك انتبه أن الـ HEAD
اصبح يشاور على commit
مجهول أي ليس هناك اي branch
عليه
وهذه الحالة تسمى detached HEAD
لكن لا تقلق ليس هناك شيء تخافه
هو فقط يقول لك أنك يمكنك فعل أي شيء في هذا الـ commit
لكن انتبه اذا كنت تريد عمل تعديلات ثم تريد عمل commit
جديد يضم هذه التعديلات فيُنصح أن تنشيء branch
في مكان الـ HEAD
ليضم التغيرات والـ commit
الجديد الذي ستنشئها
لانك ببساطة جعلت الـ HEAD
يذهب إلى commit
مجهول بالتالي اذا تريد عملت commit
جديد ثم حاولت ان تغير مكان الـ HEAD
هكذا لن يكون هناك أي شيء يشير إلى الـ commit
الذي أنشأته للتو بالتالي ستفقده
لذا عندما تدخل في حالة الـ detached HEAD
وتريد تعديل بعض الأمور وعمل commit
جديد
فيفضل أن تنشيء branch
جديد سواء عن طريق git switch -c branch-name
أو git checkout -b branch-name
لنرى الـ git log
> git log --all --oneline
290f0eb (main) add article 3
9ebb273 add article 2
0035199 edit article 1
be7a504 delete xyz article again
cbbc411 add xyz article again
937637d delete xyz article
c2d77c1 (HEAD) add xyz article
6dc050b edit article 1
4032898 add new article to the blog
لاحظ كيف أن الـ HEAD
يشاور على commit
الـ c2d77c1
والـ branch
المسمى main
كما هو لم يتغير مكانه
لذا يمكننا جعل الـ HEAD
يرجع لاصله بسهوله عن طريق git checkout main
أو git switch main
> git checkout main
Previous HEAD position was c2d77c1 add xyz article
Switched to branch 'main'
هكذا عاد الـ HEAD
لمكانه دون مشاكل ودون الاستعانة بالـ reflog
ما هو الـ Stash ؟
كما ذكرنا سابقًا فإن الـ Stash
هو مكان أشبه بمخزن يستخدم لحفظ التعديلات التي تقوم بها حاليًا بشكل مؤقت ثم يمكنك العمل على شيء آخر أو أن تنتقل إلى commit
أو branch
آخر كما كنا نفعل سابقًا
ثم بعد انتهائك يمكنك أن تعود وتستحضر التعديلات المحفوظة التي قمت بها من الـ Stash
تذكر عندما قلنا أن git reset --hard
يقوم بتعديل الملفات المتواجدة في الـ Working Directory
وسيجعلها مطابقة للـ HEAD
أو للـ commit
الذي نحدده
ويتخلص من أي Modified Files
وكل التعديلات التي تقوم بها حاليًا في الـ Working Directory
ستختفي دون رجعة
وقلنا إذا كان يوجد ملفات يتم التعديل عليها حاليًا في الـ Working Directory
ولا تريدها أن تختفي ولا تريد أن تضعها في الـ Staging
أو تقوم بعمل commit
لها حاليًا
فيمكنك هنا أن تخزن هذه التعديلات في الـ Stash
ثم تقوم بعمل git reset --hard
كما يحلوا لك
ثم يمكنك في أي لحظة سحب التعديلات التي وضعتها في الـ Stash
أهم أوامر الـ Stash
git stash
يستخدم لتخزين التعديلات التي لم تنقل بعد إلى الـ Staging
وتخزينها في الـ Stash
يمكنك استخدام -m
لكتابة رسالة توضيحية لتذكرك على ماذا كنت تعمل أو لماذا وضعتهم في الـ Stash
وهكذا
> git stash -m "test something related to feature xyz"
git stash list
يستخدم لعرض قائمة بكل شيء خزناه في الـ Stash
> git stash list
stash@{0}: On main: test something related to feature xyz
stash@{1}: On main: working on new article, should finish until weekend
git stash pop
يستخدم لاستعادة آخر stash
مُخزن ووضعه في الـ Working Directory
> git stash pop
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: feature_xyz.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (2cc2b989e15e8bab916fb20665eb5cc25216c9cf)
git stash apply stash-id
يستخدم لاستعادة stash
محدد ووضعه في الـ Working Directory
.
> git stash apply stash@{1}
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: new_article.txt
no changes added to commit (use "git add" and/or "git commit -a")
git stash drop stash-id
يستخدم لحذف stash محدد
> git stash drop stash@{0}
Dropped stash@{0} (b34271adfb27cb2e3ffc40546b570b4b9f37bfc4)
خاتمة
كما تلاحظ، الآن أصبح لديك معرفة جيدة عن الأوامر الأساسية للـ Git
وكيفية استخدامها
يجب أن تكون قادراً الآن على إدارة مشروعاتك باستخدام Git
وتنظيمها والقيام بالتعديلات والتحديثات بكل سهولة
بالطبع أنا لم اشرح جميع وظائف كل أمر، أنا فقط شرحت لك الفكرة والاستخدام العام لكل أمر
لكن اذا تعمقت ستجد ان كل أمر يحتوي على تفاصيل اعمق ودهاليز كثيرة
وأظن أنني اديت وظيفيتي في توصيل الهدف والاستخدام العام وهذا يكفي كبداية لتبدأ انت بجمع واستكشاف هذه الدهاليز والتفاصيل اثناء تقدمك وتعاملك
في مقالة التالية سنتكلم عن الـ Branch
في الـ Git
كيف
وأنها تعد من أهم المزايا التي يقدمها الـ Git
حيث أنها تساعدنا عن إنشاء عدة نسخ من المشروع، وكل نسخة تستطيع أن تطور فيها وتعدل وتفعل ما تريده دون أن تأثر على نسخة المشروع الأساسية
أو تقسم العمل ضمن الفريق حيث يمكن لكل عضو العمل على نسخته من الكود في فرع مختلف بشكل مستقل عن باقي الفريق
ويمكنكم قراءتها من هنا الـ Git Branch، آله السفر عبر الأبعاد
ثم في المقالة التالية سنتكلم الـ Remote Repository
وسنشرح مفاهيم وأوامر جديدة تتعلق بهذا العالم الآخر
ويمكنكم قراءتها من هنا الـ Git Remotes، وما علاقة الـ GitHub بالـ Git ؟