通俗易懂的 Git入門

關二哥拉二胡發表於2021-08-26

本文已參與掘金創作者訓練營第三期「話題寫作」賽道,詳情檢視:掘力計劃|創作者訓練營第三期正在進行,「寫」出個人影響力

為什麼要使用 Git

對於每一個開發人員而言,程式碼管理都是迫切需要解決的問題。一般來說,我們編寫的程式碼都會經過反覆地調整,很難一蹴而就的將程式碼交付,並且隨著時間推移,程式碼量和程式碼變更次數都會越來越多,如果沒有一種有效方案保證它們能被記錄的,那會為之後的維護增加巨大的成本。

雖然可以使用一種低效的方案,在程式碼上編寫大量的註釋以註明業務、修改等資訊,以及當程式碼需要修改時則拷貝一份副本,並且標明 v1v2 等來記錄程式碼版本;或者當需要和團隊分享時,則直接打包整個專案發給他,待他改完後再發回來,依此原則直到整個專案最終完成。

Git 能夠高效完美的解決上面遇到的問題:

  • 當我需要修改一段程式碼,我能夠檢視程式碼修改日誌,甚至找出哪些人修改過這段程式碼,從而輕易的瞭解程式碼上下文

  • 當我現在的邏輯出現了一個致命的 BUG, 但我無法確定是何時因而產生的,那麼我可以根據以往的記錄一條一條的往前找,直到找出最近沒有出現 BUG 的程式碼,通過這次提交的修改,我可以很輕易地找出 BUG 地原因

  • 當我和團隊成員同時修改一段程式碼時,我能知道我們修改的內容、以及哪些是有衝突的。

如何使用 Git

使用 Git 前需要先安裝 Git 軟體,點選右邊的官網連結前往下載並安裝 Git 最新版: Git - Downloads (git-scm.com),Git 的官網有最棒的學習教程,裡面對每一個功能都有猶如字典般詳細的解釋,對今後想深入學習/查閱資料都有很大的幫助。

但是官網的內容猶如浩瀚大海,如果一頭扎入很容易陷入迷茫方向,本文將盡可能以最短的路徑完整的演示出 Git 的日常使用,希望能在入門路上帶來一點幫助。

萬事萬物都要有一個開始

在下載並安裝完 Git 後,開啟終端(window 系統對應 CMD.exe)或者你喜愛的命令列軟體如:item2、hyper 等,在命令列輸入如下命令git --version,如果能看到對應的 git 版本則證明已經安裝成功,如下:

~❯ git --version
git version 2.24.3 (Apple Git-128)
複製程式碼

接下來我將以一個最簡單的 Web 應用為例,在我的專案目錄下只有簡單的兩個檔案:index.htmlindex.css

image.png

然後,在命令列中將當前目錄切換到我的專案目錄下並且執行 git init 命令, 當你看到 Initialized empty Git repository in xxx 這提示(如下圖),證明你已經成功的將這個專案初始化為了一個 Git 倉庫,這就意味著 Git 會管理並且跟蹤這個專案目錄下的所有檔案的變化

image.png

做一個倉庫管理員

一般的物品存取邏輯都有以下幾個流程:當一個物品需要被儲存到倉庫前,通常都需要先對其進行檢查然後再存到倉庫中,存到倉庫後會給這個物品一個編碼以及除了編碼之外一般還有一些基礎資訊用來描述這個物品是什麼。

git 的操作也是如此,在將編寫完的程式碼提交到倉庫前,需要通過 git add 命令將需要提交到倉庫的程式碼暫存起來,當確保無誤之後再通過 git commit 命令將變更存到倉庫中,並且可以對存到倉庫中的變更進行描述, git 會對這次變更生成一段 commitId

image.png

以上面的例子為例,當我修改完 index.html 這個檔案後,我執行 git add index.html, 表示我已經將 index.html 檔案加入到暫存區,接著通過 git status 我能看到暫存區有哪些檔案,如:

❯ git add index.html
❯ git status                                              
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   index.html

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	style.css
複製程式碼

在上面的例子中的:Changes to be committed 下面的 new file: index.html 意思是有一個新的檔案準備提交到倉庫中,在 Git 中,在 Changes to be committed 下通常稱為暫存區,檢視暫存區的修改內容需要通過 git diff --staged 命令。

在確保所有程式碼都沒有問題之後,執行 git commit -m "修改描述" 將在暫存區的檔案都提交到倉庫, 接著可以通過 git log 就能檢視倉庫存儲存的每一次修改的記錄:

commit 8153f327ccb273a95401672af185634370bbcf69 (HEAD -> master)
Author: Giter <giter@giter.com>
Date:   Wed Aug 25 10:16:14 2099 +0800

    first commit
複製程式碼

上面是執行 git log 命令後顯示的 git 倉庫中的一條記錄。

  • 第一行 commit 後面跟的是 git 的 commitId 通過它可以準確的定位到這次修改
  • 第二行的 Author 記錄的是誰提交的
  • 第三行顯示的是提交到倉庫的時間
  • 第四行顯示對這次提交的描述

至此就完成了從編寫程式碼到提交 git 倉庫的基本步驟,之後如果再次編寫完程式碼,則再次執行 git addgit commit 命令將編寫完成程式碼提交到倉庫,通過 git log 就能夠看到在倉庫中儲存的這次變更的記錄。

團隊協作、共享程式碼

在以團隊的形式開發專案的時候,面臨的第一個問題是如何共享和編寫程式碼,一種方案是採用雲盤,所有人都在雲端編寫程式碼,但是這種情況缺乏管理和靈活性,當同寫同一行的時候,可能會覆蓋另一個人的,或者無法知道是誰刪了一些重要的程式碼

在 Git 中還可以設定遠端倉庫,上面所提交的倉庫我們稱為本地倉庫,因為它存在於本地硬碟中的,如果硬碟損壞,或者不小心刪掉了本地倉庫,那麼會很難再找回倉庫中儲存的提交記錄。一般你不用去買另外的裝置用來專門作為遠端倉庫,一般有現成的可以用,如:github、gitee、gitlab 等,這裡我以 gitee 為例,演示如何建立一個遠端倉庫,並且把本地倉庫同步到遠端倉庫中。

假設你已經有了 gitee 的賬號密碼,如果沒有可以前往 gitee.com 免費註冊,登入成功之後點選右上角的 + 號,並且點選 新建倉庫

image.png

接著輸入遠端倉庫的資訊,如倉庫名等然後確認,在這個表單中有很多快捷的選項,如果沒有必要則保持預設的就行。

image.png

建立完後你會進入如下介面

image.png

上面的三個黑色區域中的命令都是需要回到本地的命令列進行操作,你需要根據自己的需求執行對應的命令。

  • 第一個黑色區域的命令提示如果沒有設定 git 的一些基礎資訊,可以執行下面的命令設定,設定後有助於 git 記錄是誰提交的程式碼,如果你重來沒有執行過下面的命令,至少應該執行一次。
git config --global user.name "使用者名稱"  
git config --global user.email "郵箱地址"
複製程式碼
  • 第二個區域表示如果本地沒有建立倉庫,則可以根據以下命令進行建立, 比如你能看到 git initgit addgit commit 這三個命令分別是初始化將檔案加入到暫存區將暫存區的內容提交到本地倉庫
mkdir git-lean
cd git-lean
git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin git@gitee.com:xxx/git-lean.git
git push -u origin master
複製程式碼
  • 第三個區域則是本地已經有倉庫了,如果需要將本地倉庫和遠端倉庫關聯起來,則執行下列命令, 由於我本地的專案已經初始化了一個倉庫,並且本地倉庫中有一條記錄,所以我該選擇這種方案
cd 專案地址
git remote add origin git@gitee.com:xxx/git-lean.git
git push -u origin master
複製程式碼

接下來先解剖這兩條命令:

  • git remote add origin 倉庫地址: 可以理解為給本地的 git 倉庫新增一個遠端倉庫名叫 origin,並且遠端倉庫的地址是 xxx
  • git push -u origin master: 將本地倉庫同步到 origin 這個遠端倉庫

當我在我本地終端執行完這兩行命令後, 你能看到終端中有一個同步資訊:

Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 405 bytes | 405.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-6.0]
To gitee.com:xxx/git-lean.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
複製程式碼

接著在回到 gitee 中,重新整理剛剛的頁面,你能看到頁面中已經有了本地倉庫中的檔案,如我剛剛提交到本地倉庫的 index.html 檔案

image.png

Ok, 遠端倉庫已經建立好了,並且我把本地倉庫的內容同步到了遠端倉庫中。

假設團隊中有其他人也需要同時開發這個專案,那麼他可以把遠端倉庫中所有的內容都拉下來,對應的命令有兩個 git clone 以及 git pull

  • git clone 通常用於第一次將遠端倉庫的程式碼同步到本地
  • git pull 通常是在本地倉庫已經和遠端倉庫關聯之後,用於將遠端倉庫的記錄同步到本地倉庫中,所以它和 git push 是相反的,一個是將本地倉庫同步到遠端倉庫,一個是將遠端倉庫同步回本地倉庫

接下來假設另一個團隊成員需要開發這個專案,首先他需要執行 git clone 將遠端倉庫克隆一份到本地

❯ git clone 倉庫地址
Cloning into 'xxx'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
複製程式碼

接著假設他修改了 index.html 並且重新執行 git addgit commit 將修改提交到了遠端倉庫。

回到遠端倉庫的頁面中,點選它剛剛的提交記錄就能看到他這次修改的是什麼內容

image.png

image.png

可以看到上圖右側的 + 號表示:上一次提交到倉庫中的修改和這一次提交到倉庫中的修改新增了的內容,因為上一次是我提交的,而這一次是團隊成員提交的,所以我可以輕易知道其他人改了什麼?

衝突

如果團隊成員和我同時改了同一個位置難免就出現了衝突,這時 git 是無法分辨出該用誰的程式碼,需要使用者自己決定。例如其他成員修改了 index.html 的第十行,此時我也修改了 index.html 的第十行,那麼我們提交到本地倉庫的時候並不會出現問題,但當提交到遠端倉庫的時候,則會出現異常。

簡報1.gif

發生衝突後你在程式碼中通常會遇到這樣的成對出現的內容:<<<<<<< HEAD=======>>>>>>>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>學習 Git333</title>
</head>
<body>
<<<<<<< HEAD
我也修改了這行
=======
  修改了一行
>>>>>>> 8d024ad2aae1835d99620e4a75c2c63500eba928
</body>
</html>
複製程式碼

這時你需要決定應該使用 <<<<<<< HEAD======= 部分內容,還是使用 =======>>>>>>> 8d024ad2aae1835d99620e4a75c2c63500eba928 部分的內容,選擇好之後把另外的一部分刪除以及刪除 <<<<<<< HEAD=======>>>>>>> 這三個標識,然後再重新執行 git addgit commit 提交到本地倉庫,最後再同步到遠端倉庫。當然這樣可能會導致你刪掉其他人的程式碼,所以如有必要最好聯絡對應的人確認無誤之後再確定使用誰的,或者兩個人的程式碼都使用。

查詢記錄、回退、回退後再回退

最後還有一種比較棘手的場景,假設專案出現了一個未知的 BUG,但是在之前的某一刻它是正常的,並且無法立刻就排查出問題來,通常的方案是找出導致修改 bug 出現的那次提交,然後確定出什麼原因出現的 BUG 後再回到最新的記錄來修改並且提交。

  • 首先使用 git log 找出最近記錄的 commitId

  • 如果此時有一些修改,為了避免混淆,最好使用 git stash -u 將現在正在修改的內容儲存起來

  • 接著使用 git reset 切換到未出現 BUG 的提交,如: git reset 8153f327ccb273a95401672af185634370bbcf69

  • 當定位到未出現 BUG 的提交之後,還需要定位到最先出現 Bug 的那個提交,以便找出 BUG 是在哪個提交引起的,這需要反覆操作, 如:

    1. git log 檢視之前的提交是否有出現 BUG
    2. 使用 git reset 切換到這個提交,執行專案之後確定是否出現 BUG
    3. 如果還出現了 Bug 則使用 git reset 往前繼續找,直到找到沒有出現 BUG 的提交
    4. 如果沒有出現 Bug,並且還無法確定出現 BUG 的那次提交,則需要回到最新的提交
    5. 此時的 git log 已經無法看到最新的那次提交了,需要使用 git reflog 檢視最新的那次提交
    6. 使用 git reflog 找出最新的提交,然後重複第一條,並且縮小往前查詢的範圍,直到找出出現 BUG 的那次提交。

結語

當你熟悉 Git 的基礎流程之後,可能除了命令列工具,還會使用其他的軟體輔助操作,比如 sourceTree 或者其他的軟體,但 git 終歸只是一個工具,在更多時候程式碼的管理耗費的時間和思考會更多,最後希望本文能對正在苦於 git 入門的人有所幫助,對於文中有出錯的地方,希望大家能夠及時指出,避免我誤導他人。

相關文章