最近在和同事協同開發的時候,發生了一件事情:
由於我們還沒啟用CD(自動部署)但是更新測試機器又不一定是同一個人,為了好管控目前程式碼和目前測試機器上面的版本,我們使用了一個標籤叫做TestServer
來記錄。
不過當我同事更新的時候,他使用了testserver
作為標籤,結果我這邊發生了雖然local只有一個tagtestserver
,但是在remote上面其實有兩個標籤:TestServer
和testserver
。
為什麼會發生這個事情呢?如果不處理,未來在看的時候非常混亂,這讓我想起來一直想要寫的一篇文章,為git repo、tag和branch取名的時候應該要注意什麼比較不會有問題,因此兩篇一起寫,算是不好好naming會帶來什麼問題的use case範例。
搜索關鍵字:naming guidance for git repo, tag and branch.
解決方式 - TL;DR
如果remote出現了兩個一樣名字的標點,但是只是大小寫不同,那麼:
- 建議保留小寫的版本
- 用以下指令刪掉遠端有大寫的標籤
git push --delete {tagName}
例如:
git push --delete TestServer
取名建議 - TL;DR
基本上對於取名,不管是repo、branch還是tag的時候,建議:
- 全部使用小寫
由於習慣寫.net code,因此很習慣會用Pascal Casing來取名,例如
UnReleased
。但是,由於repo、branch和tag都屬於url的一部分,而url理論上是會區分大小寫並且 作業系統檔名是否區分大小寫處理也不同(windows不區分,Linux會區分),因此為了抓下來不會有問題,建議全部使用小寫。因此應該使用unreleased
。可是如果字太長要區分怎麼辦?參考下面分割字符
關於url應該區分大小寫,可以參考W3的HTML and URLS裡面提到:Users should always consider that URLs are case-sensitive
。- 使用
-
(aka 減號、dash)作為文字切割 如果名字太長想要切割的話,可以使用
-
來區別,例如:my-name-is
。有些會習慣使用
_
(下底線)。使用
-
有幾個好處:- 輸入
-
不用加shift - 比較好輸入 -
在regex不屬於word
而_
屬於,因此用來切割符號比較明顯
當然有時候字太長了,所以會混合用。
- 輸入
- 用
/
(斜線)來分類branch和tag - 如果有在用
git flow
應該會很習慣,主要是用/
(斜線)在工具像小烏龜可以簡單找到相關內容。 - tag使用
v
做前戳,搭配SemVar - 舉例來說,
v1.0.1
。加v
的好處是如果要列出和版號有關的tag,可以先打v
然後在按下tab
當然,上面都只是建議,沒有對錯,只是習慣或者避免一些小問題。
上面有些建議裡面已經有包含說明,如果對於一開始提到的問題發生原因有興趣的話,繼續往下看.....
重現問題
首先我們先來重現一下問題(這個重現方式和我實際發生的情境有點不同,因為我的情境需要兩個帳號交叉才看得到,不過local可以用以下方式重現,問題點是一樣)。
接下來,我們要:
- 建立一個repo
- 先打一個UnReleased的標籤
- push到remote
- 修改在打一個unreleased的標籤
- push到remote
- 檢查
建立一個repo
可以參考另外一篇該怎麼開專案的資料夾結構?每個專案應該要有的資料夾結構和檔案的Powershell語法來建立一個repo:
git clone https://github.com/alantsai/mhat-common-boilerplate-repo.git
cd mhat-common-boilerplate-repo
rm .git -Recurse -Force
git init
git add -A
git commit -m "init project"
這個時候,我們修改其中一些檔案。
先打一個UnReleased的標籤
做完修改commit之後我們打一個UnReleased
的tag
push到remote
在修改之後打一個unreleased的標籤
注意這邊的大小寫應該是都小寫的unreleased
。
檢查
發現到,在本機只剩下全小寫的那個unreleased
的標籤,有大寫的不見了,但是在remote,大小寫兩個標籤都在。
了解為什麼local變成一個而remote還是兩個
要了解這個發生的原因我們需要兩個前置的訊息:
- git tag是怎麼儲存
- 作業系統對待檔名大小寫的不同
git tag是怎麼儲存
git的資訊其實是存在.git\refs\tags
裡面(好吧,這個說法其實不太精準,以下會在說明),以我這邊為例,可以看到有一個叫做unreleased
的檔案,這個檔案的內容其實存的是一個hash值指向tag的位置。
了解這個之後搭配下面的資訊我們就知道為什麼了。
作業系統對待檔名大小寫的不同
Windows和Linux在處理很多事情上面其實不太一樣,這裡面有一個不同之處在於如何對待檔名大小寫:
- Windows不區分大小寫
- Linux區分大小寫
什麼意思呢?舉例來說,如果我有兩個檔案,一個叫做UnReleased
另外一個叫做released
:
- 在Windows,兩個檔案會互蓋,換句話說,在同一個資料夾,這兩個檔案只能留1個
- 在Linux,因為區分大小寫,因此在同一個資料夾兩個檔案可以共存,換句話說不會互蓋
搭配上面的資訊,為什麼變成一個的原因就浮出水面了,當第一個tag建立的時候,會有個檔案叫做UnReleased
出現,當第二個tag unreleased
出現的時候,因為Windows不區分大小寫,直接就把 UnReleased
蓋掉了,因此local變成只有一個。
那remote為什麼還是兩個呢?因為我們push上去的地方時github,那github host在linux(應該是啦,這個我沒找到Reference,所以說錯和我說一下,不過大部分的host應該都是liunx),而linux 區分大小寫,所以保留了下來。
例外情況
假設從sample repo clone程式碼下來,會發現兩個tag都存在阿,沒有所謂互蓋的情況啊,這是為什麼呢?
這個原因是因為,其實tag還有可能存在另外一個地方,那個地方是.git\packed-refs
,這個檔案主要是為了如果refs
很多,速度會比較慢,所以會把一些refs
放到packed-refs
裡面好讓讀取的時候速度比較快。
當clone下來的git tag資訊是存在那個裡面,所以不會有檔名問題,因此兩個tag都出現了,不過如果開始建立tag可以重現上述的問題。
結語
其實這一篇混雜了兩個主題:
- 建議的naming方式
- 如何解決因為naming問題導致出現兩個標籤的問題
由於兩個主題有相關因此寫在了一起,算是為了建議naming的一個use case範例。
不過就像上面說的,這邊的是建議而不是一定要這麼做 - 也是結合了網路上大家的建議,當然大家有不同的想法,因此不一定都認同,也希望提出不同意見讓整個資訊更加豐富。
在追蹤問題的時候,會發現其實需要一些git和OS之間差異的資訊才有辦法發現實際的問題 - 告訴我們 學無止境阿.....