کدنامهمرجع‌های مهندسی نرم‌افزار، به فارسی
Git · پروژهٔ نهایی ۲Git · Final project 2

پروژهٔ ۲ — گردش کار تیمی

Project 2 — A real team workflow

در پروژهٔ قبل فقط خودت و تاریخچه بودید. این بار دو clone، یک مخزن راه دور و دو آدم داریم؛ یعنی تصمیمی که روی یک شاخه می‌گیری می‌تواند روی کار نفر دیگر اثر بگذارد.

You are no longer cleaning up only your own history. Alice and Bob are changing the same application; one change reaches main first, and the other must catch up without erasing a teammate’s work.

۲هم‌تیمیdevelopers
۵نمودارdiagrams
۰تمرین مصنوعیartificial exercises

مأموریت: دو تغییر، یک شاخهٔ اصلیMission: two changes, one main branch

آلیس و باب دو قابلیت جدا می‌سازند، اما هر دو به یک تنظیم مشترک دست می‌زنند. یکی زودتر وارد main می‌شود و دیگری عقب می‌افتد. مأموریت این نیست که هر طور شده merge سبز شود؛ باید نشان بدهی هیچ کار هم‌تیمی گم نشده و تصمیم ادغام قابل‌ردگیری است.

In the previous project, you prepared local history for review. Now a small team asks you to make its collaboration work: main should stay dependable, Alice adds CSV, Bob adds JSON, and both edit the same configuration file to enable their new format.

آلیس زودتر شاخه خودش را push می‌کند و یک PR باز می‌کند. باب هم تغییرش را push می‌کند. بعد از بازبینی، تغییر باب زودتر وارد main می‌شود. وقتی آلیس می‌خواهد کارش را به‌روز کند، شاخه او از main عقب است و هر دو تغییر روی یک خط تنظیمات دست گذاشته‌اند.

Alice pushes her branch and opens a PR first. Bob pushes his work too. After review, Bob’s change reaches main first. When Alice updates her work, her branch is behind main, and both changes touched the same configuration line.

کار تو این نیست که هر طور شده merge سبز بگیری. باید ببینی چه کسی چه چیزی دارد، از کجا عقب افتاده‌ایم، تعارض را با حفظ هر دو قابلیت حل کنیم، آزمون بگیریم و تاریخچه را مطابق سیاستی که خود تیم انتخاب کرده تحویل بدهیم.

Your job is not to make the merge button turn green at any cost. Find out who has what, identify where the branches diverged, resolve the conflict while preserving both features, test, and hand off a graph that matches the team’s chosen policy.

One bare remote and two independent working clonesAlice and Bob work in separate clones. Each pushes a branch to the central bare remote and fetches remote refs back; fetch does not merge a branch. Alice clonemain · feature/alice-csvbare remoteteam.git · refs and objectsno PR interfaceBob clonemain · feature/bob-json pushfetchpushfetch
نمودار ۱ — هر clone تاریخچهٔ محلی خودش را دارد. push شاخه را به bare مخزن راه دور می‌فرستد؛ fetch اشاره‌گرهای تازه را می‌آورد، اما شاخه فعال را خودکار ادغام نمی‌کند. PR در این نمودار نیست چون PR متعلق به میزبان است، نه Git.Diagram 1 — Each clone has its own local history. Push sends a branch to the bare remote; fetch brings back updated refs but does not merge them into the checked-out branch. A PR is absent because it belongs to a hosting platform, not Git.

شروع کوچک: برنامه‌ای که قالب‌هایش زیاد می‌شودA small starter: an app that gains export formats

نیازی به نصب package نداریم؛ Node.js و آزمون داخلی node:test کافی است. کار برنامه هم محدود است: فهرست قالب‌های فعال را از تنظیمات می‌خواند و برای هر قالب یک تابع خروجی دارد. این سادگی کمک می‌کند توجه‌مان روی کار تیمی بماند.

No package installation is needed; Node.js and its built-in node:test runner are enough. The application has a narrow job: read enabled formats from configuration and provide an exporter for each one. Keeping it small lets us focus on collaboration.

starter files · create these once in seed/
// package.json
{
  "name": "team-export",
  "private": true,
  "scripts": { "test": "node --test" }
}

// config/formats.json
{
  "enabled": ["text"]
}

// src/registry.js
const { enabled } = require("../config/formats.json");

function assertEnabled(format) {
  if (!enabled.includes(format)) {
    throw new Error(`Format is not enabled: ${format}`);
  }
}

module.exports = { enabled, assertEnabled };

// src/formats/text.js
const { assertEnabled } = require("../registry");

function text(rows) {
  assertEnabled("text");
  return rows.map((row) => `${row.name}: ${row.count}`).join("\n");
}

module.exports = { text };

// test/baseline.test.js
const test = require("node:test");
const assert = require("node:assert/strict");
const { enabled } = require("../src/registry");
const { text } = require("../src/formats/text");

test("the baseline keeps text export enabled", () => {
  assert.ok(enabled.includes("text"));
  assert.equal(text([{ name: "tea", count: 2 }]), "tea: 2");
});

فایل‌ها را در seed/ بساز و از همان‌جا node --test را اجرا کن. باید یک آزمون موفق ببینی. فقط بعد از این خط مبنا، مخزن را بساز؛ اگر آزمون از ابتدا قرمز باشد، نمی‌فهمیم تعارض بعدی علتش بوده یا برنامهٔ پایه.

Create these files in seed/ and run node --test there. You should see one passing test. Create the repository only after this baseline is green; otherwise a later conflict could be blamed for a problem that was already present.

آنچه از قبل بلدیمWhat you already know

از فصل‌های ۵ تا ۸ شاخه، merge، مخزن راه دور، fetch و push را می‌شناسی؛ فصل ۱۴ هم فرق PR با خود Git و مسئولیت بازبین را روشن کرده. اینجا قرار نیست آن‌ها را از صفر درس بدهیم: باید در یک مأموریت واحد انتخاب درستشان را انجام بدهی.

Chapters 5–8 introduced branches, merges, remotes, fetch, and push; Chapter 14 separated a PR from Git itself and explored the reviewer’s role. We will not reteach them from scratch—you will choose and use them together in one mission.

محیط تیم: یک مخزن راه دور، دو cloneTeam environment: one remote, two clones

برای بخش اصلی لازم نیست حساب GitHub یا GitLab داشته باشی. یک bare مخزن روی دیسک نقش مخزن راه دور را دارد و دو clone جدا نقش آلیس و باب را. این محیط برای دیدن اشاره‌گرها، push، fetch، تعارض و merge واقعی است؛ اما خودش PR، بازبینی تأیید، CI یا قانون محافظت از main ایجاد نمی‌کند.

The core project does not require a GitHub or GitLab account. A bare repository on disk acts as the remote, and two separate clones act as Alice and Bob. This is enough to observe refs, pushes, fetches, conflicts, and real merges—but it does not create PRs, review approvals, CI, or branch-protection rules for main.

همهٔ دستورهای مسیر محلی در یک ترمینال Bash مانند Git Bash یا WSL نوشته شده‌اند؛ مسیرهای ساخت پوشه را از workspace موقت خودت آغاز کن. فایل‌های starter را با ویرایشگر بساز. پوشه‌های seed، team.git، alice، bob و handoff کنار هم خواهند بود.

Local-route commands use a Bash-compatible terminal such as Git Bash or WSL; start from a disposable workspace. Create starter files with your editor. The seed, team.git, alice, bob, and handoff directories will sit side by side.

ساخت خط پایه و bare remote / create baseline and remote
cd /path/to/disposable/team-project
cd seed
git init -b main
git config user.name "Seed Maintainer"
git config user.email "seed@example.invalid"
git add package.json config src test
git commit -m "feat: add text export baseline"
node --test
cd ..
git clone --bare seed team.git
git clone team.git alice
git clone team.git bob
git -C alice config user.name "Alice"
git -C alice config user.email "alice@example.invalid"
git -C bob config user.name "Bob"
git -C bob config user.email "bob@example.invalid"

اگر Git قدیمی‌تر از نسخه‌ای است که git init -b را می‌شناسد، git init بزن و بعد git branch -M main. حالا از هر دو clone آزمون خط مبنا را اجرا و git remote -v را ببین: هر دو باید مخزن راه دور محلی team.git را نشان دهند. ایمیل‌های .invalid نمونه‌اند و ایمیل واقعی کسی نیستند.

If your Git predates git init -b, run git init and then git branch -M main. Run the baseline test from both clones and inspect git remote -v; each should point to the local team.git. The .invalid email addresses are examples, not real people’s addresses.

Pull request review and update loopThe author pushes a branch and opens a pull request. A reviewer reads the diff and requests a change. The author pushes the fix, checks rerun, and the PR is reviewed again before merge. Author pushes branchopens PR · explains scopeReviewer reads diffcorrectness + clarificationRequest changesreview decision on hostAuthor pushes fixtests run againfresh review → mergepolicy + chosen method
نمودار ۲ — PR چرخه‌ای از diff و گفت‌وگوست که میزبان نگه می‌دارد. push به شاخهٔ PR معمولاً همان درخواست باز را به‌روز می‌کند؛ در مسیر bare Git این چرخه را با یادداشت دستی شبیه‌سازی می‌کنیم.Diagram 2 — A PR is a host-managed cycle around a diff and its discussion. Pushing to the PR branch normally updates that open request; with bare Git, we simulate the cycle in a written record.

قیدها و سیاست تیمConstraints and team policy

قیدConstraintقاعدهٔ مأموریتMission ruleدلیلReason
mainپایدار؛ کار روزانه روی قابلیت شاخهStable; daily work happens on feature branchesتغییر ناتمام وارد خط پایه نشود.Keep unfinished work out of the baseline.
انتشار تغییرPublishing changespush معمولی؛ بدون push اجباریNormal push; no force-pushبازنویسی شاخه منتشرشده لازم نیست.There is no need to rewrite a published branch.
بازبینیReviewیک نظر correctness و یک نظر شفاف‌سازیOne correctness comment and one clarificationنظر باید به تغییر یا توضیح قابل مشاهده برسد.Feedback must lead to a visible change or explanation.
main جلو می‌رودmain advancesfetch، دیدن گراف، سپس ادغام آگاهانهFetch, inspect the graph, then integrate deliberatelypush ردشده مجوز force کردن نیست.A rejected push is not permission to force.
سیاست ادغامMerge policymerge commit با --no-ffMerge commits with --no-ffرابطهٔ شاخه‌ها و نقطهٔ ادغام در گراف می‌ماند.Branch relationships and integration points remain visible.

این انتخاب قانون همیشگی نیست. merge commit در این مأموریت کمک می‌کند مسیر هر قابلیت را بعد از ادغام ببینی. تیمی دیگر ممکن است squash را برای یک commit نهایی به‌ازای هر PR بخواهد یا rebase را برای تاریخچهٔ خطی انتخاب کند. روش‌های قابل انتخاب در GitHub هم تابع تنظیمات همان مخزن‌اند؛ merge محلی به‌تنهایی سیاست میزبان را فعال نمی‌کند.

This is not a universal rule. A merge commit helps this mission retain each feature’s path after integration. Another team might prefer squash for one final commit per PR, or rebase for a linear history. GitHub’s available methods depend on repository settings; a local merge does not enable host policy.

مسیر کار: از دو شاخه تا یک تحویلWork plan: from two branches to one handoff

۱ — خط پایه را در هر دو clone ثابت کن1 — Establish the baseline in both clones

هر مرحله باید یک وضعیت قابل‌اثبات داشته باشد: دو clone از یک پایه، دو شاخه مستقل، یک push جلوتر، یک بازبینی، یک ادغام آگاهانه و در پایان یک main مشترک که هر دو نفر به همان شناسه می‌رسند.

Tests should pass in both alice/ and bob/, with no working changes in status. Record the baseline commit ID. git rev-parse main should match in both clones.

۲ — آلیس قابلیت را push و PR را آماده می‌کند2 — Alice pushes a feature and prepares its PR

آلیس شاخه کوتاه‌عمر feature/alice-csv می‌سازد، CSV و آزمون ساده‌اش را اضافه می‌کند و مقدار csv را به آرایهٔ مشترک config/formats.json می‌افزاید. commit روشن feat: add CSV export بسازد، آزمون بگیرد و شاخه را با upstream push کند.

Alice creates short-lived feature/alice-csv, adds CSV support and a simple test, and appends csv to shared config/formats.json. She makes a clear feat: add CSV export commit, tests, and pushes with an upstream.

پروندهٔ PR را با پنج بخش بنویس: مشکل، راه‌حل، محدوده، آزمون‌های اجراشده و محدودیت شناخته‌شده. عمداً در نسخهٔ اول، comma و quote داخل مقدار CSV را escape نکن؛ آزمون ساده فعلاً سبز است. بازبین باید این نقص را پیدا کند.

Write the PR brief with five parts: problem, approach, scope, tests run, and known limitation. Deliberately leave commas and quotes inside CSV values unescaped in version one; the simple test still passes. The reviewer should catch the gap.

۳ — باب هم‌زمان JSON را جلو می‌برد3 — Bob independently advances JSON

باب از همان خط پایه شاخه feature/bob-json می‌سازد، خروجی JSON و آزمونش را اضافه می‌کند و همان config/formats.json را تغییر می‌دهد تا json هم فعال باشد. شاخه را push کند؛ هنوز کار آلیس را در clone خودش ندارد. دو clone یعنی دو واقعیت محلی متفاوت.

Bob branches from the same baseline as feature/bob-json, adds JSON output and its test, and edits the same config to enable json. He pushes; Alice’s work is not in his clone yet. Two clones mean two different local realities.

۴ — تغییر باب وارد main می‌شود؛ آلیس عقب می‌افتد4 — Bob’s change reaches main; Alice falls behind

در مسیر bare، پروندهٔ بازبینی باب را با آزمون و نظر قابل پاسخ کامل کن؛ سپس نقش maintainer را بازی کن و تغییر باب را با merge commit به main وارد و push کن. این نقش میزبان را شبیه‌سازی می‌کند؛ bare مخزن خودش PR یا مجوز push را enforce نمی‌کند.

On the bare route, complete Bob’s review record with a test and actionable comment, then act as maintainer: merge Bob into main with a merge commit and push. This simulates the host’s decision; bare Git does not enforce PRs or push permissions.

حالا از شاخه آلیس fetch کن و قبل از ادغام، گراف همهٔ اشاره‌گرها را بخوان. origin/main جلوتر است و شاخه آلیس هنوز آن commit را ندارد. در تنظیمات تعارض خواهی دید؛ هر دو قابلیت را نگه دار و آزمون CSV و JSON را اجرا کن. قبل از حل، حدس بزن کدام خط تعارض می‌کند.

Fetch from Alice’s branch and inspect the all-refs graph before integrating. origin/main is ahead, and Alice’s branch lacks that commit. Config will conflict; preserve both features and test CSV and JSON. Predict which line conflicts before resolving.

Alice is behind after Bob's feature reaches mainAlice and Bob branch from B0. Alice has A1; Bob has J1 and main advances to M1. Alice fetches and integrates origin/main, where config conflicts. B0J1A1M1shared baseBob: JSONAlice: CSV · local tiporigin/main fetch → inspect → mergethen resolve shared config
نمودار ۳ — دو شاخه از B0 جدا می‌شوند. باب به M1 می‌رسد؛ آلیس هنوز در A1 است. پیکان از نوک main تازه به تصمیم آلیس می‌رود؛ ادغام تعارض تنظیمات را آشکار می‌کند.Diagram 3 — The branches diverge at B0. Bob reaches M1; Alice remains at A1. The arrow carries the new main tip to Alice’s decision; integration exposes the config conflict.

۵ — بازبینی را به اصلاح قابل آزمون تبدیل کن5 — Turn review feedback into a tested fix

بازبین یک نظر correctness می‌گذارد: مقدار دارای comma، quote یا newline باید طبق قرارداد CSV encode شود. نظر دوم می‌پرسد اگر قالب فعال نباشد یا ورودی خالی باشد رفتار چیست، و چرا تنظیم فعال‌بودن بیرون از exporter است. این فقط سلیقه نیست؛ قرارداد را در README و آزمون روشن کن.

The reviewer leaves a correctness comment: CSV values containing commas, quotes, or newlines must be encoded correctly. A second asks what happens when the format is disabled or input is empty, and why enablement lives outside the exporter. This is more than taste; clarify the contract in docs and tests.

آلیس quoteهای داخلی را دوبرابر و سلول‌های دارای comma، quote یا newline را quote می‌کند؛ آزمون می‌نویسد و رفتار ورودی خالی را مشخص می‌کند. نظر دوم را با توضیح یا تغییر مستند پاسخ می‌دهد. بازخورد را در commit جدا مثل fix: escape CSV fields ثبت و شاخه را عادی push می‌کند. اگر سیاست میزبان تأیید قبلی را stale کند، بازبین نسخهٔ تازه را دوباره می‌بیند.

Alice doubles embedded quotes, quotes cells containing commas, quotes, or newlines, adds tests, and defines empty-input behavior. She answers the second comment with a documentation change or explanation. She records the follow-up in a separate commit such as fix: escape CSV fields and pushes normally. If host policy marks approval stale, the reviewer checks the new version.

۶ — ادغام، پاک‌سازی و همگام‌سازی باب6 — Merge, clean up, and synchronize Bob

بعد از حل تعارض و بازبینی، push را عادی انجام بده؛ اگر رد شد fetch و گراف را دوباره ببین. maintainer تغییر آلیس را با merge commit وارد main می‌کند. شاخه‌ها را فقط بعد از اثبات ادغام پاک کن؛ Bob fetch می‌کند، main خودش را fast-forward می‌کند و همان آزمون را اجرا می‌کند.

After conflict resolution and review, push normally; if rejected, fetch and inspect the graph again. The maintainer integrates Alice with a merge commit. Delete branches only after proving they were merged; Bob fetches, fast-forwards his main, and runs the same tests.

PR باید سؤال روشنی برای بازبین بسازدA PR should give reviewers a clear question

در مسیر local-bare، خود Git چیزی به اسم PR ندارد؛ برای اینکه بازبینی را فقط در ذهن اجرا نکنی، دو پرونده در handoff/ بیرون از cloneها نگه دار: alice-pr.md و review-log.md. این یادداشت‌ها جای مخزن راه دور یا commit نیستند؛ تصمیم‌های میزبان را در محیطی ثبت می‌کنند که میزبان واقعی ندارد.

In the local bare route, Git has no PR object. To make review observable rather than imaginary, keep alice-pr.md and review-log.md in handoff/, outside the clones. These notes are not a remote or commits; they record host decisions in an environment without a host.

بخش PRPR fieldپرسشQuestionمدرکEvidence
مشکلProblemچه نیازی داریم؟What need are we addressing?قالب‌های خروجی محدودند.Available export formats are limited.
راه‌حلApproachچه چیزی عوض می‌شود؟What changes?افزودن exporter و فعال‌کردن فرمت در config.Add an exporter and enable it in config.
محدودهScopeچه چیزی بیرون است؟What is out of scope?بدون dependency تازه یا تغییر API پایه.No new dependency or baseline API change.
آزمونTestingچه چیزی اجرا شد؟What ran?node --test و نتیجه‌اشand its result
ریسکRiskچه محدودیتی مانده؟What limitation remains?نسخهٔ اول CSV escape کامل ندارد و نباید merge شود.Initial CSV lacks escaping and must not merge yet.

در GitHub، نظر inline، وضعیت بازبینی و بررسی کنار PR ثبت می‌شوند؛ در bare route آن‌ها را در بازبینی-log با نام بازبین، commit بررسی‌شده، نظرها، پاسخ آلیس و آزمون یادداشت کن. “Request changes” به‌تنهایی همیشه قفل فنی نیست؛ اگر حفاظت شاخه/ruleset آن را الزام نکرده باشد، ممکن است maintainer بتواند merge کند. ظاهر بازبینی را با سیاست enforceشده یکی نگیر.

On GitHub, inline comments, review state, and checks attach to the PR; in the bare route, record reviewer, reviewed commit, comments, Alice’s response, and test result. “Request changes” is not always a technical lock: without an enforcing branch protection rule or ruleset, a maintainer may still merge. Do not confuse review status with enforced policy.

Git data and hosting review are different layersGit stores commits and branches; a host attaches PR description, review decisions, and CI to those refs and gates merge only according to configured rules. Local Gitcommits · branches · mergeobjects in repositoriesRemote refspush / fetchbare or hosted serviceHosting platformPR · review · checksrules may gate merge PR metadata and review history are not Git commit objects.
نمودار ۴ — commit و شاخه دادهٔ Git هستند؛ PR و بازبینی دادهٔ میزبان‌اند که به اشاره‌گرها اشاره می‌کنند. CI فقط وقتی gate ادغام است که سیاست مخزن آن را لازم بداند.Diagram 4 — Commits and branches are Git data; PRs and reviews are host data that refer to refs. CI gates integration only when repository policy requires it.

گیرهای تمرین: قبل از واکنش، شاهد جمع کنProject snags: collect evidence before reacting

push آلیس رد می‌شودAlice’s push is rejected

قرار است چند جا عمداً کار گیر کند. هر بار قبل از واکنش سه چیز را جدا کن: شاخهٔ خودت، آخرین خبری که از مخزن راه دور داری و چیزی که واقعاً روی مخزن مشترک است. بعد مشخص کن کدام اختلاف باید حل شود؛ نه اینکه مستقیم سراغ force بروی.

Keep the rejection message, fetch, and inspect the all-refs graph. Rejection says the remote ref is ahead or incompatible; it does not choose an integration for you. Do not force-push.

تنظیمات بعد از merge فقط یکی از فرمت‌ها را داردConfig enables only one format after merge

حل تعارض یعنی انتخاب نیت نهایی، نه بردن یک سمت. مقدار نهایی باید text، csv و json را داشته باشد. تنظیمات را بخوان و آزمون هر دو قابلیت را اجرا کن.

Conflict resolution chooses the intended result, not a winning side. The final config must include text, csv, and json. Inspect it and test both features.

نشانگر تعارض باقی ماندهConflict markers remain

قبل از commit، git diff --check و جست‌وجوی <<<<<<<، ======= و >>>>>>> را انجام بده. آزمون ممکن است نشانگرهای بی‌اثر را نبیند؛ جست‌وجو مدرک مکمل است.

Before committing, run git diff --check and search for conflict-marker strings. Tests may miss inert markers; the search is complementary evidence.

باب commitهای تازه را نمی‌بیندBob cannot see the new commits

مخزن راه دور جلو رفته اما clone خودکار عوض نمی‌شود. fetch کن، گراف را بخوان و فقط با پوشه‌ کاری تمیز git merge --ff-only origin/main بزن. اگر fast-forward ممکن نباشد، فرمان متوقف می‌شود تا تاریخچه را بفهمی.

The remote moved; Bob’s clone did not update itself. Fetch, inspect the graph, and run git merge --ff-only origin/main only with a clean tree. If fast-forward is impossible, the command stops so you can understand the history.

بازبینی سبز است اما روش merge فرق می‌کندReview is green but the merge method differs

میزبان ممکن است روش‌های خاص یا up-to-date بودن شاخه را لازم بداند. تنظیمات repo را بررسی کن؛ merge محلی جای تصمیم GitHub/GitLab نیست.

The host may restrict merge methods or require an up-to-date branch. Check repository settings; a local merge is not the host’s decision.

معیار تحویل: هر دو نفر به یک نتیجه برسندAcceptance: both people reach the same result

پروژه وقتی تمام نیست که «push موفق شد». باید هر دو clone به یک main برسند، هر دو قابلیت باقی مانده باشند، تست رد نشود و گراف نهایی با سیاست ادغامی که اول انتخاب کردی جور باشد. نتیجه و داستان تاریخچه هر دو بخشی از تحویل‌اند.

Run these checks in both clones. The important criterion is the same final commit ID on main, not merely matching branch names.

در alice/ و bob/ · run in both clones
node --test
git status --short --branch
git fetch --prune origin
git log --oneline --graph --decorate --all
git rev-parse main
git rev-parse origin/main
git diff --check origin/main
git grep -n -E '^(<<<<<<<|=======|>>>>>>>)' main --
git branch -vv
git branch -r

قبولی یعنی هر دو exporter آزمون را می‌گذرانند؛ تنظیمات هر سه فرمت را فعال می‌کند؛ فرمان جست‌وجوی نشانگر در main خروجی ندارد (کد خروجی ۱ اینجا یعنی موردی پیدا نشده)؛ وضعیت ردیابی‌شده تمیز است؛ شناسهٔ main و origin/main در هر clone برابرند و مقدار main در clone آلیس و باب هم یکی است؛ شاخه‌های قابلیت حذف شده‌اند؛ و گراف merge commitهای سیاست را نشان می‌دهد. node --test را اینجا خودت اجرا می‌کنی؛ این کار به‌تنهایی CI نیست. در مسیر میزبانی‌شده، بررسی فقط وقتی جلوی merge را می‌گیرد که تنظیمات مخزن آن را لازم کرده باشد.

Pass means both exporters pass; config enables all three formats; the marker search on main prints nothing (exit code 1 means no match here); tracked status is clean; local main equals origin/main in each clone and both clones agree; feature branches are deleted; and the graph shows the policy’s merge commits. You run node --test locally; that alone is not CI. On a hosted route, a check blocks merging only if repository rules require it.

بررسیCheckچه چیزی ثابت می‌شودWhat it provesمرزشIts limit
node --testرفتارهای آزموده‌شدهٔ هر دو فرمت درست‌اند.Tested behavior for both formats passes.هر ورودی ممکن CSV را ثابت نمی‌کند.Does not prove every possible CSV input.
git rev-parse main origin/mainmain محلی و اشاره‌گر رهگیریِ مخزن راه دور یک commit دارند.Local main and remote-tracking ref name the same commit.هر clone را جداگانه باید بسنجی.Must be checked in each clone.
git log --graph --allمسیر قابلیتها و نقطه‌های merge دیده می‌شود.Feature paths and integration points are visible.بازبینی یا CI را ثبت نمی‌کند.Does not record review or CI.
پرونده‌های تحویلHandoff filesPR، نظرها، پاسخ‌ها و نتیجهٔ بررسی مستندند.PR, comments, responses, and checks are documented.یادداشت، enforcement میزبان نیست.A note does not enforce host policy.

در bare route، گراف و شناسه‌ها را ثابت می‌کنی؛ handoff فقط بازبینی را شبیه‌سازی می‌کند. در GitHub واقعی، وضعیت PR، بازبینی ثبت‌شده و بررسیهای میزبان را هم نگه دار. هیچ‌کدام تضمین نمی‌کند تیم در آینده همیشه سیاست را رعایت کند.

With bare Git, you prove graph and commit IDs; handoff only simulates review. In GitHub, retain PR state, submitted reviews, and host checks too. None of this guarantees the team will always follow policy later.

اگر گیر کردی، پله‌پله جلو بروIf you get stuck, take hints one at a time

۱ — از کجا بفهمم دو clone جدا دارم؟1 — How do I know these are separate clones?

اگر گیر کردی، فقط یک پله از راهنما را باز کن و دوباره خودت تلاش کن. این پروژه باید حس همکاری واقعی بدهد؛ اگر تمام مسیر را از روی راه‌حل مرجع اجرا کنی، مهم‌ترین بخشش یعنی تصمیم‌گیری را از دست می‌دهی.

Inspect git rev-parse --show-toplevel and git remote -v in each. Roots differ but the remote matches. Alice’s unpushed file change should not appear in Bob’s clone.

۲ — push رد شد؛ اول چه کنم؟2 — Push was rejected; what first?

fetch کن، بعد گراف همهٔ اشاره‌گرها را ببین. commit مخزن راه دورای را پیدا کن که HEAD تو ندارد. سپس با توجه به سیاست تصمیم بگیر merge یا rebase؛ این پروژه merge را انتخاب کرده تا رابطهٔ ادغام در گراف دیده شود.

Fetch, then inspect all refs. Find the remote commit your HEAD lacks. Choose merge or rebase according to policy; this project uses merge to keep the integration relationship visible.

۳ — تعارض را به کدام سمت حل کنم؟3 — Which side should win the conflict?

اگر یکی را انتخاب کنی یک قابلیت گم می‌شود. تنظیمات نهایی باید text، csv و json داشته باشد. خط را بازنویسی کن، نشانگرها را بردار و هر دو exporter را تست کن.

Choosing one side loses a feature. Final config needs text, csv, and json. Rewrite the line, remove markers, and test both exporters.

۴ — بازبینی چه وقت پاسخ گرفته؟4 — When is review feedback addressed?

کد یا مستندات را تغییر بده، آزمون مرتبط را اضافه و پاسخ هر نظر را ثبت کن. پرسش قراردادی ممکن است با توضیح حل شود؛ نگرانی correctness به آزمون نیاز دارد. اگر diff عوض شد، در میزبانی‌شده PR درخواست بازبینی تازه بده.

Update code or docs, add relevant tests, and record each response. A contract question may be answered with clarification; a correctness concern needs a test. If the diff changes, request another hosted review.

۵ — چطور Bob را به main نهایی برسانم؟5 — How do I bring Bob to final main?

بعد از push نهایی، Bob fetch می‌کند، تمیزبودن پوشه‌ کاری را می‌سنجد و git merge --ff-only origin/main می‌زند. اگر fast-forward نشد، توقف کن و گراف را بفهم؛ دستور عمداً از ساختن ادغام پنهانی خودداری می‌کند.

After the final push, Bob fetches, checks for a clean tree, and runs git merge --ff-only origin/main. If fast-forward fails, stop and inspect the graph; the command intentionally avoids a hidden integration.

راه‌حل مرجع، بعد از انجام مأموریتReference solution, after attempting the mission

راه‌حل دو لایه دارد: A) Git خالص با bare مخزن راه دور و دو clone؛ B) مسیر PR میزبانی‌شده. لازم نیست فرمان‌ها را از حفظ بگویی؛ باید گراف، آزمون و تصمیم بازبینی را توضیح بدهی.

The reference has two layers: A) pure Git with a bare remote and two clones; B) a hosted PR path. You do not need to memorize commands; explain the graph, tests, and review decisions.

راه‌حل A — bare مخزن راه دورSolution A — bare remote +

شاخه بساز، تغییر را بفرستBranch, commit, and push

پوشهٔ Alice و Bob جداست؛ قبل از push اول، commit را در clone مربوطه و روی شاخهٔ کوتاه‌عمر بساز. فایل‌های تغییرکرده را مشخص ناحیه‌ آماده‌سازی کن؛ git add . را جای بازبینی diff نگذار.

Alice and Bob have separate working directories. Make each commit in its own clone on the short-lived feature branch. Stage intended paths explicitly; do not use git add . as a substitute for reviewing the diff.

فرمان‌های دو feature / feature branch commands
# Alice clone
cd ../alice
git switch -c feature/alice-csv
# create src/formats/csv.js and test/csv.test.js; append csv to config
node --test
git diff --check
git add config/formats.json src/formats/csv.js test/csv.test.js
git diff --cached
git commit -m "feat: add CSV export"
git push -u origin feature/alice-csv

# Bob clone, independently from baseline main
cd ../bob
git switch -c feature/bob-json
# create src/formats/json.js and test/json.test.js; append json to config
node --test
git diff --check
git add config/formats.json src/formats/json.js test/json.test.js
git diff --cached
git commit -m "feat: add JSON export"
git push -u origin feature/bob-json

پیاده‌سازی مرجع قابلیتهاReference feature implementations

Alice روی شاخه خودش تنظیمات را به text و csv تغییر می‌دهد؛ بعد از merge باب، main به text و json رسیده و تعارض باید هر سه را نگه دارد. آزمون خط پایه فقط وجود text را می‌سنجد، پس با اضافه‌شدن قالب‌ها معتبر می‌ماند. نسخهٔ کامل CSV قرارداد escape را روشن می‌کند: سلولی که comma، quote یا newline دارد quote می‌شود و quote داخلی دوبرابر می‌شود.

Alice’s branch enables text and csv; after Bob’s merge, main enables text and json, so conflict resolution must preserve all three. The baseline test checks only that text remains enabled, so it stays valid as formats are added. The completed CSV code makes escaping explicit: quote cells containing commas, quotes, or newlines, and double embedded quotes.

src/formats/csv.js و src/formats/json.js
// src/formats/csv.js
const { assertEnabled } = require("../registry");

function cell(value) {
  const raw = String(value);
  if (/[",\r\n]/.test(raw)) {
    return `"${raw.replaceAll('"', '""')}"`;
  }
  return raw;
}

function csv(rows) {
  assertEnabled("csv");
  const records = rows.map((row) => `${cell(row.name)},${cell(row.count)}`);
  return ["name,count", ...records].join("\n");
}

module.exports = { csv };

// src/formats/json.js
const { assertEnabled } = require("../registry");

function json(rows) {
  assertEnabled("json");
  return JSON.stringify(rows, null, 2);
}

module.exports = { json };

آزمون CSV باید مقدار ساده، comma، quote و ورودی خالی را بپوشاند. در این قرارداد، آرایهٔ خالی فقط header را برمی‌گرداند. آزمون JSON خروجی را با JSON.parse بخواند و داده را مقایسه کند؛ به فاصله‌گذاری اتفاقی وابسته نشود.

CSV tests should cover a plain value, comma, quote, and empty input. Under this contract, an empty array returns only the header. The JSON test should parse output and compare data rather than depend on incidental whitespace.

ترتیب تغییرهاChange sequence

  1. آلیس از main شاخهٔ feature/alice-csv می‌سازد؛ exporter CSV ساده و آزمونش را اضافه می‌کند، تنظیمات را تغییر می‌دهد، commit می‌زند و git push -u origin feature/alice-csv را اجرا می‌کند.Alice creates feature/alice-csv, adds a simple CSV exporter and test, edits config, commits, and runs git push -u origin feature/alice-csv.
  2. باب از همان baseline شاخهٔ feature/bob-json می‌سازد، JSON و آزمونش را اضافه می‌کند، همان تنظیمات را تغییر می‌دهد و push می‌کند.Bob branches from the same baseline as feature/bob-json, adds JSON and its test, changes the same config, and pushes.
  3. بعد از ثبت بازبینی باب، maintainer در clone آلیس main را به‌روز می‌کند، origin/feature/bob-json را با --no-ff merge و main را push می‌کند. این شبیه‌سازی نقش میزبان است؛ bare Git قانون PR را اجرا نکرده.After recording Bob’s review, the maintainer updates main in Alice’s clone, merges origin/feature/bob-json with --no-ff, then pushes main. This simulates the host; bare Git did not enforce a PR rule.
  4. آلیس روی قابلیت، fetch و گراف را می‌بیند، origin/main را merge می‌کند، تعارض تنظیمات را با هر سه فرمت حل می‌کند و آزمون می‌گیرد.On her feature, Alice fetches and inspects the graph, merges origin/main, resolves config with all three formats, and tests.
  5. بازخورد CSV را با escape سلول‌های دارای comma/quote/newline، دوبرابرکردن quote و آزمون پاسخ می‌دهد. برای clarification قرارداد تنظیمات و ورودی خالی را در README روشن می‌کند.She addresses CSV feedback by escaping cells with commas, quotes, or newlines, doubling embedded quotes, and testing. She clarifies config and empty-input behavior in the README.
  6. تغییر بازبینی را جدا commit و عادی push می‌کند؛ بعد از بازبینی تازه maintainer شاخه را با merge commit وارد main می‌کند.She commits review changes separately and pushes normally; after a fresh review, the maintainer merges the branch with a merge commit.
نمونهٔ conflict / representative conflict
<<<<<<< HEAD
  "enabled": ["text", "csv"]
=======
  "enabled": ["text", "json"]
>>>>>>> origin/main

نشانگرها را به نتیجهٔ موردنظر تبدیل کن؛ آن‌ها را با هم commit نکن. در این پروژه، آرایهٔ نهایی [\"text\", \"csv\", \"json\"] است. ترتیب JSON array اینجا بخشی از قرارداد آزمون تیم است؛ اگر در پروژهٔ واقعی ترتیب معنا ندارد، مستند کن که چرا هر ترتیبی پذیرفته می‌شود.

Replace the markers with the intended result; do not commit them. Here the final array is [\"text\", \"csv\", \"json\"]. Its order is part of this project’s test contract; if order is irrelevant in a real project, document why any order is accepted.

ادغام باب، تازه‌سازی آلیس و همگام‌سازی / integration sequence
# Alice clone: integrate Bob's approved branch as maintainer
git switch main
git fetch origin
git merge --ff-only origin/main
git merge --no-ff origin/feature/bob-json -m "merge: integrate Bob JSON export"
node --test
git push origin main

# Alice: return to her feature and inspect the new remote tip
git switch feature/alice-csv
git fetch origin
git log --oneline --graph --decorate --all
git merge origin/main
# resolve config/formats.json, test, then complete the merge
node --test
git diff --check
git add config/formats.json
git commit -m "merge: sync CSV branch with main"

# after review fixes and tests
node --test
git push

این ترتیب عمداً main را فقط در نقش maintainer جلو می‌برد؛ در GitHub واقعی، PR باید از میزبان merge شود تا سیاست enforce شود. بعد از حل تعارض، شاخه آلیس از مخزن راه دور عقب نیست و push معمولی کافی است. اگر مخزن راه دور باز هم جلو رفت، دوباره fetch و گراف را بررسی کن.

This sequence advances main only while acting as maintainer; on GitHub, merge the PR on the host so its policy is enforced. After conflict resolution, Alice’s branch can be pushed normally. If the remote moves again, fetch and inspect the graph again.

نمودار پایانی مورد انتظارExpected final graph

Merge-commit policy preserves both feature pathsThe base splits into Alice and Bob. Bob is merged into main first. Alice merges that updated main into her feature branch to resolve the config conflict, then Alice's reviewed feature is merged into main. B0A1A2J1M1U1M2baseAlice CSVreview fixBob JSONBob → mainAlice merges new mainAlice → main
نمودار ۵ — B0 دو شاخه می‌سازد. M1 تغییر باب را وارد main می‌کند؛ U1 آن main تازه را در شاخه آلیس ادغام می‌کند؛ M2 PR آلیس را وارد main می‌کند. پیکان‌ها والدهای commit ادغام را نشان می‌دهند، نه مقیاس زمانی.Diagram 5 — B0 splits into two branches. M1 integrates Bob into main; U1 merges updated main into Alice’s branch; M2 integrates Alice’s PR. Arrows represent merge-parent relationships, not a time scale.
باب: دریافت main نهایی و حذف refهای کهنه / Bob: sync and prune
git fetch --prune origin
git switch main
git merge --ff-only origin/main
node --test
git rev-parse main
git rev-parse origin/main

# after both PRs have been integrated
git push origin --delete feature/alice-csv feature/bob-json
git fetch --prune origin
git branch -d feature/bob-json

در clone باب، شاخه محلی آلیس وجود ندارد مگر خودت ساخته باشی؛ پس شاخه محلی را فقط جایی حذف کن که وجود دارد. در clone آلیس هم بعد از رفتن روی main، git fetch --prune و git branch -d feature/alice-csv اجرا کن. prune فقط اشاره‌گر رهگیریِ مخزن‌های راه دوری حذف‌شده را پاک می‌کند، نه شاخه‌های محلی یا commitهای reachable.

Bob’s clone has no local Alice branch unless you created one, so delete local branches only where they exist. In Alice’s clone, switch to main, prune, and delete feature/alice-csv. Prune removes stale remote-tracking refs, not local branches or reachable commits.

راه‌حل B — میزبانی‌شده PRSolution B — hosted PRs

اگر GitHub در دسترس است، مخزن خصوصی یا آزمایشی بساز و Alice و Bob را collaborator کن. مخزن را خالی بساز تا README اولیه با baseline تعارض نکند. دو PR جدا با base برابر main باز کن. باب بازبینی می‌خواهد؛ آلیس دو نظر inline ثبت می‌کند؛ باب PR خودش را تکمیل و طبق روش مجاز merge می‌کند تا main جلو برود. آلیس شاخه را از base تازه sync، تعارض را حل، آزمون را اجرا و push می‌کند؛ PR همان شاخه تازه را نشان می‌دهد.

If GitHub is available, create a private or test repository and add Alice and Bob as collaborators. Start empty so an initial README does not conflict with the baseline. Open two PRs targeting main. Bob requests review; Alice leaves two inline comments; Bob updates and merges his PR using an allowed method so main advances. Alice syncs from the new base, resolves the conflict, tests, and pushes; the existing PR reflects the updated branch.

روی مخزن تمرین حفاظت شاخه/ruleset بگذار: PR لازم، تأیید، بررسی آزمون و در صورت نیاز حل conversationها. “Request changes” بدون سیاست مرتبط الزاماً جلوی merge را نمی‌گیرد. اگر commit تازه روی شاخهٔ آلیس آمد، سیاست ممکن است تأیید قبلی را stale کند؛ پس بازبین باید diff جدید را ببیند. در پایان merge commit را طبق انتخاب این پروژه بزن و شاخهها را بعد از تأیید ادغام پاک کن.

Configure branch protection or a ruleset on the test repo: require PRs, approval, the test check, and optionally resolved conversations. “Request changes” without the relevant policy does not necessarily block merging. New commits on Alice’s branch may make approval stale; have the reviewer inspect the new diff. Finish with a merge commit as chosen for this project, then delete branches after verifying integration.

نام گزینه‌ها و رفتار UI را فقط به GitHub تعمیم بده؛ اگر از GitLab یا میزبان دیگری استفاده می‌کنی، مستندات رسمی همان میزبان را بررسی کن.

Apply UI labels and behavior only to GitHub; if you use GitLab or another host, verify that host’s official documentation.

بازنگری و تحویل به تیمReflection and team handoff

قبل از بستن پروژه، یک بار از دید هم‌تیمی‌ات نگاه کن: کدام اطلاعات را Git به تو داد و کدام را سرویس میزبانی؟ کجا ممکن بود کار نفر دیگر را بازنویسی کنی؟ و چه شاهدی ثابت کرد هر دو تغییر سالم به main رسیده‌اند؟

Answer these by pointing to your graph or artifacts; “everything synced” alone is not evidence.

  • کدام بخش را Git ذخیره کرد و کدام فقط در میزبان PR ثبت شد؟Which parts did Git store, and which existed only in the PR host?
  • اگر push باب اول رد می‌شد، قبل از ادغام چه چیزی را fetch و بررسی می‌کردی؟If Bob’s push were rejected first, what would you fetch and inspect before integrating?
  • چه مدرکی نشان می‌دهد هر دو قابلیت مانده و هر دو clone به یک main رسیده‌اند؟What proves both features survived and both clones reached the same main?
  • کدام عمل می‌توانست کار منتشرشدهٔ هم‌تیمی را بازنویسی کند و چرا لازم نبود؟Which action could rewrite a teammate’s published work, and why was it unnecessary?

گزارش handoff شامل گراف نهایی، شناسهٔ یکسان main، خروجی آزمون در هر دو clone، تنظیمات نهایی، پاسخ بازبینیها و اشاره‌گرهای پاک‌شده است. فقط از مدرکی حرف بزن که واقعاً جمع کرده‌ای؛ bare مخزن راه دور ثابت نمی‌کند PR یا CI واقعی اجرا شده است.

The handoff includes the final graph, matching main IDs, test output from both clones, final config, review responses, and deleted refs. Report only evidence you collected; a bare remote cannot prove a real PR or CI run.

پاک‌سازی امنSafe cleanup

فقط بعد از تحویل گزارش و اطمینان از اینکه workspace همان فضای disposable این پروژه است آن را پاک کن. مخزن راه دور یا clone واقعی هم‌تیمی را هدف نگیر. اگر شک داری، پوشه را نگه دار؛ از دست‌رفتن شواهد بهای تمیزشدن دیسک نیست.

Only after handoff and confirming the workspace is disposable may you remove it. Do not target a teammate’s real remote or clone. If unsure, keep it; losing evidence is not worth a tidier disk.

پروژهٔ آخر: این بار مخزن از قبل خراب استFinal project: this time, the repository is already broken

پروژهٔ آخر دیگر از یک مخزن مرتب شروع نمی‌شود. وقتی آن را تحویل می‌گیری، چند چیز از قبل خراب شده و اولین مهارتت این است که قبل از تعمیر، چیزی را بدتر نکنی.

Here the baseline was stable and we resolved conflict before work disappeared. Sometimes you join after a bad push, deleted branch, or missing working-tree file. Project 3 removes the rails: the repository is already broken, and you must rescue it with evidence rather than guesses.

مراجع رسمی برای مسیر میزبانOfficial references for the hosted route

این پیوندها رفتار GitHub را توضیح می‌دهند؛ برای میزبان دیگر، راهنمای رسمی همان سرویس را بخوان.

These links describe GitHub behavior; consult the relevant host’s official docs for another service.