爲何?還有怎樣才能保持你的 Git 提交歷史清晰?

文章轉發自專業的Laravel開發者社區,原始連接: https://learnku.com/laravel/t...

提交是 Git 倉庫的重要組成部分之一,不只於此,提交信息  貫穿於 Git 倉庫的整個生命週期。隨着項目/倉庫的發展(新特性的增長、Bugs 的修復、架構的重構),提交信息可讓咱們看到改了什麼地方以及是如何改動的。所以,這些信息以一種簡短、精確的方式反映着潛在的變化是很是重要的。css

爲何有意義的提交歷史很重要

Git 提交消息就像是你在你接觸過的代碼上面留下的指紋。任何你今天提交的代碼,一年之後當你看到這段代碼的變化時,你將感謝本身當時留下的清晰、有意義的提交信息,同時它還將使你的開發變得更爲容易。當提交被基於上下文獨立時,由某個提交引入的 Bug 將會更快被找到,而且更容易恢復到致使錯誤提交前的代碼。html

當你工做在一個大的項目中時,咱們常常更新、新增或者移動文件。確保在這種狀況下維持提交消息將很棘手,尤爲是在開發週期跨越數天,數週甚至數月的狀況下。所以,爲了簡化維護簡潔提交歷史記錄的工做,這篇文章將使用開發人員在處理 Git 倉庫時可能遇到的一些常見狀況。前端

在咱們深刻討論以前讓咱們快速的瞭解一下,在咱們假設的 Ruby 應用程序中,典型的開發工做流是什麼樣子的。laravel

注意: 這篇文章假定您瞭解基本的 Git 知識 、分支的工做方式、如何在階段中添加未提交的分支更改以及如何提交更改。若是你不瞭解這些流程, 咱們的文檔 是一個很好的起點。git

平常開發中的一天

如今,咱們正在開發一個小型的 Ruby on Rails 項目。咱們須要在首頁添加一個導航視圖,這涉及到更新和添加幾個文件。下面是整個流程的逐步細分:shell

  • 你首先從更新某個文件開始來開發新功能;咱們叫它爲application_controller.rb
  • 這個功能也須要你更新一個視圖文件:index.html.haml
  • 你添加了一個局部視圖,它會在首頁中用到:_navigation.html.haml
  • 頁面樣式也須要更新,讓它做用到咱們的視圖上:styles.css.scss
  • 新功能已經準備好全部指望的修改,該更新測試文件了;要更新的文件以下:後端

    • application_controller_spec.rb
    • navigation_spec.rb
  • 測試文件更新完畢並如期經過,如今是時候提交全部更改了!

由於全部的文件分屬於架構的不一樣區域,因此咱們對這些更改彼此隔離進行提交,確保每次提交表明一個特定的上下文,並按順序提交。我一般喜歡從後端 -> 前段的順序。首先提交大多數之後端爲中心的更改,接着是中間層,最後是在提交列表中之前端爲中心的更改。安全

  1. application_controller.rb & application_controller_spec.rbAdd routes for navigation
  2. _navigation.html.haml & navigation_spec.rbPage Navigation View
  3. index.html.hamlRender navigation partial
  4. styles.css.scssAdd styles for navigation

如今咱們已經提交了更改,咱們使用分支來建立 merge 請求。一旦建立了 merge 請求,一般會在更改合併進 master分支以前由原倉庫的擁有者進行審覈。如今咱們來了解一下,在代碼審覈期間咱們可能會面臨的不一樣狀況。架構

<span id='situation-1-i-need-to-change-the-most-recent-commit'>狀況1:我須要修改最近的提交</span>

想象一下,審閱者查看了styles.css.scss文件並建議進行更改。在這種情形下,進行更改很是簡單,由於對樣式的更改是你分支上 最近一次 提交的一部分。下面是咱們怎樣處理此次更改的步驟:app

  • 你能夠直接在你的分支上對styles.css.scss文件作必要的更改。
  • 一旦你完成了這些更改,請將這些改變添加到暫存區;運行git add styles.css.scss
  • 當這些改變被暫存以後,咱們須要將這些更改 添加 到上次的提交中;運行 git commit --amend

    • 命令分解:在這裏,咱們要求git commit命令 修改 暫存區中的最近一次提交。
  • 這將會用你已定義的 Git 文本編輯器打開最近一次提交,而且提交消息是 Add styles for navigation
  • 由於咱們只更新了 CSS 聲明,因此無需修改提交消息。此時,你只須要保存並退出 Git 爲你打開的文本編輯器,全部的更改將會反映在本次提交中。

因爲你修改了現有的提交,因此必須使用git push --force-with-lease <remote_name> <branch_name>將這些更改 強制推送 到你的遠程倉庫。這個命令將會使用咱們在本次倉庫更新後的提交,覆蓋遠程倉庫上消息爲Add styles for navigation的那次提交。

在強制推送分支時須要注意的一件事是,若是你和其餘同事工做於同一分支,當其餘人嘗試向一個剛被強制推送過的遠程分支正常推送他們的更改時,可能遇到一些麻煩。因此,請明智地使用強制推送。你能夠在 這裏 瞭解有關 Git 強制推送的更多選項。

狀況 2:我須要更正一個特定的提交

在以前的狀況下,因爲咱們只能修改最後一次提交,因此這種修復很是簡單,可是想象一下,若是審稿人建議改變_navigation.html.haml。 在這種狀況下,由於它是第二次從頂部提交,因此要想改變它不會像第一種狀況那樣直接。讓咱們仍是來看看如何處理這個問題吧:

每當在分支中進行提交時,它就被惟一的SHA1哈希字符串標識。把它看做是一個惟一的ID,它將一個提交與另外一個提交分開。經過運行git log ,您能夠查看分支中的全部提交以及它們的SHA1哈希。經過它,你會看到一個看起來有點相似下面這樣的輸出,其中最近的提交在頂部;

commit aa0a35a867ed2094da60042062e8f3d6000e3952 (HEAD -> add-page-navigation)
Author: Kushal Pandya <kushal@gitlab.com>
Date: Wed May 2 15:24:02 2018 +0530

    Add styles for navigation

commit c22a3fa0c5cdc175f2b8232b9704079d27c619d0
Author: Kushal Pandya <kushal@gitlab.com>
Date: Wed May 2 08:42:52 2018 +0000

    Render navigation partial

commit 4155df1cdc7be01c98b0773497ff65c22ba1549f
Author: Kushal Pandya <kushal@gitlab.com>
Date: Wed May 2 08:42:51 2018 +0000

    Page Navigation View

commit 8d74af102941aa0b51e1a35b8ad731284e4b5a20
Author: Kushal Pandya <kushal@gitlab.com>
Date: Wed May 2 08:12:20 2018 +0000

    Add routes for navigation

這就是 Git ReBase 命令發揮做用的地方。每當咱們但願編輯 git ReBase 的特定提交時,咱們須要先將咱們的分支從新定位爲在咱們但願編輯的提交以前,將頭移到右邊。在咱們用例中,咱們須要更改讀取的提交  Page Navigation View

這裏請注意,正確的提交哈希是咱們想要修改提交的前一個提交哈希;拷貝那個哈希,而後執行下面的步驟:

  • 變基(rebase)分支,移動到目標提交的前一次提交;運行git rebase -i 8d74af102941aa0b51e1a35b8ad731284e4b5a20

    • 命令分解:這裏咱們運行rebase命令的 交互 模式,並提供了要變基的提交哈希。
  • 這將在 Git 交互模式運行 rebase 命令,並打開你的文本編輯器,裏面顯示了正在變基的提交(8d74af)以後 的全部提交。它看起來就像這樣:
pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
pick aa0a35a867e Add styles for navigation

# 在 8d74af10294 上變基 8d74af10294..aa0a35a867e 範圍(3次提交)
#
# 命令:
# p, pick = 保留本次提交
# r, reword = 保留本次提交,但要修改提交消息
# e, edit = 保留本次提交,但暫停下來進行修改(不僅修改提交消息)
# s, squash = 保留本次提交,但合併到前一次提交
# f, fixup = 與「squash」相似,但丟棄本次提交消息
# x, exec = 運行 shell 命令(本行的剩餘內容)
# d, drop = 刪除本次提交
#
# 這些行能夠重排順序,並自頂向底依次執行。
#
# 若是你刪除一行,那次提交將會丟失。
#
# 若是你刪除全部行,本次變基(rebase)將會停止。
#
# 注意空提交已註釋掉。

注意每一個提交前面都有一個單詞pick,下面的註釋是咱們可能用到的關鍵字。由於咱們想要 編輯 某此提交(4155df),因此須要把pick 4155df1cdc7 Page Navigation View改爲edit 4155df1cdc7 Page Navigation View。保存修改,而後退出編輯器。

如今你的分支就重置到了所作的修改包含_navigation.html.haml的時刻。打開文件根據審覈反饋執行須要的修改。一旦修改完畢,經過運行git add _navigation.html.haml命令進行暫存。

既然咱們暫存了這些改變,如今是時候把 HEAD 分支移回咱們的原始提交了(包含咱們添加的最新修改),運行git rebase --continue,這將會在終端打開你的默認編輯器而後顯示在變基(rebase)期間的提交消息;Page Navigation View。若是但願的話,你能夠改變這個提交消息,可是咱們如今讓它保持現狀,保存並退出編輯器。此刻,Git 會從新播放你所編輯的提交以後的全部提交,而且如今的HEAD分支回溯到了咱們原始的頂部提交。

因爲咱們再次修改了遠程倉庫中已存在的一次提交,因此須要使用git push --force-with-lease <remote_name> <branch_name>強制推送分支。

<span id='situation-3-i-need-to-add-remove-or-combine-commits'>場景3:我須要添加、刪除或者合併 commit</span>

一個很常見的場景就是爲了修復以前提交的內容,你已經 commit 了幾回。如今咱們來儘量地減小 commit 的次數並把它們和原來的提交合並起來。

你須要作的就是就像你在其餘場景同樣啓動交互式 rebase。

pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
pick aa0a35a867e Add styles for navigation
pick 62e858a322 Fix a typo
pick 5c25eb48c8 Ops another fix
pick 7f0718efe9 Fix 2
pick f0ffc19ef7 Argh Another fix!

假設你如今想要把這些提交記錄都合併到 c22a3fa0c5c Render navigation partial。你只須要作到:

  1. 將 fixes 向上移動直到它們位於你但願保留的最後的提交下面
  2. 把每個 fix 的 pick 改成 squash 或者 fixup

注意: squash 保留了描述中的提交註釋。 fixup 不會保留提交的註釋而只保留原始註釋。

你會獲得這樣的結果:

pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
fixup 62e858a322 Fix a typo
fixup 5c25eb48c8 Ops another fix
fixup 7f0718efe9 Fix 2
fixup f0ffc19ef7 Argh Another fix!
pick aa0a35a867e Add styles for navigation

保存更改,退出編輯,你就完成了!這是產生的歷史:

pick 4155df1cdc7 Page Navigation View
pick 96373c0bcf Render navigation partial
pick aa0a35a867e Add styles for navigation

和之前同樣,你要作的就是 git push --force-with-lease <remote_name> <branch_name>,改變就生效了。

若是你想要徹底地刪除一個提交,把 squash 或者 fixup 換成 drop 或者乾脆刪掉那一行。

避免衝突

爲了不衝突,請確保你移動到的目標沒有編輯到同一個文件。

pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
fixup 62e858a322 Fix a typo                 # this changes styles.css
fixup 5c25eb48c8 Ops another fix            # this changes image/logo.svg
fixup 7f0718efe9 Fix 2                      # this changes styles.css
fixup f0ffc19ef7 Argh Another fix!          # this changes styles.css
pick aa0a35a867e Add styles for navigation  # this changes index.html (no conflict)

技巧: 快速 fixup

若是你清楚知道你將 fixup 哪個提交,你不須要浪費時間構思一個臨時的名稱,如: "Fix 1", "Fix 2", ..., "Fix 42"。你可使用如下的方法:

第一步:開始使用 --fixup

在你 stage 而且修復了你想要的內容時,使用如下命令:

git commit --fixup c22a3fa0c5c

(注意這個 hash 信息對應 c22a3fa0c5c Render navigation partial)

以上命令會生成一個 commit:: fixup! Render navigation partial.

第二步:召喚你的好朋友 --autosquash

簡單的交互性 rebase,如下命令會讓 git 在正確的位置裏設置 fixup:

git rebase -i 4155df1cdc7 --autosquash

如今你的歷史是:

pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
fixup 62e858a322 Fix a typo
fixup 5c25eb48c8 Ops another fix
fixup 7f0718efe9 Fix 2
fixup f0ffc19ef7 Argh Another fix!
pick aa0a35a867e Add styles for navigation

你還可使用 git rebase --autosquash 命令來直接跳過 review 階段,可是通常建議除非你感受特別安全,不然仍是少用爲妙,由於你沒機會 review 到具體變動。

<span id='situation-4-my-commit-history-doesnt-make-sense-i-need-a-fresh-start'>場景 4: 個人提交信息太亂了,我須要從新開始</span>

若是你在開發一個大功能,有時候你在分支裏(如:add-page-navigation)留下不少 commit ,而你不喜歡讓這些 commit 進入主分支,接下來我教你一個方法:

  • 在開始以前,請確保你的分支已是最新的而且不會與  master 分支衝突;
  • 當你 checkout 在 add-page-navigation 分支下時,可使用 git rebase master 或者 git merge master 命令來保持與 master 的更新; 
  • 接下來使用命令建立補丁文件  git diff master add-page-navigation > ~/add_page_navigation.patch

    • 命令分解:咱們使用 Git 的 diff 功能,對 master 分支和 add-page-navigation 分支作了一個 diff 操做,而且將結果輸出到  ~/add_page_navigation.patch 文件中。
  • 文件路徑你能夠隨意設置;
  • 命令成功執行後沒報錯的話,補丁文件就算建立成功了;
  • 如今咱們運行 git checkout master 切換到  master 分支上;
  • 使用命令 git branch -D add-page-navigation 刪除本地分支 add-page-navigation (咱們已經有補丁文件了,不要懼怕);
  • 接下來使用命令 git checkout -b add-page-navigation 建立一個新的分支;
  • 此時咱們在全新的 add-page-navigation 分支上,此分支沒有任何更改;
  • 最後,使用命令 git apply ~/add_page_navigation.patch 來應用補丁裏的修改;
  • 全部的修改都會應用上,而且會以 未提交 的狀態顯示着;
  • 此時你能夠選擇將全部更改一次提交,或者單個文件提交,隨意操做便可。

就跟以前遇到的場景同樣,咱們修改了整個分支,你須要作一個 force push 了。

總結

儘管咱們已經介紹了使用 Git 進行平常工做流程中出現的大多數常見的狀況,但重寫 Git 歷史是一個巨大的話題,而且當你在熟悉上述提示時,你能夠在 Git 官方文檔 學習圍繞該主題的更高級概念。祝你愉快的學習 Git。

相關文章
相關標籤/搜索