高級(jí)技巧
下面介紹幾個(gè) Git 中非常強(qiáng)大的命令逸嘀,借助這些命令我們可以完成一些非常有用的操作。
git reflog
Git 對(duì)于本地倉庫的操作都進(jìn)行了日志記錄些膨,日志文件存儲(chǔ)在目錄.git/logs
:
$ tree .git/logs
.git/logs
├── HEAD # HEAD 指針變更記錄
└── refs
├── heads # 本地分支記錄
│ ├── master
├── remotes # 遠(yuǎn)程分支記錄
│ └── origin
│ └── HEAD
│ └── master
└── stash # stash 記錄
從 Git 的日志目錄中可以看出浦马,Git 主要對(duì)一些引用文件進(jìn)行了日志記錄,包括HEAD
指針殊校、分支和儲(chǔ)藏更改記錄晴玖。當(dāng)本地更改了這些指針指向時(shí),該操作就會(huì)被記錄到對(duì)應(yīng)的日志文件中为流。比如呕屎,切換分支會(huì)導(dǎo)致HEAD
指針指向變化,則該操作會(huì)被記錄到日志文件.git/logs/HEAD
中敬察,比如秀睛,master
分支添加或刪除commit
時(shí)會(huì)同時(shí)導(dǎo)致master
指針和HEAD
指針指向變化,則該操作會(huì)同時(shí)被記錄到.git/logs/refs/heads/master
和.git/logs/HEAD
日志文件中...
由于日志記錄了所有指針莲祸、分支和儲(chǔ)藏的變更操作蹂安,因此椭迎,本地倉庫的所有提交都不會(huì)丟失,可隨時(shí)從這些日志文件中索取得回田盈。比如畜号,假設(shè)我們?cè)诋?dāng)前分支指向了回退操作,那么當(dāng)前分支的日志記錄git log
會(huì)丟失被回退的那些提交允瞧,如果想找回這些提交简软,搜索該分支日志文件即可。當(dāng)然瓷式,實(shí)際操作中替饿,我們無需手動(dòng)查詢這些日志文件(雖然這些日志文件都是文本文件),因?yàn)?Git 提供了相關(guān)命令可以讓我們直接查詢相應(yīng)日志記錄贸典,該命令為git reflog
视卢,其具體語法如下所示:
# 查詢?nèi)罩?git reflog [show] [<ref>]
# 刪除過期日志
git reflog expire [<ref>]
# 刪除日志條目
git reflog delete ref@{specifier}
# 檢測(cè)引用是否存在日志文件
git reflog exists <ref>
大多數(shù)情況下我們都是使用git reflog [show]
來查看日志記錄,因此下面我們具體介紹常用的一些查詢操作:
-
查詢
HEAD
指針變更操作:查詢HEAD
指針變更日志廊驼,如下所示:# 日志呈棧結(jié)構(gòu)据过,即最近的操作顯示在前(棧頂),初始操作顯示在最后面(棧底) $ git reflog show HEAD 5279645 (HEAD -> master, dev) HEAD@{0}: checkout: moving from dev to master 5279645 (HEAD -> master, dev) HEAD@{1}: reset: moving to HEAD db8348b HEAD@{4}: commit: feat: 222 5279645 (HEAD -> master, dev) HEAD@{5}: commit (initial): feat: 111
注:由于查詢
HEAD
指針變更日志是最常使用的操作妒挎,因此绳锅,默認(rèn)情況下,執(zhí)行不帶參數(shù)的git reflog
就相當(dāng)于執(zhí)行git reflog show HEAD
酝掩。 -
查詢分支提交變更:分支增加提交或回退提交等操作都會(huì)被記錄到日志中鳞芙,因此我們查詢分支的變更記錄:
# 查詢 master 指針變更記錄 $ git reflog show master 5279645 (HEAD -> master, dev) master@{0}: reset: moving to HEAD~1 # 回退操作 db8348b master@{1}: commit: feat: 222 # 增加提交 5279645 (HEAD -> master, dev) master@{2}: commit (initial): feat: 111 # 初始提交
-
查詢儲(chǔ)藏變更操作:當(dāng)我們執(zhí)行
git stash
命令時(shí),stash
指針會(huì)被更改期虾,因此這些操作也都會(huì)被記錄到日志中原朝。查詢儲(chǔ)藏變更命令如下:$ git reflog show stash 0ba206e (refs/stash) stash@{0}: WIP on master: 5279645 feat: 111 54044f5 stash@{1}: WIP on dev: 5279645 feat: 111
-
日志時(shí)間過濾:日志文件每條記錄都帶有一個(gè)時(shí)間戳,因此我們可以對(duì)日志記錄進(jìn)行時(shí)間過濾镶苞,只顯示某個(gè)時(shí)間范圍內(nèi)的記錄喳坠。
Git 提供了一些時(shí)間標(biāo)識(shí)符方便我們進(jìn)行時(shí)間過濾,常見的時(shí)間標(biāo)識(shí)符如下表所示:
時(shí)間標(biāo)識(shí)符 含義 1.minute.ago
1分鐘之前 1.hour.ago
1小時(shí)之前 yesterday
昨天 1.week.ago
一周之前 1.month.ago
一月之前 1.year.ago
1小時(shí)之前 2020-05-18.09:00:00
2020-05-18 09:00:00 注:時(shí)間標(biāo)識(shí)符也支持復(fù)數(shù)形式茂蚓,比如:
5.hours.ago
表示 5 小時(shí)之前壕鹉。
注:時(shí)間標(biāo)識(shí)符支持聯(lián)合使用,比如:1.day.2.hours.ago
表示 1 天 2 小時(shí)之前聋涨。舉個(gè)例子:比如查詢
master
分支 1 小時(shí) 17 分鐘之前的操作:$ git reflog master@{1.hour.17.minutes.ago} 5279645 (HEAD -> master, dev) master@{Sat Jan 9 23:18:05 2021 +0800}: commit (initial): feat: 111
最后晾浴,日志文件只存在于本地倉庫中,不會(huì)上傳到遠(yuǎn)程倉庫牍白,并且脊凰,日志條目默認(rèn)只有 90 天有效期限,過期條目可能會(huì)被自動(dòng)刪除(因?yàn)槟承┟顣?huì)觸發(fā)git gc
操作)淹朋,也可以通過手動(dòng)執(zhí)行git gc
或git reflog expire
刪除過期條目笙各。如果想更改日志條目有效期限,可以設(shè)置gc.reflogExpire
配置或執(zhí)行git reflog expire --expire=<time>
手動(dòng)指定時(shí)間础芍。比如杈抢,下面的命令將刪除 1 分鐘之前的所有日志條目:
# --all 表示清除所有日志,也可以指定清除特定日志文件仑性,比如 HEAD惶楼、master...
$ git reflog expire --expire=1.minute.ago --all
git rebase
git rebase
是 Git 提供的一個(gè)具備非常強(qiáng)大功能的命令,rebase
中文翻譯為『變基』诊杆,見名知意歼捐,即git rebase
可以更改基準(zhǔn)點(diǎn),比如晨汹,一個(gè)分支從另一個(gè)分支某個(gè)提交上創(chuàng)建豹储,使用git rebase
可以更改該分支基準(zhǔn)點(diǎn),使新分支從另一個(gè)提交上創(chuàng)建延伸淘这,擴(kuò)展來說剥扣,git rebase
不僅僅可以用于修改分支基準(zhǔn)點(diǎn),它還具備修改分支提交歷史記錄铝穷,比如刪除钠怯、合并、更換提交...
git rebase
的具體語法如下所示:
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
上述命令可以簡(jiǎn)化為如下格式:
# 將 topic_branch 變基到 base_branch 的最新提交曙聂,
# 當(dāng)未指定 topic_branch 時(shí)晦炊,則默認(rèn)變基當(dāng)前分支
git rebase [base_branch] [topic_branch]
# 交互式變基
git rebase -i [branch]
# 變基(繼續(xù) | 跳過 | 停止 | 退出 | 繼續(xù)編輯 | 顯示當(dāng)前差異包)
git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
下面我們主要對(duì)git rebase
的兩個(gè)主要功能進(jìn)行講解:
-
分支合并:可以使用
rebase
模式進(jìn)行分支合并。默認(rèn)情況下宁脊,Git 使用
merge
模式進(jìn)行分支合并断国,該方式會(huì)基于當(dāng)前合并的兩個(gè)分支最新提交以及兩者之間的公共提交做一個(gè)簡(jiǎn)單三路合并,生成一個(gè)合并提交點(diǎn)朦佩,這種情況下并思,當(dāng)我們查看歷史記錄時(shí),會(huì)看到有向無環(huán)圖存在语稠,且各分支結(jié)點(diǎn)以提交時(shí)間順序進(jìn)行排列宋彼。比如對(duì)于如下示意圖:如果我們想
master
分支合并dev
分支,執(zhí)行如下命令:$ git switch master Switched to branch 'master' $ git merge dev -m 'C5: merge branch dev' Merge made by the 'recursive' strategy. 3.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 3.txt $ git log --format='%h - %cd : %s' --date=format:'%Y-%m-%d %H:%M:%S' --graph * 1069fb9 - 2021-01-11 01:10:01 : C5: merge branch dev |\ | * 7ee751d - 2021-01-11 01:09:28 : dev: C3 * | 3c29c30 - 2021-01-11 01:09:50 : master: C4 |/ * 44b275c - 2021-01-11 01:08:47 : master: C2 * 5454078 - 2021-01-11 01:08:29 : master: C1
注:如果我們使用
--graph
進(jìn)行查看仙畦,可以看到输涕,不同分支之間的提交不一定按時(shí)間順序進(jìn)行排列(比如上述代碼中C3
早于C4
,但卻排在C4
后面)慨畸,這是因?yàn)榉种创蚱嚼晨玻燥@示效果有點(diǎn)異常,如果不使用--graph
寸士,則可以以時(shí)間順序正常排列各個(gè)提交檐什。此時(shí)的示意圖如下所示:
以上就是
merge
模式合并結(jié)果碴卧,接下來我們來看下rebase
模式合并分支效果:-
首先現(xiàn)在我們將倉庫回退到未合并之前的狀態(tài):
$ git reset --hard HEAD~1 HEAD is now at 3c29c30 master: C4
此時(shí)倉庫的示意圖如下所示:
-
然后將
dev
變基,將基準(zhǔn)點(diǎn)移動(dòng)到master
分支最新提交點(diǎn)上:$ git switch dev Switched to branch 'dev' # 變基前的分支提交記錄 $ git log --format='%h - %cd : %s' --date=format:'%Y-%m-%d %H:%M:%S' 7ee751d - 2021-01-11 01:09:28 : dev: C3 44b275c - 2021-01-11 01:08:47 : master: C2 5454078 - 2021-01-11 01:08:29 : master: C1 # 變基 $ git rebase master Successfully rebased and updated refs/heads/dev. # 變基后的分支提交記錄 $ git log --format='%h - %cd : %s' --date=format:'%Y-%m-%d %H:%M:%S' ca22a79 - 2021-01-11 01:12:55 : dev: C3 # 注意 C3 的哈希值被更改了 3c29c30 - 2021-01-11 01:09:50 : master: C4 44b275c - 2021-01-11 01:08:47 : master: C2 5454078 - 2021-01-11 01:08:29 : master: C1
注:
git rebase
時(shí)可能會(huì)存在沖突乃正,此時(shí)解決完沖突后住册,只需執(zhí)行git add .
,然后執(zhí)行git rebase --continue
繼續(xù)變基過程即可瓮具。通過查看變基前和變基后的
dev
分支提交歷史荧飞,我們可以看到,變基前名党,dev
分支是基于C2
提交點(diǎn)的叹阔,而變基后,dev
分支是基于C4
提交點(diǎn)的传睹,也就是git rebase master
會(huì)將dev
分支基準(zhǔn)點(diǎn)移動(dòng)到master
分支最新提交處耳幢。此時(shí)倉庫的示意圖如下所示:
這里簡(jiǎn)單介紹下
git rebase
的實(shí)現(xiàn)原理:以本例子進(jìn)行闡述,當(dāng)執(zhí)行git rebase master
時(shí)蒋歌,Git 首先會(huì)找到這兩個(gè)分支(即dev
和master
分支)的最近共同祖先提交C2
帅掘,然后將當(dāng)前分支(即dev
分支)超前共同祖先C2
的所有提交一一打散并提取出相應(yīng)的修改保存為臨時(shí)patch
文件,文件存儲(chǔ)在.git/rebase
目錄下堂油;然后將當(dāng)前分支指向master
分支最新提交點(diǎn)C4
上修档;最后,依序?qū)?code>patch文件應(yīng)用到dev
分支上府框,這個(gè)步驟相當(dāng)于重新播放之前dev
分支的各個(gè)提交點(diǎn)進(jìn)行的修改操作吱窝,這樣就保證了生成新的提交的內(nèi)容是一致的(假設(shè)不存在沖突)。 -
此時(shí)
master
分支就可以合并dev
分支:# 切換到 master 分支 $ git switch master Switched to branch 'master' # 合并 dev 分支 $ git merge dev -m 'C5: merge branch dev with rebase' Updating 3c29c30..ca22a79 Fast-forward (no commit created; -m option ignored) # 采用 Fast-forward 模式合并 3.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 3.txt # 查看提交歷史迫靖,沒有分支合并信息 $ git log --oneline --graph * ca22a79 (HEAD -> master, dev) dev: C3 * 3c29c30 master: C4 * 44b275c master: C2 * 5454078 master: C1
由于變基后院峡,
dev
分支和master
分支位于同一條時(shí)間線上,因此系宜,git merge
默認(rèn)采用Fast-forward
模式合并分支照激,這樣分支合并信息就被消除了,提交歷史記錄呈現(xiàn)一條線盹牧,非常簡(jiǎn)潔俩垃。注:前面我們說過,盡量禁用
Fast-forward
模式汰寓,以保留合并分支信息口柳,而rebase
模式卻是打平分支,消除分支合并信息有滑,與我們的建議截然相反跃闹。其實(shí),分支合并需要依據(jù)具體場(chǎng)景進(jìn)行選擇,當(dāng)我們?cè)诒镜剡M(jìn)行開發(fā)時(shí)望艺,我們最好保留自己的分支合并信息苛秕,而在協(xié)同工作時(shí),如果我們直接git pull origin master
找默,master
分支就會(huì)前進(jìn)(假設(shè)拉取到新提交)想帅,這樣當(dāng)我們合并分支到master
時(shí),其他人的提交就交織到我們的分支中啡莉,默認(rèn)以merge
模式生成一個(gè)合并提交,通常來說旨剥,我們不希望合并其他人的提交咧欣,因?yàn)檫@樣會(huì)污染我們的分支,并且隨著合并次數(shù)增多轨帜,提交歷史會(huì)非称枪荆混亂,在這種情況下蚌父,采用rebase
模式進(jìn)行分支合并就是一個(gè)不錯(cuò)的選擇哮兰。具體來說,
rebase
模式常用于協(xié)同開發(fā)苟弛,常見的場(chǎng)景有如下兩種:變基本地私有分支:當(dāng)我們基于
master
分支創(chuàng)建一個(gè)私有分支喝滞,并做了一些提交后,此時(shí)通常會(huì)先執(zhí)行git pull origin master
拉取新提交膏秫,這樣本地master
分支就前進(jìn)了右遭,然后對(duì)私有分支執(zhí)行git rebase master
,將私有分支變基到master
分支最新提交點(diǎn)處缤削,最后再合并到master
分支窘哈,這樣就能消除其他人的提交對(duì)我們分支的污染,提交記錄呈一條線亭敢,沒有分叉滚婉,并且在一個(gè)區(qū)間內(nèi)都是我們自己的提交,不會(huì)在中間夾雜其他人的提交帅刀,這樣歷史記錄就非常清晰让腹。-
變基追蹤分支:當(dāng)我們對(duì)本地追蹤分支修改并進(jìn)行了多個(gè)提交后,如果要這些提交上傳到遠(yuǎn)程倉庫劝篷,首先都會(huì)
git pull
拉取遠(yuǎn)程倉庫更新哨鸭,但這樣做遠(yuǎn)程倉庫該分支的新提交(假設(shè)成功拉取到新提交)需要與我們的提交進(jìn)行一個(gè)merge
操作,如下圖所示:當(dāng)我們執(zhí)行
git pull
時(shí)娇妓,上述示意圖中像鸡,本地追蹤分支dev
就會(huì)與遠(yuǎn)程分支origin/dev
進(jìn)行一個(gè)分支合并,生成一個(gè)新的合并提交,如下圖所示:我們并不想讓自己的提交與其他的提交交織在一起只估,因?yàn)檫@樣相當(dāng)于自己的提交被污染了志群,因此,對(duì)于追蹤分支的上傳蛔钙,推薦使用
git pull --rebase
方式拉取更新锌云,這種方式相當(dāng)于執(zhí)行git fetch && git rebase origin/dev
,Git 會(huì)將我們的提交變基到遠(yuǎn)程分支origin/dev
的最新提交點(diǎn)吁脱,如下圖所示:這樣桑涎,所有人的提交都不會(huì)互相污染,分支提交歷史呈一條線展示兼贡,且每個(gè)人的多個(gè)提交都集中在一個(gè)連續(xù)區(qū)間內(nèi)攻冷,方便查閱。
注:變基追蹤分支指的是更改本地已提交但未上傳到服務(wù)器的提交遍希,千萬不要變基已存在于服務(wù)器上的分支提交等曼,因?yàn)檫@相當(dāng)于修改了遠(yuǎn)程倉庫分支歷史,會(huì)導(dǎo)致協(xié)同開發(fā)產(chǎn)生問題凿蒜。
注:如果項(xiàng)目比較大禁谦,協(xié)同開發(fā)的人比較多,這種情況下废封,可能每天遠(yuǎn)程分支都有很多次新提交州泊,此時(shí)如果使用變基追蹤分支,可能存在過多沖突漂洋,解決這些沖突會(huì)非常耗時(shí)耗力拥诡,這種情況下,也許直接合并會(huì)更加簡(jiǎn)單氮发。
-
-
交互式變基:交互式變基主要的作用就是用于更改分支提交歷史記錄渴肉。其語法如下所示:
# 編輯 commit 之后的所有提交(不包含 commit) git rebase { -i | --interactive } <commit>
交互式變基提供了一系列操作可以讓我們修改分支提交歷史記錄,具體操作如下表所示:
操作 描述 pick
保留該提交 reword
修改該提交信息 edit
編輯該提交 squash
將該提交合并到前一個(gè)提交中(即合并到當(dāng)前提交的上一個(gè)舊提交中) fixup
與 squash
作用一致爽冕,但直接丟棄該提交信息exec
(該行剩余部分)使用 shell 運(yùn)行命令 break
到該提交處暫停變基(后續(xù)使用 git rebase --contine
從此處繼續(xù)開始變基)drop
移除該提交 label
為當(dāng)前 HEAD
打一個(gè)標(biāo)記reset
重置 HEAD
到該標(biāo)記merge
合并提交 git rebase -i <commit>
需要指定一個(gè)提交commit
仇祭,然后會(huì)彈出vi
編輯模式以提交時(shí)間順序展示該commit
之后的所有提交(即展示比該commit
新的提交),比如颈畸,倒數(shù)第三個(gè)提交可以使用HEAD~2
表示乌奇,則命令git rebase -i HEAD~2
會(huì)展示最新的兩個(gè)提交,不包含HEAD~2
眯娱。在編輯模式中礁苗,可以移動(dòng)提交行來更改提交順序,也可以刪除某個(gè)提交行徙缴,這樣改提交就會(huì)從提交歷史中進(jìn)行移除(但是不支持刪除全部提交,此時(shí) Git 會(huì)自動(dòng)停止變基)。下面列舉一些常用的分支歷史記錄修改操作:
-
reword
:該操作可以修改提交信息潘靖。舉個(gè)例子:比如本地倉庫存在如下提交記錄:
$ git log --oneline a75e892 (HEAD -> master) C3 8837671 c2 c830b70 C1
可以看到,
8837671
提交的信息使用了小寫字母蚤蔓,如果我們希望將其修改為大寫卦溢,則可以如下進(jìn)行操作:# 首先啟動(dòng)交互式變基,展示前兩條提交 $ git rebase -i HEAD~2
上面命令執(zhí)行完后秀又,會(huì)彈出
vi
編輯模式单寂,其內(nèi)容如下所示:pick 8837671 c2 pick a75e892 C3
此時(shí),我們將
c2
對(duì)應(yīng)的提交8837671
修改為reword
吐辙,然后保存退出:reword 8837671 c2 # 修改提交信息 pick a75e892 C3 # 保留該提交
此時(shí)凄贩,另一個(gè)
vi
編輯模式窗口會(huì)自動(dòng)彈出,我們可在此修改提交信息袱讹,此處我們將c2
修改為C2
,保存并退出昵时。到此捷雕,我們就成功修改了
8837671
的提交信息,如下所示:$ git log --oneline 2de4c14 (HEAD -> master) C3 35e3b4c C2 # 已成功修改 c830b70 C1
-
squash
:該操作可以壓縮提交壹甥,也即將多個(gè)提交壓縮到前一個(gè)提交中救巷。舉個(gè)例子:比如對(duì)于本地倉庫,其提交歷史記錄如下所示:
$ git log --oneline 7b8e084 (HEAD -> master) C4 2de4c14 C3 35e3b4c C2 c830b70 C1
假設(shè)現(xiàn)在我們想將
C2
和C3
壓縮成一個(gè)提交句柠,此時(shí)可以如下操作:# 啟動(dòng)交互式變基浦译,展示 C1 之后的所有提交 $ git rebase -i c380b70
此時(shí)會(huì)彈出編輯窗口,我們將
C3
設(shè)置為squash
溯职,表示它會(huì)壓縮到C2
中:pick 35e3b4c C2 # 保留 C2 squash 2de4c14 C3 # 壓縮 C3(壓縮到上一個(gè)提交精盅,即 C2) pick 7b8e084 C4 # 保留 C4
此時(shí)會(huì)彈出另一個(gè)窗口,該窗口同時(shí)包含
C2
和C3
的提交信息谜酒,我們可以手動(dòng)編輯壓縮合并生成的新提交信息:# 默認(rèn)采用上一個(gè)提交信息叹俏,這里我將其修改為如下 squash C2 and C3
此時(shí),我們就完成了
C2
和C3
的合并僻族,效果如下所示:$ git log --oneline cfed515 (HEAD -> master) C4 fb5e7f3 squash C2 and C3 # 合并成功 c830b70 C1
-
fixup
:該操作也squash
作用一樣粘驰,也是用于壓縮提交评抚。但與squash
不同的是晨抡,該操作會(huì)丟棄被壓縮提交(即被fixup
標(biāo)注的提交)的提交信息俏拱。舉個(gè)例子:比如我們還是壓縮
C2
和C3
找田,但是直接將C3
壓縮到C2
:# 倉庫初始狀態(tài) $ git log --oneline 3ba4ca0 (HEAD -> master) C4 31b3716 C3 b32f587 C2 c830b70 C1 # 啟動(dòng)變基 $ git rebase -i HEAD~3
此時(shí)將
C3
標(biāo)注為fixup
妇汗,表示將C3
直接壓入到上一個(gè)提交(即C2
)中:pick b32f587 C2 # 保留 fixup 31b3716 C3 # 壓入到上一個(gè) pick 3ba4ca0 C4 # 保留
由于是直接壓縮乖仇,因此直接就返回了廉油,不會(huì)像
squash
需要再?gòu)棾鲆粋€(gè)窗口合并提交信息势誊,此時(shí)的效果如下所示:$ git log --oneline 1a32d3c (HEAD -> master) C4 fd46dfc C2 c830b70 C1
-
edit
:該操作可以用于編輯提交。該操作具體的執(zhí)行邏輯為:當(dāng)提交被標(biāo)注為
edit
時(shí)枝哄,Git 會(huì)自動(dòng)變基到該提交上肄梨,然后我們就可以編輯該提交,比如增加挠锥、刪除一些內(nèi)容众羡,然后使用git comit --amend
修改此次提交,也可以在該提交上增添新提交蓖租,這樣就相當(dāng)于在之前的提交歷史記錄中插入一些新提交...舉個(gè)例子:除了重置粱侣、增加提交外,
edit
操作也常常用來將提交切分為多個(gè)小提交蓖宦,其實(shí)就是重置+增加提交的操作齐婴,比如,前面我們將倉庫提交C2
和C3
壓縮到一起稠茂,如下所示:$ git log --oneline cfed515 (HEAD -> master) C4 fb5e7f3 squash C2 and C3 c830b70 C1
現(xiàn)在如果我們想重新分離開
C2
和C3
柠偶,則可以借助edit
操作,如下所示:# 啟動(dòng)變基 $ git rebase -i HEAD~2
我們將
fb5e7f3
標(biāo)注為edit
:edit fb5e7f3 squash C2 and C3 pick cfed515 C4
保存退出編輯窗口后睬关,Git 會(huì)自動(dòng)變基到
fb5e7f3
中:$ git show HEAD --oneline -s fb5e7f3 (HEAD) squash C2 and C3
我們將該提交重新拆分為兩個(gè)小提交
C2
和C3
:# 暫存區(qū)移除 C3 內(nèi)容 $ git rm --cached 3.txt rm '3.txt' # 拆分出 C2 內(nèi)容并重新提交 $ git commit --amend -m 'C2' [detached HEAD b32f587] C2 Date: Tue Jan 12 11:37:16 2021 +0800 1 file changed, 1 insertion(+) create mode 100644 2.txt # 添加 C3 內(nèi)容 $ git add 3.txt # 提交 C3 內(nèi)容 $ git commit -m 'C3' [detached HEAD 31b3716] C3 1 file changed, 1 insertion(+) create mode 100644 3.txt # 拆分完成后诱担,繼續(xù)變基過程 $ git rebase --continue Successfully rebased and updated refs/heads/master.
此時(shí),變基結(jié)束电爹,我們成功將一個(gè)大提交拆分為多個(gè)小提交蔫仙,如下所示:
$ git log --oneline 3ba4ca0 (HEAD -> master) C4 31b3716 C3 # 拆分 b32f587 C2 # 拆分 c830b70 C1
-
drop
:如果要?jiǎng)h除某個(gè)提交,只需將該提交標(biāo)注為drop
丐箩,或者直接在編輯窗口中刪除該提交即可摇邦。舉個(gè)例子:假設(shè)我們當(dāng)前倉庫本地分支提交歷史記錄如下所示:
$ git log --oneline 45fcf79 (HEAD -> master) C3 6992e50 C2 c830b70 C1
假設(shè)現(xiàn)在我們想要?jiǎng)h除
C2
和C3
這兩個(gè)提交,其步驟如下所示:# 啟動(dòng)交互式變基 $ git rebase -i HEAD~2
此時(shí)彈出的編輯窗口內(nèi)容如下:
pick 6992e50 C2 pick 45fcf79 C3
這里我們將
C2
標(biāo)注為drop
屎勘,然后直接刪除C3
施籍,同時(shí)驗(yàn)證這兩種刪除方法:drop 6992e50 C2
保存并退出編輯窗口,此時(shí)查看倉庫提交歷史記錄:
$ git log --oneline c830b70 (HEAD -> master) C1
可以看到概漱,我們已經(jīng)成功刪除了
C2
和C3
法梯。 -
更換提交順序:更換提交順序只需在編輯窗口中直接更換提交的順序即可。
舉個(gè)例子:比如我們當(dāng)前本地倉庫提交歷史記錄如下所示:
$ git log --oneline 45fcf79 (HEAD -> master) C3 6992e50 C2 c830b70 C1
假設(shè)現(xiàn)在我們想更換
C2
和C3
的順序犀概,其步驟如下:# 啟動(dòng)交互式變基 $ git rebase -i HEAD~2
此時(shí)的編輯窗口如下所示:
pick 6992e50 C2 pick 45fcf79 C3
要更換
C2
和C3
提交順序立哑,只需在編輯窗口中更換兩者順序即可:pick 45fcf79 C3 pick 6992e50 C2
如此我們就已經(jīng)完成提交順序更換,此時(shí)的提交歷史記錄如下所示:
$ git log --oneline 0486bd0 (HEAD -> master) C2 afa4a24 C3 c830b70 C1
-
git cherypick
在多分支工作流中姻灶,當(dāng)我們需要獲取另一個(gè)分支的所有變動(dòng)時(shí)铛绰,通常采用的都是分支合并(git merge
)策略,但是如果我們只對(duì)分支的一個(gè)或某幾個(gè)提交感興趣产喉,那么也可以只摘取這幾個(gè)提交捂掰,將他們各自的修改一一應(yīng)用到我們當(dāng)前分支上敢会,Git 中,具備提交摘取的命令為git cherry-pick
这嚣,其具體語法如下所示:
# 支持摘取多個(gè) commit
git cherry-pick [<options>] <commit-ish>...
git cherry-pick (--continue | --skip | --abort | --quit)
git cherry-pick
的本質(zhì)是摘取提交鸥昏,將其修改應(yīng)用到當(dāng)前分支上。
git cherry-pick
支持摘取一個(gè)或多個(gè)提交姐帚,每一個(gè)提交應(yīng)用到當(dāng)前分支吏垮,都會(huì)生成一個(gè)新的提交,該提價(jià)的修改完全與摘取的提交一致罐旗。其實(shí)git cherry-pick
就是將摘取的提交在當(dāng)前分支上進(jìn)行重復(fù)播放膳汪。
git cherry-pick
常用的命令選項(xiàng)有如下:
-n, --no-commit
:應(yīng)用摘取提交時(shí),只進(jìn)行更新九秀,但不提交遗嗽。
注:默認(rèn)情況下,git cherry-pick
在應(yīng)用摘取提交完成時(shí)鼓蜒,會(huì)自動(dòng)進(jìn)行提交痹换,生成一個(gè)新提交。-e, --edit
:如果想更改提交信息都弹,可以添加-e, --edit
娇豫。
注:默認(rèn)情況下,git cherry-pick
直接將摘取的提交信息作為新生成提交的提交信息缔杉。-m parent-number, --mainline parent-number
:當(dāng)摘取的提交是一個(gè)合并提交時(shí),此時(shí)git cherry-pick
無法區(qū)分應(yīng)當(dāng)使用哪個(gè)分支上進(jìn)行的修改搁料,因此默認(rèn)失敗處理或详。此時(shí)必須指定一個(gè)parent-number
,表示要摘取的變動(dòng)分支郭计。parent-number
取值由1
開始霸琴,具體查找方法可參考:高級(jí)技巧 - 查看合并提交的parent-number
舉個(gè)例子:假設(shè)本地倉庫存在master
和dev
分支,現(xiàn)在突然發(fā)現(xiàn)線上版本出現(xiàn)漏洞昭伸,因此緊急從master
分支上創(chuàng)建一個(gè)hotfix/add_file
分支梧乘,然后做了兩個(gè)提交,如下圖所示:
簡(jiǎn)單起見庐杨,每個(gè)提交都只是增加了相應(yīng)數(shù)字的文件选调。
當(dāng)漏洞修改完成后,就需要將hotfix/add_file
分支合并到master
分支中:
$ git switch master
Switched to branch 'master'
# 合并 hotfix 分支
$ git merge --no-ff hotfix/add_file -m 'fix: C6 => merge branch hotfix/add_file'
Merge made by the 'recursive' strategy.
4.txt | 1 +
5.txt | 1 +
2 files changed, 2 insertions(+)
create mode 100644 4.txt
create mode 100644 5.txt
此時(shí)的示意圖如下所示:
同樣的灵份,hotfix/add_file
分支上的修改也要合并到dev
分支上仁堪,此時(shí),dev
分支可以git cherry-pick
或直接合并hotfix/add_file
分支填渠,也可以git cherry-pick
主分支master
上的合并提交C6
弦聂,下面對(duì)這兩種方法分別進(jìn)行講解:
-
git cherry-pick
摘取hotfix/add_file
分支所有提交:這里我們不采用合并方式鸟辅,而是將hotfix/add_file
分支的所有提交,即C4
和C5
直接摘取到dev
分支中:# 切換到 dev 分支 $ git switch dev Switched to branch 'dev' # 查看 hotfix/add_file 分支所有提交 $ git log --oneline hotfix/add_file 35a10e5 (hotfix/add_file) fix: C5 # 目標(biāo)提交 f79b3b1 fix: C4 # 目標(biāo)提交 d0b972f feat: C2 537ba3c feat: C1 # cherry-pick C4 和 C5 $ git cherry-pick f79b3b1 35a10e5 [dev 8bf6c28] fix: C4 # 應(yīng)用 C4 Date: Tue Jan 12 21:40:29 2021 +0800 1 file changed, 1 insertion(+) create mode 100644 4.txt [dev 6e3d3ef] fix: C5 # 應(yīng)用 C5 Date: Tue Jan 12 21:40:46 2021 +0800 1 file changed, 1 insertion(+) create mode 100644 5.txt # 合并成功 $ git log --oneline 6e3d3ef (HEAD -> dev) fix: C5 8bf6c28 fix: C4 12b54a1 feat: C3 d0b972f feat: C2 537ba3c feat: C1
從提交歷史中莺葫,我們已經(jīng)可以看到成功摘取
C4
和C5
到dev
分支上了匪凉,此時(shí)的示意圖如下所示: -
cherry-pick
合并提交:第二種方法是摘取合并提交,即摘取C6
應(yīng)用到dev
分支上捺檬。具體步驟如下:-
首先我們將
dev
分支重置到C3
提交:# 回退到 C3 $ git reset --hard 12b54a1 HEAD is now at 12b54a1 feat: C3 # 回退成功 $ git log --oneline 12b54a1 (HEAD -> dev) feat: C3 d0b972f feat: C2 537ba3c feat: C1
此時(shí)的倉庫示意圖如下所示:
-
此時(shí)
dev
分支可以摘取C6
再层,需要注意的是,由于C6
是一個(gè)合并提交欺冀,因此需要指定摘取分支树绩,對(duì)于C6
而言,其是由master
分支合并hotfix/add_file
分支生成的合并提交隐轩,這里我們應(yīng)當(dāng)選擇摘取分支為hotfix/add_file
:# 查找 C6 的哈希值 $ git log --oneline master | grep C6 02d311d fix: C6 => merge branch hotfix/add_file # 查看 C6 的 parent-number $ git cat-file -p 02d311d | grep -i parent parent d0b972f09705aaf330c59be6eedbd69a1e49ccbc # parent-number = 1 parent 35a10e51533ae17c42ecdf3ad9598334cdaeca08 # parent-number = 2 # 比對(duì) C6 和 parent_commit1 的差異饺饭,可以看到,parent_commit1 就是 hotfix/add_file职车, # 因此 parent-number = 1 $ git diff --stat d0b972f 02d311d 4.txt | 1 + 5.txt | 1 + 2 files changed, 2 insertions(+) # 比對(duì) C6 和 parent_commit2 的差異(此步可忽略瘫俊,因?yàn)樯弦徊揭颜页?parent-number) $ git diff --stat 35a10e5 02d311d # 摘取 parent-number = 1 的提交 $ git cherry-pick -m 1 02d311d [dev fdfc2c9] fix: C6 => merge branch hotfix/add_file Date: Tue Jan 12 21:55:29 2021 +0800 2 files changed, 2 insertions(+) create mode 100644 4.txt create mode 100644 5.txt # 查看摘取合并結(jié)果 $ git log --oneline fdfc2c9 (HEAD -> dev) fix: C6 => merge branch hotfix/add_file 12b54a1 feat: C3 d0b972f feat: C2 537ba3c feat: C1
可以看到,我們成功將
C6
的修改應(yīng)用到了dev
分支悴灵,此時(shí)的示意圖如下所示:注:可以看到扛芽,
git cherry-pick
合并提交的操作還是相對(duì)麻煩的,建議盡量避免對(duì)合并提交進(jìn)行摘取积瞒。
-
git bisect
git bisect
命令可以讓我們很方便快速查找到出現(xiàn) bug 的提交川尖,它的原理是對(duì)給定范圍的提交進(jìn)行二分查找,由用戶判斷當(dāng)前提交是否存在 bug茫孔,依次迭代不斷縮小規(guī)模進(jìn)行二分查找叮喳,這樣我們就可以很快從一個(gè)大范圍提交區(qū)間找到引入 bug 的那個(gè)提交。
git bisect
命令的具體語法如下所示:
# 啟動(dòng)二分查找
git bisect start [<paths>...]
# 當(dāng)前提交存在 bug
git bisect bad [<rev>]
# 當(dāng)前提價(jià)良好(即不存在 bug)
git bisect good [<rev>...]
# 退出二分查找
git bisect reset [<commit>]
git bisect terms [--term-good | --term-bad]
git bisect skip [(<rev>|<range>)...]
git bisect (visualize|view)
git bisect replay <logfile>
git bisect log
git bisect run <cmd>...
git bisect help
可以看到缰贝,git bisect
攜帶了很多的子命令選項(xiàng)馍悟,但是通常我們只會(huì)使用git bisect { start | good | bad | reset }
這四個(gè)子命令來進(jìn)行二分查找。其中:
-
git bisect start
:該命令會(huì)啟動(dòng)二分查找過程剩晴。其具體語法如下所示:git bisect start [end_point] [start_point]
其中锣咒,
end_point
表示最近的提交,start_point
表示最早之前的提交赞弥,如果兩者都指定了毅整,那么二分查找第一個(gè)提交就是start_point
和end_point
的中間提交,如果未指定start_point
和end_point
绽左,則進(jìn)入二分搜索時(shí)毛嫉,還需手動(dòng)使用git bisect bad <commit>
和git bisect good <commit>
手動(dòng)指定end_point
和start_point
。 -
git bisect good
:執(zhí)行該命令會(huì)將當(dāng)前提交設(shè)置為良好狀態(tài)妇菱,即表示當(dāng)前提交不存在 bug承粤。其語法如下所示:git bisect good [<rev>...]
我們也可以在啟動(dòng)二分查找后暴区,手動(dòng)指定一個(gè)提交設(shè)置為良好狀態(tài)(比如
git bisect good 31af8d
,表示提交31af8d
未引入 bug)辛臊,這樣可以人為縮短搜索范圍仙粱。 -
git bisect bad
:執(zhí)行該命令會(huì)將當(dāng)前提交設(shè)置為出錯(cuò)狀態(tài),即表示當(dāng)前提交存在 bug彻舰。其語法如下所示:git bisect bad [<rev>]
我們也可以在啟動(dòng)二分查找后伐割,手動(dòng)指定一個(gè)提交設(shè)置為出錯(cuò)狀態(tài)(比如
git bisect bad 31af8d
,表示提交31af8d
存在 bug)刃唤,這樣可以人為縮短搜索范圍隔心。 -
git bisect reset
:該命令會(huì)結(jié)束二分查找過程。其語法如下所示:git bisect reset [<commit>]
默認(rèn)情況下尚胞,該命令會(huì)退出二分查找過程硬霍,然后回到先前的提交,即執(zhí)行
git bisect start
時(shí)的提交笼裳。如果想退出時(shí)回到其他提交唯卖,直接在后面添加目標(biāo)提交<commit>
即可。
舉個(gè)例子:假設(shè)我們當(dāng)前倉庫存在 5 個(gè)提交歷史躬柬,為了方便演示拜轨,我們假設(shè)某個(gè)提交刪除了ReadMe.md
文件,現(xiàn)在想找出刪除該文件的提交允青,操作過程如下:
# 所有提交
$ git log --oneline
15c3cdb (HEAD -> master) feat: 555
7b93852 feat: 444
c40d88f feat: 333
b9ce941 docs: add ReadMe.md
58bdc1d feat: 111
# HEAD 58bdc1d 表示對(duì)所有提交進(jìn)行二分查找橄碾,這里也可以忽略不寫
$ git bisect start HEAD 58bdc1d
Bisecting: 1 revision left to test after this (roughly 1 step)
[c40d88fc907538e7392509c7b221ebf78ae42516] feat: 333 # 表示當(dāng)前處于 c40d88f,也就第三個(gè)提交
# 查看當(dāng)前提交颠锉,可以看到法牲,確實(shí)是處于第三個(gè)提交
$ git show HEAD --oneline --stat -s
c40d88f (HEAD) feat: 333
# 當(dāng)前提交存在 ReadMe.md
$ ls
1.txt 3.txt ReadMe.md
# 由于當(dāng)前提交存在 ReadMe.md,故設(shè)置為良好狀態(tài)
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[7b9385209361db20ce6b1d4e1e7c81d999ae84b5] feat: 444 # 此時(shí)處于 7b93852木柬,即第四個(gè)提交
# 當(dāng)前提交不存在 ReadMe.md
$ ls
1.txt 3.txt 4.txt
# 由于當(dāng)前提交不存在 ReadMe.md皆串,故將其設(shè)置為 bad 狀態(tài)
$ git bisect bad
7b9385209361db20ce6b1d4e1e7c81d999ae84b5 is the first bad commit # 這里表示當(dāng)前提交就是第一個(gè)引入 bug 的提交
commit 7b9385209361db20ce6b1d4e1e7c81d999ae84b5
Author: Why8n <Why8n@gmail.com>
Date: Sun Jan 10 18:18:08 2021 +0800
feat: 444
4.txt | 1 +
ReadMe.md | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 4.txt
delete mode 100644 ReadMe.md # 刪除了文件 ReadMe.md
# 找到引入 bug 的提交后淹办,就可以退出二分查找了
$ git bisect reset
Previous HEAD position was 7b93852 feat: 444
Switched to branch 'master'
# 因?yàn)閯h除了 ReadMe.md 的提交還進(jìn)行了其他修改眉枕,因此這里不能直接使用 git revert
# 但是既然找到了刪除 ReadMe.md 的提交,那么我們只需從該提交之前的提交獲取 ReadMe.md 文件怜森,進(jìn)行恢復(fù)即可
# 7b93852 提交刪除了 ReadMe.md速挑,該提交之前的提交為 c40d88f
$ git log --oneline | grep 7b93852 -A 1
7b93852 feat: 444
c40d88f feat: 333
# 查詢 c40d88f 所有文件,獲取 ReadMe.md 的對(duì)象文件
$ git ls-tree c40d88f
100644 blob 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 1.txt
100644 blob 55bd0ac4c42e46cd751eb7405e12a35e61425550 3.txt
100644 blob c200906efd24ec5e783bee7f23b5d7c941b0c12c ReadMe.md # 目標(biāo)文件
# 將目標(biāo)文件內(nèi)容寫到當(dāng)前工作目錄
$ git cat-file -p c20090 > ReadMe.md
以上副硅,就是git bisect
的整個(gè)基本操作過程姥宝。
查看合并提交的parent-number
前面我們介紹過的git revert
和git cherry-pick
等命令,在遇到合并提交時(shí)恐疲,都需要指定主線分支腊满,也即parent-number
套么。Git 似乎并沒有直接提供查詢parent-number
的命令,因此我們只能手動(dòng)進(jìn)行查找碳蛋。
我們以一個(gè)例子來驅(qū)動(dòng)講解查找合并提交的parent-number
胚泌,假設(shè)現(xiàn)在我們有一個(gè)合并提交02d311d
,可以通過如下命令查看其內(nèi)容:
# 法一
git show --pretty=raw <merge_commit>
# 法二
git cat-file -p <merge_commit>
比如肃弟,查看合并提交02d311d
玷室,結(jié)果如下:
$ git show --pretty=raw 02d311d
commit 02d311d5c9bb0989c1285e068fc3c2a4de02b027
tree 03c45eebbff1c1fa9f9152d9800d4e65f4602052
parent d0b972f09705aaf330c59be6eedbd69a1e49ccbc # parent1_commit,其 parent-number = 1
parent 35a10e51533ae17c42ecdf3ad9598334cdaeca08 # parent2_commit笤受,其 parent-number = 2
author Why8n <Why8n@gmail.com> 1610459729 +0800
committer Why8n <Why8n@gmail.com> 1610459729 +0800
fix: C6 => merge branch hotfix/add_file
該命令會(huì)顯示合并提交的父提交穷缤,第一個(gè)parent1_commit
的parent-number
就是1
,第二個(gè)parent2_commit
的paretn-number
就是2
箩兽,依次類推...
然后津肛,我們只需一一比對(duì)parent_commit
與合并提交之間的差別,就可以判斷得出應(yīng)當(dāng)使用哪個(gè)parent_commit
了:
git diff --stat <parent_commit> <merge_commit>
更多詳細(xì)內(nèi)容比肄,請(qǐng)參考:Git cherry-pick syntax and merge branches
其他
git blame
如果我們想查看文件每一行對(duì)應(yīng)的版本以及最后修改的作者時(shí)快耿,則可以使用git blame
命令。其具體語法如下所示:
git blame [<options>] [<rev-opts>] [<rev>] [--] <file>
下面列舉幾種常用的git blame
操作:
-
指定顯示行數(shù):可以通過添加
-L <start>,<end>
選項(xiàng)來指定只顯示文件start
到end
之間的行:# 只顯示 .gitignnore 文件第 1 到 3 行的內(nèi)容 $ git blame -L 1,3 .gitignore aa658574bfc (Josh Steadmon 2019-01-15 14:25:50 -0800 1) /fuzz-commit-grap 5e472150800 (Josh Steadmon 2018-10-12 17:58:40 -0700 2) /fuzz_corpora 5e472150800 (Josh Steadmon 2018-10-12 17:58:40 -0700 3) /fuzz-pack-header # 只顯示 .gitignnore 文件第 3 行修改信息 $ git blame -L 3,+1 HEAD~1 .gitignore 5e472150800 (Josh Steadmon 2018-10-12 17:58:40 -0700 3) /fuzz-pack-headers
-
顯示特定版本的文件修改:
git blame
默認(rèn)只顯示文件最后一個(gè)版本的修改(當(dāng)然文件中每一行內(nèi)容都可能處于不同的版本中)芳绩,如果想顯示某個(gè)提交該文件的信息時(shí)掀亥,可以指定該提交版本:# 顯示 .gitignore 文件倒數(shù)第二次提交的第一行修改信息 $ git blame -L 1,+1 HEAD~1 .gitignore aa658574bfc (Josh Steadmon 2019-01-15 14:25:50 -0800 1) /fuzz-commit-graph
git gc
前文說過,Git 本質(zhì)是一個(gè)全量快照的文件系統(tǒng)妥色,因此當(dāng)我們暫存次數(shù)過多時(shí)搪花,Git 對(duì)象數(shù)據(jù)庫會(huì)存儲(chǔ)很多對(duì)象文件,有些對(duì)象文件實(shí)際上沒有被任何提交對(duì)象直接或間接進(jìn)行引用嘹害,這些對(duì)象稱為『松散對(duì)象(loose objects)』撮竿。
我們使用命令git gc
來垃圾回收這些松散對(duì)象,減小倉庫大小笔呀。簡(jiǎn)單來說幢踏,當(dāng)運(yùn)行git gc
時(shí),Git 會(huì)收集所有松散對(duì)象并將它們存入一個(gè)packfile
文件中许师,并將多個(gè)packfile
文件合并成一個(gè)大的packfile
文件房蝉,然后移除不被任何提交引用且超過一定期限的對(duì)象文件。除此之外微渠,git gc
還會(huì)將所有的引用文件(即.git/refs
)打包到另一個(gè)單獨(dú)的文件中搭幻。
更多 Git 垃圾回收相關(guān)內(nèi)容,可參考如下文章:
分頁器
Git 中幾乎所有命令都提供了分頁器(Pager)功能逞盆,當(dāng)命令輸出超出一頁時(shí)檀蹋,會(huì)自動(dòng)啟動(dòng)分頁器。
分頁器的交互方式并不人性化云芦,可通過如下幾種方法進(jìn)行分頁:
-
--no-pager
:手動(dòng)為 Git 添加--no-pager
選項(xiàng)俯逾,可禁止啟動(dòng)分頁器:$ git --no-pager log -n 10 --oneline
-
全局配置分頁器贸桶,使用
less
命令進(jìn)行翻頁:$ git config --global core.pager "less -FRSX"
別名
可以通過git config alias
來為其他命令設(shè)置一個(gè)簡(jiǎn)短的別名,方便使用桌肴,比如:
$ git config --global alias.br branch
$ git br # ==> 擴(kuò)展為:git branch
上述命令為branch
設(shè)置了一個(gè)別名br
刨啸,此時(shí)使用git br
就相當(dāng)于使用git branch
。
注:Git 的別名就是一個(gè)字符串识脆,使用時(shí)會(huì)自動(dòng)擴(kuò)展為設(shè)置的內(nèi)容设联,自動(dòng)拼接到git
指令后面。
Git 也可以為外部命令指定別名灼捂,只需在命令前面添加!
即可:
$ git config --global alias.ls '!ls -alrt'
$ git ls # ==> ls -alrt
舉個(gè)例子:這里我們?cè)O(shè)置一個(gè)別名(命令)离例,來顯示本地倉庫對(duì)象數(shù)據(jù)庫所有對(duì)象文件及其類型:
# 由于命令太長(zhǎng),我們選擇直接在全局配置文件中進(jìn)行修改悉稠,方便很多
$ git config --global --edit
然后在標(biāo)簽[alias]
下設(shè)置如下內(nèi)容:
[alias]
; 注釋:sdo represent show data objects
sdo = "!find .git/objects -type f | awk -F '/' '{ hash=$3$4; cmd = sprintf(\"git cat-file -t %s\", hash); printf(\"%s\t\", hash); system(cmd); }'"
此時(shí)使用命令git sdo
就可以顯示對(duì)象數(shù)據(jù)庫中所有的對(duì)象文件及其類型:
# sdo represents show data objects
$ git sdo
0767f3e206a0a431633b2063bbda680026c33f70 commit
10f86d6b803b8962653f16a9967a4578215dcb22 tree
778d49177a4b6da0e967ac3e9308076ad500e6e7 blob
git clean
如果需要移除工作區(qū)中未被追蹤的文件或文件夾宫蛆,可以使用git clean
命令,其語法如下所示:
git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] [<paths>]...
其中的猛,常用的選項(xiàng)有:
-n, --dry-run
:表示只顯示將要被移除的文件/文件夾耀盗,而不進(jìn)行真正的刪除。-d
:表示移除文件和文件夾卦尊。
注:默認(rèn)情況下叛拷,為了盡可能減少文件刪除,git clean
不會(huì)刪除未被追蹤的文件夾岂却。-f, --force
:表示強(qiáng)制執(zhí)行刪除操作忿薇。
注:如果配置了選項(xiàng)clean.requireForce
為false
的話,git clean
默認(rèn)不進(jìn)行刪除動(dòng)作躏哩,此時(shí)可通過添加-f
選項(xiàng)真正執(zhí)行刪除操作署浩。
版本及范圍表示法
大多數(shù) Git 命令都會(huì)攜帶一個(gè)revision
(修訂版本)作為參數(shù),因此 Git 也內(nèi)置了一些版本及其范圍的簡(jiǎn)便引用方法扫尺,大致有如下:
-
版本指定:版本指定可引用一個(gè)提交或多個(gè)提交:
-
<sha1>
:表示對(duì)象文件的哈希字符串筋栋。比如:dae86e1950b1277e545cee180551750029cfe735
或dae86e
。 -
<refspec>
:表示符號(hào)引用正驻。比如:master
弊攘、heads/master
和refs/heads/master
都表示master
分支,比如HEAD
表示HEAD
引用文件... -
@
:一個(gè)單獨(dú)的@
等同于HEAD
拨拓。 -
<refspec>@{<date>}
:表示指定時(shí)間段的引用肴颊。比如:master@{yesterday}
氓栈,HEAD@{5 minutes ago}
渣磷。 -
<refspec>@{<number>}
:表示引用refspec
之前的第number
個(gè)提交。比如:master@{0}
等同于master
授瘦,master@{1}
為master
分支第二個(gè)最新提交醋界。 -
@{<number>}
:與<refspec>@{<number>}
功能一致竟宋,只是refspec
為HEAD
,即表示當(dāng)前分支的最新第number
個(gè)提交形纺。 -
<rev>^[n]
:表示提交rev
的前n
個(gè)父提交丘侠,n
表示字符^
的重復(fù)個(gè)數(shù)。比如:a95fabe^
表示提交a95fabe
的前一個(gè)提交逐样,HEAD^^
表示HEAD
的前第二個(gè)提交...
注:n
也可以為數(shù)字蜗字,但只能為0
和1
。比如a95fabe^0
等同于a95fabe
脂新,HEAD^1
等同于HEAD^
挪捕。 -
<rev>~<number>
:表示提交rev
的第number
祖先提交。比如:a95fabe~0
等同于a95fabe
争便,a95fabe~1
表示a95fabe
的前一個(gè)提交级零,HEAD~5
表示當(dāng)前分支的最新第 5 個(gè)提交。
注:<rev>~<number>
也支持<rev>~<n>
操作滞乙,比如:HEAD~
等同于HEAD~1
奏纪,HEAD~~
等同于HEAD~2
...
注:該模式也支持<rev>^{tree}
,表示獲取提交rev
的樹對(duì)象斩启。 -
<rev>:<path>
:表示版本rev
下的文件序调。比如:HEAD:1.txt
表示當(dāng)前版本下的1.txt
文件內(nèi)容,a95fabe:1.txt
表示版本a95fabe
下的1.txt
文件內(nèi)容兔簇。
注:該模式可以讓我們很方便對(duì)不同版本的同一文件進(jìn)行比對(duì):# HEAD:1.txt 相對(duì)于 HEAD~1:1.txt 的文件差異 $ git diff HEAD~1:1.txt HEAD:1.txt
-
-
范圍指定:當(dāng)分支提交歷史記錄包含多個(gè)提交時(shí)炕置,可以指定提交范圍:
-
^<rev>:表示不包含
rev`的提交。 -
<rev1>..<rev2>
:表示包含rev2
男韧,但是不包含rev1
朴摊,即(rev1,rev2]
。 -
<rev1>...<rev2>
:表示同時(shí)包含rev1
和rev2
此虑,即[rev1,rev2]
甚纲。
-
更多其他版本與版本范圍表示法,可參考:Git 版本及版本范圍表示法
顯示引用哈希值
可以通過git rev-parse
查看引用或?qū)ο笪募V担?/p>
# 顯示 master 最新提交哈希值
$ git rev-parse master
378a269ba0c11542ade35eef1df88e094d935548
# 顯示 HEAD 樹對(duì)象簡(jiǎn)短哈希值
$ git rev-parse --short HEAD^{tree}
5466733
顯示所有對(duì)象文件
其命令為:
git rev-list [OPTION] <commit-id>... [ -- paths... ]
舉個(gè)例子:
-
顯示最新版本所有的對(duì)象文件
$ git rev-list --objects HEAD
-
顯示倉庫所有對(duì)象文件
$ git rev-list --objects --all
附錄
本人配置
以下是本人的 Git 配置選項(xiàng):
# 用戶名
git config --global user.name Why8n
# 郵箱
git config --global user.email whyncai@gmail.com
# 默認(rèn)編輯器
git config --global core.editor nvim
# 解決 git status 中文亂碼
git config --global core.quotepath false
# 設(shè)置 git gui 界面編碼
git config --global gui.encoding utf-8
# 設(shè)置 git log 提交內(nèi)容編碼
git config --global i18n.commitencoding utf-8
# 分頁器替換
git config --global core.pager "less -FRSX"