一入前端深似海,從此紅塵是路人系列第十彈之如何合理利用Git進行團隊協作(一)
前言:
這裡簡單介紹一下Git的歷史。
同生活中的許多偉大事件一樣,Git 誕生於一個極富紛爭大舉創新的年代。Linux 內核開源項目有著為數眾廣的參與者。絕大多數的 Linux 內核維護工作都花在了提交補丁和保存歸檔的繁瑣事務上(1991-2002年間)。到 2002 年,整個項目組開始啟用分散式版本控制系統 BitKeeper 來管理和維護代碼。
到了 2005 年,開發 BitKeeper 的商業公司同 Linux 內核開源社區的合作關係結束,他們收回了免費使用 BitKeeper 的權力。這就迫使 Linux 開源社區(特別是 Linux 的締造者 Linus Torvalds )不得不吸取教訓,只有開發一套屬於自己的版本控制系統才不至於重蹈覆轍。他們對新的系統制訂了若干目標:
- 速度
- 簡單的設計
- 對非線性開發模式的強力支持(允許上千個並行開發的分支)
- 完全分散式
- 有能力高效管理類似 Linux 內核一樣的超大規模項目(速度和數據量)
自誕生於 2005 年以來,Git 日臻成熟完善,在高度易用的同時,仍然保留著初期設定的目標。它的速度飛快,極其適合管理大項目,它還有著令人難以置信的非線性分支管理系統(見第三章),可以應付各種複雜的項目開發需求。
一、Git起步
1、直接記錄快照,而非差異比較
Git 和其他版本控制系統的主要差別在於,Git 只關心文件數據的整體是否發生變化,而大多數其他系統則只關心文件內容的具體差異。這類系統(CVS,Subversion,Perforce,Bazaar 等等)每次記錄有哪些文件作了更新,以及都更新了哪些行的什麼內容,具體如圖
Git 並不保存這些前後變化的差異數據。實際上,Git 更像是把變化的文件作快照後,記錄在一個微型的文件系統中。每次提交更新時,它會縱覽一遍所有文件的指紋信息並對文件作一快照,然後保存一個指向這次快照的索引。為提高性能,若文件沒有變化,Git 不會再次保存,而只對上次保存的快照作一鏈接。Git 的工作方式就像圖所示
2、運行Git前的配置這裡如果還有小夥伴們沒有安裝好Git,請自行去安裝一下先哦(Git - Downloads)。
Git 提供了一個叫做 git config 的工具,專門用來配置或讀取相應的工作環境變數。而正是由這些環境變數,決定了 Git 在各個環節的具體工作方式和行為。這些變數可以存放在以下三個不同的地方:
- /etc/gitconfig 文件:系統中對所有用戶都普遍適用的配置。若使用 git config 時用 --system 選項,讀寫的就是這個文件。
- ~/.gitconfig 文件:用戶目錄下的配置文件只適用於該用戶。若使用 git config 時用 --global 選項,讀寫的就是這個文件。
- 當前項目的 git 目錄中的配置文件(也就是工作目錄中的 .git/config 文件):這裡的配置僅僅針對當前項目有效。每一個級別的配置都會覆蓋上層的相同配置,所以 .git/config 里的配置會覆蓋 /etc/gitconfig 中的同名變數。
a. 用戶信息配置
第一個要配置的是你個人的用戶名稱和電子郵件地址。這兩條配置很重要,每次 Git 提交時都會引用這兩條信息,說明是誰提交了更新,所以會隨更新內容一起被永久納入歷史記錄
git config --global user.name "qiangdada"ngit config --global user.email qiangdada@example.comn
如果用了 --global 選項,那麼更改的配置文件就是位於你用戶主目錄下的那個,以後你所有的項目都會默認使用這裡配置的用戶信息。如果要在某個特定的項目中使用其他名字或者電郵,只要去掉 --global 選項重新配置即可,新的設定保存在當前項目的 .git/config 文件里。
b. 差異分析工具
Git 可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff 等合併工具的輸出信息,這裡,如果說在解決合併衝突時使用的是vimdiff差異分析工具。改用命令如下
git config --global merge.tool vimdiffn
c. 查看配置信息
git config --listn #user.name=qiangdadan #user.email=qiangdada@tencent.comn #color.status=auton #color.branch=auton #color.interactive=auton #color.diff=auton #...n
這裡會看到重複的變數名,那就說明它們來自不同的配置文件(比如 /etc/gitconfig 和 ~/.gitconfig),不過最終 Git 實際採用的是最後一個。也可以直接查閱某個環境變數的設定,只要把特定的名字跟在後面即可,像這樣
git config user.namen #qiangdadan
d. 獲取幫助
想了解 Git 的各式工具該怎麼用,可以閱讀它們的使用幫助,方法有三
# 方法1ngit helpn#方法二ngit --helpn#方法三nman gitn
比如,要學習 config 命令可以怎麼用,運行
git help confign
1、取得項目的Git倉庫
有兩種取得 Git 項目倉庫的方法。第一種是在現存的目錄下,通過導入所有文件來創建新的 Git 倉庫。第二種是從已有的 Git 倉庫克隆出一個新的鏡像倉庫來
a. 從工作目錄中初始化新倉庫
要對現有的某個項目開始用 Git 管理,只需到此項目所在的目錄,執行
git initn
初始化後,在當前目錄下會出現一個名為 .git 的目錄,所有 Git 需要的數據和資源都存放在這個目錄中。如果當前目錄下有幾個文件想要納入版本控制,需要先用 git add 命令告訴 Git 開始對這些文件進行跟蹤,然後提交
# step 1ngit add *.cn# step 2ngit add READMEn# step 3ngit commit -m initial project versionn
b. 從現有倉庫克隆
如果想對某個開源項目出一份力,可以先把該項目的 Git 倉庫複製一份出來,這就需要用到 git clone 命令。如果你熟悉其他的 VCS 比如 Subversion,你可能已經注意到這裡使用的是 clone 而不是 checkout。這是個非常重要的差別,Git 收取的是項目歷史的所有數據(每一個文件的每一個版本),伺服器上有的數據克隆之後本地也都有了。實際上,即便伺服器的磁碟發生故障,用任何一個克隆出來的客戶端都可以重建伺服器上的倉庫,回到當初克隆時的狀態
git clone [url]n# 如 git clone https://github.com/xuqiang521/data-visualization.gitn
2、記錄每次更新到倉庫
先上一張文件的狀態變化周期的圖示
a. 檢查當前文件狀態
git statusn
b. 跟蹤新文件
使用命令 git add 開始跟蹤一個新文件README。運行# step 1ngit add READMEn# step 2ngit statusn# 此時文件屬於暫存狀態n # On branch mastern # Changes to be committed:n # (use "git reset HEAD <file>..." to unstage)n # new file: READMEn
假設在這之前我就已經跟蹤過一個文件叫 benchmarks.rb,然後再次運行 status 命令,會看到這樣的狀態報告
git statusn # On branch mastern # Changes to be committed:n # (use "git reset HEAD <file>..." to unstage)n #n # new file: READMEn #n # Changes not staged for commit:n # (use "git add <file>..." to update what will be committed)n #n # modified: benchmarks.rbn
文件 benchmarks.rb 出現在 「Changes not staged for commit」 這行下面,說明已跟蹤文件的內容發生了變化,但還沒有放到暫存區。要暫存這次更新,需要運行 git add 命令。現在讓我們運行 git add 將 benchmarks.rb 放到暫存區,然後再看看 git status 的輸出
# step 1ngit add benchmarks.rbn# step 2ngit statusn # On branch mastern # Changes to be committed:n # (use "git reset HEAD <file>..." to unstage)n #n # new file: READMEn # modified: benchmarks.rbn
現在兩個文件都已暫存,下次提交時就會一併記錄到倉庫。假設此時,你想要在 benchmarks.rb 里再加條注釋,重新編輯存檔後,準備好提交。不過且慢,再運行 git status 看看
git statusn # On branch mastern # Changes to be committed:n # (use "git reset HEAD <file>..." to unstage)n #n # new file: READMEn # modified: benchmarks.rbn #n # Changes not staged for commit:n # (use "git add <file>..." to update what will be committed)n #n # modified: benchmarks.rbn
很明顯,benchmarks.rb 文件出現了兩次!一次算未暫存,一次算已暫存,這怎麼可能呢?好吧,實際上 Git 只不過暫存了你運行 git add命令時的版本,如果現在提交,那麼提交的是添加註釋前的版本,而非當前工作目錄中的版本。所以,運行了 git add 之後又作了修訂的文件,需要重新運行 git add 把最新版本重新暫存起來
# step 1ngit add benchmarks.rbn# step 2ngit statusn # On branch mastern # Changes to be committed:n # (use "git reset HEAD <file>..." to unstage)n #n # new file: READMEn # modified: benchmarks.rbn
c. 忽略某些文件
一般我們總會有些文件無需納入 Git 的管理,也不希望它們總出現在未跟蹤文件列表。通常都是些自動生成的文件,比如日誌文件,或者編譯過程中創建的臨時文件等。我們可以創建一個名為 .gitignore 的文件,列出要忽略的文件模式。來看一個實際的例子
cat .gitignoren #告訴 Git 忽略所有以 .o 或 .a 結尾的文件。一般這類對象文件和存檔文件都是編譯過程中出現的,我們用不著跟蹤它們的版本n *.[oa]n #告訴 Git 忽略所有以波浪符(~)結尾的文件,許多文本編輯軟體(比如 Emacs)都用這樣的文件名保存副本n *~n#此外,你可能還需要忽略 log,tmp 或者 pid 目錄,以及自動生成的文檔等等。n#要養成一開始就設置好 .gitignore 文件的習慣,以免將來誤提交這類無用的文件n
文件 .gitignore 的格式規範如下:
- 所有空行或者以注釋符號 # 開頭的行都會被 Git 忽略。
- 可以使用標準的 glob 模式匹配。
- 匹配模式最後跟反斜杠(/)說明要忽略的是目錄。
- 要忽略指定模式以外的文件或目錄,可以在模式前加上驚嘆號(!)取反。
所謂的 glob 模式是指 shell 所使用的簡化了的正則表達式。星號(*)匹配零個或多個任意字元;[abc] 匹配任何一個列在方括弧中的字元(這個例子要麼匹配一個 a,要麼匹配一個 b,要麼匹配一個 c);問號(?)只匹配一個任意字元;如果在方括弧中使用短劃線分隔兩個字元,表示所有在這兩個字元範圍內的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的數字)。
我們再看一個 .gitignore 文件的例子
# 此為注釋 – 將被 Git 忽略n # 忽略所有 .a 結尾的文件n *.an # 但 lib.a 除外n !lib.an # 僅僅忽略項目根目錄下的 TODO 文件,不包括 subdir/TODOn /TODOn # 忽略 build/ 目錄下的所有文件n build/n # 會忽略 doc/notes.txt 但不包括 doc/server/arch.txtn doc/*.txtn
d. 查看已暫存和未暫存的更新
實際上 git status 的顯示比較簡單,僅僅是列出了修改過的文件,假如再次修改 README 文件後暫存,然後編輯 benchmarks.rb 文件後先別暫存,運行 status 命令將會看到
git statusn # On branch mastern # Changes to be committed:n # (use "git reset HEAD <file>..." to unstage)n #n # new file: READMEn #n # Changes not staged for commit:n # (use "git add <file>..." to update what will be committed)n #n # modified: benchmarks.rbn
如果這個時候我需要查看尚未暫存的文件更新了哪些部分,那麼不妨不加參數直接輸入 git diff
git diffn
此命令比較的是工作目錄中當前文件和暫存區域快照之間的差異,也就是修改之後還沒有暫存起來的變化內容。
若要看已經暫存起來的文件和上次提交時的快照之間的差異,可以用 git diff --cached 命令。
git diff --cachedn
e. 提交更新
現在的暫存區域已經準備妥當可以提交了。在此之前,請一定要確認還有什麼修改過的或新建的文件還沒有 git add 過,否則提交的時候不會記錄這些還沒暫存起來的變化。所以,每次準備提交前,先用 git status 看下,是不是都已暫存起來了,然後再運行提交命令 git commit
git commitn
這種方式會啟動文本編輯器以便輸入本次提交的說明,編輯器會顯示類似下面的文本信息(本例選用 Vim 的屏顯方式展示)
# Please enter the commit message for your changes. Lines startingn # with # will be ignored, and an empty message aborts the commit.n # On branch mastern # Changes to be committed:n # (use "git reset HEAD <file>..." to unstage)n #n # new file: READMEn # modified: benchmarks.rbn ~n ~n ~n ".git/COMMIT_EDITMSG" 10L, 283Cn
另外也可以用 -m 參數後跟提交說明的方式,在一行命令中提交更新
git commit -m "up"n
如果我們需要跳過使用暫存區域,不用擔心。Git 提供了一個跳過使用暫存區域的方式,只要在提交的時候,給 git commit 加上 -a 選項,Git 就會自動把所有已經跟蹤過的文件暫存起來一併提交,從而跳過 git add 步驟
# step 1ngit statusn # On branch mastern #n # Changes not staged for commit:n #n # modified: benchmarks.rbn #n# step 2ngit commit -a -m upn #[master 83e38c7] added new benchmarksn #1 files changed, 5 insertions(+), 0 deletions(-)n
f. 移除文件
要從 Git 中移除某個文件,就必須要從已跟蹤文件清單中移除(確切地說,是從暫存區域移除),然後提交。可以用 git rm 命令完成此項工作,並連帶從工作目錄中刪除指定的文件,這樣以後就不會出現在未跟蹤文件清單中了。
如果只是簡單地從工作目錄中手工刪除文件,運行 git status 時就會在 「Changes not staged for commit」 部分(也就是未暫存清單)看到
# step 1nrm benchmarks.rbn# step 2ngit statusn # On branch mastern #n # Changes not staged for commit:n # (use "git add/rm <file>..." to update what will be committed)n #n # deleted: benchmarks.rbn
然後再運行 git rm 記錄此次移除文件的操作:
# step 1ngit rm benchmarks.rbn# step 2ngit statusn # On branch mastern #n # Changes to be committed:n # (use "git reset HEAD <file>..." to unstage)n #n # deleted: benchmarks.rbn
這樣最後提交的時候,該文件就不再納入版本管理了。如果刪除之前修改過並且已經放到暫存區域的話,則必須要用強制刪除選項 -f(force 的首字母),以防誤刪除文件後丟失修改的內容。
另外一種情況是,我們想把文件從 Git 倉庫中刪除(亦即從暫存區域移除),但仍然希望保留在當前工作目錄中。換句話說,僅是從跟蹤清單中刪除。比如一些大型日誌文件或者一堆 .a 編譯文件,不小心納入倉庫後,要移除跟蹤但不刪除文件,以便稍後在 .gitignore 文件中補上,用 --cached 選項即可
git rm --cached readme.txtn
在提交了若干更新之後,又或者克隆了某個項目,想回顧下提交歷史,可以使用 git log 命令查看。
git log n
默認不用任何參數的話,git log 會按提交時間列出所有的更新,最近的更新排在最上面。
git log 命令支持的選項具體如下所示
#選項 說明n -p 按補丁格式顯示每個更新之間的差異。n --stat 顯示每次更新的文件修改統計信息。n --shortstat 只顯示 --stat 中最後的行數修改添加移除統計。n --name-only 僅在提交信息後顯示已修改的文件清單。n --name-status 顯示新增、修改、刪除的文件清單。n --abbrev-commit 僅顯示 SHA-1 的前幾個字元,而非所有的 40 個字元。n --relative-date 使用較短的相對時間顯示(比如,「2 weeks ago」)。n --graph 顯示 ASCII 圖形表示的分支合併歷史。n --pretty 使用其他格式顯示歷史提交信息。可用的選項包括 oneline,short,full,fuller 和 format(後跟指定格式)。n -(n) 僅顯示最近的 n 條提交n --since, --after 僅顯示指定時間之後的提交。n --until, --before 僅顯示指定時間之前的提交。n --author 僅顯示指定作者相關的提交。n --committer 僅顯示指定提交者相關的提交。n
有時候我們提交完了才發現漏掉了幾個文件沒有加,或者提交信息寫錯了。想要撤消剛才的提交操作,可以使用 --amend 選項重新提交:
git commit --amendn
如果剛才提交時忘了暫存某些修改,可以先補上暫存操作,然後再運行 --amend 提交:
# step 1ngit commit -m initial commitn# step 2ngit add forgotten_filen# step 3ngit commit --amendn
上面的三條命令最終只是產生一個提交,第二個提交命令修正了第一個的提交內容。
這篇博客先寫到這裡,後期我會再寫上一篇作為後續補上,後續中詳細介紹Git分支以及遠程倉庫的操作。希望可以幫助到大家,也希望大家可以支持一下我,大家的支持將是我寫作最大的動力(*^__^*) !
推薦閱讀:
※【開源必備】常用git命令
※如何在Linux上搭建一個Git中央倉庫
※如何用簡單的語言解釋 Git 的基礎命令?
※起底Git系列——精通Git的捷徑