پرش به مطلب اصلی

تمرین عملی گروهی

در این تمرین، شما و هم‌تیمی‌تان نقش مهندسان نرم‌افزاری را بازی می‌کنید که روی هسته یک ماشین حساب کار می‌کنند. هدف این است که با چالش‌های واقعی کار تیمی مثل Merge Conflict و History Rewrite دست و پنجه نرم کنید.

پیش‌نیاز: گروه‌های ۲ نفره تشکیل دهید (نفر اول: Maintainer، نفر دوم: Contributor).

مرحله ۱: راه‌اندازی اولیه

مسئولیت: نفر اول (Maintainer)

  1. در گیت‌هاب یک ریپازیتوری Public با نام git-workshop-calc بسازید.

  2. در تنظیمات ریپازیتوری (Settings > Collaborators)، نفر دوم را اضافه کنید.

  3. نفر دوم باید دعوت‌نامه ایمیل شده را قبول کند.

  4. هر دو نفر پروژه را روی سیستم خود clone کنند.

  5. نفر اول: یک فایل با نام main.c بسازید و کد اولیه زیر را در آن قرار دهید، سپس Push کنید:

#include <stdio.h>

// Function declarations
int add(int a, int b);
int multiply(int a, int b);
int power(int base, int exp);

int main() {
printf("Calculator v1.0\n");
return 0;
}
مهم

از این لحظه به بعد، هیچکس حق ندارد مستقیماً روی برنچ main کامیت کند (Direct Commit ممنوع). همه تغییرات باید از طریق برنچ و Pull Request باشد.


مرحله ۲: توسعه موازی

در این مرحله هر دو عضو همزمان روی دو ویژگی مختلف کار می‌کنند.

وظایف:

  • نفر اول: برنچ feature/add را بسازد و تابع add را پیاده‌سازی کند.

  • نفر دوم: برنچ feature/multiply را بسازد و تابع multiply را پیاده‌سازی کند.

دستور کار:

  1. هر کس برنچ خود را بسازد (git checkout -b ...).

  2. کد مربوطه را در انتهای فایل main.c اضافه کنید.

  3. تغییرات را add و commit کنید.

  4. برنچ خود را به گیت‌هاب push کنید.

  5. در گیت‌هاب، یک Pull Request به سمت main باز کنید.

  6. PR نفر مقابل را بازبینی (Approve) کرده و سپس Merge کنید.

  7. خیلی مهم: بعد از ادغام، هر دو نفر در سیستم خود دستور git pull origin main را اجرا کنند تا کدهای یکدیگر را دریافت کنند.


مرحله ۳: ایجاد Conflict

حالا می‌خواهیم عمداً یک Merge Conflict ایجاد کنیم. هر دو نفر قرار است تابع power (توان) را پیاده‌سازی کنند، اما با روش‌های متفاوت!

وظایف:

  • نفر اول: در برنچ feature/power-loop، تابع توان را با حلقه (Loop) بنویسد.

  • نفر دوم: در برنچ feature/power-recursive، تابع توان را به صورت بازگشتی (Recursive) بنویسد.

دستور کار:

  1. هر دو نفر برنچ‌های جدید را بسازند.

  2. دقیقاً در یک محل مشخص از فایل main.c (انتهای فایل)، تابع int power(...) را پیاده‌سازی کنند.

  3. هر دو نفر commit و push کنند.

  4. هر دو نفر Pull Request بسازند.

  5. نفر اول PR خود را بدون مشکل Merge کند.

  6. نفر دوم هنگام Merge با خطا مواجه می‌شود (چون خطوط فایل تغییر کرده‌اند).

حل Conflict (توسط نفر دوم):

  1. در سیستم محلی، به برنچ خود بروید.

  2. برنچ main را در برنچ خود ادغام کنید: git merge main.

  3. فایل main.c را باز کنید. علائم <<<<<<< و ======= را پیدا کنید.

  4. با مشورت هم‌تیمی، یکی از پیاده‌سازی‌ها (یا ترکیبی از هر دو) را نگه دارید.

  5. فایل را ذخیره، add و commit کنید.

  6. دوباره push کنید و PR را در گیت‌هاب کامل کنید.


مرحله ۴: آزمایشگاه خرابکاری

حالا که پروژه کامل شد، بیایید کمی با ابزارهای بازگردانی بازی کنیم.

  1. آخرین نسخه main را دریافت کنید (git pull).

  2. در فایل main.c یک تغییر خرابکارانه بدهید (مثلاً کل کدها را پاک کنید!).

  3. با دستور git diff تغییرات را بررسی کنید و ببینید چه فاجعه‌ای رخ داده.

  4. پشیمان شدید؟ با دستور زیر فایل را نجات دهید:

    git restore main.c
  5. حالا یک خط کامنت به فایل اضافه کنید و آن را commit کنید.


مرحله ۵: سفر در زمان

می‌خواهیم تاریخچه را دستکاری کنیم و سپس آن را نجات دهیم.

۱. تمیز کردن تاریخچه (Interactive Rebase):

  1. دو کامیت الکی (مثلاً اضافه کردن خط فاصله) انجام دهید.

  2. با دستور زیر، ۲ کامیت آخر را با هم یکی (Squash) کنید:

    git rebase -i HEAD~2

    (در ادیتوری که باز می‌شود، جلوی کامیت دوم کلمه pick را به squash تغییر دهید).

۲. بازگشت به عقب (Hard Reset):

  1. حالا فرض کنید کلاً پشیمان شدید و می‌خواهید به ۳ کامیت قبل برگردید:

    git reset --hard HEAD~3
  2. اوه نه! کامیت‌های اصلی پروژه هم پریدند!

۳. عملیات نجات (Reflog):

  1. نترسید. لیست تمام حرکت‌هایتان را ببینید:

    git reflog
  2. هش (Hash) مربوط به زمانی که پروژه سالم بود (قبل از Reset) را پیدا کنید.

  3. زمان را به عقب برگردانید:

    git reset --hard <COMMIT_HASH>

پایان تمرین

اگر توانستید فایل main.c را سالم و شامل ۳ تابع (add, multiply, power) تحویل دهید، شما این تمرین را به پایان رسانده‌اید!

# وضعیت نهایی مورد انتظار در main.c
#include <stdio.h>
...
int main() { ... }
int add(...) { ... }
int multiply(...) { ... }
int power(...) { ... }