Git 2.1有哪些新特性?
Git
2.1
有哪些新特性?
在git
2.0.0
發布2個半月后,作為小版本更新迎來了2.1.0
,帶來了一大波令人興奮的新特性。
完整的發布說明文檔可以在這里查看,但如果你不怎么接觸git
社區,會覺得發布說明文檔有些太簡明了。這篇文章是我對這次發布在Atlassian
使用中令我們興奮的方面所做的評注。
更好的分頁程序缺省設置
本文引文都是直接摘自發布說明文檔,其中會加上自己的評注。
從很早期的
Git
開始,調用less
分頁程序用的LESS
環境變量設置的缺省值是FRSX
。S
選項(截斷長文本行而不是折行)從缺省值中刪除了,因為對不同的人有不同的說法,這個選項或多或少是個人口味問題。相比,其它的選項作為缺省設置是合理的(如,R
選項非常合理,因為很多不同的輸出都是彩色的,而FX
也是合理的,因為輸出常常短于一頁。)
如果你還沒有覆蓋過git
分頁程序的缺省值,這個變化意味著git
命令的分頁輸出會在終端寬度的地方折行而不是截斷行。下面是git
2.1.0
(折行,左圖)和git
2.0.3
(截斷,右圖)顯示的例子:
這個只會影響你日志的輸出,如果你用的是一個窄的終端,或者在提交消息中有長行。一般git
推薦提交日志信息的寬度不要超過72字符,但如果覺得折行很煩,可以通過恢復原來的行為來禁用:
$ git config core.pager "less -S"
當然,分頁程序也會用于其它的輸出,比如git blame
,這種情況下由于作者名的長度和代碼風格,可以能會有很長的行。2.1.0
的發布說明文檔也指出了可以只在blame
的分頁程序中啟用-S
選項:
$ git config pager.blame "less -S"
如果你對git
還在使用的缺省less
選項很好奇,說明如下:
-
-F
:讓less
進程自動退出,如果輸出少于一頁。 -
-R
:保證只有ANSI
顏色轉義序列按原始形式輸出,這樣git
控制臺顏色才能生效。 -
-X
:避免屏幕在less
啟動時被清空。這個也是在less
輸出少于一頁時才有用。
更好的Bash
補全
更新了
Bash
的補全腳本(在contrib/
目錄),能更好地處理定義了復雜命令序列的別名。
這個超酷!我是一個自定義git
別名的大粉絲。能夠在復雜的別名上用上git
的Bash
自動補全,讓這些別名在命令行上使用起來更強大和方便。舉個例子,我定義一個可以從日志中grep
出JIRA
風格的issue
主鍵(如STASH-123
)的別名:
issues = !sh -c 'git log --oneline $@ | egrep -o [A-Z]+-[0-9]+ | sort | uniq' -
所有的命令行參數傳給git log
命令,這樣可以限制提交的范圍用于返回issue
主鍵。比如,git issues -n 1
只會顯示我的分支最近一次提交所關聯的issue
主鍵。在2.1.0
中,git
的Bash
補全讓我可以像git log
命令一樣去補全git
的issue
別名。
在git
2.0.3
下,鍵入git issues m<tab>
會退化成缺省的Bash
補全行為,列出當前目錄下m
開頭的文件。在git
2.1.0
下,正確地補全成master
,就和git log
命令下補全動作一樣。通過在別名加上空命令前綴:
,可以用于提示Bash
補全行為。如果要補全的不是別名中的第一個命令,這個很有用。舉個例子:
issues = "!f() { echo 'Printing issue keys'; git log --oneline $@ | egrep -o [A-Z]+-[0-9]+ | sort | uniq; }; f"
這個別名不能正常補全,因為git
不能把echo
命令識別為補全目標。但如果加上前綴成: git log;
,補全就正確了:
issues = "!f() { : git log ; echo 'Printing issue keys'; git log --oneline $@ | egrep -o [A-Z]+-[0-9]+ | sort | uniq; }; f"
如果你喜歡編寫復雜的別名腳本,這是個可用性的巨大改進!請記住,補全功能的腳本在contrib/
目錄下,不是git
核心的一部分,所以如果你要使用這個功能,不要忘了更新Bash profile
指向新版本的contrib/completion/git-completion.bash
。
git commit
命令使用approxidate
git commit ‐‐date=<date>
選項有了更多的時間戳格式選項,包括--date=now
。
當嚴格的parse_date()
函數不能解析給的日期字符串時,git
提交的--date
選項現在會回退到git
酷炫的(也有些古怪的)approxidate
(大概日期)解析器。approxidate
可以處理顯而易見的值,像--date=now
,也允許一些略復雜格式,像--date="midnight the 12th of october, anno domini 1979"
或是--date=teatime
。如果你想了解更多,Alex Peattie有一篇優秀的關于git
酷炫日期處理的博文。
更好的路徑顯示方式grep.fullname
git grep
會讀取grep.fullname
配置變量,強制‐‐full-name
成為缺省。這可能會讓以腳本方式使用的用戶出錯,這些用戶不期望這樣的新行為。
省得你去翻git-grep
的man
,下面是--full-name
選項的文檔說明:
--full-name
當從子目錄運行時,命令輸出的路徑通常相對于當前目錄。這個選項強制輸出的路徑是相對項目的頂級目錄。
非常貼心!這個缺省行為非常符合我的工作方式,我常常會運行git grep
找出一個文件的路徑,拷貝粘貼到一個XML
文件中(這樣的做法可能出賣了我是個Java
開發 )。如果你也覺得有用,只要簡單運行:
$ git config --global grep.fullname true
在你的配置文件開啟這個選項。
--global
選項把配置應用到$HOME/.gitconfig
文件中,這樣配置值就會成為我系統上所有git
倉庫的缺省行為。如果有必要,你可以也只在倉庫級別覆蓋配置值。
更聰明的git replace
停一會兒!先看看git replace
能做什么?
簡單地說,git replace
重寫git
倉庫中的某個對象并且不保持對應樹或是提交的SHA
不變。如果你是第一次聽到git replace
并且知道git
的數據模型,會覺得這樣的做法聽起來很逆天!我就是這么覺得。我有另一篇正在寫的博文討論什么時候和為什么要使用這樣的功能。如果現在你想了解更多,看這篇文章比看man
手冊好得多,手冊中只有很少且有些牽強的用例說明。
git replace
會讀取--edit
選項,可以編輯一個已有的對象再做替換。
--edit
選項會dump
一個對象的內容到一個臨時文件,啟動你喜歡的編輯器,這樣就可以方便地拷貝和替換這個對象。要替換master
分支的最近那次提交,可以簡單運行命令:
$ git replace --edit master
或者編輯最近那次提交的blob
,假設是文件jira-components/pom.xml
,可以運行命令:
$ git replace --edit master:jira-components/pom.xml
應該這么做?基本上不會 大部分情況應該用
git rebase
重寫對象,這樣會正確的重寫提交的SHA
,保證歷史是健全的(sane history
)。
git replace
會讀取--graft
選項,可以編輯父提交。
--graft
選項是替換一個提交有相同的內容但用不同的父提交的快捷操作。這可以方便地完成一個稍微正常一點的git replace
的用例,縮短git
歷史。要替換master
分支的最近那次提交的父,可以簡單運行命令:
$ git replace master --graft [new parent]..
或者要砍掉某個點之后的歷史,可以忽略所有父提交讓這個提交成為孤兒提交:
$ git replace master --graft
再次說明,如果沒有好的理由基本上不應該這么做。通常重寫歷史的首選方法是用明智的git rebase
。
更合理的tag
排序通過tag.sort
git tag
開始注意tag.sort
的配置問題了,這個配置在沒有指定--sort
選項時做為缺省排序。
如果你在tag
名中使用版本號(我想99.9%你就是這么做的),這真是好消息。一旦你發布的版本號中有一段多于一個數字(比如 v10
或v1.10
),git
缺省的字典排序就不好用了。舉個例子,看看Atlassian Stash
的git
倉庫的缺省tag
排序:
src/stash $ git tag -l *.*.0
..
stash-parent-2.0.0
stash-parent-2.1.0
stash-parent-2.10.0
stash-parent-2.11.0
stash-parent-2.12.0
stash-parent-2.2.0
stash-parent-2.3.0
stash-parent-2.4.0
stash-parent-2.5.0
stash-parent-2.6.0
stash-parent-2.7.0
stash-parent-2.8.0
stash-parent-2.9.0
stash-parent-3.0.0
..
有問題啊!2.10.0
是2.3.0
之后發的,所以缺省的tag
排序不對的。從git
2.0.0
開始,可以用--sort
選項可以正確按數值做版本排序:
src/stash $ git tag --sort="version:refname" -l *.*.0
..
stash-parent-2.0.0
stash-parent-2.1.0
stash-parent-2.2.0
stash-parent-2.3.0
stash-parent-2.4.0
stash-parent-2.5.0
stash-parent-2.6.0
stash-parent-2.7.0
stash-parent-2.8.0
stash-parent-2.9.0
stash-parent-2.10.0
stash-parent-2.11.0
stash-parent-2.12.0
stash-parent-3.0.0
..
這好多了。在git
2.1.0
中,可以設定這種排序成缺省方式,運行命令:
$ git config --global tag.sort version:refname
順便說一下,上面的示例git tag
命令中使用了方便的-l
選項,限制了只顯示符合指定模式的tag
名。-l *.*.0
用于只顯示大版本(major
)和小版本(minor
)的Stash
的發布。
更簡單地驗證有簽名的提交
新加了
git verify-commit
命令用于檢查有簽名提交的GPG
簽名,使用方式和git verify-tag
檢查簽名的tag
類似。
如果你要用提交簽名來認證提交的作者,verify-commit
命令大大簡化了驗證簽名的操作。不再需要自己寫個腳本去分析git log --show-signature
,只要簡單把要驗證簽名的那些提交傳給git verify-commit
。有可能你沒有用簽名提交(在Atlassian
我們沒有用),因為這么做需要管理Key
和開發額外麻煩的操作。對于多數項目,一般情況下簽名的Tag
是在方便性和安全性之間一個更好的平衡。如果你想知道為什么有項目要使用簽名提交,Mike Gerwitz講了一個在假設場景下git
的恐怖故事,這個場景下簽名提交是非常有用的。所以如果你在特別敏感的企業工作,可能要把它加入到工作流中。
更多的性能加速
git
2.1.0
也帶來了一些不錯的性能提升。
引入了使用2個文件(一個基礎文件和一個相應的增量文件)來表示索引的試驗性格式;當要重寫只有小部分工作樹變化的大索引時,這樣可能減少I/O消耗。
復述一下意思就是:如果你的提交有大量文件修改時,運行git add
可能會更快。我本地的任何增量操作,git add
已經像閃電一樣快了,所以我看不出和測試的git
版本之間有什么大的性能變化。有意思的是,大量文件的第一次add
好像快了一點。做了個快糙猛的性能測試,我試著暫存所有在JIRA
代碼庫從JIRA 5
到JIRA 6
的修改。
$ git checkout jira-v6.0$ git reset jira-v5.0$ time git add --all
git
2.0.3
平均使用2.44秒。而git
2.1.0
平均使用2.18秒 —— 節省超過10%的時間!注意,由于實驗條件這是個很不準確的測試,添加14500+個文件到索引中節省了1/4秒,所以在日常git
使用中不會看到大的變化。關于新索引格式可以在索引格式文檔中了解更多。
缺省開啟了
core.preloadindex
配置項,以充分利用現代平臺的多核。
不錯!之前我沒有開啟這個功能,但升級到2.1.0
后性能變化很顯著。再做一個快糙猛的測試,運行git status
顯示之前我用的從JIRA 5
到JIRA 6
的暫存修改。顯示暫存的14500+個文件,git
2.0.3
平均使用4.94秒。而git
2.1.0
平均使用3.99秒 —— 節省了多達~19%的時間。如果你使用了自定義的shell
提示符,在每次提示符顯示時檢查工作拷貝中是否有未提交的修改,這個性能就非常有用!當索引很大時,我明顯覺得bash
反應快了一些。
通過重組用于跟蹤已有提交的數據結構,大大優化了
git blame
。
在分析出誰提交某行(搞壞項目的)代碼,git blame
更快了。我很高興看到這個改進,就是說git-guilt
(我寫的一個小工具,用于研究如何blame
提交的修改)可以有相當的性能提高,因為它重度依賴于blame
到函數的輸出。
又來一個快糙猛的測試,看一下算出git
源碼倉庫從2.0.0
到2.1.0
的『罪行證據』(guilt transfer
)要花多長時間。這個操作git-guilt
要在git
2.0.0
到2.1.0
修改過的不同大小的文件上調用886次git blame
命令。
$ git guilt v2.0.0 v2.1.0
git
2.0.3
平均使用72.1秒。而git
2.1.0
平均使用66.7秒,提升了7.5%!如果有興趣,你可以看看git-guilt transfer
的實際代碼(Karsten Blees的66 LOC行的實現,以微弱優勢勝出Junio C Hamano)。
上面的性能測試都有些隨意,但我們正在進行Bitbucket
的git
2.1.0
升級。線上會監控升級前后的功能,可以確定新版本對這些操作的性能影響,特別是blame
和diff
操作。過幾周我會發出結果讓大家知道。
等等,還有還有!
在git
2.1.0
中還有其它很好的內容我沒有在一篇文章中涉及到,所以有興趣可以看看完整的發布說明文檔。由衷地感謝git
團隊又提供了一個高質量和豐富新功能的版本。如果你有興趣了解更多有關于git
的實用小建議和花邊新聞,歡迎在推ter上關注我(@kannonboy)和Atlassian開發工具(@atldevtools)。
來自:https://github.com/quickhack/translations/tree/master/whats-new-git-2-1