教你搭建你本身的Git服務器

http://lib.csdn.net/article/git/50086


導讀 如今咱們將要學習如何搭建 git 服務器,如何編寫自定義的 Git 鉤子來在特定的事件觸發相應的動做(例如通知),或者是發佈你的代碼到一個站點。

直到如今,咱們主要討論的仍是以一個使用者的身份與 Git 進行交互。這篇文章中我將討論 Git 的管理,而且設計一個靈活的 Git 框架。你可能會以爲這聽起來是 「高階 Git 技術」 或者 「只有狂熱粉才能閱讀」的一句委婉的說法,可是事實是這裏面的每一個任務都不須要很深的知識或者其餘特殊的訓練,就能基本理解 Git 的工做原理,有可能須要一丁點關於 Linux 的知識。

html

共享Git服務器

建立你本身的共享 Git 服務器意外地簡單,並且在不少狀況下,遇到的這點麻煩是徹底值得的。不只僅是由於它保證你有權限查看本身的代碼,它還能夠經過擴展爲 Git 的使用敞開了一扇大門,例如我的 Git 鉤子、無限制的數據存儲、和持續集成與分發(CI & CD)。linux

若是你知道如何使用 Git 和 SSH,那麼你已經知道怎麼建立一個 Git 服務器了。Git 的設計方式,就是讓你在建立或者 clone 一個倉庫的時候,就完成了一半服務器的搭建。而後容許用 SSH 訪問倉庫,並且任何有權限訪問的人均可以使用你的倉庫做爲 clone 的新倉庫的基礎。git

可是,這是一個小的點對點環境(ad-hoc)。按照一些方案你能夠建立一些帶有一樣的功能的設計優良的 Git 服務器,同時有更好的拓展性。web

首要之事:確認你的用戶們,如今的用戶以及以後的用戶都要考慮。若是你是惟一的用戶那麼沒有任何改動的必要。可是若是你試圖邀請其餘的代碼貢獻者使用,那麼你應該容許一個專門的分享系統用戶給你的開發者們。shell

假定你有一個可用的服務器(若是沒有,這不成問題,Git 會幫忙解決,CentOS 的 樹莓派 3 是個不錯的開始),而後第一步就是隻容許使用 SSH 密鑰認證的 SSH 登陸。這比使用密碼登陸安全得多,由於這能夠免於暴力破解,也能夠經過直接刪除用戶密鑰而禁用用戶。數組

一旦你啓用了 SSH 密鑰認證,建立 gituser 用戶。這是給你的全部受權的用戶們的公共用戶:安全

su -c 'adduser gituser'

而後切換到剛建立的 gituser 用戶,建立一個 ~/.ssh 的框架,並設置好合適的權限。這很重要,若是權限設置得太開放會使本身所保護的 SSH 沒有意義。服務器

su gituser
        $ mkdir .ssh && chmod 700 .ssh
        $ touch .ssh/authorized_keys
        $ chmod 600 .ssh/authorized_keys

authorized_keys 文件裏包含全部你的開發者們的 SSH 公鑰,你開放權限容許他們能夠在你的 Git 項目上工做。他們必須建立他們本身的 SSH 密鑰對而後把他們的公鑰給你。複製公鑰到 gituser 用戶下的 authorized_keys 文件中。例如,爲一個叫 Bob 的開發者,執行如下命令:app

cat ~/path/to/id_rsa.bob.pub >> /home/gituser/.ssh/authorized_keys

只要開發者 Bob 有私鑰而且把相對應的公鑰給你,Bob 就能夠用 gituser 用戶訪問服務器。框架

可是,你並非想讓你的開發者們能使用服務器,即便只是以 gituser 的身份訪問。你只是想給他們訪問 Git 倉庫的權限。由於這個特殊的緣由,Git 提供了一個限制的 shell,準確的說是 git-shell。以 root 身份執行如下命令,把 git-shell 添加到你的系統中,而後設置成 gituser 用戶的默認 shell。

grep git-shell /etc/shells || su -c "echo `which git-shell` >> /etc/shells"
        # su -c 'usermod -s git-shell gituser'

如今 gituser 用戶只能使用 SSH 來 push 或者 pull Git 倉庫,而且沒法使用任何一個能夠登陸的 shell。你應該把你本身添加到和 gituser 同樣的組中,在咱們的樣例服務器中這個組的名字也是 gituser。

舉個例子:

usermod -a -G gituser seth

僅剩下的一步就是建立一個 Git 倉庫。由於沒有人能在服務器上直接與 Git 交互(也就是說,你以後不能 SSH 到服務器而後直接操做這個倉庫),因此建立一個空的倉庫 。若是你想使用這個放在服務器上的倉庫來完成工做,你能夠從它的所在處 clone 下來,而後在你的 home 目錄下進行工做。

嚴格地講,你不是必須建立這個空的倉庫;它和一個正常的倉庫同樣工做。可是,一個空的倉庫沒有工做分支(working tree) (也就是說,使用 checkout 並無任何分支顯示)。這很重要,由於不容許遠程使用者們 push 到一個有效的分支上(若是你正在 dev 分支工做而後忽然有人把一些變動 push 到你的工做分支,你會有怎麼樣的感覺?)。由於一個空的倉庫能夠沒有有效的分支,因此這不會成爲一個問題。

你能夠把這個倉庫放到任何你想放的地方,只要你想要放開權限給用戶和用戶組,讓他們能夠在倉庫下工做。千萬不要保存目錄到好比說一個用戶的 home 目錄下,由於那裏有嚴格的權限限制。保存到一個常規的共享地址,例如 /opt 或者 /usr/local/share。

以 root 身份建立一個空的倉庫:

git init --bare /opt/jupiter.git
        # chown -R gituser:gituser /opt/jupiter.git
        # chmod -R 770 /opt/jupiter.git

如今任何一個用戶,只要他被認證爲 gituser 或者在 gituser 組中,就能夠從 jupiter.git 庫中讀取或者寫入。在本地機器嘗試如下操做:

git clone gituser@example.com:/opt/jupiter.git jupiter.clone
        Cloning into 'jupiter.clone'...
        Warning: you appear to have cloned an empty repository.

謹記:開發者們必定要把他們的 SSH 公鑰加入到 gituser 用戶下的 authorized_keys 文件裏,或者說,若是他們有服務器上的用戶(若是你給了他們用戶),那麼他們的用戶必須屬於 gituser 用戶組。

Git鉤子

運行你本身的 Git 服務器最讚的一件事之一就是可使用 Git 鉤子。Git 託管服務有時提供一個鉤子類的接口,可是他們並不會給你真正的 Git 鉤子來讓你訪問文件系統。Git 鉤子是一個腳本,它將在一個 Git 過程的某些點運行;鉤子能夠運行在當一個倉庫即將接收一個 commit 時、或者接受一個 commit 以後,或者即將接收一次 push 時,或者一次 push 以後等等。

這是一個簡單的系統:任何放在 .git/hooks 目錄下的腳本、使用標準的命名體系,就可按設計好的時間運行。一個腳本是否應該被運行取決於它的名字; pre-push 腳本在 push 以前運行,post-receive 腳本在接受 commit 以後運行等等。這或多或少的能夠從名字上看出來。

腳本能夠用任何語言寫;若是在你的系統上有能夠執行的腳本語言,例如輸出 ‘hello world’ ,那麼你就能夠這個語言來寫 Git 鉤子腳本。Git 默認帶了一些例子,可是並不有啓用。

想要動手試一個?這很簡單。若是你沒有現成的 Git 倉庫,首先建立一個 Git 倉庫:

mkdir jupiter
        $ cd jupiter
        $ git init .

而後寫一個 「hello world」 的 Git 鉤子。由於我爲了支持老舊系統而使用 tsch,因此我仍然用它做爲個人腳本語言,你能夠自由的使用本身喜歡的語言(Bash,Python,Ruby,Perl,Rust,Swift,Go):

echo "#/!/bin/tcsh" .git/hooks/post-commit
        $ echo "echo 'POST-COMMIT SCRIPT TRIGGERED'" >> ~/jupiter/.git/hooks/post-commit
        $ chmod +x ~/jupiter/.git/hooks/post-commit

如今測試它的輸出:

echo "hello world" foo.txt
        $ git add foo.txt
        $ git commit -m 'first commit'
        ! POST-COMMIT SCRIPT TRIGGERED
        [master (root-commit) c8678e0] first commit
        1 file changed, insertion(+)
        create mode 100644 foo.txt

如今你已經實現了:你的第一個有功能的 Git 鉤子。

有名的push-to-web鉤子

Git 鉤子最流行的用法就是自動 push 更改的代碼到一個正在使用中的產品級 Web 服務器目錄下。這是擺脫 FTP 的很好的方式,對於正在使用的產品保留完整的版本控制,整合並自動化內容的發佈。

若是操做正確,網站發佈工做會像之前同樣很好的完成,並且在某種程度上,很精準。Git 真的好棒。我不知道誰最初想到這個主意,可是我是從 Emacs 和 Git 方面的專家,IBM 的 Bill von Hagen 那裏第一次聽到它的。他的文章包含關於這個過程的權威介紹:Git 改變了分佈式網頁開發的遊戲規則。

Git 變量

每個 Git 鉤子都有一系列不一樣的變量對應觸發鉤子的不一樣 Git 行爲。你需不須要這些變量,主要取決於你寫的程序。若是你只是須要一個當某人 push 代碼時候的通用郵件通知,那麼你就不須要什麼特殊的東西,甚至也不須要編寫額外的腳本,由於已經有現成的適合你的樣例腳本。若是你想在郵件裏查看 commit 信息和 commit 的做者,那麼你的腳本就會變得相對麻煩些。

Git 鉤子並非被用戶直接執行,因此要弄清楚如何收集可能會混淆的重要信息。事實上,Git 鉤子腳本相似於其餘的腳本,像 BASH、Python、C++ 等等同樣從標準輸入讀取參數。不一樣的是,咱們不會給它提供這個輸入,因此,你在使用的時候,須要知道可能的輸入參數。

在寫 Git 鉤子以前,看一下 Git 在你的項目目錄下 .git/hooks 目錄中提供的一些例子。舉個例子,在這個 pre-push.sample 文件裏,註釋部分說明了以下內容:

$1 -- 即將 push 的遠程倉庫的名字
        # $2 -- 即將 push 的遠程倉庫的 URL
        # 若是 push 的時候,並無一個命名的遠程倉庫,那麼這兩個參數將會同樣。
        # 提交的信息將如下列形式按行發送給標準輸入
        #    

並非全部的例子都是這麼清晰,並且關於鉤子獲取變量的文檔依舊缺少(除非你去讀 Git 的源碼)。可是,若是你有疑問,你能夠從線上其餘用戶的嘗試中學習,或者你只是寫一些基本的腳本,好比 echo $1, $2, $3 等等。

分支檢測示例

我發現,對於生產環境來講有一個共同的需求,就是須要一個只有在特定分支被修改以後,纔會觸發事件的鉤子。如下就是如何跟蹤分支的示例。

首先,Git 鉤子自己是不受版本控制的。 Git 並不會跟蹤它本身的鉤子,由於對於鉤子來講,它是 Git 的一部分,而不是你倉庫的一部分。因此,Git 鉤子能夠監控你的 Git 服務器上的一個空倉庫的 commit 記錄和 push 記錄,而不是你本地倉庫的一部分。

咱們來寫一個 post-receive(也就是說,在 commit 被接受以後觸發)鉤子。第一步就是須要肯定分支名:

    #!/bin/tcsh
        foreach arg $< )
          set argv $arg )
          set refname $1
        end

這個 for 循環用來讀入第一個參數 $1 ,而後循環用第二個參數 $2 去覆蓋它,而後用第三個參數 $3 再這樣。在 Bash 中有一個更好的方法,使用 read 命令,而且把值放入數組裏。可是,這裏是 tcsh,而且變量的順序能夠預測的,因此,這個方法也是可行的。

當咱們有了 commit 記錄的 refname,咱們就能使用 Git 去找到這個分支的供人看的名字:

    set branch `git rev-parse --symbolic --abbrev-ref $refname`
        echo $branch #DEBUG

而後把這個分支名和咱們想要觸發的事件的分支名關鍵字進行比較:

    if "$branch" == "master" then
          echo "Branch detected: master"
          git /
            --work-tree=/path/to/where/you/want/to/copy/stuff/to /
            checkout -f $branch || echo "master fail"
        else if "$branch" == "dev" then
          echo "Branch detected: dev"
          Git /
            --work-tree=/path/to/where/you/want/to/copy/stuff/to /
            checkout -f $branch || echo "dev fail"
          else
            echo "Your push was successful."
            echo "Private branch detected. No action triggered."
        endif

給這個腳本分配可執行權限:

chmod +x ~/jupiter/.git/hooks/post-receive

如今,當一個用戶提交到服務器的 master 分支,那些代碼就會被複制到一個生產環境的目錄,提交到 dev 分支則會被複制到另外的地方,其餘分支將不會觸發這些操做。

同時,創造一個 pre-commit 腳本也很簡單。好比,判斷一個用戶是否在他們不應 push 的分支上 push 代碼,或者對 commit 信息進行解析等等。

Git 鉤子也能夠變得複雜,並且它們由於 Git 的工做流的抽象層次不一樣而變得難以理解,可是它們確實是一個強大的系統,讓你可以在你的 Git 基礎設施上針對全部的行爲進行對應的操做。若是你是一個 Git 重度用戶,或者一個全職 Git 管理員,那麼 Git 鉤子是值得學習的,只有當你熟悉這個過程,你才能真正掌握它。

在咱們這個系列下一篇也是最後一篇文章中,咱們將會學習如何使用 Git 來管理非文本的二進制數據,好比音頻和圖片。

 

本文地址:http://www.linuxprobe.com/found-git-server.html

 免費提供最新Linux技術教程書籍,爲開源技術愛好者努力作得更多更好:http://www.linuxprobe.com/

相關文章
相關標籤/搜索