天天看點

Git學習-圖文并茂還有遊戲玩!

目錄

主要

基礎篇

Git Commit

Git Branch

Git Merge

Git Rebase

在送出樹上移動HEAD

進階篇

分離HEAD

相對引用

撤銷變更

移動送出記錄

Git Cherry-pick

互動式 rebase

雜項

隻取一個送出記錄

Git Tag

Git Describe

遠端

Push & Pull —— Git 遠端倉庫!

Git clone

Git Fetch

Git Pull

Git Push

偏離的送出曆史

鎖定的Master

IDEA中使用Git

參考

學習網址:https://learngitbranching.js.org/?locale=zh_CN

闖關的形式,很棒。

循序漸進地介紹 Git 主要指令。

Git 倉庫中的送出記錄儲存的是你的目錄下所有檔案的快照,就像是把整個目錄複制,然後再粘貼一樣,但比複制粘貼優雅許多!

Git 希望送出記錄盡可能地輕量,是以在你每次進行送出時,它并不會盲目地複制整個目錄。條件允許的情況下,它會将目前版本與倉庫中的上一個版本進行對比,并把所有的差異打包到一起作為一個送出記錄。

Git 還儲存了送出的曆史記錄。這也是為什麼大多數送出記錄的上面都有父節點的原因 —— 我們會在圖示中用箭頭來表示這種關系。對于項目組的成員來說,維護送出曆史對大家都有好處。

關于送出記錄太深入的東西咱們就不再繼續探讨了,現在你可以把送出記錄看作是項目的快照。送出記錄非常輕量,可以快速地在這些送出記錄之間切換!

Git學習-圖文并茂還有遊戲玩!

Git 的分支也非常輕量。它們隻是簡單地指向某個送出紀錄 —— 僅此而已。是以許多 Git 愛好者傳頌:

這是因為即使建立再多的分支也不會造成儲存或記憶體上的開銷,并且按邏輯分解工作到不同的分支要比維護那些特别臃腫的分支簡單多了。

在将分支和送出記錄結合起來後,我們會看到兩者如何協作。現在隻要記住使用分支其實就相當于在說:“我想基于這個送出以及它所有的父送出進行新的工作。”

Git學習-圖文并茂還有遊戲玩!

太好了! 我們已經知道如何送出以及如何使用分支了。接下來咱們看看如何将兩個分支合并到一起。就是說我們建立一個分支,在其上開發某個新功能,開發完成後再合并回主線。

咱們先來看一下第一種方法 —— <code>git merge</code>。在 Git 中合并兩個分支時會産生一個特殊的送出記錄,它有兩個父節點。翻譯成自然語言相當于:“我要把這兩個父節點本身及它們所有的祖先都包含進來。”

Git學習-圖文并茂還有遊戲玩!
Git學習-圖文并茂還有遊戲玩!

第二種合并分支的方法是 <code>git rebase</code>。Rebase 實際上就是取出一系列的送出記錄,“複制”它們,然後在另外一個地方逐個的放下去。

Rebase 的優勢就是可以創造更線性的送出曆史,這聽上去有些難以了解。如果隻允許使用 Rebase 的話,代碼庫的送出曆史将會變得異常清晰。

Git學習-圖文并茂還有遊戲玩!

在接觸 Git 更進階功能之前,我們有必要先學習在你項目的送出樹上前後移動的幾種方法。

一旦熟悉了如何在 Git 送出樹上移動,你駕馭其它指令的能力也将水漲船高!

我們首先看一下 “HEAD”。 HEAD 是一個對目前檢出記錄的符号引用 —— 也就是指向你正在其基礎上進行工作的送出記錄。

HEAD 總是指向目前分支上最近一次送出記錄。大多數修改送出樹的 Git 指令都是從改變 HEAD 的指向開始的。

HEAD 通常情況下是指向分支名的(如 bugFix)。在你送出時,改變了 bugFix 的狀态,這一變化通過 HEAD 變得可見。

要開始介紹 Git 的超棒特性了,快來吧!

git ckeckout c4

通過指定送出記錄哈希值的方式在 Git 中移動不太友善。在實際應用時,并沒有像本程式中這麼漂亮的可視化送出樹供你參考,是以你就不得不用 <code>git log</code> 來查檢視送出記錄的哈希值。

并且哈希值在真實的 Git 世界中也會更長(譯者注:基于 SHA-1,共 40 位)。例如前一關的介紹中的送出記錄的哈希值可能是 <code>fed2da64c0efc5293610bdd892f82a58e8cbc5d8</code>。舌頭都快打結了吧...

比較令人欣慰的是,Git 對哈希的處理很智能。你隻需要提供能夠唯一辨別送出記錄的前幾個字元即可。是以我可以僅輸入<code>fed2</code> 而不是上面的一長串字元。

相對引用非常給力,這裡我介紹兩個簡單的用法:

使用 <code>^</code> 向上移動 1 個送出記錄

使用 <code>~</code> 向上移動多個送出記錄,如 <code>~3</code>

在 Git 裡撤銷變更的方法很多。和送出一樣,撤銷變更由底層部分(暫存區的獨立檔案或者片段)和上層部分(變更到底是通過哪種方式被撤銷的)組成。我們這個應用主要關注的是後者。

主要有兩種方法用來撤銷變更 —— 一是 <code>git reset</code>,還有就是 <code>git revert</code>。接下來咱們逐個進行講解。

Git Reset

<code>git reset</code> 通過把分支記錄回退幾個送出記錄來實作撤銷改動。你可以将這想象成“改寫曆史”。<code>git reset</code> 向上移動分支,原來指向的送出記錄就跟從來沒有送出過一樣。

Git學習-圖文并茂還有遊戲玩!

Git Revert

雖然在你的本地分支中使用 <code>git reset</code> 很友善,但是這種“改寫曆史”的方法對大家一起使用的遠端分支是無效的哦!

Git學習-圖文并茂還有遊戲玩!

到現在我們已經學習了 Git 的基礎知識 —— 送出、分支以及在送出樹上移動。 這些概念涵蓋了 Git 90% 的功能,同樣也足夠滿足開發者的日常需求

然而, 剩餘的 10% 在處理複雜的工作流時(或者當你陷入困惑時)可能就顯得尤為重要了。接下來要讨論的這個話題是“整理送出記錄” —— 開發人員有時會說“我想要把這個送出放到這裡, 那個送出放到剛才那個送出的後面”, 而接下來就講的就是它的實作方式,非常清晰、靈活,還很生動。

看起來挺複雜, 其實是個很簡單的概念。

本系列的第一個指令是 <code>git cherry-pick</code>, 指令形式為:

<code>git cherry-pick &lt;送出号&gt;...</code>

如果你想将一些送出複制到目前所在的位置(<code>HEAD</code>)下面的話, Cherry-pick 是最直接的方式了。我個人非常喜歡 <code>cherry-pick</code>,因為它特别簡單。

Git學習-圖文并茂還有遊戲玩!

當你知道你所需要的送出記錄(并且還知道這些送出記錄的哈希值)時, 用 cherry-pick 再好不過了 —— 沒有比這更簡單的方式了。

但是如果你不清楚你想要的送出記錄的哈希值呢? 幸好 Git 幫你想到了這一點, 我們可以利用互動式的 rebase —— 如果你想從一系列的送出記錄中找到想要的記錄, 這就是最好的方法了。

互動式 rebase 指的是使用帶參數 <code>--interactive</code> 的 rebase 指令, 簡寫為 <code>-i</code>

如果你在指令後增加了這個選項, Git 會打開一個 UI 界面并列出将要被複制到目标分支的備選送出記錄,它還會顯示每個送出記錄的哈希值和送出說明,送出說明有助于你了解這個送出進行了哪些更改。

在實際使用時,所謂的 UI 視窗一般會在文本編輯器 —— 如 Vim —— 中打開一個檔案。

當 rebase UI界面打開時, 你能做3件事:

調整送出記錄的順序(通過滑鼠拖放來完成)

删除你不想要的送出(通過切換 <code>pick</code> 的狀态來完成,關閉就意味着你不想要這個送出記錄)

合并送出。 簡而言之,它允許你把多個送出記錄合并成一個。

Git 技術、技巧與貼士大集合。

本地棧式送出

來看一個在開發中經常會遇到的情況:我正在解決某個特别棘手的 Bug,為了便于調試而在代碼中添加了一些調試指令并向控制台列印了一些資訊。

這些調試和列印語句都在它們各自的送出記錄裡。最後我終于找到了造成這個 Bug 的根本原因,解決掉以後覺得沾沾自喜!

最後就差把 <code>bugFix</code> 分支裡的工作合并回 <code>main</code> 分支了。你可以選擇通過 fast-forward 快速合并到 <code>main</code> 分支上,但這樣的話 <code>main</code> 分支就會包含我這些調試語句了。你肯定不想這樣,應該還有更好的方式……

實際我們隻要讓 Git 複制解決問題的那一個送出記錄就可以了。跟之前我們在“整理送出記錄”中學到的一樣,我們可以使用

<code>git rebase -i</code>

<code>git cherry-pick</code>

來達到目的。

相信通過前面課程的學習你已經發現了:分支很容易被人為移動,并且當有新的送出時,它也會移動。分支很容易被改變,大部分分支還隻是臨時的,并且還一直在變。

你可能會問了:有沒有什麼可以 永遠 指向某個送出記錄的辨別呢,比如軟體釋出新的大版本,或者是修正一些重要的 Bug 或是增加了某些新特性,有沒有比分支更好的可以永遠指向這些送出的方法呢?

當然有了!Git 的 tag 就是幹這個用的啊,它們可以(在某種程度上 —— 因為标簽可以被删除後重新在另外一個位置建立同名的标簽)永久地将某個特定的送出命名為裡程碑,然後就可以像分支一樣引用了。

更難得的是,它們并不會随着新的送出而移動。你也不能檢出到某個标簽上面進行修改送出,它就像是送出樹上的一個錨點,辨別了某個特定的位置。

Git學習-圖文并茂還有遊戲玩!

由于标簽在代碼庫中起着“錨點”的作用,Git 還為此專門設計了一個指令用來描述離你最近的錨點(也就是标簽),它就是 <code>git describe</code>!

Git Describe 能幫你在送出曆史中移動了多次以後找到方向;當你用 <code>git bisect</code>(一個查找産生 Bug 的送出記錄的指令)找到某個送出記錄時,或者是當你坐在你那剛剛度假回來的同僚的電腦前時, 可能會用到這個指令。

是時候分享你的代碼了,讓編碼變得社交化吧

遠端倉庫

遠端倉庫并不複雜, 在如今的雲計算盛行的世界很容易把遠端倉庫想象成一個富有魔力的東西, 但實際上它們隻是你的倉庫在另個一台計算機上的拷貝。你可以通過網際網路與這台計算機通信 —— 也就是增加或是擷取送出記錄。

話雖如此, 遠端倉庫卻有一系列強大的特性

首先也是最重要的的點, 遠端倉庫是一個強大的備份。本地倉庫也有恢複檔案到指定版本的能力, 但所有的資訊都是儲存在本地的。有了遠端倉庫以後,即使丢失了本地所有資料, 你仍可以通過遠端倉庫拿回你丢失的資料。

還有就是, 遠端讓代碼社交化了! 既然你的項目被托管到别的地方了, 你的朋友可以更容易地為你的項目做貢獻(或者拉取最新的變更)

現在用網站來對遠端倉庫進行可視化操作變得越發流行了(像 GitHub 或 Phabricator),但遠端倉庫永遠是這些工具的頂梁柱, 是以了解其概念非常的重要!

直到現在, 教程都聚焦于本地倉庫的操作(branch、merge、rebase 等等)。但我們現在需要學習遠端倉庫的操作 —— 我們需要一個配置這種環境的指令, 它就是 <code>git clone</code>。

從技術上來講,<code>git clone</code> 指令在真實的環境下的作用是在本地建立一個遠端倉庫的拷貝(比如從 github.com)。

Git學習-圖文并茂還有遊戲玩!

遠端分支

既然你已經看過 <code>git clone</code> 指令了,咱們深入地看一下發生了什麼。

你可能注意到的第一個事就是在我們的本地倉庫多了一個名為 <code>o/main</code> 的分支, 這種類型的分支就叫遠端分支。由于遠端分支的特性導緻其擁有一些特殊屬性。

遠端分支反映了遠端倉庫(在你上次和它通信時)的狀态。這會有助于你了解本地的工作與公共工作的差别 —— 這是你與别人分享工作成果前至關重要的一步。

遠端分支有一個特别的屬性,在你檢出時自動進入分離 HEAD 狀态。Git 這麼做是出于不能直接在這些分支上進行操作的原因, 你必須在别的地方完成你的工作, (更新了遠端分支之後)再用遠端分享你的工作成果。

為什麼有 <code>o/</code>?

你可能想問這些遠端分支的前面的 <code>o/</code> 是什麼意思呢?好吧, 遠端分支有一個命名規範 —— 它們的格式是:

<code>/</code>

是以,如果你看到一個名為 <code>o/main</code> 的分支,那麼這個分支就叫 <code>main</code>,遠端倉庫的名稱就是 <code>o</code>。

大多數的開發人員會将它們主要的遠端倉庫命名為 <code>origin</code>,并不是 <code>o</code>。這是因為當你用 <code>git clone</code> 某個倉庫時,Git 已經幫你把遠端倉庫的名稱設定為 <code>origin</code> 了

不過 <code>origin</code> 對于我們的 UI 來說太長了,是以不得不使用簡寫 <code>o</code> 😃 但是要記住, 當你使用真正的 Git 時, 你的遠端倉庫預設為 <code>origin</code>!

Git 遠端倉庫相當的操作實際可以歸納為兩點:向

遠端倉庫傳輸資料

從遠端倉庫擷取資料。

既然我們能與遠端倉庫同步,那麼就可以分享任何能被 Git 管理的更新(是以可以分享代碼、檔案、想法、情書等等)。

Git學習-圖文并茂還有遊戲玩!

git fetch 做了些什麼?

<code>git fetch</code> 完成了僅有的但是很重要的兩步:

從遠端倉庫下載下傳本地倉庫中缺失的送出記錄

更新遠端分支指針(如 <code>o/main</code>)

<code>git fetch</code> 實際上将本地倉庫中的遠端分支更新成了遠端倉庫相應分支最新的狀态。

遠端分支反映了遠端倉庫在你最後一次與它通信時的狀态,<code>git fetch</code> 就是你與遠端倉庫通信的方式了!希望我說的夠明白了,你已經了解 <code>git fetch</code> 與遠端分支之間的關系了吧。

<code>git fetch</code> 通常通過網際網路(使用 <code>http://</code> 或 <code>git://</code> 協定) 與遠端倉庫通信。

git fetch 不會做的事?

<code>git fetch</code> 并不會改變你本地倉庫的狀态。它不會更新你的 <code>main</code> 分支,也不會修改你磁盤上的檔案。

了解這一點很重要,因為許多開發人員誤以為執行了 <code>git fetch</code> 以後,他們本地倉庫就與遠端倉庫同步了。它可能已經将進行這一操作所需的所有資料都下載下傳了下來,但是并沒有修改你本地的檔案。

是以, 你可以将 <code>git fetch</code> 的了解為單純的下載下傳操作。

既然我們已經知道了如何用 <code>git fetch</code> 擷取遠端的資料, 現在我們學習如何将這些變化更新到我們的工作當中。

其實有很多方法的 —— 當遠端分支中有新的送出時,你可以像合并本地分支那樣來合并遠端分支。也就是說就是你可以執行以下指令:

<code>git cherry-pick o/main</code>

<code>git rebase o/main</code>

<code>git merge o/main</code>

等等

實際上,由于先抓取更新再合并到本地分支這個流程很常用,是以 Git 提供了一個專門的指令來完成這兩個操作。它就是我們要講的 <code>git pull</code>。

<code>git pull</code> 就是 git fetch 和 git merge 的縮寫!

OK,我們已經學過了如何從遠端倉庫擷取更新并合并到本地的分支當中。這非常棒……但是我如何與大家分享我的成果呢?

嗯,上傳自己分享内容與下載下傳他人的分享剛好相反,那與 <code>git pull</code> 相反的指令是什麼呢?<code>git push</code>!

<code>git push</code> 負責将你的變更上傳到指定的遠端倉庫,并在遠端倉庫上合并你的新送出記錄。一旦 <code>git push</code> 完成, 你的朋友們就可以從這個遠端倉庫下載下傳你分享的成果了!

你可以将 <code>git push</code> 想象成釋出你成果的指令。它有許多應用技巧,稍後我們會了解到,但是咱們還是先從基礎的開始吧……

*注意 —— <code>git push</code> 不帶任何參數時的行為與 Git 的一個名為 <code>push.default</code> 的配置有關。它的預設值取決于你正使用的 Git 的版本,但是在教程中我們使用的是 <code>upstream</code>。 這沒什麼太大的影響,但是在你的項目中進行推送之前,最好檢查一下這個配置。*

現在我們已經知道了如何從其它地方 <code>pull</code> 送出記錄,以及如何 <code>push</code> 我們自己的變更。看起來似乎沒什麼難度,但是為何還會讓人們如此困惑呢?

困難來自于遠端庫送出曆史的偏離。

例子

Git學習-圖文并茂還有遊戲玩!
Git學習-圖文并茂還有遊戲玩!

很好!但是要敲那麼多指令,有沒有更簡單一點的?

當然 —— 前面已經介紹過 <code>git pull</code> 就是 fetch 和 merge 的簡寫,類似的 <code>git pull --rebase</code> 就是 fetch 和 rebase 的簡寫!

其實,你直接 pull push也是一樣的,…………

如果你是在一個大的合作團隊中工作, 很可能是main被鎖定了, 需要一些Pull Request流程來合并修改。如果你直接送出(commit)到本地main, 然後試圖推送(push)修改, 你将會收到這樣類似的資訊:

遠端伺服器拒絕直接推送(push)送出到main,因為政策配置要求 pull requests 來送出更新。

你應該按照流程,建立一個分支,,推送(push)這個分支并申請pull request,但是你忘記并直接送出給了main。現在你卡住并且無法推送你的更新。

解決方法: 建立一個分支feature,推送到遠端伺服器。 然後reset你的Master分支和遠端伺服器保持一緻, 否則下次你pull并且他人的送出和你沖突的時候就會有問題。

IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git

JetBrains IDEs(比如 IntelliJ IDEA,PyCharm,WebStorm,PhpStorm,RubyMine,以及其他)自帶 Git 內建插件。插件在 IDE 中提供了一個專門的頁面,可以使用 Git 和 GitHub 的 Pull Request。

該內建插件依賴于 Git 的指令行用戶端,是以需要先安裝一個 Git 用戶端。官方文檔請通路: https://www.jetbrains.com/help/idea/using-git-integration.html 。

學習網址:https://learngitbranching.js.org/,闖關的形式,很棒。

關卡答案:https://blog.csdn.net/qq_34519487/article/details/107882290

Git-中文文檔:https://git-scm.com/book/zh/v2