基本工具之Git

本篇介紹Git,內容皆總結摘抄自《Pro Git》,僅用作學習筆記。

版本控制

版本控制(Version Control Systems,簡稱VCS)是一種記錄一個或多個文件內容變化,以便來查閱特定版本修訂情況的系統。使用版本控制系統可以將某個文件回溯到之前的狀態,甚至將整個項目都回退到過去某個時間點的狀態。也可以比較文件的變化細節,查出最後是誰修改了哪個地方,從而找出問題出現的原因等等。

很久以前人們習慣用複製整個項目目錄的方式來保存不同的版本,但有時候會混淆所在的工作目錄,一旦弄錯了文件丟了數據就無法撤銷恢復。爲了解決這個問題,人們開發了許多種本地版本控制系統,大多都是採用簡單的數據庫來記錄文件的歷次更新差異。

接下來人們遇到了一個問題,怎麼讓不同系統上的開發者協同工作呢?於是,集中化的版本控制系統(Centralized Version Control Systems,簡稱CVCS)應運而生。我們常用的Subversion就屬於這種,特點是都有一個單一的集中管理的服務器用來保存所有文件的修訂版本,而協同工作的人們都通過客戶端連到這臺服務器,取出最新的文件或提交更新。

這樣做相對於老式的本地VCS來說,每個人都可以在一定程度上看到項目中的其他人正在做些什麼。而管理員也可以輕鬆掌控每個開發者的權限,並且管理一個CVCS遠比在各個客戶端上維護本地數據庫來的輕鬆容易。但顯而易見的缺點是中央服務器的單點故障。如果宕機一小時,在這一小時內誰都無法提交更新,也就無法協同工作。如果中央服務器的磁盤發生故障還有丟書數據的風險。

爲了解決以上問題,分佈式版本控制系統(Distributed Version Control System,簡稱DVCS)面世了。我們要介紹的Git就屬於這類系統,特點是客戶端並不只提取最新版本的文件快照,而是把代碼倉庫完整地鏡像下來。這麼一來,任何一處協同工作的服務器發生故障,事後都可以用任何一個鏡像出來的本地倉庫恢復。因爲每一次的提取操作,實際上都是一次對代碼倉庫的完整備份。

更進一步,許多這類系統都可以指定和若干不同的遠端代碼倉庫進行交互。藉此,就可以在同一個項目中,分別和不同工作小組的人相互協作。

Git簡介

直接記錄快照,而非差異比較

Git與其他版本控制系統的主要差別在於,Git只關心文件數據的整體是否發生變化,而大多數其他系統只關心文件內容的具體差異。例如CVS,Subversion等都是每次記錄有哪些文件作了更新,以及都更新了哪些行的什麼內容。Git並不保存這些前後變化的差異數據。實際上,Git更像是把變化的文件作快照後,記錄在一個微型的文件系統中。每次提交更新時,它會縱覽一遍所有文件的指紋信息並對文件作一快照,然後保存一個指向這次快照的索引。爲提高性能,若文件沒有變化,Git不會再次保存,而只對上次保存的快照作一個鏈接。

近乎所有操作都是本地執行

與CVCS幾乎所有操作都需要連接網絡不同,在Git中的絕大多數操作都只需要訪問本地文件和資源,無需聯網。因爲Git在本地磁盤上就保存着所有當前項目的歷史更新,所以處理起來速度飛快。

在沒有網絡時,也可以非常愉快的頻繁提交更新,只需要等到有網絡時再上傳到遠程倉庫即可。

時刻保持數據完整性

在保存到Git之前,所有數據都要進行內容的校驗和計算,並將此結果作爲數據的唯一標識和索引。Git使用SHA-1算法計算數據的校驗和,通過對文件的內容或目錄的結構計算出一個SHA-1哈希值,作爲指紋字符串。所以保存在Git數據庫中的東西都是用詞哈希值來作索引的,而不是文件名。

文件的三種狀態

對於任何一個文件,在Git內部都只有三種狀態,如下:

  1. 已提交(committed):表示該文件已被安全的保存在本地數據庫中了;
  2. 已修改(modified):表示修改了某個文件,但還沒有提交保存;
  3. 已暫存(staged):表示把已修改的文件放在下次提交時要保存的清單中。

使用Git管理項目時,文件流轉有三個工作區域:Git的工作目錄,暫存區域和本地倉庫。

每個項目都有一個Git目錄,它是Git用來保存元數據和對象數據庫的地方。該目錄非常重要,每次克隆鏡像倉庫的時候,實際拷貝的就是這個目錄裏面的數據。

從項目中取出某個版本的所有文件和目錄,用以開始後續工作的叫做工作目錄。這些文件實際上都是從Git目錄的壓縮對象數據庫中提取出來的,接下來就可以在工作目錄中對這些文件進行編輯。

所謂的暫存區域只不過是個簡單的文件,一般都放在Git目錄中。

基本的Git工作流程如下:

  1. 在工作目錄中修改某些文件。
  2. 對修改後的文件進行快照,然後保存到暫存區域。
  3. 提交更新,將保存在暫存區域的文件快照永久轉儲到Git目錄中。

下載、安裝和配置

到Git官網https://git-scm.com/下載,如下圖:

點擊Download之後你會發現下載得超級慢,這時候我們除了等之外還可以找一下其他資源。例如在https://npm.taobao.org/mirrors/git-for-windows/這裏也可以下載,當然,一般還是在官網下載更安全一些,除了這種下載很慢的情況。打開之後如下圖:

這裏我們選擇與官網相同的版本號,選擇之後如下圖:

根據電腦是32位還是64位下載對應的.exe文件。下載完成後打開如下圖:

由於筆者電腦上安裝了Git的其他版本,因此會出現圖中的Only show new options,勾選之後原本的版本設置應用到此次安裝中,後面只設置這個版本相比原本的版本沒有的。這裏不勾選,直接點擊Next。

根據自己的需求選擇勾選,然後點擊Next。下圖是選擇Git默認的編輯器,這裏默認Vim,點擊Next。後面的幾個都默認即可。

 

最後點擊install直到安裝成功,出現下圖:

在使用之前還要配置一下Git的工作環境。Git提供了一個叫做git config的工具,專門用來配置或讀取相應的工作環境變量。正是這些環境變量決定了Git在各個環節的具體工作方式和行爲。這些變量可以存放在一下三個位置:

  • /etc/gitconfig文件:系統中對所有用戶都普遍適用的配置。使用git config時用--system選項,讀寫的就是這個文件。
  • ~/.gitconfig文件:用戶目錄下的配置文件只適用於該用戶。使用git config時用--global選項,讀寫的就是這個文件。
  • 當前項目的git目錄中的配置文件(即工作目錄中的.git/config文件):僅對當前項目有效。

每一個級別的配置都會覆蓋上層的相同配置。

第一個要配置的是個人的用戶名和電子郵件地址,每次Git提交時都會引用這兩條信息,會隨更新內容一起被永久納入歷史記錄。打開Git Bash,在命令行中先後輸入以下命令:

git config --global user.name "用戶名"
git config --global user.email 郵箱

例如設置用戶名爲“老趙”,郵箱爲[email protected]

命令git config --global --list的作用是查看已有的配置信息。上面使用了--global選項,那麼更改的配置文件就是位於用戶主目錄的那個。如果要在某個特定的項目中使用其他名字或郵箱,只要去掉--global選項重新配置即可,新的設置保存在當前項目的.git/config文件裏。

Git基礎

取得項目的Git倉庫

有兩種取得Git項目倉庫的方法,第一種是在現存的目錄下,通過導入所有文件來創建新的Git倉庫;第二種是從已有的Git倉庫克隆出一個新的鏡像倉庫。

先介紹第一種,打開命令行進入創建倉庫的項目所在的目錄,執行以下命令:

git init

初始化後,在當前目錄下就會出現一個名爲.git的目錄,所有Git需要的數據和資源都存放在這個目錄中。但目前僅僅是按照基友的結構框架初始化好了裏面所有的文件和目錄,我們還沒有開始跟蹤管理項目中的任何一個文件。

如果要將文件納入版本控制,需要使用git add命令告訴Git開始對這些文件進行跟蹤,例如將pom.xml納入版本控制命令如下:

git add pom.xml

現在我們就得到了一個實際維護着若干文件的Git倉庫。

克隆倉庫的命令格式如下:

例如如果要克隆筆者在github上的一個倉庫,可以使用如下命令:

git clone https://github.com/ZhaoCharles/hello-world.git

這個命令將會在當前目錄下創建一個名爲grit的目錄,其中包含一個.git的目錄,用於保存下載下來的所有版本記錄,然後從中取出最新版本的文件拷貝。項目中的所有文件都包含在grit目錄中了。如果要自定義新建的項目目錄名稱,可以在上面的命令末尾指定新的名字。

Git支持許多數據傳輸協議,上面的例子使用的是https://協議,還可以使用git://協議或者user@server:/path.git協議,後面會詳細介紹。

記錄每次更新到倉庫

工作目錄下面的文件不外乎有兩種狀態:已跟蹤和未跟蹤。已跟蹤的文件是指本來就已經被納入版本控制管理的文件,在上次的快照中有它們的記錄,在工作了一段時間之後,它們的狀態可能是未更新,已修改或已放入暫存區。所以其他文件都屬於未跟蹤文件。它們既沒有上次更新時的快照,也不在當前的暫存區。

初次克隆某個倉庫時,工作目錄中的所有文件都屬於已跟蹤文件,且狀態爲未修改。在編輯過某些文件之後,Git將這些文件標爲已修改。逐步把這個修改過的文件放到暫存區,最後一次性提交所有這些暫存的文件。使用Git時的文件狀態變化週期如下圖:

檢查當前文件狀態

要確定哪些文件當前處於什麼狀態,可以用git status命令,例如在之前克隆的倉庫目錄下執行此命令,輸入如下:

這說明現在的工作目錄相當乾淨,即所有已跟蹤文件在上次提交後都未被更改過。此外,上面的信息還表明,當前目錄下沒有任何處於未跟蹤的新文件,否則Git會在這裏列出來。命令還顯示了當前所在的分支是master,這是默認的分支名稱。

在當前目錄下創建一個hello.txt文件,保存後退出再次運行git status,會看到如下輸出:

在狀態報告中可以看到新建的hello.txt出現在Untracked files下面。未跟蹤的文件意味着Git在之前的快照中沒有這些文件;Git不會自動將其納入跟蹤範圍,除非我們使用命令來把其納入進去。

跟蹤新文件

使用git add命令跟蹤一個新文件,例如跟蹤hello.txt的命令如下:

git add hello.txt

執行上述命令後,再次執行git status命令可以看到如下輸出:

可以看到hello.txt文件已被跟蹤,由於出現在Changes to the committed下面,表示現在是已暫存狀態。如果此時提交,該文件此時此刻的版本將被留存在歷史記錄中。

暫存已修改文件

現在修改一下hello.txt文件並再次運行git status命令,輸出如下:

hello.txt出現在Changes not staged for commit下面,表示已跟蹤文件的內容發生了變化,但還沒有放入暫存區。要暫存這次更新,需要運行git add命令(根據目標文件的狀態的不同,此命令的效果也不同:文件處於未跟蹤狀態時,它的作用是開始跟蹤文件;文件是已跟蹤狀態但已修改時,它的作用是將其放入暫存區;合併文件有衝突時,它的作用是把衝突文件標記爲已解決狀態等)。

忽略某些文件

有時候有些文件無需納入Git的管理,也不希望他們總出現在未跟蹤文件列表。通常是些自動生成的文件,例如在使用idea時自動生成的.idea文件等。我們可以創建一個名爲.gitignore的文件,列出要忽略的文件模式。文件.gitignore的格式規範如下:

  • 所有空行或者以註釋符號#開頭的行都會被Git忽略。
  • 可以使用標準的glob模式匹配。
  • 匹配模式最後跟反斜扛(/)說明要忽略的是目錄。
  • 要忽略指定模式以外的文件或目錄,可以在模式前加上!取反。

所謂的glob模式是指sheel所使用的簡化了的正則表達式,例如*匹配零個或多個任意字符;[abc]匹配任意一個列在方括號中的字符;?匹配任意一個字符等等。例如下面的.gitignore文件的例子:

# 忽略所有.idea結尾的文件
*.idea
# 忽略項目根目錄下的todo文件,不包括aaa/todo
/todo
# 忽略build/目錄下的所有文件
build/

查看已暫存和未暫存的更新

使用git diff命令可以看到當前目錄尚未暫存的文件更新了哪些,在修改hello.txt文件後使用git diff命令輸出如下:

此命令比較的是工作目錄中當前文件和暫存區快照之間的差異,也就是修改之後還沒有暫存起來的內容。如果要查看已經暫存起來的文件和上次提交時的快照之間的差異,可以用git diff --staged命令,輸出如下:

提交更新

在提交之前最好先使用git status查看是否將全部文件都暫存起來了,然後再使用git commit命令提交文件。可以看到如下輸出:

這時需要輸入一些提交說明,如果不輸入會中止提交,這裏我們隨便輸入一些。然後按esc,再輸入:wq保存退出,輸出如下:

提交成功的信息高速我們當前再master分支,本次提交的完整SHA-1校驗和是a8765f7。

提交時記錄的是放在暫存區域的快照,任何還未暫存的仍然保持已修改狀態。每一次運行提交操作,都是對項目作一次快照,以後可以回到這個狀態或者作比較。

跳過暫存區域

儘管使用暫存區域的方式可以精心準備要提交的細節,但有時這麼做略顯繁瑣。Git提供了一個跳過使用暫存區域的方式,在提交時給git commit加上-a選項,Git就會自動把所有已跟蹤過的文件暫存起來一併提交,從而跳過git add步驟。例如我們修改一下hello.txt文件,然後運行git status查看狀態,輸出如下:

如果我們此時使用git commit命令,會再次輸出以上內容。上圖輸出的最後一行也提示我們可以使用git add將文件放入暫存區或者使用git commit -a直接提交文件。使用git commit -a命令後就進去了與上面提交更新中一樣的頁面。

移除文件

要從Git中移除某個文件,就必須從已跟蹤文件清單中清除,然後提交。可以使用git rm命令完成並連帶從工作目錄中刪除指定的文件,這樣以後就不會出現在未跟蹤文件清單中。

如果只是手動從工作目錄中刪除文件,運行git status時可以看到。例如刪除hello.txt文件後運行git status命令,輸出如下:

此時可以再運行git rm記錄此次移除文件的操作,最後提交的時候,該文件就不再納入版本管理了。運行git rm命令後再運行git status命令輸出如下:

如果刪除之前修改過並且已經放到暫存區,則必須要用強制刪除現象-f,以防止誤刪文件後丟失修改的內容。

有時我們想把文件從Git倉庫中刪除,但在工作目錄中保留。例如我們不小心將.idea納入跟蹤清單,我們要移除跟蹤但保留該文件。此時可以使用--cached選項,例如移除跟蹤.idea文件但在工作目錄中保留的命令如下:

git rm --cached .idea

後面可以列出文件或目錄名稱,也可以使用glob模式。

移動文件

移動文件的命令可以使用git mv命令,此命令也可以給文件改名。例如將文件a移動到b目錄下並改名爲A的命令爲:

git mv a b/A

查看提交歷史

在項目中運行git log目錄,可以看到該項目的提交歷史。在上面的項目中運行git log命令輸出如下:

不用任何參數時,git log會按提交時間列出所有的更新,最近的更新在最上面。每次更新都有一個SHA-1校驗和、作者的名字和電子郵件地址、提交時間,最後縮進一個段落顯示提交說明。

git log有很多選項可以幫助我們搜索,最常用的幾個如下:

  • -p:展開顯示每次提交的內容差異,後面再加上-n則僅顯示最近的n次更新。
  • -stat:僅顯示簡要的增改行數統計。
  • -pretty:指定使用完全不同於默認格式的方式展示提交歷史。

修改最後一次提交

git checkout -- 文件名稱

使用--amend選項可以重新提交:

git commit --amend

此命令將使用當前的暫存區域快照提交。如果剛纔提交完沒有作任何改動直接運行此命令,可以重新編輯提交說明,但將要提交的文件快照與之前的一樣。如果剛剛提交時忘了暫存某些修改,可以先補上暫存操作,再運行--amend命令提交。

取消已經暫存的文件

有時我們修改過的文件用git add加到了暫存區域,但其中某個文件我們想稍後提交,我們可以使用以下命令取消暫存。

git reset HEAD 文件名

取消對文件的修改

使用以下命令可以拋棄文件的修改。這條命令有些危險,因爲對文件的修改都沒有了,已經恢復到了修改前的版本。

git checkout -- 文件名

查看當前的遠程倉庫

要查看當前配置有哪些遠程倉庫,可以使用git remote命令,它會列出每個遠程庫的簡短名字。在克隆完某個項目後,至少可以看到一個名爲origin的遠程庫,Git默認使用這個名字來標識克隆的原始倉庫。也可以加上-v選項,顯示對應的克隆地址。

添加遠程倉庫

要添加一個遠程倉庫,可以指定一個名字以便將來引用,使用git remote add 名字 url可以達到此目的。例如用hello來指代上面的遠程倉庫,命令如下:

git remote add hello https://github.com/ZhaoCharles/hello-world.git

從遠程倉庫抓取信息

可以使用git fetch remote-name命令從遠程倉庫抓取數據到本地。此命令到遠程倉庫中拉取所有你本地倉庫還沒有的數據。運行完成後,就可以在本地訪問該遠程倉庫的所有分支,將其中某個分支合併到本地,或者只是取出某個分支。

推送數據到遠程倉庫

使用git push remote-name branch-name可以將本地倉庫中的數據推送到遠程倉庫,如果要把本地的master分支推送到origin服務器上,可以運行下面的命令:

git push origin master

輸出如下:

只有在所克隆的服務器上有寫權限,且同一時刻沒有其他人在推送數據,這條命令才能成功。如果有其他人在你推送數據前推送了更新,那你的推送操作就會被駁回。必須將他們的更新抓取到本地,才能再次推送。

查看遠程倉庫信息

使用命令git remote show remote-name可以查看某個遠程倉庫的詳細信息,輸出如下:

遠程倉庫的刪除和重命名

可以使用git remote rename命令修改某個遠程倉庫在本地的簡稱,例如將hello修改爲hi,可以使用如下命令:

git remote rename hello hi

對遠程倉庫的重命名,也會使對應的分支名稱發生變化,原來的hello/master變成了hi/master。

移除遠程倉庫可以使用git remote rm命令,例如移除遠程倉庫hi命令如下:

git remote rm hi

列出已有的標籤

使用命令git tag可以列出現有的標籤,顯示的標籤按照字母順序排列。

新建標籤

Git使用的標籤有兩種類型:輕量級的和含附註的。輕量級標籤就是一個指向特定提交對象的引用。含附註標籤實際上是存儲在倉庫中的一個獨立對象,有自身的校驗和信息,包含標籤的名字、電子郵件、日期和標籤說明。

創建含附註標籤,使用-a選項指定標籤名字,-m選項指定對應的標籤說明,例如指定標籤名字爲v1.0,標籤說明爲“first version”,命令如下:

git tag -a v1.0 -m "first version"

使用git show命令查看相應標籤的版本信息,並連同打標籤時的提交對象。例如查看剛剛創建的v1.0輸出如下:

而創建輕量級的標籤只需要直接給出標籤名字即可,例如git tag v1.0。

Git分支

在Git中提交時,會保存一個提交對象,該對象包含一個指向暫存內容快照的指針,包含本次提交的作者等相關附屬信息,包含零個或多個指向該提交對象的父對象指針:首次提交是沒有直接祖先的,普通提交有一個祖先,由兩個或多個分支合併產生的提交則有多個祖先。

假設工作目錄中有三個文件,將它們暫存後提交。暫存操作會對每一個文件計算校驗和,然後把當前版本的文件快照保存到Git倉庫中,並將校驗和加入暫存區域。當使用git commit新建一個提交對象前,Git會先計算每一個子目錄的校驗和,然後再Git倉庫中將這些目錄保存爲樹對象。之後Git創建的提交對象,除了包含相關提交信息以外,還包含指向這個樹對象的指針,如此就可以在將來需要的時候重現此次快照的內容。

現在Git倉庫中有五個對象:三個表示文件快照內容的blob對象,一個記錄目錄樹內容以及每個文件對應blob對象索引的tree對象,以及一個包含指向tree對象的索引和其他提交信息元數據的comit對象。

當對文件做些修改後提交,這次的提交對象會包含一個指向上次提交對象的指針。

Git中的分支,本質上僅僅是個指向commit對象的可變指針。Git使用master作爲分支的默認名字,在若干次提交後,就已經有了一個指向最後一次提交對象的master分支,它在每次提交的時候都會自動向前移動。

創建一個分支的命令爲git branch,例如創建一個test分支的命令如下:

git branch test

Git保存一個名爲HEAD的指針,它是一個指向你正在工作中的本地分支的指針(將HEAD想象爲當前分支的別名)。運行git branch命令只是建立了一個新的分支,但不會自動切換到這個分支中去。切換分支的命令爲git checkout,例如將分支切換爲test的命令爲:

git checkout test

這時HEAD就指向了test指針。當我們在test分支再次提交一次時,test分支有了新的提交數據,而master分支仍然指向原先git checkout時所在的commit對象。這時如果把分支切回master會發現,工作目錄中的文件切換成了master所指向的快照內容,而現在所做的改動也基於了本項目中一個較老的版本。它的主要作用是將test分支裏做的修改暫時取消,這樣就可以向另一個方向進行開發。

在master分支再次提交時,我們的項目提交歷史就產生了多個分支,我們可以在這多個分支中反覆切換,並在時機成熟時將它們合併到一起。

命令git checkout -b相當於運行了git branch命令和git checkout命令,即創建一個新的分支並切換到此分支上。

合併分支的命令爲git merge,例如合併分支test的命令爲:

git merge test

執行上述命令時,分支需要切換到master上。

有時在兩個分支中都修改了同一個文件的同一部分,這是Git在合併之後不會提交,會等你解決衝突。使用git status命令可以查看哪些文件在合併時發生了衝突。任何未解決衝突的文件都會以unmerged的狀態列出。解決衝突的方法時二者選其一或者自己整合到一起。

git branch命令還可以刪除分支,例如刪除test分支的命令爲:

git branch -d test

而當git branch命令不加任何參數時,它會給出當前所有分支的清單,輸出如下:

其中master前面的*表示當前所在的分支。

推送本地分支

可以將本地的分支推送到一個擁有寫權限的遠程倉庫,推送分支的命令爲git push 遠程倉庫名 分支名。當其他人從服務器上獲取數據時,將會得到一個新的分支。

跟蹤遠程分支

從遠程分支checkout出來的本地分支稱爲跟蹤分支。跟蹤分支是一種和某個遠程分支有直接聯繫的本地分支。在跟蹤分支裏輸入git push,Git會自行判斷應該向哪個服務器的哪個分支推送數據。同樣,在這些分支裏運行git pull會獲取所有遠程索引。

在克隆倉庫時,Git通常會自動創建一個名爲master的分支來跟蹤origin/master。可以使用命令git checkout -b 分支名 遠程倉庫名/分支名來獲取遠程分支。

刪除遠程分支

刪除遠程分支的命令爲git push 遠程倉庫名稱:分支名稱。

idea配置Git

使用快捷鍵Ctrl + Alt + s打開Settings,找到Git,然後按下圖設置:

點擊TEST之後,出現git版本號即爲設置成功。

我們還需要配置github或者gitlab,以github爲例。首先創建一個github賬號,然後在idea的Settings中找到github,如下圖:

點擊+之後如下圖:

在上方菜單欄的VCS中可以使用對應之前介紹的一些git命令的按鈕,如下圖:

另外,也可以在idea的命令行使用git命令。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章