أساسيات الـ Git لتتبع التغييرات في المشاريع
السلام عليكم ورحمة الله وبركاته
المقدمة
اليوم لدينا مقالة دسمة إلى حد ما، سأتكلم في هذه المقالة عن الـ Git وكيف يعمل وكيف نستفيد منه ونستخدمه وما أهم الأوامر الخاصة به
وكيف يفيدنا في تنظيم مشاريعنا وتنظيم طريق العمل ضمن فريق
في هذه المقالة سأركز على الـ Git في بيئة عمل Local أي على جهازك الشخصي
ثم في مقالة أخرى سنتكلم عن مفهوم الـ Branch
ويمكنكم قراءتها من هنا Git Branches آلة السفر عبر الأبعاد
ثم في مقالة أخرى سنتطرق لجزء الـ Remote الخاص بالـ Git وكيف نتعامل مع GitHub وكل تلك الأمور إن شاء الله
يمكنكم قراءتها من هنا التعامل مع Remote Repository وموقع GitHub
ما هو الـ 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 "your@email.com" و git config --global user.name "your name"
لتسجيل اسمك وبريدك الإلكتروني في Git على مستوى جهازك الشخصي
أما اذا اردت على مستوى المشروع الحالي فيمكنك ازالة --global
> git config --global user.email "eltabaraniahmed@gmail.com"
> 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 <eltabaraniahmed@gmail.com>
Date: Fri Apr 12 03:17:02 2024 +0200
delete xyz article again
commit cbbc4119e47737ea5c96cd441b7bc69f51b16f1b
Author: AhmedEl-Tabarani <eltabaraniahmed@gmail.com>
Date: Fri Apr 12 01:51:45 2024 +0200
add xyz article again
commit 937637d020fbc80114f291c841d2f17f88f6178a
Author: AhmedEl-Tabarani <eltabaraniahmed@gmail.com>
Date: Fri Apr 12 01:02:54 2024 +0200
delete xyz article
commit c2d77c1cd3b94208816aefa916c8feb7f933f8ec
Author: AhmedEl-Tabarani <eltabaraniahmed@gmail.com>
Date: Thu Apr 11 23:55:25 2024 +0200
add xyz article
commit 6dc050b8d781d0247277fda29e8c330cd8b7f689
Author: AhmedEl-Tabarani <eltabaraniahmed@gmail.com>
Date: Thu Apr 11 05:50:45 2024 +0200
edit article 1
commit 4032898bcaf1dd2b5fbbaac2f15840e93097955f
Author: AhmedEl-Tabarani <eltabaraniahmed@gmail.com>
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 Branches آلة السفر عبر الأبعاد
ثم في المقالة التالية سنتكلم الـ Remote Repository وسنشرح مفاهيم وأوامر جديدة تتعلق بهذا العالم الآخر
ويمكنكم قراءتها من هنا التعامل مع Remote Repository وموقع GitHub