توی این درس یاد میگیری کنترل نسخه (version control) چه مشکلی را حل میکند و Git کجای ماجراست. میفهمی فرق سیستم متمرکز و توزیعشده چیست، چرا هر کپی از یک پروژهی Git یک بکاپ کامل است، و چرا Git و GitHub دو چیز جدا هستند. Git را نصب میکنی (git --version)، اولین مخزن را میسازی و داخل پوشهی مخفی .git را نگاه میکنی. تمرین: Git را نصب کن و نسخهاش را ببین.
مسئله: «نهایی»، «نهاییتر»، «نهاییترین»
Section titled “مسئله: «نهایی»، «نهاییتر»، «نهاییترین»”حتماً پوشهای مثل این دیدهای: یک گزارش، که هر بار قبل از تغییر یک کپی از آن ساختهای تا اگر خراب شد برگردی:
mkdir -p ~/messy && cd ~/messyecho "گزارش فروش: ۱۰۰ واحد" > report.txtcp report.txt report-final.txtecho "گزارش فروش: ۱۲۰ واحد (اصلاح شد)" > report-final.txtcp report-final.txt report-final-v2.txtecho "گزارش فروش: ۱۲۰ واحد (اصلاح شد)، تأیید مدیر" > report-final-v2.txtcp report-final-v2.txt report-final-REAL-this-one.txtlsreport-final-REAL-this-one.txtreport-final-v2.txtreport-final.txtreport.txtالان کدام نسخه آخرین است؟ هر کدام چه فرقی با قبلی دارد؟ چه کسی و کی عوضش کرد؟ برای جواب باید diff بزنی و فایلها را یکییکی باز کنی:
cd ~/messydiff report-final.txt report-final-REAL-this-one.txt1c1< گزارش فروش: ۱۲۰ واحد (اصلاح شد)---> گزارش فروش: ۱۲۰ واحد (اصلاح شد)، تأیید مدیراین روش با یک نفر هم دردسر دارد؛ با سه نفر و صد فایل غیرممکن است. کنترل نسخه (version control) یک سیستم است که این کار را بهجای تو انجام میدهد: از هر مرحله از کار یک عکس ثبت میکند، به هر عکس یک نام و یک پیام میدهد، و هر وقت خواستی به هر عکس قدیمی برمیگردی یا دو عکس را مقایسه میکنی.
تشبیه: نقطهی ذخیرهی بازی
Section titled “تشبیه: نقطهی ذخیرهی بازی”در یک بازی بزرگ، قبل از مرحلهی سخت ذخیره میکنی؛ اگر باختی، برمیگردی همان نقطه. میتوانی چند ذخیرهی مختلف داشته باشی، و حتی از یک نقطهی قدیمی یک مسیر جدید شروع کنی. Git همین است، برای فایلهای پروژهات: هر commit یک نقطهی ذخیره است (با نام و تاریخ و توضیح)، و میتوانی به هر نقطه برگردی، یا از آن یک شاخهی (branch) تازه بکشی تا بدون ریسک چیزی را امتحان کنی.
سه مشکل که کنترل نسخه حل میکند
Section titled “سه مشکل که کنترل نسخه حل میکند”| مشکل | با کنترل نسخه |
|---|---|
| برگشتناپذیری: تغییری دادم و خراب شد | به هر نقطهی قبلی برمیگردی؛ ریسک آزمایش صفر میشود |
| همکاری: چند نفر یک پروژه | هر کس روی کپی خودش کار میکند و تغییرها ترکیب (merge) میشوند |
| ردیابی: چه کسی، کی، چرا این خط را عوض کرد؟ | هر commit نویسنده، زمان و پیام دارد (git log، git blame) |
متمرکز در برابر توزیعشده
Section titled “متمرکز در برابر توزیعشده”سیستمهای قدیمی کنترل نسخه (مثل SVN و CVS) متمرکز بودند: یک سرور مرکزی تاریخچه را نگه میدارد و هر کس فقط یک «نسخهی کاری» میگیرد. Git توزیعشده (distributed) است: وقتی پروژه را کلون (clone) میکنی، نه فقط فایلها، کل تاریخچه را هم کپی میگیری.
Git در برابر GitHub
Section titled “Git در برابر GitHub”این دو را زیاد با هم اشتباه میگیرند:
| Git | GitHub | |
|---|---|---|
| چیست؟ | برنامهای روی کامپیوتر تو (کنترل نسخه) | سرویس آنلاین برای نگهداری و اشتراک مخزنهای Git |
| سازنده | لینوکس توروالدز (سازندهی لینوکس)، ۲۰۰۵ | یک شرکت جدا (از ۲۰۰۸؛ حالا متعلق به مایکروسافت) |
| بدون اینترنت؟ | کار میکند | نه |
| امکانات | commit، branch، merge، log | همهی آن + Issues، Pull Request، Actions، صفحهی پروژه |
| جایگزینها | (Git خودش استاندارد است) | GitLab، Bitbucket، Gitea، Forgejo |
مثل ایمیل: Git مثل «پروتکل و برنامهی ایمیل» است و GitHub مثل «Gmail» یکی از سرویسهایی که از آن استفاده میکند. میشود سالها فقط با Git و بدون هیچ سایتی کار کرد (و ما در مثالها همین را خواهیم دید).
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: Git نصب است؟ git --version
Section titled “مثال ۱: Git نصب است؟ git --version”اولین کار، پرسیدن نسخه است. اگر جواب بدهد، Git نصب است:
git --versioncommand -v gitgit version 2.43.0/usr/bin/gitروی macOS هم همین دستور را روی همین مک اجرا کردم (جواب مک ممکن است Apple Git داشته باشد؛ مک نسخهی خودش از Git را همراه ابزارهای توسعه دارد):
git --versiongit version 2.54.0 (Apple Git-157)اگر Git نصب نباشد، شل جواب میدهد دستور پیدا نشد. این را شبیهسازی میکنم (با مسیر خالی که Git در آن نیست):
env PATH=/nonexistent /bin/sh -c 'git --version'/bin/sh: 1: git: not foundمثال ۲: نصب Git روی سیستمهای مختلف
Section titled “مثال ۲: نصب Git روی سیستمهای مختلف”روی Ubuntu و Debian از مدیر بسته (درس مدیریت بستهها) نصب میشود. ببین از کدام نسخه و کدام مخزن:
apt-cache policy git | head -4git: Installed: 1:2.43.0-1ubuntu7.3 Candidate: 1:2.43.0-1ubuntu7.3 Version table:دستورهای نصب برای سیستمهای دیگر (اینها را روی این ماشین اجرا نکردهام چون Git قبلاً نصب است یا سیستم دیگری لازم دارند؛ طبق مستندات رسمی git-scm.com/downloads):
# Ubuntu / Debiansudo apt update && sudo apt install git
# Fedorasudo dnf install git-all
# macOS (با Homebrew؛ یا فقط git --version را بزن تا ابزارهای Xcode نصب شوند)brew install git
# Windows (در PowerShell؛ یا نصبکنندهی رسمی از git-scm.com)winget install --id Git.Git -e --source wingetروی ویندوز، نصب Git for Windows شامل «Git Bash» (یک ترمینال شبیه لینوکس) هم هست؛ اگر WSL2 داری (درس راهاندازی محیط تمرین)، میتوانی داخل Ubuntu همان دستور apt را بزنی و همهی مثالهای این دوره عیناً کار میکنند.
مثال ۳: اولین مخزن، git init
Section titled “مثال ۳: اولین مخزن، git init”مخزن (repository) یعنی یک پوشهی پروژه که Git تاریخچهاش را دنبال میکند. با git init یک پوشهی معمولی را مخزن میکنی (جزئیاتش در درس «اولین مخزن و commit»):
mkdir -p ~/gitlab/hello && cd ~/gitlab/helloecho "--- قبل از git init، آیا اینجا مخزن است؟"git status 2>&1git initecho "--- حالا:"git statusecho "--- و پوشهی مخفی .git:"ls -a--- قبل از git init، آیا اینجا مخزن است؟fatal: not a git repository (or any of the parent directories): .gitInitialized empty Git repository in /home/ali/gitlab/hello/.git/--- حالا:On branch main
No commits yet
nothing to commit (create/copy files and use "git add" to track)--- و پوشهی مخفی .git:....gitدو چیز دیدی: (۱) git status قبل از init گفت fatal: not a git repository؛ یعنی Git فقط داخل مخزن کار میکند. (۲) بعد از init یک پوشهی مخفی .git ساخته شد؛ تمام تاریخچه همینجاست. (دستور git status در درس بعد کامل توضیح داده میشود؛ فعلاً بدان No commits yet یعنی هنوز هیچ نقطهی ذخیرهای نداریم.)
مثال ۴: اولین «نقطهی ذخیره»
Section titled “مثال ۴: اولین «نقطهی ذخیره»”یک فایل میسازم و با دو دستور اولین commit را میگیرم. (نام و ایمیل را از قبل تنظیم کردهام؛ در درس «نصب و تنظیم اولیه» میبینی چطور. و add و commit را هم در درس سوم کامل میآموزی؛ فعلاً فقط تماشا کن.)
cd ~/gitlab/helloecho "# پروژهی سلام" > README.mdgit add README.mdgit commit -m "اولین نسخه"echo "--- تاریخچه:"git log[main (root-commit) 6d3cc13] اولین نسخه 1 file changed, 1 insertion(+) create mode 100644 README.md--- تاریخچه:commit 6d3cc13f710867eb53f329c2a5a2d5fd85d728b1Author: Ali <ali@example.com>Date: Sun Oct 4 08:03:46 2026 +0000
اولین نسخهدر git log چهار چیز هست: یک شناسهی طولانی (hash) که هویت یکتای این commit است، نویسنده، تاریخ و پیام. این شناسه از محتوای commit حساب میشود و عملاً یکتاست.
مثال ۵: توزیعشده بودن، کلون کامل
Section titled “مثال ۵: توزیعشده بودن، کلون کامل”حالا وعدهی «هر کلون یک بکاپ کامل است» را امتحان میکنم. چند commit دیگر میگیرم و بعد پروژه را کلون میکنم؛ نه از GitHub، از یک پوشهی دیگر روی همین ماشین (Git فرق نمیگذارد مبدأ کجاست):
cd ~/gitlab/helloecho "خط دوم" >> README.mdgit commit -am "خط دوم" -qecho "خط سوم" >> README.mdgit commit -am "خط سوم" -qecho "--- مخزن اصلی:"git log --onelineecho "--- کلون (git clone مسیر پوشه):"cd ~/gitlabgit clone hello hello-copycd hello-copyecho "--- تاریخچهی کلون، بدون هیچ اتصالی به مخزن اصلی:"git log --oneline--- مخزن اصلی:8226020 خط سومb8e2301 خط دوم6d3cc13 اولین نسخه--- کلون (git clone مسیر پوشه):Cloning into 'hello-copy'...done.--- تاریخچهی کلون، بدون هیچ اتصالی به مخزن اصلی:8226020 خط سومb8e2301 خط دوم6d3cc13 اولین نسخهکلون هر سه commit را دارد، نه فقط آخرین فایلها. حالا مخزن اصلی را کلاً پاک میکنم، مثل وقتی که سرور میسوزد:
cd ~/gitlabrm -rf helloecho "--- مخزن اصلی پاک شد. کلون چی؟"cd hello-copygit log --onelineecho "--- و فایلهایش:"cat README.md--- مخزن اصلی پاک شد. کلون چی؟8226020 خط سومb8e2301 خط دوم6d3cc13 اولین نسخه--- و فایلهایش:# پروژهی سلامخط دومخط سومتاریخچهی کامل در کلون هست. این یعنی اگر GitHub یک روز در دسترس نباشد (یا از ایران به آن دسترسی نباشد)، کار تو و همهی همکارانت متوقف نمیشود: هر کدام یک نسخهی کامل دارید. (فقط هماهنگکردن بین شما معطل میماند؛ آن را در درس «مخزن راه دور» میبینی.)
مثال ۶: راهنمای داخلی Git
Section titled “مثال ۶: راهنمای داخلی Git”مثل لینوکس، Git راهنمای خودش را دارد (درس «کمک گرفتن از خود لینوکس»):
git help | head -22echo "--- راهنمای کوتاه یک دستور (-h):"git commit -h 2>&1 | head -6echo "--- راهنمای کامل یک دستور (git help دستور؛ همان صفحهی man):"git help commit | head -6echo "--- فهرست راهنماهای مفهومی (git help -g):"git help -g | head -4usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path] [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare] [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]
These are common Git commands used in various situations:
start a working area (see also: git help tutorial) clone Clone a repository into a new directory init Create an empty Git repository or reinitialize an existing one
work on the current change (see also: git help everyday) add Add file contents to the index mv Move or rename a file, a directory, or a symlink restore Restore working tree files rm Remove files from the working tree and from the index
examine the history and state (see also: git help revisions) bisect Use binary search to find the commit that introduced a bug diff Show changes between commits, commit and working tree, etc grep Print lines matching a pattern--- راهنمای کوتاه یک دستور (-h):usage: git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend] [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>)] [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]--- راهنمای کامل یک دستور (git help دستور؛ همان صفحهی man):GIT-COMMIT(1) Git Manual GIT-COMMIT(1)
NAME git-commit - Record changes to the repository
SYNOPSIS--- فهرست راهنماهای مفهومی (git help -g):The Git concept guides are: core-tutorial A Git core tutorial for developers credentials Providing usernames and passwords to Git cvs-migration Git for CVS usersgit help دستور صفحهی کامل (شبیه man) را باز میکند؛ در ترمینال واقعی با q خارج میشوی. و راهنماهای مفهومی (مثلاً آموزش رسمی Git: git help tutorial) را با git help -g پیدا میکنی.
پشت پرده: Git عکس میگیرد، نه تفاوت
Section titled “پشت پرده: Git عکس میگیرد، نه تفاوت”بسیاری فکر میکنند Git فقط «تفاوتها» را نگه میدارد. در واقع هر commit یک عکس کامل از همهی فایلهای پروژه در آن لحظه است (فایلهای بدون تغییر فقط به نسخهی قبلی اشاره میکنند و دوباره ذخیره نمیشوند). Git هر commit را یک شیء کوچک مینویسد که اینها را دارد: اشاره به عکس فایلها (tree)، اشاره به commit قبلی (parent)، نویسنده، زمان و پیام. ببین واقعاً اینطور است:
cd ~/gitlab/hello-copyecho "--- محتوای خود شیء commit (cat-file):"git cat-file -p HEADecho "--- و عکس فایلها (tree) که commit به آن اشاره میکند:"git ls-tree HEADecho "--- پوشهی .git چه دارد؟"ls .git--- محتوای خود شیء commit (cat-file):tree b5a0aeaade9c71c8221d60c4707a898a81f6d17cparent b8e2301a7fd464bd01719aaa02e8e8e8d7e0fb76author Ali <ali@example.com> 1791101026 +0000committer Ali <ali@example.com> 1791101026 +0000
خط سوم--- و عکس فایلها (tree) که commit به آن اشاره میکند:100644 blob 721ebef44a64f9073e1f497689b3256c9994cf8e README.md--- پوشهی .git چه دارد؟HEADbranchesconfigdescriptionhooksindexinfologsobjectspacked-refsrefsخط tree همان عکس فایلهاست، parent خط commit قبلی، و author و committer و پیام. شیءها داخل .git/objects هستند. سه نکته:
- هر commit به commit قبلیاش اشاره میکند؛ همین زنجیره تاریخچه است (شکل بالا).
- شناسه (hash) از محتوای commit حساب میشود؛ اگر کسی چیزی را در تاریخچه دستکاری کند، شناسهها عوض میشوند و فوراً معلوم میشود. برای همین تاریخچهی Git قابلاعتماد است.
mainوHEADفقط برچسب هستند، نه چیز سنگین. (در درس «پشت پردهی Git» عمیق میشویم.)
پوشهی .git که دیدی چند بخش دارد:
در .git |
کار |
|---|---|
objects/ |
همهی commit ها و فایلها (محتوای واقعی تاریخچه) |
refs/ |
برچسبها: شاخهها (heads/) و tag ها |
HEAD |
میگوید الآن روی کدام شاخه هستی |
config |
تنظیمات همین مخزن |
واژهنامهی لازم
Section titled “واژهنامهی لازم”| واژه | معنی |
|---|---|
| مخزن (repository / repo) | پوشهی پروژه + تاریخچهاش (.git) |
| commit | یک نقطهی ذخیره با پیام، نویسنده و زمان |
| branch (شاخه) | یک خط مستقل از تاریخچه (برچسبی که به یک commit اشاره میکند) |
| clone | کپی کامل یک مخزن، با تاریخچه |
| remote | مخزن دیگری که با آن هماهنگ میشوی (مثلاً روی GitHub) |
| working directory | همین فایلهایی که میبینی و ویرایش میکنی |
| HEAD | «من الآن اینجا هستم»: اشاره به commit فعلی |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) دستور Git بیرون از مخزن
Section titled “۱) دستور Git بیرون از مخزن”mkdir -p ~/gitlab/plain && cd ~/gitlab/plaingit log 2>&1git status 2>&1fatal: not a git repository (or any of the parent directories): .gitfatal: not a git repository (or any of the parent directories): .gitfatal: not a git repository (or any of the parent directories): .git: Git از پوشهی فعلی تا ریشه دنبال .git میگردد و پیدا نکرد. راهحل: cd به پوشهی پروژه، یا git init اگر پروژهی تازه است.
۲) یکی گرفتن Git و GitHub
Section titled “۲) یکی گرفتن Git و GitHub”اگر GitHub باز نشد، این یعنی سرویس در دسترس نیست، نه اینکه Git خراب باشد. تو هنوز همهی تاریخچه را داری (مثال ۵) و commit، log، branch به شبکه نیاز ندارند. فقط push و pull به سرور نیاز دارند.
۳) فکر اینکه Git فقط برای کد است
Section titled “۳) فکر اینکه Git فقط برای کد است”Git هر فایل متنی را دنبال میکند: مستندات، تنظیمات سرور، یادداشتها، این سایت. فایلهای بزرگ باینری (فیلم، ایمیج دیسک) را خوب مدیریت نمیکند (برای آنها ابزار دیگری مثل Git LFS هست).
۴) نگهداری رمز و کلید در مخزن
Section titled “۴) نگهداری رمز و کلید در مخزن”هر چه commit شود، برای همیشه در تاریخچه میماند (حتی اگر بعداً پاکش کنی). رمز، کلید خصوصی و توکن را هیچوقت commit نکن؛ درس .gitignore راهش را میگوید.
بدون اینکه برنامهی دیگری باز کنی، بگو Git روی این ماشین کجا نصب است و چه نسخهای.
دیدن جواب
command -v gitgit --version/usr/bin/gitgit version 2.43.0تمرین اصلی: Git را نصب کن و نسخهاش را ببین. (روی ماشین خودت همین کار را با روش سیستمعاملت بکن؛ اینجا Git از قبل نصب است، پس درستی نصب را بهصورت کامل بررسی میکنم: نسخه، مسیر، بستهی مدیر بسته، و اینکه واقعاً میتواند یک مخزن بسازد.)
دیدن جواب
git --versiondpkg -s git | grep -E '^(Package|Status|Version)'mkdir -p ~/gitlab/check && cd ~/gitlab/checkgit init -q && echo "Git درست کار میکند: مخزن ساخته شد" && ls -d .gitgit version 2.43.0Package: gitStatus: install ok installedVersion: 1:2.43.0-1ubuntu7.3Git درست کار میکند: مخزن ساخته شد.gitثابت کن Git «توزیعشده» است: یک مخزن با دو commit بساز، آن را کلون کن، مخزن اصلی را پاک کن، و نشان بده کلون (۱) تاریخچهی کامل را دارد و (۲) آدرس مخزن اصلی را بهعنوان origin به خاطر دارد، ولی حالا به آن دسترسی ندارد.
دیدن جواب
mkdir -p ~/gitlab/proof && cd ~/gitlab/proofgit init -q src && cd srcecho a > a.txt && git add a.txt && git commit -qm "اولی"echo b > b.txt && git add b.txt && git commit -qm "دومی"cd .. && git clone -q src copy && rm -rf srccd copyecho "--- تاریخچهی کلون:"git log --onelineecho "--- مبدأ را یادش هست (origin):"git remote -v | head -1echo "--- ولی مبدأ دیگر نیست:"git fetch 2>&1 | head -3--- تاریخچهی کلون:203f305 دومی736b24e اولی--- مبدأ را یادش هست (origin):origin /home/ali/gitlab/proof/src (fetch)--- ولی مبدأ دیگر نیست:fatal: '/home/ali/gitlab/proof/src' does not appear to be a git repositoryfatal: Could not read from remote repository.آزمونک
Section titled “آزمونک”تفاوت اصلی Git و GitHub چیست؟
Git بدون اینترنت و بدون GitHub هم کامل کار میکند؛ GitHub (و GitLab و...) فقط جایی برای همکاری و پشتیبانگیری آنلایناند.
کنترل نسخه کدام مشکل را حل نمیکند؟
کنترل نسخه دربارهی تاریخچه و همکاری است، نه سرعت اجرای برنامه.
توزیعشده بودن Git یعنی چه؟
برای همین هر کلون یک بکاپ کامل است.
اگر پوشهی .git را پاک کنی چه میشود؟
تمام commit ها داخل .git هستند؛ فایلهای کاری جدا و باقی میمانند.
هر commit در Git چیست؟
فایلهای بدون تغییر دوباره ذخیره نمیشوند؛ فقط به نسخهی قبلی اشاره میکنند.
پیام «fatal: not a git repository» یعنی چه و چه میکنی؟
Git از پوشهی فعلی تا ریشه دنبال .git میگردد.
جمعبندی
Section titled “جمعبندی”- کنترل نسخه از هر مرحلهی کار یک عکس (commit) با نام، زمان و پیام نگه میدارد؛ میتوانی برگردی، مقایسه کنی و کار چند نفر را ترکیب کنی.
- Git توزیعشده است: هر کلون کل تاریخچه را دارد؛ commit، log و branch بدون اینترنت کار میکنند.
- Git ≠ GitHub: Git برنامه است (ساختهی لینوکس توروالدز)؛ GitHub (و GitLab و Bitbucket) سرویس میزبانی مخزن.
git --versionنصب را آزمایش میکند؛git initپوشه را مخزن میکند و پوشهی مخفی.gitمیسازد که همهی تاریخچه آنجاست.- هر commit یک عکس کامل + اشاره به commit قبلی است؛ شناسهی آن (hash) از محتوایش حساب میشود.
- هیچوقت رمز یا کلید خصوصی را commit نکن؛ هر چه وارد تاریخچه شود میماند.
| دستور | کاری که میکند |
|---|---|
git --version | نسخهی Git (و آزمایش نصببودن) |
command -v git | مسیر برنامهی git |
git init | پوشهی فعلی را مخزن کن (.git میسازد) |
git clone مسیر-یا-آدرس | کپی کامل یک مخزن با تاریخچه |
git log --oneline | تاریخچهی کوتاه |
git help | فهرست دستورهای رایج |
git help دستور / git دستور -h | راهنمای کامل / خلاصهی یک دستور |
git cat-file -p HEAD | محتوای خود شیء commit (نگاه پشت پرده) |