Git由淺入深之操作與指令

熊建剛發表於2017-04-20

本篇正式開始介紹Git的基礎操作與原理,看完本篇,你應該知道如何使用Git進行一次基礎的版本控制,包括:Git倉庫的生成和獲取,新增和忽略版本控制物件;暫存,檢視,快取,變更;檢視和查詢提交歷史;格式化歷史記錄輸出;刪除和移動Git倉庫內物件。

獲取Git倉庫(Git repository)

使用Git的第一步是要獲取一個Git倉庫,我們使用Git的操作物件都是存放在Git倉庫裡,獲取Git倉庫的方式有兩種:

  • 匯入一個專案或目錄到Git,初始化(init)生成Git倉庫;
  • 從遠端伺服器克隆(clone)一個Git倉庫。

初始化倉庫(init)

若我們需要使用一個專案或目錄生成一個Git倉庫,只需要通過命令列進入該目錄,執行如下Git指令:

git init複製程式碼

此操作將在此目錄生成一個.git子目錄,該目錄包含整個倉庫結構,即倉庫的所有檔案;同時會檢出(checkout)一個預設工作分支,通常名為master。

此時,我們只是初始化生成了一個Git倉庫,還未新增需要進行版本控制的物件--檔案或目錄。

克隆倉庫(clone)

很多時候,我們需要從遠端伺服器獲取一個已存在的Git倉庫,我們只需要使用如下指令:

git clone https://github.com/codingplayboy/javascript_notes複製程式碼

git clone後面跟著的url就是已存在的Git倉庫地址,我們需要知道的是Git克隆是對伺服器上倉庫的一次近乎完整的資料拷貝,當前倉庫專案的所有檔案及其各版本歷史都會被獲取。

執行如上指令後,會在當前目錄建立一個javascript_notes目錄並在javascript_notes中初始化一個.git子目錄,拉取倉庫的所有資料,然後根據倉庫(或專案)的最新版本檢出(check out)一個工作分支,通常該分支預設名稱為master。

倉庫別名

我們克隆一個Git倉庫時,其預設名還是倉庫名,但是也支援我們自定義本地別名:

git clone https://github.com/codingplayboy/javascript_notes js_notes複製程式碼

執行如上指令,會克隆一個倉庫,並匯入到當前目錄下的js_notes目錄。

倉庫Url

Git倉庫Url支援的協議有很多,最常見的是https:// 和git@;還有使用SSH傳輸協議的,
形如git://或者user@server:path/xxx/repository.git。

版本控制物件

在上節,我們已經知道如何獲取一個Git倉庫,但是到目前為止也僅僅是存在一個倉庫,我們進行版本控制的物件(檔案或目錄)並沒有新增進倉庫。

工作目錄及其內物件的狀態

相關指令:git status

當我們獲取一個倉庫,如克隆一個遠端倉庫後,在倉庫目錄執行上面指令,如圖所示:

Git由淺入深之操作與指令
git-status

  • 圖中第一行on branch master告訴我們當前我們處在名為master的分支;
  • 第二行up-to-date with 'origin/master',說明目前分支與遠端倉庫的master分支保持同步最新版本;
  • 第三行working directory clean,說明目前倉庫中沒有新加或修改過的物件(檔案或目錄)。
關於控制物件的狀態(git status)

在當前工作目錄的檔案或目錄,可能出於兩種狀態:

  • 已標記(tracked)

    所謂已標記檔案或目錄,即那些在Git最新快照裡存在的物件,可能是未修改(unmodified),已修改(modified)或暫存(staged)的檔案或目錄。

  • 未標記(untracked)

    未標記物件,即除去已標記物件外,所有物件,比如在工作目錄下但是未包含在最新快照裡並且不在暫存區(staging area)。

可以想象一下,當第一次克隆一個遠端倉庫後,倉庫下的所有檔案都應該是處於已標記(tracked)但是未修改的狀態。

Git由淺入深之操作與指令
物件狀態

新增物件(git add)

新增版本控制物件的指令是git add,比如,我們可以使用如下指令新增一個README.txt檔案,當然我們首先需要在倉庫目錄下,建立該檔案(任意方式建立),檢視狀態:

Git由淺入深之操作與指令
untracked file

如上圖,顯示README.txt檔案為Untracked file,並且提示:use git add to track;

然後使用git add指令後:

git add README.txt複製程式碼

再次執行git status指令:

Git由淺入深之操作與指令
after git add

如上,出現:Changes to be committed,說明該檔案已被標記(tracked)且被暫存,我們可以進行提交了。

當然該指令還可以對目錄使用:

git add test/複製程式碼

如上指令,將新增該test目錄及其內所有檔案或子目錄。

最後,也許我們希望一次新增所有變更,而不是一個一個新增,如下指令,可以實現:

git add .複製程式碼

我們還需要關注git add指令,不只是能新增版本控制物件,還有以下功能:

  • 暫存變更(staged)
  • 標記衝突已解決(resolved)

git add指令,更貼切的作用應該是新增新內容(檔案/目錄/變更)到下一次提交,即將新內容加入最新快照,等待提交;
關於後兩點功能,後文有詳細介紹。

忽略物件(.gitignore)

有時候,我們並不希望對倉庫內所有物件(檔案或目錄)進行管理,
某些開發依賴或本地除錯使用檔案和目錄,我們不需要在團隊間共享,這些檔案應該被Git忽略,我們只需要建立一個.gitignore檔案,在此檔案中列出希望被忽略的檔案或目錄,如:

Git由淺入深之操作與指令
.gitignore

.gitignore檔案語法主要如下:

  • 以行為單位,一行匹配一個條件
  • 空行或以#開頭的行會被忽略
  • 表示式支援glob格式
  • 排除某表示式匹配項在該表示式前加!;

暫存變更(git add)

在新增README.txt檔案後,我們可以對其進行修改,如在該檔案加入一行文字http://blog.codingplayboy.com,然後執行git status指令,結果如下:

Git由淺入深之操作與指令
git add to staging area

發現,除了之前新新增檔案的記錄,又多了一條記錄:Changes not staged for commit,表明已標記物件發生變更但是未被暫存,其下面第一第二行是輔助資訊,第三行告訴我們,README.txt已經修改(modified);

隨後我們可以使用git add指令暫存此次變更,此時,再執行git status指令,可以看到:

Git由淺入深之操作與指令
git add .

所有變更都已暫存,等待下一次提交。

檢視簡短狀態資訊(git status -s)

上文已經指出,使用git status可以檢視當前工作目錄完整的狀態,同時,git還支援使用引數指明檢視簡短狀態資訊:

git status -s
or 
git status --short複製程式碼

如下,其資訊比git status簡潔明瞭:

Git由淺入深之操作與指令
git status -s

檢視變更資訊(git diff)

前文的git status可以檢視當前工作目錄的狀態資訊,包括當前分支,變更檔案等,但都屬於檔案層次的資訊;
有時我們希望知道:

  • 哪些變更未被暫存;
  • 哪些變更已暫存,等待提交

git status顯然是不能告訴我們,因為這是屬於git diff的使命,如,在README.txt檔案中內容新增‘/’:

Git由淺入深之操作與指令
git diff

,如上圖,git diff以行為單位,告訴我們所有變更檔案哪些行發生變更(增加或刪除)。

git diff比較當前工作目錄和暫存區的內容,然後展示哪些檔案內容發生變更並且尚未暫存;
同時,其支援額外引數--staged(或--cached),該引數指定時,將輸出上次提交內容與暫存區內容比較後的的變更,
簡言之,git diff輸出未暫存的變更,git diff --staged輸出已暫存待提交的變更。

在使用git add .暫存變更後,使用git diff指令是沒用的,而使用git diff --staged指令,輸出如下:

Git由淺入深之操作與指令
git diff --staged

提交變更(git commit)

所有的變更,最終都需要提交,才能在本地持久化報存,在將所有變更暫存(git add)後,我們就可以進行提交了,相關指令就是:

git commit複製程式碼

在輸入如上指令後,將進入Git commit資訊編輯狀態:

Git由淺入深之操作與指令
git commit

我們可以編輯本次提交的備註資訊,其中的預設備註資訊都以#開頭,表明提交時會被忽略。

git commit指令告訴Git持久化記錄(提交)我們暫存區(staging area)中的快照,任何未被暫存的變更,不會被新增進暫存區的快照,仍然保留在當前工作目錄,我們可以隨後提交。

更詳細的提交資訊(git commit -v)

除了使用預設的git commit指令,我們還可以新增-v引數,在提交資訊中顯示變更內容,如下:

Git由淺入深之操作與指令
git-commit-v

如上圖,和之前的比較,除了基本的提示,還有檔案變更內容提示,可以避免某些誤提交。

行內輸入提交資訊(git commit -m)

當然,Git還支援我們使用-m引數,指明我們在使用git commit指令時直接填寫提交備註資訊:

Git由淺入深之操作與指令
git-commit-m

跳過暫存區(git commit -a)

我們知道對於發生變更的物件,我們需要先使用git add,暫存變更,再使用git commit提交變更;
但是還有一種可以不使用git add指令的方式,可以提交變更,就是給git commit指令新增-a引數:

git commit -a複製程式碼

但是需要注意的是,該引數,只能直接提交工作目錄中已標記的物件(檔案或目錄)的變更,對於未標記,如新新增的物件,是無效的。

檢視提交歷史(Git log)

本節要介紹的是如何檢視之前的提交歷史及資訊,你應該知道的git log指令,
預設地,不帶引數時,執行git log指令,輸出的是當前倉庫按逆序排序(最近提交在最前)的提交記錄:

Git由淺入深之操作與指令
git log

如上圖,每個提交記錄包含其SHA-1校驗和,提交者使用者名稱,提交日期,提交備註資訊。

git log指令支援指定許多引數,以過濾輸出不同提交記錄,下文展開介紹。

指定檢視提交記錄數(git log -2)

Git支援我們指定數量引數,限定該次檢視提交記錄數量,如git log -2,之後輸出最近的兩條提交記錄。

格式化輸出提交記錄(git log --pretty)

Git支援我們格式化輸出的提交記錄資訊,使用--pretty引數,其值主要有以下幾個:

  • oneline: 指定一行輸出一條提交記錄;
  • short: 指定按原格式輸出簡要資訊;
  • full: 指定按原格式輸出資訊;
  • fuller: 指定按原格式輸出更多資訊;
  • format:"": 允許指定自定義輸出格式,如:
git log --pretty=format:"%h - %an, %ar : %s"複製程式碼

Git由淺入深之操作與指令
git log --pretty=format

檢視簡要提交變更(git log --stat)

git log --stat輸出如下:

Git由淺入深之操作與指令
git log --stat

除了輸出基礎提交資訊,後面還輸出了本次提交的簡要變更資訊:變更了多少檔案,及每個檔案變更的行數,並在最後輸出總結資料。

檢視詳細提交變更(git log -p)

相對於git log --stat輸出簡要提交變更資訊,我們可以指定-p引數:git log -p,輸出提交變更的詳細內容,如:

Git由淺入深之操作與指令
git-log-p

更多引數說明
引數 說明
-p 顯示詳細提交變更
--stat 顯示簡要提交變更
--shortstat 在--stat引數輸出基礎上只輸出修改,新增或刪除的行
--name-only 顯示提交時發生變更的檔名
--name-status 顯示提交時發生變更的檔名,並顯示變更型別(刪除,新增,或修改)
--abbrev-commit 只顯示提交記錄SHA-1校驗和的前幾個字元
--relative-date 顯示簡要提交日期(如, “2 weeks ago”)
--graph 顯示分支提交歷史的ASCII圖
--pretty 支援預定義輸出格式,詳細說明見上文
過濾提交歷史輸出

Git支援我們在輸出歷史記錄時,新增多種過濾條件,最簡單的比如-<num>引數條件,指定輸出最近的若干條提交記錄,還有諸如提交時間,提交作者等條件。

  • --since/--after: git log --since=2.weeks

    輸出兩週內的提交記錄,引數值還可以形如"2016-01-15","2 years 1 day 10 minutes ago"

  • --until/--before: git log --until=2016-01-15

  • --grep: git log --grep=README

    可以指定關鍵字,只有提交資訊中存在關鍵字才會輸出

  • --author: 過濾輸出指定提交作者的記錄

  • --commiter: 輸出滿足指定commiter的提交記錄
  • -S: git -log -Swebpack

    輸出提交內容(程式碼或檔案或目錄)包含指定字串的記錄

刪除物件(git rm)

很多時候,我們也會需要從Git倉庫中刪除某些物件,rm就是刪除檔案或目錄的指令,但是需要特別強調的是,該指令只是將某物件從當前工作目錄刪除,如:

Git由淺入深之操作與指令
rm file

使用rm後,當前狀態是"Changes not staged for commit:"此次變更未被暫存和提交。

若你需要將某物件從已標記檔案或暫存區刪除,則需要使用git rm指令,如下:

Git由淺入深之操作與指令
git rm

使用git rm後,變更會被暫存。

強制刪除(git rm -f)

當我們發生某次變更,且將其新增到暫存區(index索引),我們只使用git rm指令是不行的:

Git由淺入深之操作與指令
git rm -f

我們必須加上-f引數,指明強制刪除。

快取變更(git rm --cached)

有時候,我們在某次變更新增了某檔案(甚至可能已經新增到暫存區),但是暫時不需要提交,又不想直接刪除它,即只在工作目錄存在,而不將其放入暫存區,只需要新增--cached引數,如修改README.txt同時,新增了test.txt檔案,並且暫存了變更,之後提交時我們不希望此次提交test.txt,但又不希望刪除它,則在提交前使用git rm --cached test.txt

Git由淺入深之操作與指令
git rm --cached

移動物件(git mv)

通常,我們也許需要移動或重新命名某檔案或目錄,Git有mv指令,還要一個更方便的指令git mv,如:

git mv a.txt b.txt複製程式碼

另一方面,重新命名或移動某檔案或目錄,這兩個操作對於Git來說是沒有太大區別的,比如上面的重新命名檔案操作等效於:

mv a.txt b.txt
git rm a.txt
git add b.txt複製程式碼

我們可以看到git mv指令是在mv指令操作的基礎上暫存此次操作的變更,而mv只是一個重新命名或移動指令,不涉及版本控制流程。

到此,Git的基本使用,已經介紹完成,不過,本篇講解的基礎操作,都是在計算機本地進行的版本控制,並沒有同步到伺服器,那麼下一篇的主題就出來了:Git如何與遠端伺服器進行協同工作。

更多相關閱讀請移步

相關文章