Git教程及常用命令
1. Git 簡介
Git 的誕生:
Linus(Linux之父)花了兩週時間自己用C寫了一個分散式版本控制系統,這就是Git!
一個月之內,Linux系統的原始碼已經由Git管理了!
幾個概念:
工作區
、版本庫
、暫存區
如下圖所示:
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種方式,如果想對本地現有的一個專案用 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~1
或 git 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
用於顯示本地所有分支以及當前所在哪個分支。
圖中顯示本地有 master
和 dev
兩個分支,並且正處在 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
- 非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 分支。
master
分支:是主分支,是最終上線程式碼的分支,該分支被設定被保護分支(鎖住),普通開發人員沒有許可權操作,只有團隊 leader 有合併的許可權;dev
分支:是用於開發的分支,該分支被設定為預設 clone 的分支,也用於合併到 master 之前進行測試的分支,普通開發人員從遠端 clone 到本地的預設分支,可以進行合併等操作;test
分支:是用於測試的分支,測試人員可以將自己開發分支中的修改合併到 test 分支在測試環境進行測試,一般該分支不合併到任何分支;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