1. 程式人生 > >Git教程及常用命令

Git教程及常用命令

1. Git 簡介

Git 的誕生:

Linus(Linux之父)花了兩週時間自己用C寫了一個分散式版本控制系統,這就是Git!
一個月之內,Linux系統的原始碼已經由Git管理了!

幾個概念:

工作區版本庫暫存區
如下圖所示:

image

1. 圖中左側為工作區,右側為版本庫。在版本庫中標記為index的區域是暫存區(stage,index),標記為master的是master分支所代表的目錄樹。

2. 圖中我們可以看出此時HEAD指標實際是指向master分支的一個“遊標”。所以圖示的命令中出現 HEAD 的地方可以用 master 來替換。

3. 圖中的objects標識的區域為Git的物件庫,實際位於.git/objects目錄下。

4. 當對工作區修改(或新增)的檔案執行git add命令時,暫存區的目錄樹被更新,同時工作區修改(或新增)的檔案內容被寫入到物件庫中的一個新的物件中,而該物件的ID被記錄在暫存區的檔案索引中。

5. 當執行提交操作git commit時,暫存區的目錄樹寫到版本庫(物件庫)中,master分支會做相應的更新。即master指向的目錄樹就是提交時暫存區的目錄樹。

6. 當執行git reset HEAD命令時,暫存區的目錄樹會被重寫,被master分支指向的目錄樹所替換,但是工作區不受影響。

7. 當執行git rm --cached <file>命令時,會直接從暫存區刪除檔案,工作區則不做出改變。

8. 當執行git checkout .或者git checkout -- <file>命令時,會用暫存區全部或指定的檔案替換工作區的檔案。這個操作很危險,會清除工作區中未新增到暫存區的改動。

9. 當執行git checkout HEAD .或者git checkout HEAD <file>命令時,會用HEAD指向的master分支中的全部或者部分檔案替換暫存區和以及工作區中的檔案。這個命令也是極具危險性的,因為不但會清除工作區中未提交的改動,也會清除暫存區中未提交的改動。

2. 基本命令

2.1. git config: 環境設定命令

通常情況下,安裝完Git後的第一件事就是設定使用者名稱稱郵件地址。每一個Git的提交都會使用這些資訊,如果不設定則無法進行提交。

$ git config --global user.name "goto456" // 設定使用者名稱稱
$ git config --global user.email "[email protected]" // 設定郵件地址

使用--global引數表示設定了全域性的環境,如果想對與特定的專案使用不同的使用者名稱和郵件地址,則可已在該專案目錄下不使用--global引數設定不同的使用者名稱和郵件地址。

git config --list命令可以列出當前Git所有的配置資訊。

2.2. git init: 初始化本地倉庫

獲取一個 Git 倉庫有2中方法:

  1. 本地初始化一個倉庫
  2. 從遠端克隆一個倉庫到本地

對於第1種方式,如果想對本地現有的一個專案用 Git 來管理,可以直接進入該專案的目錄下執行如下命令,就可以將其初始化成一個 Git 倉庫了。
$ git init

2.3. git clone: 克隆遠端倉庫到本地

對於第二種方式,也是最常用的方式,比如你在 GitHub 上(或者其他程式碼託管網站)已經建立了一個專案,你就可以將該專案從遠端克隆到本地,這就有了該專案在本地的 Git 倉庫。

$ git clone [email protected]:goto456/leetcode.git // 通過ssh方式克隆
$ git clone https://github.com/goto456/leetcode.git // 通過https方式克隆

以上2種方式有如下區別:

1. https方式:不管是誰,只要拿到該專案的 url 可以隨便 clone,但是在 push 到遠端的時候需要驗證使用者名稱和密碼;
2. ssh方式:需要現將你電腦的SSH key(SSH公鑰)新增到GitHub(或者其他程式碼託管網站)上,這樣在 clone 專案和 push 專案到遠端時都不需要輸入使用者名稱和密碼。

如何生成SSH key,參見下一條命令:ssh-keygen

2.4. ssh-keygen: 生成SSH公鑰

生成公鑰之前先檢查系統中是否已經有了公鑰,公鑰一般在~/.ssh/目錄下。如果該目錄下存在id_rsa.pub檔案,這就是公鑰(id_rsa 檔案是私鑰);如果不存在此檔案,甚至連.ssh目錄都不存在,則需要用 ssh-keygen 命令來生成。如下所示:

$ ssh-keygen // 然後一直按回車鍵

這就可以生成 SSH key 了,公鑰id_rsa.pub檔案的內容大致如下所示:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/bdrJjo6HNCuzhpTZNVsTju5NnksTca2kRzfbZlFs+8S4bs8hTlo6aV/GJPkTT0zxJfuOgfd4B4O8Xh0NQg51Zl4yCsFdnIKFA0tgBOg/H5oi48Ypoo8h3LO+S8gd8HR5eqndjINx3kCrQx4gISCW25d7GKTm3c40YPoUCIL0Zg4+iuu2ioeVCpv+FSCKhlcrYew7c2aEe3p/oOmATT3UX9S4t94qzy8KkxRlnQ4a8zQ3OTRE23r7+PPHGN8oBAen//NQNn9COUhqU3pHfvtxqYrG1Blhf0t6HhkHz9Fd8StCUIDQv7yHHvuuPFumh3/8PNG8DDPQqWsqXmK2XozT [email protected]


2.5. git status 檢視當前狀態

可以在任何時候使用該命令檢視當前的狀態。一般會顯示當前所處的分支,以及當前工作區是否乾淨,是否有檔案要提交。
當你克隆遠端倉庫到本地後,通過該命令檢視當前狀態時,顯示資訊如下圖所示:

當你修改了一個檔案後,再用該命令檢視當前狀態,顯示資訊如下圖所示:

它會提示你把當前的變更新增到暫存區或者丟棄該變更。

當你新增一個檔案後,用該命令檢視當前狀態,顯示資訊如下圖所示:

它會顯示新增的檔案狀態是未跟蹤,並且提示用git add命令將其新增到暫存區。

2.6. git add: 新增到暫存區

無論你新增了一個檔案或者對已有的檔案進行了修改,都需要將其新增到暫存區,然後提交到版本庫。

$ git add hello.txt      //後面接檔名,表示將某個檔案新增到暫存區   
$ git add .                 //後面接一個點,表示將全部檔案新增到暫存區

分別執行上述2條命令後,如下圖所示:

將新增/修改的檔案新增到暫存區後,並未提交到版本庫,還需要通過git commit命令提交到版本庫。

2.7. git commit: 提交到版本庫

該命令是將新增到暫存去的變更提交到版本庫,主要有一下幾個常用的:

$ git commit -m "變更的說明資訊"       //標準用法,提交到版本庫並填寫相關說明資訊
$ git commit -am "變更的說明資訊"     //加上-a引數,則不需要上一步的git add過程,可以直接將修改過的變更直接提交到版本庫,但不會把新增的檔案提交

分別執行上述2條命令後,如下圖所示:

注:第2條命令一般在不想把新增的檔案提交到版本庫時用的比較多

執行完git commit命令提交到版本庫後,一次簡單的流程就算走完了。接下來可以通過git log命令來檢視所有的提交歷史。

2.8. git log: 檢視提交歷史

如果發現提交了一個錯誤版本,想回退到上個版本時,就可以通過該命令來檢視所有的歷史版本,以選擇回退到歷史的哪個版本。

$ git log                  //檢視歷史版本
$ git log --graph     //以圖形的方式檢視歷史(這個我比較常用,能很好的顯示各個分支之間的關係)

執行後,如下圖所示:

可以看出第2個圖的左邊顯示出了分支的圖形


2.9. git diff: 顯示變更內容

當你對檔案進行了修改,想檢視進行了哪些修改時,可以通過該命令檢視。
git diff命令會顯示修改的檔案中哪些內容進行了修改,包括新增了哪些內容,刪除了哪些內容。

$ git diff                            //後面不接引數,表示顯示所有修改檔案的變更
$ git diff README.md      //後面接檔名,表示只顯示該檔案的變更

例如:我對 README.md 檔案進行了修改,刪除了1行,新增了2行,然後用該命令檢視進行了哪些修改,如下圖所示: (“+” 表示新增的內容,“-” 表示刪除的內容)

2.10. git reset: 回退版本

常用於回退版本或者在各個版本間進行跳躍切換。
我們可以先用 git log 看一下當前歷史版本,如下圖所示:

如果要回退到前一個版本,則只需要輸入:git reset HEAD~1,執行完再看一下歷史版本:


我們已經回退到前一個版本了。

如果需要回退到前2個版本,命令是:git reset HEAD~2,回退到前n個版本就是:git reset HEAD~n
注意:在 Git 中,用 HEAD 表示指向當前版本的指標

如果需要回退到任何一個版本,則需要替換成該版本的 commit id 就可以了,例如:git reset a8336834b50daafa0793370,執行完再看一下歷史:


發現我們已經回退到該版本了。

一般情況下會加一個 --hard 引數:git reset --hard HEAD~1git reset --hard a8336834b50daafa0793370,表示回退到某個版本並且丟棄調工作區進行的修改,而不加該引數表示回退到某個版本但保留工作區的修改。

2.11. git push: 推送本地分支到遠端

當修改完成,本地的改動都已經提交到本地庫,則可以將本地分支推送到遠端程式碼庫了。

命令:git push origin master
origin 表示遠端程式碼庫的一個別名(也可以修改為其他名字,可通過 git remote 修改),master 表示需要推送的分支名稱。

如果,push 的過程中提示當前分支進度落後於遠端的分支,則需要通過 git pull 命令來拉取遠端最新狀態和本地分支進行合併,完成之後再 push 到遠端就可以了。

2.12. git pull: 拉取遠端分支到本地併合並

一般是本地分支的進度落後於遠端分支時,需要使用該命令。

命令:git pull origin master
origin 表示遠端程式碼庫的一個別名(也可以修改為其他名字,可通過 git remote 修改),master 表示需要拉取合併的分支名稱。

常用 git pull --rebase origin master 用 rebase 的方式進行,不會產生 merge 保持分支幹淨、整潔

3. 分支管理

3.1. git branch: 分支操作

命令:git branch 用於顯示本地所有分支以及當前所在哪個分支。


圖中顯示本地有 masterdev 兩個分支,並且正處在 master 分支上。

命令:git branch branchName 用於新建名為 branchName 的新分支。


圖中新建了一個名為 test 的新分支。

命令:git branch -d branchName 用於刪除名為 branchName 的分支。


圖中刪除了一個名為 test 的分支。

命令:git branch -D branchName 用於強制刪除分支。

3.2. git checkout: 分支間切換

該命令除了進行分支間切換功能外,還可以用來丟棄工作區中的修改內容,這裡就不作介紹了,僅介紹分之間的切換功能。

命令:git checkout branchName 用於從當前分支切換到名為 branchName 的分支上。


圖中先顯示在 master 分支上,後來切換到了 dev 分支上。

命令:git checkout -b branchName 用於新建名為 branchName 的分支並切換到該分支上。


圖中新建了 test 分支並切換到了該分支上。
注意與 git branch 新建分支的區別,此處除了新建分支外還進行了切換操作

3.3. git merge: 合併分支

該命令用於合併兩個分支。
命令:git merge branchName 用於將名為 branchName 的分支合併到當前分支。
有兩種合併方式:
1. fast-forward 方式合併:
命令:git merge dev

  1. 非fast-forward 方式合併:
    命令:git merge dev

注意兩種方式的區別:fast-forward 方式僅僅是移動了 HEAD 指標,而非 fast-forward 方式則是新建了一個節點

3.4. git rebase: 分支的變基

命令:git rebase master 將當前分支 rebase 到 master 分支
命令:git rebase master dev 將 dev 分支 rebase 到 master 分支

這種操作很難用語言解釋,我用一個圖來說明其與 merge 操作的區別:


由圖可知,rebase 操作相當於將 C3 節點拿下來換了一個位置重新放置。最後不會產生合併的痕跡,所有分支都是同一條直線。

我們來看一個詳細的例子:
你從 master 分支的 C2 上建立了一個新分支 server,為服務端添加了一些功能,提交了 C3 和 C4。 然後從 C3 上建立了特性分支 client,為客戶端添加了一些功能,提交了 C8 和 C9。 最後,你回到 server 分支,又提交了 C10。

假設你希望將 client 中的修改合併到主分支併發布,但暫時並不想合併 server 中的修改,因為它們還需要經過更全面的測試。這時,你就可以使用 git rebase 命令的 –onto 選項,選中在 client 分支裡但不在 server 分支裡的修改(即 C8 和 C9),將它們在 master 分支上重放:

命令:git rebase --onto master server client
以上命令的意思是:“取出 client 分支,找出處於 client 分支和 server 分支的共同祖先之後的修改,然後把它們在 master 分支上重放一遍”。 這理解起來有一點複雜,不過效果非常酷。

現在可以快進合併 master 分支了。(如下圖 快進合併 master 分支,使之包含來自 client 分支的修改):

$ git checkout master
$ git merge client

接下來你決定將 server 分支中的修改也整合進來。 使用 git rebase master server 命令可以直接完成,這樣做能省去你先切換到 server 分支,再對其執行變基命令的多個步驟。

$ git rebase master server
如下圖 將 server 中的修改變基到 master 上,server 中的程式碼被“續”到了 master 後面。

然後就可以快進合併主分支 master 了:

$ git checkout master
$ git merge server
至此,client 和 server 分支中的修改都已經整合到主分支裡了,你可以刪除這兩個分支,最終提交歷史會變成下圖的樣子,非常乾淨、整潔:

$ git branch -d client
$ git branch -d server

3.5. git cherry-pick: 挑揀節點合併到當前分支上

該命令一般用於從其他分支上挑揀某些節點到當前分支。
命令:git cherry-pick commit_id


上圖中我想把 ruby_client 分支上的 e43a6 這個節點合併到 master 分支上,但不需要 5ddae 這個節點,那麼我們就可以使用下面的命令:
$ git checkout master 先切換到 master 分支
$ git cherry-pick e43a6 將 e43a6 節點挑揀合併到當前分支
完成後如下圖所示:

注意:該節點被挑揀合併到 master 上後會產生一個新的節點

4. 其他命令

4.1. git revert: 撤銷某次提交

該命令用於撤銷歷史上的某次提交,注意該撤銷操會作為一個新節點存在於分支上:


上圖中顯示了當前的歷史提交,當我要撤銷 ab1591eb4e06c1e93fdd50126b9fab8a88d89155 這個節點時,執行如下命令:
命令:git revert ab1591eb4e06c1e93fdd50126b9fab8a88d89155


從圖中可以看出,完成操作後,會產生一個新的節點,該節點就是撤銷操作產生的。

注意與 git reset 的區別

4.2. git tag: 標籤的操作

用於給某次提交打個標籤,例如截止到某次提交後完成了某個重大版本的開發,則可以在該次提交打上一個版本的 tag 。


例如,截止到圖中的最近一次提交,我們完成了 1.0 版本的開發,則可以通過以下命令為其打上版本的標籤。
命令:git tag v1.0 為當前提交打上 v1.0 的標籤
命令:git tag v1.0 ab1591eb4e06c1e93fdd50126b9fab8a88d89155 為這個節點打上 v1.0 的標籤


圖中可以看出 v1.0 標籤已經打上了。
如果發現標籤打錯了,想刪除某個標籤,則可以通過如下命令來執行。
命令:git tag -d v1.0 刪除 v1.0 標籤

如果想將標題推送到遠端庫,則可以使用如下命令來完成。
命令:git push origin --tags 將打的 tag 都推送到遠端庫

4.3. git show: 顯示資訊

可用於顯示某次提交或者某個 tag 相關的資訊。
命令:git show commit_id 顯示某次提交的詳細資訊

命令:git show tag_name 顯示某個 tag 的詳細資訊

4.4. git blame: 檢視檔案每行的提交歷史(追責)

可用於檢視某個檔案中的每一行是那次提交產生的,是誰提交的,什麼時候提交的,提交的版本號是多少等等詳細資訊,在實際工作中方便對出問題的程式碼進行追責,找到產生 BUG 的責任人。

命令:git blame file_name


上圖中可以看到 README.md 這個檔案有 5 行,其中後 4 行都是我在 2018 年提交的,第 1 行是另外一個人在 2017 年提交的。

5. 團隊協作應用

在團隊協作過程中一般會有多個分支,比如有預設的 master 分支,有用於開發的 dev 分支,還有用於測試的 test 分支,用於對外發布的 release 分支,以及每個開發人員開發不同功能時用到的 feature_xx 分支等等。

公司中一般是用 GitLab 搭建的程式碼託管服務,幾個人的小團隊也可以自己搭建。

每個團隊業務不一樣,分支數量的設定也會不一樣,下面我介紹一下我們團隊的分支設定,以及普通開發人員和專案 leader 對不同分支的不同許可權以及不同的操作。

5.1. 分支設定

我們常用的分支有3個(master 分支、dev 分支、test 分支)以及若干個 feature_xx 分支。

  1. master 分支:是主分支,是最終上線程式碼的分支,該分支被設定被保護分支(鎖住),普通開發人員沒有許可權操作,只有團隊 leader 有合併的許可權;
  2. dev 分支:是用於開發的分支,該分支被設定為預設 clone 的分支,也用於合併到 master 之前進行測試的分支,普通開發人員從遠端 clone 到本地的預設分支,可以進行合併等操作;
  3. test 分支:是用於測試的分支,測試人員可以將自己開發分支中的修改合併到 test 分支在測試環境進行測試,一般該分支不合併到任何分支;
  4. feature_xx 分支:是使用者開發自己模組功能的特徵分支,可以叫 feature_login, feature_ui, feature_payment 等與開發的功能相關的名稱,該分支上的功能開發完、測試無誤後可合併到 dev 分支上。

5.2. 普通開發人員的操作

普通開發人員,一般按照如下幾個步驟來進行開發、測試工作就可以了:
1. 將遠端 dev 分支 clone 到本地,例如:git clone [email protected]:goto456/test.git
2. 從 dev 分支拉出(新建)自己的 feature 分支用於開發,例如:git checkout -b feature_login
3. 在自己的 feature 分支上進行開發工作;
4. 開發完了用 add、commit 等操作提交到當前分支;
5. 如果需要在測試環境進行測試,則將遠端 test 分支拉到本地,例如:git branch test origin/test;
6. 將自己的 feature 分支合併到 test 分支,並將 test 分支 push 到遠端,例如:git rebase test, git checkout test, git merge feature_login, git push origin test(注意:我們推薦用 rebase 來合併,以保證分支的整潔、美觀)
7. 通過公司的釋出平臺將遠端 test 分支釋出到測試環境進行測試;
8. 如果測試沒問題或者開始就不需要測試,這可以直接將當前 feature 分支合併到 dev 分支,並 push 到遠端庫,例如:git rebase dev, git checkout dev, git merge feature_login, git push origin dev(注意:我們推薦用 rebase 來合併,以保證分支的整潔、美觀)
9. 這時表示該功能已經開發完成了,程式碼的 review 以及釋出,需要團隊 leader 在合併到 master 操作時進行;這時可以刪除了自己的 feature 分支,例如:git branch -d feature_login
10. 如果在 push 到遠端的時候提示需要先 pull 時,我們推薦使用 rebase 的方式:git pull --rebase 以保持分支的整潔、美觀。

5.3. 團隊 leader 的操作

因為只有 leader 有操作 master 分支的許可權,所以需要完成 dev 分支到 master 分支的合併,以及後續打 tag 和正式上線釋出的工作:
1. 先切換到 dev 分支,並拉取最新的狀態,例如:git checkout dev, git pull --rebase origin dev
2. 進行程式碼 review 等過程後,合併到 master 分支,例如:git rebase master, git checkout master, git merge dev;(注意:我們推薦用 rebase 來合併,以保證分支的整潔、美觀)
3. 為本次完成的版本打上標籤,例如:git tag v1.0 -m "release version 1.0"
4. 將本地合併後的 master 分支以及標籤 push 到遠端庫,例如:git push orgin master --tags

6. 結束語

以上就是我從自己平時的應用中整理出的一個比較簡潔的教程,以及我們團隊在實際工作中是如何使用的。希望對大家有所幫助!詳情參見我的GitHub上的相關專案:https://github.com/goto456/simple-git