Twin

無法建立遠端分支

今早同學遇到一個問題,發現某個分支怎麼推都推不上去,甚至是加了 -f 參數也一樣推不動,先來還原一下案發現場:

$ git branch
  feature/cch
* master

$ git push origin feature/cch
Total 0 (delta 0), reused 0 (delta 0)
remote: error: cannot lock ref 'refs/heads/feature/cch': 'refs/heads/feature' exists; cannot create 'refs/heads/feature/cch'
To /tmp/repo
! [remote rejected] feature/cch -> feature/cch (failed to update ref)
error: failed to push some refs to '/tmp/repo'

已經確定 remote 上並沒有 feature/cch 這個分支,但為什麼推不上去…在這之前,我們得先了解一下分支到底是什麼玩意兒。

分支其實是…

很多剛接觸 Git 的人,會以為「分支」是像樹枝或觸手一樣從本體(master)伸出去的,但在「為你自己學 Git」一書中也跟大家介紹過,Git 的分支就像貼紙一樣的概念,它會黏著某個 Commit,而它本身就是一個大小為 40 Bytes 的檔案,就這樣而已。

也就是說,當你每開一個分支,你的硬碟空間就少了 40 個 Bytes 的空間。

即然知道它是一個 40 個 Bytes 的檔案,那它放在哪裡?它就躺每個專案的 .git/refs/heads 目錄裡:

Branches

再比對一下我目前的分支:

$ git branch
  feature/cch
* master

不難發現所謂的「分支」,其實是以檔案的方式存放在這個目錄裡。

建立分支

即然知道建立分支就是在 .git/refs/heads 裡建立檔案(或目錄),讓我們暫時跳離 Git,先來看看這個狀況:

$ ls -al
total 0
drwxr-xr-x   2 kaochenlong  wheel   64 Oct  1 14:03 .
drwxrwxrwt  19 root         wheel  608 Oct  1 14:03 ..

現在目錄是空的,我試著建立一個叫做 cch 的檔案:

$ touch cch

$ ls -al
total 0
drwxr-xr-x   3 kaochenlong  wheel   96 Oct  1 14:04 .
drwxrwxrwt  19 root         wheel  608 Oct  1 14:03 ..
-rw-r--r--   1 kaochenlong  wheel    0 Oct  1 14:04 cch

看起來一切正常,然後再試著建立 cch 這個「目錄」:

$ mkdir cch
mkdir: cch: File exists

咦?沒辦法建立目錄?我是要建立目錄啊,跟我說「檔案已存在」做什麼?

事實上,對作業系統來說,「目錄」也只是一種比較特別的「檔案」,所以當你試著要在同一個目錄底下建立同名字的目錄的時候,作業系統會給你「檔案已存在」的訊息。

這跟在 Git 建立分支有什麼關係?我們前面才提到建立分支其實就是建立檔案(或目錄),所以如果我這樣做:

$ git branch feature

我先建立一個叫做 feature 的分支,接著再來試著建立 feature/cch 分支:

$ git branch feature/cch
fatal: cannot lock ref 'refs/heads/feature/cch': 'refs/heads/feature' exists; cannot create 'refs/heads/feature/cch'

這樣建立分支就失敗了,而且 Git 給你的訊息是 'refs/heads/feature' exists,就是跟你說那個「檔案」已經存在,你不能在已經有 feature 檔案的情況下,再建一個叫做 feature 的目錄了。

再回頭看看一開始推不上去的訊息:

$ git push origin feature/cch
Total 0 (delta 0), reused 0 (delta 0)
remote: error: cannot lock ref 'refs/heads/feature/cch': 'refs/heads/feature' exists; cannot create 'refs/heads/feature/cch'
To /tmp/repo
! [remote rejected] feature/cch -> feature/cch (failed to update ref)
error: failed to push some refs to '/tmp/repo'

中間那段訊息有覺得面熟嗎?看起來就是這個問題了。後來問了一下,果然是有其它同學在 remote 上有先建立了一個叫做 feature 的遠端分支,所以後面要推 feature/* 的人就推不上去了。

至於為什麼會有同學推了一個叫做 feature 的分支上去,這又是另一個故事了 :)

Comments