Git 本地倉庫和裸倉庫

TaoBeier發表於2016-12-04

通常我們會用 git init 命令來將我們所在的目錄轉換為一個 Git 本地倉庫或者初始化一個新的空倉庫。

用法

  • 將當前目錄轉換為一個本地倉庫

git init

這個命令執行後會在本地生成一個 .git 的資料夾,用來追蹤倉庫的所有變更。效果如下:

git init

  • 指定某個目錄成為本地倉庫

git init <repo>

這個命令執行後, 將建立一個名為repo且只包含 .git 子資料夾的空目錄。效果如下:

git init repo

  • 指定某個目錄成為中心倉庫(裸倉庫)

git init --bare <repo> 

這個命令執行後,將在本地建立一個名為 repo 的資料夾, 裡面包含著 Git 的基本目錄, 我們一般會將這個資料夾命名為後面加 .git 的形式,如 repo.git (這也是為什麼我們從 GitHub clone 倉庫的時候,地址都是 xxx.git 這樣的形式的原因)。效果如下:

git init --bare repo.git

詳細說一下使用 --bare 引數的含義,使用 --bare 引數初始化的倉庫,我們一般稱之為裸倉庫, 因為這樣建立的倉庫並不包含 工作區 , 也就是說,我們並不能在這個目錄下執行我們一般使用的 Git 命令。

對比

我們來對比一下直接使用 git init 建立的倉庫和加了 --bare 引數的兩個倉庫。 我們直接看兩個倉庫的的 config 檔案中的內容:

  • 直接 git init 建立的倉庫:

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
  • 加了 --bare 建立的裸倉庫:

[core]
    repositoryformatversion = 0
    filemode = true
    bare = true
    ignorecase = true
    precomposeunicode = true

可以看到最直觀的差異在於 bare 配置項是否為 true , 此外不加 --bare 建立的本地倉庫配置中有一項 logallrefupdates = true , 作用根據名字就可以看出來, 記錄所有的 ref (引用) 更新, 關於 ref 的部分之後有時間可以再寫,這個配置可以理解為是 Git 的一道防線。

功能差異

我們可以使用最簡單的例子演示一下。


# 直接建立本地倉庫
(Tao) ➜  git init repo

# 建立裸倉庫
(Tao) ➜  git init --bare repo.git

# 分別 clone 兩個倉庫
(Tao) ➜  git clone repo c1
Cloning into `c1`...
warning: You appear to have cloned an empty repository.
done.
(Tao) ➜  git clone repo.git c2
Cloning into `c2`...
warning: You appear to have cloned an empty repository.
done.

# 進入 c1 倉庫

(Tao) ➜  cd c1
(Tao) ➜  c1 git:(master) touch test
(Tao) ➜  c1 git:(master) ✗ g add -A
(Tao) ➜  c1 git:(master) ✗ g commit -m "test commit"
[master (root-commit) b1e32ad] test commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test
(Tao) ➜  c1 git:(master) git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 200 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require `git reset --hard` to match
remote: error: the work tree to HEAD.
remote: error:
remote: error: You can set `receive.denyCurrentBranch` configuration variable to
remote: error: `ignore` or `warn` in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error:
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: `receive.denyCurrentBranch` configuration variable to `refuse`.
To /Users/tao/repo
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to `/Users/tao/repo`

# 進入 c2 倉庫重複執行

(Tao) ➜  c1 git:(master) cd ../c2
(Tao) ➜  c2 git:(master) touch test
(Tao) ➜  c2 git:(master) ✗ git add -A
(Tao) ➜  c2 git:(master) ✗ git commit -m "test commit"
[master (root-commit) 7aacc58] test commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test
(Tao) ➜  c2 git:(master) git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 201 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Users/tao/repo.git
 * [new branch]      master -> master

從裸倉庫 clone 下來的本地倉庫可以進行正常的 push 操作, 但是從一般倉庫 clone 下來的本地倉庫卻不行。 這也正是裸倉庫存在的意義。 裸倉庫一般情況下是作為遠端的中心倉庫而存在的。

總結

使用 git init --bare <repo> 可以建立一個裸倉庫,並且這個倉庫是可以被正常 clonepush 更新的, 裸倉庫不包含工作區,所以並不會存在在裸倉庫上直接提交變更的情況。


可以通過公眾號 TheMoeLove 和我聯絡

TheMoeLove

相關文章