Git 分支簡介、Git 和 GitHub 日常操作

本文將介紹 Git 的三種狀態和三個工作區,然後介紹 Git 的核心功能:Git 分支,最後介紹 Git 的一些日常操作,例如如何進行一次完整的代碼提交以及其它常用操作 log、status 等。

Git 的三種狀態和三個工作區域

一個文件在 Git 中被管理時有三種狀態以及對應所處的三種工作區域,理解這一特性將有助於我們更好的理解 Git 的常用命令的原理。在隨後的 Git 操作介紹中,也會經常提到文件的各種狀態變化和所處的工作區域。

三種狀態

  • 已修改(Modified):表示代碼被修改了,但還沒有被保存到代碼庫中被管理起來。
  • 已暫存(Staged):表示將修改保存到暫存區(Staging Area)。對應於 add/rm/mv 命令(添加/刪除/移動)。git add/rm/mv 可將對應的修改保存到暫存區。
  • 已提交(Committed):表示已經將修改提交至代碼庫中被管理起來。對應於 commit 命令。git commit 命令可將已暫存的修改提交到代碼庫中。

三個工作區域

Git 中有三個工作區域與上述三種狀態相對應,如下圖 1 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 1. 三個工作區域和三種狀態

  • 工作目錄(Working Directory):工作目錄是我們常用的使用或修改代碼的目錄,它可以從 Git 倉庫目錄中 checkout 出特定的分支或者版本來使用。在工作目錄的修改如果未添加到暫存區,那麼該修改仍處在已修改狀態。
  • 暫存區域(Staging Area):當我們在工作目錄中修改了文件,我們需要先將修改添加到暫存區。暫存區的修改就是已暫存狀態。
  • Git 倉庫目錄(.git directory):Git 倉庫目錄就是真正存儲和管理代碼庫的目錄。提交修改到代碼庫本質上就是將暫存區的修改提交(commit)到代碼庫中。處在 Git 倉庫目錄中的修改就是已提交狀態。

總結下來,一次完整的提交包含以下操作:

  1. 修改文件。
  2. 將修改的文件保存到暫存區(git add/rm/mv)。
  3. 將暫存區的文件提交(git commit)到代碼庫中。

當然如果需要將本地代碼庫的修改同步到遠程代碼庫中(例如 GitHub),還需要將本地修改 push 到遠程。

為什麼要有暫存區?

暫存區是 Git 另一個區別於傳統版本控制系統的概念之一。傳統的版本控制系統例如 SVN、Perforce,提交代碼時直接將修改提交到了代碼庫中。暫存區相當於在工作目錄和代碼倉庫之間建立了一個緩衝區,在真正 commit 之前,我們可以做任意的修改,先將修改保存到暫存區,待所有修改完成之後就可以將其完整的 commit 進代碼庫,這樣可以保證提交的歷史是乾淨清晰的;保存到暫存區的修改也可以被撤銷,而不會影響到現有的版本庫和提交歷史。暫存區另一個作用是在進行多分支工作時,我們常常在某一分支上進行了修改,但又不想提交到代碼庫中,這時候我們可以使用 git stash 命令將暫存的和未暫存的修改保存到一個緩衝棧裡,使得當前工作分支恢復到乾淨的狀態;待我們想再次恢復工作時,只需要將緩衝棧的修改恢復到暫存區即可。

Git 分支

理解了 Git 的工作區和幾個狀態之後,我們來看一下 Git 另一重要概念:分支。Git 的分支技術是 Git 的核武器,理解併合理的使用 Git 分支,將大大的提升我們的工作效率。本章將會通過一系列實驗來講解 Git 的分支技術。

理解 Git 分支

在 Git 中,分支本質上是指向提交對象的可變指針。首先我們可以使用 git branch 或者 git branch -a命令列出本地所有的分支。如圖 2 所示,git branch 列出了本地已經被 check out 分支,其中帶星號的綠色標註的分支是當前的 check out 出來的工作分支。而 git branch -a 除了列出本地已經被 check out 分支,還列出了所有本地倉庫中與遠端相對應的分支,即圖中的紅色標註的分支。

Git 分支簡介、Git 和 GitHub 日常操作

圖 2.查看分支

注意:

  • 不像其它的 SCM 創建的分支是物理複製出額外的文件夾來創建分支,Git 的所有分支都在同一個目錄之下,我們一般只需要將正在進行開發的分支 check out 出來並切換成當前工作分支即可,如上圖中的 dev 分支。
  • 雖然上圖顯示出來紅色的分支是 remote 分支,但它們本質上還是存儲於本地的分支,只是這些分支是指向對應的遠端分支。後面會再詳細說明該類分支。

接下來使用 git log 命令可以查看每個分支所指向的提交。如圖 3 所示,可以看到綠色標註的兩個本地分支 dev 和 master 分別指向的 commit。

Git 分支簡介、Git 和 GitHub 日常操作

圖 3. 查看分支對應的 commit

理解 origin

從上圖 3 可以看到,有些紅色標註的分支名稱前帶有 origin 的前綴。origin 實際上是 git 默認生成的一個倉庫名稱,在每次 clone 的時候 git 會生成一個 origin 倉庫,該倉庫是一個本地倉庫,它指向其對應的遠程倉庫。前面提到的 remote 分支 remotes/origin/*,實際上就是儲存於 origin 倉庫的本地分支,它只是與對應的遠端分支具有映射關係。通過 git remote -v 命令可以查看本地所有的倉庫所指向的遠程倉庫。如圖 4 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 4. 查看本地倉庫指向的遠端倉庫

基於此機制,我們也可以 clone 其它的倉庫到同一個本地目錄。如圖 5 所示,執行 git remote add remote-sample [email protected]:caozhi/sample-project.git 命令添加一個本地倉庫 remote-sample 向我的另一個遠端倉庫 [email protected]:caozhi/sample-project.git,再通過git remote -v 命令我們可以看到新建的本地倉庫 remote-sample 向以及指向的遠端倉庫。

Git 分支簡介、Git 和 GitHub 日常操作

圖 5. 添加本地倉庫

注意,在本地代碼庫中建立多個 remote 倉庫的映射對於大多數開發者來說,不是一個最佳實踐,因為這樣會使得本地開發環境比較混亂。一般只有在做持續集成時,為了方便在同一個代碼目錄下編譯打包項目,才推薦在本地建立多個遠端倉庫的映射。

理解 HEAD 指針

HEAD 針是指向當前工作分支中的最新的分支或者 commit。Git 通過 HEAD 知道當前工作分支指向的哪條 commit 上。HEAD 針存在的意義在於我們可以通過設定 HEAD 針指向的 commit 來靈活地設定我們當前的工作分支,由於 HEAD 針並不僅僅指向實際存在的分支,也可以指向任意一條 commit,因此我們可以任意地設定當前工作分支指向任一歷史 commit。

首先我們通過 checkout 操作切換當前工作分支來查看 HEAD 針的變化,如圖 6 所示,我們當前的分支是 dev 分支,HEAD 針就指向了 dev 分支,我們再 checkout master 分支,當前工作分支變為了 master 分支,而 HEAD 針就指向了 master 分支對應的 commit。

Git 分支簡介、Git 和 GitHub 日常操作

圖6. 切換HEAD指針指向的分支

我們再執行 git checkout 075c130 嘗試 checkout 一個歷史 commit,如圖 7 所示,此時可以看到 Git 會為我們創建一個 detached 的分支,該分支並不指向一個實際存在的分支。執行 git log 命令也能看到,HEAD 針指向了 075c130 這個 commit,而非一個分支。

Git 分支簡介、Git 和 GitHub 日常操作

圖7. 切換HEAD指針指向任意 commit

理解 push

當我們完成了本地的代碼提交,需要將本地的 commit 提交到遠端,我們會使用 git push 命令。Push 操作實際上是先提交代碼到本地的 remote/** 分支中,再將 remote/** 分支中的代碼上傳至對應的遠端倉庫。

當遠端倉庫的提交歷史要超前於本地的 remote/** 提交歷史,說明本地的 remote 分支並不是遠端最新的分支,因此這種情況下 push 代碼,Git 會提交失敗並提示 fetch first 要求我們先進行同步,下圖 8 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 8. push 失敗

理解 fetch, pull

fetch 和 pull 操作都可以用來同步遠端代碼到本地。在多數開發者的實踐中,可能更習慣使用git pull去同步遠端代碼到本地, 但是 git fetch 也可以用於同步遠端代碼到本地,那二者的區別是什麼呢?

  • fetch 操作是將遠端代碼同步到本地倉庫中的對應的 remote 分支,即我們執行 git fetch操作時,它只會將遠端代碼同步到本地的 remote/**分支中,而本地已經 checkout 的分支是不會被同步的。
  • pull 操作本質上是一條命令執行了兩步操作,git fetch 和 git merge。執行一條 git pull 命令,首先它會先執行 fetch 操作將遠端代碼同步到本地的 remote 分支,然後它會執行 git merge 操作將本地的 remote 分支的 commits 合併到本地對應的已經 check out 的分支。這也是為什麼在 pull 時常常會出現 merge 的衝突,這是在執行 merge 操作時,git 無法自動的完成 merge 操作而提示衝突。另一種經常出現的情況是,pull 會自動產生一條 merge 的 commit,這是因為本地工作分支出現了未提交的 commit,而在 pull 時 Git 能夠自動完成合並,因此合併之後會生成一條 merge 的 commit。

讓 Git 自動為我們去生成這樣的 merge commit 可能會打亂我們的提交歷史,因此比較好的實踐方式是先 git fetch 同步代碼到本地 remote 分支再自己執行 git merge 來合併代碼到本地工作分支,通過這種方式來代替 git pull 命令去同步代碼。

Git 的日常操作

通過前文介紹,相信您對 Git 工作區和 Git 分支技術已經有了更深入的瞭解,下面我再介紹一些日常使用的 Git 和 GitHub 操作。

Git 分支操作

  • 查看本地分支:git branch [-av]


git branch 可以用於查看本地分支。-a 選項會列出包括本地未 checkout 的遠端分支。-v選項會額外列出各分支所對應的 commit,如下圖 9 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 9. 查看分支

  • 創建本地分支:git branch branchname,如圖 10 所示。創建本地分支時時會基於當前的分支去創建,因此需要注意當前工作分支是什麼分支。
Git 分支簡介、Git 和 GitHub 日常操作

圖 10. 創建本地分支

  • 推送本地分支到遠端:git push origin branchname:remote_branchname,如圖 11 和 圖 12 所示。技術上本地分支 branchname 和遠端分支 remote_branchname 必是相同的名字,但實踐中為了方便記憶,最好使用相同的名字。
Git 分支簡介、Git 和 GitHub 日常操作

圖 11. 推送本地分支到遠端

Git 分支簡介、Git 和 GitHub 日常操作

圖 12. 在 GitHub 上查看推送的分支

  • 切換工作分支:git checkout branchname,如圖 13 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 13. 切換工作分支

  • 刪除本地分支:git branch -d branchname,如圖 14 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 14. 刪除本地分支

  • 刪除遠端分支:git push :remote_branchname,如圖 15 和圖 16 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 15. 刪除遠端分支

Git 分支簡介、Git 和 GitHub 日常操作

圖 16. 在 GitHub 上查看被刪除的分支

GitHub 分支操作

除了本地創建,然後推送到遠端的方式之外,我們也可以直接在 GitHub 上創建遠程分支,本地只需要 fetch 下來即可。如圖 17 和圖 18 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 17. GitHub 中創建分支

Git 分支簡介、Git 和 GitHub 日常操作

圖 18. 查看創建的分支

在 GitHub 上我們也可以直接刪除分支。首先我們進入代碼庫的 branches 頁面,該頁面列出了我們所有的分支, 如圖 19 和圖 20 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 19. 進入 branches 頁面

branches 頁面,我們找到想要刪除的分支,點擊分支條目後方的垃圾箱按鈕,即可刪除該分支,如圖 20、圖 21 和 圖 22 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 20. 在 GitHub 上刪除分支

Git 分支簡介、Git 和 GitHub 日常操作

圖 21. 刪除分支後

Git 分支簡介、Git 和 GitHub 日常操作

圖 22. 代碼庫主界面再次查看該分支

分支的其它進階操作,如合併分支、比較分支差異等我們將在下一篇進行介紹。

從遠端同步代碼

在前面章節 Git 分支的介紹時已經講解了 pull 和 fetch 區別。二者都可以用來從遠端同步代碼到本地。本處不再贅述。

一次完整的提交

下面列出了一次完成的提交流程:

  1. 總是先同步遠端代碼到本地:一個 Git 的最佳實踐是,在每次正式提交代碼前都先將遠端最新代碼同步到本地。同步代碼使用 git pull 或者 git fetch & git merge。
  2. 將本地修改提交到暫存區:使用 git add/rm/mv 命令將本地修改提交到暫存區中。此處需要注意,為了使 Git 能夠完整的跟蹤文件的歷史,使用對應的 git rm/mv 命令去操作文件的刪除、移動和複製,而不要使用操作系統本身的刪除、移動和複製操作之後再進行 git add。
  3. 將暫存區的修改提交到本地倉庫:使用 git commit 命令將暫存區中的修改提交到本地代碼庫中。
  4. 使用 git push 命令提交本地 commit 到遠端。

Git 其它常用操作

Log 操作

Log 命令用於查看代碼庫的提交歷史。結合 log 命令提供的各種選項,可以幫助我們查看提交歷史中有用的提交信息。

  • --oneline 選項:不顯示詳細信息,只列出 commit 的 id 和標題, 如圖 23 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 23. log 的 --oneline 選項

  • -p 選項:列出 commit 裡的文件差異,如圖 24 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 24. log 的 -p 選項

  • -number 選項:只列出 number 數的 commit 歷史,如圖 25 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 25. log 的-number 選項

  • --name-only 選項:列出每條 commit 所修改的文件名。此選項只列出修改的文件名,不列出修改類型,如圖 26 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 26. log 的 --name-only 選項

  • --name-status 選項:列出每條 commit 所修改的文件名和對應的修改類型,如圖 27 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 27. log 的 --name-status 選項

  • --stat 選項:列出每條 commit 所修改的統計信息,如圖 28 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 28. log 的 --stat 選項

Blame 操作

Blame 命令是一個非常實用但是鮮為人知的命令,它可以用來查看單個文件中每行代碼所對應的最新的提交歷史。為了展現更多的提交歷史,本操作是在我的另一個代碼庫devops-all-in-one 中進行的實驗。如圖 29 所示,可以看到每行代碼都列出了對應的最新的 commit、文件名、提交者、時間等信息。

Git 分支簡介、Git 和 GitHub 日常操作

圖 29. git blame 操作

我們也可以添加 -L 選項控制只顯示我們所關心的行。如以下所示:

1`git blame -L 10,20 filename``git blame -L 10,+10 filename``git blame -L 20,-5 filename`

10,20 即顯示第 10 行到第 20 行代碼的信息;10,+10 即顯示第 10 行開始往後 10 行代碼的信息;10,-5即顯示第 10 行開始往前 5 行代碼的信息。如圖 30 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 30. 執行 git blame -L

Status 操作

git status 是另一個常用的命令,用於查看當前分支的修改狀態。當前分支沒有任何修改時,執行 git status 命令會顯示 working tree clean,如圖 31 所示:

Git 分支簡介、Git 和 GitHub 日常操作

圖 31. 無修改時執行 git status 操作

當我們對當前分支進行了更改時,git status 會根據被修改文件的狀態顯示不同的信息,如圖 32 所示:

  • 紅色框的修改表明這些修改已經提交到了暫存區。
  • 藍色框的修改表示它們還在工作區未被提交到暫存區。
  • 綠色框的修改表示是新文件,這些文件沒有被代碼庫所跟蹤。
Git 分支簡介、Git 和 GitHub 日常操作

圖 32. 有修改時執行 git status

Diff 操作

Diff 操作用於查看比較兩個 commit 或者兩個不同代碼區域的文件異同。

  • git diff:默認比較工作區和暫存區,如圖 33 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 33. 比較工作區和暫存區

  • --cached 選項:比較暫存區和代碼庫的差異,例如圖 34 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 34. 比較暫存區和本地代碼庫

  • 在命令後面指定特定的文件名,也可以比較特定文件的差異,如圖 35 所示:
Git 分支簡介、Git 和 GitHub 日常操作

圖 35. 比較工作區和暫存區

結束語

本文重點介紹了 Git 的分支,講解了一些不容易理解的概念如 HEAD 指針、origin 倉庫等,並通過實驗介紹了分支的常用操作:創建、刪除、切換等。同時,本文還介紹了 Git 的日常常用操作。相信您在閱讀完本文之後將有能力使用 Git 和 GitHub 進行日常開發。在下一篇文章中將會通過一系列實驗和實際應用場景講解一些我們在日常工作中經常遇到的 Git 進階操作,例如撤銷、回滾、分支比較等。

十五年編程經驗,今年1月整理了一批2019年最新WEB前端教學視頻,不論是零基礎想要學習前端還是學完在工作想要提升自己,這些資料都會給你帶來幫助,從HTML到各種框架,幫助所有想要學好前端的同學,學習規劃、學習路線、學習資料、問題解答。只要關注我的頭條號,後臺私信我【前端】兩個字,即可免費獲取,學習不怕從零開始,就怕從不開始,早日學習,早日達到月薪30k。


分享到:


相關文章: