EduFlow
Git و GitHub
⁦0%⁩

الوحدة 1 · نموذج Git

قراءة السجلّ: الـ commits كرسم بياني

16 دقراءة 4 داختر بحكمة

الدرس في لمحة

اقرأ سجلّ الـ repository بـ git log و git show و git diff، وسمِّ أي commit نسبةً إلى الـ HEAD، وتخيّل السجلّ رسمًا بيانيًا من اللقطات المرتبطة بآبائها.

  1. 1العرض اليومي
  2. 2الـ commits تشكّل رسمًا بيانيًا
  3. 3تسمية الـ commits
  4. 4النظر داخل الـ commits
  5. 5تحقيق حقيقي

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

صار في موقعك الشخصي بضعة commits الآن. بعد ستة أشهر سيصبح فيه المئات، والسؤال الذي ستطرحه فعلًا لن يكون «ما هو السجلّ؟» بل شيئًا أدقّ: متى تعطّل الـ footer، وكيف كان هذا الملف قبل إعادة التصميم، وأيّ commit أضاف ذلك اللون. قراءة السجلّ جيدًا هي ما يجعلك تجيب عن هذه الأسئلة في ثوانٍ.

العرض اليومي

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

Terminal
git log --oneline --graph --all
Text
*   e5d81c0 (HEAD -> main) Merge branch 'dark-mode'
|\
| * d4a7f20 (dark-mode) Add dark mode colors
* | c39be11 Add projects page
|/
* b2e04f3 Add navigation bar with base styles
* a1c9d72 Add home page

اقرأه من الأسفل إلى الأعلى لتتبع الترتيب الزمني. كل * هي commit، والخطوط تبيّن أيّ commit جاء من أيّ commit. وبين الأقواس أسماء دالّة (labels): main و dark-mode فرعان، و HEAD -> main تعني أنك على main. الخيار --all مهم: دونه يعرض Git فقط الـ commits التي يمكن الوصول إليها من مكانك، ولن يبقى خط dark-mode ظاهرًا إلا لأنه دُمج.

الناتج الطويل يُفتح في أداة تصفّح (pager). استخدم الأسهم أو المسافة للتمرير و q للخروج.

مرشّحات مفيدة يمكن جمعها كلها: -n 5 (آخر خمسة)، و --stat (أيّ الملفات تغيّرت)، و -p (الـ diffs كاملة)، و --author="Maya"، و --since="2 weeks ago"، و -- index.html (فقط الـ commits التي لمست ذلك الملف).

الـ commits تشكّل رسمًا بيانيًا

الناتج أعلاه تمثيل لـ رسم بياني موجَّه غير دوري (directed acyclic graph)، أو DAG. موجَّه لأن كل commit يشير إلى أبيه. وغير دوري لأنك لا تستطيع تتبّع الآباء والعودة إلى حيث بدأت؛ فلا يمكن لـ commit أن يكون جدًّا لنفسه. معظم الـ commits لها أب واحد. الـ root commit لا أب له. والـ merge commit له أبوان.

الـ commitان a1c9 و b2e0 متتاليان، ثم يتفرّع الخط: c39b على خط main و d4a7 على خط dark-mode. كلاهما يقود إلى الـ merge commit ذي المعرّف e5d8، وله أبوان. الاسم main والـ HEAD يشيران إلى e5d8؛ والاسم dark-mode يشير إلى d4a7. الأسهم تتّجه من كل commit إلى أبيه. a1c9 b2e0 c39b d4a7 e5d8 main HEAD dark-mode
السجلّ نفسه كرسم بياني للـ commits

احتفظ بهذه الصورة في ذهنك. في القسم الثاني سترى أن الـ branch ليس أكثر من واحد من هذه الأسماء الدالّة، وأن كل أمر متعلّق بالفروع إما يحرّك اسمًا أو يضيف دائرة.

تسمية الـ commits

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

غالبًا ما يكون العدّ انطلاقًا من مكانك أسهل:

  • HEAD هو الـ commit الذي أنت عليه.
  • HEAD~1 (أو HEAD~) هو أبوه، و HEAD~3 هو ثلاثة أجيال إلى الخلف، مع تتبّع الأب الأول في كل مرة.
  • HEAD^2 هو الأب الثاني لـ merge commit؛ وفي e5d81c0 أعلاه هو d4a7f20.
  • اسم الفرع يصلح في أي مكان يصلح فيه الـ commit: dark-mode~1 هو b2e04f3.

النظر داخل الـ commits

Terminal
git show c39be11              # message, author, date and the diff it introduced
git show HEAD~2:styles.css    # the whole file as it was in that snapshot
git diff a1c9d72 HEAD         # everything that changed between two snapshots
git diff HEAD~3 HEAD -- styles.css   # the same, for one file only

يقبل git diff أي commitين لأن كلًا منهما لقطة كاملة. ويُحسب الـ diff عند الطلب بمقارنتهما. وكذلك الحال مع الـ diff الذي يطبعه git show: إنه لقطة هذا الـ commit مقارنةً بلقطة أبيه.

تحقيق حقيقي

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

Terminal
git log --oneline --since="1 week ago" -- projects.html
Text
8f1e2aa Move project cards into a grid
c39be11 Add projects page

مشتبهان اثنان. انظر إلى الأحدث بالكامل:

Terminal
git show 8f1e2aa -- projects.html

يُظهر الـ diff كتلة <footer> باللون الأحمر، أُزيلت مع ترميز القائمة القديمة. هذا هو الجاني، وجدته في أقل من دقيقة. ولتأخذ ترميز الـ footer القديم دون إرجاع أي شيء، اطبع الملف كما كان في الـ commit الأب وانسخ ما تحتاجه:

Terminal
git show 8f1e2aa~1:projects.html

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

رأيت أسماء مثل main و dark-mode معلّقة على الـ commits. يبدأ القسم الثاني بشرح حقيقة هذه الأسماء، ولماذا يكاد إنشاء واحد منها لا يكلّف شيئًا.

أهم النقاط

  • السجلّ رسم بياني موجَّه غير دوري، يشير فيه كل commit إلى أبيه، ويشير الـ merge commit إلى أبوين.
  • git log --oneline --graph --all يرسم الرسم البياني في الطرفية، بما فيها الفروع التي لست عليها.
  • git show <commit> يطبع رسالة commit واحد وتغييراته؛ و git show <commit>:<path> يطبع ملفًا كما كان حينها.
  • HEAD~1 هو الـ commit الذي يسبق الـ commit الذي أنت عليه، و HEAD~3 ثلاث خطوات إلى الخلف عبر الآباء الأوائل.
  • git diff A B يقارن أي لقطتين، و -- <path> يحصر المقارنة في ملف واحد.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

الأمر git log على main لا يعرض commit أنشأته على فرعك dark-mode. لماذا؟

السؤال 2 من 4

تريد رؤية styles.css كما كان تمامًا قبل commitين، دون تغيير أي ملف. أيّ أمر يفعل ذلك؟

السؤال 3 من 4

كم أبًا للـ merge commit الذي يدمج dark-mode في main؟

السؤال 4 من 4

أيّ أمر يسرد فقط الـ commits التي أضافت تغييراتها النص dark-mode أو أزالته؟

تدريب

اختر بحكمة

اقرأ الرسم البياني

هذا سجلّ الموقع الشخصي مرسومًا كرسم بياني للـ commits، وهو الشكل نفسه الذي يرسمه git log --oneline --graph --all نصًّا في الطرفية. الأسهم تتّجه من كل commit إلى أبيه.

:::figure رسم الـ commits في الموقع الشخصي

خمسة commits. الـ commitان 1a2b و 3c4d متتاليان. من 3c4d يخرج خط إلى 5e6f وآخر إلى 7a8b. الـ merge commit ذو المعرّف 9c0d أبواه 5e6f و 7a8b. الاسم main والـ HEAD يشيران إلى 9c0d؛ والاسم hero يشير إلى 7a8b. 1a2b 3c4d 5e6f 7a8b 9c0d main HEAD hero :::

أنت على main. ما الـ commits التي يسردها git log --oneline hero؟

اقرأ الرسم البياني