Git commits歷史是如何做到如此清爽的?
今天在看vue源碼時隨手看了一下commit歷史,然後就被震驚了。請問這是怎麼做到的?怎樣的git實踐才能做到這麼清爽?
多用 rebase
---
我真是服了貴乎了,樓下居然有人能從 commit 風格看出我愛慕虛榮,哈哈哈哈,只能說這葡萄是很甜了
我 rebase 純粹是因為我不喜歡 git log --graph 的時候一堆 branch 擾亂視線。另外,難道你不知道 rebase 不一定要 squash 的?
Vue作者已經放出了權威答案,我也在這補充一下自己平時的git workflow。在開發一個feature或者修個bug的時候,一般都會在最終要merge進的分支上開個新的分支,所有的工作都commit到這個新的分支上。feature寫完或者bug修完,在要merge之前,rebase新分支到最後要merge的分支上,這相當於把你在新分支上的所有新commit依次cherry pick到要merge的分支的最新commit後面,這樣後續的merge就一定會是一個非常爽的fast forward。而且在rebase的同時可以進行squash,把邏輯上相似的commit都塞到一個commit裡面,然後給他一個描述性比較強的commit message。這套操作下來之後commit歷史會非常清晰一目了然,看某些同事的項目的commit歷史裡面各種save, save work, fix bug, fix bug again的確是一種煎熬,好的commit應該反映出一個項目是怎麼一步步開發下來的,是軟體開發的航海日誌,黑匣子,任何碼農都應該建立良好的commit習慣。
Linus曾經在Linux的某個pr下面討論了好的commit應該長啥樣,非常有啟發: Add support for AR5BBU22 [0489:e03c] by WNeZRoS · Pull Request #17 · torvalds/linux
rebase是很好的工具,不過我其實反對合進master分支的時候做rebase或者fast-forward,我喜歡的方式是
- 待合併的分支rebase到master
- master merge待合併的分支(禁用fast-forward)
這樣最大的好處是能清楚看到每次merge到master分支的界限在哪裡,萬一出問題了,可以把整個merge都revert掉。最後的graph上大體是每次分出來一個branch然後馬上合回來的形狀,也沒有比一條單鏈差很多。另外,merge的commit自己可以有message,如果發生了conflict也可以記錄下來。
當然如果這次合併的branch一共只有一個commit那麼直接fast-forward也沒有什麼不可以的。
反對濫用amend和squash(以及soft reset),這才是愛慕虛榮的表現,除了引入莫名其妙的conflict以外沒有任何積極意義。保留每次提交的細粒度歷史版本對於合併、回滾是非常重要的。比如說開發完成了一個新功能,突然發現其中對某個文件的某步修改是錯誤的,需要撤銷,如果已經squash了那就難找了,可能這個文件被前後修改過很多遍,很難分離出來某一批次的修改了。再比如說cherry-pick,可能master分支上發現了一個重大的bug,這個commit需要cherry-pick到各個版本分支和開發分支上,如果這個cherry-pick以後被squash掉了,再合併的時候就會出現conflict。僅僅當很少的情況,比如這個commit僅僅是修復上個commit中的提交錯誤或低級失誤的時候,用amend和squash有一定意義(去掉一堆無意義的bugfix提交)
看了看 commit message,還是有改進空間的,如在 phodal/growth-ng 中,我建議 contributors 按這樣的規範來提交:
建議提交信息按如下規範:
[任務分類] 主要修改組件:修改內容
示例 1,[T] tabs: add icons 。其中的 T 表示這是一個技術卡,tabs 表示修改的是 Tabs,add icons 則表示添加了圖標。
示例 2,[SkillTree] detail: add link data。其中的 SkillTree 表示修改的是技能樹 Tab 下的內容,detail 則表示修改的是詳情頁,add link data 則表示是添加了技能的數據
至少提交信息可以變成:
[Bug] fix xxxx
[Feature] add ...
[T] CI: update build script
這樣做,有利於後期對項目進行度量,統計出不同維度的內容,如 Bug 數、CI 問題等等。
我一直認為,history還是應該要恢復其原有的結構才好。譬如說一組人開了兩個branch,每個星期合併到main一次,那你的history看起來就應該要有三股繩。好處是對於大眾情況而言,你經常可以去教同事解決因為瞎** merge錯造成的問題,因為一眼就看出來了。
git pull --rebase
git rebase 是正解, 無意義的merge,還有無意義的commit message 都可以通過 rebase 做到合併,丟棄,修改commit message
你也可以使用 git rebase -i 來進入互動式的操作
要求功能分支進入master branch必須要squash-and-rebase,不許用merge,在github(或者你喜歡的git repo管理工具)中加上這樣的強制措施。
merge的好處,是每一個commit都可以看見,配合使用git bisect,可以找到一個bug具體到底是哪一個commit引入的;缺點,就是git的commit圖變成下面這個鳥樣。
如果團隊成員很多,分支也很多,簡直就沒法看啊,所以還是強制使用squash-and-rebase吧。
Rebase正解,另外,不要大驚小怪,很多項目都這樣,再推薦一個吧 eggjs
最權威的答案 @尤雨溪 已經說了"多用 rebase"
我也覺得 rebase 是絕大多數人 git commit 混亂的原因:
1. 不會合併補丁
2. 不會修改倉促的 commit message
3. 不會調整 git commit 順序
等。
因為我在 @洪謙 指導下給Wine 貢獻過幾十個補丁,正好 Wine 對補丁有著嚴格的要求,所以對 git flow 比較熟悉。
因此寫過一篇博客,介紹了這部分知識,你們仍未掌握那天所學的 git 知識 ,並分享到 V 站, V 站的討論 你們仍未掌握那天所學的 git 知識 - V2EX ,很多大神也在後面也補充了一些重要的東西。
----
絕大多數人對於 git 的認識只停留在git status, git add, git push, git pull, 好一點會知道git merge, 那就是全部了。
不信?
試試你能回答出以下問題不:
- git push origin master命令中,origin 代表的是什麼,整個命令是什麼意思,origin 可以修改不
- git fetch origin; git rebase origin master 這些命令知道嗎?跟 merge 有什麼區別?
- git 如何合併兩個補丁,對第三個補丁進行修改?(這個很重要,因為你不會才導致 git commit 的信息沒有意義,commit 不夠小和多)
又或者,你試過合併 commit 嗎?commit message 寫的不好時如何修改?如何改變 commit 的順序?
如果以上有不清楚的話,那麼我希望以下的文章對你有幫助。
太長,在知乎粘貼不了。
請查看你們仍未掌握那天所學的 git 知識
Angular開發組有一個很明確的commit規範,包括英語語法要求之類的都給你講清楚了,不想研究的話直接follow就行了。至於時間線方面,rebase已經是常規的開發習慣了,只是有些場合有坑,坑不多,自己用用,避開就是了。
這個結構並不爽。絕大多數真實的研發細節都被湮沒了。愛這麼乾的人定是愛慕虛榮的人,因為他只想做個標杆秀給別人看。
-- 補,電腦補兩句
git log --no-merges
git log --pretty=oneline --graph
git log --since=3.weeks
git log --until=2017-03-17
git log --authors=linus
我的建議是一切細節丟上去,reviewer按需挖掘。
從未用過 git log --grep 的,去用用。
-- 回somebody
假設一個app及其後端作為若干repositories在管理範圍中,app開出一個feature以及issue,通常需要後端配合一個對應的feature及issue,若干天后調試完畢,staging通過然後上線,這是研發中的常見場景。具體而論,其中,假設後端涉及到6個連續的變更分別分步驟推進該feature,這就是細節。例如:
1. 固化原api#307,#308的實現,
2. 新增api#309,
3. refactoring for #307, #309,
4. 修正#308原實現適應wechat,alipay接入,
5. 重構 #308,#307,#309。
6. 修正crashes
這些,就是細節。留下他們的每個提交在分離的分支中,就能復現研發的實際進度。當若干合併後需要回溯api#211為何現在失敗了,就能夠找到原來是step 3重構時做的不完善。很顯然,這對整個團隊的提升是有積極意義的,這並不是為了KPI。
rebase它們到devel分支中的話,複查代碼很難一目了然這是某某人某一周的工作內容。
squash他們的話,5個提交的細節就被湮沒在一個新的commit中了,對於提交者和複查者來說都是不利的。
-- 小結
不得不說,如果你打算放到github你們的項目的話,這些細節暴露了提交者們的愚笨,這就是為什麼他們要美化commits的原因。
--- 給實例總是會帶偏
blame很好,我本是針對「美麗」的commits回的,自然不會真的討論blame。而且squash掉之後blame也找不出來原committer了。
一言之,你一個人的分支,愛怎麼玩怎麼玩,你在團隊里,就保留merges到公共分支或基線;但在merge你自己的分支或內容之前要rebase甚至squash那也是可以隨便的。所以其實我的觀點並不是針對私人分支的。
作為git應有的哲學,保留原貌勝於粉飾。
否則你用git幹什麼呢,用著svn的套路去玩git是要不得的。要是你看到什麼去錯什麼在log裡面就覺得好臟,我覺得這是人之常情,只不過做了就是做了,粉飾一定是沒意義的,無論你是為了log清晰也好,commits漂亮也好。
其次的原則,就是小迭代頻繁提交,@skylr提到的「一個commit完整的對應一次單一功能邏輯修改」就是這個意思。然而 @skylr 所說的擔憂其實並不存在,"git log --grep="fix" --invert-grep"就可以了,看blame也類似但要用到git blame|grep -v。至於回滾不回滾、找不著背鍋俠已經不在這題里了,是我隨便舉例太弱了。
而我回題主其實就是說,不要管log臟,會用的人看log看不到臟。然而保留了原貌時會用的人想看到臟在何處也就有證據可依。
不過,突然發現做了四次無用功。用git是個很私人的行為。贊。bye。
愛慕虛榮都出來了,果然是有人的地方就有江湖。
禁止使用 git commit -m
git rebase -i
老闆問我如何能做到 commit 這麼少,我說我幾乎每次做 feature 完了都是先 rebase 再 squash,下次我注意 ...
git rebase -i HEAD~{int}
Then use squash to meld commits.
不懂清爽是什麼意思。。限制許可權,禁止提交代碼看起來更清爽。
https://github.com/angular/angular.js/ 的commit 更贊,使用了commitizen插件來做commit控制,這樣的commit甚至可以用來生成非常漂亮的changelog
git merge --no-commit --squash branch_name
是不是只要是中文,就賊難看 -,-
推薦閱讀: