کامیتهای متعارف در Git (Conventional Commits)

خلاصه
مشخصهی Conventional Commits یک قانون سبک و ساده بر روی پیامهای commit است. این مشخصه مجموعهای از قواعد آسان برای ایجاد تاریخچهای شفاف از commitها فراهم میکند که نوشتن ابزارهای اتوماتیک را سادهتر میسازد. این قانون در کنار SemVer عمل میکند؛ بهطوری که ویژگیها، رفع باگها و تغییرات ناسازگار در پیامهای commit توصیف میشوند.
پیام commit باید به ساختار زیر نوشته شود:
[optional scope]: \n\n[optional body]\n\n[optional footer(s)]
پیش تعریف
کامیت | commit
کامیت در سیستمهای مدیریت نسخه مانند Git، به ثبت یک نقطه در تاریخچهی تغییرات کد منبع گفته میشود. در یک commit، تغییراتی که روی فایلها اعمال شدهاند (مانند اضافه کردن، حذف یا تغییر کد) همراه با یک پیام توضیحی ذخیره میشوند. این ثبت تغییرات به توسعهدهندگان امکان میدهد تا به نسخههای قبلی کد بازگردند، تغییرات را دنبال کنند و تاریخچهی پروژه را مرور نمایند.
پایانه | footer در comimit
در مفهوم commitهای نرمافزاری، پایانه به بخشی از پیام commit گفته میشود که پس از بدنهی اصلی commit (بعد از یک خط خالی) قرار میگیرد. در این قسمت، اطلاعات اضافی مانند شناسههای مربوط به باگ، تغییرات خاص (مانند تغییر ناسازگار)، مرجع commitهای مرتبط و سایر جزئیات بهصورت ساختاریافته درج میشوند. این اطلاعات به ابزارهای اتوماتیک کمک میکنند تا بتوانند تغییرات را بهتر دستهبندی کنند یا تغییرات ناسازگار را شناسایی نمایند.
یک commit شامل عناصر ساختاری زیر است تا نیت نویسنده برای مصرفکنندگان کتابخانه یا نرمافزار شما بهخوبی منتقل شود:
- fix:
کامیت هایی از نوع fix یک اشکال در کد را برطرف میکنند (این مورد با PATCH در نسخهبندی معنایی همراستا است).
2. feat:
کامیت هایی از نوع feat ویژگی جدیدی به کد اضافه میکنند (این مورد با MINOR در نسخهبندی معنایی همراستا است).
3. BREAKING CHANGE:
کامیت هایی که یا دارای پایانهای با متن BREAKING CHANGE: هستند یا در نوع/محدوده از علامت ! استفاده میکنند، تغییرات ناسازگاری در API را معرفی میکنندکه باعث میشوند واسط برنامه (API) بهطور ناسازگار تغییر کند و نیاز به تطبیق کد client ها داشته باشد. در این صورت، این تغییرات به عنوان افزایش نسخه MAJOR در نسخهبندی معنایی در نظر گرفته میشوند. تغییر ناسازگار میتواند در commitهای هر نوعی وجود داشته باشد.
- علاوه بر
fix:وfeat:، استفاده از انواع دیگر نیز مجاز است. بهعنوان مثال، توصیه شده است که از نوعهایی مانندbuild:,chore:,ci:,docs:,style:,refactor:,perf:,test:و سایر موارد استفاده شود (مثال از @commitlint/config-conventional که مبتنی بر Angular convention است).
- علاوه بر
BREAKING CHANGE: <توضیح>، میتوان پایانههای دیگری نیز ارائه کرد که از قانونی مشابه فرمت git trailer پیروی میکنند.
همچنین، انواع اضافی توسط این مشخصه الزامی نیستند و تأثیر ضمنی بر نسخهبندی معنایی ندارند (مگر آنکه شامل تغییر ناسازگار شوند). همچنین میتوان به یک commit، محدودهای به عنوان اطلاعات زمینهای اضافه کرد؛ محدوده در داخل پرانتز نوشته میشود، بهعنوان مثال:
feat(parser): add ability to parse arrays
مثالها
پیام commit با توضیح و پایانهی تغییر ناسازگار
feat: allow provided config object to extend other configs
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
پیام commit با استفاده از علامت ! برای جلب توجه به تغییر ناسازگار
feat!: send an email to the customer when a product is shipped
پیام commit با محدوده و استفاده از ! برای جلب توجه به تغییر ناسازگار
feat(api)!: send an email to the customer when a product is shipped
پیام commit با استفاده همزمان از ! و پایانهی BREAKING CHANGE
chore!: drop support for Node 6
BREAKING CHANGE: use JavaScript features not available in Node 6.
پیام commit بدون بدنه
docs: correct spelling of CHANGELOG
پیام commit با محدوده
feat(lang): add Polish language
پیام commit با بدنه چندبند و چندین پایانه
fix: prevent racing of requests
Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.
Remove timeouts which were used to mitigate the racing issue but are obsolete now.
Reviewed-by: Z
Refs: #123
توضیحات اجمالی
کلمات کلیدی MUST، MUST NOT، REQUIRED، SHALL، SHALL NOT، SHOULD، SHOULD NOT، RECOMMENDED، MAY و OPTIONAL در این سند به معنایی تعبیر میشوند که در RFC 2119 توضیح داده شده است.
کامیت ها باید با یک نوع پیشوند شوند که از یک اسم (مثلاً
feat،fixو ...) تشکیل شده و به دنبال آن میتواند محدوده اختیاری، علامت!اختیاری و سپس دو نقطه به همراه یک فاصله (الزامی) بیاید.نوع feat باید زمانی استفاده شود که commit یک ویژگی جدید به برنامه یا کتابخانه اضافه میکند.
نوع fix باید زمانی استفاده شود که commit نمایانگر رفع یک اشکال در برنامه است.
پس از نوع، ممکن است محدودهای ارائه شود. محدوده باید از یک اسم تشکیل شده و بخش مشخصی از کد را توضیح دهد و در داخل پرانتز قرار گیرد، بهعنوان مثال:
fix(parser):باید یک توضیح بلافاصله پس از دو نقطه و فاصله قرار گیرد. توضیح یک خلاصه کوتاه از تغییرات کد است، مثلاً:
fix: array parsing issue when multiple spaces were contained in string.ممکن است یک بدنهی طولانیتر نیز ارائه شود که اطلاعات زمینهای بیشتری در مورد تغییرات کد فراهم کند. بدنه باید یک خط خالی بعد از توضیح کوتاه آغاز شود.
بدنهی commit میتواند بهصورت آزاد و شامل هر تعداد پاراگراف جداگشته با خط جدید باشد.
ممکن است یک یا چند پایانه (footer) نیز ارائه شود؛ این پایانهها باید یک خط خالی پس از بدنه قرار گیرند. هر پایانه باید از یک توکن (کلمه) شروع شده و پس از آن با استفاده از
:یایک رشته متنی ارائه شود (این الگو از فرمت git trailer الهام گرفته شده است).# توکنهای پایانه باید به جای فاصله از علامت
-استفاده کنند، مثلاًAcked-by(این کار به تشخیص بخش پایانه از بدنه چندبندی کمک میکند). استثنای این قانون، توکنBREAKING CHANGEاست که مجاز به استفاده بهصورت مستقیم میباشد.مقدار یک پایانه میتواند شامل فضاها و خطهای جدید باشد و تجزیه (parse) باید زمانی خاتمه یابد که یک جفت توکن/جداکنندهی معتبر بعدی مشاهده شود.
تغییرات ناسازگار (Breaking changes) باید یا در پیشوند نوع/محدوده commit با علامت
!یا بهعنوان یک ورودی در پایانه مشخص شوند.اگر بهعنوان یک پایانه درج شود، تغییر ناسازگار باید شامل متن BREAKING CHANGE به صورت حروف بزرگ، به دنبال آن دو نقطه، یک فاصله و سپس توضیح مربوط باشد، مثلاً:
BREAKING CHANGE: environment variables now take precedence over config files.اگر در پیشوند نوع/محدوده درج شود، تغییر ناسازگار باید با یک
!درست قبل از:مشخص گردد. در این صورت، درجBREAKING CHANGE:در بخش پایانه میتواند نادیده گرفته شود و توضیح موجود در پیام commit برای توصیف تغییر ناسازگار استفاده شود.میتوان از انواعی به جز
featوfixدر پیامهای commit استفاده کرد، مثلاً:
docs: update ref docs.واحدهای اطلاعاتی که مشخصهی Conventional Commits را تشکیل میدهند نباید از نظر حروف (بزرگ/کوچک) حساس باشند، به استثنای
BREAKING CHANGEکه باید به صورت حروف بزرگ نوشته شود.تغییرات ناسازگار باید معادل BREAKING CHANGE تلقی شود، زمانی که بهعنوان توکن در یک پایانه استفاده میشود.
چرا از Conventional Commits استفاده کنیم؟
ایجاد خودکار CHANGELOGها: با استفاده از این مشخصه میتوان بهطور اتوماتیک تغییرات را ثبت و در changelog درج کرد.
تعیین خودکار افزایش نسخه معنایی: بر اساس نوع commitهای انجامشده (مثلاً رفع اشکال یا ویژگی جدید) میتوان افزایش نسخه را بهصورت خودکار مشخص نمود.
ارتباط مؤثر: انتقال ماهیت تغییرات به همتیمیها، کاربران نهایی و سایر ذینفعان.
فعالسازی فرایندهای build و publish: امکان راهاندازی خودکار فرایندهای ساخت و انتشار.
سازماندهی بهتر commitها: با فراهم آوردن تاریخچهای ساختارمند، مشارکت در پروژهها برای افراد آسانتر میشود.
سوالات متداول
در فاز اولیهی توسعه، چگونه باید با پیامهای commit برخورد کنم؟
پیشنهاد میکنیم گامهای خود را بهگونهای بردارید که انگار محصول شما قبلاً منتشر شده است. معمولاً شخصی — حتی اگر از توسعهدهندگان همکار شما باشد — از نرمافزار شما استفاده میکند و میخواهد بداند چه مشکلاتی رفع شده و چه تغییراتی ایجاد شدهاند.
آیا نوعهای موجود در عنوان commit به حروف بزرگ یا کوچک نوشته میشوند؟
هر نوع از حروف (بزرگ یا کوچک) قابل استفاده است؛ اما بهتر است در سراسر پروژه یکپارچه و یکسان باشد.
اگر commit به بیش از یک نوع commit مطابقت داشته باشد، چه کاری انجام دهم؟
تا حد امکان commitها را به چند بخش تقسیم کنید. یکی از مزایای استفاده از Conventional Commits این است که ما را به ساخت commitها و pull requestهای سازمانیافتهتر ترغیب میکند.
آیا این موضوع توسعه سریع و تکرار سریع را کاهش نمیدهد؟
این روش از حرکت سریع بهصورت بینظم جلوگیری میکند. بلکه به شما کمک میکند تا در بلندمدت در پروژههای مختلف با مشارکتکنندگان متنوع به سرعت پیش بروید.
آیا استفاده از Conventional Commits ممکن است باعث شود توسعهدهندگان نوع commitهایی که انجام میدهند را محدود کنند چون به نوعهای از پیش تعریفشده فکر میکنند؟
Conventional Commits ما را تشویق میکند که commitهای بیشتری از نوعهایی مانند رفع اشکال ایجاد کنیم. علاوه بر آن، انعطافپذیری این مشخصه به تیم شما اجازه میدهد نوعهای خاص خود را تعریف کرده و در طول زمان تغییر دهند.
این موضوع چه ارتباطی با SemVer دارد؟
commitهای از نوع fix باید به عنوان نسخههای PATCH در نظر گرفته شوند؛ commitهای از نوع feat باید به عنوان نسخههای MINOR در نظر گرفته شوند؛ و commitهایی که شامل BREAKING CHANGE هستند، بدون در نظر گرفتن نوع، باید به عنوان نسخههای MAJOR در نظر گرفته شوند.
چگونه باید نسخهبندی افزونههای خود به مشخصهی Conventional Commits (مانند @jameswomack/conventional-commit-spec) را انجام دهم؟
پیشنهاد میکنیم برای انتشار افزونههای خود از نسخهبندی SemVer استفاده کنید (و از ایجاد این افزونهها استقبال میکنیم!).
اگر بهطور تصادفی از نوع اشتباه commit استفاده کردم، چه کار کنم؟
اگر از نوعی استفاده کردهاید که در مشخصه موجود است اما اشتباه بهکار رفته، مثلاً fix به جای feat:
پیش از ادغام یا انتشار، توصیه میکنیم با استفاده از دستور git rebase -i تاریخچهی commitها را اصلاح کنید. پس از انتشار، فرآیند پاکسازی به ابزارها و روشهای مورد استفاده شما بستگی خواهد داشت.
اگر از نوعی استفاده کردهاید که در مشخصه وجود ندارد، مثلاً feet به جای feat:
در بدترین حالت، انتشار commitی که با مشخصه مطابقت ندارد پایان دنیا نیست؛ تنها به این معناست که آن commit توسط ابزارهایی که مبتنی بر این مشخصه هستند نادیده گرفته میشود.
آیا تمامی همنوشتاران من باید از مشخصهی Conventional Commits استفاده کنند؟
خیر! اگر از روندی مبتنی بر squash در Git استفاده کنید، مدیران اصلی میتوانند پیامهای commit را در حین ادغام پاکسازی کنند بدون آنکه برای commitکنندگان عادی بار اضافی ایجاد شود. یکی از روندهای معمول این است که سیستم Git شما بهطور خودکار commitهای یک pull request را squash کند و فرم مناسبی برای وارد کردن پیام commit صحیح توسط مدیر اصلی فراهم کند.
آیا Conventional Commits کامیت های revert را مدیریت میکند؟
بله ، برگرداندن تغییرات کد میتواند پیچیده باشد: آیا شما چندین commit را برمیگردانید؟ اگر یک ویژگی را برگردانید، آیا نسخهی بعدی بهعنوان یک patch منتشر شود؟ مشخصهی Conventional Commits تلاشی صریح برای تعریف رفتار revert نمیکند؛ بلکه این موضوع را به عهدهی توسعهدهندگان ابزار میگذارد تا با استفاده از انعطافپذیری نوعها و پایانهها، منطق مدیریت revert را توسعه دهند.
یکی از توصیهها این است که از نوع revert استفاده کنید و در پایانه، SHA commitهایی که در حال برگرداندن هستند را ذکر نمایید:
revert: let us never again speak of the noodle incident
Refs: 676104e, a215868
منبع: Conventional Commits 1.0.0