کدنامهمرجع‌های مهندسی نرم‌افزار، به فارسی
گیت · فصل ۸Git · Chapter 8

مخزن راه دور

Remotes: fetch, pull, push, and tracking branches

تا اینجا تقریباً همه‌چیز روی یک مخزن بود. حالا دو توسعه‌دهنده و یک مخزن مشترک وارد داستان می‌شوند؛ جایی که origin/main، fetch و pull اگر مدلشان را نداشته باشی خیلی زود قاطی می‌شوند.

Alice pushes a commit and her terminal says it worked. Bob opens his project at the same moment; neither his files nor his git log has changed. Where is Alice’s commit on Bob’s machine? Before answering, we’ll create two clones and one bare repository, then inspect each ref separately.

۱۵بخشsections
۸۰دقیقهminزمان مطالعهreading time
۱۸تمرین با پاسخsolved exercises
۵نمودارdiagrams

آلیس push کرد؛ باب هنوز چیزی نمی‌بیندAlice pushed; Bob still sees nothing

فصل ۰۷ همه‌چیز را داخل یک مخزن نگه داشت: اشاره‌گرهای main و feature محلی بودند. حالا سه نسخه داریم: یک مخزن مرکزی به نام server.git و دو clone به نام‌های alice و bob. فرض کن آلیس commit تازه‌ای بسازد و push کند. قبل از اینکه باب فرمانی اجرا کند، فایل او عوض می‌شود؟ شاخه محلی main او جلو می‌رود؟

Chapter 07 kept everything inside one repository: main and feature were local refs. Now we have three copies: a central repository named server.git and two clones named alice and bob. Suppose Alice creates and pushes a commit. Before Bob runs a command, do his files change? Does his local main advance?

نه. push آلیس اشاره‌گرهای مخزن راه دور را به‌روزرسانی می‌کند. clone باب مستقل است؛ تا وقتی از مخزن راه دور خبر نگیرد، حتی نمی‌داند commit تازه‌ای وجود دارد. امروز سه واژه را از هم جدا می‌کنیم: fetch برای گرفتن خبر و شیءها، انتخاب آگاهانه برای واردکردن تغییر به شاخهٔ خودمان، و push برای فرستادن commitها.

No. Alice’s push updates refs in the remote repository. Bob’s clone is independent; until it contacts the remote, it does not know that the new commit exists. We will separate three actions: fetch objects and information, intentionally integrate changes into our branch, and push commits outward.

Alice pushes to a bare repository; Bob fetches from it into his local repository Alice local clone server.git bare repository shared refs + objects Bob local clone push fetch Bob changes only after Bob fetches and then integrates
نمودار ۱ — فلش آبی حرکت push از clone آلیس به مخزن مشترک است؛ فلش بنفش fetch از مخزن به clone باب. این‌ها دو عملیات جدا هستند و هیچ‌کدام به‌تنهایی فایل‌های کاری باب را ادغام نمی‌کند.
Diagram 1 — The blue arrow is Alice pushing from her clone to the shared repository; the violet arrow is Bob fetching from it. These are separate operations, and neither alone integrates changes into Bob’s working files.

«مخزن راه دور» نام یک مخزن است؛ «origin» فقط اسم رایج آنA remote names a repository; origin is only a common name

داخل clone باب، تنظیماتی هست که می‌گوید آدرس نسخهٔ مشترک کجاست و اشاره‌گرهایش چطور در این مخزن محلی ثبت شوند. Git به این مقصد یک نام کوتاه می‌دهد؛ origin قرارداد رایج clone است، نه واژه‌ای رزروشده و نه نام خود GitHub.

Bob’s clone stores configuration describing where the shared copy lives and how its refs map into this local repository. Git gives that destination a short name. origin is the conventional name created by clone, not a reserved keyword and not the name of GitHub itself.

در بخش بعد پوشه‌های آزمایش را می‌سازیم؛ فعلاً این فرمان‌ها را فقط به‌عنوان نمونهٔ تنظیمات یک clone بخوان. وقتی به مرحلهٔ ساخت رسیدی، آن‌ها را در پوشهٔ bob/ اجرا کن. این فرمان‌ها فقط تنظیمات محلی را می‌خوانند و چیزی push یا fetch نمی‌کنند. آدرس‌های fetch و push را مقایسه کن. اگر token اشتباهی داخل URL است، آن را در خروجی یا تصویر پخش نکن و آدرس را امن اصلاح کن.

We create the lab folders in the next section; for now, read these as a sample of one clone’s configuration. When you reach the setup, run them inside bob/. They only read local settings; they do not push or fetch. Compare fetch and push URLs. If a token was accidentally embedded in a URL, do not share it in output or screenshots; replace the URL safely.

inside bob/
git remote -v
git remote show origin
representative output · URL and branch names vary
origin  ../server.git (fetch)
origin  ../server.git (push)

* remote origin
  Fetch URL: ../server.git
  Push  URL: ../server.git
  HEAD branch: main
  Remote branch:
    main tracked

در این آزمایش، آدرس نسبی است چون همه‌چیز روی یک دیسک است. در کار واقعی ممکن است HTTPS یا SSH ببینی. git remote show origin بدون گزینهٔ -n معمولاً از مخزن راه دور هم اطلاعات می‌پرسد؛ اگر شبکه یا دسترسی مشکل داشته باشد ممکن است خطا بدهد. خروجی می‌گوید Git چه تنظیم و چه اطلاعاتی دارد؛ تضمین نمی‌کند push مجاز است یا مخزن راه دور در همین لحظه بی‌خطاست.

The URL is relative because our lab lives on one disk. In real work, you may see HTTPS or SSH. Without -n, git remote show origin normally queries the remote; network or access problems can make it fail. The output describes configuration and known information, not whether pushing is authorized or the remote is currently healthy.

چهار نام شبیه‌اند، اما یکی نیستندFour similar names, four different things

server.git همان مخزن راه دور در آزمایش ماست. تنظیم origin در clone می‌گوید با کدام مقصد حرف بزن. main شاخهٔ محلی توست. origin/main یک اشاره‌گر رهگیریِ مخزن راه دور در مخزن محلی توست: آخرین وضعیت شاخه‌ای که از مخزن راه دور به نام origin دریافت کرده‌ای. این اسم، خودِ شاخهٔ زندهٔ روی سرور نیست.

server.git is our remote repository. The origin configuration tells a clone which destination to contact. main is your local branch. origin/main is a remote-tracking ref in your local repository: the latest state of the branch from origin that you have fetched. It is not the live branch on the server.

نامNameکجا ثبت می‌شود؟Where is it stored?چه معنایی دارد؟What does it mean?
server.gitمخزن جداA separate repositoryشیءها و اشاره‌گرهای مشترک تیم را نگه می‌دارد.Stores shared team objects and refs.
originتنظیمات clone محلیLocal clone configurationنام کوتاه برای URL و refspecهای مقصد.A short name for the destination URL and refspecs.
mainاشاره‌گر محلی refs/heads/mainLocal ref refs/heads/mainشاخه‌ای که ممکن است checkout و روی آن commit بسازی.A branch you may check out and commit on.
origin/mainاشاره‌گر محلی refs/remotes/origin/mainLocal ref refs/remotes/origin/mainآخرین وضعیت دریافتی از main روی origin.The latest fetched state of origin’s main.
upstreamتنظیم شاخه محلیLocal branch configurationمی‌گوید مقایسه و pull پیش‌فرض با کدام اشاره‌گر انجام شود.Names the ref used for comparison and default pull.

fetch خبر را می‌آورد، اما main باب را جلو نمی‌بردFetch brings the news, but does not advance Bob’s main

برای دیدن تفاوت، دقیقاً سه پوشه می‌سازیم: alice/، bob/ و server.git. آلیس مخزن اولیه را می‌سازد و اولین push را انجام می‌دهد؛ بعد باب clone می‌گیرد. مخزن bare محل نگهداری شیءها و اشاره‌گرهای مشترک است و برخلاف clone معمولی پوشه‌ کاری ندارد، اما Git می‌تواند از آن clone بگیرد و به آن push کند.

To see the distinction, create exactly three directories: alice/, bob/, and server.git. Alice creates the initial repository and makes the first push; Bob clones afterward. The bare repository stores shared objects and refs without an ordinary working tree, but Git can clone from it and accept pushes.

bash · create a bare remote and two clones
mkdir git-remote-lab && cd git-remote-lab
git init -b main alice
cd alice
git config user.name 'Nika Example'
git config user.email nika@example.test
printf 'شروع پروژه\n' > README.txt
git add README.txt
git commit -m 'Create project'
cd ..
git init --bare server.git
git --git-dir=server.git symbolic-ref HEAD refs/heads/main
cd alice
git remote add origin ../server.git
git push -u origin main
cd ..
git clone server.git bob

حالا آلیس یک فایل را عوض می‌کند و commit را به مخزن مشترک می‌فرستد. چون clone آلیس از شاخهٔ main گرفته شده و upstream معمول دارد، git push بدون آرگومان در این آزمایش مقصد و شاخه را از تنظیمات می‌فهمد. در یک مخزن دیگر ممکن است این پیش‌فرض‌ها فرق کند؛ بعداً رابطهٔ upstream را جدا می‌بینیم.

Alice now changes a file and sends the commit to the shared repository. Her clone has the usual upstream configured for main, so argument-less git push knows the destination in this lab. Other repositories may have different defaults; we will inspect the upstream relationship shortly.

alice/ · make one commit and push
cd alice
printf 'یادداشت آلیس\n' > alice.txt
git add alice.txt
git commit -m 'Add Alice note'
git push

حالا قبل از اجرای هر فرمان شبکه‌ای در باب، دو شناسه را یادداشت کن. این مقایسه نشان می‌دهد clone باب از آخرین ارتباطش با مخزن راه دور چه می‌داند و شاخه محلی خودش کجاست:

Before Bob runs any network command, record two IDs. This comparison shows what his clone knows from its last contact with the remote and where his local branch points:

bob/ · before fetching
cd ../bob
git status -sb
git rev-parse main
git rev-parse origin/main
git log --oneline --graph --decorate --all

قبل از fetch، معمولاً main و origin/main به یک commit می‌رسند؛ اما هیچ‌کدام commit آلیس را نام نمی‌برند. حالا فقط خبر را بگیر:

Before fetch, main and origin/main usually resolve to the same commit, and neither names Alice’s new commit. Now fetch only the remote state:

bob/ · fetch, then inspect both refs
git fetch origin
git rev-parse main
git rev-parse origin/main
git log --oneline --graph --decorate --all
git status -sb
representative fetch result
From ../server
   <old>..<new>  main  -> origin/main

خط main -> origin/main شاهد اصلی است: Git شیءهای لازم را گرفته و اشاره‌گر رهگیریِ مخزن راه دور باب را جلو برده. شناسه main محلی هنوز همان قبلی است و فایل‌های باب هم عوض نشده‌اند. git fetch معمولاً FETCH_HEAD را هم می‌نویسد. این فرمان خبر را می‌آورد، نه اینکه خودش تصمیم بگیرد تغییر را در شاخه کاری تو ادغام کند.

The line main -> origin/main is the key evidence: Git fetched the required objects and advanced Bob’s remote-tracking ref. Local main still has its old ID, and Bob’s files have not changed. git fetch also normally writes FETCH_HEAD. Fetch brings the news; it does not decide to integrate it into your working branch.

Fetching advances origin/main to a new commit while local main remains at the old commit inside Bob’s local clone · before and after fetch Bold commit CAlice’s commit main stays → B origin/main moves → C fetch updates this local tracking ref working files stay unchanged for now
نمودار ۲ — گره C فقط در clone باب شناخته شده است؛ fetch آن را به origin/main وصل می‌کند. main محلی هنوز روی B است و فایل‌ها هم تا زمان ادغام همان نسخهٔ B را نشان می‌دهند.
Diagram 2 — Bob’s clone now knows commit C; fetch moves origin/main to it. Local main stays at B, and the files still show B until an integration step.

upstream یک رابطهٔ محلی است، نه فرمان مخفی سرورAn upstream is a local relationship, not a server command

باب حالا دو اشاره‌گر را می‌بیند: main خودش و origin/main که fetch تازه به‌روزش کرده. برای اینکه Git بداند مقایسه و pull پیش‌فرض را با کدام اشاره‌گر انجام دهد، شاخه محلی می‌تواند upstream داشته باشد. clone معمولاً این رابطه را از همان اول تنظیم می‌کند.

Bob now sees two refs: his main and origin/main, updated by the last fetch. A local branch can have an upstream so Git knows which ref to compare against and use for a default pull. Clone normally sets this relationship up.

The local main branch has an upstream relationship to the local remote-tracking ref origin/main both refs are stored in Bob’s local repository main refs/heads/main origin/main refs/remotes/origin/main upstream config may differ
نمودار ۳ — خط‌چین رابطهٔ upstream در تنظیمات شاخه محلی است، نه جریان داده. main و origin/main هر دو نام اشاره‌گرهای محلی‌اند و ممکن است به commitهای متفاوت اشاره کنند.
Diagram 3 — The dashed line is an upstream relationship in local branch configuration, not data flow. Both main and origin/main are local refs and may point to different commits.

برای دیدن این رابطه git branch -vv را اجرا کن. کروشهٔ [origin/main] upstream را معرفی می‌کند؛ علامت‌هایی مثل ahead و behind نسبت به آخرین اطلاعاتی‌اند که در clone داری. اگر از آخرین fetch مدت زیادی گذشته باشد، این شمارش نمی‌گوید مخزن راه دور همین حالا چه وضعی دارد.

Run git branch -vv to see the relationship. The bracketed [origin/main] names the upstream; ahead and behind counts use the information currently stored in your clone. If you have not fetched recently, those counts do not tell you the remote’s live state.

representative output
* main  1a2b3c4 [origin/main: behind 1] Create project

اگر شاخه‌ای را از اشاره‌گر رهگیریِ مخزن راه دور بسازی، Git معمولاً upstream را تنظیم می‌کند: git switch -c feature/notes --track origin/feature/notes. اگر شاخه از قبل هست، رابطه را با git branch --set-upstream-to=origin/main main تنظیم می‌کنی. این فرمان تنظیمات مخزن خودت را تغییر می‌دهد، نه چیزی را روی server.

When you create a branch from a remote-tracking ref, Git normally sets its upstream: git switch -c feature/notes --track origin/feature/notes. For an existing branch, use git branch --set-upstream-to=origin/main main. This changes local repository configuration, not the server.

push شاخهٔ تو را به مخزن مشترک می‌فرستدPush sends your branch to the shared repository

باب تغییر آلیس را fetch و بعد از دیدن گراف، با git merge --ff-only origin/main به main خودش آورده است. حالا می‌خواهد یک شاخهٔ تازه برای یادداشت‌ها بسازد و همان شاخه را برای همکارش منتشر کند. قبل از push حدس بزن: آیا مخزن راه دور هم خودبه‌خود همهٔ شاخه‌های محلی باب را دارد؟

Bob fetched Alice’s change, inspected the graph, then used git merge --ff-only origin/main to advance his main. Now he wants to create and publish a new branch for notes. Before pushing, predict: does the remote automatically have every branch in Bob’s clone?

bob/ · explicitly integrate the fetched main first
git switch main
git merge --ff-only origin/main
git status -sb

نه؛ شاخهٔ تازه فقط در clone باب است تا آن را صریحاً push کنی. -u (یا --set-upstream) علاوه بر push موفق، upstream را هم برای شاخهٔ محلی تنظیم می‌کند. از این به بعد ابزارهایی مثل status و فرمان‌های بدون آرگومان می‌توانند آن رابطه را به‌کار ببرند.

No. The new branch exists only in Bob’s clone until he explicitly pushes it. -u (or --set-upstream) sets the local branch’s upstream after a successful push. Commands such as status and some argument-less operations can then use that relationship.

bob/ · publish a new branch
git switch -c feature/notes
printf 'نکتهٔ باب\n' > bob.txt
git add bob.txt
git commit -m 'Add Bob note'
git push -u origin feature/notes
git branch -vv

اگر push موفق باشد، Git روی مخزن راه دور شاخهٔ feature/notes می‌سازد و upstream محلی را به origin/feature/notes پیوند می‌دهد. این خروجی ثابت می‌کند شاخه به مقصد نام‌برده رسیده؛ ثابت نمی‌کند کسی آن را بازبینی کرده یا آمادهٔ ادغام است. بعد از اولین push هم می‌توانی صریح بمانی: git push origin feature/notes.

On success, Git creates feature/notes at the remote and links the local branch’s upstream to origin/feature/notes. This proves the branch reached the named destination; it does not prove anyone reviewed it or that it is ready to merge. You can keep being explicit on later pushes with git push origin feature/notes.

اگر شاخه upstream ندارد، git push ساده ممکن است مقصد را حدس نزند یا طبق تنظیمات تو رفتار متفاوتی داشته باشد. برای اولین انتشار، نام مخزن راه دور و شاخه را بنویس و -u را فقط وقتی می‌خواهی رابطه ثبت شود اضافه کن. شاخهٔ جاری را با git branch --show-current تأیید کن؛ اشتباه‌گرفتن نام شاخه می‌تواند تغییر را به مقصد دیگری بفرستد.

Without an upstream, plain git push may not infer a destination or may follow your configuration. For the first publication, name the remote and branch, adding -u if you want the relationship recorded. Confirm the current branch with git branch --show-current; a wrong branch name can send changes somewhere unintended.

در این فصل فقط شاخهها را منتشر کردیم. tag اشاره‌گر جداگانه‌ای است و انتشارش را می‌توان جداگانه کنترل کرد؛ در فصل ۱۱ سراغ tag و push کردن آن می‌رویم.

This chapter published branches only. Tags are separate refs whose publication can be controlled separately; Chapter 11 covers tags and pushing them.

pull را باز کن: اول fetch، بعد یک انتخاب برای ادغامUnpack pull: fetch first, then choose an integration

آلیس تغییر تازه‌ای push کرده و باب می‌خواهد سریع sync شود. git pull فقط یک اسم دیگر برای fetch نیست؛ اول fetch می‌کند، بعد اشاره‌گر انتخاب‌شده را با شاخه جاری ادغام می‌کند. اینکه ادغام با fast-forward، merge یا rebase انجام شود به گزینه‌ها و تنظیمات Git بستگی دارد.

Alice pushed another change and Bob wants to sync. git pull is not just another name for fetch: it fetches first, then integrates the selected ref into the current branch. Whether integration uses fast-forward, merge, or rebase depends on options and configuration.

Pull fetches remote refs and then integrates a selected upstream using fast-forward, merge, or rebase remoteselected ref fetchobjects + ref integrationchosen behavior fast-forward merge or rebase git pull = fetch + selected integration
نمودار ۵ — pull دو مرحلهٔ مفهومی دارد: دریافت اشاره‌گرها و شیءها، سپس ادغام. گزینه‌هایی مثل --ff-only، --no-rebase و --rebase انتخاب مرحلهٔ دوم را روشن می‌کنند.
Diagram 5 — Pull has two conceptual steps: fetch refs and objects, then integrate. Options such as --ff-only, --no-rebase, and --rebase make the second step explicit.

برای شاخه‌ای که فقط عقب مانده و commit محلی جداگانه ندارد، git pull --ff-only انتخاب محافظه‌کارانه‌ای است: اگر fast-forward ممکن نباشد، متوقف می‌شود تا اول تاریخچه را ببینی. وقتی شاخه‌ها واگرا شده‌اند، می‌توانی بعد از بررسی با git pull --no-rebase merge کنی یا با git pull --rebase commitهای محلی را روی پایهٔ تازه بازپخش کنی. دومی همان ملاحظهٔ فصل ۰۷ را دارد: commitهای محلی را بازنویسی می‌کند.

For a branch that is simply behind with no separate local commits, git pull --ff-only is a conservative choice: it stops if a fast-forward is impossible, giving you a chance to inspect the graph. When histories diverge, you can inspect and then use git pull --no-rebase to merge or git pull --rebase to replay local commits onto the new base. The latter has Chapter 07’s caveat: it rewrites local commits.

فرمانCommandچه می‌کند؟What it doesچه چیزی را خودکار نمی‌کند؟What it does not do by itself
git fetch originشیءها و اشاره‌گرهای رهگیریِ مخزن راه دور مثل origin/main را به‌روز می‌کند.Updates objects and remote-tracking refs such as origin/main.شاخهٔ محلی جاری را ادغام یا جابه‌جا نمی‌کند.Does not integrate or move the current local branch.
git pull --ff-onlyfetch می‌کند و فقط اگر fast-forward ممکن باشد شاخهٔ جاری را جلو می‌برد.Fetches and advances the current branch only when fast-forward is possible.تاریخچهٔ واگرا را خودکار حل نمی‌کند؛ متوقف می‌شود.Does not resolve divergent history; it stops.
git pull --no-rebasefetch می‌کند و از روش merge استفاده می‌کند؛ نتیجه به وضعیت تاریخچه بستگی دارد.Fetches and uses merge integration; the result depends on the history.جای بررسی تعارض یا انتخاب آگاهانهٔ سیاست را نمی‌گیرد.Does not replace conflict review or a deliberate policy choice.
git pull --rebasefetch می‌کند و commitهای محلی را روی پایهٔ تازه بازپخش می‌کند.Fetches and replays local commits on the updated base.شناسهٔ commitهای بازپخش‌شده عوض می‌شود؛ برای تاریخچهٔ مشترک بی‌فکر به‌کار نبر.Replayed commits get new IDs; do not apply casually to shared history.
git pushاشاره‌گرها و شیءهای لازم را به مقصد می‌فرستد، اگر مجاز و امن باشد.Sends refs and required objects when authorized and safely fast-forwardable.پوشه‌ کاری cloneهای دیگر را به‌روز نمی‌کند.Does not update other clones’ working trees.
pull را بدون دیدن تنظیمات، یک رفتار قطعی فرض نکنDo not assume pull has one fixed integration behavior

گزینهٔ فرمان و تنظیم‌هایی مثل pull.rebase و pull.ff روی ادغام اثر دارند؛ در شاخهٔ واگرا، نسخه‌ها و تنظیم‌ها ممکن است رفتار متفاوت یا درخواست انتخاب صریح نشان دهند. برای یادگیری، fetch و ادغام را جدا اجرا کن. اگر از pull استفاده می‌کنی، گزینهٔ مورد نظر را در فرمان بنویس تا منظور روشن باشد.

Command-line options and settings such as pull.rebase and pull.ff affect integration. For divergent branches, versions and configuration may behave differently or ask for an explicit choice. While learning, run fetch and integration separately. When using pull, name the intended option so the choice is visible.

push باب رد شد؛ این شکست نیست، یک هشدار محافظتی استBob’s push was rejected; that is protection, not a crash

باب و آلیس هر دو از یک commit شروع کرده‌اند. آلیس commit خودش را push می‌کند؛ باب هم قبل از fetch یک commit مستقل می‌سازد. وقتی باب push می‌زند، نوک مخزن راه دور دیگر نیاکان commit او نیست. Git معمولاً push عادی را رد می‌کند تا commit آلیس از شاخهٔ مشترک کنار نرود.

Alice and Bob started from one commit. Alice pushes her change while Bob creates an independent commit before fetching. When Bob pushes, the remote tip is no longer an ancestor of his commit. Git normally rejects the push so Alice’s commit is not displaced from the shared branch.

Alice and Bob have diverged from a shared base; Bob's non-fast-forward push is rejected Bshared base A Bob’s B′ remote/main → A rejected
نمودار ۴ — A و B′ فرزندهای جداگانهٔ B هستند؛ B′ از A نیامده، پس push باب fast-forward نیست. فلش‌چین درخواست ردشده را نشان می‌دهد، نه تغییری روی مخزن راه دور.
Diagram 4 — A and B′ are separate children of B; B′ does not descend from A, so Bob’s push is not a fast-forward. The dashed arrow marks a rejected request, not a remote update.

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

Instead of forcing, fetch first and inspect the graph and commits. Then combine both lines in a new history; the team’s policy determines whether to merge or rebase. Since Bob’s commit is still private in this exercise, you can practice rebase; coordinate before rewriting a shared branch. Test after integration, then retry a normal push.

در دستورهای آزمایش، باب را از شاخهٔ feature/notes به main برمی‌گردانیم. این commit آزمایشی روی شاخهٔ خودش می‌ماند؛ اینجا فقط main را برای ساختن واگرایی تازه بررسی می‌کنیم.

In the experiment, Bob switches from feature/notes back to main. The test commit remains on its own branch; here we use main to create a separate divergence scenario.

a safe investigation sequence
git fetch origin
git status -sb
git log --oneline --graph --decorate --all
git show --stat HEAD
git show --stat origin/main

اگر بعد از fetch دیدی دو شاخه واگرا هستند، حالا انتخاب روشن است. برای merge کنترل‌شده: git merge origin/main و حل هر تعارض؛ برای بازپخش commitهای خصوصی باب: git rebase origin/main. بعد از هرکدام، تست و log را بررسی کن و push معمولی بزن. --force راهی برای ردشدن از این بررسی نیست.

If the fetch shows divergence, the choice is now informed. For a controlled merge, use git merge origin/main and resolve any conflicts; to replay Bob’s private commits, use git rebase origin/main. After either, test, inspect the log, then make a normal push. --force is not a substitute for this investigation.

reproduce a rejection · run each labeled part in that clone
# from the lab parent, enter alice/ · both clones currently share the same main
cd alice
printf 'new work from Alice\n' >> alice.txt
git add alice.txt
git commit -m 'Add Alice update'
git push origin main

# from alice/, switch to Bob's sibling clone before fetching
cd ../bob
git switch main
printf 'new work from Bob\n' >> bob.txt
git add bob.txt
git commit -m 'Add Bob update'
git push origin main

# bob/ · the push is rejected; now inspect and integrate
git fetch origin
git log --oneline --graph --decorate --all
git merge origin/main
git show --stat --oneline HEAD
git push origin main

# from bob/, return to alice/ to learn about Bob's accepted commit
cd ../alice
git fetch origin
git log --oneline --graph --decorate --all
git merge --ff-only origin/main

در این آزمایش، اولین push آلیس موفق می‌شود. push باب باید رد شود، چون commit او از commit تازهٔ مخزن راه دور فرزند نشده. بعد از fetch، هر دو فایل را جداگانه می‌بینی؛ merge آن دو مسیر را وصل می‌کند، سپس push باب مخزن راه دور را به‌صورت fast-forward جلو می‌برد. در پایان آلیس باید fetch کند تا از commit باب باخبر شود.

In this experiment, Alice’s first push succeeds. Bob’s push should be rejected because his commit is not a descendant of the remote’s new commit. After fetching, both files are present; merge joins the lines, then Bob’s push fast-forwards the remote. Alice must fetch afterward to learn about Bob’s commit.

force-with-lease حصار است؛ مجوز بی‌فکری نیستforce-with-lease is a guard, not permission to stop thinking

فصل ۰۷ mechanism بازنویسی را نشان داد: بعد از rebase، commitهای تازه شناسههای تازه دارند. اگر تاریخچه را پیش‌تر push کرده‌ای، push عادی آن جایگزینی را رد می‌کند. --force-with-lease فقط در یک شاخهٔ دورریختنی کمک می‌کند بفهمی مخزن راه دور از زمانی که آخرین بار وضعیتش را گرفتی جلو رفته یا نه.

Chapter 07 showed the rewrite mechanism: rebasing creates new commit IDs. If you already pushed the old history, a normal push rejects its replacement. On a disposable branch, --force-with-lease helps detect whether the remote moved since your last recorded view.

آزمایش را فقط در cloneهای تازه و دورریختنی همین درس انجام بده. آلیس شاخهٔ آزمایشی می‌سازد و push می‌کند؛ باب آن را fetch می‌کند، commit را با پیام تازه amend می‌کند، اما هنوز fetch دیگری نمی‌زند. آلیس روی commit اصلی یک commit جدید push می‌کند. حالا اشاره‌گری که باب آخرین بار از مخزن راه دور دیده با اشاره‌گر واقعی فرق دارد:

Run this only in fresh, disposable clones for this lesson. Alice creates and pushes a test branch. Bob fetches it, amends the commit message, and does not fetch again. Alice then pushes another commit on top of the original. Bob’s last-seen remote ref now differs from the actual remote:

alice/ · create a disposable branch
git switch -c scratch/lease main
printf 'lease test base\n' > lease.txt
git add lease.txt
git commit -m 'Create lease test'
git push -u origin scratch/lease
bob/ · fetch, create branch, then rewrite locally
git fetch origin
git switch -c scratch/lease --track origin/scratch/lease
git commit --amend -m 'Rewrite disposable lease commit'
alice/ · advance the remote after Bob's fetch
git switch scratch/lease
printf 'Alice advanced the remote\n' > alice-lease.txt
git add alice-lease.txt
git commit -m 'Advance lease test'
git push origin scratch/lease

حالا Bob می‌تواند گراف محلی خودش را ببیند و lease را امتحان کند:

Bob can now inspect his local graph and test the lease:

bob/ · disposable branch only
git log --oneline --graph --decorate --all
git push --force-with-lease origin scratch/lease

push باید رد شود، چون origin/scratch/lease در clone باب هنوز به commitی اشاره می‌کند که قبل از push تازهٔ آلیس دیده بود. این شاهد، حفاظ lease را در این تنظیم معمول نشان می‌دهد؛ برای تنظیم‌های fetch پس‌زمینهٔ غیرمعمول تضمین مطلق نیست. اگر خروجی unexpectedly موفق شد، ادامه نده: این مخزن فقط آزمایشی است، وضعیت مخزن راه دور را بررسی کن و فهم علت را به‌جای تکرار فرمان در اولویت بگذار.

The push should be rejected because Bob’s local origin/scratch/lease still names the commit he saw before Alice’s latest push. This demonstrates the lease guard in the usual setup; it is not an absolute guarantee under unusual background-fetch configurations. If the push unexpectedly succeeds, stop: this repository is disposable. Inspect the remote and understand why instead of repeating the command.

اگر lease رد شد، فوراً fetch نکن که فقط اشاره‌گر رهگیریِ مخزن راه دور را به‌روز کنی و بعد همان force را تکرار کنی؛ ممکن است آن‌وقت lease با مقدار تازه موافق شود، درحالی‌که تو هنوز تغییر آلیس را وارد نکرده‌ای. اول وضعیت واقعی مخزن راه دور را بشناس، تغییرها را ادغام کن و با هم‌تیمی هماهنگ شو. lease از حدس کهنه محافظت می‌کند، نه از تصمیم آگاهانهٔ اشتباه یا همهٔ تنظیم‌های غیرمعمول fetch در پس‌زمینه.

If the lease is rejected, do not simply fetch to refresh the tracking ref and retry the same force; the lease may then match even though you have not integrated Alice’s change. First inspect the remote state, integrate the work, and coordinate with your teammate. A lease guards against a stale assumption, not an informed but wrong choice or every unusual background-fetch setup.

شاخهٔ مخزن راه دور حذف شده، اما ردّش در clone تو ماندهThe remote branch is gone, but your clone still remembers it

آلیس شاخهٔ feature/notes را که باب push کرده از مخزن راه دور حذف می‌کند. باب قبلاً آن را fetch کرده و هنوز در git branch -r نام origin/feature/notes را می‌بیند. آیا حذف مخزن راه دور شکست خورده؟ نه؛ باب فقط هنوز خبر حذف را نگرفته است.

Alice deletes the feature/notes branch Bob pushed. Bob fetched it earlier and still sees origin/feature/notes in git branch -r. Did the remote deletion fail? No; Bob has not fetched the deletion yet.

این بار باب قبل از حذف، اشاره‌گر را fetch می‌کند تا واقعاً نسخهٔ رهگیریِ مخزن راه دور داشته باشد. بعد از حذف، fetch معمولی ممکن است اشاره‌گر stale را نگه دارد؛ برای پاک‌کردن نام قدیمی از git fetch --prune origin استفاده کن. این پاک‌سازی، شاخه محلی feature/notes را حذف نمی‌کند و فرمان حذف شاخه روی سرور هم نیست؛ فقط نام محلیِ ازمدافتاده را تمیز می‌کند.

Bob fetches the ref before deletion so he has a remote-tracking copy. After deletion, a normal fetch may retain the stale name; use git fetch --prune origin to clean it up. This does not delete Bob’s local feature/notes branch and does not issue a deletion to the server; it removes only the stale local tracking name.

alice deletes remote ref · bob prunes stale knowledge
# from alice/, switch to bob/ and record the remote branch
cd ../bob
git fetch origin
git branch -r

# switch back to alice/ and delete the published branch
cd ../alice
git push origin --delete feature/notes

# return to bob/, confirm it is stale, then prune
cd ../bob
git branch -r
git fetch --prune origin
git branch -r
git branch --list feature/notes

اگر Git نام شاخه را اشتباه می‌گیرد، قبل از prune می‌توانی git remote prune --dry-run origin را اجرا کنی تا ببیند چه اشاره‌گرهایی را stale می‌داند، بدون اینکه پاکشان کند. نکتهٔ پیشرفته: prune بر اساس refspec تنظیم‌شده عمل می‌کند؛ اگر tagها را با refspec ویژه در فضای نام مشترک fetch کرده‌ای، prune ممکن است tagهای محلی بیشتری از انتظار حذف کند. با --prune-tags بی‌فکر بازی نکن.

If you are unsure what Git considers stale, run git remote prune --dry-run origin first; it reports candidates without deleting them. Advanced note: pruning follows configured refspecs. If tags are explicitly fetched into a shared namespace, pruning may remove more local tags than expected. Do not add --prune-tags casually.

حالا همین مدل را روی GitHub ببینNow map the same model onto GitHub

آزمایش bare مخزن آفلاین بود، اما مدل عوض نمی‌شود: GitHub یا GitLab میزبانیِ مخزن راه دور است؛ clone تو همچنان اشاره‌گرها محلی و تنظیمات مخزن راه دور خودش را دارد. اسم origin هنوز قراردادی است و می‌توانی مخزن راه دور دیگری مثل upstream هم داشته باشی.

The bare-repository lab was offline, but the model is unchanged: GitHub or GitLab hosts a remote; your clone still has its own local refs and remote configuration. origin remains conventional, and you can add another remote such as upstream.

برای افزودن URL، اول مطمئن شو در پوشهٔ مخزن درست هستی و مخزن راه دور فعلی را بررسی کن. اگر origin از قبل وجود دارد، به‌جای تکرار add، همان URL را با git remote set-url اصلاح کن یا مخزن راه دور دوم را با نام مناسب اضافه کن.

Before adding a URL, confirm you are in the right repository and inspect existing remotes. If origin already exists, update it with git remote set-url or add a second remote under a suitable name instead of repeating add.

hosted remote · replace placeholders with your repository URL
git remote add origin https://github.com/OWNER/REPOSITORY.git
git remote -v
git fetch origin

برای GitHub، HTTPS برای عملیات نوشتن به روش احراز هویت پشتیبانی‌شده نیاز دارد؛ رمز حساب را در prompt گیت وارد نکن. از روش فعلی مورد تأیید GitHub مثل اعتبارنامه helper یا SSH key استفاده کن و secret را در URL، تاریخچهٔ shell، فایل پروژه یا screenshot نگذار. موفقیت fetch فقط دسترسی خواندن را نشان می‌دهد؛ برای push ممکن است مجوز نوشتن جدا لازم باشد.

GitHub requires a supported authentication method for HTTPS writes; do not use your account password at Git’s prompt. Use a currently supported method such as a credential helper or SSH key, and keep secrets out of URLs, shell history, project files, and screenshots. A successful fetch proves read access only; pushing may require separate write permission.

اگر origin از قبل هست، این‌ها شواهد مفیدند: git remote -v برای URL تنظیم‌شده، git remote show origin برای شاخه‌ها و وضعیت قابل‌مشاهده، و git ls-remote origin برای اشاره‌گرهایی که مخزن راه دور اکنون اعلام می‌کند. هیچ‌کدام به‌تنهایی نشان نمی‌دهد کدام commit در فایل‌های پوشه‌ کاری تو checkout شده است.

If origin already exists, useful checks include git remote -v for configured URLs, git remote show origin for branches and observed status, and git ls-remote origin for refs the remote currently advertises. None alone tells you which commit is checked out in your working tree.

وقتی حرف‌زدن با مخزن راه دور به مشکل می‌خوردWhen communication with a remote goes wrong

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

Read an error as a clue, not an invitation to try random flags. First identify whether the problem is the URL, authentication, write permission, refspec, or divergent history.

origin از قبل وجود دارد

origin already exists

با git remote -v ببین origin الان به کجا می‌رود. اگر فقط URL اشتباه است، بعد از تأیید مقصد از git remote set-url origin URL استفاده کن. حذف و افزودن دوباره معمولاً لازم نیست و ممکن است اطلاعات مفید تنظیمات را پاک کند.

Inspect the current destination with git remote -v. If only the URL is wrong, verify the intended target and use git remote set-url origin URL. Removing and recreating the remote is usually unnecessary and may discard useful configuration.

authentication failed یا مجوز رد شد

Authentication failed or permission denied

اول URL و نوع پروتکل را ببین؛ بعد روش احراز هویت همان میزبان را بررسی کن. fetch موفق و push ناموفق می‌تواند یعنی خواندن مجاز است اما نوشتن نه. token را چاپ نکن و برای حل خطا --force نزن؛ force مجوز دسترسی نمی‌سازد.

Check the URL and protocol first, then the host’s authentication method. A successful fetch with a failed push may mean read access exists but write access does not. Do not print tokens or use --force; force does not grant permission.

push با non-fast-forward رد شد

Push was rejected as non-fast-forward

مخزن راه دور از پایهٔ commit تو جلوتر است یا تاریخچه‌ات بازنویسی شده. git fetch بزن، گراف دو مسیر را ببین، commitهای هر طرف را بخوان، بعد merge یا rebase را آگاهانه انتخاب کن. تست و push عادی را تکرار کن؛ force کور ممکن است کار همکار را کنار بزند.

The remote is ahead of your base, or your history was rewritten. Fetch, inspect both lines, review each side’s commits, then choose merge or rebase deliberately. Test and retry a normal push; blind force can displace a teammate’s work.

fetch کردم؛ چرا فایل تغییر نکرد؟

I fetched; why did no file change?

fetch شیءها و اشاره‌گر رهگیریِ مخزن‌های راه دور را به‌روز می‌کند؛ پوشه‌ کاری و main محلی را ادغام نمی‌کند. git status -sb و git log --graph --oneline --decorate --all را ببین، بعد در صورت نیاز ادغام صریح انجام بده.

fetch updates objects and remote-tracking refs; it does not integrate into local main or the working tree. Inspect git status -sb and the graph, then choose an explicit integration if needed.

HEAD جداشده است و می‌خواهی push کنی

HEAD is detached and you want to push

اول با git status و git branch --show-current تأیید کن HEAD به شاخه اشاره نمی‌کند. commit ممکن است وجود داشته باشد ولی نام شاخه نداری که منتشرش کنی. اگر commit باید بماند، یک شاخه بساز و آن را checkout کن، سپس push را با مقصد صریح انجام بده؛ تا قبل از آن چیزی را پاک نکن.

Confirm the detached state with git status and git branch --show-current. The commit may exist without a branch name to publish. If it should be kept, create a branch at it, switch to that branch, then push explicitly; do not delete anything first.

مخزن راه دور شاخه حذف شده اما هنوز در شاخه -r هست

A deleted remote branch still appears in branch -r

این فهرست آخرین اشاره‌گر رهگیریِ مخزن‌های راه دوری ثبت‌شده در clone توست. git fetch --prune origin آن اشاره‌گرهای منطبق را تمیز می‌کند؛ شاخه محلی هم‌نام را جداگانه بررسی کن، چون prune آن را حذف نمی‌کند.

The list shows remote-tracking refs last recorded in your clone. git fetch --prune origin cleans matching stale refs; inspect a same-named local branch separately because pruning does not delete it.

تمرین‌ها: از اشاره‌گر محلی تا تصمیم دوهم‌تیمیExercises: from local refs to two-developer decisions

در هر تمرین سه نقطه را جدا نگه دار: شاخهٔ محلی، اشاره‌گر رهگیری مخزن راه دور و شاخهٔ واقعی روی مخزن مشترک. اگر این سه را یکی فرض کنی، fetch و push شبیه رفتارهای تصادفی به نظر می‌رسند؛ اگر جداشان کنی، جواب تقریباً همیشه از روی گراف پیدا می‌شود.

Before opening each solution, name the clone and the ref that changes. “I update the remote” is not precise enough; identify the ref and what remains unchanged.

۱۸ تمرین حل‌شده · پیش‌بینی، عیب‌یابی و همکاری18 solved exercises · prediction, debugging, and collaboration
۰۱مدل پایه / Basic model

آلیس commit می‌سازد ولی هنوز push نکرده. باب git fetch origin می‌زند. آیا commit آلیس حالا در اشاره‌گر رهگیریِ مخزن راه دور باب هست؟

Alice makes a commit but has not pushed it. Bob runs git fetch origin. Is Alice’s commit now in Bob’s remote-tracking ref?

راهنماییHint

مخزن راه دور هنوز از commit آلیس خبر ندارد.

The remote does not know Alice’s commit yet.

پاسخ و دلیلReasoning

نه. fetch باب از مخزن راه دور می‌خواند و commit محلی push‌نشدهٔ آلیس در آن نیست. آلیس باید اول به مخزن راه دور push کند؛ بعد باب می‌تواند آن را fetch کند.

No. Bob fetches from the remote, which does not contain Alice’s unpushed local commit. Alice must push first; then Bob can fetch it.

۰۲چه چیزی حرکت کرد؟ / What moved?

قبل از fetch، main و origin/main هر دو به X می‌رسند. بعد از fetch، origin/main به Y رفته ولی main هنوز X است. چه اتفاقی افتاد؟

Before fetch, main and origin/main both point to X. Afterward, origin/main points to Y while main stays at X. What happened?

راهنماییHint

یک اشاره‌گر دانش محلی از مخزن راه دور است؛ دیگری شاخه کاری.

One ref records remote state locally; the other is the working branch.

بیا بازش کنیمInterpretation

fetch اشاره‌گر محلی origin/main را به Y جلو برد، اما شاخه محلی main و پوشه‌ کاری را ادغام نکرد. برای واردکردن Y باید بعد از بررسی، مثلاً git merge --ff-only origin/main را اجرا کنی.

Fetch advanced the local origin/main ref to Y but did not integrate it into local main or the working tree. After inspection, you can integrate it with a command such as git merge --ff-only origin/main.

۰۳origin / Convention

آیا Git اجازه می‌دهد نام مخزن راه دور تو upstream باشد و نه origin؟

Can your remote be named upstream instead of origin?

راهنماییHint

فکر کن origin از کجا به تنظیمات آمد.

Consider where origin came from in the first place.

راه‌حل و توضیحAnswer

بله. origin فقط نام قراردادی‌ای است که clone معمولاً می‌سازد. می‌توانی نام‌های دیگری به‌کار ببری یا مخزن راه دور دوم اضافه کنی؛ دستوراتت باید نام درست همان مخزن را بنویسند.

Yes. origin is the conventional name clone normally creates. You may use another name or add another remote; commands must use the correct name for that repository.

۰۴خواندن remote -v / Read remote -v

git remote -v یک URL برای fetch و URL دیگری برای push نشان می‌دهد. این ناممکن است؟

git remote -v shows one URL for fetch and another for push. Is that impossible?

راهنماییHint

تنظیم fetch و push می‌تواند جدا باشد، اما مقصدها باید آگاهانه انتخاب شده باشند.

Fetch and push URLs can differ, but the destinations must be intentional.

چرا این جواب درست استExplanation

ممکن است، اما دلیل و تنظیمش را بررسی کن. اگر fetch از یک upstream و push به مخزن انتشار جداست، اغلب دو مخزن راه دور با نام‌های جدا شفاف‌ترند. آدرس‌ها را افشا نکن اگر token در URL باشد.

It is possible, but inspect why it is configured. If you fetch from an upstream and publish elsewhere, separate named remotes are often clearer. Do not expose URLs containing tokens.

۰۵upstream / Tracking

در خروجی git branch -vv کنار main نوشته [origin/main: behind 2]. آیا این یعنی مخزن راه دور همین الآن دو commit جلوتر است؟

git branch -vv shows [origin/main: behind 2] next to main. Does this mean the live remote is exactly two commits ahead right now?

راهنماییHint

آخرین fetch چه زمانی بوده؟

When was the last fetch?

بررسی جوابEvidence limit

این می‌گوید بر اساس اشاره‌گر محلی origin/main که آخرین بار fetch شده، main دو commit عقب است؛ اگر از آن زمان مخزن راه دور جلو رفته باشد، شمارش stale است. برای وضعیت تازه، fetch کن و دوباره شاخه -vv را ببین.

It says main is two commits behind the locally stored origin/main from the last fetch. If the remote moved since then, the count is stale. Fetch and inspect again for a fresher view.

۰۶اولین push / First push

شاخهٔ feature/help محلی را تازه ساخته‌ای و می‌خواهی آن را منتشر کنی و upstream هم تنظیم شود. فرمان؟

You created local feature/help and want to publish it while setting its upstream. What command?

راهنماییHint

برای push اول، مقصد و نام شاخه را صریح بنویس.

Name the destination and branch explicitly on the first push.

پاسخ پیشنهادیCommand

git push -u origin feature/help. این شاخه را به مقصد origin می‌فرستد و رابطهٔ upstream را در تنظیم محلی می‌نویسد. بعد git branch -vv را ببین تا رابطه را تأیید کنی.

git push -u origin feature/help. This sends the branch to origin and records the upstream locally. Verify the relationship afterward with git branch -vv.

تا اینجا فقط خبر تازه را از مخزن راه دور گرفته‌ایم یا به آن فرستاده‌ایم. از اینجا تمرین‌ها عمداً شاخهٔ محلی و origin/main را از هم دور می‌کنند تا مجبور شوی دقیق بگویی هر اسم کجاست.

So far we have mainly fetched from or pushed to the remote. From here the exercises deliberately separate the local branch from origin/main so you must say exactly where each name points.

۰۷pull یا fetch؟ / Pull or fetch?

می‌خواهی اول گراف تغییرهای مخزن راه دور را ببینی و بعد تصمیم بگیری merge کنی یا rebase. کدام شروع مناسب‌تر است؟

You want to inspect the remote graph before deciding whether to merge or rebase. Which is the better first step?

راهنماییHint

کدام فرمان خبر را می‌گیرد ولی ادغام را انتخاب نمی‌کند؟

Which command fetches news without choosing integration?

پاسخ و دلیلDecision

git fetch origin بزن، سپس git log --graph --oneline --decorate --all را بخوان. بعد ادغام آگاهانه انتخاب کن. pull ممکن است همان لحظه مرحلهٔ ادغام را هم بر اساس گزینه یا تنظیمات انجام دهد.

Run git fetch origin, then inspect git log --graph --oneline --decorate --all. Choose integration afterward. Pull may immediately perform the integration step according to options or configuration.

۰۸تنظیم pull / Pull configuration

در دو دستگاه، اجرای git pull برای یک شاخهٔ واگرا یکی merge commit می‌سازد و دیگری خطای نیاز به انتخاب strategy می‌دهد. آیا الزاماً یکی Git خراب دارد؟

On two machines, git pull on a divergent branch creates a merge commit on one and asks for a strategy on the other. Must one Git installation be broken?

راهنماییHint

تنظیمات pull می‌تواند متفاوت باشد.

Pull configuration can differ.

بیا بازش کنیمExplanation

نه. گزینه‌ها و تنظیم‌هایی مثل pull.rebase و pull.ff رفتار ادغام را عوض می‌کنند و نسخه‌ها هم ممکن است راهنمای متفاوتی بدهند. برای رفتار قابل‌پیش‌بینی از گزینهٔ صریح مثل --ff-only، --no-rebase یا --rebase استفاده و پیام تنظیمات را بخوان.

No. Options and settings such as pull.rebase and pull.ff affect integration, and versions may guide you differently. Use an explicit option such as --ff-only, --no-rebase, or --rebase, and inspect the configuration message.

۰۹fast-forward / Push safety

مخزن راه دور روی A است و تو می‌خواهی شاخه را به B ببری. Git می‌گوید B نیاکان A نیست. چرا push را رد می‌کند؟

The remote is at A and you want to move the branch to B. Git says B is not a descendant of A. Why reject the push?

راهنماییHint

ممکن است commitهای A با جابه‌جایی اشاره‌گر از دسترس شاخه خارج شوند.

Moving the ref might make commits from A disappear from that branch.

راه‌حل و توضیحProtection

این به‌روزرسانی non-fast-forward است؛ B شامل A نیست. push عادی از کنارزدن تاریخچهٔ مخزن راه دور جلوگیری می‌کند. fetch کن، گراف و commitهای هر دو طرف را ببین، merge/rebase کن، تست بگیر و push عادی بزن.

This is a non-fast-forward update; B does not contain A. A normal push prevents replacing remote history. Fetch, inspect both lines and commits, integrate, test, then push normally.

۱۰remote add خطا داد / Remote already exists

بعد از git remote add origin URL پیام remote origin already exists می‌گیری. اولین بررسی چیست؟

After git remote add origin URL, Git says the remote already exists. What do you check first?

راهنماییHint

شاید origin الان تنظیم شده باشد.

Origin may already be configured.

چرا این جواب درست استCheck

git remote -v را اجرا و مقصد موجود را بخوان. اگر URL مقصد اشتباه است، بعد از تأیید از git remote set-url origin URL استفاده کن؛ اگر به دو مخزن نیاز داری، مخزن راه دور دوم با نام متفاوت بساز.

Run git remote -v and inspect the existing destination. If it is wrong, verify the intended URL and use git remote set-url origin URL; if you need two repositories, add the second under another name.

۱۱حذف branch دور / Delete a remote branch

برای حذف شاخه آزمایشی feature/notes روی مخزن راه دور، چه فرمانی می‌زنی؟ آیا شاخه محلی باب هم حذف می‌شود؟

How do you delete the test branch feature/notes on the remote? Does Bob’s local branch disappear too?

راهنماییHint

حذف مخزن راه دور و prune دو کار جدا هستند.

Remote deletion and pruning are separate operations.

بررسی جوابPrecise answer

git push origin --delete feature/notes اشاره‌گر مخزن راه دور را حذف می‌کند. clone باب تا fetch/prune بعدی ممکن است origin/feature/notes قدیمی را داشته باشد؛ شاخه محلی feature/notes هم با این فرمان حذف نمی‌شود.

git push origin --delete feature/notes deletes the remote ref. Bob may retain stale origin/feature/notes until fetch/prune, and the command does not delete his local feature/notes branch.

۱۲prune / Clean stale refs

پس از git fetch --prune origin، origin/feature/notes حذف شده ولی git branch --list feature/notes هنوز شاخه محلی را نشان می‌دهد. چه برداشتی درست است؟

After git fetch --prune origin, origin/feature/notes is gone but git branch --list feature/notes still shows a local branch. What is the right interpretation?

راهنماییHint

این دو اشاره‌گر فضای نام متفاوت دارند.

The refs live in different namespaces.

پاسخ پیشنهادیExplanation

prune فقط اشاره‌گر رهگیریِ مخزن راه دور قدیمی را پاک کرده؛ شاخه محلی مستقل است و باقی می‌ماند. اگر آن شاخه را هم نمی‌خواهی، قبل از حذف بررسی کن commitهایش merge یا محفوظ شده‌اند؛ prune خودش مجوز حذف کار محلی نیست.

Pruning removed only the stale remote-tracking ref; the independent local branch remains. Before deleting it, check whether its commits are merged or preserved. Prune is not permission to delete local work.

در تمرین‌های آخر push رد می‌شود یا تاریخچه‌ها از هم فاصله می‌گیرند. رد شدن push دشمن تو نیست؛ Git دارد می‌گوید قبل از نوشتن روی تاریخ مشترک، چیزی هست که هنوز ندیده‌ای.

In the final exercises, pushes are rejected or histories diverge. A rejected push is not the enemy; Git is telling you there is shared history you have not accounted for yet.

۱۳detached HEAD / Detached checkout

روی commit مشخصی در جداشده HEAD هستی و git push پاسخ روشنی نمی‌دهد. commit قطعاً گم شده؟

You are at a commit in detached HEAD and git push does not behave as expected. Is the commit definitely lost?

راهنماییHint

commit شیء و نام شاخه یکی نیستند.

A commit object and a branch name are different things.

پاسخ و دلیلAnalysis and next step

نه؛ جداشده HEAD یعنی HEAD به شاخه نام‌دار وصل نیست، نه اینکه commit وجود ندارد. git status و git log را بررسی کن. اگر باید نگهش داری، git switch -c save-this-work را از همان commit بساز، بعد شاخه را با مقصد صریح push کن.

No. Detached HEAD means HEAD is not attached to a named branch, not that the commit is absent. Inspect status and log. If you need to keep it, create a branch at that commit with git switch -c save-this-work, then push it explicitly.

۱۴lease رد شد / Lease rejected

push با --force-with-lease رد شده است. آیا بهتر است fetch کنی و همان فرمان را دوباره بزنی؟

A push with --force-with-lease was rejected. Should you fetch and immediately repeat the command?

راهنماییHint

fetch می‌تواند اشاره‌گر مورد استفادهٔ lease را به‌روز کند.

Fetching can update the ref the lease uses as its expectation.

بیا بازش کنیمSafe decision

نه، نه بدون بررسی. ردشدن یعنی وضعیت مخزن راه دور با انتظار ثبت‌شده فرق دارد. اول از هم‌تیمی یا از log/remote بپرس چه commitی اضافه شده، آن را ادغام و تست کن؛ فقط بعد از هماهنگی دربارهٔ بازنویسی تصمیم بگیر.

Not without inspection. Rejection means the remote differs from the recorded expectation. Find out what commit arrived, integrate and test it, and decide about rewriting only after coordination.

۱۵احراز هویت / Authentication

fetch از مخزن خصوصی GitHub موفق است اما push با مجوز رد شد رد می‌شود. آیا اعتبارنامه اشتباه تنها احتمال است؟

Fetching a private GitHub repository works, but push fails with permission denied. Is a bad credential the only possibility?

راهنماییHint

خواندن و نوشتن دو مجوز جدا هستند.

Read and write access are distinct permissions.

راه‌حل و توضیحDiagnosis

نه. ممکن است احراز هویت کار کند اما حساب/کلید مجوز نوشتن نداشته باشد، شاخه محافظت شده باشد یا URL push متفاوت باشد. git remote -v را بدون افشای راز بررسی کن و مجوز همان میزبان را جداگانه تأیید کن؛ force این محدودیت را رفع نمی‌کند.

No. Authentication may work while the account/key lacks write permission, the branch is protected, or the push URL differs. Inspect remotes without exposing secrets and verify host permissions separately; force does not bypass these controls.

۱۶تغییر نرفته / Change did not travel

آلیس push موفق می‌بیند؛ باب fetch می‌کند اما origin/main تکان نمی‌خورد. سه بررسی تشخیصی چیست؟

Alice sees a successful push, but Bob fetches and origin/main does not move. Name three diagnostic checks.

راهنماییHint

آیا هر دو به همان مخزن راه دور و همان شاخه نگاه می‌کنند؟

Are both looking at the same remote and branch?

چرا این جواب درست استInvestigation plan

۱) در هر clone git remote -v را مقایسه کن. ۲) روی آلیس git branch --show-current و خروجی push را بررسی کن تا شاخه مقصد روشن شود. ۳) در باب git fetch origin، git branch -r و git rev-parse origin/main را ببین؛ شاید commit به شاخه دیگری یا مخزن راه دور دیگری رفته باشد.

1) Compare git remote -v in both clones. 2) On Alice’s side, inspect the current branch and push result to identify the destination ref. 3) On Bob’s side, fetch origin and inspect remote branches and origin/main; the commit may have gone to another branch or remote.

۱۷تصمیم integration / Integration decision

شاخهٔ محلی بدون تغییر است و فقط از origin/main عقب مانده؛ commit محلی جدا ندارد. کدام pull انتخاب کمترین بازنویسی/اتصال اضافه را دارد؟

The local branch is clean and only behind origin/main, with no separate local commit. Which pull choice makes the fewest history changes?

راهنماییHint

اگر رابطهٔ والدها اجازه دهد، اشاره‌گر را مستقیم جلو ببر.

If ancestry allows it, advance the ref directly.

بررسی جوابConservative choice

git pull --ff-only فقط در صورت fast-forward جلو می‌رود و اگر پیش‌بینی غلط باشد متوقف می‌شود. این گزینه تاریخچه واگرا را حل نمی‌کند؛ توقفش فرصتی است برای fetch و بررسی گراف، نه نشانهٔ خرابی.

git pull --ff-only advances only when a fast-forward is possible and stops otherwise. It does not resolve divergence; a stop is a chance to fetch and inspect, not proof of corruption.

۱۸پروندهٔ دو clone / Two-clone incident

Bob از آلیس commit می‌گیرد، تغییر محلی مستقل می‌سازد، push رد می‌شود، و مخزن راه دور شاخه هم‌نام حذف شده است. قبل از هر ادغام چه ترتیب کار معقولی داری؟

Bob receives Alice’s commit, creates an independent local change, gets a rejected push, and a same-named remote branch was deleted. What is a sensible sequence before integration?

راهنماییHint

ابتدا خبر تازه و گراف کامل را بگیر؛ حذف stale اشاره‌گر کار ادغام نیست.

Fetch fresh information and inspect the whole graph first; pruning is not integration.

پاسخ پیشنهادیAnalysis

۱) git fetch origin را اجرا و اشاره‌گرها را تازه کن. ۲) git log --graph --decorate --oneline --all و git branch -vv را بخوان تا commitهای هر طرف و upstream معلوم شوند. ۳) شاخهٔ حذف‌شده را با git fetch --prune از فهرست رهگیریِ مخزن راه دور تمیز کن، اما شاخه محلی را نگه دار تا محتوایش بررسی شود. سپس merge/rebase را با توجه به مالکیت commitها انتخاب، تست، و push عادی کن.

1) Fetch origin to refresh refs. 2) Inspect the graph and branch -vv to identify each side’s commits and upstreams. 3) Prune stale remote-tracking refs while retaining the local branch for review. Then choose merge or rebase based on commit ownership, test, and push normally.

پروژهٔ کوچک: دو توسعه‌دهنده، یک bare مخزن راه دورMini-project: two developers, one bare remote

با دو clone نقش دو هم‌تیمی را بازی می‌کنی. عمداً اجازه می‌دهیم push نفر دوم رد شود تا به جای force کردن، همان کاری را انجام دهی که در تیم واقعی لازم است: خبر تازه را بگیری، گراف را بخوانی، تغییر هر دو طرف را بفهمی و بعد راه ادغام را انتخاب کنی.

Nika and Sam work from a shared repository. Each must make an independent commit; one pushes and the other is rejected. They inspect history, preserve both changes, and finish with both clones at the same result. The goal is to reason about refs and show evidence, not recite commands.

در یک پوشهٔ خالی مخزن اولیه بساز، آن را به server.git bare تبدیل کن و دو clone با نام‌های nika و sam بساز. در هر دو clone نام و ایمیل محلی آزمایشی تنظیم کن. فایل‌ها را جدا نگه دار تا اختلاف از تاریخچه باشد نه تعارض بی‌ربط محتوایی.

In an empty folder, create a seed repository, clone it bare as server.git, then make nika and sam clones. Set test-only names and emails in each clone. Keep edits in separate files so the lesson is about history divergence, not an unrelated content conflict.

یکی commit خود را push کند. نفر دوم بدون fetch، commit مستقلی روی main بسازد و push کند؛ باید رد شدن معمول non-fast-forward را ببیند. قبل از هر ادغام، این چهار مدرک را تحویل بده:

One developer pushes a commit. The other makes an independent main commit without fetching and attempts to push; observe the normal non-fast-forward rejection. Before integrating, collect these four pieces of evidence:

evidence checklist · run in the rejected clone
git remote -v
git branch -vv
git status -sb
git log --oneline --graph --decorate --all

حالا fetch کن، تغییرهای هر طرف را با git show بخوان و با تیم تصمیم بگیر merge یا rebase مناسب است. در این پروژه commit بازندهٔ push هنوز در clone خودش است؛ آن را با reset یا force کنار نگذار. بعد از ادغام، تست سادهٔ فایل‌ها را اجرا و push عادی را تکرار کن.

Fetch, inspect each side with git show, and choose merge or rebase intentionally. The rejected commit still exists in its clone; do not discard it with reset or force. After integrating, run a simple file check and retry a normal push.

در پایان، نفر دیگر fetch کند، گراف و اشاره‌گرها را ببیند و ادغام نهایی را انجام دهد. پروژه وقتی تمام است که هر دو clone بتوانند شناسه commit نهایی مشترک را نشان دهند، فایل هر دو توسعه‌دهنده حاضر باشد و پوشه‌ کاری بدون تغییر باشد. اگر شاخه آزمایشی را از مخزن راه دور حذف می‌کنید، clone دیگر باید prune کند و تفاوت شاخه محلی با اشاره‌گر رهگیریِ مخزن راه دور را ثبت کند.

Finally, the other developer fetches, inspects refs and graph, and integrates. The project is complete when both clones identify the same final shared commit, both developers’ files exist, and the working trees are clean. If you delete a test branch remotely, have the other clone prune it and record the difference between local and remote-tracking refs.

مدرک پایان کارProof of completion
  • خروجی push اول، رد شدن دوم و fetch بعد از آن را ذخیره کن.
  • Save the first push, the second push rejection, and the later fetch output.
  • گراف پیش و بعد از ادغام را همراه شناسه commit مشترک ثبت کن.
  • Record before-and-after graphs and the shared final commit ID.
  • نشان بده هر دو فایل باقی مانده و هر دو clone بدون تغییر و هم‌گام‌اند.
  • Show both files remain and both clones are clean and synchronized.

حالا می‌توانیم تاریخچه را به اشتراک بگذاریمNow we can share history

حالا می‌دانی «اشتراک تاریخچه» یعنی چه و fetch دقیقاً کدام نام‌های محلی را تازه می‌کند. سؤال بعدی خطرناک‌تر است: وقتی می‌گویی «برگرد عقب»، دقیقاً می‌خواهی کدام بخش را برگردانی؟

We have seen which refs are local and remote-tracking, how to fetch updates, integrate deliberately, and why a non-fast-forward push is rejected. Commits now travel between clones. The next dangerous question is: “How do I undo this?” Chapter 09 shows several answers because “undo” can mean several different things.

مرور کوتاهQuick reference

این پنج نکته را نگه دارKeep these five ideas
  • origin نام رایج یک مخزن راه دور است؛ origin/main اشاره‌گر محلی برای آخرین وضعیت fetchشده است، نه شاخهٔ زندهٔ سرور.
  • origin is a common remote name; origin/main is a local ref for the last fetched state, not the server’s live branch.
  • fetch شیءها و اشاره‌گر رهگیریِ مخزن‌های راه دور را می‌آورد؛ به‌تنهایی شاخه کاری و فایل‌ها را ادغام نمی‌کند.
  • fetch obtains objects and updates remote-tracking refs; it does not by itself integrate into your working branch or files.
  • upstream رابطهٔ تنظیم‌شدهٔ محلی بین شاخه و اشاره‌گر پیگیری‌شده است؛ git branch -vv آن را نشان می‌دهد.
  • An upstream is a local configured relationship between a branch and the ref it tracks; git branch -vv displays it.
  • pull ابتدا fetch و بعد ادغام انتخاب‌شده را اجرا می‌کند؛ گزینه و تنظیمات می‌توانند نتیجه را عوض کنند.
  • pull fetches and then performs a chosen integration; options and configuration affect the result.
  • اگر push رد شد، fetch کن، گراف را بخوان، merge/rebase را آگاهانه انتخاب کن، تست بگیر و push عادی بزن. force کور پاسخ پیش‌فرض نیست.
  • If push is rejected, fetch, inspect the graph, choose merge or rebase deliberately, test, and retry normally. Blind force is not the default fix.
بعد از این فصل باید بتوانیBy the end of this chapter, you can
  • در دو clone میان شاخه محلی و اشاره‌گر رهگیریِ مخزن راه دور فرق بگذاری.
  • Distinguish a local branch from a remote-tracking ref in two clones.
  • با fetch خبر بگیری، گراف را بررسی کنی و ادغام مناسب را انتخاب کنی.
  • Fetch updates, inspect the graph, and choose an appropriate integration.
  • upstream را با branch -vv بخوانی و push اول را با -u ثبت کنی.
  • Read upstream with branch -vv and set it on first push with -u.
  • رد شدن را تشخیص بدهی، تغییرهای دو طرف را نگه داری و از force بی‌بررسی پرهیز کنی.
  • Diagnose a rejection, preserve both sides’ work, and avoid uninspected force pushes.

اگر در مخزن‌های راه دور گیج شدی، به جای حفظ دستورها این سه اسم را روی کاغذ بنویس: main من، origin/main من، main روی مخزن مشترک. بعد هر دستور را ببین کدام‌یک را می‌خواند یا تغییر می‌دهد.

Official fetch documentation, pull documentation, and push documentation cover the details. For a hosted remote, see GitHub’s official guide to managing remote repositories.