[譯] Git:透過命令學概念 —— 第二部分

Git:透過命令學概念 —— 第二部分

用交互式的教程教你 Git 的原理,而非羅列經常使用命令。html

因此,你想正確地使用 Git 嗎?前端

但你確定不想僅僅學一些操做命令,你還想要理解其背後的原理,對吧?android

那麼本文就是爲你量身定作的!ios

讓咱們快點開動吧!git


本文的落筆點基於 Rachel M. Carmena 撰寫的 如何教授 Git 一文中說起的常規概念。github

網上有不少重方法輕原理的 Git 教程,但我仍是挖掘到了兼得兩者的寶貴資源(也是本教程的靈感源泉),那就是 git BookReference page後端

所以,若是你讀完了本文還意猶未盡,就快點擊上面兩個連接一探究竟吧!我真心但願本教程中介紹的概念,能幫你理解另外兩篇文章中詳解的其餘 Git 功能bash

建議按照順序閱讀本系列文章:app



合併

咱們全部人通常都會工做在分支上,咱們須要討論下如何經過合併來從一個分支上獲取變動到另外一個分支上。編輯器

咱們剛在 change_alice 分支上修改了 Alice.txt,我想說咱們對所作的改變感到滿意。

若是你接着執行 git checkout master 命令,那麼咱們在其餘分支上建立的 提交 沒法在此看到爲了將變動弄到 master 分支,咱們須要 合併 change_alice 分支 master 分支上。

注意:你老是將某個分支 合併 到當前分支。

快進合併

既然咱們已經執行了 checked out 來切換到 master 分支,如今咱們能夠執行 git merge change_alice 合併命令。

因爲 Alice.txt 並無其餘衝突變動,咱們在 master 分支未作修改,所以合併將在所謂的快進合併中進行。

在下面的圖表中,咱們能夠看到,這僅僅意味着:master 的指針會被簡單地前進到 change_alice 分支存在的位置。

第一張圖顯示了咱們執行 合併 前的狀態,master 指針仍處於它以前的提交位置,同時另外一個分支上咱們又作了一次提交。

快進合併以前

第二張圖顯示了在咱們 合併 以後發生了什麼變化。

快進合併以後

合併相異的分支

讓咱們試一些更復雜的。

在 master 分支的 Bob.txt 文件新行中添加一些文字,而後提交它。

接着執行 git checkout change_alice 命令,改變 Alice.txt 文件並提交。

在下圖中,你能夠看到咱們的提交歷史如今的樣子。masterchange_alice 分支都源於同一個提交,但那以後它們發生了分歧 ,每一個分支都有本身額外的提交。

不一樣的提交

若是你如今使用命令 git merge change_alice 來執行一個快進合併是不可能的了。取代它的是,你最愛的文本編輯器將會打開,而且容許你修改 合併提交 操做的提交信息,git 即將執行這個提交從而將兩個分支從新保持一致。你如今使用默認提交信息就行。下圖顯示了咱們在執行 合併 後的 git 歷史狀態。

合併分支

新的提交將咱們在 change_alice 分支上的修改引入到 master 分支。

正如你以前記得那樣,git 中的修訂不只僅是文件的快照,還包含了它們來自何處的一些信息。每次 提交 都包含一個或多個父級提交信息。咱們的新 合併 提交包含了 master 分支的最後提交,以及咱們在另外一個分支上的提交來做爲此次合併的父級提交。

解決衝突

目前爲止,咱們的修改都沒有相互干擾。

讓咱們介紹一種衝突,而後解決它。

建立一個新分支,而後將它 檢出。你知道如何操做,不過或許可使用 git checkout -b 命令爲你減小麻煩。

我把它命名爲 bobby_branch

在這個分支上,咱們會修改 Bob.txt 文件。

第一行應該仍然是 Hi!! I'm Bob. I'm new here.,把它修改爲 Hi!! I'm Bobby. I'm new here.

暫存文件以後,在你再次 檢出 master 分支以前,提交 你的修改。在 master 分支咱們將同一行修改成 Hi!! I'm Bob. I've been here for a while now.,接着 提交 該修改。

如今是將新分支 合併master 的時候了。

若是你嘗試這麼作,你將會看到以下的結果:

Auto-merging Bob.txt
CONFLICT (content): Merge conflict in Bob.txt
Automatic merge failed; fix conflicts and then commit the result.
複製代碼

兩個分支都修改了同一行,此時 git 工具沒法本身徹底處理這種狀況。

若是你運行 git status 命令,你會獲取到用來指導接下來如何繼續的全部常見幫助命令。

首先咱們必須手動解決衝突。

對於像這個簡單衝突來講,你最喜好的文本編輯器就夠用了。而對於合併含有不少變化的多個文件來講,使用更強大的工具會讓你輕鬆很多,我建議選用包含版本控制工具,具備友好合並界面的你最喜好的 IDE。

若是你打開 Bob.txt 文件,你會看到一些相似下面的內容(我已經截斷了以前可能放在第二行的其餘內容):

<<<<<<< HEAD
Hi! I'm Bob. I've been here for a while now.
=======
Hi! I'm Bobby. I'm new here.
>>>>>>> bobby_branch
[...你在第 2 行傳入的隨便什麼內容]
複製代碼

在上面你能夠看到當前 HEAD 上 Bob.txt 發生的變化,在下面你能夠看到咱們正嘗試合併進來的分支所作的更改。

爲了手工解決衝突,你只須要確保文件最終保留一些合理內容,而不包含 git 引入文件的特殊行。

因此繼續修改文件爲下面這種內容:

Hi! I'm Bobby. I've been here for a while now.
[...]
複製代碼

從這裏開始,咱們即將要作的事是適用於任何變動。

在咱們執行 add Bob.txt 添加文件後,暫存這些變動,而後執行 提交

咱們已經瞭解爲解決衝突所作的變動提交,就是合併過程當中一直都有的合併提交

實際上你應該意識到在解決衝突的過程當中,若是你不想繼續 合併 進程,你能夠經過運行 git merge --abort 命令直接 停止 它。

變基

Git 有另一種純淨方式來集成兩個分支的變化,叫作 變基

咱們始終記得一個分支老是基於另一個分支。當你建立它時,從某處開始分叉

在咱們簡單的合併樣例中,咱們從 master 分支的某次提交建立了一個分支,而後提交了在 masterchange_alice 分別提交了一些變動。

當一個分支相對於它所基於的分支產生了改變,若是你想要把最新的變動整合到你當前的分支,變基 提供了一種比 合併 更加純淨的處理方式。

正如你所看到的,一次 合併 引入了一個合併提交,這個過程當中兩邊的歷史記錄獲得整合。

很容易看得出,變基僅僅改變了你的分支所依賴的歷史記錄點(建立分支所基於的某次提交)。

爲了嘗試這一點,咱們首先將 master 分支再次檢出,而後基於它來建立/檢出一個新分支。

我稱本身的新分支爲 add_patrick,而後我添加了一個新文件 Patrick.txt,而後以信息 「Add Patrick」 提交了該文件。

在你爲該分支添加了一條提交後,返回 master 分支,作一點修改而後提交它。我作的修改是爲 Alice.txt 文件多加了一些文本。

就像咱們合併樣例中那樣,兩個分支有公共祖先,然而歷史是不一樣的,你能夠從下圖中看出:

一次變基以前的歷史記錄

如今讓咱們再執行 checkout add_patrick 命令,而後把 master 上作的修改獲取到咱們正在操做的分支上!

當咱們執行 git rebase master 命令,咱們讓 add_patrick 分支從新以當前狀態的 master 分支作了基準。

上面這條命令爲咱們提供了目前操做的友好提示:

First, rewinding head to replay your work on top of it...
Applying: Add Patrick
複製代碼

咱們知道 HEAD 是咱們所在的工做環境中當前提交的指針。

在變基操做執行以前,它的指向與 add_patrick 分支一致。發生了變基,它會首先移回到兩個分支的公共祖先,而後移動到咱們想要定爲基點的那個分支的當前頂點。

因此 HEAD 移動到 0cfc1d2 此次提交,而後到 7639f4b 此次提交,它是位於 master 分支的頂點。

而後變基操做會將咱們在 add_patrick 分支上作的每個提交都應用到那個頂點上。

爲了更精確瞭解 gitHEAD 指針移回到分支的公共祖先過程當中作了什麼,能夠把你在被操做的分支上每次提交都存儲一部分(修改的 差別點、提交信息、做者等等。)。

在上面操做以後,你正在變基的分支須要 檢出 最新的提交,而後把存下的全部變化以一條新提交應用到前面的提交之上。

因此在咱們原先簡單的視圖中,咱們認爲在 變基 以後,0cfc1d2 此次提交再也不指向它歷史中原公共祖先,而是指向 master 分支的頂部。

事實上,0cfc1d2 此次提交消失了,而且 add_patrick 分支以一個新提交 0ccaba8 爲開始,它以 master 分支的最新提交做爲公共祖先。

咱們讓它看起來就像,add_patrick 分支是以當前 master 分支爲基點,而不是分支的較舊版本,不過咱們這樣作至關於重寫了該分支的歷史。

在本教程的末尾,咱們會多學習一些重寫歷史以及何時適宜和不適宜這麼作。

變基以後的歷史記錄

當你本身的工做分支是基於一個共享分支時,例如 master 分支,變基 是一種至關強大的工具。

使用變基操做,能夠確保你能常常整合別人提交到 master 分支的變動和推送,而且保證一條幹淨線性的歷史,在你的工做文本須要引入到共享分支時,這種歷史能夠作 快進合併

相對於包含合併提交的凌亂歷史,保持歷史的線性也可使提交日誌更加有用(試一下 git log --graph,或者看一下 GitHubGitLab 的分支視圖)。

解決衝突

就像 合併 過程當中,若是遇到兩次提交修改了一個文件中一樣位置的內容塊,你可能會遇到衝突。

然而當你在 變基 過程當中遇到衝突,你無需在額外的合併提交中解決它,卻能夠在當前正在執行的提交中解決它。

一樣地,將你的修改直接以原始分支的當前狀態爲基準。

事實上,你在 變基 過程當中的解決衝突操做很是相似你在 合併 中的操做,因此若是你不太肯定如何操做的話,能夠回過頭查看那個小節。

惟一的區別在於,因爲你沒有引入合併提交,因此不須要你提交衝突解決結果。只需添加變動到暫存環境,而後執行 git rebase --continue 命令。衝突將會在剛剛執行的提交中獲得解決。

當合並時,你一直均可以中止和丟棄目前你作的全部內容,經過執行 git rebase --abort 命令。

更新遠程變動到本地工做環境

目前爲止,咱們已經學習瞭如何生成和共享內容變動。

若是你是獨自工做,這些已經夠用了。可是一般咱們是多人共同處理一項工做,而且咱們想要從遠程倉庫以某種方式獲取他們的變動到咱們本身的工做環境中。

因爲已通過了一段時間,讓咱們看一下 git 的組件:

git 組件

就像你的工做環境,每一個工做在同一份源代碼的人都有他們本身的工做環境。

許多工做環境

全部這些工做環境都有它們本身的進行中暫存的變動,這些會在某個節點被 提交本地倉庫,最終 推送遠程倉庫

咱們的例子中,咱們會使用 GitHub 提供的在線工具,來模擬在咱們工做時其餘人對遠程倉庫生成的變動。

查看你在 github.com 網上對這個倉庫的 fork 分支,打開 Alice.txt 文件。

找到編輯按鈕,經過網站來生成和提交一個變動。

github 編輯

在這個倉庫中,我已經在一個叫作 fetching_changes_sample 的分支上爲 Alice.txt 文件添加了一個遠程倉庫變動,可是在你的該倉庫版本,你固然能夠直接改變 master 分支上這個文件。

獲取更新

咱們還記得,當你執行 git push 命令時,會將本地倉庫的變動同步到遠程倉庫

爲了獲取遠程倉庫中的變動到本地倉庫,你可使用 git fetch 命令。

這個操做獲取到遠程的任何變動到你的本地倉庫,包含提交和分支。

這點要注意,變動尚未被整合到本地分支,更不用說工做空間暫存區域

獲取更新

若是你如今執行 git status 命令,你會看到 git 命令的另外一個很棒的例子,告訴你如今正發生什麼:

git status
On branch fetching_changes_sample
Your branch is behind 'origin/fetching_changes_sample' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
複製代碼

拉取更新

因爲咱們沒有工做中暫存的變動,咱們如今能夠執行 git pull 命令來從倉庫中拉取全部變動到咱們的工做空間。

拉取會隱式地 獲取 遠程倉庫,但有時候單獨執行 獲取 是個好選擇。 例如,當你想要同步任何新的遠程分支,或者你想在像 origin/master 這種分支上執行 git rebase 以前,須要確保你的本地倉庫是最新的。

拉取更新

在咱們 拉取 以前,讓咱們本地修改一個文件來看會發生什麼。

讓咱們在工做空間再次修改 Alice.txt 文件!

若是你如今嘗試作一次 git pull,你會看到以下錯誤:

git pull
Updating df3ad1d..418e6f0
error: Your local changes to the following files would be overwritten by merge:
Alice.txt
Please commit your changes or stash them before you merge.
Aborting
複製代碼

你沒法 拉取 任何變動,由於工做空間中有些文件被修改,同時你正在拉取進來的提交也有這些文件的變化。

這種狀況的一種解決方法是,爲了獲取你比較信任的某個點上的變動,在你最終提交它們以前,能夠把本地變動 添加暫存空間。可是在你最終提交它們以前,而這是學習另外一個很棒工具的一個好時機。

儲藏變動

在任什麼時候刻若是你有一些本地變動,還不想放進一個提交中,或者想存在某個地方來以其餘某種角度來解決一個問題,你能夠將這些變動 儲藏 起來。

一次 git stash 基本上是一個變動的堆棧,這裏面你能夠存儲對工做空間的任何變動。

最經常使用的命令是:git stash,它將對工做空間的任何修改儲藏起來。還有 git stash pop 命令,它拿到儲藏起來的最近修改,並將其再次應用到工做空間

就如堆棧命令的命名,git stash pop 命令在應用變動以前,將最近存儲的變動移除。

若是你想保留儲藏的變動,你可使用 git stash apply 命令,這種方式不會在應用變動以前從儲藏中移除它們。

爲了檢查你當前的 儲藏,你可使用 git stash list 命令來列出各個單獨的條目,還可使用 git stash show 命令來顯示 儲藏 中最近條目的變動。

另外一個好用的命令是 git stash branch {BRANCH NAME},它從當前的 HEAD 開始建立一個分支,此時你儲藏了變動,並把它們應用到了新建分支中。

如今咱們瞭解了 git stash 命令,讓咱們執行它,用來從工做空間中移除咱們對 Alice.txt 文件作的本地變動,這樣咱們就能夠繼續上面的操做,執行 git pull 命令來拉取咱們在網站上作的遠程變動。

在那以後,讓咱們執行 git stash pop 命令來取回本地變動。

由於咱們 拉取 的提交和 儲藏 的變動都修改了 Alice.txt 文件,因此你須要解決衝突,就像在 合併變基 中你作的那樣。 完成 添加 後,提交這個變動。

包含衝突的拉取

如今咱們已經理解如何 獲取拉取 遠程變動到咱們的工做環境,正是製造一些衝突的時候!

不要推送那個修改 Alice.txt 文件的提交,回到你位於 github.com遠程倉庫

這裏咱們又要修改 Alice.txt 文件並提交它。

如今實際上在咱們的本地遠程倉庫之間存在兩處衝突。

不要忘了運行 git fetch 命令來查看遠程的變動,而不是當即 拉取 它。

若是你如今運行 git status 命令,你會看到兩個分支各有一個與對方不一樣的提交。

git status
On branch fetching_changes_sample
Your branch and 'origin/fetching_changes_sample' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
複製代碼

另外,咱們已經在上面不一樣提交中修改了同一個文件,爲了介紹 合併 中的衝突這個概念,因此咱們須要解決它。

當你執行 git pull 命令時,而本地遠程倉庫之間存在着差別,就會發生與 合併 兩個分支過程時一樣的事情。

額外的,你能夠認爲遠程倉庫本地倉庫分支之間的關係是一種從一個分支上建立另外一個分支的特殊狀況。

本地分支是基於你從遠程倉庫最近一次獲取的分支狀態的。

若是以這種方式思考,這兩種選項來獲取遠程倉庫變化就頗有道理:

當你執行 git pull 命令,本地遠程倉庫的版本就會 合併。就像 合併 不一樣分支同樣,這會引入一個合併提交。

由於任何本地分支都基於它們各自的遠程版本,咱們也能夠對它執行 變基,這樣作的話咱們在本地作的任何變動,都表現爲基於遠程倉庫中的最新可用版本。

爲了這麼作,咱們可使用 git pull --rebase 命令(或者簡寫git pull -r)。

變基這小節中已經詳細介紹了,保持一個乾淨線性的歷史提交記錄是有好處的,因此我才強烈建議當你須要執行 git pull 命令時,不妨使用 git pull -r 替代。

你也能夠告訴 git 使用 變基 來代替 合併,做爲你執行 git pull 命令時的默認策略,經過一個像這樣 git config --global pull.rebase true 的命令來設置 pull.rebase 標識。

在我介紹前面幾個段落以後,若是你尚未執行過 git pull 命令的話,讓我如今一塊兒執行 git pull -r 來獲取遠程變動吧,讓它顯得就像咱們的新提交位於那些遠程變動以後。

固然就像一個正常的 變基(或者 合併)操做,你須要解決咱們引入的衝突,以便 git pull 命令能夠完成。

歡迎繼續閱讀本系列其餘文章:

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索