一入前端深似海,從此紅塵是路人系列第十彈之如何合理利用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,請自行去安裝一下先哦(https://git-scm.com/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"
git config --global user.email [email protected]

如果用了 --global 選項,那麼更改的配置文件就是位於你用戶主目錄下的那個,以後你所有的項目都會默認使用這裏配置的用戶信息。如果要在某個特定的項目中使用其他名字或者電郵,只要去掉 --global 選項重新配置即可,新的設定保存在當前項目的 .git/config 文件裏。

b. 差異分析工具

Git 可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff 等合併工具的輸出信息,這裏,如果說在解決合併衝突時使用的是vimdiff差異分析工具。改用命令如下

git config --global merge.tool vimdiff

c. 查看配置信息

git config --list
   #user.name=qiangdada
   #[email protected]
   #color.status=auto
   #color.branch=auto
   #color.interactive=auto
   #color.diff=auto
   #...

這裏會看到重複的變量名,那就說明它們來自不同的配置文件(比如 /etc/gitconfig 和 ~/.gitconfig),不過最終 Git 實際採用的是最後一個。也可以直接查閱某個環境變量的設定,只要把特定的名字跟在後面即可,像這樣

git config user.name
   #qiangdada

d. 獲取幫助

想了解 Git 的各式工具該怎麼用,可以閱讀它們的使用幫助,方法有三

# 方法1
git help
#方法二
git --help
#方法三
man git

比如,要學習 config 命令可以怎麼用,運行

git help config


二、Git基礎

1、取得項目的Git倉庫

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

a. 從工作目錄中初始化新倉庫

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

git init

初始化後,在當前目錄下會出現一個名爲 .git 的目錄,所有 Git 需要的數據和資源都存放在這個目錄中。如果當前目錄下有幾個文件想要納入版本控制,需要先用 git add 命令告訴 Git 開始對這些文件進行跟蹤,然後提交

# step 1
git add *.c
# step 2
git add README
# step 3
git commit -m 'initial project version'

b. 從現有倉庫克隆

如果想對某個開源項目出一份力,可以先把該項目的 Git 倉庫複製一份出來,這就需要用到 git clone 命令。如果你熟悉其他的 VCS 比如 Subversion,你可能已經注意到這裏使用的是 clone 而不是 checkout。這是個非常重要的差別,Git 收取的是項目歷史的所有數據(每一個文件的每一個版本),服務器上有的數據克隆之後本地也都有了。實際上,即便服務器的磁盤發生故障,用任何一個克隆出來的客戶端都可以重建服務器上的倉庫,回到當初克隆時的狀態

git clone [url]
# 如 git clone https://github.com/xuqiang521/data-visualization.git


2、記錄每次更新到倉庫

先上一張文件的狀態變化週期的圖示

a. 檢查當前文件狀態

git status

b. 跟蹤新文件

使用命令 git add 開始跟蹤一個新文件README。運行

# step 1
git add README
# step 2
git status
# 此時文件屬於暫存狀態
    # On branch master
    # Changes to be committed:
    # (use "git reset HEAD <file>..." to unstage)
    # new file: README

假設在這之前我就已經跟蹤過一個文件叫 benchmarks.rb,然後再次運行 status 命令,會看到這樣的狀態報告

git status
    # On branch master
    # Changes to be committed:
    # (use "git reset HEAD <file>..." to unstage)
    #
    # new file: README
    #
    # Changes not staged for commit:
    # (use "git add <file>..." to update what will be committed)
    #
    # modified: benchmarks.rb

文件 benchmarks.rb 出現在 “Changes not staged for commit” 這行下面,說明已跟蹤文件的內容發生了變化,但還沒有放到暫存區。要暫存這次更新,需要運行 git add 命令。現在讓我們運行 git add 將 benchmarks.rb 放到暫存區,然後再看看 git status 的輸出

# step 1
git add benchmarks.rb
# step 2
git status
    # On branch master
    # Changes to be committed:
    # (use "git reset HEAD <file>..." to unstage)
    #
    # new file: README
    # modified: benchmarks.rb

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

git status
    # On branch master
    # Changes to be committed:
    # (use "git reset HEAD <file>..." to unstage)
    #
    # new file: README
    # modified: benchmarks.rb
    #
    # Changes not staged for commit:
    # (use "git add <file>..." to update what will be committed)
    #
    # modified: benchmarks.rb

很明顯,benchmarks.rb 文件出現了兩次!一次算未暫存,一次算已暫存,這怎麼可能呢?好吧,實際上 Git 只不過暫存了你運行 git add命令時的版本,如果現在提交,那麼提交的是添加註釋前的版本,而非當前工作目錄中的版本。所以,運行了 git add 之後又作了修訂的文件,需要重新運行 git add 把最新版本重新暫存起來

# step 1
git add benchmarks.rb
# step 2
git status
    # On branch master
    # Changes to be committed:
    # (use "git reset HEAD <file>..." to unstage)
    #
    # new file: README
    # modified: benchmarks.rb

c. 忽略某些文件

一般我們總會有些文件無需納入 Git 的管理,也不希望它們總出現在未跟蹤文件列表。通常都是些自動生成的文件,比如日誌文件,或者編譯過程中創建的臨時文件等。我們可以創建一個名爲 .gitignore 的文件,列出要忽略的文件模式。來看一個實際的例子

cat .gitignore
    #告訴 Git 忽略所有以 .o 或 .a 結尾的文件。一般這類對象文件和存檔文件都是編譯過程中出現的,我們用不着跟蹤它們的版本
    *.[oa]
    #告訴 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 文件,不包括 subdir/TODO
    /TODO
    # 忽略 build/ 目錄下的所有文件
    build/
    # 會忽略 doc/notes.txt 但不包括 doc/server/arch.txt
    doc/*.txt

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

實際上 git status 的顯示比較簡單,僅僅是列出了修改過的文件,假如再次修改 README 文件後暫存,然後編輯 benchmarks.rb 文件後先別暫存,運行 status 命令將會看到

git status
    # On branch master
    # Changes to be committed:
    # (use "git reset HEAD <file>..." to unstage)
    #
    # new file: README
    #
    # Changes not staged for commit:
    # (use "git add <file>..." to update what will be committed)
    #
    # modified: benchmarks.rb

如果這個時候我需要查看尚未暫存的文件更新了哪些部分,那麼不妨不加參數直接輸入 git diff

git diff

此命令比較的是工作目錄中當前文件和暫存區域快照之間的差異,也就是修改之後還沒有暫存起來的變化內容。

若要看已經暫存起來的文件和上次提交時的快照之間的差異,可以用 git diff --cached 命令。

git diff --cached

e. 提交更新

現在的暫存區域已經準備妥當可以提交了。在此之前,請一定要確認還有什麼修改過的或新建的文件還沒有 git add 過,否則提交的時候不會記錄這些還沒暫存起來的變化。所以,每次準備提交前,先用 git status 看下,是不是都已暫存起來了,然後再運行提交命令 git commit

git commit

這種方式會啓動文本編輯器以便輸入本次提交的說明,編輯器會顯示類似下面的文本信息(本例選用 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 <file>..." to unstage)
    #
    # new file: README
    # modified: benchmarks.rb
    ~
    ~
    ~
    ".git/COMMIT_EDITMSG" 10L, 283C

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

git commit -m "up"

如果我們需要跳過使用暫存區域,不用擔心。Git 提供了一個跳過使用暫存區域的方式,只要在提交的時候,給 git commit 加上 -a 選項,Git 就會自動把所有已經跟蹤過的文件暫存起來一併提交,從而跳過 git add 步驟

# step 1
git status
    # On branch master
    #
    # Changes not staged for commit:
    #
    # modified: benchmarks.rb
    #
# step 2
git commit -a -m 'up'
    #[master 83e38c7] added new benchmarks
    #1 files changed, 5 insertions(+), 0 deletions(-)

f. 移除文件

要從 Git 中移除某個文件,就必須要從已跟蹤文件清單中移除(確切地說,是從暫存區域移除),然後提交。可以用 git rm 命令完成此項工作,並連帶從工作目錄中刪除指定的文件,這樣以後就不會出現在未跟蹤文件清單中了。

如果只是簡單地從工作目錄中手工刪除文件,運行 git status 時就會在 “Changes not staged for commit” 部分(也就是未暫存清單)看到

# step 1
rm benchmarks.rb
# step 2
git status
    # On branch master
    #
    # Changes not staged for commit:
    # (use "git add/rm <file>..." to update what will be committed)
    #
    # deleted: benchmarks.rb

然後再運行 git rm 記錄此次移除文件的操作:

# step 1
git rm benchmarks.rb
# step 2
git status
    # On branch master
    #
    # Changes to be committed:
    # (use "git reset HEAD <file>..." to unstage)
    #
    # deleted: benchmarks.rb

這樣最後提交的時候,該文件就不再納入版本管理了。如果刪除之前修改過並且已經放到暫存區域的話,則必須要用強制刪除選項 -f(force 的首字母),以防誤刪除文件後丟失修改的內容。

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

git rm --cached readme.txt


3、查看提交歷史

在提交了若干更新之後,又或者克隆了某個項目,想回顧下提交歷史,可以使用 git log 命令查看。

git log 

默認不用任何參數的話,git log 會按提交時間列出所有的更新,最近的更新排在最上面。

git log 命令支持的選項具體如下所示

#選項 說明
    -p 按補丁格式顯示每個更新之間的差異。
    --stat 顯示每次更新的文件修改統計信息。
    --shortstat 只顯示 --stat 中最後的行數修改添加移除統計。
    --name-only 僅在提交信息後顯示已修改的文件清單。
    --name-status 顯示新增、修改、刪除的文件清單。
    --abbrev-commit 僅顯示 SHA-1 的前幾個字符,而非所有的 40 個字符。
    --relative-date 使用較短的相對時間顯示(比如,“2 weeks ago”)。
    --graph 顯示 ASCII 圖形表示的分支合併歷史。
    --pretty 使用其他格式顯示歷史提交信息。可用的選項包括 oneline,short,full,fuller 和 format(後跟指定格式)。
    -(n) 僅顯示最近的 n 條提交
    --since, --after 僅顯示指定時間之後的提交。
    --until, --before 僅顯示指定時間之前的提交。
    --author 僅顯示指定作者相關的提交。
    --committer 僅顯示指定提交者相關的提交。


4、撤銷操作

有時候我們提交完了才發現漏掉了幾個文件沒有加,或者提交信息寫錯了。想要撤消剛纔的提交操作,可以使用 --amend 選項重新提交:

git commit --amend

如果剛纔提交時忘了暫存某些修改,可以先補上暫存操作,然後再運行 --amend 提交:

# step 1
git commit -m 'initial commit'
# step 2
git add forgotten_file
# step 3
git commit --amend

上面的三條命令最終只是產生一個提交,第二個提交命令修正了第一個的提交內容。

 

這篇博客先寫到這裏,後期我會再寫上一篇作爲後續補上,後續中詳細介紹Git分支以及遠程倉庫的操作。希望可以幫助到大家,也希望大家可以支持一下我,大家的支持將是我寫作最大的動力(*^__^*) !

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