1. 程式人生 > >Git版本控制系統之基本使用

Git版本控制系統之基本使用

     最早是通過接觸著名的開源社群Github瞭解到Git的,但一直沒有系統學習過。這次下定決心從頭到尾系統的學一學,也將學習過程記錄於此,供大家批駁。本篇文章先從以下幾個方面簡單瞭解一下Git:

  • Git的簡介以及Git與GitHub的關係
  • Git的下載和初始化資訊配置
  • Git的基礎指令及其含義

一、Git的簡介以及Git和GitHub的關係
     首先Git是由Linux 的締造者Linus Torvalds 創造的,當初這位大牛本著開源的精神將Linux 開源,那麼就需要對成千上萬的貢獻者的程式碼進行管理,起初所有的程式碼都以郵件的形式發給Linus ,然後由他來手動整合。但是隨著Linux越做越大,程式碼量也越來越大,已經無法完全由人來管理了。於是Linus選擇使用商業系統BitKeeper來管理這些程式碼,BitKeeper是一個成熟的版本控制系統,直到2005年兩家合作結束。於是Linus大神花了兩週時間用C寫出來了一個版本控制系統,就是現在已經無敵的Git。(牛就是這個樣子的!),想要了解更多Git歷史,可自行查閱資料,此處簡單擷取部分時間點以激發對git的興趣。

     版本控制系統就是一類用於管理我們專案進度點的系統,對於每次的修改給予記錄,一旦出現錯誤可以立即回滾。想想我們平時對於檔案的修改,一旦修改之後,對於之前的內容將會丟失,就是你想要回到過去某個未修改時的狀態,是無法做到的。而我們用版本控制系統追蹤我們的專案檔案,每次的修改都是記錄在案的,可以隨時回到過去。這裡我們不討論集中式版本控制系統和分散式版本控制系統的區別,我們只告訴你Git的分散式的,等學完之後你就會明白分散式的各種優點。

     相比於Git,我們可能更加熟悉的是GitHub。它是一個開源社群,是一個平臺。很多優秀的專案都在上面開源,供全世界的程式設計師學習和完善。例如:apache/tomcat,eclipse/jetty,jquery/jquery等。我們說Git可以記錄我們對工作檔案的每次修改,以便於我們可以快速恢復,但是為了多人協同工作,我們就需要共享同一個專案原始碼,所以一般就會將專案原始碼放在一個伺服器上,每個開發者從伺服器上下載當前最新的專案並進行工作,工作結束之後向伺服器推送自己所做的更改,這樣每個人無時無刻都在開發(修改)著專案的各個功能,當然位於伺服器上的專案也會無時無刻的記錄每個人的每次提交修改記錄。(出問題了可以迅速找到罪魁禍首)。而GitHub作為一個開源社群平臺,它可以充當那個伺服器。但是由於它是開源的,所以一般公司內部專案也是不建議利用GitHub作為公共伺服器來進行開發的。一般會自建伺服器配置成Git伺服器,在內網中使用。

二、Git的下載和初始化資訊配置
     三大平臺中,我從我自己使用的的windows平臺介紹Git的安裝,至於Linux和MacOS上的安裝大家可自行查閱資料。windows平臺下對Git的安裝是簡單的,前往https://git-for-windows.github.io下載msysgit,然後一鍵式安裝即可。msysgit整合的是Git和Cygwin,Cygwin是一種在windows中模擬Linux/Unix環境的工具,因為Git是需要用到Linux/Unix的一些工具的,所以有人集成了兩者以使得在windows平臺下也能使用Git。下面我們簡單說一下對Git的初始化資訊的配置。
     首先在安裝完成之後,滑鼠右鍵會增加了Git Bash here選項,那就是Git的命令列模式,我們點選即可展示命令列視窗。我們首先要做的就是告訴Git,本機的身份。因為以後我們在多人協作的時候,每次Git在提交的時候會署名當前提交者,而Git是如何得知提交者的資訊的?就是在初始化資訊的時候我們告訴它的。

$ git config --global user.name "Your name"
$ git config --global user.email "Your email"

一般我們配置以上兩條資訊即可,至於其他的資訊配置,例如:預設編輯器等 ,大家在需要用到的時候可以查詢資料簡單配置一下即可。到此為止,有關Git的簡單介紹基本介紹完成,下面我們看看在日常專案中最長使用的幾個Git命令及其含義。

三、基本Git指令
     這一小節是本篇文章的核心,主要涉及到初始化倉庫,新增追蹤檔案,暫存檔案,提交本地資料庫,恢復到以前狀態等。這些內容基本涵蓋了日常專案中最常見的操作,但是想要更加靈活的掌握Git以發揮其在專案管理中的巨大作用,這些是僅僅不夠的。下面我們看第一個指令,初始化倉庫。

     1、git init
     這是一條用於初始化倉庫的指令,執行它會在當前目錄下建立.git資料夾,該資料夾就是用於管理當前目錄中所有檔案的改動的,至於裡面都有哪些內容,後續文章會介紹,請記住,裡面的內容不可隨意更改,否則會破壞git結構導致無法跟蹤工作區檔案。例如,我在電腦 F盤中建立一個資料夾single,在git命令列中cd到該目錄,然後執行git init ,結果如下:

這裡寫圖片描述

開啟當前目錄,看到一個資料夾.git,這就是init命令執行之後建立的結果,它就是整個版本控制的核心。至於裡面的內容結構我們在後續的文章中介紹。

這裡寫圖片描述

從此,在f:/single目錄下為工作區,在這個目錄下的檔案都是可以被git追蹤的。

     2、git add filename
     我們說初始化之後,工作區中的檔案是可以被追蹤的,但是隻有告訴git哪些檔案需要追蹤,它才會顯式的去追蹤該檔案,否則git永遠會在你提交的時候告訴你工作區還用哪些檔案處於Untracked狀態。而我們add命令就是用來顯式告訴git哪些檔案從此時開始追蹤。例如我們在上述的工作區中建立一個檔案test.txt,然後使用以下命令就可以追蹤該檔案的任意改動情況:

git add test.txt

當然該命令可以多次使用,以新增對多個檔案的追蹤。有時候,我們為了方便會使用以下命令新增所有檔案的追蹤:

git add .

當然這個add指令的第一個作用就是為未被追蹤的檔案新增追蹤,第二個作用是對工作區某個檔案的修改進行儲存。因為git是根據你儲存後的狀態進行提交的,也就是說git的提交併不是提交的工作區的當前狀態,他提交的是你每次儲存後的所有檔案狀態,例如:我們建立了一個檔案test,並使用add命令對其進行追蹤了,下面我們為該檔案輸入一些內容(對檔案進行了修改,隨意輸入一些即可)。然後我們提交一下當前狀態:

這裡寫圖片描述

你看,git告訴我們,檔案test已經被修改,但是你並沒有進行儲存,所有此次no changes added to commit,沒有東西可提交的。而當我們add儲存該檔案修改之後:

這裡寫圖片描述

這次,git告訴我們本次提交已經成功新增到master分支上了,相比於上次commit狀態有一個檔案的修改。

     2、git commit
     在介紹commit提交之前,我們先簡單瞭解一下git的大致結構。深入理解它的結構會在後續文章中學習,此處是為了更加方便的理解幾個命令作用物件的不同。首先,git有三塊區塊,工作區,暫存區,分支。

這裡寫圖片描述

工作區就是我們能看見的,所謂的“當前目錄”。該目錄下有很多的檔案,這些檔案就是我們版本控制所要追蹤的內容。暫存區也稱為stage或者index,它儲存的是工作區的一次次修改情況,如上我們使用add命令儲存某個檔案的修改,記錄下該檔案當前快照。然後我們用commit命令向分支上提交,位於分支上的每個點都是一次commit留下的。當然我們在回滾的時候也是根據需要回滾到指定的點上。

有人會疑問,為什麼要有暫存區呢,直接工作區對接分支不就好了嗎?其實這樣做具有更高的效率,一般來說,分支上的每次commit都代表著一次重要功能的完成,不能說我們每寫一點程式碼就commit一下,這樣會導致太多的commit,未來想要迅速找到想要回滾的點不易。所以git使用暫存區儲存每一次小小的修改,等所有修改完成時候,commit命令將會把暫存的修改更新到分支上完成一次commit。

下面理解我們的commit命令就比較容易了,該命令會將暫存區的所有內容提交到分支上形成一次commit。一般我們只有在完成某一個完整的模組或者功能之後才進行一次commit提交,較小的修改則一般先在暫存區儲存。

     3、git clone
     當我們遇到比較好的開源專案並想要參與其中的時候,我們就需要獲取到別人專案的所有程式碼,這時候我們的clone命令就可以發揮作用了。我們在遠端伺服器上上建立一個庫,新建一個檔案index.txt,然後我們利用clone命令完成拷貝遠端倉庫的動作。

這裡寫圖片描述

然後我們開啟目錄,f://1testgit下即可看到一個倉庫test被完整的拷貝下來,test目錄裡面就是我們完整專案加上版本控制檔案。這裡需要說明一點,我們可以在命令後面指定拷貝下來的倉庫名稱,如果沒有顯式指定則預設和遠端的一樣。例如:

git clone https://github.com/Programer-yang/test.git myResptory

這樣本地倉庫的名稱不再和遠端的一樣了,被顯式修改成myResptory了。簡而言之,想要使用clone命令,首先需要知道將要被clone的倉庫的地址,可以是基於http協議的,也可以是基於git協議的,然後我們需要進入到指定目錄,選擇性的指定被克隆下來的倉庫在本地的名稱。

     4、git status
     該命令會和我們接下來要介紹的diff命令有點混淆,但是如果掌握他們的本質,區別也是顯而易見的。首先我們要知道,status命令是用來檢視當前工作區狀態的,也就是說它會把當前工作區的所有檔案狀態和我們本地分支上最近一次的提交進行比較,並列出所有做出的修改條目。例如,我們在當前目錄下建立一個檔案test並鍵入簡單內容,commit到master分支(此時工作區和暫存區以及本地分支都是乾淨的,一樣的),而我們執行status命令也會得到以下輸出:

這裡寫圖片描述

它告訴我們工作區和分支上內容完全一樣,並沒有多餘的修改。但是當我們修改index檔案內容之後執行status命令,得到以下結果:

這裡寫圖片描述

輸出結果很顯然的告訴我們,和本地分支的最近一次提交相比,工作區對index這個檔案做了修改,並且還沒有向暫存區儲存。這是該命令額外的功能,如果是紅色字型說明你不僅修改了工作區還沒有向暫存區儲存,如果是綠色的字型則說明你修改了工作區並向暫存區儲存了。看:

這裡寫圖片描述

以上我們簡單介紹了status這個命令的基本使用,下面介紹diff命令。

     5、git diff
     diff命令也是用來檢視當前狀態的,只是它不同於status,它比較的是工作區和暫存區之間的區別。例如,我們對於乾淨的工作區,修改index檔案,然後執行diff命令。輸出如下:

這裡寫圖片描述

從輸出結果我們可以看出來,diff命令為我們列出了當前工作區和暫存區檔案狀態的詳細區別。例如上圖告訴我們,暫存區檔案(紅色字型代表暫存區檔案的所有內容)index中只有一行資訊 hello world,而當前工作區(綠色字型代表工作區檔案內容)index中有兩行資訊。如果你是多個檔案發生了更改,那麼該命令會列出所有被修改檔案前後所有內容供程式設計師檢視。

當然我們以上介紹的diff命令是無參的,所以它比較的是工作區和暫存區之間的差異,我們也可以使用引數來指定工作區,暫存區,本地分支兩兩之間的比較。例如:

git diff --cached:比較的是暫存區和本地分支的差異

git difff HEAD:比較的是工作區和本地分支的差異

由此看來,對於git中三個不同區的狀態之間差異的檢視我們都可以通過diff命令來完成。顯而易見,diff命令要比status命令輸出內容更加詳細,但是status命令也有其自己的使用場景。

     6、git log
     這是一個幫助我們檢視歷史提交資訊的命令,例如:

這裡寫圖片描述

我們可以看到,輸出結果主要是歷次commit對應的id以及提交者的資訊時間,還有提交的描述資訊。當然最重要的還是這個由40位十六進位制數字組成的SHA-1值,這樣每次提交都對應一個唯一的id,也方便我們回退歷史版本。下面簡單介紹一個該命令的一些選項引數,使用帶引數或者選項的log命令可以為我們提供更加直觀的資訊,快速的獲取到我們需要的內容。

直接使用git log命令將會輸出歷史所有的提交資訊,有時我們只需要檢視進幾次的提交資訊,就可以使用-<n>選項來顯式指定輸出的commit次數。例如:

這裡寫圖片描述

我們也可以使用-pretty=xxx引數來指定輸出的提交資訊的簡易程度,有以下一些引數值可供選擇,oneline,short,full 和 fuller。例如:

這裡寫圖片描述

其他引數的輸出情況大家可以自行嘗試實踐。

還有一個選項是-p,我們往往對於某次提交都是會附帶描述資訊的,但是有時候這些描述資訊不是很準確,而我們又想確定此次提交做出了那些修改,這時可以使用-p選項來輸出此次提交的修改內容。例如:

這裡寫圖片描述

輸出結果告訴我們,該命令會列出當前提交對其中檔案所有的更改情況。和我們的diff命令輸出一樣。

     7、git reset
     我們使用git最重要的一個目的就是,隨時可以回退歷史版本,而我們的reset命令就是完成這個工作的。例如:我們在git工作區建立一個檔案index然後做一次提交(描述資訊one),然後我們為index檔案鍵入一些內容,再一次提交(描述資訊two),這樣我們構建了兩次提交,最新一次的提交為two,我們可以利用reset回退到one。

這裡寫圖片描述

我們看到,在回退之前最新的兩次commit分別是two和one,但當我們回退到上一個版本的時候,原最新的commit被丟棄,head指標指向one。這裡需要說明一點的是,head^符號表示的上一個commit的id,我們除了可以這麼回退以外,還可以使用某個commit的唯一標識來回退,例如:

這裡寫圖片描述

如圖所示,我們成功的回退到描述為 ”the forth commit”的版本,很顯然,我們是使用commit的唯一標識來回退的,所以針對任意的一個commit,我們都可以根據他的標識直接回退。但是這裡需要注意的是,reset命令的執行將會直接導致工作區,暫存區,本地分支完全一致,也就是工作樹是乾淨的,所以回退前要慎重檢查是否有內容尚未儲存,否則將會導致全部丟失。

至此,本篇文章簡單介紹了git的幾個常用的指令以及他們的簡單應用,當然git有很多命令,我們只是列舉了幾個常用的,至於其他的一些命令部分會出現在後續文章中,我們還將介紹git很核心的內容:分支,這也是git能夠多人協作的一個關鍵點。系列文章參考了一些書籍和網路教程,總結不到之處,望大家指出。