高富帥們的Git技巧

譯者序​

Git是一個分佈式版本控制系統,擁有許多神奇而易用的特性(好比:分支),這讓它能夠輕鬆適應各類工做流程。這篇文章不涉及Git的基本使用,而是介紹了一些高級卻有用的小技巧。讓咱們一塊兒來看看高富帥們的Git技巧,準備好逆襲吧!linux

以「塊」形式暫存你的改動

你確定已經很熟悉的使用git add命令來將改動暫存到暫存區(staging area)了。你可能也會偶然由於兩個不一樣的緣由而作了一次改動,卻沒有分別提交(僅僅提交了一次),因此,當你執行git log時,會看到諸如這樣的提交信息:「修改X,改動無關的Y」。若是這看起來像是你的工做方式,交互式add將是你的有力工具。git

交互式add(或者叫add塊),將會一個塊一個快的循環你的改動。使用命令git add -p時,你能夠在每一個改動「塊」(即:連續的改動會被組織到一塊兒)時進行一些選擇,好比:切分當前塊爲更小的塊、跳過一個改動塊、甚至手動的編輯該塊,你能夠敲入?來查看全部該命令提供的選項。github

開始以「塊」形式暫存改動簡單到只需一條命令(括號部分替換爲特定文件):安全

git add -p (path/file)

譯者注:感受這條命令日常用的較少,我遇到須要分別提交的狀況時,都是手動來add而後提交,該命令是這種方法的高級版本。咱們日常可能對提交歷史的重視比較低,經常出現一些無用的、無心義的提交信息,能夠試試這條命令。分佈式

切換到最後所在分支

做爲一個善良的碼農,當你須要快速作些修正或是清理工做時,你都應該花些時間來對待。若是你的工做流程是十分依賴分支的話(譯者注:強烈建議如此),你可能不但願無關的修正影響到如今正在進行功能開發的分支。這意味着,你應該使用git stash命令來暫時存放你的改動,而後切到master分支(譯者注:或是其它啥分支,我通常是取名爲fix),在那個分支進行修正。(譯者注:修正完了,能夠切回正在進行功能開發的分支,執行git stash pop來彈出以前暫存的改動,繼續進行開發)。在不一樣分支間切換很乏味,幸虧這裏有個快捷命令能夠切換到你最後所在的分支:工具

git checkout -

這個語法對於使用linux的高富帥們來講必定不陌生,cd命令有個相似的縮寫cd -,表示切換到你最後所在的目錄。當你須要切回功能開發分支時,你根本不用關心那個分支是啥名,只需git checkout -。post

譯者注:感受tab鍵的自動補全也挺好用的,不過這條命令能夠少敲點字。有了這條命令,媽媽不再用擔憂個人分支切換了。版本控制

顯示哪些分支被合併了(或是哪些沒有被合併)

在使用git時,你可能會建立許多分支,致使執行git branch命令列出分支時變得有些雜亂。因而,你想處理那些已經合併到master分支的無用分支,可是,當你執行git checkout -d 來刪除分支時可能會遇到「麻煩」(譯者注:git會拒絕刪除未合併的分支並提示你),若是使用如下命令,你將再也不須要三思然後刪,能夠自信的處理那些已經合併了的分支。code

若是你想要看看你的本地分支裏哪些分支是已經合併進你當前所在的分支時,可使用:orm

git branch --merged

反過來,若是須要查看哪些分支尚未合併進當前所在的分支,可使用:

git branch --no-merged

結合高富帥的UNIX工具,你能夠輕鬆的刪除那些已經合併了的分支:

git branch --merged | xargs git branch -d

譯者注:xargs是UNIX平臺的一個工具,它的做用是將參數列表轉換成小塊分段傳遞給其餘命令,以免參數列表過長的問題。若是git branch --merged顯示的是a,b,c三個分支已經合併,上面的命令會轉換爲:git branch -d a b c。更多xargs的信息:http://zh.wikipedia.org/wiki/Xargs

從另外一分支獲取文件內容而不用切換分支

設想你正在進行重構,你建立了好幾個分支並在各分支下進行改動。這時,你想把另外一個分支裏某一個文件的改動引入到當前工做的分支裏,爲了達到目的你可能須要好幾步:git stash你的改動;切換到那個分支;獲取文件的改動;切回工做分支(固然是使用git checkout -);繼續進行編輯(譯者注:別忘了git stash pop)。可是,你也能夠直接檢出另外一分支的文件,而且合併到你當前所在的工做分支,使用命令(括號部分替換爲對應的分支和文件):

git checkout (branch) -- (path/file)

以最後提交排序的Git分支

想必你已經使用上面的tip處理了雜亂的分支,有一些是用--merged選項標誌來清理的吧。那其它的分支咋辦呢?你咋知道哪些是有用的,哪些是徹底過時無用的呢?git for-each-ref命令能夠打印出一個列表,該列表顯示每一個分支最後一次提交的引用(reference)信息。咱們能夠自定義輸出來包含一些有用的信息,更重要的是咱們還能夠按日期排序。可使用下面的命令來輸出一個列表,該表將顯示按時間前後排序的每一個分支的最後提交信息、提交者等信息:

git for-each-ref --sort=-committerdate --format="%(committername)@%(refname:short) [%(committerdate:short)] %(contents)"

還能夠把它定義在gitconfig裏:

[alias]
  latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"

譯者注:定義後就只需執行git latest了。注意雙引號須要轉義!

在玻璃房內的人們別用git blame

或者說,在玻璃房內的人們不該該直接使用git blame而不帶下文的選項標誌。(譯者注:玻璃房內的人是徹底能被別人看到的人。這裏的意思應該是想說,你每一次提交的變更都會被記錄到git倉庫的歷史,對於git倉庫來講,你就像是住在玻璃房裏的人,沒有任何祕密,你根本逃不過git的」責問「)git blame是頗有用的命令,它就像使用科學來證實你是正確的!可是請注意,許多文件的變更是很表面的,發現問題的來源須要更多的探索。像是移除空白、移動內容到新行、移動內容到另外一文件等動做均可以使用選項來忽略掉,以便更容易的找到代碼變更的始做俑者。

在你blame(責備)他人前,記得用如下命令看看結果:

git blame -w  # 忽略移除空白這類改動
git blame -M  # 忽略移動文本內容這類改動
git blame -C  # 忽略移動文本內容到其它文件這類改動

譯者注:git blame用來顯示一份文件每一行的最近一次提交的提交hash值和提交者。當你跟別人說「我真的沒改過這個文件啊」以前,就得git blame下。

在整個git倉庫提交歷史中找尋內容(而後刪掉它)

你有時可能須要查找一行你寫的代碼,可是就是沒法找到。它可能安放在了一些已經被遺忘的分支,或是刪除了好久,又或是就在那顯而易見的地方。不管哪一種方式,你均可以經過一些命令在整個git倉庫的歷史中搜尋特定的字符串。

首先,咱們須要拿到全部的提交,而後,使用git grep來搜尋特定的字符串。以下:

git rev-list --all | xargs git grep -F '搜尋的字符串'

你可能有一個粗心的朋友不當心在倉庫裏提交了諸如,用戶名、密碼、外婆的大蒜食譜等敏感信息。首先,他們得更改用戶名、密碼(並向外婆道歉)。而後,你須要搜尋這些得罪人的文件,並將他們從整個倉庫的歷史裏抹去(這聽起來好像很容易)。通過這個處理,那些執行git pull的夥計們就會發現全部提交中包含的敏感信息都被清理乾淨了,而那些沒有合併你的遠程改動的傢伙仍是擁有敏感信息(因此,千萬別忘記先改用戶名和密碼)。咱們來看看怎麼操做。

首先,重寫每一個分支的歷史,移除敏感信息:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch (filename)' --prune-empty --tag-name-filter cat -- --all

而後,將記錄敏感信息的文件增長到.gitignore文件,並提交(括號部分替換爲對應文件名):

echo (filename) >> .gitignore
git add .gitignore
git commit -m "Add sensitive (filename) file to gitignore"

接着,因爲咱們改寫了歷史,咱們須要「強制」的將改動推到遠程:

git push origin master --force
# 譯者注:還可使用命令
git push origin +master

最後,這個文件還在你的本地倉庫裏,還須要將它徹底抹除:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

你這粗心的朋友從敏感文件的危機中解脫,而你用你高超的git知識成功逆襲,成爲了他的英雄!

譯者注:一天,妹子叫我去她家幫她把她的三圍信息從git倉庫的歷史裏徹底刪除,我研究了好久不得要領。妹子說,不如咱們作點其它的事吧。我以爲個人git知識被她鄙視了,堅決的說,我必定要把它刪掉!而後,就沒有而後了… …

忽略文件跟蹤

在和他人合做時可能經常意味着你須要更改一些配置才能讓應用在環境裏跑起來,這時,經常會不當心把這些只對你有意義的配置文件也給提交了。爲了避免再經常關注這些文件,看着它們在git status時放肆的顯示「modified」,你能夠告訴git忽略它們的改動。這種方式,能夠當作是一種和倉庫綁定的gitignore文件(括號部分替換爲對應文件):

git update-index --assume-unchanged (path/file)

譯者注:感受,.gitignore文件更方便和好理解。

讓分支的歷史歸零

無論出於啥理由,有時從頭開始正是你須要的。也許是你接手了一個不確信能安全開源的代碼倉庫;也許是你要着手作些全新的事情;也許是你想建立用於其它目的一個新分支,又但願繼續在倉庫裏維護它(好比:github頁面,項目的文檔一類的東西)。上述的情形下,你能夠很是簡單的建立一個沒有提交歷史的分支(括號部分替換爲對應分支):

git checkout --orphan (branch)

譯者注:咱們知道,分支只是對提交的一個引用,因此,每當從當前分支建立一個分支時,被建立的分支都會延續以前的歷史,可是這種方式卻不會,是一個完徹底全乾淨的git分支,沒有任何的提交!

你必定離不開的別名

不討論能節省大量敲擊時間的「git別名(git alias)」技巧的git文章必定都是在耍流氓。中止輸入冗長的命令,使用超級有用的別名吧!git別名能夠加到.gitconfig文件裏,或是使用命令(譯者注:本質就是改寫.gitconfig命令)來增長(括號部分替換爲別名和對應的命令):

git config --global alias.(name) "(command)"
  1. 在依賴分支的工做流程中,你經常要在不一樣分支間切換,每次敲擊節約你6個字母。

    co = checkout
  2. 在提交前瞧瞧你將要提交的都有什麼改動是一個好習慣,這能夠幫助你發現拼寫錯誤、不當心的提交敏感信息、將代碼組織成符合邏輯的組。使用git add暫存你的改動,而後使用git ds查看你將要提交的改動動。

    ds = diff --staged
  3. 你可能十分熟悉git輸出的詳細狀態信息了,當到達必定境界時,你可能須要忽略全部那些描述,直擊問題的核心。這個別名輸出將輸出git status的簡短形式和分支的詳細信息。

    st = status -sb
  4. 你是否在提交後才發現忘記git add某個文件了,或是提交了纔想再改動些啥?amend(修正)暫存區到最近的一次提交吧。(譯者注:這個命令不太好理解,--amend是重寫提交歷史,-C是重用某次提交的提交信息。場景是當你提交完了發現還有些改動沒提交,又不想寫什麼「改動了X,再次提交」這種狗血的提交信息。從新git add並git amend後,重用上次的提交信息再次提交,替換上次的不完整提交。特別注意--amend重寫了提交,若是你已經push到遠程了,慎用這條命令!)

    amend = commit --amend -C HEAD
  5. 有時上面的修正可能很差使了,你須要undo(撤銷)。undo會回退到上次提交,暫存區也會回退到那次提交時的狀態。你能夠進行額外的改動,用新的提交信息來再次進行提交。

    undo = reset --soft HEAD^
  6. 維護一個多人編輯的代碼倉庫經常意味着試着發現何人在改動什麼,這個別名能夠輸出提交者和提交日期的log信息。

    ls = log --pretty=format:'%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green) [%cn]' --decorate --date=short
  7. 這個別名用來在一天的開啓時回顧你昨天作了啥,或是在早晨刷新你的記憶(括號內替換爲本身的email)。

    standup = log --since '1 day ago' --oneline --author (YOUREMAIL)
  8. 一個複雜的倉庫可能很難用直線式的輸出來查看,這個別名能夠用圖表的形式向你展現提交是怎樣及什麼時候被加到當前分支的。

    graph = log --graph --pretty=format:'%C(yellow)%h %C(blue)%d %C(reset)%s %C(white)%an, %ar%C(reset)'

譯者注:我根據上面的別名進行了一些整理修改,這是我如今的.gitconfig裏的別名配置:

[alias]
  st = status -sb
  co = checkout
  br = branch
  mg = merge
  ci = commit
  ds = diff --staged
  dt = difftool
  mt = mergetool
  last = log -1 HEAD
  latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"
  ls = log --pretty=format:\"%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green)[%cn]\" --decorate --date=short
  hist = log --pretty=format:\"%C(yellow)%h %C(red)%d %C(reset)%s %C(green)[%an] %C(blue)%ad\" --topo-order --graph --date=short
  type = cat-file -t
  dump = cat-file -p

via alimama mux
做者:Chris Kelly 譯者:棲邀
英文原文

相關文章
相關標籤/搜索