最近在整理一個版本管理比較亂的項目,于是找了下版本號管理規(guī)范相關(guān)文檔丹莲;以下內(nèi)容均摘抄自語義化版本 2.0.0昼汗,感興趣的朋友可以去原文看下;
語義化版本 2.0.0
摘要
版本格式:主版本號.次版本號.修訂號取视,版本號遞增規(guī)則如下:
- 主版本號:當(dāng)你做了不兼容的 API 修改硝皂,
- 次版本號:當(dāng)你做了向下兼容的功能性新增,
- 修訂號:當(dāng)你做了向下兼容的問題修正作谭。
先行版本號及版本編譯元數(shù)據(jù)可以加到“主版本號.次版本號.修訂號”的后面稽物,作為延伸。
簡介
在軟件管理的領(lǐng)域里存在著被稱作“依賴地獄”的死亡之谷折欠,系統(tǒng)規(guī)模越大贝或,加入的包越多吼过,你就越有可能在未來的某一天發(fā)現(xiàn)自己已深陷絕望之中。
在依賴高的系統(tǒng)中發(fā)布新版本包可能很快會成為噩夢咪奖。如果依賴關(guān)系過高盗忱,可能面臨版本控制被鎖死的風(fēng)險(必須對每一個依賴包改版才能完成某次升級)。而如果依賴關(guān)系過于松散羊赵,又將無法避免版本的混亂(假設(shè)兼容于未來的多個版本已超出了合理數(shù)量)售淡。當(dāng)你專案的進展因為版本依賴被鎖死或版本混亂變得不夠簡便和可靠,就意味著你正處于依賴地獄之中慷垮。
作為這個問題的解決方案之一揖闸,我提議用一組簡單的規(guī)則及條件來約束版本號的配置和增長。這些規(guī)則是根據(jù)(但不局限于)已經(jīng)被各種封閉料身、開放源碼軟件所廣泛使用的慣例所設(shè)計汤纸。為了讓這套理論運作,你必須先有定義好的公共 API 芹血。這可以透過文件定義或代碼強制要求來實現(xiàn)贮泞。無論如何,這套 API 的清楚明了是十分重要的幔烛。一旦你定義了公共 API啃擦,你就可以透過修改相應(yīng)的版本號來向大家說明你的修改《鲂考慮使用這樣的版本號格式:X.Y.Z (主版本號.次版本號.修訂號)修復(fù)問題但不影響API 時令蛉,遞增修訂號;API 保持向下兼容的新增及修改時狡恬,遞增次版本號珠叔;進行不向下兼容的修改時,遞增主版本號弟劲。
我稱這套系統(tǒng)為“語義化的版本控制”祷安,在這套約定下,版本號及其更新方式包含了相鄰版本間的底層代碼和修改內(nèi)容的信息兔乞。
語義化版本控制規(guī)范(SemVer)
以下關(guān)鍵詞 MUST汇鞭、MUST NOT、REQUIRED庸追、SHALL霍骄、SHALL NOT、SHOULD锚国、SHOULD NOT腕巡、 RECOMMENDED玄坦、MAY血筑、OPTIONAL 依照 RFC 2119 的敘述解讀绘沉。(譯注:為了保持語句順暢, 以下文件遇到的關(guān)鍵詞將依照整句語義進行翻譯豺总,在此先不進行個別翻譯车伞。)
使用語義化版本控制的軟件必須(MUST)定義公共 API。該 API 可以在代碼中被定義或出現(xiàn)于嚴謹?shù)奈募?nèi)喻喳。無論何種形式都應(yīng)該力求精確且完整另玖。
標(biāo)準的版本號必須(MUST)采用 X.Y.Z 的格式,其中 X表伦、Y 和 Z 為非負的整數(shù)谦去,且禁止(MUST NOT)在數(shù)字前方補零。X 是主版本號蹦哼、Y 是次版本號鳄哭、而 Z 為修訂號。每個元素必須(MUST)以數(shù)值來遞增纲熏。例如:1.9.1 -> 1.10.0 -> 1.11.0妆丘。
標(biāo)記版本號的軟件發(fā)行后,禁止(MUST NOT)改變該版本軟件的內(nèi)容局劲。任何修改都必須(MUST)以新版本發(fā)行勺拣。
主版本號為零(0.y.z)的軟件處于開發(fā)初始階段,一切都可能隨時被改變鱼填。這樣的公共 API 不應(yīng)該被視為穩(wěn)定版药有。
1.0.0 的版本號用于界定公共 API 的形成。這一版本之后所有的版本號更新都基于公共 API 及其修改內(nèi)容苹丸。
修訂號 Z(x.y.Z
|
x > 0)必須(MUST)在只做了向下兼容的修正時才遞增塑猖。這里的修正指的是針對不正確結(jié)果而進行的內(nèi)部修改。次版本號 Y(x.Y.z
|
x > 0)必須(MUST)在有向下兼容的新功能出現(xiàn)時遞增谈跛。在任何公共 API 的功能被標(biāo)記為棄用時也必須(MUST)遞增羊苟。也可以(MAY)在內(nèi)部程序有大量新功能或改進被加入時遞增,其中可以(MAY)包括修訂級別的改變感憾。每當(dāng)次版本號遞增時蜡励,修訂號必須(MUST)歸零。主版本號 X(X.y.z
|
X > 0)必須(MUST)在有任何不兼容的修改被加入公共 API 時遞增阻桅。其中可以(MAY)包括次版本號及修訂級別的改變凉倚。每當(dāng)主版本號遞增時,次版本號和修訂號必須(MUST)歸零嫂沉。先行版本號可以(MAY)被標(biāo)注在修訂版之后稽寒,先加上一個連接號再加上一連串以句點分隔的標(biāo)識符來修飾。標(biāo)識符必須(MUST)由 ASCII 字母數(shù)字和連接號 [0-9A-Za-z-] 組成趟章,且禁止(MUST NOT)留白杏糙。數(shù)字型的標(biāo)識符禁止(MUST NOT)在前方補零慎王。先行版的優(yōu)先級低于相關(guān)聯(lián)的標(biāo)準版本。被標(biāo)上先行版本號則表示這個版本并非穩(wěn)定而且可能無法滿足預(yù)期的兼容性需求宏侍。范例:1.0.0-alpha赖淤、1.0.0-alpha.1、1.0.0-0.3.7谅河、1.0.0-x.7.z.92咱旱。
版本編譯元數(shù)據(jù)可以(MAY)被標(biāo)注在修訂版或先行版本號之后,先加上一個加號再加上一連串以句點分隔的標(biāo)識符來修飾绷耍。標(biāo)識符必須(MUST)由 ASCII 字母數(shù)字和連接號 [0-9A-Za-z-] 組成吐限,且禁止(MUST NOT)留白。當(dāng)判斷版本的優(yōu)先層級時褂始,版本編譯元數(shù)據(jù)可(SHOULD)被忽略毯盈。因此當(dāng)兩個版本只有在版本編譯元數(shù)據(jù)有差別時,屬于相同的優(yōu)先層級病袄。范例:1.0.0-alpha+001搂赋、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85益缠。
版本的優(yōu)先層級指的是不同版本在排序時如何比較脑奠。判斷優(yōu)先層級時,必須(MUST)把版本依序拆分為主版本號幅慌、次版本號宋欺、修訂號及先行版本號后進行比較(版本編譯元數(shù)據(jù)不在這份比較的列表中)。由左到右依序比較每個標(biāo)識符胰伍,第一個差異值用來決定優(yōu)先層級:主版本號齿诞、次版本號及修訂號以數(shù)值比較,例如:1.0.0 < 2.0.0 < 2.1.0 < 2.1.1骂租。當(dāng)主版本號祷杈、次版本號及修訂號都相同時,改以優(yōu)先層級比較低的先行版本號決定渗饮。例如:1.0.0-alpha < 1.0.0但汞。有相同主版本號、次版本號及修訂號的兩個先行版本號互站,其優(yōu)先層級必須(MUST)透過由左到右的每個被句點分隔的標(biāo)識符來比較私蕾,直到找到一個差異值后決定:只有數(shù)字的標(biāo)識符以數(shù)值高低比較,有字母或連接號時則逐字以 ASCII 的排序來比較胡桃。數(shù)字的標(biāo)識符比非數(shù)字的標(biāo)識符優(yōu)先層級低踩叭。若開頭的標(biāo)識符都相同時,欄位比較多的先行版本號優(yōu)先層級比較高。范例:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0容贝。
為什么要使用語義化的版本控制自脯?
這并不是一個新的或者革命性的想法。實際上嗤疯,你可能已經(jīng)在做一些近似的事情了。問題在于只是“近似”還不夠闺兢。如果沒有某個正式的規(guī)范可循茂缚,版本號對于依賴的管理并無實質(zhì)意義。將上述的想法命名并給予清楚的定義屋谭,讓你對軟件使用者傳達意向變得容易脚囊。一旦這些意向變得清楚,彈性(但又不會太彈性)的依賴規(guī)范就能達成桐磁。
舉個簡單的例子就可以展示語義化的版本控制如何讓依賴地獄成為過去悔耘。假設(shè)有個名為“救火車”的函式庫,它需要另一個名為“梯子”并已經(jīng)有使用語義化版本控制的包我擂。當(dāng)救火車創(chuàng)建時衬以,梯子的版本號為 3.1.0。因為救火車使用了一些版本 3.1.0 所新增的功能校摩, 你可以放心地指定依賴于梯子的版本號大等于 3.1.0 但小于 4.0.0看峻。這樣,當(dāng)梯子版本 3.1.1 和 3.2.0 發(fā)布時衙吩,你可以將直接它們納入你的包管理系統(tǒng)互妓,因為它們能與原有依賴的軟件兼容。
作為一位負責(zé)任的開發(fā)者坤塞,你理當(dāng)確保每次包升級的運作與版本號的表述一致》朊悖現(xiàn)實世界是復(fù)雜的,我們除了提高警覺外能做的不多摹芙。你所能做的就是讓語義化的版本控制為你提供一個健全的方式來發(fā)行以及升級包灼狰,而無需推出新的依賴包,節(jié)省你的時間及煩惱浮禾。
如果你對此認同伏嗜,希望立即開始使用語義化版本控制,你只需聲明你的函式庫正在使用它并遵循這些規(guī)則就可以了伐厌。請在你的 README 文件中保留此頁連結(jié)承绸,讓別人也知道這些規(guī)則并從中受益。
FAQ
在 0.y.z 初始開發(fā)階段挣轨,我該如何進行版本控制军熏?
最簡單的做法是以 0.1.0 作為你的初始化開發(fā)版本,并在后續(xù)的每次發(fā)行時遞增次版本號卷扮。
如何判斷發(fā)布 1.0.0 版本的時機荡澎?
當(dāng)你的軟件被用于正式環(huán)境均践,它應(yīng)該已經(jīng)達到了 1.0.0 版。如果你已經(jīng)有個穩(wěn)定的 API 被使用者依賴摩幔,也會是 1.0.0 版彤委。如果你很擔(dān)心向下兼容的問題,也應(yīng)該算是 1.0.0 版了或衡。
這不會阻礙快速開發(fā)和迭代嗎焦影?
主版本號為零的時候就是為了做快速開發(fā)。如果你每天都在改變 API封断,那么你應(yīng)該仍在主版本號為零的階段(0.y.z)斯辰,或是正在下個主版本的獨立開發(fā)分支中。
對于公共 API坡疼,若即使是最小但不向下兼容的改變都需要產(chǎn)生新的主版本號彬呻,豈不是很快就達到 42.0.0 版?
這是開發(fā)的責(zé)任感和前瞻性的問題柄瑰。不兼容的改變不應(yīng)該輕易被加入到有許多依賴代碼的軟件中闸氮。升級所付出的代價可能是巨大的。要遞增主版本號來發(fā)行不兼容的改版教沾,意味著你必須為這些改變所帶來的影響深思熟慮湖苞,并且評估所涉及的成本及效益比。
為整個公共 API 寫文件太費事了详囤!
為供他人使用的軟件編寫適當(dāng)?shù)奈募乒牵悄阕鳛橐幻麑I(yè)開發(fā)者應(yīng)盡的職責(zé)。保持專案高效一個非常重要的部份是掌控軟件的復(fù)雜度藏姐,如果沒有人知道如何使用你的軟件或不知道哪些函數(shù)的調(diào)用是可靠的隆箩,要掌控復(fù)雜度會是困難的。長遠來看羔杨,使用語義化版本控制以及對于公共 API 有良好規(guī)范的堅持捌臊,可以讓每個人及每件事都運行順暢。
萬一不小心把一個不兼容的改版當(dāng)成了次版本號發(fā)行了該怎么辦兜材?
一旦發(fā)現(xiàn)自己破壞了語義化版本控制的規(guī)范理澎,就要修正這個問題,并發(fā)行一個新的次版本號來更正這個問題并且恢復(fù)向下兼容曙寡。即使是這種情況糠爬,也不能去修改已發(fā)行的版本【偈可以的話执隧,將有問題的版本號記錄到文件中,告訴使用者問題所在,讓他們能夠意識到這是有問題的版本镀琉。
如果我更新了自己的依賴但沒有改變公共 API 該怎么辦峦嗤?
由于沒有影響到公共 API,這可以被認定是兼容的屋摔。若某個軟件和你的包有共同依賴烁设,則它會有自己的依賴規(guī)范,作者也會告知可能的沖突钓试。要判斷改版是屬于修訂等級或是次版等級装黑,是依據(jù)你更新的依賴關(guān)系是為了修復(fù)問題或是加入新功能。對于后者亚侠,我經(jīng)常會預(yù)期伴隨著更多的代碼曹体,這顯然會是一個次版本號級別的遞增俗扇。
如果我變更了公共 API 但無意中未遵循版本號的改動怎么辦呢硝烂?(意即在修訂等級的發(fā)布中,誤將重大且不兼容的改變加到代碼之中)
自行做最佳的判斷铜幽。如果你有龐大的使用者群在依照公共 API 的意圖而變更行為后會大受影響滞谢,那么最好做一次主版本的發(fā)布,即使嚴格來說這個修復(fù)僅是修訂等級的發(fā)布除抛。記住狮杨, 語義化的版本控制就是透過版本號的改變來傳達意義。若這些改變對你的使用者是重要的到忽,那就透過版本號來向他們說明橄教。
我該如何處理即將棄用的功能?
棄用現(xiàn)存的功能是軟件開發(fā)中的家常便飯喘漏,也通常是向前發(fā)展所必須的护蝶。當(dāng)你棄用部份公共 API 時,你應(yīng)該做兩件事:
(1)更新你的文件讓使用者知道這個改變翩迈,
(2)在適當(dāng)?shù)臅r機將棄用的功能透過新的次版本號發(fā)布持灰。在新的主版本完全移除棄用功能前,至少要有一個次版本包含這個棄用信息负饲,這樣使用者才能平順地轉(zhuǎn)移到新版 API堤魁。
語義化版本對于版本的字串長度是否有限制呢?
沒有返十,請自行做適當(dāng)?shù)呐袛嗤兹Ee例來說,長到 255 個字元的版本已過度夸張洞坑。再者涛漂,特定的系統(tǒng)對于字串長度可能會有他們自己的限制。
關(guān)于
語義化版本控制的規(guī)范是由 Gravatars 創(chuàng)辦者兼 GitHub 共同創(chuàng)辦者 Tom Preston-Werner 所建立。
如果您有任何建議匈仗,請到 GitHub 上提出您的問題瓢剿。
許可證
知識共享 署名 3.0 (CC BY 3.0)