Git探祕:深入探索(2/2)

徐輝jser發表於2017-01-18

概念

通過上篇文章,初步接觸Git的朋友應該已經對Git有所瞭解了,既然你已經開啟了這篇文章,說明你對深入瞭解Git有強烈的慾望,那就跟著我一起先來深入瞭解一下Git相關的概念。

工作區(Working Directory)

就是你在電腦裡能看到的目錄,比如上文中的 reading-ist 資料夾就是一個工作區

本地版本庫(Local Repository)

工作區有一個隱藏目錄.git,這個不算工作區,而是Git的版本庫。

暫存區(stage)

本地版本庫裡存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區,還有Git為我們自動建立的第一個分支 master,以及指向master的一個指標叫 HEAD

遠端版本庫(Remote Repository)

一般指的是git伺服器上所對應的倉庫,本文的示例所在的github倉庫就是一個遠端版本庫

工作區暫存區本地版本庫遠端版本庫之間幾個常用的git操作流程如下圖所示:

分支 (Branch)

分支是為了將修改記錄的整個流程分開儲存,讓分開的分支不受其它分支的影響,所以在同一個資料庫裡可以同時進行多個不同的修改

主分支(Master)

前面提到過 master 是git為我們自動建立的第一個分支,也叫主分支,其它分支開發完成後都要合併到master

標籤(Tag)

標籤是用於標記特定的點或提交的歷史,通常會用來標記釋出版本的名稱或版本號(如:publish/0.0.1),雖然標籤看起來有點像分支,但打上標籤的提交是固定的,不能隨意的改動,參見上圖中的1.0 / 2.0 / 3.0

HEAD

HEAD指向的就是當前分支的最新提交

以上概念瞭解的差不多,那就可以繼續往下看,下面將以具體的操作型別來講解Git的高階用法

操作檔案

git add

新增檔案到暫存區

git add -i

通過此命令將開啟互動式子命令系統,你將看到如下子命令

*** Commands ***
  1: status      2: update      3: revert      4: add untracked
  5: patch      6: diff      7: quit      8: help

通過輸入序列號或首字母可以選擇相應的功能,具體的功能解釋如下:

1、status:功能上和 git add -i 相似,沒什麼鳥用
2、update:功能跟 git add -u 一樣,但它會先列出工作區 修改刪除 的檔案列表,新增 的檔案不會被顯示,在命令列 Update>> 後輸入相應的列表序列號表示選中該項,回車繼續選擇,如果已選好,直接回車回到命令主介面
3、revert:把已經新增到暫存區的檔案從暫存區剔除,其操作方式和 update 類似
4、add untracked:可以把新增的檔案新增到暫存區,其操作方式和 update 類似
5、patch:詳見下方
6、diff:比較暫存區檔案和本地版本庫的差異,其操作方式和 update 類似
7、quit:退出git add -i命令系統
8、help:檢視幫助資訊

patch

這是互動命令中最有用的模式,其操作方式和 update 類似,選擇後Git會顯示這些檔案的當前內容與本地版本庫中的差異,然後您可以自己決定是否新增這些修改到暫存區,在命令列 Stage deletion [y,n,q,a,d,/,?]? 後輸入 y,n,q,a,d,/,? 其中一項選擇操作方式,具體功能解釋如下:

  • y:接受修改
  • n:忽略修改
  • q:退出當前命令
  • a:新增修改
  • d:放棄修改
  • /:通過正規表示式匹配修改內容
  • ?:檢視幫助資訊

git add -p

直接進入互動命令中最有用的patch模式

git add -u

直接進入互動命令中的update模式

git add –ignore-removal .

新增工作區 修改新增 的檔案列表, 刪除 的檔案不會被新增

git commit

把暫存區的檔案提交到本地版本庫

git commit -m `第一行提交原因` -m `第二行提交原因`

不開啟編輯器,直接在命令列中輸入多行提交原因

git commit -am `提交原因`

將工作區 修改刪除 的檔案提交到本地版本庫, 新增 的檔案不會被提交

git commit –amend -m `提交原因`

修改最新一條提交記錄的提交原因

git commit -C HEAD

將當前檔案改動提交到 HEAD 或當前分支的歷史ID

git mv

移動或重新命名檔案、目錄

git mv a.md b.md -f

a.md 重新命名為 b.md ,同時新增變動到暫存區,加-f引數可以強制重新命名,相比用 mv a.md b.md 命令省去了 git add 操作

git rm

從工作區和暫存區移除檔案

git rm b.md

從工作區和暫存區移除檔案 b.md ,同時新增變動到暫存區,相比用 rm b.md 命令省去了 git add 操作

git rm src/ -r

允許從工作區和暫存區移除目錄

git status

git status -s

以簡短方式檢視工作區和暫存區檔案狀態,示例如下:

 M demo.html
?? test.html

git status –ignored

檢視工作區和暫存區檔案狀態,包括被忽略的檔案

操作分支

git branch

檢視、建立、刪除分支

git branch -a

檢視本地版本庫和遠端版本庫上的分支列表

git branch -r

檢視遠端版本庫上的分支列表,加上 -d 引數可以刪除遠端版本庫上的分支

git branch -D

分支未提交到本地版本庫前強制刪除分支

git branch -vv

檢視帶有最後提交id、最近提交原因等資訊的本地版本庫分支列表

git merge

將其它分支合併到當前分支

     A---B---C topic
    /               

D—E—F—G—H master

git merge –squash

將待合併分支上的commit合併成一個新的commit放入當前分支,適用於待合併分支的提交記錄不需要保留的情況

git merge –no-ff

預設情況下,Git執行”快進式合併”(fast-farward merge),會直接將Master分支指向Develop分支,使用–no-ff引數後,會執行正常合併,在Master分支上生成一個新節點,保證版本演進更清晰。

git merge –no-edit

在沒有衝突的情況下合併,不想手動編輯提交原因,而是用git自動生成的類似 Merge branch `test` 的文字直接提交

git checkout

切換分支

git checkout -b daily/0.0.1

建立 daily/0.0.1 分支,同時切換到這個新建立的分支

git checkout HEAD demo.html

從本地版本庫的 HEAD(也可以是提交ID、分支名、Tag名) 歷史中檢出 demo.html 覆蓋當前工作區的檔案,如果省略 HEAD則是從暫存區檢出

git checkout –orphan new_branch

這個命令會建立一個全新的,完全沒有歷史記錄的新分支,但當前源分支上所有的最新檔案都還在,真是強迫症患者的福音,但這個新分支必須做一次 git commit 操作後才會真正成為一個新分支。

git checkout -p other_branch

這個命令主要用來比較兩個分支間的差異內容,並提供互動式的介面來選擇進一步的操作,這個命令不僅可以比較兩個分支間的差異,還可以比較單個檔案的差異。

git stash

在Git的棧中儲存當前修改或刪除的工作進度,當你在一個分支裡做某項功能開發時,接到通知把昨天已經測試完沒問題的程式碼釋出到線上,但這時你已經在這個分支里加入了其它未提交的程式碼,這個時候就可以把這些未提交的程式碼存到棧裡。

git stash

將未提交的檔案儲存到Git棧中

git stash list

檢視棧中儲存的列表

git stash show stash@{0}

顯示棧中其中一條記錄

git stash drop stash@{0}

移除棧中其中一條記錄

git stash pop

從Git棧中檢出最新儲存的一條記錄,並將它從棧中移除

git stash apply stash@{0}

從Git棧中檢出其中一條記錄,但不從棧中移除

git stash branch new_banch

把當前棧中最近一次記錄檢出並建立一個新分支

git stash clear

清空棧裡的所有記錄

git stash create

為當前修改或刪除的檔案建立一個自定義的棧並返回一個ID,此時並未真正儲存到棧裡

git stash store xxxxxx

create 方法裡返回的ID放到 store 後面,此時在棧裡真正建立了一個記錄,但當前修改或刪除的檔案並未從工作區移除

$ git stash create
09eb9a97ad632d0825be1ece361936d1d0bdb5c7
$ git stash store 09eb9a97ad632d0825be1ece361936d1d0bdb5c7
$ git stash list
stash@{0}: Created via "git stash store".

操作歷史

git log

顯示提交歷史記錄

git log -p

顯示帶提交差異對比的歷史記錄

git log demo.html

顯示 demo.html 檔案的歷史記錄

git log –since=”2 weeks ago”

顯示2周前開始到現在的歷史記錄,其它時間可以類推

git log –before=”2 weeks ago”

顯示截止到2周前的歷史記錄,其它時間可以類推

git log -10

顯示最近10條歷史記錄

git log f5f630a..HEAD

顯示從提交ID f5f630aHEAD 之間的記錄,HEAD可以為空或其它提交ID

git log –pretty=oneline

在一行中輸出簡短的歷史記錄

git log –pretty=format:”%h”

格式化輸出歷史記錄

git用各種placeholder來決定各種顯示內容,我挑幾個常用的顯示如下:

  • %H: commit hash
  • %h: 縮短的commit hash
  • %T: tree hash
  • %t: 縮短的 tree hash
  • %P: parent hashes
  • %p: 縮短的 parent hashes
  • %an: 作者名字
  • %aN: mailmap的作者名
  • %ae: 作者郵箱
  • %ad: 日期 (–date= 制定的格式)
  • %ar: 日期, 相對格式(1 day ago)
  • %cn: 提交者名字
  • %ce: 提交者 email
  • %cd: 提交日期 (–date= 制定的格式)
  • %cr: 提交日期, 相對格式(1 day ago)
  • %d: ref名稱
  • %s: commit資訊標題
  • %b: commit資訊內容
  • %n: 換行

git cherry-pick

合併分支的一條或幾條提交記錄到當前分支末梢

git cherry-pick 170a305

合併提交ID 170a305 到當前分支末梢

git reset

將當前的分支重設(reset)到指定的或者HEAD

git reset –mixed

–mixed 是不帶引數時的預設引數,它退回到某個版本,保留檔案內容,回退提交歷史

git reset –soft

暫存區和工作區中的內容不作任何改變,僅僅把HEAD指向

git reset –hard

自從以來在工作區中的任何改變都被丟棄,並把HEAD指向

git rebase

重新定義分支的版本庫狀態

git rebase branch_name

合併分支,這跟 merge 很像,但還是有本質區別,看下圖:

合併過程中可能需要先解決衝突,然後執行 git rebase –continue

git rebase -i HEAD~~

開啟文字編輯器,將看到從HEAD到HEAD~~的提交如下

pick 9a54fd4 新增commit的說明
pick 0d4a808 新增pull的說明

# Rebase 326fc9f..0d4a808 onto d286baa
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit`s log message
#  x, exec = run command (the rest of the line) using shell
#

將第一行的“pick”改成Commands中所列出來的命令,然後儲存並退出,所對應的修改將會生效。
如果移動提交記錄的順序,將改變歷史記錄中的排序。

git revert

撤銷某次操作,此次操作之前和之後的commit和history都會保留,並且把這次撤銷作為一次最新的提交

git revert HEAD

撤銷前一次提交操作

git revert HEAD –no-edit

撤銷前一次提交操作,並以預設的 Revert “xxx” 為提交原因

git revert -n HEAD

需要撤銷多次操作的時候加 -n 引數,這樣不會每次撤銷操作都提交,而是等所有撤銷都完成後一起提交

git diff

檢視工作區、暫存區、本地版本庫之間的檔案差異,用一張圖來解釋

git diff –stat

通過 –stat 引數可以檢視變更統計資料

 test.md | 1 -
 1 file changed, 1 deletion(-)

git reflog

reflog 可以檢視所有分支的所有操作記錄(包括commit和reset的操作、已經被刪除的commit記錄,跟 git log 的區別在於它不能檢視已經刪除了的commit記錄

遠端版本庫連線

如果在GitHub專案初始化之前,檔案已經存在於本地目錄中,那可以在本地初始化本地版本庫,再將本地版本庫跟遠端版本庫連線起來

git init

在本地目錄內部會生成.git資料夾

git remote

git remote -v

不帶引數,列出已經存在的遠端分支,加上 -v 列出詳細資訊,在每一個名字後面列出其遠端url

git remote add origin https://github.com/gafish/reading-list.git

新增一個新的遠端倉庫,指定一個名字,以便引用後面帶的URL

git fetch

將遠端版本庫的更新取回到本地版本庫

git fetch origin daily/0.0.1

預設情況下,git fetch取回所有分支的更新。如果只想取回特定分支的更新,可以指定分支名。

問題排查

git blame

檢視檔案每行程式碼塊的歷史資訊

git blame -L 1,10 demo.html

擷取 demo.html 檔案1-10行歷史資訊

git bisect

二分查詢歷史記錄,排查BUG

git bisect start

開始二分查詢

git bisect bad

標記當前二分提交ID為有問題的點

git bisect good

標記當前二分提交ID為沒問題的點

git bisect reset

查到有問題的提交ID後回到原分支

更多操作

git submodule

通過 Git 子模組可以跟蹤外部版本庫,它允許在某一版本庫中再儲存另一版本庫,並且能夠保持2個版本庫完全獨立

git submodule add https://github.com/gafish/movie-list.git movie

movie-list 倉庫新增為子模組

git submodule update movie

更新子模組 movie

git gc

執行Git的垃圾回收功能,清理冗餘的歷史快照

git archive

將加了tag的某個版本打包提取

git archive -v –format=zip v0.1 > v0.1.zip

–format表示打包的格式,如zip,-v表示對應的tag名,後面跟的是tag名,如v0.1。

總結

本文只是對Git的所有功能中的部分實用功能做了一次探祕,Git非常強大,還有很多功能有待我們去發現,限於本文篇幅,我們就此打住吧,預知更多好用功能,請聽下回分解。


歡迎大家關注我的微信公眾號,將不定期推送前端開發相關原創文章


相關文章