標籤:

Git的基礎使用教程

Git作為一個工具,在現在的代碼託管領域毫無疑問是使用率最高的。之所以稱作其為一個工具而非一門技能,是因為不會有人刻意的關注你到底了不了解Git的底層原理,也不會讓你去拿Git實現某些需求,求職面試也完全不會將使用git的熟練度作為一個考核依據。工具要做的就是輔助你高效的完成其他的任務,僅此而已。

鑒於團隊內部有些成員對於Git的使用還處於剛剛上手的階段,也有些人雖然使用了有一段時間,但是其認知程度也僅僅只是停留在用圖形化工具sourceTree等進行簡單的add、commit和pull、push,對於分支管理、合併這些概念一知半解。所以抽了半天時間整理了一下git的基本概念和基本命令,做了一次小的分享。

下面將分享的內容貼出來,由於是作為分享稿的形式,所以文章更像是一個PPT的升級版,很多地方都是一些提示性的概念,但從整體上來看,已經囊括了git的基本使用的方方面面,可以作為一個手冊性質的綱要作為後續查閱使用。

============================分割線===================================

(知乎編輯器不支持markdown很蛋疼,更好的閱讀體驗請查看Git的基礎使用教程)

概念

1. 版本控制

版本控制是一種記錄一個或若干文件內容變化,以便將來查閱特定版本修訂情況的系統。

  • 本地版本控制(local)

複製整個項目目錄的方式來保存不同的版本,或許還會改名加上備份時間以示區別。

  • 集中化的版本控制(CVCS)

集中化版本控制系統解決了在不同系統上的開發者能協同工作的問題。這種形式都有一個單一的集中管理的伺服器,保存所有文件的修訂版本,而協同工作的人們都通過客戶端連到這台伺服器,取出最新的文件或者提交更新。

缺點是速度慢、必須聯網操作、分支概念模糊,不夠靈活。

常見的有CVS、SVN等。

以SVN為例,一般以目錄形式進行分支開發,如:trunk、branch、tag。所有的操作均可以看作是CS的文件上傳,除了寫代碼外其餘的所有的操作均需要與中央伺服器聯網。

  • 分散式的版本控制(DVCS)

客戶端並不只提取最新版本的文件快照,而是把代碼倉庫完整地鏡像下來,所以本地的倉庫是一份完整的數據,查看log等操作就完全不需要聯網,只是在多人協作,代碼完成後才推到伺服器作為共享。

這麼一來,任何一處協同工作用的伺服器發生故障,事後都可以用任何一個鏡像出來的本地倉庫恢復。

因為每一次的克隆操作,實際上都是一次對代碼倉庫的完整備份。

GIT、Mercurial、Bazaar等

集中式和分散式區別:是否聯網提交、非線性分支管理、本地倉庫是否存儲完整的倉庫信息等

2. GIT相關

2.1 倉庫

  • Git 倉庫

Git 倉庫目錄是Git用來保存項目的元數據和快照資料庫的地方。

這是 Git中最重要的部分,從其它計算機克隆倉庫時,拷貝的就是這裡的數據。相當於把.git克隆下來取得倉庫的相關信息,我們看到的文件是git根據.git里的信息檢出了你當前分支當前版本的所有文件而已。(查看.git目錄)

  • 工作目錄

工作目錄是對項目的某個版本獨立提取出來的內容。

這些從 Git倉庫的壓縮資料庫中提取出來的文件,放在磁碟上供你使用或修改。

  • 暫存區

快照存儲,生成blob對象,待提交到某分支。

2.2 文件狀態

  • 已修改(modified)

與當前分支的文件的最新狀態對比。

  • 已暫存(staged)

表示對一個已修改文件的當前版本做了標記,使之包含在下次提交的快照中

  • 已提交(committed)

表示數據已經安全的保存在本地資料庫中

1.工作區是當前工作、文件、目錄修改的副本,不同分支對應不同的副本,全部信息都在倉庫下的.git目錄中以壓縮數據的形式保存。

2.快照和分支

每次add命令都在倉庫中形成一個當前修改的快照,快照記錄當前修改內容。分支是快照hash的指針列表。快照負責記錄修改,分支hash負責記錄修改順序。

3.(Untracked)未跟蹤,嚴格意義上不在庫中。在切換分支的時候,未跟蹤的文件不作校驗,但是已跟蹤的文件,如果處於修改狀態,則必須先stage才能切換。(例子)

2.3. HEAD

HEAD是個指針,這個指針可以移動,這個指針移動到哪個快照,你就可以查看該快照也就是當時的狀態。因此HEAD的概念簡單理解為指向當前版本,即他指向當前分支所在的提交。

比如我在master分支上開發,HEAD就指向master,master指向最新的提交。然後切換到daily_01分支上繼續開發,那麼HEAD就跟隨者daily_01分支的提交繼續向前移動。

HEAD永遠指向倉庫的當前位置。

如果說分支是commit的一條時間線,那麼HEAD就是指向所有分支的所有的commit記錄的某一個節點。你的工作目錄副本的內容就是HEAD指向時的內容。

可以用來定位提交記錄,包括回退和前進等操作。

2.4. refs和commit

SHA1 安全哈希值,每次commit都會生成一個唯一的標識。用這個唯一的標識我們可以在任意時間找到全部的提交記錄。

Git 用以計算校驗和的機制叫做 SHA-1 散列(hash,哈希)。 這是一個由 40 個十六進位字元(0-9 和 a-f)組成字元串,基於 Git 中文件的內容或目錄結構計算出來。

Git 中使用這種哈希值的情況很多,你將經常看到這種哈希值。 實際上,Git 資料庫中保存的信息都是以文件內容的哈希值來索引,而不是文件名。

2.5. 分支

分支說白了其實就是commit_id串起來的一條條時間線,是指向提交對象的可變指針。(結合2.9)

2.6. 遠端

我們經常在操作中會見到origin或者remote,這是什麼意思?

remote是一條git命令,他可以設置關於本地倉庫和遠端伺服器之間如何關聯的先關信息。

origin和master一樣,是一個默認值,是遠端伺服器的默認命名。一個本地倉庫可以設置多個遠端伺服器關聯,分別取不同的名字,當你與服務端交互(pull或者push)時,指定哪個遠端名字,就會向哪個遠端的伺服器交互。

2.7 快照

Git 保存的不是文件的變化或者差異,而是一系列不同時刻的文件快照。

  • SVN等以文件變更列表的方式存儲信息。 這類系統(CVS、Subversion、Perforce、Bazaar 等等)將它們保存的信息看作是一組基本文件和每個文件隨時間逐步累積的差異。

  • Git 不按照以上方式對待或保存數據。 反之,Git 更像是把數據看作是對小型文件系統的一組快照。 每次你提交更新,或在 Git 中保存項目狀態時,它主要對當時的全部文件製作一個快照並保存這個快照的索引。 並非每個當前版本都需要做備份,如果沒有改變,那麼快照其實是鏈接上一個版本。git會在隱藏目錄.git里存在object里,定期會優化,保證快照空間,和讀取時間的平衡。所以一旦需要查看某版本直接load即可,空間換時間。

為什麼要先add才能commit?

暫存區域是一個文件,保存了下次將提交的文件列表信息,一般在Git倉庫目錄中。

暫存區記錄修改而非記錄文件(文件經過壓縮存儲在objects中),可以通過多次添加,將多次修改全部加到暫存區後一次性提交,最終我們後期找尋記錄的依據是commit而非add。但是add是必不可少。

2.8 一次操作內幕

暫存操作會為每一個文件計算校驗和(使用SHA-1 哈希演算法),然後會把當前版本的文件快照保存到 Git 倉庫中(Git 使用 blob 對象來保存它們),最終將校驗和加入到暫存區域等待提交:

當使用 git commit 進行提交操作時,Git 會先計算每一個子目錄(本例中只有項目根目錄)的校驗和,然後在 Git 倉庫中這些校驗和保存為樹對象。 隨後,Git 便會創建一個提交對象,它除了包含上面提到的那些信息外,還包含指向這個樹對象(項目根目錄)的指針。如此一來,Git 就可以在需要的時候重現此次保存的快照。

操作

1. 安裝

略.

2. 配置

Git 自帶一個 git config 的命令來幫助設置控制Git外觀和行為的配置變數。 這些變數存儲在三個不同的位置:

按照影響範圍可分為system、user(global)和project。

  • /etc/gitconfig 文件

    包含系統上每一個用戶及他們倉庫的通用配置。 如果使用帶有 --system 選項的 git config 時,它會從此文件讀寫配置變數。
  • ~/.gitconfig 或 ~/.config/git/config 文件:

只針對當前用戶。 可以傳遞 --global 選項讓 Git 讀寫此文件。

  • .git/config

當前使用倉庫的Git目錄中的 config 文件,只針對該倉庫。

每一個級別覆蓋上一級別的配置,所以 .git/config 的配置變數會覆蓋 /etc/gitconfig 中的配置變數。

必要設置:

user.name和user.email

每一個 Git 的提交都會使用這些信息,並且它會寫入到你的每一次提交中,不可更改

$ git config --global user.name 「jizhi.w056" $ git config --global user.email 「jizhi.w056@shulidata.com」

ssh-key

Git 支持多種數據傳輸協議。 http、 SSH 等。使用SSH協議時需要在本地生成公密鑰並將公鑰添加到伺服器中的配置項中。

3. 開發流程

基本的 Git 工作流程如下:

  1. 在工作目錄中修改文件。
  2. 暫存文件,將文件的快照放入暫存區域。
  3. 提交更新,找到暫存區域的文件,將快照永久性存儲到 Git 倉庫目錄。

    以及git中最為重要的分支操作。

4. 常用命令

4.1 help

命令:

git verb --help 或 git help verb

說明:

  • []可選項
  • <>必選項
  • …並列項
  • |或,一般為花括弧中出現,表示兩者為同一個意思,一般一個是另一個的簡寫形式;
  • [<>]嵌套形式,即假如你使用了一個可選的選項,那麼這個可選項還有一個選項是必須要設定的。

    栗子:git clone --help
  • NAME表示該git 命令名稱
  • SYNOPSIS表示該命令包含的全部選項
  • DESCRIPTION是該命令的簡要描述
  • OPTIONS配置項的簡要描述

4.2 init

初始化一個目錄為git倉庫,生成.git。

4.3 clone

接上例,同為創建一個倉庫目錄。

克隆遠端已有倉庫到本地,等同於SVN的checkout。本地生成git倉庫用init命令。

git@git.shuli.com:DevGroup/authcoll-frontend.git

這會在當前目錄下創建一個名為authcoll-frontend 的目錄,並在這個目錄下初始化一個 .git 文件夾,從遠程倉庫拉取下所有數據放入 .git 文件夾,然後從中讀取最新版本的文件的拷貝。

  • 主要選項:

-o <name>、-b<name>、<repository>、[<directory>

4.4 status

查看當前分支及工作區變動內容,輸錯命令會有智能提醒,針對你當前版本狀態給出下一步建議操作(很有用)。

4.5 diff

建議用diff工具。sourceTree、git gui、IDE,圖形化工具可查看已修改狀態和已提交狀態的diff。

4.6 add

  • 用途

添加修改到暫存區(索引區)

  • 說明

該命令以當前工作區副本中的最新修改內容來更新索引,即將修改添加到暫存區。可以添加全部內容,也可以只添加部分修改。

在commit之前可以執行一次或多次。

默認忽略.gitignore中所列文件和目錄。

add的是修改而非文件,即每次add操作記錄的都是本次的修改,而非直接記錄當前的文件。多次add後的快照通過一次commit合併,後期查看的時候可以通過包含這多次add快照的那次commit記錄檢查都有哪些修改。

add是生成此次修改的文件快照並存儲到temp中,但是並沒有實際的保存。

  • 主要選項

[…]:

直接執行git add默認不添加任何內容,可指定目錄、特定文件,可並列指定、可通配符指定;

[--dry-run | -n]

不真正的add,只是查看他們是否存在或者是否被忽略;

[--force | -f]

可強制添加已忽略的內容

[.|—all|-A]

添加全部

此時執行 git checkout -- file..會丟棄掉所有更改,保持版本庫之前未修改的狀態

4.7 commit

  • 用途

提交變更到當前分支

  • 說明

commit前必須add,add是針對工作副本文件的快照,commit是針對分支,保證能將每次的變更串成一條修改記錄線,後續查看記錄也是針對commit後的分支記錄。

commit會將當前暫存區中的快照進行保存。

  • 主要選項

直接git commit 則進入編輯message的狀態。

[---message= | -m ]

信息記錄。如果多個同時指定,將被合併。(沒有硬性要求,但是為了保持團隊協作,提交記錄格式以之前的簡單版本為準,後續可能會優化)

[—amend]

覆蓋(合併)最近一次提交。git log及GUI里是看不到被覆蓋的提交的,可以通過git reflog查看。

此功能可以完美的解決需要多次提交但是不想搞亂log的情景

[-a]

跳過暫存操作,相當於一次執行了add和commit兩個操作,但是只針對已跟蹤的文件,未跟蹤的文件還是需要執行add操作先進行跟蹤加入跟蹤列表。

[-a | -all]

快捷提交。相當於同時指定git add,但是這樣add的內容不包含未track的內容。

[-C |--reuse-message=]

重複使用某一次提交的所有信息,包括作者時間等。

[-F |--file=]

從指定文件讀取message內容。這裡有個強制性要求就是每人都要按照約定模板設置一份提交消息模板。

此時執行git reset HEAD file.. 會將文件回退到git add的狀態。

4.8 stash

當我們正在開發過程中,需要切換分支操作,但是又不想提交當前的修改,完全可以直接將當前的修改在不進行add和commit的情況下執行stash,這樣就會將本次的修改壓到一個存儲棧中,與commit記錄脫離但是又保存了我們的修改。

後續回到當前版本狀態時,再將這個存到棧中的內容取出便可。

git stash list 列出存儲棧(stash@{0}標識棧中的哪一次存儲,後續提取可以根據這個標識進行提取)

git stash apply 提取存儲棧,但是不刪除提取棧的內容

git stash pop 提取存儲棧並且刪除當前棧的內容

4.9 reset

重置HEAD(當前位置)到某個提交。

有三種reset形式和五種mode。如果想撤銷某個分支的最新的一次提交,可以用git revert(1)。

用reset會發現有些提交看不到了,可以利用git reflog查看。

盡量不要在不同分支的commit之間reset。

4.10 rm

刪除當前工作區某些內容。

-r、-f、—cached。

git rm 等同於手動刪除文件,會將文件從當前目錄刪除但不會踢出跟蹤列表;-f選項是強制刪除已修改但是還未add的文件;--cached是保留文件但清除他的已跟蹤狀態,結合.gitignore可以忽略跟蹤該文件。

4.11 branch

  • 用途

分支操作

  • 主要選項

git branch [<branchname>]:建立新分支。

-r、-a:列出本地、遠程和全部的分支,當前分支有星號標識。

—set-upstream-to=<upstream> | -u <upstream> [<branchname>]:與遠程倉庫對應分支建立跟蹤關係,如果遠程倉庫沒有同名分支則失敗,可以通過git push -u 生成遠端對應分支。

-m|-M、-d|-D:重命名或者刪除,不能在當前分支刪除當前分支。

切換分支使用git checkout <branch>

刪除遠程倉庫分支用git push origin :<branchname>,應保持分支不再繼續開發時刪除的習慣。git fetch -p 可以切斷遠程和本地分支之間斷掉的追蹤關係。

4.12 checkout

$ git checkout -b <branch> --track <remote>/<branch>:很好用的一句新建並切換而且跟蹤到遠程對應分支的語句。

和SVN的checkout做區分。

4.13 merge

合併兩個或者多個分支。merge會出現衝突,最大可能避免衝突可以約定每次拉取分支的上游分支。

要從策略上規避產生衝突,但是產生了衝突也不要害怕,因為衝突都是能解決的。不改出問題的解決衝突的前提是你知道兩個衝突文件的不同點在哪,而且依據HEAD、=====和other_branch的劃分線去對比信息,就能很輕鬆的解決衝突。

4.14 log

建議用圖形化工具。

顯示commit記錄。

  • 主要選項

—follow:單獨列出某個指定文件的記錄,通過GUI很方便。

reflog:全部的記錄,可以看到一些正常log看不到的提交。

4.15 fetch

只拉取不合併的拉取動作。合併需要將拉取到的commit主動合併到當前分支。

  • 主要選項

-p|—prune:遠程倉庫應該保持分支發布後刪除的習慣,遠程倉庫已刪除的分支本地還有對應跟蹤關係,為了精簡本地,可以利用這個選項。

4.16 pull

將遠程存儲庫中的更改合併到當前分支中。在其默認模式下,git pull是git fetch後面跟著的縮寫 git merge FETCH_HEAD。

遠程倉庫名和分支名可默認。

4.17 remote

管理遠程倉庫包括了解如何添加遠程倉庫、移除無效的遠程倉庫、管理不同的遠程分支並定義它們是否被跟蹤等等。

如果你使用 clone 命令克隆了一個倉庫,命令會自動將其添加為遠程倉庫並默認以 「origin」 為簡寫。

4.18 rebase

衍合(變基)。前期不建議用。

無論是通過變基,還是通過三方合併,整合的最終結果所指向的快照始終是一樣的,只不過提交歷史不同罷了。

變基和合併哪種方式好?

有一種觀點認為,倉庫的提交歷史即是 記錄實際發生過什麼。 它是針對歷史的文檔,本身就有價值,不能亂改。 從這個角度看來,改變提交歷史是一種褻瀆,你使用謊言掩蓋了實際發生過的事情。 如果由合併產生的提交歷史是一團糟怎麼辦? 既然事實就是如此,那麼這些痕迹就應該被保留下來,讓後人能夠查閱。

另一種觀點則正好相反,他們認為提交歷史是 項目過程中發生的事。 沒人會出版一本書的第一版草稿,軟體維護手冊也是需要反覆修訂才能方便使用。 持這一觀點的人會使用 rebase 及 filter-branch 等工具來編寫故事,怎麼方便後來的讀者就怎麼寫。

其他相關

1. .gitignore

未被跟蹤的文件直接加入.gitignore便會生效

已被跟蹤的文件要顯示的將其從庫中刪除,`git rm --cached file...

.gitignore

2. hook

簡單了解,分為本地和伺服器鉤子。

3. 規範

結合測試環境發布流程,參考後端分支管理約定,近期制定出前端的git分支管理規範,避免隨意拉取分支及殭屍分支的存在。

發布打tag。

總結

使用好git,首先要明確我們使用版本管理都有哪些訴求。

1. 跟蹤部分文件和排除某些文件

.gitignore

  • 初始化時就設定好哪些不跟蹤哪些跟蹤,可以查看模板。
  • 後期開發過程中更新.gitignore文件
  • 從已跟蹤的文件列表中如何去除某些文件

2. 修改了代碼但是不想add該怎麼辦

stash

3. add了怎麼回退?

checkout

3. commit了怎麼回退?

reset HEAD file

4. 如何修正一次錯誤的commit?

--amend

5. 提交了若干次後怎麼回退到前面的某次特定提交(回滾)?

reset HEAD|commit_id

盡量只在最近的幾次提交之間穿梭,避免大規模的回滾review及代碼丟失

6. 如何管理好分支?

  • 根據需求、上線版本、bug修復規範分支命名
  • 上下游關係,從upstream拉取盡量做到再合併到該upstream。
  • 盡量不在不同分支上大規模改動相同文件
  • 分支要靈活,解耦功能點,依據功能點拉取分支

7. 如何保持提交記錄清爽乾淨?

約定提交模板

8. 如何避免衝突及如何解決衝突?

  • 避免同時大規模修改同一個文件
  • 依據衝突標識查看不同點

GIT都有哪些需要理解的概念

  • .git
  • 工作副本
  • 暫存區
  • 分支
  • HEAD
  • 提交記錄

文檔

  • 命令索引
  • Pro Git book
  • git-cheasheat
  • git help

推薦閱讀:

Git提交歷史的修改刪除合併等實踐
ss和gitlab的配置
git中有的命令參數為什麼使用一個橫杠 - 有的命令卻使用兩個橫杠 -- ?
【Git操作系列】Git由淺入深之安裝與配置
如何高效地使用 Git

TAG:Git |