天天看點

送給你 12 個 Git 使用技巧!

使用這12個Git的訣竅與技巧來令你的版本控制經驗更加有用。

Git,一個分布式版本控制系統,它已經成為了開源世界的源碼控制預設工具,在4月7号12歲了。但是使用Git中更另人沮喪的是,你需要了解多少才能讓你更有效的使用它。同時這也是使用Git中比較美妙的一件事,因為沒有什麼比發現一個新的小技巧來簡化或提高你的工作流的效率更加令人快樂了。

為了紀念Git的12歲生日,這篇文章提供12個訣竅與技巧來讓你的Git經驗更加有用和強大,從一些你可能會忽視的基礎開始到一些真正的強大技巧!

1. 你的 ~/.gitconfig 檔案

在第一次用git指令來送出一個倉庫的修改,你可能會首先看到像下面這種内容:

Please tell me who you are.
Run
  git config --global user.email "[email protected]"
  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. 你的倉庫的.gitconfig檔案

在之前的技巧中,你可能會想知道在git config 指令中的—global辨別是做什麼的。它告訴Git更新“global”配置,也就是~/.gitconfig發現的這個配置。當然,擁有一個全局的配置代表了一個本地配置,而且足夠肯定的是,如果你省略—global選項,git config 會更新這個倉庫自己的配置,這個配置檔案存儲在.git/config。

在.git/config中設定的選項會推翻在~/.gitconfig檔案中的對應設定。是以,例如,如果你需要在一個特定的倉庫中使用一個不同的郵箱位址,你可以運作“git config user.email "[email protected]"”。然後,你在這個倉庫中送出會使用你單獨配置的這個郵箱位址。如果你使用一個工作的電腦在開源項目中工作,但是希望在這個項目中使用個人的郵箱位址,而其他在主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檔案,INI是一種帶有特定段落的基礎鍵值對檔案格式。添加一個别名時,你将改變[alias]段落。例如:上面提到的定義相同的git st别名,需要添加下面這段代碼:

[alias]
st = status      

(如果已經有了[alias]這個段落,隻需要在這個段落中添加到第二行)

4. shell指令中的别名

别名不僅僅是運作其他Git子指令——你也可以定義别名,這些别名可以運作其他shell指令。這是一個很好的方法來處理一個重複的、罕見的、複雜的任務:一旦你已經想到第一次怎麼做,那就使用一個别名儲存這個指令。例如,我有幾個倉庫是我fork了一個開源項目,而且在本地做了一些修改,這些修改不用貢獻給這個項目。在項目的持續的開發的過程中我想保持最新的版本,同時保留我的本地修改。為了完成這個想法,我需要定期地從upstream倉庫中合并這些修改到我的fork——我定義一個别名“upstream-merge”來完成這個操作。定義如下:

upstream-merge = !"git fetch origin -v && git fetch upstream -v && git merge upstream/master && git push"      

别名定義開始的這個“!”是告訴Git來通過shell運作這個指令。這個例子包括了運作一些git指令,但是使用這種方式定義别名可以運作任何shell指令。

(注意:如果你想複制我的upstream-merge别名,你将需要确認你有一個Git remote命名為upstream來指定這個你fork的upstream倉庫。你可以通過“git remote add upstream ”來添加一個。)

5. 可視化送出圖

如果你從事的是一個有很多分支活動的項目,有時可能很難掌握所有正在發生的工作以及它們之間的相關性。各種GUI工具可讓你弄清楚不同分支的概況以及在所謂的“送出圖”中送出記錄。例如,以下是我使用 GitLab 送出圖檢視器進行可視化的一個存儲卡的部分截圖:

送給你 12 個 Git 使用技巧!

John Anderson, CC BY

如果你是專注于指令行的使用者,就可以不在多個工具之間切換導緻分心,這個工具在指令行上實作了類似圖形界面的送出視圖。通過 -- graph 參數擷取 git 的記錄:

送給你 12 個 Git 使用技巧!

下面的指令可以得到一樣的倉庫可視化片段:

git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative      

--graph 選項将圖表添加到日志的左側, --abbrev-commit 存儲送出使用了 SHA 方法, --date=relative 表達式用相對的術語來表示日期,并且 --pretty 以 bit 格式處理自定義格式。我知道 git lg 的别名,它是我最常運作的10個指令之一。

6. 更優雅的強制推送(force-push)

有時,就跟你盡量避免使用它一樣困難的是,你會發現你需要運作 git push --force 來覆寫你倉庫的遠端副本上的曆史記錄。你可能已得到了一些回報,他們會要求你進行互動式的變基(rebase),或者你可能已經搞砸了,并且希望隐藏證據。

當他人在倉庫的遠端副本的同一分支上進行改動後,會發生強制推送的風險。當你強制推送已重寫的曆史記錄時,某些送出将會丢失。這是 git push --force-with-lease 出現的原因 - 如果遠端分支已更新,它不會允許你執行強制推送,這将確定你不會丢棄他人的工作。

7. git add -N

你是否使用過git commit -a在一次行動中送出你所有未完成的修改,隻有在你push完你的送出後才發現git commit -a忽略了新添加的檔案?解決這個問題你可以用git add -N(“通知”)來告訴Git你想把新添加的檔案包含在送出中在你第一次實際送出之前。

8. git add -p

一最佳的實踐為當使用Git時確定每個送出隻包含一個邏輯更改--不管是修複一個bug還是(實作)一個新功能。然而, 有時當你工作 ,會在你的倉庫中出現一個以上的修改 送出 。你怎麼樣把事情分開,使每個送出隻包含适當的修改呢?git add --patch來解救!

這個标志将會使git add指令檢視你工作副本中所有的變更,詢問你是否願意将它送出,跳過,或者推遲決定(還有其他一些更強大的選項,你可以通過在運作這指令後選擇?來檢視)。git add -p是一個神奇的工具來生産結構良好的送出。

9. git checkout -p

與 git add -p類似,git checkout指令将使用 --patch 或 -p 選項,這會使 git 在本地工作副本中展示每個“大塊”的改動,并允許丢棄對應改動 —— 簡單地說就是恢複本地工作副本到你改變之前的狀态。

某些場景下這非常有用,例如,在你跟蹤一個 bug 時引入了一堆調試日志語句,在修正了這個 bug 之後,你可以先使用 git checkout -p 删除所有新加的調試日志,之後使用 git add -p 來添加 bug 修複。沒有比組合一個極好的、結構良好的送出更令人滿意的了

10. Rebase with command execution

有些項目有一條規則,即存儲庫中的每個送出都必須處于可工作狀态 - 也就是說,在每次送出時,代碼應該是可編譯的,或運作測試套件應該不會失敗的。當你在某分支上工作時間長時,但如果你最終因為某種原因需要rebase時,那麼跳過每個變基後的送出以確定你沒有意外引入一個中斷是有些冗長乏味的。

幸運的是,git rebase已經支援了-x或--exec選項。git rebase -x 将在每次送出應用到rebase後運作該指令。是以,例如,如果你有一個項目,其中npm run tests會運作你的測試套件,那麼在rebase期間應用每次送出後,git rebase -x npm run tests将會運作測試套件。這使你可以檢視測試套件是否在任何變基後的送出中有失敗情況,是以你可以確定測試套件在每次送出時仍能通過。

11. 基于時間修改的指南

很多Git子指令都接受一個修正的參數來決定指令作用于倉庫的哪個部分,可能是某次特定的送出的 sha1 值,或者一個分支的名稱,又或者是一個符号性的名稱如 HEAD(代表目前檢出分支最後一次的送出),除了這些簡單的形式以外,你還可以附加一個指定的日期或時間作為參數,表示“這個時間的引用”。

這個功能在某些時候會變得十分有用,比如當你處理最新出現的 bug,自言自語道:“這個功能明明昨天還是好好的,到底又改了些什麼”,不用盯着滿屏的 git 日志的輸出試圖弄清楚什麼時候更改了送出,您隻需運作 git diff HEAD@{yesterday},會看到從昨天以來的所有修改,這也适用于較長的時間段(例如 git diff HEAD@{'2 months ago'}) ,以及一個确切的日期(例如git diff HEAD@{'2010-01-01 12:00:00'})。

您還可以将這些基于日期的修改參數與使用修正參數的任何 Git 子指令一起使用。在 gitrevisions 手冊頁中有關于具體使用哪種格式的詳細資訊。

12. 全知的 reflog

你是不是試過在 rebase 時幹掉過某次送出,後來又發現你需要保留這次送出的一些東西?你可能覺得這些送出的東西已經永遠找不回來了,隻能從頭再來了。其實不然,但如果你在本地工作副本中送出了,送出就會進入到 "引用日志" ,你仍然可以通路到。

運作 git reflog 将在本地工作副本中顯示目前分支的所有活動的清單,并為您提供每個送出的 SHA1 值。一旦發現你 rebase 時放棄的那個送出,你可以運作 git checkout 來檢出該次送出,複制好你需要的資訊,然後再運作 git checkout HEAD 傳回到分支最新的送出去。

希望這些技巧中至少有一個能教你一些關于 Git 的新知識,Git 已經 12 歲了,在這個持續創新,不斷添加新特性的項目裡,你最喜歡哪個技巧?

繼續閱讀