【Git 系列】基礎知識全集

風塵部落格發表於2021-11-19

Git 是一種分散式版本控制系統,它可以不受網路連線的限制,加上其它眾多優點,目前已經成為程式開發人員做專案版本管理時的首選,非開發人員也可以用 Git 來做自己的文件版本管理工具。

一、Git 基礎

1.1 Git 與 SVN 區別

Git 不僅僅是個版本控制系統,它也是個內容管理系統(CMS),工作管理系統等。

如果你是一個具有使用 SVN 背景的人,你需要做一定的思想轉換,來適應 Git 提供的一些概念和特徵。

GitSVN 區別點:

  1. Git 是分散式的,SVN 不是:這是 Git 和其它非分散式的版本控制系統,例如 SVNCVS 等,最核心的區別。
  2. Git 把內容按後設資料方式儲存,而 SVN 是按檔案:所有的資源控制系統都是把檔案的元資訊隱藏在一個類似 .svn.cvs 等的資料夾裡。
  3. Git 分支和 SVN 的分支不同:分支在 SVN 中一點都不特別,其實它就是版本庫中的另外一個目錄。
  4. Git 沒有一個全域性的版本號,而 SVN 有:目前為止這是跟 SVN 相比 Git 缺少的最大的一個特徵。
  5. Git 的內容完整性要優於 SVNGit 的內容儲存使用的是 SHA-1 雜湊演算法。這能確保程式碼內容的完整性,確保在遇到磁碟故障和網路問題時降低對版本庫的破壞。

1.2 版本控制

版本控制(Revision control)是一種在開發的過程中用於管理我們對檔案、目錄或工程等內容的修改歷史,方便檢視更改歷史記錄,備份以便恢復以前的版本的軟體工程技術。一句話就是用於管理多人協同開發專案的技術。

  • 優點
  1. 實現跨區域多人協同開發;
  2. 追蹤和記載一個或者多個檔案的歷史記錄;
  3. 組織和保護你的原始碼和文件;
  4. 統計工作量;
  5. 並行開發、提高開發效率;
  6. 跟蹤記錄整個軟體的開發過程;
  7. 減輕開發人員的負擔,節省時間,同時降低人為錯誤。

1.3 Git 工作區、暫存區和版本庫

  1. 工作區:就是你在電腦裡能看到的目錄。

  2. 版本庫:工作區有一個隱藏目錄 .git,這個不算工作區,而是 Git 的版本庫。

  3. 暫存區:本地版本庫裡存了很多東西,其中最重要的就是稱為 stage(或者叫index)的暫存區。

下面這個圖展示了工作區、版本庫中的暫存區和版本庫之間的關係:
在這裡插入圖片描述

1.4 分支

分支是為了將修改記錄的整個流程分開儲存,讓分開的分支不受其它分支的影響,所以在同一個資料庫裡可以同時進行多個不同的修改。

  • 主分支(Master)

是 Git 為我們自動建立的第一個分支,也叫主分支,其它分支開發完成後都要合併到 master

  • 分支合併(Merge)

將某分支上的更改聯接到此主幹或同為主幹的另一個分支。

  • 合併衝突(Conflict)

多人對同一檔案的工作副本進行更改,並將這些更改提交到倉庫。

1.5 標籤

標籤是用於標記特定的點或提交的歷史,通常會用來標記釋出版本的名稱或版本號(如:publish/0.0.1),雖然標籤看起來有點像分支,但打上標籤的提交是固定的,不能隨意的改動。

1.6 頭(HEAD)

頭是一個象徵性的參考,最常用以指向當前選擇的分支。

二、四個區域

2.1 Workspace:工作區

  1. 程式設計師進行開發改動的地方,是你當前看到的,也是最新的;
  2. 平常我們開發就是拷貝遠端倉庫中的一個分支,基於該分支進行開發。在開發過程中就是對工作區的操作。

2.2 Index / Stage:暫存區

  1. .git 目錄下的 index 檔案, 暫存區會記錄 git add 新增檔案的相關資訊(檔名、大小、timestamp...),不儲存檔案實體, 通過 id 指向每個檔案實體。可以使用 git status 檢視暫存區的狀態。暫存區標記了你當前工作區中,哪些內容是被 Git 管理的;
  2. 當你完成某個需求或功能後需要提交到遠端倉庫,那麼第一步就是通過git add 先提交到暫存區,被 Git 管理。

2.3 Repository:本地倉庫

  1. 儲存了物件被提交 過的各個版本,比起工作區和暫存區的內容,它要更舊一些;
  2. git commit 後同步 index 的目錄樹到本地倉庫,方便從下一步通過 git push 同步本地倉庫與遠端倉庫的同步。

2.4 Remote:遠端倉庫

遠端倉庫的內容可能被分佈在多個地點的處於協作關係的本地倉庫修改,因此它可能與本地倉庫同步,也可能不同步,但是它的內容是最舊的。

2.5 四個區域的關係

  1. 任何物件都是在工作區中誕生和被修改;
  2. 任何修改都是從進入 index 區才開始被版本控制;
  3. 只有把修改提交到本地倉庫,該修改才能在倉庫中留下痕跡;
  4. 與協作者分享本地的修改,可以把它們 push 到遠端倉庫來共享。

三、Git 檔案操作

版本控制就是對檔案的版本控制:首先要知道檔案當前在什麼狀態,然後才能對檔案進行修改、提交等操作,不然可能會提交了現在還不想提交的檔案。

3.1 檔案的四種狀態

  1. Untracked: 未跟蹤, 此檔案在資料夾中, 但並沒有加入到 Git 庫, 不參與版本控制.通過 git add 狀態變為 Staged;
  2. Unmodify: 檔案已經入庫, 未修改, 即版本庫中的檔案快照內容與資料夾中完全一致. 這種型別的檔案有兩種去處, 如果它被修改, 而變為 Modified. 如果使用 git rm 移出版本庫, 則成為 Untracked 檔案;
  3. Modified: 檔案已修改, 僅僅是修改, 並沒有進行其他的操作. 這個檔案也有兩個去處,通過 git add 可進入暫存 staged 狀態, 使用 git checkout則丟棄修改過, 返回到 unmodify 狀態, 這個 git checkout 即從庫中取出檔案, 覆蓋當前修改;
  4. Staged: 暫存狀態.執行 git commit 則將修改同步到庫中, 這時庫中的檔案和本地檔案又變為一致, 檔案為 Unmodify 狀態. 執行 git reset HEAD filename 取消暫存, 檔案狀態為 Modified

3.2 檢視檔案狀態

git status # 新增指定檔案到暫存區;
git add (dir) # 新增當前目錄的所有檔案到暫存區。

3.3 移除檔案與目錄(撤銷 add)

git rm --cached # 直接從暫存區刪除檔案,工作區不做出改變
git checkout . # 用贊尋去全部或指定檔案替換工作區的檔案,這個操作很危險,會清楚工作區中未新增到暫存區的改動。
git clean # 一般會加上引數 -df,-d 表示包含目錄,-f 表示強制清除;

3.4 檢視檔案修改後的差異

git status # 只能檢視對哪些檔案做了改動,如果要看改動了什麼,可以用;
git diff (files) # 檢視檔案修改後的差異

此外,還有以下兩個常用命令:

git diff --cached # 比較暫存區的檔案與之前已經提交過的檔案
git diff HEAD~n # 比較 repo 與工作空間中的檔案差異

3.5 簽出

  • 使用情景

該檔案已經存在倉庫中,工作區已經對其進行修改過了,如果想撤銷修改,可以使用 checkout

檢出命令git checkout 是 git 最常用的命令之一,同時也是一個很危險的命令,因為折騰命令會重寫工作區。

3.6 提交

通過 add 只是將檔案或者目錄新增到 index 即暫存區,使用 commit 可以實現將暫存區的檔案提交到本地倉庫。

git commit -m [message] # 提交暫存區到倉庫區
git commit [file1] [file2] ... -m [message] # 提交暫存區的指定檔案到倉庫區
git commit -a # 提交工作區自上次 commit 之後的變化,直接到倉庫區,跳過了 add,對新檔案無效
git commit -v # 提交時顯示所有 diff 資訊

3.7 撤銷更新

  • 撤銷暫存區更新
git reset HEAD [filename] # 將暫存區指定檔案移出到工作區
  • 撤銷本地倉庫更新

放棄工作區和暫存區的改動,同時 HEAD 指標指向前一個 commit 物件:

git log  #檢視提交日誌,  
git reset --hard HELD~1  #撤銷了上一次的提交,這裡的上一次提交日誌就不存在了。
cat [filename]  #檢視檔案內內容
git revert #把指定的提交修改回滾,並同時生成一個新的提交

3.8 日誌與歷史

git log #檢視所有提交日誌;
git log [filename] #檢視某檔案提交日誌;
history #檢視在 bash 下輸入過的指令;
git reflog #檢視倉庫中所有分支的所有更新記錄,包括已經撤銷的更新。

四、Git 配置

4.1 分類介紹

Git 配置檔案有三個級別,分別是系統級別、全域性級別以及倉庫級別。下面的表格展示了各個級別的配置的具體資訊:

配置級別 檔案位置 配置命令 優先順序別
系統 Git 安裝目錄\etc\gitconfig git config --system
全域性 使用者資料夾.gitconfig git config --global
倉庫 倉庫資料夾.git\config git config --local

4.2 Git 常用配置命令

  • 對全域性的 Git 使用者名稱和郵箱進行配置
git config --global user.name "xxxx" :設定所有倉庫提交的使用者名稱
git config --global user.email "xxxx@example.com":設定所有倉庫提交的郵箱
  • 檢視所有配置或某一項配置:
git config --global  --list;
git config --global  --get user.name;
git config --global  --get user.email;

系統和當前專案的命令同上,僅需將global改為system/local即可。

五、本地倉庫

建立本地倉庫的方法有兩種:一種是建立全新的倉庫,另一種是克隆遠端倉庫。

5.1 建立全新倉庫

  • 使用當前目錄作為 Git 倉庫

只需使它初始化:git init ;執行後可以看到,僅僅在專案目錄多出了一個.git 目錄;

  • 使用指定目錄作為 Git 倉庫

使用如下命令,可以把建立目錄與倉庫一起完成:

git init [project-name] # 新建一個目錄,將其初始化為 Git 程式碼庫 

5.2 克隆遠端倉庫

  1. 將遠端伺服器上的倉庫完全映象一份至本地,而不是取某一個特定版本,所以用 clone 而不是 checkout,語法格式為:
git clone (url) 

例如:我們要從克隆的遠端倉庫託管在 Github 上,首先,我們先前往 Github 上拷貝地址如:
https://github.com/vanDusty/Java-Note,然後執行:

git clone https://github.com/vanDusty/Java-Note```

克隆遠端倉庫到當前目錄。

### 5.3 Git 倉庫目錄結構

- 初始化倉庫使用 `git init` 命令,初始目錄(`.git`)結構:

```xml
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags
 
9 directories, 13 files
  • 這個初始化後的倉庫,當建立完分支 dev-branch,並 push 後的目錄結構:
.git/
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       ├── heads
│       │   ├── dev-branch
│       │   └── master
│       └── remotes
│           └── origin
│               └── master
├── objects
│   ├── 2d
│   │   └── d2c71b69c837a7459f4bd9c9f7db6520731d06
│   ├── 5c
│   │   └── 7b8eda18a75e13d27c31e65a54b0abd7948510
│   ├── 77
│   │   └── cad3aecf7c2754231095598119979d62a1e1da
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   ├── dev-branch
    │   └── master
    ├── remotes
    │   └── origin
    │       └── master
    └── tags
 
19 directories, 25 files

5.4 目錄結構說明

  • branches 目錄

一種不常用的儲存速記方式,用於指定 git fetchgit pullgit pushURL ,目前已基本不用。

  • COMMIT_EDITMSG 檔案

commit 編輯資訊,僅記錄最近一次提交的 commit 編輯資訊。

  • config 檔案

儲存當前倉庫的配置資訊

  • description 檔案

用於在 GitWeb 中展示專案的描述資訊。

  • HEAD 檔案

儲存 HEAD 指標,指向當前分支,即:記錄當前活動分支。

  • hooks 目錄

目錄下儲存了許多鉤子檔案(一些指令碼),這些檔案是各種 Git 命令使用的自定義指令碼,可以在 Git 特定階段自動執行。

  • index 檔案

暫存區(stage),二進位制檔案。

  • info 目錄

儲存庫的其他資訊將記錄在此目錄中。

  • logs 目錄

儲存所有更新的引用記錄。

  • objects 目錄

簡單理解就是:objects 目錄是 Git 的資料庫,儲存所有資料內容。

  • refs 目錄

(1)heads 目錄

目錄有以各個本地分支名命名的檔案,儲存對應分支最新提交的 ID,是通過 SHA1 演算法計算的一個字串。

(2)remotes 目錄

目錄有以各個遠端分支名命名的檔案,儲存對應分支最新提交的 ID,和 heads 目錄一個原理。

(3)tags 目錄

儲存在開發過程中打的標籤,裡面的檔案以標籤名命令,檔案內容為對應的 ID

六、遠端倉庫

Git 是分散式版本控制系統,同一個 Git 倉庫,可以分佈到不同的機器上,即釋出到不同的遠端倉庫上。

6.1 常見的託管平臺

  • GitHub

對,就是那個“全世界最大同性交友網站”。https://github.com

絕大多數好的開源專案都來自 GitHub,但是 GitHub 只能新建公開的 Git 倉庫,私有倉庫要收費,有時候訪問比較卡,如果你做的是一個開源專案,可以首選 GitHub。

  • Gitlab

提到 GitHub 就會自然的想到 Gitlab,Gitlab 支援無限的公有專案和私有專案。https://about.gitlab.com

  • Bitbucket

Bitbucket免費支援 5 個開發成員的團隊建立無限私有程式碼託管庫。https://bitbucket.org

  • Gitee 碼雲

前面說的都是國外的,下面來說幾個國內的。碼雲,個人開發者可免費建立 1000 個專案(不限公有、私有),提供最多 5G 的免費程式碼儲存空間。http://git.oschina.net

  • Coding.net

談到 Coding.net,首先必須提的是速度快,同樣一個賬號最多可以建立 1000 個專案(5 個私有),也支援任務的建立等。https://coding.net

6.2 遠端倉庫操作

申請到了 Git 遠端倉庫的帳號並建立了一個空的遠端倉庫現在我們就可以結合本地的倉庫與遠端倉庫一起協同工作了,多人協同開發,這也是我們工作時的情形,這裡我們全部使用命令完成(實際開發中很多開發工具例如:IDEA 等,無需命令操作)。

  • 克隆專案

遠端操作的第一步,通常是從遠端主機克隆一個版本庫,這時就要用到 git clone 命令,例如:

git clone https://github.com/vanDusty/Java-Note

該命令會在本地主機生成一個目錄,與遠端主機的版本庫同名。如果要指定不同的目錄名,可以將目錄名作為 git clone 命令的第二個引數。

git clone <版本庫的網址> <本地目錄名>

git clone 支援多種協議
除了 HTTP(s)以外,還支援 SSHGit、本地檔案協議等。通常來說,Git 協議下載速度最快,SSH 協議用於需要使用者認證的場合。各種協議優劣的詳細討論請參考官方文件。

  • git remote

為了便於管理,Git 要求每個遠端主機都必須指定一個主機名。git remote 命令就用於管理主機名。
不帶選項的時候, git remote 命令列出所有遠端主機名。

git remote -v  #參看遠端主機的網址。

結果表示,當前只有一臺遠端主機,叫做 origin,以及它的網址。

origin  git@github.com:jquery/jquery.git (fetch)
origin  git@github.com:jquery/jquery.git (push)

克隆版本庫的時候,所使用的遠端主機自動被 Git 命名為 origin 。如果想用其他的主機名,需要用 git clone 命令的 -o 選項指定:

git clone -o dusty https://github.com/vanDusty/Java-Note

上面命令表示,克隆的時候,指定遠端主機叫做 dusty

更多命令如下:

$ git remote show <主機名>  #檢視該主機的詳細資訊
$ git remote add <主機名> <網址> #新增遠端主機
$ git remote rm <主機名> #刪除遠端主機
$ git remoterename<原主機名> <新主機名> #遠端主機的改名
  • git fetch

一旦遠端主機的版本庫有了更新,需要將這些更新取回本地,這時就要用到該命令。

git fetch  #將某個遠端主機的更新,全部取回本地。
git fetch <遠端主機名> <分支名> #取回指定主機名的指定分支

所取回的更新,在本地主機上要用"遠端主機名/分支名"的形式讀取。比如 origin 主機的 master,就要用 origin/master 讀取。

git checkout -b newBrach origin/master  #在 origin/master 的基礎上,建立一個新分支。

也可以使用 git merge 命令或者 git rebase 命令,在本地分支上合併遠端分支。

git merge origin/master #或者$ git rebase origin/master
  • git pull

git pull 命令的作用是,取回遠端主機某個分支的更新,再與本地的指定分支合併。它的完整格式稍稍有點複雜。

git pull <遠端主機名> <遠端分支名>:<本地分支名>

示例:取回 origin 主機的 master 分支,與本地的 develop 分支合併:

git pull origin master:develop

如果遠端分支( master)要與當前分支合併,則冒號後面的部分可以省略。可以簡寫為:

git pull origin master

上面命令表示,取回 origin/master 分支,再與當前分支合併。實質上,這等同於先做 git fetch ,再做 git merge

git fetch origin
git merge master/develop
  • git fetchgit pull 的區別
git fetch:相當於是從遠端獲取最新版本到本地,不會自動合併。
git pull:相當於是從遠端獲取最新版本並merge到本地

七、Git 命令

偷個懶,發現一個很全的【Git常用命令參考手冊