<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    I want to fly higher
    programming Explorer
    posts - 114,comments - 263,trackbacks - 0

    Git

    
    
    分布版本控制系統(tǒng)

    Git誕生

    
    
    為什么Linus不把Linux代碼放到版本控制系統(tǒng)里呢?不是有CVS、SVN這些免費(fèi)的版本控制系統(tǒng)嗎?  
    因為Linus堅定地反對CVS和SVN,這些集中式的版本控制系統(tǒng)不但速度慢,而且必須聯(lián)網(wǎng)才能使用。
    有一些商用的版本控制系統(tǒng),雖然比CVS、SVN好用,但那是付費(fèi)的,和Linux的開源精神不符。

    集中式vs分布式

    
    
    集中式版本控制系統(tǒng),版本庫是集中存放在中央服務(wù)器的,而干活的時候,用的都是自己的電腦,所以要先從中央服務(wù)器取得最新的版本,然后開始干活,干完活了,再把自己的活推送給中央服務(wù)器。  

        集中式版本控制系統(tǒng)最大的毛病就是必須聯(lián)網(wǎng)才能工作,如果在局域網(wǎng)內(nèi)還好,帶寬夠大,速度夠快,可如果在互聯(lián)網(wǎng)上,遇到網(wǎng)速慢的話,可能提交一個10M的文件就需要5分鐘,這還不得把人給憋死啊。  
        
        分布式版本控制系統(tǒng)根本沒有“中央服務(wù)器”,每個人的電腦上都是一個完整的版本庫,這樣,你工作的時候,就不需要聯(lián)網(wǎng)了,因為版本庫就在你自己的電腦上。既然每個人電腦上都有一個完整的版本庫,那多個人如何協(xié)作呢?比方說你在自己電腦上改
    了文件A,你的同事也在他的電腦上改了文件A,這時,你們倆之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。----遠(yuǎn)程倉庫

        和集中式版本控制系統(tǒng)相比,分布式版本控制系統(tǒng)的安全性要高很多,因為每個人電腦里都有完整的版本庫,某一個人的電腦壞掉了不要緊,隨便從其他人那里復(fù)制一個就可以了。而集中式版本控制系統(tǒng)的中央服務(wù)器要是出了問題,所有人都沒法干活了。

        在實際使用分布式版本控制系統(tǒng)的時候,其實很少在兩人之間的電腦上推送版本庫的修改,因為可能你們倆不在一個局域網(wǎng)內(nèi),兩臺電腦互相訪問不了,也可能今天你的同事病了,他的電腦壓根沒有開機(jī)。因此,分布式版本控制系統(tǒng)通常也有一臺充當(dāng)
    “中央服務(wù)器”的電腦,但這個服務(wù)器的作用僅僅是用來方便“交換”大家的修改,沒有它大家也一樣干活,只是交換修改不方便而已。

        當(dāng)然,Git的優(yōu)勢不單是不必聯(lián)網(wǎng)這么簡單,后面我們還會看到Git極其強(qiáng)大的分支管理,把SVN等遠(yuǎn)遠(yuǎn)拋在了后面。

        集中式的服務(wù)器掛了所有人都掛了,因為完整倉庫只存在服務(wù)器上,分布式如果github掛了你可以重新建一個服務(wù)器,然后把任何一個人的倉庫clone過去。
        一句話總結(jié):分布式版本控制的每個節(jié)點都是完整倉庫。
        反之SVN則不可以,如果svn中央服務(wù)器掛了,是無法根據(jù)本地的項目搭建一個服務(wù)器的,因為本地沒有歷史版本。

        svn上你要創(chuàng)建分支,必須聯(lián)網(wǎng)。git就不用,直接就可以創(chuàng)建分支。感覺上就像是你在自己的機(jī)器上同時安裝了svn服務(wù)端和客戶端。git你可以在自己的機(jī)器上一個人隨便開發(fā),它擁有完整的版本控制服務(wù)。

        git沒有權(quán)限控制。
        “權(quán)限”問題是很多商業(yè)軟件選擇svn的最重要的原因。企業(yè)開發(fā)商業(yè)軟件,通常一個開發(fā)者只有一部分模塊的權(quán)限,用來避免完整的代碼被太多人獲取從而影響利益。商業(yè)軟件release一個版本通常會有專人負(fù)責(zé),或者用軟件自動構(gòu)建。開源就不同了,
    完全不用考慮這些。

    安裝Git(windows)

    我是從 https://git-scm.com/ 下載的Git-2.8.4-64-bit.exe
    安裝完畢后,命令后可以直接使用git:
        usage: git [--version] [--help] [-C <path>] [-c name=value]
               [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
               [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
               [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
               <command> [<args>]
        
    另外因為使用TortoiseSVN很長時間,所以對于git-gui,選擇了TortoiseGit-2.1.0.0-64bit.msi
    These are common Git commands used in various situations:

    創(chuàng)建版本庫

    
    
    1.git init
        Initialized empty Git repository
        目錄下生成一個.git隱藏目錄
    2.git add <file>,添加多個文件
    3.git commit -m "comment",提交
        注:后面兩個命令和svn命令相關(guān)命令相似,只是命令相似而已,具體底層實現(xiàn)并一定相同。

    
    
    git add readme.md   // 文件顏色變化顯示+號(tortoise-git)
    git commit -m "wrote a readme.md"   // 文件顯示綠色對勾(tortoise-git),提示顯示2(+)
        [master (root-commit) 90857d7] wrote a readme.md
        1 file changed, 2 insertions(+)
        create mode 100644 readme.md

    獲取工作區(qū)狀態(tài)、查看修改內(nèi)容

    
    
    git status  // 提示沒有提交的內(nèi)容
        On branch master
        nothing to commit, working directory clean

    
    
    // 修改readme.md,此時readme.md文件顯示紅色嘆號(tortoise-git)
    git status  // 顯示readme.md被修改了
        On branch master
        Changes not staged for commit:
          (use "git add <file>" to update what will be committed)
          (use "git checkout -- <file>" to discard changes in working directory)
            modified:   readme.md
        no changes added to commit (use "git add" and/or "git commit -a")

    
    
    git diff // 查看具體修改的內(nèi)容,最后面顯示加了兩行
        diff --git a/readme.md b/readme.md
        index 25339a4..9995557 100644
        --- a/readme.md
        +++ b/readme.md
        @@ -1,2 +1,4 @@
         ### This a git tutorial
        +
        +* git status

    
    
    git add readme.md // git add
    git status // add之后查看狀態(tài),顯示變化將被提交
        On branch master
        Changes to be committed:
            (use "git reset HEAD <file>" to unstage)
        modified:   readme.md

    
    
    git commit -m "add 2 lines"
    git status // commit之后,查看狀態(tài),顯示沒有東西提交
        On branch master
        nothing to commit, working directory clean

    版本回退

    
    
    git log // 查看歷史記錄,可以看到三次提交記錄
        commit d1eb111fe227cbd39168de8237726d5df575415f
        Author: mavsplus <mavsplus@aliyun.com>
        Date:   Tue Jun 14 16:23:31 2016 +0800
            delete chinese
        commit 0b602fb113c8bbad53e6fbfe7edea62ebcea781e
        Author: mavsplus <mavsplus@aliyun.com>
        Date:   Tue Jun 14 16:12:32 2016 +0800
            add 2 lines
        commit 90857d7381c9c860e23e01c1bca20724e250c94a
        Author: mavsplus <mavsplus@aliyun.com>
        Date:   Tue Jun 14 15:52:16 2016 +0800
            wrote a readme.md
    git log --pretty=oneline // 調(diào)整展示形式
        d1eb111fe227cbd39168de8237726d5df575415f delete chinese
        0b602fb113c8bbad53e6fbfe7edea62ebcea781e add 2 lines
        90857d7381c9c860e23e01c1bca20724e250c94a wrote a readme.md
    // commit id,即版本號,和SVN不同,不是從1,2,3遞增的數(shù)字,而是一個SHA1計算出來。因為Git是一個分布式的版本控制系統(tǒng),版本號不能沖突。

    
    
    版本回退的話,Git必須知道當(dāng)前版本是哪個版本,在Git中,用HEAD表示當(dāng)前版本。上一個版本就是HEAD^,上上一個版本就是HEAD^^,當(dāng)然往上100個版本寫100個^比較容易數(shù)不過來,所以寫成HEAD~100。
    git reset --hard HEAD^  // 版本回退到上一個版本
    // 注意用這個命令出現(xiàn) More?
    git reset --hard HEAD~  // 用~替換^,版本順利回退到上一個版本
        HEAD is now at 0b602fb add 2 lines // 0b602fb即上一個版本的commit id的前幾位 
    git reset --hard d1eb111 // 根據(jù)版本號回退,目前這個版本號是最新的一個版本號
        HEAD is now at d1eb111 delete chinese
    // 1.版本號沒有必要全寫,前幾位就可以,這里是7位
    // 2.版本回退既可以向前,也可以向后,只需要版本號即可,是不是有點雙向鏈表的意思。
    // 3.版本回退快的原因是因為Git在內(nèi)部有個指向當(dāng)前版本的HEAD【指針],當(dāng)你回退版本的時候,Git僅僅是不斷的將HEAD指針指向不同的commit id

    
    
    如果找不到commit id怎么辦?
    git reflog  // 用來記錄你的每一次命令
        d1eb111 HEAD@{0}: reset: moving to d1eb111
        0b602fb HEAD@{1}: reset: moving to HEAD~
        d1eb111 HEAD@{2}: commit: delete chinese
        0b602fb HEAD@{3}: commit: add 2 lines
        90857d7 HEAD@{4}: commit (initial): wrote a readme.md
    // 可以看到每次的commit id,有了commit id,有回退到任何版本都可以,前進(jìn)后退都可以

    工作區(qū)和暫存區(qū)

    
    
    Git和其他版本控制系統(tǒng)如SVN的一個不同之處就是有暫存區(qū)的概念。
    電腦里能看到的目錄,如本人的git_demo文件夾就是一個工作區(qū),該目錄下有上面舉例的readme.md,以及初始化創(chuàng)建的版本庫,.git隱藏目錄。
    Git版本庫中重要的東西:
        1.稱為stage(或者叫index)的暫存區(qū)
        2.Git為我們自動創(chuàng)建的第一個分支master
        3.指向master的一個指針叫HEAD

    示意圖:

    image

    
    
    git add 命令實際上就是把要提交的所有修改放到暫存區(qū)(Stage),然后,執(zhí)行g(shù)it commit就可以一次性把暫存區(qū)的所有修改提交到分支。

    經(jīng)過git add后,示意圖:

    image

    經(jīng)過git commit后,示意圖:

    image

    
    
    注意:這只是示意圖
    1.從理解上看,暫存區(qū)就像購物車。
    2.當(dāng)多個文件修改完成放入暫存區(qū)的時候,發(fā)現(xiàn)其中一個文件的代碼有問題,這時候你可以用checkout單獨(dú)將這個文件還原重改;如果你將這些文件一次性全部放進(jìn)庫里,等你發(fā)現(xiàn)有問題時就不能單獨(dú)拿出一個文件了,只能版本回檔。
    3.其實commit后,暫存區(qū)并不是"沒有任何內(nèi)容了"。這句話不妥,暫存區(qū)不是沒有任何內(nèi)容了,而是暫存區(qū)的內(nèi)容都已經(jīng)同步到了最近的一次提交。  
        git diff --cached 命令可以對比暫存區(qū)和最近一次提交的不同。當(dāng)使用該命令后,會發(fā)現(xiàn)沒有任何輸出,則說明暫存區(qū)和已提交倉庫是完全一樣的。
    4.git diff 查看當(dāng)前沒有add的內(nèi)容修改,即是比較的是a(暫存區(qū))和 b(工作區(qū),本地文件)。
    5.git diff --cached 比較的是a(已提交倉庫)和 b(暫存區(qū))。
    6.其實commit操作只是讓暫存區(qū)和分支(master)版本保持一致,是同步操作。

    管理修改

    
    
    Git管理的是修改,當(dāng)你用git add命令后,在工作區(qū)的第一次修改被放入暫存區(qū),準(zhǔn)備提交,但是,在工作區(qū)的第二次修改并沒有放入暫存區(qū),所以,git commit只負(fù)責(zé)把暫存區(qū)的修改提交了,也就是第一次的修改被提交了,第二次的修改不會被提交。
    git diff HEAD -- readme.md  命令可以查看工作區(qū)和版本庫里面最新版本的區(qū)別
    第一次修改 -> git add -> 第二次修改 -> git add -> git commit,好,現(xiàn)在,把第二次修改提交了。
    每次修改,如果不add到暫存區(qū),那就不會加入到commit中。

    
    
    git commit -m "not add,commit directly" // 沒有add,直接commit,會提示"Changes not staged"
        On branch master
        Changes not staged for commit:
                modified:   readme.md
        no changes added to commit

    
    
    git commit -m "add,then commit" // 下面是正確commit的輸出
        [master b60ed5f] add,then commit
         1 file changed, 1 insertion(+)

    撤銷修改

    
    
    git checkout -- readme.md 命令 意思就是,把readme.md文件在工作區(qū)的修改全部撤銷,這里有兩種情況:
        一種是readme.md自修改后還沒有被放到暫存區(qū),現(xiàn)在,撤銷修改就回到和版本庫一模一樣的狀態(tài);
        一種是readme.md已經(jīng)添加到暫存區(qū)后,又作了修改,現(xiàn)在,撤銷修改就回到添加到暫存區(qū)后的狀態(tài)。
    總之,就是讓這個文件回到最近一次git commit或git add時的狀態(tài)。

    
    
    git checkout -- readme.md   // 撤銷修改,沒有任何輸出
    git reset HEAD file //  可以把暫存區(qū)的修改撤銷掉(unstage),重新放回工作區(qū)
    git reset HEAD readme.md
        Unstaged changes after reset:
        M       readme.md
    git reset命令既可以回退版本,也可以把暫存區(qū)的修改回退到工作區(qū)。當(dāng)我們用HEAD時,表示最新的版本。

    
    
    場景1:當(dāng)你改亂了工作區(qū)某個文件的內(nèi)容,想直接丟棄工作區(qū)的修改時,用命令git checkout -- file。
    場景2:當(dāng)你不但改亂了工作區(qū)某個文件的內(nèi)容,還添加到了暫存區(qū)時,想丟棄修改,分兩步,第一步用命令git reset HEAD file,就回到了場景1,第二步按場景1操作。
    場景3:已經(jīng)提交了不合適的修改到版本庫時,想要撤銷本次提交,則參考"版本回退"一節(jié)。不過前提是沒有推送到遠(yuǎn)程庫。

    刪除文件

    
    
    git add delete.txt  // 增加一個新文件
    git commit -m "add delete.txt"  // 提交至master
        [master 6e56cbf] add delete.txt
         1 file changed, 0 insertions(+), 0 deletions(-)
         create mode 100644 delete.txt
    del delete.txt  // 刪除,dos命令
    git status  // 查看狀態(tài),顯示刪除了文件
        On branch master
        Changes not staged for commit:
          (use "git add/rm <file>" to update what will be committed)
          (use "git checkout -- <file>" to discard changes in working directory)
                deleted:    delete.txt
        no changes added to commit (use "git add" and/or "git commit -a")
    git rm delete.txt   // 從版本庫中刪除并且提交
        rm 'delete.txt'
    git commit -m "delete delete.txt"
        [master 211d1ae] delete delete.txt
         1 file changed, 0 insertions(+), 0 deletions(-)
         delete mode 100644 delete.txt
    git status  // 此時工作區(qū)"干凈"了
        On branch master
        nothing to commit, working directory clean

    
    
    git add delete2.txt // 添加另一個文件
    git commit -m "another delete.txt"  // 提交
        [master 7655a07] another delete.txt
         1 file changed, 0 insertions(+), 0 deletions(-)
         create mode 100644 delete2.txt
    del delete2.txt // dos刪除
    git checkout -- delete2.txt // 還原,還原可能誤刪的文件
    git checkout其實是用版本庫里的版本替換工作區(qū)的版本,無論工作區(qū)是修改還是刪除,都可以“一鍵還原”。

    遠(yuǎn)程倉庫

    
    
    Git是分布式版本控制系統(tǒng),同一個Git倉庫,可以分布到不同的機(jī)器上。怎么分布呢?最早,肯定只有一臺機(jī)器有一個原始版本庫,此后,別的機(jī)器可以“克隆”這個原始版本庫,而且每臺機(jī)器的版本庫其實都是一樣的,并沒有主次之分。
    GitHub,提供Git倉庫托管服務(wù)。只要注冊一個GitHub賬號,就可以免費(fèi)獲得Git遠(yuǎn)程倉庫。
    注:
        1.本人未按照教程的方式創(chuàng)建SSH Key,我是之前clone了github的項目,然后push遠(yuǎn)程的時候輸入
        了github的賬號和密碼。
        2.因為需要將本地庫push,所以創(chuàng)建ssh key,因為本地Git倉庫和GitHub倉庫之間的傳輸是通過SSH加密
        3.因為安裝了Git-2.8.4-64-bit.exe,安裝完畢后,會有一個GitBash,打開,輸入:
            $ ssh-keygen -t rsa -C "mavsplus@aliyun.com"
          則在用戶主.ssh目錄下生成id_rsa、id_rsa.pub
        4.在github的settings#SSH Keys#New SSH key,將id_ras.pub中的添加進(jìn)去即可。
        5.為什么GitHub需要SSH Key呢?因為GitHub需要識別出你推送的提交確實是你推送的,而不是別人冒充的,而Git支持SSH協(xié)議,所以,GitHub只要知道了你的公鑰,就可以確認(rèn)只有你自己才能推送。

    添加遠(yuǎn)程庫

    
    
    1. github上New repository,取名gitTutorial,訪問地址:https://github.com/mavsplus/gitTutorial
    2. git remote add origin git@github.com:mavsplus/gitTutorial.git // 將本地倉庫與github上的庫關(guān)聯(lián),遠(yuǎn)程庫的名字是origin
    3. git push -u origin master // 把本地庫的所有內(nèi)容推送到遠(yuǎn)程庫中
        報錯:
            Permission denied (publickey).
            fatal: Could not read from remote repository.
        解決:
            還是需要添加publickey,即需要創(chuàng)建ssh key并和github賬號綁定.
        繼續(xù)報錯:
            ! [rejected]        master -> master (fetch first)
            ! [rejected]        master -> master (non-fast-forward)
        原因:因為github倉庫中自動創(chuàng)建了一個README.md,不允許直接覆蓋上去.
        解決: git push --force origin master // 強(qiáng)制push,遠(yuǎn)程倉庫的readme.md被覆蓋或者fetch first.
                Counting objects: 18, done.
                Delta compression using up to 8 threads.
                Compressing objects: 100% (12/12), done.
                Writing objects: 100% (18/18), 1.56 KiB | 0 bytes/s, done.
                Total 18 (delta 2), reused 0 (delta 0)
                To git@github.com:mavsplus/gitTutorial.git
                 + fb1fd6a7655a07 master -> master (forced update)
    4. 此時 https://github.com/mavsplus/gitTutorial 展示和本地完全同步

    
    
    要關(guān)聯(lián)一個遠(yuǎn)程庫,使用命令git remote add origin git@server-name:path/repo-name.git;
    關(guān)聯(lián)后,使用命令git push -u origin master第一次推送master分支的所有內(nèi)容;
    此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
    加上了-u參數(shù),Git不但會把本地的master分支內(nèi)容推送的遠(yuǎn)程新的master分支,還會把本地的master分支和遠(yuǎn)程的master分支關(guān)聯(lián)起來。

    從遠(yuǎn)程庫克隆

    
    
    git clone git@github.com:mavsplus/gitTutorial.git // 從github上遠(yuǎn)程克隆
        Cloning into 'gitTutorial'
        remote: Counting objects: 18, done.
        remote: Compressing objects: 100% (10/10), done.
        remote: Total 18 (delta 2), reused 18 (delta 2), pack-reused 0
        Receiving objects: 100% (18/18), done.
        Resolving deltas: 100% (2/2), done.
        Checking connectivity done.
    要克隆一個倉庫,首先必須知道倉庫的地址,然后使用git clone命令克隆。
    Git支持多種協(xié)議,包括https,但通過ssh支持的原生git協(xié)議速度最快。
    使用https除了速度慢以外,還有個最大的麻煩是每次推送都必須輸入口令,但是在某些只開放http端口的公司內(nèi)部就無法使用ssh協(xié)議而只能用https。

    分支管理

    
    
    分支在實際中有什么用呢?假設(shè)你準(zhǔn)備開發(fā)一個新功能,但是需要兩周才能完成,第一周你寫了50%的代碼,如果立刻提交,由于代碼還沒寫完,不完整的代碼庫會導(dǎo)致別人不能干活了。如果等代碼全部寫完再一次提交,又存在丟失每天進(jìn)度的巨大風(fēng)險。
    現(xiàn)在有了分支,就不用怕了。你創(chuàng)建了一個屬于你自己的分支,別人看不到,還繼續(xù)在原來的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到開發(fā)完畢后,再一次性合并到原來的分支上,這樣,既安全,又不影響別人工作。
    其他版本控制系統(tǒng)如SVN等都有分支管理,但是用過之后你會發(fā)現(xiàn),這些版本控制系統(tǒng)創(chuàng)建和切換分支比蝸牛還慢,簡直讓人無法忍受,結(jié)果分支功能成了擺設(shè),大家都不去用。
    但Git的分支是與眾不同的,無論創(chuàng)建、切換和刪除分支,Git在1秒鐘之內(nèi)就能完成!無論你的版本庫是1個文件還是1萬個文件。

    創(chuàng)建與合并分支

    
    
    Git倉庫初始化有一個默認(rèn)主分支,即Master分支.
    一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當(dāng)前分支,以及當(dāng)前分支的提交點:

    image

    
    
    每次提交,master分支都會向前移動一步,這樣,隨著你不斷提交,master分支的線也越來越長:
    當(dāng)我們創(chuàng)建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當(dāng)前分支在dev上:

    image

    
    
    你看,Git創(chuàng)建一個分支很快,因為除了增加一個dev指針,改改HEAD的指向,工作區(qū)的文件都沒有任何變化!不過,從現(xiàn)在開始,對工作區(qū)的修改和提交就是針對dev分支了,比如新提交一次后,dev指針往前移動一步,而master指針不變:

    image

    
    
    假如我們在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最簡單的方法,就是直接把master指向dev的當(dāng)前提交,就完成了合并:

    image

    
    
    所以Git合并分支也很快!就改改指針,工作區(qū)內(nèi)容也不變!
    合并完分支后,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉后,我們就剩下了一條master分支:

    image


    
    
    git checkout -b dev // 創(chuàng)建dev分支,然后切換到dev分支,-b參數(shù)表示創(chuàng)建并切換
        Switched to a new branch 'dev'
    // 上面的命令等同于下面兩個命令
    // git branch dev
    // git checkout dev
    git branch  // 列出所有分支,當(dāng)前分支前面會標(biāo)一個*號
        * dev
          master
    git add readme.md   // 在dev分支add并commit
    git commit -m "branch dev"  // 從commit的輸出看,會顯示在當(dāng)前哪個分支進(jìn)行操作,即[dev 10c4e87]
        [dev 10c4e87] branch dev
        1 file changed, 1 insertion(+)
    git checkout master // dev分支的工作完成,我們就可以切換回master分支
        Switched to branch 'master'
    // 此時發(fā)現(xiàn)readme.md被reload了,之前在dev添加的內(nèi)容不見了.因為那個提交是在dev分支上,而master分支此刻的提交點并沒有變:

    image


    
    
    git merge dev   // 把dev分支的工作成果合并到master分支上
        Updating 7655a07..10c4e87
        Fast-forward
        readme.md | 1 +
        1 file changed, 1 insertion(+)
    git merge命令用于合并指定分支到當(dāng)前分支。合并后,再查看readme.txt的內(nèi)容,就可以看到,和dev分支的最新提交是完全一樣的。
    注意到上面的Fast-forward信息,Git告訴我們,這次合并是“快進(jìn)模式”,也就是直接把master指向dev的當(dāng)前提交,所以合并速度非???。
    當(dāng)然,也不是每次合并都能Fast-forward.
    合并完成后,就可以放心地刪除dev分支了.
    git branch -d dev // 合并完成后,就可以放心地刪除dev分支了.
        Deleted branch dev (was 10c4e87).
    git branch  // 查看分支,此時只剩下master分支了
        * master
    因為創(chuàng)建、合并和刪除分支非???,所以Git鼓勵你使用分支完成某個任務(wù),合并后再刪掉分支,這和直接在master分支上工作效果是一樣的,但過程更安全。

    
    
    Git鼓勵大量使用分支:
    查看分支:git branch
    創(chuàng)建分支:git branch <name>
    切換分支:git checkout <name>
    創(chuàng)建+切換分支:git checkout -b <name>
    合并某分支到當(dāng)前分支:git merge <name>
    刪除分支:git branch -d <name>

    解決沖突

    
    
    git checkout -b dev2 // 準(zhǔn)備一個新分支
        Switched to a new branch 'dev2'
    git add readme.md   // 修改dev2分支readme.md,添加一行并提交
    git commit -m "dev2 branch"
        [dev2 fd26fe3] dev2 branch
        1 file changed, 1 insertion(+)
    git checkout master // 切回master
        Switched to branch 'master'
    git add readme.md // 同樣修改readme.md,和dev2分支修改同一行,造成沖突
    git commit -m "switch 2 master"
        git commit -m "switch 2 master"
    git merge dev2 // 合并,提示沖突
        Auto-merging readme.md
        CONFLICT (content): Merge conflict in readme.md
        Automatic merge failed; fix conflicts and then commit the result.
    git status // 通過查看狀態(tài)也可以看到?jīng)_突
        On branch master
        You have unmerged paths.
          (fix conflicts and run "git commit")
        Unmerged paths:
          (use "git add <file>" to mark resolution)
                both modified:   readme.md
        no changes added to commit (use "git add" and/or "git commit -a")
    此時查看readme.md文件,發(fā)現(xiàn)沖突(沖突形式也類似svn)
        <<<<<<< HEAD
        * swith to master branch
        =======
        * create a new dev2 branch
        >>>>>>> dev2

    image

    
    
    master分支和dev2分支各自都分別有新的提交.這種情況下,Git無法執(zhí)行“快速合并”,只能試圖把各自的修改合并起來,但這種合并就可能會有沖突。
    只有解決沖突,再次提交,我是這樣解決的:
        * create a new dev2 branch
        * swith to master branch
    git add readme.md   // 在master解決沖突后add,commit
    git commit -m "conflict fixed"

    image

    此時master分支和dev2分支變成了是上圖所示
    

    
    
    git log --graph --pretty=oneline --abbrev-commit // 可以看到分支合并的情況
        *   aea7a48 conflict fixed
        |\
        | * fd26fe3 dev2 branch
        * | 45458d8 switch 2 master
        |/
        * 10c4e87 branch dev
        * 7655a07 another delete.txt
        * 211d1ae delete delete.txt
        * 6e56cbf add delete.txt
        * b60ed5f add,then commit
        * d1eb111 delete chinese
        * 0b602fb add 2 lines
        * 90857d7 wrote a readme.md
    git branch -d dev2 // 最后刪除dev2分支
        Deleted branch dev2 (was fd26fe3).

    
    
    首先git分支沒有主次之分,master叫主分支其實和別的分支完全一樣,其次沖突是由于兩個分支merge引起的。沖突是因為兩個分支A、B各自都有自己的commit,而且修改內(nèi)容可能是同一文件的同一行,導(dǎo)致git合并時無法自動merge,就有了沖突。

    分支管理策略

    
    
    通常,合并分支時,如果可能,Git會用Fast forward模式,但這種模式下,刪除分支后,會丟掉分支信息。
        即一旦刪除分支后,我們既無法知道分支存在過,也無法區(qū)分哪些修改是在分支上提交的。
    如果要強(qiáng)制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。
    首先看一下使用ff模式:
        git checkout -b dev2 // 分支dev2
        git add readme.md
        git commit -m "ff"
        git checkout master // 切回master
        git merge dev2 // ff模式合并
            Updating aea7a48..f2c207f
            Fast-forward
             readme.md | 1 +
             1 file changed, 1 insertion(+)
        git branch -d dev2 // 刪除dev2分支,注:無論是否刪除分支,都看不出來曾經(jīng)做過合并
        git log --graph --pretty=oneline --abbrev-commit // 查看分支歷史,看不出來曾經(jīng)做過合并
            * f2c207f ff

    
    
    下面看一下禁用Fast forward模式
        git checkout -b dev3 // 分支dev3
        git add readme.md
        git commit -m "no ff"
        git checkout master
        git merge --no-ff -m "merge with no-ff" dev3 // 禁用ff模式,因為會生成一個新的commit,所以-m指定了提交描述,另外可以看到合并使用的是'recursive'策略
            Merge made by the 'recursive' strategy.
            readme.md | 1 +
            1 file changed, 1 insertion(+)
        git log --graph --pretty=oneline --abbrev-commit // 查看分支歷史,可以清楚的看到合并的記錄
            *   5faa867 merge with no-ff
            |\
            | * 77d4303 no ff
            |/
        可以看到,不使用Fast forward模式,merge后就像這樣,即master又commit了一次

    image

    分支策略
    
    
    首先,master分支應(yīng)該是非常穩(wěn)定的,也就是僅用來發(fā)布新版本,平時不能在上面干活;
    那在哪干活呢?干活都在dev分支上,也就是說,dev分支是不穩(wěn)定的,到某個時候,比如1.0版本發(fā)布時,再把dev分支合并到master上,在master分支發(fā)布1.0版本;
    你和你的小伙伴們每個人都在dev分支上干活,每個人都有自己的分支,時不時地往dev分支上合并就可以了。
    所以,團(tuán)隊合作的分支看起來就像這樣:

    image


    
    
    ff和no ff的結(jié)果都是一樣的,都會合并,其最大區(qū)別在于,ff會"默默"地合并,其僅僅是將指針移動到分支的最新處(當(dāng)然刪除分支并不會對master產(chǎn)生任何影響),而no ff則是重新建立了一個commit,然后將合并當(dāng)做一個comiit來處理,自然就產(chǎn)生了合并的信息.
    所以,所謂的分支信息丟失,在這里是指:合并的時候,master的log里面沒有任何的合并記錄!
    所以,可想而知,如果你的master很重要,某一次合并后出現(xiàn)問題,而你又是使用ff,這里沒有任何記錄可以讓你回退版本的!(回退只有靠commit id).

    
    
    合并分支時,加上--no-ff參數(shù)就可以用普通模式合并,合并后的歷史有分支,能看出來曾經(jīng)做過合并,而fast forward合并就看不出來曾經(jīng)做過合并。

    Bug分支

    
    
    在Git中,由于分支是如此的強(qiáng)大,所以,每個bug都可以通過一個新的臨時分支來修復(fù),修復(fù)后,合并分支,然后將臨時分支刪除。
    當(dāng)你接到一個修復(fù)一個代號101的bug的任務(wù)時,很自然地,你想創(chuàng)建一個分支issue-101來修復(fù)它,但是,等等,當(dāng)前正在dev上進(jìn)行的工作還沒有提交:
    并不是你不想提交,而是工作只進(jìn)行到一半,還沒法提交,預(yù)計完成還需1天時間。但是,必須在兩個小時內(nèi)修復(fù)該bug,怎么辦?
    幸好,Git還提供了一個stash功能,可以把當(dāng)前工作現(xiàn)場“儲藏”起來,等以后恢復(fù)現(xiàn)場后繼續(xù)工作:

    
    
    git checkout -b dev // 有一個dev分支,修改readme.md,此時發(fā)現(xiàn)了master有一個bug:
    git stash // 存儲當(dāng)前工作現(xiàn)場,此時發(fā)現(xiàn)readme.md中之前在dev分支修改的內(nèi)容不見了
        Saved working directory and index state WIP on dev: 0085d48 try ff,not delete branch
        HEAD is now at 0085d48 try ff,not delete branch
    git status  // 隱藏當(dāng)前工作區(qū)后,發(fā)現(xiàn)工作區(qū)"clean"
        On branch dev
        nothing to commit, working directory clean
    git checkout master // 切換master并從master建立一個臨時分支bug-001
    git checkout -b bug-001
    git add readme.md   // 在bug-001分支修復(fù)bug并提交
    git commit -m "fix bug 001"
    git checkout master
    git merge --no-ff -m "merged bug fix 001" bug-001 // 合并回master
    git branch -d bug-001 // 刪除臨時分支
    git checkout dev // 切換會dev分支繼續(xù)干活
    git stash list // 查看之前保存的工作現(xiàn)場,發(fā)現(xiàn)現(xiàn)場還在
        stash@{0}: WIP on dev: 0085d48 try ff,not delete branch
    git stash pop // 恢復(fù)現(xiàn)場并刪除stash內(nèi)容,此時發(fā)現(xiàn)之前dev的修改的內(nèi)容已經(jīng)恢復(fù)
        On branch dev
        Changes not staged for commit:
          (use "git add <file>" to update what will be committed)
          (use "git checkout -- <file>" to discard changes in working directory)
                modified:   readme.md
        no changes added to commit (use "git add" and/or "git commit -a")
        Dropped refs/stash@{0} (24ac8b196b4e9ff1191e7b1a9e5812e792e6b92e)
    git stash list // 此時沒有任何之前保存的現(xiàn)場
    還有一種是用git stash apply恢復(fù),但是恢復(fù)后,stash內(nèi)容并不刪除:
    你可以多次stash,恢復(fù)的時候,先用git stash list查看,然后恢復(fù)指定的stash,用命令"
        git stash apply stash@{0}

    Feature分支

    
    
    添加一個新功能時,你肯定不希望因為一些實驗性質(zhì)的代碼,把主分支搞亂了,所以,每添加一個新功能,最好新建一個feature分支,在上面開發(fā),完成后,合并,最后,刪除該feature分支。
    git checkout -b dev // 我們本身在dev分支
    git checkout -b feature // 創(chuàng)建一個feature分支用來開發(fā)新功能,新建了一個feature.txt
    git branch  // 查看分支,三個分支,當(dāng)前在feature分支
          dev
        * feature
          master
    git add feature.txt // 在feature分支提交
    git commit -m "add feature.txt"
    git checkout dev // 切回dev分支準(zhǔn)備合并新功能
    一切順利的話,feature分支和bug分支是類似的,合并,然后刪除。
    但是,就在此時,接到上級命令,因經(jīng)費(fèi)不足,新功能必須取消!
    雖然白干了,但是這個分支還是必須就地銷毀:
    git branch -d feature // 刪除分支,提示feature分支還沒有被合并
        error: The branch 'feature' is not fully merged.
        If you are sure you want to delete it, run 'git branch -D feature'.
    git branch -D feature // 強(qiáng)制刪除
        Deleted branch feature (was 48030ee).
    git branch // 此時查看分支,只剩下dev和master了
        * dev
        master

    
    
    開發(fā)一個新feature,最好新建一個分支;
    如果要丟棄一個沒有被合并過的分支,可以通過git branch -D <name>強(qiáng)行刪除。

    
    
    新分支是基于當(dāng)前分支而創(chuàng)建的,如果是在dev下新建分支issue,那么issue就是dev下的分支,issue分支的所有文件都繼承了dev分支的所有文件

    多人協(xié)作

    
    
    當(dāng)你從遠(yuǎn)程倉庫克隆時,實際上Git自動把本地的master分支和遠(yuǎn)程的master分支對應(yīng)起來了,并且,遠(yuǎn)程倉庫的默認(rèn)名稱是origin。
    git remote // 查看遠(yuǎn)程庫信息
        origin
    git remote -v // 查看更詳細(xì)的遠(yuǎn)程庫信息
        origin  git@github.com:mavsplus/gitTutorial.git (fetch)
        origin  git@github.com:mavsplus/gitTutorial.git (push)
    上面顯示了可以抓取和推送的origin的地址
    現(xiàn)在本地的dev分支和master分支各自有不同的內(nèi)容,比如dev分支下有一個dev.txt,而master分支下有一個master.txt
    而:推送分支,就是把該分支上的所有本地提交推送到遠(yuǎn)程庫。推送時,要指定本地分支,這樣,Git就會把[該分支推送到遠(yuǎn)程庫對應(yīng)的遠(yuǎn)程分支上]:
    git push origin master // 推送本地master分支到遠(yuǎn)程庫對應(yīng)的master分支
        Warning: Permanently added the RSA host key for IP address '192.30.252.121' to the list of known hosts.
        Counting objects: 34, done.
        Delta compression using up to 8 threads.
        Compressing objects: 100% (34/34), done.
        Writing objects: 100% (34/34), 3.01 KiB | 0 bytes/s, done.
        Total 34 (delta 13), reused 0 (delta 0)
        To git@github.com:mavsplus/gitTutorial.git
           7655a07..1da3118  master -> master
    git push origin dev // 推送本地dev分支到遠(yuǎn)程庫對應(yīng)的dev分支
        Warning: Permanently added the RSA host key for IP address '192.30.252.131' to the list of known hosts.
        Counting objects: 2, done.
        Delta compression using up to 8 threads.
        Compressing objects: 100% (2/2), done.
        Writing objects: 100% (2/2), 267 bytes | 0 bytes/s, done.
        Total 2 (delta 0), reused 0 (delta 0)
        To git@github.com:mavsplus/gitTutorial.git
         * [new branch]      dev -> dev
    
    
    此時查看github,https://github.com/mavsplus/gitTutorial/branches/all,可以github的遠(yuǎn)程庫有兩個分支,master和dev,完美的和本地同步但是,并不是一定要把本地分支往遠(yuǎn)程推送,那么,哪些分支需要推送,哪些不需要呢?
        master分支是主分支,因此要時刻與遠(yuǎn)程同步;
        dev分支是開發(fā)分支,團(tuán)隊所有成員都需要在上面工作,所以也需要與遠(yuǎn)程同步;
        bug分支只用于在本地修復(fù)bug,就沒必要推到遠(yuǎn)程了,除非老板要看看你每周到底修復(fù)了幾個bug;
        feature分支是否推到遠(yuǎn)程,取決于你是否和你的小伙伴合作在上面開發(fā)。
        總之,就是在Git中,分支完全可以在本地自己藏著玩,是否推送,視你的心情而定!

    
    
    多人協(xié)作的工作模式通常是這樣:
        1.首先,可以試圖用git push origin branch-name推送自己的修改;
        2.如果推送失敗,則因為遠(yuǎn)程分支比你的本地更新,需要先用git pull試圖合并;
        3.如果合并有沖突,則解決沖突,并在本地提交;
        4.沒有沖突或者解決掉沖突后,再用git push origin branch-name推送就能成功!
            如果git pull提示“no tracking information”,則說明本地分支和遠(yuǎn)程分支的鏈接關(guān)系沒有創(chuàng)建,用命令git branch --set-upstream branch-name origin/branch-name。
        這就是多人協(xié)作的工作模式,一旦熟悉了,就非常簡單。

    模擬一下多人協(xié)作的情形:
    
    git clone git@github.com:mavsplus/gitTutorial.git // 另一個小伙伴從遠(yuǎn)程克隆到本地,注意也可以使用https,只不過較慢(https://github.com/mavsplus/gitTutorial.git)
        Cloning into 'gitTutorial'
        remote: Counting objects: 54, done.
        remote: Compressing objects: 100% (33/33), done.
        remote: Total 54 (delta 18), reused 51 (delta 15), pack-reused 0
        Receiving objects: 100% (54/54), 4.60 KiB | 0 bytes/s, done.
        Resolving deltas: 100% (18/18), done.
        Checking connectivity done.
    gitTutorial>git branch // 從遠(yuǎn)程庫clone時,默認(rèn)情況下,你的小伙伴只能看到本地的master分支
        * master
    gitTutorial>git checkout -b dev origin/dev // 小伙伴要在dev分支上開發(fā),就必須創(chuàng)建遠(yuǎn)程origin的dev分支到本地,此時本地可以看到dev分支的dev.txt了
        Branch dev set up to track remote branch dev from origin.
        Switched to a new branch 'dev'
    gitTutorial>git add dev.txt // 本地修改dev.txt,默認(rèn)文件為空,增加一行
    gitTutorial>git commit -m "first modify dev.txt" // 本地提交
    gitTutorial>git push origin dev // 推送至遠(yuǎn)程的dev分支,此時github的dev分支已同步
        Counting objects: 3, done.
        Delta compression using up to 8 threads.
        Compressing objects: 100% (2/2), done.
        Writing objects: 100% (3/3), 336 bytes | 0 bytes/s, done.
        Total 3 (delta 0), reused 0 (delta 0)
        To git@github.com:mavsplus/gitTutorial.git
           9c93773..34fe4de  dev -> dev
    git_demo>git checkout dev // 而此時你也在dev下面進(jìn)行了修改,注意是git_demo目錄,和上面區(qū)分,表示是兩個伙伴進(jìn)行開發(fā)
    git_demo>git add dev.txt // 修改并提交
    git_demo>git commit -m "landon modify dev.txt"
    git_demo>git push origin dev // 推送至遠(yuǎn)程,此時提示拒絕push,原因很簡單:沖突了,因為你的小伙伴的最新提交和你試圖推送的提交有沖突
        To git@github.com:mavsplus/gitTutorial.git
        ! [rejected]        dev -> dev (fetch first)
        error: failed to push some refs to 'git@github.com:mavsplus/gitTutorial.git'
        hint: Updates were rejected because the remote contains work that you do
        hint: not have locally. This is usually caused by another repository pushing
        hint: to the same ref. You may want to first integrate the remote changes
        hint: (e.g., 'git pull ') before pushing again.
        hint: See the 'Note about fast-forwards' in 'git push --help' for details.
    解決辦法也很簡單,先用git pull(提示fetch first)把最新的提交從origin/dev抓下來,然后,在本地合并,解決沖突,再推送:
    git_demo>git pull // 從遠(yuǎn)程拉取,提示no tracking information并給出了命令提示
        remote: Counting objects: 3, done.
        remote: Compressing objects: 100% (2/2), done.
        remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
        Unpacking objects: 100% (3/3), done.
        From github.com:mavsplus/gitTutorial
           9c93773..34fe4de  dev        -> origin/dev
        There is no tracking information for the current branch.
        Please specify which branch you want to merge with.
        See git-pull(1) for details.
        git pull <remote> <branch>
        If you wish to set tracking information for this branch you can do so with:
        git branch --set-upstream-to=origin/<branch> dev
    git_demo>git branch --set-upstream-to=origin/dev dev // 指定本地dev分支與遠(yuǎn)程origin/dev分支的鏈接,注意教程中的git branch --set-upstream dev origin/dev已經(jīng)被deprecated了.
        Branch dev set up to track remote branch dev from origin.
    git_demo>git pull // 再次拉取,提示自動合并沖突(dev.txt顯示黃色三角嘆號_TortoiseGit)
        Auto-merging dev.txt
        CONFLICT (content): Merge conflict in dev.txt
        Automatic merge failed; fix conflicts and then commit the result.
    下面是沖突的文件
        <<<<<<< HEAD
        i'm the landon's modify
        =======
        i'm first modify the dev.txt
        >>>>>>> 34fe4deebc74c4a8a780c3dd0f61c45976a25786
    需要手動解決沖突,然后提交,再push:
    git_demo>git add dev.txt
    git_demo>git commit -m "fix merge conflict"
    git_demo>git push origin dev
        Counting objects: 6, done.
        Delta compression using up to 8 threads.
        Compressing objects: 100% (5/5), done.
        Writing objects: 100% (6/6), 676 bytes | 0 bytes/s, done.
        Total 6 (delta 0), reused 0 (delta 0)
        To git@github.com:mavsplus/gitTutorial.git
           34fe4de..ef938cd  dev -> dev
    順利push到遠(yuǎn)程,此時查看遠(yuǎn)程github的dev分支的dev.txt,已經(jīng)是完美同步

    
    
    總結(jié):
    1.查看遠(yuǎn)程庫信息,使用git remote -v;
    2.本地新建的分支如果不推送到遠(yuǎn)程,對其他人就是不可見的;
    3.從本地推送分支,使用git push origin branch-name,如果推送失敗,先用git pull抓取遠(yuǎn)程的新提交;
    4.在本地創(chuàng)建和遠(yuǎn)程分支對應(yīng)的分支,使用git checkout -b branch-name origin/branch-name,本地和遠(yuǎn)程分支的名稱最好一致;
    5.建立本地分支和遠(yuǎn)程分支的關(guān)聯(lián),使用git branch --set-upstream-to=origin/<branch> branch-name;// 教程中的該命令已經(jīng)被廢棄了
    6.從遠(yuǎn)程抓取分支,使用git pull,如果有沖突,要先處理沖突。

    標(biāo)簽管理

    
    
    發(fā)布一個版本時,我們通常先在版本庫中打一個標(biāo)簽,這樣,就唯一確定了打標(biāo)簽時刻的版本。將來無論什么時候,取某個標(biāo)簽的版本,就是把那個打標(biāo)簽的時刻的歷史版本取出來。所以,標(biāo)簽也是版本庫的一個快照。
    Git的標(biāo)簽雖然是版本庫的快照,但其實它就是指向某個commit的指針(跟分支很像對不對?但是分支可以移動,標(biāo)簽不能移動),所以,創(chuàng)建和刪除標(biāo)簽都是瞬間完成的。

    創(chuàng)建標(biāo)簽

    
    
    git branch // 查看分支并切換至master
    git checkout master
    git tag v1.0.0 // 打一個新標(biāo)簽
    git tag // 查看所有標(biāo)簽,默認(rèn)標(biāo)簽是打在最新提交的commit上的
        v1.0.0
    git log --pretty=oneline --abbrev-commit // 查看提交記錄
        1da3118 add master.txt
        4447507 conflict fixed
        b434e98 dev develop
        caf4705 merged bug fix 001
        884d29c fix bug 001
        0085d48 try ff,not delete branch
        5faa867 merge with no-ff
        77d4303 no ff
        f2c207f ff
        aea7a48 conflict fixed
        45458d8 switch 2 master
        fd26fe3 dev2 branch
        10c4e87 branch dev
        7655a07 another delete.txt
        211d1ae delete delete.txt
        6e56cbf add delete.txt
        b60ed5f add,then commit
        d1eb111 delete chinese
        0b602fb add 2 lines
        90857d7 wrote a readme.md
    git tag v0.9.1 10c4e87 // 可以給某個commit id打tag
    git show v0.9.1 // 查看某個指定的tag
        commit 10c4e875cffda57cd4eff41782c23dfb59897f60
        Author: mavsplus <mavsplus@aliyun.com>
        Date:   Thu Jun 16 10:04:29 2016 +0800
            branch dev
        diff --git a/readme.md b/readme.md
        index 68f2f90..b65fc2f 100644
        --- a/readme.md
        +++ b/readme.md
        @@ -2,3 +2,4 @@
         * git status
         * not add,commit directly
        +* create a new dev branch
    git tag -a v0.9.2 -m "version 0.9.2 tag" 884d29c // 創(chuàng)建帶有說明的tag
    git show v0.9.2 // 查看v0.9.2的tag
        tag v0.9.2
        Tagger: mavsplus <mavsplus@aliyun.com>
        Date:   Sat Jun 18 14:46:39 2016 +0800
        version 0.9.2 tag
        commit 884d29c5fbbf720f07fe842f3bf2d2173d3787e9
        Author: mavsplus <mavsplus@aliyun.com>
        Date:   Thu Jun 16 15:20:33 2016 +0800
            fix bug 001
        diff --git a/readme.md b/readme.md
        index 048f963..ece4787 100644
        --- a/readme.md
        +++ b/readme.md
        @@ -7,4 +7,4 @@
         * swith to master branch
         * Fast Forward
         * no Fast Forward
        -* Try Fast Forward,no delete branch
        \ No newline at end of file
    git tag -s v0.9.3 -m "signed version 0.9.3 released" b434e98 // 過-s用私鑰簽名一個tag,這里提示錯誤
        gpg: directory `/c/Users/pc/.gnupg' created
        gpg: new configuration file `/c/Users/pc/.gnupg/gpg.conf' created
        gpg: WARNING: options in `/c/Users/pc/.gnupg/gpg.conf' are not yet active during this run
        gpg: keyring `/c/Users/pc/.gnupg/secring.gpg' created
        gpg: keyring `/c/Users/pc/.gnupg/pubring.gpg' created
        gpg: skipped "mavsplus <mavsplus@aliyun.com>": secret key not available
        gpg: signing failed: secret key not available
        error: gpg failed to sign the data
        error: unable to sign the tag
    // 注:需要安裝gnupg,https://www.gnupg.org/ (The GNU Privacy Guard),并gpg --gen-key
    // tag待gpg簽名原因:保證項目作者(私鑰持有者)所制定的里程碑別人將無法修改。那么,就可以說,作者的代碼是安全傳播的。

    操作標(biāo)簽

    
    
    git tag // 本地master有三個tag
        v0.9.1
        v0.9.2
        v1.0.0
    git tag -d v0.9.2 // 刪除某個tag
    git push origin v0.9.1 // 將tag推送到遠(yuǎn)程
        Warning: Permanently added the RSA host key for IP address '192.30.252.130' to the lis
        Total 0 (delta 0), reused 0 (delta 0)
        To git@github.com:mavsplus/gitTutorial.git
         * [new tag]         v0.9.1 -> v0.9.1
    此時我們在github項目的release中可以看到版本v0.9.1,https://github.com/mavsplus/gitTutorial/releases,并且可以查看該版本相關(guān)信息.
    git push origin --tags // 一次性推送全部尚未推送到遠(yuǎn)程的本地標(biāo)簽
        Total 0 (delta 0), reused 0 (delta 0)
        To git@github.com:mavsplus/gitTutorial.git
         * [new tag]         v1.0.0 -> v1.0.0
    此時可以看到github可以看到1.0.0和0.9.1兩個版本,https://github.com/mavsplus/gitTutorial/releases
    如何刪除遠(yuǎn)程標(biāo)簽?
    git tag -d v0.9.1 // 先本地刪除
        Deleted tag 'v0.9.1' (was 10c4e87)
    git push origin :refs/tags/v0.9.1 // 從遠(yuǎn)程刪除,注意origin后面的空格
        To git@github.com:mavsplus/gitTutorial.git
        - [deleted]         v0.9.1
    此時去github項目的release頁面,發(fā)現(xiàn)只剩下v1.0.0版本了

    
    
    小結(jié)
    1.命令git push origin <tagname>可以推送一個本地標(biāo)簽;
    2.命令git push origin --tags可以推送全部未推送過的本地標(biāo)簽;
    3.命令git tag -d <tagname>可以刪除一個本地標(biāo)簽;
    4.命令git push origin :refs/tags/<tagname>可以刪除一個遠(yuǎn)程標(biāo)簽。

    使用GitHub

    
    
    如何參與一個開源項目呢?
    如,https://github.com/netty/netty,點擊左上角的Fork即可在自己的賬號下克隆了一個netty倉庫,注:[fork-分叉,用餐的叉子,兩個方向,一模一樣,可以理解為副本],如 https://github.com/mavsplus/netty
    從自己的賬號下clone:
    git clone git@github.com:mavsplus/netty.git
        Cloning into 'netty'
        remote: Counting objects: 203726, done.
        remote: Total 203726 (delta 0), reused 0 (delta 0), pack-reused 203726
        Receiving objects: 100% (203726/203726), 40.11 MiB | 785.00 KiB/s, done.
        Resolving deltas: 100% (90325/90325), done.
        Checking connectivity done.
    一定要從自己的賬號下clone倉庫,這樣你才能推送修改。從原作者的倉庫克隆,沒有權(quán)限,不能推送修改.
    你想修復(fù)netty的一個bug,或者新增一個功能,立刻就可以開始干活,干完后,往自己的倉庫推送。
    如果你希望netty的官方庫能接受你的修改,你就可以在GitHub上發(fā)起一個[pull request]。當(dāng)然,對方是否接受你的pull request就不一定了。

    
    
    1.在GitHub上,可以任意Fork開源倉庫;
    3.自己擁有Fork后的倉庫的讀寫權(quán)限;
    3.可以推送pull request給官方倉庫來貢獻(xiàn)代碼。

    自定義git

    
    
    因為我們安裝的windows版本的Git-2.8.4-64-bit,所以目前cmd下使用命令行,目前會顯示配色,還是比較方便。
    git config --global color.ui true // 顯示顏色,這個命令先記下來

    忽略特殊文件

    這個和svn的ignore類似
    在Git工作區(qū)的根目錄下創(chuàng)建一個特殊的.gitignore文件,然后把要忽略的文件名填進(jìn)去,Git就會自動忽略這些文件。
    不需要從頭寫.gitignore文件,GitHub已經(jīng)為我們準(zhǔn)備了各種配置文件,只需要組合一下就可以使用了。所有配置文件可以直接在線瀏覽:https://github.com/github/gitignore
    如:https://github.com/github/gitignore/blob/master/Java.gitignore,直接參考該文件內(nèi)容,編寫一個.gitignore文件(cmd>type nul > .gitignore生成一個.文件,直接無法新建),放到工作區(qū)根目錄,該文件格式如下
        *.class
        # Mobile Tools for Java (J2ME)
        .mtj.tmp/
        # Package Files #
        *.jar
        *.war
        *.ear
        # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
        hs_err_pid*
    檢驗.gitignore的標(biāo)準(zhǔn)是git status命令是不是說working directory clean
        編輯一個Hello.java,命令行用javac編譯,生成Hello.class
        此時發(fā)現(xiàn)Hello.class加了一個白色橫線(TortoiseGit),說明.gitigore起了作用
    git add Hello.class // 如果強(qiáng)迫add,則提示該文件被忽略,當(dāng)然可以使用-f參數(shù)強(qiáng)制添加
        The following paths are ignored by one of your .gitignore files:
        Hello.class
        Use -f if you really want to add them.
    git check-ignore -v Hello.class //  你發(fā)現(xiàn),可能是.gitignore寫得有問題,需要找出來到底哪個規(guī)則寫錯了,可以用check-ginore命令檢查
        .gitignore:1:*.class    Hello.class
            可以看到提示.gitignore的第一行規(guī)則忽略了該文件,那么我們就可以知道應(yīng)該修訂哪個規(guī)則
    1.忽略某些文件時,需要編寫.gitignore;
    2..gitignore文件本身要放到版本庫里,并且可以對.gitignore做版本管理

    配置別名

    
    
    svn經(jīng)常中有一些別名,如checkout (co),commit (ci)等(可以通過svn --help查看)
    git config --global alias.co checkout // 定義checkout的別名為co
    git co dev  // 使用別名的checkout
        Switched to branch 'dev'
        Your branch is up-to-date with 'origin/dev'.
    --global參數(shù)是全局參數(shù),也就是這些命令在這臺電腦的所有Git倉庫下都有用
    配置Git的時候,加上--global是針對當(dāng)前用戶起作用的,如果不加,那只針對當(dāng)前的倉庫起作用。
    每個倉庫的Git配置文件都放在.git/config文件中,配置別名也可以修改這個文件:[alias] aliasName = originName
        [core]
            repositoryformatversion = 0
            filemode = false
            bare = false
            logallrefupdates = true
            symlinks = false
            ignorecase = true
        [remote "origin"]
             url = git@github.com:mavsplus/gitTutorial.git
             fetch = +refs/heads/*:refs/remotes/origin/*
        [branch "dev"]
              remote = origin
              merge = refs/heads/dev
    當(dāng)前用戶的Git配置文件放在用戶主目錄下的一個隱藏文件.gitconfig中:
        [user]
                name = mavsplus
                email = mavsplus@aliyun.com
        [core]
                autocrlf = true
        [alias]
                co = checkout
    可以看到別名一行,配置別名也可以修改這個文件

    
    
    git config --get-regexp alias // 查看別名列表
    git config --global --unset alias.co // 取消別名

    搭建Git服務(wù)器

    
    
    主要是windows搭建(因為linux太簡單了)
        主要是git和ssh即可,git已經(jīng)安裝,這里git要用ssh協(xié)議。ssh軟件用Copssh即可,主要搭建過程略.(說實話,之所以略過,1.因為實際環(huán)境操作中,用windows作為git服務(wù)器的太少,而且即使如svn,windows上面有快速搭建git服務(wù)器的一系列工具如visualsvn,后續(xù)肯定會有類似工具,一鍵搭建工具 2.實際環(huán)境中,我肯定是用linux搭建git服務(wù)器的 3.而對于客戶端開發(fā)而言,因為相對來說大部分都是windows進(jìn)行開發(fā),所以之前的例子全部是在dos下進(jìn)行g(shù)it命令的講解)
        ps:如What is Gitblit? Gitblit is an open-source, pure Java stack for managing, viewing, and serving Git repositories。

    
    
    要方便管理公鑰,用Gitosis;
    要像SVN那樣變態(tài)地控制權(quán)限,用Gitolite。

    使用linux搭建Git服務(wù)器
    
    
    本次主要使用vmware-centos 6.5-Final作為目標(biāo)linux。
    安裝git,使用yum:
    1.[root@localhost ~]# yum -y install git // yum安裝git
        Loaded plugins: fastestmirror, refresh-packagekit, security
        Loading mirror speeds from cached hostfile
         * base: mirror.bit.edu.cn
         * extras: mirror.bit.edu.cn
         * updates: mirrors.sina.cn
        Setting up Install Process
        Resolving Dependencies
        --> Running transaction check
        ---> Package git.x86_64 0:1.7.1-4.el6_7.1 will be installed
        --> Processing Dependency: perl-Git = 1.7.1-4.el6_7.1 for package: git-1.7.1-4.el6_7.1.x86_64
        --> Processing Dependency: perl(Git) for package: git-1.7.1-4.el6_7.1.x86_64
        --> Processing Dependency: perl(Error) for package: git-1.7.1-4.el6_7.1.x86_64
        --> Running transaction check
        ---> Package perl-Error.noarch 1:0.17015-4.el6 will be installed
        ---> Package perl-Git.noarch 0:1.7.1-4.el6_7.1 will be installed
        
    2.[root@localhost ~]# git // 可以看到git已成功安裝
        usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
                   [-p|--paginate|--no-pager] [--no-replace-objects]
                   [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
                   [--help] COMMAND [ARGS]
        The most commonly used git commands are:
           add        Add file contents to the index
           bisect     Find by binary search the change that introduced a bug
           branch     List, create, or delete branches
           checkout   Checkout a branch or paths to the working tree
           clone      Clone a repository into a new directory
           commit     Record changes to the repository
           diff       Show changes between commits, commit and working tree, etc
           fetch      Download objects and refs from another repository
           grep       Print lines matching a pattern
           init       Create an empty git repository or reinitialize an existing one
           log        Show commit logs
           merge      Join two or more development histories together
           mv         Move or rename a file, a directory, or a symlink
           pull       Fetch from and merge with another repository or a local branch
           push       Update remote refs along with associated objects
           rebase     Forward-port local commits to the updated upstream head
           reset      Reset current HEAD to the specified state
           rm         Remove files from the working tree and from the index
           show       Show various types of objects
           status     Show the working tree status
           tag        Create, list, delete or verify a tag object signed with GPG
        See 'git help COMMAND' for more information on a specific command.
    3.[root@localhost ~]# cat /etc/ssh/sshd_config  // 查看ssh#RSA配置信息,并去掉注釋
        #RSAAuthentication yes
        #PubkeyAuthentication yes
        #AuthorizedKeysFile     .ssh/authorized_keys
      可以看到公鑰存儲在.ssh/authorized_keys中
    4.[root@localhost ~]# adduser git // 這里注意和useradd的區(qū)別
      [root@localhost ~]# cd /home/git/
      [root@localhost ~]# mkdir .ssh
      [root@localhost ~]# chown -R git:git .ssh/
      [root@localhost ~]# cat id_rsa.pub >> authorized_keys
        添加一個git用戶,在/home/git下建一個.ssh目錄,在ssh目錄下創(chuàng)建authorized_keys,然后將之前的ssh-keygen生成的SSH Key導(dǎo)入該文件中
    5.[root@localhost git]# git init --bare cavs.git // 初始化一個空庫(為什么用cavs,因為北京時間2016年6月20日,克里夫蘭騎士隊奪得NBA總冠軍)
        Initialized empty Git repository in /git/cavs.git/
    6.[root@localhost git]# chown -R git:git cavs.git // change owner
    7.[root@localhost git]# vim /etc/passwd // 禁用git用戶的shell登陸
        修改為類似的某行為:
        git:x:501:501::/home/git:/usr/bin/git-shell
        git用戶可以正常通過ssh使用git,但無法登錄shell,因為我們?yōu)間it用戶指定的git-shell每次一登錄就自動退出。
    8.>git clone git@192.168.168.129:/git/cavs.git // 客戶端順利克隆(windows)
            Cloning into 'cavs'
            warning: You appear to have cloned an empty repository.
            Checking connectivity done.
    注意:
        a) 需要用adduser命令,如果用useradd需要手動設(shè)置一些權(quán)限
        b) .ssh目錄owner為git


    landon總結(jié)


    • 實際項目過程中用TortoiseGit和Git的IDE插件較多(和SVN一樣)
    • Git命令行多用于腳本工具等
    • 雖然開發(fā)過程用GUI的GIT工具較多,但是Git的底層實現(xiàn)、原理以及鼓勵的開發(fā)模式還是非常值得學(xué)習(xí)的
    References

    posted on 2016-06-20 18:10 landon 閱讀(1660) 評論(0)  編輯  收藏 所屬分類: Utils

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 一级做a爱过程免费视频高清| 亚洲国产精品综合久久20| 国产男女爽爽爽爽爽免费视频| 亚洲特级aaaaaa毛片| a级毛片免费完整视频| 亚洲精品456在线播放| 国产成人免费a在线视频色戒| 国产精品亚洲综合久久| 全黄a免费一级毛片人人爱| 综合一区自拍亚洲综合图区 | 免费欧洲美女牲交视频| 亚洲日韩av无码中文| 亚洲熟伦熟女新五十路熟妇| 久久精品无码专区免费东京热| 爱情岛论坛亚洲品质自拍视频网站| 久久国产精品亚洲综合| 不卡视频免费在线观看| 亚洲熟妇丰满xxxxx| 永久中文字幕免费视频网站| 在线观看特色大片免费网站| 亚洲AV一区二区三区四区| 亚洲va在线va天堂va不卡下载| 国产又长又粗又爽免费视频| 99视频精品全部免费观看| 无套内射无矿码免费看黄| 亚洲av无码一区二区三区天堂古代 | jzzjzz免费观看大片免费| 国产亚洲精品bv在线观看| 亚洲AV日韩AV永久无码绿巨人| 免费一区二区视频| 久久不见久久见免费影院| 精品国产日韩亚洲一区在线| 亚洲精品中文字幕乱码| 亚洲国产另类久久久精品| 免费又黄又爽的视频| 最近高清国语中文在线观看免费| 特色特黄a毛片高清免费观看| 亚洲国产视频久久| 亚洲婷婷综合色高清在线| 男女交性永久免费视频播放| **一级一级毛片免费观看|