Git詳解之二 Git基礎(第一部分)

Git 基礎

      讀完本章你就能上手使用 Git 了。本章將介紹幾個最基本的,也是最經常使用的 Git 命令,之後絕大多數時間裏用到的也就是這幾個命令。讀完本章,你就能初始化一個新的代碼倉庫,作一些適當配置;開始或中止跟蹤某些文件;暫存或提交某些更 新。咱們還會展現如何讓 Git 忽略某些文件,或是名稱符合特定模式的文件;如何既快且容易地撤消犯下的小錯誤;如何瀏覽項目的更新歷史,查看某兩次更新之間的差別;以及如何從遠程倉庫 拉數據下來或者推數據上去。python

2.1  取得項目的 Git 倉庫

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

在工做目錄中初始化新倉庫github

要對現有的某個項目開始用 Git 管理,只需到此項目所在的目錄,執行:正則表達式

$ git init

初始化後,在當前目錄下會出現一個名爲 .git 的目錄,全部 Git 須要的數據和資源都存放在這個目錄中。不過目前,僅僅是按照既有的結構框架初始化好了裏邊全部的文件和目錄,但咱們尚未開始跟蹤管理項目中的任何一個文件。(在第九章咱們會詳細說明剛纔建立的.git 目錄中究竟有哪些文件,以及都起些什麼做用。)shell

若是當前目錄下有幾個文件想要歸入版本控制,須要先用 git add 命令告訴 Git 開始對這些文件進行跟蹤,而後提交:vim

$ git add *.c $ git add README 
$ git commit -m 'initial project version'

稍後咱們再逐一解釋每條命令的意思。不過如今,你已經獲得了一個實際維護着若干文件的 Git 倉庫。服務器

從現有倉庫克隆框架

       若是想對某個開源項目出一份力,能夠先把該項目的 Git 倉庫複製一份出來,這就須要用到 git clone 命令。若是你熟悉其餘的 VCS 好比 Subversion,你可能已經注意到這裏使用的是 clone 而不是 checkout。這是個很是重要的差異,Git 收取的是項目歷史的全部數據(每個文件的每個版本),服務器上有的數據克隆以後本地也都有了。實際上,即使服務器的磁盤發生故障,用任何一個克隆出來 的客戶端均可以重建服務器上的倉庫,回到當初克隆時的狀態(雖然可能會丟失某些服務器端的掛鉤設置,但全部版本的數據仍舊還在,有關細節請參考第四章)。ssh

克隆倉庫的命令格式爲 git clone [url]。好比,要克隆 Ruby 語言的 Git 代碼倉庫 Grit,能夠用下面的命令:編輯器

$ git clone git://github.com/schacon/grit.git

      這會在當前目錄下建立一個名爲「grit」的目錄,其中包含一個 .git 的目錄,用於保存下載下來的全部版本記錄,而後從中取出最新版本的文件拷貝。若是進入這個新建的grit 目錄,你會看到項目中的全部文件已經在裏邊了,準備好後續的開發和使用。若是但願在克隆的時候,本身定義要新建的項目目錄名稱,能夠在上面的命令末尾指定新的名字:

$ git clone git://github.com/schacon/grit.git mygrit

惟一的差異就是,如今新建的目錄成了 mygrit,其餘的都和上邊的同樣。

Git 支持許多數據傳輸協議。以前的例子使用的是

 git:// 協議,不過你也能夠用 
 http(s):// 
 ssh://user@server:/path.git 表示的 SSH 傳輸協議.

咱們會在第四章詳細介紹全部這些協議在服務器端該如何配置使用,以及各類方式之間的利弊。

2.2  記錄每次更新到倉庫

     如今咱們手上已經有了一個真實項目的 Git 倉庫,並從這個倉庫中取出了全部文件的工做拷貝。接下來,對這些文件做些修改,在完成了一個階段的目標以後,提交本次更新到倉庫。

請記住,工做目錄下面的全部文件都不外乎這兩種狀態:已跟蹤或未跟蹤。已跟蹤的文件是指原本就被歸入版本控制管理的文件,在上次快照中有它們的記 錄,工做一段時間後,它們的狀態多是未更新,已修改或者已放入暫存區。而全部其餘文件都屬於未跟蹤文件。它們既沒有上次更新時的快照,也不在當前的暫存 區域。初次克隆某個倉庫時,工做目錄中的全部文件都屬於已跟蹤文件,且狀態爲未修改。

在編輯過某些文件以後,Git 將這些文件標爲已修改。咱們逐步把這些修改過的文件放到暫存區域,直到最後一次性提交全部這些暫存起來的文件,如此重複。因此使用 Git 時的文件狀態變化週期如圖 2-1 所示。

圖 2-1. 文件的狀態變化週期

檢查當前文件狀態

要肯定哪些文件當前處於什麼狀態,能夠用 git status 命令。若是在克隆倉庫以後當即執行此命令,會看到相似這樣的輸出:

$ git status # On branch master nothing to commit (working directory clean)

這說明你如今的工做目錄至關乾淨。換句話說,當前沒有任何跟蹤着的文件,也沒有任何文件在上次提交後更改過。此外,上面的信息還代表,當前目錄下沒 有出現任何處於未跟蹤的新文件,不然 Git 會在這裏列出來。最後,該命令還顯示了當前所在的分支是 master,這是默認的分支名稱,實際是能夠修改的,如今先不用考慮。下一章咱們就會詳細討論分支和引用。

如今讓咱們用 vim 編輯一個新文件 README,保存退出後運行 git status 會看到該文件出如今未跟蹤文件列表中:

$ vim README $ git status 
# On branch master 
# Untracked files:
# (use "git add ..." to include in what will be committed) # #

就是在「Untracked files」這行下面。Git 不會自動將之歸入跟蹤範圍,除非你明明白白地告訴它「我須要跟蹤該文件」,於是不用擔憂把臨時文件什麼的也納入版本管理。不過如今的例子中,咱們確實想要跟蹤管理 README 這個文件。

跟蹤新文件

使用命令 git add 開始跟蹤一個新文件。因此,要跟蹤 README 文件,運行:

$ git add README

此時再運行 git status 命令,會看到 README 文件已被跟蹤,並處於暫存狀態:

$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # #	new file: README #

只要在 「Changes to be committed」 這行下面的,就說明是已暫存狀態。若是此時提交,那麼該文件此時此刻的版本將被留存在歷史記錄中。你可能會想起以前咱們使用git init 後就運行了 git add 命令,開始跟蹤當前目錄下的文件。在git add 後面能夠指明要跟蹤的文件或目錄路徑。若是是目錄的話,就說明要遞歸跟蹤該目錄下的全部文件。(譯註:其實git add 的潛臺詞就是把目標文件快照放入暫存區域,也就是 add file into staged area,同時不曾跟蹤過的文件標記爲須要跟蹤。這樣就好理解後續 add 操做的實際意義了。)

暫存已修改文件

如今咱們修改下以前已跟蹤過的文件 benchmarks.rb,而後再次運行 status 命令,會看到這樣的狀態報告:

$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # #	new file: README # #

文件 benchmarks.rb 出如今 「Changed but not updated」 這行下面,說明已跟蹤文件的內容發生了變化,但尚未放到暫存區。要暫存此次更新,須要運行git add 命令(這是個多功能命令,根據目標文件的狀態不一樣,此命令的效果也不一樣:能夠用它開始跟蹤新文件,或者把已跟蹤的文件放到暫存區,還能用於合併時把有衝突的文件標記爲已解決狀態等)。如今讓咱們運行git add 將 benchmarks.rb 放到暫存區,而後再看看 git status 的輸出:

$ git add benchmarks.rb 
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # #

如今兩個文件都已暫存,下次提交時就會一併記錄到倉庫。假設此時,你想要在 benchmarks.rb 裏再加條註釋,從新編輯存盤後,準備好提交。不過且慢,再運行git status 看看:

$ vim benchmarks.rb 
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # #	ne

怎麼回事?benchmarks.rb 文件出現了兩次!一次算未暫存,一次算已暫存,這怎麼可能呢?好吧,實際上 Git 只不過暫存了你運行 git add 命令時的版本,若是如今提交,那麼提交的是添加註釋前的版本,而非當前工做目錄中的版本。因此,運行了git add 以後又做了修訂的文件,須要從新運行 git add 把最新版本從新暫存起來:

$ git add benchmarks.rb 
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) #

忽略某些文件

通常咱們總會有些文件無需歸入 Git 的管理,也不但願它們總出如今未跟蹤文件列表。一般都是些自動生成的文件,好比日誌文件,或者編譯過程當中建立的臨時文件等。咱們能夠建立一個名爲 .gitignore 的文件,列出要忽略的文件模式。來看一個實際的例子:

$ cat .gitignore
 *.[oa] *~

第一行告訴 Git 忽略全部以 .o 或 .a 結尾的文件。通常這類對象文件和存檔文件都是編譯過程當中出現的,咱們用不着跟蹤它們的版本。第二行告訴 Git 忽略全部以波浪符(~)結尾的文件,許多文本編輯軟件(好比 Emacs)都用這樣的文件名保存副本。此外,你可能還須要忽略 log,tmp 或者 pid 目錄,以及自動生成的文檔等等。要養成一開始就設置好 .gitignore 文件的習慣,以避免未來誤提交這類無用的文件。

文件 .gitignore 的格式規範以下:

全部空行或者以註釋符號 # 開頭的行都會被 Git 忽略。

可使用標準的 glob 模式匹配。 * 匹配模式最後跟反斜槓(/)說明要忽略的是目錄。 * 要忽略指定模式之外的文件或目錄,能夠在模式前加上驚歎號(!)取反。

所謂的 glob 模式是指 shell 所使用的簡化了的正則表達式。星號(*)匹配零個或多個任意字符;[abc] 匹配任何一個列在方括號中的字符(這個例子要麼匹配一個 a,要麼匹配一個 b,要麼匹配一個 c);問號(?)只匹配一個任意字符;若是在方括號中使用短劃線分隔兩個字符,表示全部在這兩個字符範圍內的均可以匹配(好比[0-9] 表示匹配全部 0 到 9 的數字)。

咱們再看一個 .gitignore 文件的例子:

# 此爲註釋 – 將被 Git 忽略 *.a # 忽略全部 .a 結尾的文件 !lib.a # 但 lib.a 除外 /TODO # 僅僅忽略項目根目錄下的 TODO 文件,不包

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

實際上 git status 的顯示比較簡單,僅僅是列出了修改過的文件,若是要查看具體修改了什麼地方,能夠用 git diff命令。稍後咱們會詳細介紹git diff,不過如今,它已經能回答咱們的兩個問題了:當前作的哪些更新尚未暫存?有哪些更新已經暫存起來準備好了下次提交? git diff 會使用文件補丁的格式顯示具體添加和刪除的行。

假如再次修改 README 文件後暫存,而後編輯 benchmarks.rb 文件後先別暫存,運行 status 命令,會看到:

$ git status
 # On branch master # Changes to be committed: 
 # (use "git reset HEAD ..." to unstage) # 
 #	new file: README # # Changed but not updated: 
 # (use "git add ..." to update what will be committed) # 
 #	modified: benchmarks.rb #

要查看還沒有暫存的文件更新了哪些部分,不加參數直接輸入 git diff:

$ git diff diff --git a/benchmarks.rb b/benchmarks.rb index 3cb747f..da65585 100644 --- a/benchmarks.rb +++ b/benchmarks.rb @@ -36,6 +36,10 @@ def main @commit.parents[0].parents[0].parents[0] end + run_code(x, 'commits 1') do + git.commits.size + end + run_code(x, 'commits 2') do log = git.commits('master', 15) log.size

此命令比較的是工做目錄中當前文件和暫存區域快照之間的差別,也就是修改以後尚未暫存起來的變化內容。

若要看已經暫存起來的文件和上次提交時的快照之間的差別,能夠用 git diff --cached 命令。(Git 1.6.1 及更高版本還容許使用git diff --staged,效果是相同的,但更好記些。)來看看實際的效果:

$ git diff 
--cached diff --git a/README b/README new file mode 100644 index 0000000..03902a1 --- /dev/null +++ b/README2 @@ -0,0 +1,5 @@ +grit + by Tom Preston-Werner, Chris Wanstrath + http://github.com/mojombo/grit + +Grit is a Ruby library for extracting information from a Git repository

請注意,單單 git diff 不過是顯示尚未暫存起來的改動,而不是此次工做和上次提交之間的差別。因此有時候你一會兒暫存了全部更新過的文件後,運行git diff 後卻什麼也沒有,就是這個緣由。

像以前說的,暫存 benchmarks.rb 後再編輯,運行 git status 會看到暫存先後的兩個版本:

$ git add benchmarks.rb 
$ echo '# test line' >> benchmarks.rb 
$ git status # On branch master # # Changes to be committed: # #	modified: benchmarks.rb # # Changed but not updated: # #	modified: benchmarks.rb #

如今運行 git diff 看暫存先後的變化:

$ git diff 
diff --git a/benchmarks.rb b/benchmarks.rb index e445e28..86b2f7c 100644 --- a/benchmarks.rb +++ b/benchmarks.rb @@ -127,3 +127,4 @@ end main() ##pp Grit::GitRuby.cache_client.stats +# test line

而後用 git diff --cached 查看已經暫存起來的變化:

$ git diff --cached 
diff --git a/benchmarks.rb b/benchmarks.rb index 3cb747f..e445e28 100644 --- a/benchmarks.rb +++ b/benchma

提交更新

      如今的暫存區域已經準備穩當能夠提交了。在此以前,請必定要確認還有什麼修改過的或新建的文件尚未 git add 過,不然提交的時候不會記錄這些還沒暫存起來的變化。因此,每次準備提交前,先用git status 看下,是否是都已暫存起來了,而後再運行提交命令 git commit:

$ git commit

      這種方式會啓動文本編輯器以便輸入本次提交的說明。(默認會啓用 shell 的環境變量 $EDITOR 所指定的軟件,通常都是 vim 或 emacs。固然也能夠按照第一章介紹的方式,使用git config --global core.editor 命令設定你喜歡的編輯軟件。)

編輯器會顯示相似下面的文本信息(本例選用 Vim 的屏顯方式展現):

# Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # new file: README # modified: benchmarks.rb ~ ~ ~ ".git/COMMIT_EDITMSG" 10L, 283C

能夠看到,默認的提交消息包含最後一次運行 git status 的輸出,放在註釋行裏,另外開頭還有一空行,供你輸入提交說明。你徹底能夠去掉這些註釋行,不過留着也不要緊,多少能幫你回想起此次更新的內容有哪些。(若是以爲這還不夠,能夠用-v 選項將修改差別的每一行都包含到註釋中來。)退出編輯器時,Git 會丟掉註釋行,將說明內容和本次更新提交到倉庫。

另外也能夠用 -m 參數後跟提交說明的方式,在一行命令中提交更新:

$ git commit -m "Story 182: Fix benchmarks for speed" [master]: created 463dc4f: "Fix benchmarks for speed" 2 files changed, 3 insertions(+), 0 deletions(-) create mode 100644 README

好,如今你已經建立了第一個提交!能夠看到,提交後它會告訴你,當前是在哪一個分支(master)提交的,本次提交的完整 SHA-1 校驗和是什麼(463dc4f),以及在本次提交中,有多少文件修訂過,多少行添改和刪改過。

記住,提交時記錄的是放在暫存區域的快照,任何還未暫存的仍然保持已修改狀態,能夠在下次提交時歸入版本管理。每一次運行提交操做,都是對你項目做一次快照,之後能夠回到這個狀態,或者進行比較。

跳過使用暫存區域

儘管使用暫存區域的方式能夠精心準備要提交的細節,但有時候這麼作略顯繁瑣。Git 提供了一個跳過使用暫存區域的方式,只要在提交的時候,給 git commit 加上-a 選項,Git 就會自動把全部已經跟蹤過的文件暫存起來一併提交,從而跳過 git add 步驟:

$ git status # On branch master # # Changed but not updated: # #modified: benchmarks.rb #
$ git commit -a -m 'added new benchmarks' 
 [master 83e38c7] added new benchmarks 1 files changed, 5 insertions(+), 0 deletions(-)

看到了嗎?提交以前再也不須要 git add 文件 benchmarks.rb 了。

移除文件

      要從 Git 中移除某個文件,就必需要從已跟蹤文件清單中移除(確切地說,是從暫存區域移除),而後提交。能夠用git rm 命令完成此項工做,並連帶從工做目錄中刪除指定的文件,這樣之後就不會出如今未跟蹤文件清單中了。

若是隻是簡單地從工做目錄中手工刪除文件,運行 git status 時就會在 「Changed but not updated」 部分(也就是_未暫存_清單)看到:

$ rm grit.gemspec
$ git status # On branch master # # Changed but not updated: # (use "git add/rm ..." to update what will be committed) # # deleted: grit.gemspec #

而後再運行 git rm 記錄這次移除文件的操做:

$ git rm grit.gemspec rm 'grit.gemspec' 
$ git status # On branch master # # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # deleted: grit.gemspec #

       最後提交的時候,該文件就再也不歸入版本管理了。若是刪除以前修改過而且已經放到暫存區域的話,則必需要用強制刪除選項 -f(譯註:即 force 的首字母),以防誤刪除文件後丟失修改的內容。

      另一種狀況是,咱們想把文件從 Git 倉庫中刪除(亦即從暫存區域移除),但仍然但願保留在當前工做目錄中。換句話說,僅是從跟蹤清單中刪除。好比一些大型日誌文件或者一堆.a 編譯文件,不當心歸入倉庫後,要移除跟蹤但不刪除文件,以便稍後在 .gitignore 文件中補上,用 --cached 選項便可:

$ git rm --cached readme.txt

後面能夠列出文件或者目錄的名字,也可使用 glob 模式。比方說:

$ git rm log/\*.log

注意到星號 * 以前的反斜槓 \,由於 Git 有它本身的文件模式擴展匹配方式,因此咱們不用 shell 來幫忙展開(譯註:實際上不加反斜槓也能夠運行,只不過按照 shell 擴展的話,僅僅刪除指定目錄下的文件而不會遞歸匹配。上面的例子原本就指定了目錄,因此效果等同,但下面的例子就會用遞歸方式匹配,因此必須加反斜 槓。)。此命令刪除全部log/ 目錄下擴展名爲 .log 的文件。相似的好比:

$ git rm \*~

會遞歸刪除當前目錄及其子目錄中全部 ~ 結尾的文件。

相關文章
相關標籤/搜索