Git 很是複雜。我即便幾乎天天都在用,也不敢保證對 Git 的每個角落都瞭如指掌。
本文試圖盤點一些不知名,但在我看來較經常使用的操做。若是沒有你預期的操做,說明要麼在我看來它很知名,要麼在我看來它不太經常使用。git
git log --graph --abbrev-commit --decorate --all --oneline
github
git cherry
shell
git show ref:path
vim
git reset [--hard|--soft|--keep]
緩存
git update-index --assume-unchanged
less
若是要看項目中的分支狀況,一般的作法是打開一個 Git GUI 軟件,經過 GUI 界面來查看全景。
對於有些人來講,這是裝 GUI 的惟一需求。
其實這個需求能夠砍掉,由於 Git 提供了在終端中輸出提交記錄全景的操做:編輯器
git log --graph --abbrev-commit --decorate --all --oneline
輸出結果至關驚豔,你能夠像看 GUI 界面同樣,在終端中查看不一樣分支的提交信息。
(這裏採用了一個開源項目的提交歷史來示例,若是是公司的商業項目,輸出會更炫目)lua
這個輸出跟 man xxx
同樣,會經過 $PAGER(一般是 less) 顯示在終端上。這意味着你能夠在輸出中搜索特定的提交或 tag。
許多 GUI 界面並不提供這一功能,致使用戶只能瞪大眼睛,一點點地挪動,才能揪出特定的提交。
就這一點上,終端版的功能要比 GUI 版本勝出許多。
終端版的另外一個優勢是,提供了自由組合各類選項的能力。你能夠自定義輸出信息和顯示的提交範圍。
若是嫌命令太長,能夠給它一個 alias(經過 git alias 或 shell 的 alias 功能)。spa
有一個經常使用的 Git 操做是 git cherry-pick
。git cherry
顧名思義,天然跟 cherry-pick
有關。git cherry
能夠比較兩個分支,篩選出存在於甲分支卻不存在於乙分支的提交。顯而易見,這是在爲後面的 cherry-pick
作準備。插件
¥ git cherry 4.2.0 | tail + bb5f4b02340b3c269d7a8e11842d4a16b2b8eba1 + 2451c0c202dd48bc610ef9731c503ea54f19af83 + 64b4a599e8af4b31444f2a86559d4860401925f4 + c80b4e773f746ffe11aca9010b4433f6b6ba13ad + b2c7de9a5a5c8ea0254001a07ae24ffc27118dbb + 719dd2c3af9ba801114101ccdec1cd9e2f1bbc5d + 518db6e648aaf646db4be3067a583bdcc37ee2a8 + b01359efc5761a17cd3b893fd51219a005009641 + 422c9a8e69acc5cf013340715f8d2fa7d7cabf75 + cc0dff2f49cfbd786eca498fe2532de0455e1759
美中不足的是,git cherry
的輸出是 sha1,對機器來講友好,對人來講就是天書。若是要看具體的提交信息,須要借其餘命令之力。好比 git show
:
git show --no-patch --format='%s' cc0dff2f49cfbd786eca498fe2532de0455e1759
git show
是我最喜歡的命令之一。輸出提交的內容,是 git show
的拿手好戲之一。舉個例子,git show @
能夠顯示當前分支上最新提交。(@
是 HEAD
的別名,能少敲三個字符呢)
不過這不算 git show
的殺手鐗。git show
的絕活,是能在不檢出特定提交的前提下,顯示特定提交的某個文件的內容。
假設咱們想看看 xxx 提交上的 db.lua
,純粹只想看一看,不涉及調試或其餘複雜的操做。咱們固然能夠 git checkout xxx
,而後想看啥就看啥。然而用 git show
的話,就能保持當前分支不變,直接顯示 xxx 提交上的 db.lua
了:
git show xxx:path/to/db.lua
編輯器的 Git 插件通常支持在編輯器內直接打開 git show
的輸出。若是你用的是 Vim,又裝了 fugitive,
經過敲 :Gedit revision:%
,能夠打開當前文件的特定版本。
若是想找到哪幾個提交修改了特定文件,能夠先用 git log --format='%s' -- /path/to/your/file
獲取涉及的提交。
git reset
可不能算是「不知名」操做。任何 Git 入門教程,都會提到怎麼用 git reset
把文件移出暫存區。但在本人的平常開發流程中,移出暫存區實際上是最少用上的git reset
操做。因此我以爲能夠打個擦邊球,講講 git reset
其餘的打開方式。
移出暫存區的 git reset
操做,至關於 git reset --mixed
。除了 --mixed
,經常使用的選項還有下面三個:
--soft
--hard
--keep
開發中常有的狀況:劈劈啪啪寫了一堆代碼,連續好幾個提交。而後腦子裏靈機一動,頓悟了更好的作法。這個作法是如此地好,以至於以前的提交記錄都成了你的黑歷史。你巴不得
把這些提交記錄一筆勾銷,但該怎麼辦呢?這時候就輪到 reset --soft
上場了。reset --soft
俗稱 squash
,能夠在保留修改的同時,把最近的幾個提交歷史給抹掉。好比 git reset --soft @~3
就能重置到倒數第四個提交,但保留被抹掉的三個提交的更改。
這麼操做以後再提交一次,而後選一個夜深人靜的時刻,push -f
到遠程倉庫,你的黑歷史就煙消雲散了。固然仍是會留下些痕跡什麼的,但至少別人通常發現不了……
跟 --soft
對應的是 --hard
。--hard
也會抹掉最近的幾個提交,但不保留修改。若是感到本身的代碼實在無法看,能夠直接 reset --hard
試試。不過 reset --hard
倒不失清理環境的好方法。
--keep
正好在 --soft
和 --hard
之間。--keep
會保留未提交的更改,這一點跟 --soft
同樣;同時會抹掉已經提交的更改,這一點跟 --hard
相同。
有些時候,這一特性會派上用場。
若是你在開發的時候,忽然發現本身忘記切分支了,能夠這麼作:
git branch feature/you_need git reset --keep comit_your_hack_starts_from git checkout feature/you_need
這樣作既消除了錯誤分支上的提交,又保留了工做區裏未提交的內容。
提到 reset
,不能不提誤操做時的補救辦法。
被抹掉的內容已經提交了。這種狀況好辦。經過 git reflog
,咱們能夠找到被抹掉的提交,而後 cherry-pick
回來,抑或 reset --hard HEAD{x}
到對應的提交上去。
被抹掉的內容原來在暫存區裏。這個比較蛋疼。你須要 git fsck | grep blob
揀出每個 dangling blob,逐個 git show xxx
看看,應該能找回被抹掉的內容。這種感受,就像在垃圾桶裏翻出丟失的手稿同樣。
被抹掉的內容不在暫存區裏。靠 Git 已經救不回來了。要不找下編輯器的備份文件,要不嘗試能不能從文件系統緩存裏把文件摳出來。若是不行,試試磁盤數據恢復程序……
說這麼多,關鍵記住一點:reset 有風險,用時需謹慎。
假設代碼倉庫裏存在這麼一個配置文件:每一個人在開發時,會根據本地狀況修改裏面的值。好比某些 IDE 的項目描述文件,裏面可能夾雜着項目的路徑。
有什麼辦法,能夠避免本地的修改不會提交到倉庫裏去呢?
我的認爲 git update-index --assume-unchanged
是衆多解決方法中最好的。git update-index
能夠修改 Git 對特定路徑下的文件的處理方式。其中 --assume-unchanged
選項告訴 Git,指定路徑下的文件不會有修改。
即便在本地修改了那些文件,也不會被 Git 記錄在案。
若是我有一天須要修改這個配置文件併入庫,能夠用 --no-assume-unchanged
選項解開封印,讓 Git 從新追蹤修改。
¥ gs On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: check_integrity.sh no changes added to commit (use "git add" and/or "git commit -a") ¥ git update-index --assume-unchanged check_integrity.sh ¥ gs On branch master nothing to commit, working tree clean ¥ git update-index --no-assume-unchanged check_integrity.sh ¥ gs On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: check_integrity.sh