相見恨晚的 Git 命令動畫演示,一看就懂!

雖然 Git 是一個強大的工具,可是我以爲大部分人都會贊成我說的:它也能夠是一個……噩夢!我一直以爲,使用 Git 的時候把操做過程在腦海裏視覺化會很是有用:當我執行某個命令的時候,分支之間是如何交互的?又是如何影響提交歷史的?當我在master分支執行hard resetforce pushorigin、在.git文件夾執行rimraf的時候,爲何個人同事都哭了?css

我認爲建立一些最多見、最實用的命令的可視化示例是最佳使用指南!接下來介紹的這些命令,不少都有可選參數,用於改變命令的行爲。文中的示例只討論命令的默認行爲,不會涉及太多的配置選項。這些命令包括 mergerebaseresetrevertcherry-pickfetchpullreflog 等。git


merge

多分支能夠很是方便地將新的改動互相隔離,並確保你不會意外地將未經批准或破壞性的變動推到生產環境。一旦變動被批准,咱們就能在生產分支中獲得這些變動。工具

從一個分支獲取變動到另外一個分支的方式之一是執行git merge命令。Git 有兩類合併操做:fast-forwardno-fast-forwardfetch

這麼說你可能沒什麼概念,咱們來看看區別吧。動畫

fast-forward (--ff)

若是當前分支與即將合併過來的分支相比,沒有額外的提交,這種就是fast-forward合併。Git 很會偷懶,它會首先嚐試最簡單的方案,即fast-forward。這種合併方式不會建立新的提交,只是把另外一個分支的提交記錄直接合併到當前分支。
fast-forward翻譯

沒毛病!如今咱們在master分支上有了dev分支上的全部變動。那麼,no-fast-forward 又是什麼呢?日誌

no-fast-foward (--no-ff)

跟即將合併過來的分支比較,當前分支若是沒有額外的提交,這當然很好,但實際狀況每每不是這樣!若是咱們在當前分支上也提交了一些改動,那麼 Git 就會執行no-fast-forward合併。code

對於 no-fast-forward 合併,Git 會在當前分支上建立一個新的合併提交。該提交的父提交同時指向當前分支和合並過來的分支。
no-fast-forwardblog

也沒毛病!如今master分支上有了咱們在dev分支上作的全部變動。索引

合併衝突

雖然 Git 擅長決定如何合併分支和更改文件,但它也不是總能本身作出決定。當咱們試圖合併的兩個分支在同一文件的同一行上都有改動時,或者一個分支刪除了文件,另外一個分支又修改了它,均可能發生這種狀況。

這種狀況下,Git 會要求你幫助決定要保留哪邊的改動。假設在兩個分支上,咱們都編輯了README.md文件的第一行:

若是把dev合併到master,會致使合併衝突:你是要 Hello!呢,仍是要 Hey!

合併分支時,Git 會顯示衝突的位置。咱們能夠手動刪除不想保留的改動,而後保存,再添加改動後的文件(git add)並提交。
合併衝突

大功告成!合併衝突雖然很煩人,但也是合理的:Git 不該該自做主張保留哪邊的改動。


rebase

剛剛咱們看到了如何經過執行git merge將一個分支的改動應用到另外一個分支。另外一種方式是使用git rebase

git rebase 命令複製當前分支的提交,而後把這些提交放到指定分支之上。
git rebase

如今master分支上的全部改動都跑到dev分支上了!

merge相比,最大的區別是 Git 不會去找出哪些文件須要保留,哪些文件不須要保留。咱們要rebase的分支老是包含了咱們想要保留的最新改動。這種方式不會有合併衝突,而且保持了良好的線性 Git 歷史記錄。

這個例子演示了在 master分支上執行rebase。不過,在大項目裏你可能不會這麼作。 git rebase命令會修改項目歷史記錄,由於複製的提交會產生新的 hash。

當你在特性分支上開發時,master分支有更新的時候,rebase 頗有用。這樣你在當前分支就能拿到全部更新,避免了未來可能的合併衝突。

交互式 rebase

rebase 以前,咱們還能夠修改!這是經過交互式 rebase 實現的。交互式 rebase 也能夠用於當前正在處理的分支,在但願修改某些提交的時候。
對於即將 rebase 的提交,能夠執行 6 種操做:

  • reword:修改提交說明
  • edit:修改提交內容(amend)
  • squash:將該提交合併到前一個提交
  • fixup: 將該提交合併到前一個提交,不保留提交的日誌消息
  • exec: 在想要 rebase 的每個提交上執行命令
  • drop: 刪除提交

666!這樣,咱們就能徹底控制提交記錄了。若是想刪除某個提交,只要 drop它就好了。
Alt Text

或者,若是咱們想要把多個提交合併到一塊兒,這樣歷史記錄會更清晰,也沒問題!

交互式 rebase 給了你對想要 rebase 的提交不少控制權,哪怕是當前的活動分支。


reset

有時候咱們提交了一些改動,後來又不想要了。有多是WIP提交,也多是某個引入了 bug 的提交。這種狀況,咱們能夠執行git reset

git reset會丟棄當前全部暫存的文件,並讓咱們決定 HEAD 應該指向哪裏。

soft reset

soft resetHEAD 移動到指定的提交(或者相對於HEAD 的位置索引),同時不會丟棄這些提交帶來的改動。

假設咱們不想保留添加了style.css文件的提交9e78i,也不想保留添加了index.js文件的提交035cc 。可是,咱們卻想要保留新增的style.cssindex.js 文件。這裏用 soft reset 就很是合適。
image

執行git status,你會看到咱們依然可以查看以前提交所作的改動。這頗有用,由於這樣咱們就能繼續修改文件內容,後續再次提交了。

hard reset

有時候,咱們不想保留某些提交帶來的改動。跟 soft reset 不同,咱們再也不須要訪問這些變更了。Git 應該簡單地重置到指定的提交,而且會重置工做區和暫存區的文件。
Alt Text

Git 已經丟棄了9e78i 和 035cc 兩個提交引發的改動,並把狀態重置到了提交ec5be的位置。


revert

撤銷改動的另外一種方式是執行git revert。復原某個提交後,會建立一個新的提交,包含了恢復後的改動。

假設提交 ec5be添加了一個index.js文件。隨後,咱們發現實際上再也不須要這個改動了,就能夠恢復ec5be這個提交。
Alt Text

提交9e78i恢復了ec5be 這個提交帶來的改動。執行 git revert對於撤銷某個提交很是有用,同時又不會修改分支的歷史。


cherry-pick

當活動分支須要某個分支的某個提交包含的改動時,咱們能夠用cherry-pick命令。經過cherry-pick某個提交,在當前活動分支上會建立一個新提交,包含了前者帶來的改動。

假設 dev 分支上的提交76d12改動了index.js文件,咱們在master分支上也須要。咱們不須要整個分支上的改動,只要這個提交。
Alt Text

666,master分支如今也包含了76d12提交的改動了。


fetch

若是存在遠程分支,遠程分支可能有些提交是當前的本地分支沒有的。有多是其餘分支合併過去了,或者你的同事推送了某些改動,等等。

咱們能夠用 git fetch 把這些改動獲取到本地。這不會影響本地分支,fetch只是下載數據。
Alt Text

如今就能夠看到從最近一次推送以來的全部變更。本地有了這些新數據,咱們就能夠決定如何使用了。


pull

除了用git fetch 獲取遠程分支信息外,還能夠用git pullgit pull其實是兩個命令合而爲一:git fetchgit merge。當咱們從 origin 拉取改動時,先是像git fetch同樣獲取全部數據,而後最新改動會自動合併到本地分支。
Alt Text

這樣就跟遠程分支保持同步了,包含了全部的最新改動。


reflog

每一個人都會犯錯誤,這徹底沒有關係!有時候你可能以爲本身把倉庫搞得一團糟,只想把它刪了完事。

git reflog是個很是有用的命令,能夠顯示全部操做的日誌。包括 mergeresetrevert 等,基本上包括了對分支的任何更改。
Alt Text

若是出錯了,你能夠根據reflog提供的信息經過重置HEAD 來撤銷改動。

好比,咱們實際上並不想合併分支。當咱們執行 git reflog命令時,咱們看到在合併前倉庫位於 HEAD@{1}。咱們執行下git reset命令,讓 HEAD 從新指回原來的HEAD@{1}位置。
Alt Text

咱們能夠看到,最新的操做也記錄到reflog裏了。


總結

Git 還有不少有用的命令,篇幅所限不能一一列舉。但願經過上面這些形象的動畫演示,你可以更好地理解這些分支操做。

原文:CS Visualized: Useful Git Commands
翻譯:李中凱

看到這個很有氣質的 logo,不來關注下嗎?

相關文章
相關標籤/搜索