رفتن به محتوا
LoopX

tag و نسخه‌گذاری

توی این درس یاد می‌گیری نسخه‌های مهم پروژه را با یک برچسب ثابت (tag) علامت بزنی تا بعداً بدون جست‌وجوی هش به آن‌ها برگردی. فرق tag سبک (lightweight) و annotated (git tag -a v1.0.0 -m "...") را می‌فهمی، با Semantic Versioning (MAJOR.MINOR.PATCH) شماره‌ی نسخه را درست انتخاب می‌کنی، tag ها را فهرست و فیلتر می‌کنی (git tag -l)، آن‌ها را روی سرور می‌فرستی (git push --tags) و از یک tag قدیمی یک hotfix می‌سازی. تمرین: اولین نسخه‌ی پروژه را tag بزن.

مسئله: «همان نسخه‌ای که دیروز به مشتری دادیم»

Section titled “مسئله: «همان نسخه‌ای که دیروز به مشتری دادیم»”

هش یک commit مثل 9fceb02d0ae598e95dc970b74767f19372d61af8 برای آدم‌ها قابل‌یادآوری نیست. وقتی مشتری می‌گوید «در نسخه‌ی ۱٫۲ باگ دارد»، باید بتوانی دقیقاً همان نسخه را بیرون بیاوری. tag یک اسم خوانا و ثابت روی یک commit می‌گذارد: v1.2.0. برخلاف شاخه، tag با commit های جدید جلو نمی‌رود؛ همیشه به همان لحظه اشاره می‌کند.

تشبیه: برچسب ثابت روی قفسه

Section titled “تشبیه: برچسب ثابت روی قفسه”

شاخه مثل برچسب متحرک است که با هر commit جلو می‌رود (main همیشه «آخرین»). tag مثل علامت‌گذاری یک صفحه‌ی کتاب با ماژیک: صفحه همان است و تا همیشه به همان صفحه اشاره می‌کند. شاخه می‌پرسد «الان کجایی؟»؛ tag می‌گوید «آن لحظه‌ی مهم این‌جا بود».

شاخه‌ها (main) با هر commit جلو می‌روند ولی tag ها (v1.0.0 و v1.1.0) روی همان commit ای که به آن زده شدند ثابت می‌مانند.
tag سبک فقط یک اسم برای یک commit است. tag annotated یک شیء مستقل است که نویسنده، تاریخ و پیام دارد (و می‌شود امضایش کرد)؛ برای انتشار همیشه annotated.

شماره‌ی نسخه‌ها باید معنی داشته باشند. قرارداد رایج Semantic Versioning است: MAJOR.MINOR.PATCH، مثل 2.4.1:

بخش کی بالا می‌رود نمونه
MAJOR تغییر ناسازگار با نسخه‌ی قبل (کاربران باید کدشان را عوض کنند) 1.9.3 ← 2.0.0
MINOR ویژگی جدید سازگار با قبل 1.4.2 ← 1.5.0
PATCH رفع باگ سازگار 1.4.2 ← 1.4.3
پیش‌انتشار نسخه‌ی آزمایشی قبل از انتشار 2.0.0-rc.1، 1.5.0-beta.2

قواعد مهم: با بالا رفتن MAJOR، MINOR و PATCH صفر می‌شوند؛ نسخه‌های 0.y.z یعنی «در حال توسعه، هر چیزی ممکن است عوض شود»؛ و 1.0.0 یعنی «API عمومی را تثبیت کردیم». رسم رایج این است که tag با v شروع شود: v1.0.0.

یک پروژه‌ی کوچک «ماشین‌حساب» با چند commit:

Terminal window
mkdir -p ~/gitlab/calc && cd ~/gitlab/calc && git init -q
echo "def add(a, b): return a + b" > calc.py
git add calc.py && git commit -qm "Add add()"
echo "def sub(a, b): return a - b" >> calc.py
git commit -qam "Add sub()"
echo "def mul(a, b): return a * b" >> calc.py
git commit -qam "Add mul()"
git log --oneline
خروجی
f73665d Add mul()
8a7aa60 Add sub()
7824976 Add add()
Terminal window
cd ~/gitlab/calc
git tag demo-light
echo "--- فهرست tag ها:"
git tag
echo "--- tag به کدام commit اشاره می‌کند؟"
git rev-parse --short demo-light
git rev-parse --short HEAD
echo "--- git show:"
git show -s --oneline demo-light
echo "--- نوع شیء:"
git cat-file -t demo-light
git tag -d demo-light
خروجی
--- فهرست tag ها:
demo-light
--- tag به کدام commit اشاره می‌کند؟
f73665d
f73665d
--- git show:
f73665d Add mul()
--- نوع شیء:
commit
Deleted tag 'demo-light' (was f73665d)

tag سبک فقط یک اسم است: نوع شیء commit است (یعنی همان commit، با یک اسم دیگر)؛ هیچ پیام و نویسنده‌ای ندارد. (دستور آخر آن را پاک کرد.)

مثال ۲: tag annotated، برای نسخه‌ی واقعی

Section titled “مثال ۲: tag annotated، برای نسخه‌ی واقعی”

برای انتشار، همیشه -a (annotated) با -m (پیام):

Terminal window
cd ~/gitlab/calc
git tag -a v1.0.0 -m "First stable release: add, sub, mul"
echo "--- git show v1.0.0:"
git show v1.0.0 -s
echo "--- نوع شیء (tag واقعی، نه commit):"
git cat-file -t v1.0.0
خروجی
--- git show v1.0.0:
tag v1.0.0
Tagger: Ali <ali@example.com>
Date: Sun Oct 4 08:31:43 2026 +0000
First stable release: add, sub, mul
commit f73665d1711db25e608de1dce180d5f576690b58
Author: Ali <ali@example.com>
Date: Sun Oct 4 08:31:43 2026 +0000
Add mul()
--- نوع شیء (tag واقعی، نه commit):
tag

git show برای tag annotated اول اطلاعات tag (نام، Tagger، تاریخ، پیام) و بعد خود commit را نشان می‌دهد. نوع شیء tag است: یک شیء مستقل که به commit اشاره می‌کند.

مثال ۳: tag زدن روی یک commit قدیمی

Section titled “مثال ۳: tag زدن روی یک commit قدیمی”

اگر یادت رفت یک commit قدیمی را tag بزنی، هش (یا اسمش) را آخر دستور بده:

Terminal window
cd ~/gitlab/calc
echo "--- تاریخچه:"
git log --oneline
git tag -a v0.1.0 -m "Prototype: add only" HEAD~2
echo "--- tag ها (به ترتیب الفبا):"
git tag
git show -s --oneline v0.1.0
خروجی
--- تاریخچه:
f73665d Add mul()
8a7aa60 Add sub()
7824976 Add add()
--- tag ها (به ترتیب الفبا):
v0.1.0
v1.0.0
tag v0.1.0
Prototype: add only
7824976 Add add()

مثال ۴: فهرست‌کردن، فیلتر و مرتب‌سازی

Section titled “مثال ۴: فهرست‌کردن، فیلتر و مرتب‌سازی”

با زیاد‌شدن tag ها، دنبال یک الگو بگرد. دقت کن مرتب‌سازی پیش‌فرض الفبایی است و v1.10.0 قبل از v1.2.0 می‌آید؛ برای مرتب‌سازی نسخه‌ای از --sort=v:refname استفاده کن:

Terminal window
cd ~/gitlab/calc
for v in v1.1.0 v1.2.0 v1.10.0 v2.0.0-rc.1; do git tag $v; done
echo "--- الفبایی (پیش‌فرض، غلط‌انداز):"
git tag -l 'v1.*'
echo "--- مرتب بر اساس نسخه:"
git tag -l 'v1.*' --sort=v:refname
echo "--- جدیدترین نسخه‌ها اول:"
git tag --sort=-v:refname | head -3
echo "--- پیام tag ها (-n):"
git tag -n | head -3
خروجی
--- الفبایی (پیش‌فرض، غلط‌انداز):
v1.0.0
v1.1.0
v1.10.0
v1.2.0
--- مرتب بر اساس نسخه:
v1.0.0
v1.1.0
v1.2.0
v1.10.0
--- جدیدترین نسخه‌ها اول:
v2.0.0-rc.1
v1.10.0
v1.2.0
--- پیام tag ها (-n):
v0.1.0 Prototype: add only
v1.0.0 First stable release: add, sub, mul
v1.1.0 Add mul()

-l 'الگو' با الگوی * فیلتر می‌کند؛ -n یک خط از پیام هر tag را نشان می‌دهد؛ و --sort=-v:refname از جدید به قدیم بر اساس نسخه‌ی واقعی مرتب می‌کند. (در --sort، - یعنی معکوس.)

مثال ۵: git describe، «من الان کجای نسخه‌ها هستم؟»

Section titled “مثال ۵: git describe، «من الان کجای نسخه‌ها هستم؟»”

بعد از آخرین tag چند commit دیگر آمده؟ git describe از نزدیک‌ترین tag annotated شروع می‌کند و می‌گوید چند commit از آن جلوتر هستی؛ برای شماره‌ی build عالی است:

Terminal window
cd ~/gitlab/calc
git tag -d v1.1.0 v1.2.0 v1.10.0 v2.0.0-rc.1 > /dev/null
echo "--- روی خود v1.0.0:"
git describe
echo "x = 1" >> calc.py && git commit -qam "Add a variable"
echo "y = 2" >> calc.py && git commit -qam "Add another variable"
echo "--- دو commit بعد از tag:"
git describe
echo "--- جزئیات: tag-تعداد-g+هش (g یعنی git):"
git describe --long
خروجی
--- روی خود v1.0.0:
v1.0.0
--- دو commit بعد از tag:
v1.0.0-2-g6a49ff8
--- جزئیات: tag-تعداد-g+هش (g یعنی git):
v1.0.0-2-g6a49ff8

شکل v1.0.0-2-gabc1234 یعنی «۲ commit بعد از v1.0.0، و هش commit فعلی abc1234». (describe فقط tag های annotated را در نظر می‌گیرد مگر --tags بدهی.)

مثال ۶: رفتن به یک نسخه‌ی قدیمی و ساختن hotfix

Section titled “مثال ۶: رفتن به یک نسخه‌ی قدیمی و ساختن hotfix”

با git switch --detach به آن لحظه می‌روی (فقط برای نگاه). برای تغییر از آن tag یک شاخه بساز:

Terminal window
cd ~/gitlab/calc
echo "--- نگاه به نسخه‌ی v1.0.0 (detached):"
git switch -q --detach v1.0.0
git status | head -1
cat calc.py
echo "--- از همان نسخه یک شاخه‌ی hotfix بساز:"
git switch -qc hotfix/1.0.1
sed -i 's/a + b/a + b # fixed/' calc.py
git commit -qam "Fix add() comment"
git tag -a v1.0.1 -m "Hotfix: add() fixed"
git log --oneline --graph --all --decorate
خروجی
--- نگاه به نسخه‌ی v1.0.0 (detached):
HEAD detached at v1.0.0
def add(a, b): return a + b
def sub(a, b): return a - b
def mul(a, b): return a * b
--- از همان نسخه یک شاخه‌ی hotfix بساز:
* 6b7908a (HEAD -> hotfix/1.0.1, tag: v1.0.1) Fix add() comment
| * 6a49ff8 (main) Add another variable
| * 986c253 Add a variable
|/
* f73665d (tag: v1.0.0) Add mul()
* 8a7aa60 Add sub()
* 7824976 (tag: v0.1.0) Add add()

شاخه‌ی hotfix از v1.0.0 جدا شد و main (که x و y را دارد) تأثیر نگرفت. حالا اگر لازم باشد همان رفع را در main هم داشته باشی، با merge یا cherry-pick می‌آوری (درس نجات). git log --decorate نام tag ها را کنار commit ها می‌نویسد (داخل پرانتز).

مثال ۷: tag ها را به سرور بفرست

Section titled “مثال ۷: tag ها را به سرور بفرست”

tag ها با git push معمولی نمی‌روند؛ باید صریحاً بفرستی. این‌جا یک مخزن bare محلی نقش سرور را دارد (مثل GitHub):

Terminal window
cd ~/gitlab
git init -q --bare calc-server.git
cd calc
git remote add origin ~/gitlab/calc-server.git
git push -q origin main
echo "--- tag های سرور بعد از push معمولی:"
git ls-remote --tags origin; echo "(خالی)"
echo "--- فقط یک tag:"
git push -q origin v1.0.0
git ls-remote --tags origin
echo "--- همه‌ی tag ها:"
git push -q --tags origin
git ls-remote --tags origin
خروجی
--- tag های سرور بعد از push معمولی:
(خالی)
--- فقط یک tag:
8c6d26fe6f9960542a1e0e7d39513309a7da883f refs/tags/v1.0.0
f73665d1711db25e608de1dce180d5f576690b58 refs/tags/v1.0.0^{}
--- همه‌ی tag ها:
fd1e8c0b1656670034e8f9979329550590dfed61 refs/tags/v0.1.0
78249764646c3548c1f4ff6a7a214c4896eb356d refs/tags/v0.1.0^{}
8c6d26fe6f9960542a1e0e7d39513309a7da883f refs/tags/v1.0.0
f73665d1711db25e608de1dce180d5f576690b58 refs/tags/v1.0.0^{}
b4bbe28f2430e254026b6bc46d72a5a3c64b33fc refs/tags/v1.0.1
6b7908ae64ee45fd0c1992c2fe53f60fe05aa97b refs/tags/v1.0.1^{}

سه شکل: git push origin v1.0.0 (یکی)، git push --tags (همه‌ی tag ها، حتی سبک و آزمایشی)، و git push --follow-tags (فقط tag های annotated که به commit های در حال push اشاره می‌کنند؛ شکل تمیزتر برای انتشار). git ls-remote --tags origin tag های سرور را بدون دانلود نشان می‌دهد؛ هر tag annotated دو خط دارد: خط اول هش خود شیء tag و خط دوم (با ^{} در آخر) هش commit ای که به آن اشاره می‌کند. (جزئیات remote و push در درس «مخزن راه دور».)

مثال ۸: حذف و جابه‌جایی tag

Section titled “مثال ۸: حذف و جابه‌جایی tag”
Terminal window
cd ~/gitlab/calc
echo "--- tag اشتباه می‌زنم و بلافاصله پاکش می‌کنم (فقط محلی):"
git tag v9.9.9
git tag -d v9.9.9
echo "--- حذف از سرور هم (اگر قبلاً push کرده بودی):"
git tag v9.9.9 && git push -q origin v9.9.9
git push origin --delete v9.9.9 2>&1 | tail -1
git tag -d v9.9.9
echo "--- زدن tag تکراری:"
git tag v1.0.0 2>&1
خروجی
--- tag اشتباه می‌زنم و بلافاصله پاکش می‌کنم (فقط محلی):
Deleted tag 'v9.9.9' (was 6b7908a)
--- حذف از سرور هم (اگر قبلاً push کرده بودی):
- [deleted] v9.9.9
Deleted tag 'v9.9.9' (was 6b7908a)
--- زدن tag تکراری:
fatal: tag 'v1.0.0' already exists

git tag -d نام محلی را پاک می‌کند و git push origin --delete نام از سرور. و ساختن دوباره‌ی یک اسم موجود خطا می‌دهد: already exists؛ برای جابه‌جایی git tag -f هست، ولی tag ای را که منتشر شده هرگز جابه‌جا نکن (کاربران نسخه‌ی قبلی را دارند؛ اشتباه‌های رایج ۳).

مثال ۹: چه چیزی بین دو نسخه عوض شد؟ (changelog)

Section titled “مثال ۹: چه چیزی بین دو نسخه عوض شد؟ (changelog)”

دو tag یک بازه هستند؛ قدیم..جدید همه‌ی commit های بین دو نسخه است. پایه‌ی هر changelog:

Terminal window
cd ~/gitlab/calc
git tag -a v1.1.0 -m "Release 1.1.0" main
echo "--- commit های بین v1.0.0 و v1.1.0:"
git log v1.0.0..v1.1.0 --oneline
echo "--- فایل‌های عوض‌شده:"
git diff v1.0.0 v1.1.0 --stat
echo "--- چه کسی چند commit زد؟"
git shortlog -sn v1.0.0..v1.1.0
خروجی
--- commit های بین v1.0.0 و v1.1.0:
6a49ff8 Add another variable
986c253 Add a variable
--- فایل‌های عوض‌شده:
calc.py | 2 ++
1 file changed, 2 insertions(+)
--- چه کسی چند commit زد؟
2 Ali

git log tag1..tag2 فهرست commit های تازه، git diff tag1 tag2 تغییر کامل، git shortlog خلاصه به‌تفکیک نویسنده. بسیاری از ابزارها (و GitHub Releases) همین را برای ساخت «یادداشت انتشار» به‌کار می‌برند.

مثال ۱۰: tag امضاشده و Release در GitHub

Section titled “مثال ۱۰: tag امضاشده و Release در GitHub”

برای اینکه کاربران مطمئن شوند tag واقعاً از تو است، می‌شود آن را با GPG امضا کرد (-s) و با git tag -v تأیید کرد. و GitHub روی هر tag یک صفحه‌ی Release می‌سازد که یادداشت انتشار و فایل‌های دانلودی دارد. این دو را اجرا نکرده‌ام (کلید GPG و حساب GitHub لازم دارند)؛ فقط دستورها طبق مستندات رسمی:

نمونه (اجرا نشده): tag امضاشده و GitHub Release
# tag امضاشده با کلید GPG (کلید باید قبلاً ساخته و به Git معرفی شده باشد)
git tag -s v1.2.0 -m "Release 1.2.0"
git tag -v v1.2.0 # تأیید امضا
# ساخت Release روی GitHub با ابزار gh (نیاز به ورود به حساب)
gh release create v1.2.0 --title "v1.2.0" --notes "ویژگی‌های جدید و رفع باگ‌ها"

پشت پرده: tag ها فایل‌هایی در refs/tags

Section titled “پشت پرده: tag ها فایل‌هایی در refs/tags”

مثل شاخه‌ها، tag ها فایل‌های کوچک هستند. تفاوت: شاخه‌ها (refs/heads) با commit جدید عوض می‌شوند، tag ها نه. و tag annotated اشاره به یک شیء tag می‌کند که خودش به commit اشاره دارد:

Terminal window
cd ~/gitlab/calc
echo "--- فایل‌های tag:"
ls .git/refs/tags | head -5
echo "--- tag سبک مستقیماً هش commit است:"
git tag light-demo
cat .git/refs/tags/light-demo
git rev-parse HEAD
echo "--- tag annotated هش یک شیء tag را دارد (نه commit):"
cat .git/refs/tags/v1.0.0
git rev-parse v1.0.0^{commit}
echo "--- محتوای شیء tag:"
git cat-file -p v1.0.0
git tag -d light-demo > /dev/null
خروجی
--- فایل‌های tag:
v0.1.0
v1.0.0
v1.0.1
v1.1.0
--- tag سبک مستقیماً هش commit است:
6b7908ae64ee45fd0c1992c2fe53f60fe05aa97b
6b7908ae64ee45fd0c1992c2fe53f60fe05aa97b
--- tag annotated هش یک شیء tag را دارد (نه commit):
8c6d26fe6f9960542a1e0e7d39513309a7da883f
f73665d1711db25e608de1dce180d5f576690b58
--- محتوای شیء tag:
object f73665d1711db25e608de1dce180d5f576690b58
type commit
tag v1.0.0
tagger Ali <ali@example.com> 1791102703 +0000
First stable release: add, sub, mul

شیء tag این‌ها را دارد: object (commit مقصد)، type، tag (نام)، tagger (نویسنده‌ی tag) و پیام. و v1.0.0^{commit} یعنی «commit ای که این tag به آن اشاره می‌کند». (بعد از git gc، فایل‌های refs در packed-refs جمع می‌شوند، ولی معنی تغییر نمی‌کند.)

دستور کار
git tag / git tag -l 'v1.*' فهرست / فیلتر
git tag نام tag سبک روی commit فعلی
git tag -a نام -m "پیام" tag annotated
git tag -a نام commit -m "پیام" tag روی یک commit دیگر
git show نام جزئیات tag و commit
git tag --sort=-v:refname مرتب‌سازی نسخه‌ای، جدید اول
git describe [--long] نزدیک‌ترین tag و فاصله‌ی commit
git switch -c شاخه نام-tag شاخه از یک نسخه‌ی قدیمی
git push origin نام / --tags / --follow-tags فرستادن tag
git tag -d نام حذف محلی
git push origin --delete نام حذف از سرور
git ls-remote --tags origin tag های سرور
تغییر بالا می‌رود مثال
باگ‌فیکس سازگار PATCH 1.4.2 → 1.4.3
ویژگی جدید سازگار MINOR 1.4.3 → 1.5.0
تغییر ناسازگار MAJOR 1.5.0 → 2.0.0

۱) tag محلی ماند و به سرور نرفت

Section titled “۱) tag محلی ماند و به سرور نرفت”
Terminal window
mkdir -p ~/gitlab/notpushed && cd ~/gitlab/notpushed && git init -q
echo a > f && git add f && git commit -qm "Base"
git init -q --bare ../notpushed-srv.git && git remote add origin ../notpushed-srv.git
git tag -a v1.0.0 -m "First"
git push -q origin main
echo "--- tag های سرور:"; git ls-remote --tags origin; echo "(هیچ‌کدام: همکارها v1.0.0 را نمی‌بینند)"
خروجی
--- tag های سرور:
(هیچ‌کدام: همکارها v1.0.0 را نمی‌بینند)

git push فقط شاخه‌ها را می‌فرستد. راه‌حل: git push origin v1.0.0 یا git push --follow-tags.

اگر هنوز push نکرده‌ای، ساده است: git tag -d نام و دوباره روی commit درست git tag -a نام commit -m .... اگر push کرده‌ای، بهتر است یک نسخه‌ی تازه (v1.0.1) بسازی تا اینکه tag منتشرشده را عوض کنی.

۳) جابه‌جاکردن tag منتشرشده

Section titled “۳) جابه‌جاکردن tag منتشرشده”

اگر v1.0.0 را به commit دیگری ببری و force push کنی، همه‌ی کسانی که قبلاً v1.0.0 را گرفته‌اند نسخه‌ی قدیمی را دارند و Git دیگران را مطلع نمی‌کند: «همان tag» دو معنی پیدا می‌کند. قانون: tag منتشرشده تغییرناپذیر است. اشتباه بود؟ نسخه‌ی بعدی را منتشر کن.

۴) استفاده از tag سبک برای انتشار

Section titled “۴) استفاده از tag سبک برای انتشار”

tag سبک نویسنده و پیام ندارد و git describe پیش‌فرض آن را نمی‌بیند. راه‌حل: برای هر نسخه‌ی واقعی -a (و ترجیحاً -s).

Terminal window
cd ~/gitlab/notpushed
git branch release
git tag release
git rev-parse release 2>&1 | head -2
git branch -D release > /dev/null; git tag -d release > /dev/null
خروجی
warning: refname 'release' is ambiguous.
27a47152cf7d4ff5cf0eb8ee90c2ceb913f0b6d9

وقتی یک شاخه و یک tag هم‌اسم باشند، Git هشدار refname is ambiguous می‌دهد و ممکن است اسم را به یکی از آن‌ها ببرد. راه‌حل: اسم‌ها را یکتا انتخاب کن؛ tag ها v1.2.3 و شاخه‌ها feature/....

✎ تمرینآسان

یک مخزن با دو commit بساز، آخرین commit را با یک tag annotated به اسم v0.1.0 علامت بزن و با git show پیام tag را ببین.

دیدن جواب
Terminal window
mkdir -p ~/gitlab/ex1 && cd ~/gitlab/ex1 && git init -q
echo 1 > a && git add a && git commit -qm "First"
echo 2 >> a && git commit -qam "Second"
git tag -a v0.1.0 -m "First prototype"
git show v0.1.0 -s | head -6
خروجی
tag v0.1.0
Tagger: Ali <ali@example.com>
Date: Sun Oct 4 08:31:43 2026 +0000
First prototype
✎ تمرینمتوسط

تمرین اصلی: اولین نسخه‌ی پروژه را tag بزن. یک پروژه با سه commit بساز؛ بعد از ثبت VERSION (فایلی با محتوای 1.0.0)، با یک tag annotated به‌نام v1.0.0 نسخه را منتشر کن، یک commit دیگر بزن (ویژگی جدید) و با git describe و git log v1.0.0..HEAD نشان بده چه چیزی بعد از انتشار آمده.

دیدن جواب
Terminal window
mkdir -p ~/gitlab/ex2 && cd ~/gitlab/ex2 && git init -q
echo "print('hello')" > app.py && git add app.py && git commit -qm "Add app"
echo "print('bye')" >> app.py && git commit -qam "Add goodbye"
echo "1.0.0" > VERSION && git add VERSION && git commit -qm "Bump version to 1.0.0"
git tag -a v1.0.0 -m "Release 1.0.0"
echo "print('new feature')" >> app.py && git commit -qam "Add new feature"
echo "--- describe:"; git describe
echo "--- بعد از انتشار چه آمده؟"
git log v1.0.0..HEAD --oneline
git tag -n
خروجی
--- describe:
v1.0.0-1-g2a839a2
--- بعد از انتشار چه آمده؟
2a839a2 Add new feature
v1.0.0 Release 1.0.0
✎ تمرینسخت

یک مخزن «سرور» (bare) بساز. نسخه‌ی v1.0.0 را tag بزن و push کن؛ main را جلو ببر؛ بعد یک باگ در v1.0.0 پیدا شده: از tag یک شاخه‌ی hotfix بساز، رفع کن، v1.0.1 را tag بزن و فقط همان tag و شاخه را push کن. ثابت کن tag های سرور v1.0.0 و v1.0.1 هستند.

دیدن جواب
Terminal window
mkdir -p ~/gitlab/ex3 && cd ~/gitlab/ex3 && git init -q
git init -q --bare ../ex3-server.git && git remote add origin ../ex3-server.git
echo "price=10" > cfg && git add cfg && git commit -qm "Add config"
git tag -a v1.0.0 -m "Release 1.0.0"
git push -q origin main v1.0.0
echo "new=1" >> cfg && git commit -qam "Start next feature"
echo "--- باگ در v1.0.0: شاخه‌ی hotfix از tag"
git switch -qc hotfix/1.0.1 v1.0.0
sed -i 's/price=10/price=12/' cfg && git commit -qam "Fix price"
git tag -a v1.0.1 -m "Hotfix 1.0.1"
git push -q origin hotfix/1.0.1 v1.0.1
echo "--- tag های سرور:"
git ls-remote --tags origin | awk '{print $2}'
echo "--- شاخه‌های سرور:"
git ls-remote --heads origin | awk '{print $2}'
خروجی
--- باگ در v1.0.0: شاخه‌ی hotfix از tag
--- tag های سرور:
refs/tags/v1.0.0
refs/tags/v1.0.0^{}
refs/tags/v1.0.1
refs/tags/v1.0.1^{}
--- شاخه‌های سرور:
refs/heads/hotfix/1.0.1
refs/heads/main
⚡ بررسی سریع

تفاوت اصلی tag و branch؟

؟ آزمونک
  1. فرق tag سبک و annotated؟

  2. در SemVer، رفع یک باگ سازگار کدام بخش را بالا می‌برد؟

  3. آیا git push معمولی tag ها را می‌فرستد؟

  4. می‌خواهی از نسخه‌ی v1.2.0 یک hotfix بسازی. کدام دستور؟

  5. خروجی «v1.0.0-3-g9fceb02» از git describe یعنی؟

  • tag یک اسم ثابت برای یک commit است (برخلاف شاخه که جلو می‌رود)؛ برای نسخه‌های انتشار. annotated (git tag -a v1.0.0 -m "...") برای انتشار؛ سبک فقط علامت موقت.
  • SemVer: MAJOR.MINOR.PATCH؛ ناسازگار ← MAJOR، ویژگی ← MINOR، باگ‌فیکس ← PATCH؛ 0.y.z در حال توسعه؛ پیشوند v.
  • git tag -l 'v1.*'، --sort=-v:refname (نسخه‌ای)، git show نام، git describe (نزدیک‌ترین tag).
  • از tag یک شاخه بساز: git switch -c hotfix/1.0.1 v1.0.0. بازه‌ی نسخه‌ها: git log v1.0.0..v1.1.0.
  • tag ها با git push نمی‌روند: git push origin v1.0.0 / --tags / --follow-tags. حذف: git tag -d و git push origin --delete.
  • tag منتشرشده را جابه‌جا نکن؛ نسخه‌ی بعدی را منتشر کن. پشت پرده: فایل در refs/tags؛ annotated یک شیء tag دارد.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
git tag -a v1.0.0 -m "پیام"tag annotated روی commit فعلی
git tag -a v0.9.0 commit -m "پیام"tag روی commit دیگر
git tag -l "v1.*" --sort=-v:refnameفهرست نسخه‌ای
git show v1.0.0جزئیات tag
git describeنزدیک‌ترین tag و فاصله
git switch -c hotfix/1.0.1 v1.0.0شاخه از یک نسخه
git push origin v1.0.0 / git push --tagsفرستادن tag
git tag -d v1.0.0حذف محلی
git push origin --delete v1.0.0حذف از سرور
git log v1.0.0..v1.1.0 --onelineتغییرهای بین دو نسخه