作者 | John Sj Anderson
譯者 | MjSeven ? ? ? 共計翻譯:28 篇 貢獻時間:80 天
這 13 個 Git 技巧將使你的版本控制技能 +1、+1、+1……
Git[1] 是一個分散式版本控制系統,它已經成為開源世界中原始碼控制的預設工具,在 4 月 7 日這天,它 13 歲了。使用 Git 令人沮喪的事情之一是你需要知道更多才能有效地使用 Git。但這也可能是使用 Git 比較美妙的一件事,因為沒有什麼比發現一個新技巧來簡化或提高你的工作流的效率更令人快樂了。
為了紀念 Git 的 13 歲生日,這裡有 13 條技巧和訣竅來讓你的 Git 經驗更加有用和強大。從你可能忽略的一些基本知識開始,並擴充套件到一些真正的高階使用者技巧!
1、 你的 ~/.gitconfig 檔案
當你第一次嘗試使用 git
命令向倉庫提交一個更改時,你可能會收到這樣的歡迎資訊:
*** Please tell me who you are.
Run
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
to set your account's default identity.
你可能沒有意識到正是這些命令在修改 ~/.gitconfig
的內容,這是 Git 儲存全域性配置選項的地方。你可以透過 ~/.gitconfig
檔案來做大量的事,包括定義別名、永久性開啟(或關閉)特定命令選項,以及修改 Git 工作方式(例如,git diff
使用哪個 diff 演演算法,或者預設使用什麼型別的合併策略)。你甚至可以根據倉庫的路徑有條件地包含其他配置檔案!所有細節請參閱 man git-config
。
2、 你倉庫中的 .git/config 檔案
在之前的技巧中,你可能想知道 git config
命令中 --global
標誌是乾什麼的。它告訴 Git 更新 ~/.gitconfig
中的“全域性”配置。當然,有全域性配置也意味著會有本地配置,顯然,如果你省略 --global
標誌,git config
將改為更新倉庫特有的配置,該配置儲存在 .git/config
中。
在 .git/config
檔案中設定的選項將改寫 ~/.gitconfig
檔案中的所有設定。因此,例如,如果你需要為特定倉庫使用不同的電子郵件地址,則可以執行 git config user.email "also_you@example.com"
。然後,該倉庫中的任何提交都將使用你單獨配置的電子郵件地址。如果你在開源專案中工作,而且希望它們顯示自己的電子郵件地址,同時仍然使用自己工作郵箱作為主 Git 配置,這非常有用。
幾乎任何你可以在 ~/.gitconfig
中設定的東西,你也可以在 .git/config
中進行設定,以使其作用於特定的倉庫。在下麵的技巧中,當我提到將某些內容新增到 ~/.gitconfig
時,只需記住你也可以在特定倉庫的 .git/config
中新增來設定那個選項。
3、 別名
別名是你可以在 ~/.gitconfig
中做的另一件事。它的工作原理就像命令列中的 shell —— 它們設定一個新的命令名稱,可以呼叫一個或多個其他命令,通常使用一組特定的選項或標誌。它們對於那些你經常使用的又長又複雜的命令來說非常有效。
你可以使用 git config
命令來定義別名 —— 例如,執行 git config --global --add alias.st status
將使執行 git st
與執行 git status
做同樣的事情 —— 但是我在定義別名時發現,直接編輯 ~/.gitconfig
檔案通常更容易。
如果你選擇使用這種方法,你會發現 ~/.gitconfig
檔案是一個 INI 檔案[2]。INI 是一種帶有特定段落的鍵值對檔案格式。當新增一個別名時,你將改變 [alias]
段落。例如,定義上面相同的 git st
別名時,新增如下到檔案:
[alias]
st = status
(如果已經有 [alias]
段落,只需將第二行新增到現有部分。)
4、 shell 命令中的別名
別名不僅僅限於執行其他 Git 子命令 —— 你還可以定義執行其他 shell 命令的別名。這是一個用來處理一個反覆發生的、罕見和複雜的任務的很好方式:一旦你確定瞭如何完成它,就可以在別名下儲存該命令。例如,我有一些復刻的開源專案的倉庫,併進行了一些本地修改。我想跟上專案正在進行的開發工作,並儲存我本地的變化。為了實現這個標的,我需要定期將來自上游倉庫的更改合併到我復刻的專案中 —— 我透過使用我稱之為 upstream-merge
的別名來完成。它是這樣定義的:
upstream-merge = !"git fetch origin -v && git fetch upstream -v && git merge upstream/master && git push"
別名定義開頭的 !
告訴 Git 透過 shell 執行這個命令。這個例子涉及到執行一些 git
命令,但是以這種方式定義的別名可以執行任何 shell 命令。
(註意,如果你想複製我的 upstream-merge
別名,你需要確保你有一個名為 upstream
的 Git 遠端倉庫,指向你已經分配的上游倉庫,你可以透過執行 git remote add upstream
來新增一個。)
5、 視覺化提交圖
如果你在一個有很多分支活動的專案上開發,有時可能很難掌握所有正在發生的工作以及它們之間的相關性。各種圖形使用者介面工具可讓你獲取不同分支的圖片併在所謂的“提交圖表”中提交。例如,以下是我使用 GitLab[3] 提交圖表檢視器視覺化的我的一個倉庫的一部分:
GitLab commit graph viewer
如果你是一個專註於命令列的使用者或者發現分支切換工具讓人分心,那麼可以從命令列獲得類似的提交檢視。這就是 git log
命令的 --graph
引數出現的地方:
Repository visualized with --graph command
以下命令視覺化相同倉庫可達到相同效果:
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)%Creset' --abbrev-commit --date=relative
--graph
選項將圖新增到日誌的左側,--abbrev-commit
縮短提交的 SHA[4]值,--date=relative
以相對方式表示日期,以及 --pretty
來處理所有其他自定義格式。我有個 git lg
別名用於這個功能,它是我最常用的 10 個命令之一。
6、 更優雅的強制推送
有時,你越是想避開越避不開,你會發現你需要執行 git push --force
來改寫倉庫遠端副本上的歷史記錄。你可能得到了一些反饋,需要你進行互動式變基,或者你可能已經搞砸了,並希望隱藏“罪證”。
當其他人在倉庫的遠端副本的同一分支上進行更改時,會發生強制推送的危險。當你強制推送已重寫的歷史記錄時,這些提交將會丟失。這就是 git push --force-with-lease
出現的原因 -- 如果遠端分支已經更新,它不會允許你強制推送,這確保你不會丟掉別人的工作。
7、 git add -N
你是否使用過 git commit -a
在一次行動中提交所有未完成的修改,但在你推送完提交後才發現 git commit -a
忽略了新新增的檔案?你可以使用 git add -N
(想想 “notify”) 來解決這個問題,告訴 Git 在第一次實際提交它們之前,你希望在提交中包含新增檔案。
8、 git add -p
使用 Git 時的最佳做法是確保每次提交都只包含一個邏輯修改 —— 無論這是修複錯誤還是新增新功能。然而,有時當你在工作時,你的倉庫中的修改最終應該使用多個提交。你怎樣才能設法把事情分開,使每個提交只包含適當的修改呢?git add --patch
來拯救你了!
這個標誌會讓 git add
命令檢視你工作副本中的所有變化,併為每個變化詢問你是否想要將它提交、跳過,或者推遲決定(你可以在執行該命令後選擇 ?
來檢視其他更強大的選項)。git add -p
是生成結構良好的提交的絕佳工具。
9、 git checkout -p
與 git add -p
類似,git checkout
命令也接受 --patch
或 -p
選項,這會使其在本地工作副本中顯示每個“大塊”的改動,並允許丟棄它 —— 簡單來說就是將本地工作副本恢復到更改之前的狀態。
這真的很棒。例如,當你追蹤一個 bug 時引入了一堆除錯日誌陳述句,修正了這個 bug 之後,你可以先使用 git checkout -p
移除所有新的除錯日誌,然後 git add -p
來新增 bug 修複。沒有比組合一個優雅的、結構良好的提交更令人滿意!
10、 變基時執行命令
有些專案有一個規則,即儲存庫中的每個提交都必須處於可工作狀態 —— 也就是說,在每次提交時,應該可以編譯該程式碼,或者應該執行測試套件而不會失敗。 當你在分支上工作時,這並不困難,但是如果你最終因為某種原因需要變基時,那麼需要逐步完成每個變基的提交以確保你沒有意外地引入一個中斷,而這個過程是乏味的。
幸運的是,git rebase
已經改寫了 -x
或 --exec
選項。git rebase -x
將在每個提交在變基中被應用後執行該命令。因此,舉個例子,如果你有一個專案,其中使用 npm run tests
執行你的測試套件,git rebase -x npm run tests
將在變基期間每次提交之後執行測試套件。這使你可以檢視測試套件是否在任何變基的提交中失敗,以便你可以確認測試套件在每次提交時仍能透過。
11、 基於時間的修訂取用
很多 Git 子命令都接受一個修訂引數來決定命令作用於倉庫的哪個部分,可以是某次特定的提交的 SHA1 值,一個分支的名稱,甚至是一個符號性的名稱如 HEAD
(代表當前檢出分支最後一次的提交),除了這些簡單的形式以外,你還可以附加一個指定的日期或時間作為引數,表示“這個時間的取用”。
這個功能在某些時候會變得十分有用。當你處理最新出現的 bug,自言自語道:“這個功能昨天還是好好的,到底又改了些什麼”,不用盯著滿屏的 git log
的輸出試圖弄清楚什麼時候更改了提交,你只需執行 git diff HEAD@{yesterday}
,看看從昨天以來的所有修改。這也適用於更長的時間段(例如 git diff HEAD@{'2 months ago'}
),以及一個確切的日期(例如 git diff HEAD@{'2010-01-01 12:00:00'}
)。
你也可以將這些基於日期的修訂引數與使用修訂引數的任何 Git 子命令一起使用。在 gitrevisions
手冊頁中有關於具體使用哪種格式的詳細資訊。
12、 全知的 reflog
你是不是試過在變基時幹掉過某次提交,然後發現你需要保留那個提交中一些東西?你可能覺得這些資訊已經永遠找不回來了,只能重新建立。但是如果你在本地工作副本中提交了,提交就會被新增到取用日誌(reflog)中 ,你仍然可以訪問到。
執行 git reflog
將在本地工作副本中顯示當前分支的所有活動的串列,併為你提供每個提交的 SHA1 值。一旦發現你變基時放棄的那個提交,你可以執行 git checkout
跳轉到該提交,複製任何你需要的資訊,然後再執行 git checkout HEAD
傳回到分支最近的提交去。
13、 自己清理
哎呦! 事實證明,我的基本數學技能不如我的 Git 技能。 Git 最初是在 2005 年釋出的,這意味著它今年會變成 13 歲,而不是 12 歲(LCTT 譯註:本文原來是以 12 歲生日為題的)。為了彌補這個錯誤,這裡有可以讓我們變成十三歲的第 13 條技巧。
如果你使用基於分支的工作流,隨著在一個長期專案上的工作,除非你在每個分支合併時清理乾凈,否則你最終會得到一大堆分支。這使得你難於找到想要的分支,分支的森林會讓你無從找起。甚至更糟糕的是,如果你有大量活躍的分支,確定一個分支是否被合併(可以被安全刪除)或仍然沒有被合併而應該留下會非常繁瑣。幸運的是,Git 可以幫到你:只需要執行 git branch --merged
就可以得到已經被合併到你的當前分支的分支串列,或者 git branch --no-merged
找出被合併到其它分支的分支。預設情況下這會列出你本地工作副本的分支,但是如果你在命令列包括 --remote
或 -r
引數,它也會列出僅存於遠端倉庫的已合併分支。
重要提示:如果你計劃使用 git branch --merged
的輸出來清理那些已合併的分支,你要小心它的輸出也包括了當前分支(畢竟,這個當前的分支就被合併到當前分支!)。確保你在任何銷毀動作之前排除了該分支(如果你忘記了,參見第 12 條技巧來學習 reflog 怎樣幫你把分支找回來,希望有用……)。
以上是全部內容
希望這些技巧中至少有一個能夠教給你一些關於 Git 的新東西,Git 是一個有 13 年曆史的專案,並且在持續創新和增加新功能中。你最喜歡的 Git 技巧是什麼?
via: https://opensource.com/article/18/4/git-tips
作者:John SJ Anderson[6] 選題:lujun9972 譯者:MjSeven 校對:wxy
本文由 LCTT 原創編譯,Linux中國 榮譽推出