Git-版本控制

(參考Udacity-侵刪)

“版本控制”即“控制版本”!版本控制系統(tǒng)是幫助你控制(或管理)某個(gè)事物(通常是源代碼)的不同版本。

版本控制系統(tǒng)信息

有很多版本控制系統(tǒng)可供我們選擇。單憑這一點(diǎn)就證明版本控制非常重要。以下是三大最熱門的版本控制系統(tǒng):

版本控制系統(tǒng)模型包括兩大主要類型:

  • 集中式模型 - 所有用戶都連接到一個(gè)中央的主倉(cāng)庫(kù)(master repository)
  • 分布式模型 - 每個(gè)用戶都在自己的計(jì)算機(jī)上擁有完整的倉(cāng)庫(kù)

深入研究

版本控制系統(tǒng)的主要目的是幫助你保留項(xiàng)目的詳細(xì)歷史記錄,并且能夠在不同的版本上進(jìn)行工作。保留詳細(xì)的項(xiàng)目歷史記錄很重要,因?yàn)檫@樣可以看出一段時(shí)間內(nèi)項(xiàng)目的進(jìn)度。如果需要,你還可以回到項(xiàng)目的某個(gè)階段,并恢復(fù)數(shù)據(jù)或文件。

一、術(shù)語(yǔ)

版本控制系統(tǒng) / 源代碼管理器

版本控制系統(tǒng)(簡(jiǎn)稱VCS)是一個(gè)管理源代碼不同版本的工具。源代碼管理器(簡(jiǎn)稱SCM)是版本控制系統(tǒng)的另一個(gè)名稱。

Git 是一個(gè) SCM(因此也是 VCS?。it 網(wǎng)站的 URL 是 https://git-scm.com/ (注意它的域名中直接包含“SCM”?。?。

提交(Commit)

Git 將數(shù)據(jù)看做微型文件系統(tǒng)的一組快照。每次 commit(在 Git 中保持項(xiàng)目狀態(tài)),它都對(duì)文件當(dāng)時(shí)的狀況拍照,并存儲(chǔ)對(duì)該快照的引用。你可以將其看做游戲中的保存點(diǎn),它會(huì)保存項(xiàng)目的文件和關(guān)于文件的所有信息。

你在 Git 中的所有操作都是幫助你進(jìn)行 commit,因此 commit 是 Git 中的基本單位。

倉(cāng)庫(kù)(Repository / repo)

倉(cāng)庫(kù)是一個(gè)包含項(xiàng)目?jī)?nèi)容以及幾個(gè)文件(在 Mac OS X 上默認(rèn)地處于隱藏狀態(tài))的目錄,用來與 Git 進(jìn)行通信。倉(cāng)庫(kù)可以存儲(chǔ)在本地,或作為遠(yuǎn)程副本存儲(chǔ)在其他計(jì)算機(jī)上。倉(cāng)庫(kù)是由 commit 構(gòu)成的。

工作目錄 / 工作區(qū)(Working Directory)

工作目錄是你在計(jì)算機(jī)的文件系統(tǒng)中看到的文件。當(dāng)你在代碼編輯器中打開項(xiàng)目文件時(shí),你是在工作目錄中處理文件。

與這些文件形成對(duì)比的是保持在倉(cāng)庫(kù)中(在 commit 中?。┑奈募?/p>

在使用 Git 時(shí),工作目錄與命令行工具的 current working directory(當(dāng)前工作目錄)不一樣,后者是 shell 當(dāng)前正在查看的目錄。

檢出(Checkout)

檢出是指將倉(cāng)庫(kù)中的內(nèi)容復(fù)制到工作目錄下。

暫存區(qū) / 暫存索引 / 索引(Staging Area / Staging Index / Index)

Git 目錄下的一個(gè)文件,存儲(chǔ)的是即將進(jìn)入下個(gè)commit 內(nèi)容的信息??梢詫捍鎱^(qū)看做準(zhǔn)備工作臺(tái),Git 將在此區(qū)域獲取下個(gè)commit。暫存索引中的文件是準(zhǔn)備添加到倉(cāng)庫(kù)中的文件。

SHA

SHA 是每個(gè) commit 的 ID 編號(hào)。以下是 commit 的 SHA 示例:e2adf8ae3e2e4ed40add75cc44cf9d0a869afeb6。

它是一個(gè)長(zhǎng) 40 個(gè)字符的字符串(由 0–9 和 a–f 組成),并根據(jù) Git 中的文件或目錄結(jié)構(gòu)的內(nèi)容計(jì)算得出。SHA 的全稱是"Secure Hash Algorithm"(安全哈希算法)。

分支(Branch)

分支是從主開發(fā)流程中分支出來的新的開發(fā)流程。這種分支開發(fā)流程可以在不更改主流程的情況下繼續(xù)延伸下去。

二、安裝 與 配置

Git下載

自行下載安裝

初次配置 Git

在命令行工具中運(yùn)行以下每行,確保所有選項(xiàng)都已被配置好。

# 設(shè)置你的 Git 用戶名
git config --global user.name "<Your-Full-Name>"

# 設(shè)置你的 Git 郵箱
git config --global user.email "<your-email-address>"

# 確保 Git 輸出內(nèi)容帶有顏色標(biāo)記
git config --global color.ui auto

# 對(duì)比顯示原始狀態(tài)
git config --global merge.conflictstyle diff3

git config --list
Git 與代碼編輯器

以下是三個(gè)最熱門的代碼編輯器。如果你使用的是其他編輯器,則在 Google (國(guó)內(nèi)請(qǐng)用梯子)中搜索“修改 Git 默認(rèn)編輯器為 X 編輯器”(將 X替換為你的代碼編輯器的名稱)。

Atom Editor 設(shè)置

git config --global core.editor "atom --wait"

Sublime Text 設(shè)置

git config --global core.editor "'/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl' -n -w"

VSCode 設(shè)置

git config --global core.editor "code --wait"

三、創(chuàng)建 Git 倉(cāng)庫(kù)

git init

在對(duì) Git 倉(cāng)庫(kù)進(jìn)行 commit 或執(zhí)行任何其他操作之前,需要一個(gè)實(shí)際存在的倉(cāng)庫(kù)。要使用 Git 新建一個(gè)倉(cāng)庫(kù),我們將使用 git init 命令。

簡(jiǎn)單來說:git init 可以在計(jì)算機(jī)上從頭創(chuàng)建全新倉(cāng)庫(kù)。

init 子命令是"initialize"(初始化)的簡(jiǎn)稱,這個(gè)命令很有用,因?yàn)樗鼘⑦M(jìn)行所有倉(cāng)庫(kù)初始設(shè)置。

運(yùn)行 git init 命令會(huì)初始化 Git 跟蹤所有內(nèi)容會(huì)用到的所有必要文件和目錄。所有這些文件都存儲(chǔ)在叫做 .git(注意開頭有個(gè) .它是一個(gè)隱藏目錄)的目錄下。這個(gè) .git 目錄是一個(gè)庫(kù)!Git 會(huì)將所有 commit 記錄在這里,并跟蹤所有內(nèi)容!

警告:請(qǐng)勿直接修改.git目錄下的任何文件。這是倉(cāng)庫(kù)的核心。如果你更改了文件名或文件內(nèi)容,Git 可能就無法跟蹤你保存在倉(cāng)庫(kù)中的文件,你可能會(huì)丟失很多內(nèi)容!可以查看這些文件,但是請(qǐng)勿編輯或刪除這些文件。

.git 目錄內(nèi)容

(對(duì)git的使用并不重要,因此不用記住任何東西)

  • config 文件 -存儲(chǔ)了所有與項(xiàng)目有關(guān)的配置設(shè)置。

    Git 會(huì)查看 Git 目錄下你當(dāng)前所使用倉(cāng)庫(kù)對(duì)應(yīng)的配置文件(.git/config)中的配置值。這些值僅適用于當(dāng)前倉(cāng)庫(kù)。

    例如,假設(shè)你將 Git 全局配置為使用你的個(gè)人電子郵箱。如果你想針對(duì)某個(gè)項(xiàng)目使用你的工作郵箱,則此項(xiàng)更改會(huì)被添加到該文件中。

  • description 文件 - 此文件僅用于 GitWeb 程序,因此可以忽略

  • hooks 目錄 - 我們會(huì)在此處放置客戶端或服務(wù)器端腳本,以便用來連接到 Git 的不同生命周期事件(hooks 目錄可以用來連接到 Git 工作流的不同部分或事件)

  • info 目錄 - 包含全局排除文件

  • objects 目錄 - 此目錄將存儲(chǔ)我們提交的所有 commit

  • refs 目錄 - 此目錄存儲(chǔ)了指向 commit 的指針(通常是“分支”和“標(biāo)簽”)

總結(jié): 運(yùn)行此命令可以創(chuàng)建隱藏 .git 目錄。此 .git 目錄是倉(cāng)庫(kù)的核心/存儲(chǔ)中心。它存儲(chǔ)了所有的配置文件和目錄,以及所有的 commit。

深入研究
實(shí)用鏈接
git clone

git clone 可以將一個(gè)現(xiàn)有倉(cāng)庫(kù)從其他地方克隆或復(fù)制到本地計(jì)算機(jī)。

輸入命令 git clone,然后輸入你要克隆的 Git 倉(cāng)庫(kù)的路徑。(一般個(gè)人項(xiàng)目或開源項(xiàng)目在github碼云上,自行注冊(cè)并創(chuàng)建倉(cāng)庫(kù))

git clone [倉(cāng)庫(kù)url]
//clone并重命名
git clone [倉(cāng)庫(kù)url] [new_name]

總結(jié):
該命令:

  • 會(huì)獲取現(xiàn)有倉(cāng)庫(kù)的路徑
  • 默認(rèn)地將創(chuàng)建一個(gè)與被克隆的倉(cāng)庫(kù)名稱相同的目錄
  • 可以提供第二個(gè)參數(shù),作為該目錄的名稱
  • 將在現(xiàn)有工作目錄下創(chuàng)建一個(gè)新的倉(cāng)庫(kù)
實(shí)用鏈接
git status

git status 查看倉(cāng)庫(kù)狀態(tài)。

git status 是了解 Git 的核心所在。它將告訴我們 Git 正在考慮什么,以及 Git 所看到的我們倉(cāng)庫(kù)的狀態(tài)。當(dāng)你第一次使用 Git 時(shí),你應(yīng)該一直都要使用 git status 命令!說真的,你應(yīng)該習(xí)慣于運(yùn)行任何其他命令之后,都運(yùn)行下該命令。這樣可以幫助你了解 Git 的工作原理,并避免你對(duì)文件 / 倉(cāng)庫(kù)狀態(tài)做出不正確的推論。

git status 命令將顯示很多信息,具體取決于你的文件狀態(tài)、工作目錄和倉(cāng)庫(kù)。但是你不需要過于關(guān)心這些內(nèi)容…

總結(jié): 該命令:

  • 告訴我們已在工作目錄中被創(chuàng)建但 Git 尚未開始跟蹤的新文件
  • Git 正在跟蹤的已修改文件
  • 其他信息
實(shí)用鏈接

四、查看倉(cāng)庫(kù)的歷史紀(jì)錄

git log 顯示有關(guān)現(xiàn)有提交的信息。

默認(rèn)情況下,該命令會(huì)顯示倉(cāng)庫(kù)中每個(gè) commit 的:

  • SHA
  • 作者
  • 日期
  • 消息

但是有些信息我們不關(guān)心,所以可以使用:

<!--只顯示簡(jiǎn)略版SHA(7位數(shù))和commit消息-->
$ git log --oneline
<!--【簡(jiǎn)約信息】-->

git 使用命令行分頁(yè)器 less 瀏覽所有信息。以下是 less 的重要快捷鍵:

  • 要向下滾動(dòng),按下
    • j 或 ↓ 一次向下移動(dòng)一行
    • d 按照一半的屏幕幅面移動(dòng)
    • f 按照整個(gè)屏幕幅面移動(dòng)
  • 要按頁(yè)向下滾動(dòng),使用空格鍵或 Page Down 按鈕
  • 要向上滾動(dòng),按上
    • k 或 ↑ 一次向上移動(dòng)一行
    • u 按照一半的屏幕幅面移動(dòng)
    • b 按照整個(gè)屏幕幅面移動(dòng)
  • 要按頁(yè)向下滾動(dòng),使用 b 或 Page Up 按鈕
  • 按下 q 可以退出日志(返回普通的命令提示符)

git log --stat

顯示 commit 中更改的文件以及添加或刪除的行數(shù)。stat(stat 是“【統(tǒng)計(jì)信息】 statistics”的簡(jiǎn)稱)

$ git log --stat
git log 與 git log --stat對(duì)比

git log -p/--patch

顯示對(duì)文件作出實(shí)際更改的選項(xiàng)。該選項(xiàng)是 --patch,可以簡(jiǎn)寫為 -p?!狙a(bǔ)丁信息】

$ git log -p
git log -p

帶注釋的 git log -p 輸出:

  • ?? - 正在顯示的文件
  • ?? - 文件第一版的哈希值和第二版的哈希值
    • 通常不重要,因此可以忽略
  • ?? - 文件的舊版本和當(dāng)前版本
  • ?? - 添加的行所在的位置以及添加了多少行
    • -15,83 表示舊版本(用 - 表示)從第 15 行開始,顯示了 83 行
    • +15,85 表示當(dāng)前版本(用 + 表示)從第 15 行開始,現(xiàn)在變成了 85 行...這 85 行顯示在下方
  • ?? - 在 commit 中實(shí)際進(jìn)行的更改
    • 用紅色標(biāo)示并以減號(hào) (-) 開頭的行是位于文件原始版本中,但是被 commit 刪除的行
    • 用綠色標(biāo)示并以加號(hào) (+) 開頭的行是 commit 新加的行

鏈接
使用 -p 生成補(bǔ)?。ㄓⅲ?/a>

以上命令可以組合使用:

另外,如果知道SHA值,可以直接查找:

$ git log [SHA]
$ git log -p fdf5493
...
git show

git show 顯示有關(guān)給定提交的信息,但需要向其提供提交ID也被稱為SHA,該命令就會(huì)顯示有關(guān)這一提交的信息。若僅運(yùn)行 git show 則只顯示最近的commit。

$ git show
$ git show fdf5493

作用:
git show 命令將僅顯示一個(gè) commit。因此,你看不到任何其他 commit,
git show 命令的輸出和 git log -p 命令的完全一樣。因此默認(rèn)情況下,git show 會(huì)顯示:

  • commit
  • 作者
  • 日期
  • commit 消息
  • 補(bǔ)丁信息

但是,git show 可以與我們了解過的大部分其他選項(xiàng)一起使用:

  • --stat - 顯示更改了多少文件,以及添加/刪除的行數(shù)
  • -p 或 --patch - 顯示默認(rèn)補(bǔ)丁信息,但是如果使用了 --stat,將不顯示補(bǔ)丁信息,因此傳入 -p 以再次添加該信息
  • -w - 忽略空格變化

五、向倉(cāng)庫(kù)添加commit

git add

將文件從工作目錄添加到暫存區(qū)中。

倉(cāng)庫(kù)模型
暫存文件

在終端上運(yùn)行以下命令,使用 git add 將 index.html 添加到暫存區(qū):

$ git add index.html

注意: 這里僅添加了 index.html 文件。稍后我們將添加 CSS 和 JavaScript 文件。

git status

Changes to be committed

輸出結(jié)果中現(xiàn)在出現(xiàn)了全新的區(qū)域:"Changes to be committed"區(qū)域!這一新的"Changes to be committed"區(qū)域顯示了位于暫存區(qū)的文件!目前只顯示了 index.html 文件,因此暫存區(qū)只有這個(gè)文件。繼續(xù)這一思路,如果我們現(xiàn)在提交 commit,則只有 index.html 文件會(huì)被提交。

提示:你注意到"Changes to be committed"下方的幫助文本了嗎?它提示 (use "git rm --cached <file>..." to unstage),也就是當(dāng)你不小心運(yùn)行了 git add 并提供了錯(cuò)誤文件,它會(huì)提示你應(yīng)該怎么操作。

順便提下,git rm --cached 與 shell 的 rm 命令不同。git rm --cached 不會(huì)破壞任何屬于你的文件,它只是從暫存區(qū)刪掉了文件。

此外,幫助文本中出現(xiàn)了"unstage"(撤消暫存)字眼。將文件從工作目錄移到暫存區(qū)叫做"staging"(暫存)。如果已移動(dòng)文件,則叫做"staged"(已暫存)。從暫存區(qū)將文件移回工作目錄將"unstage"(撤消暫存)。如果你閱讀的文檔中提示“stage the following files”,則表明你應(yīng)該使用 git add 命令。

暫存剩余的文件

index.html 文件已暫存。我們?cè)贂捍媪硗鈨蓚€(gè)文件?,F(xiàn)在我們可以運(yùn)行以下命令:

$ git add css/app.css js/app.js

但是要輸入的內(nèi)容好多啊。我們可以使用一個(gè)特殊的命令行字符:

句點(diǎn) .

句點(diǎn)指代當(dāng)前目錄,可以用來表示所有文件和目錄(包括所有嵌套文件和目錄?。?。

$ git add css/app.css js/app.js
# 等同于
$ git add .

唯一要注意的是,你可能會(huì)不小心包含多余的文件?,F(xiàn)在,我們希望同時(shí)暫存 css/app.css 和 js/app.js,因此運(yùn)行該命令沒問題?,F(xiàn)在假設(shè)你向 img 目錄添加了一些圖片,但是暫時(shí)不想暫存這些圖片。運(yùn)行 git add . 將暫存這些圖片。如果你暫存了不想暫存的文件,git status 會(huì)告訴你撤消暫存需要用到的命令。

暫存剩余的文件
$ git add .
小結(jié)

git add 命令用于將文件從工作目錄移到暫存區(qū)。

$ git add <file1> <file2> … <fileN>

此命令:

  • 可接受多個(gè)文件名(用空格分隔)
  • 此外,可以使用句點(diǎn) . 來代替文件列表,告訴 git 添加當(dāng)前目錄至?xí)捍鎱^(qū)(以及所有嵌套文件)
  • 其實(shí)也可以用 git add --all
提交 Commit

git commit

git commit 將文件從暫存區(qū)取出并保存到本地倉(cāng)庫(kù)區(qū),也就是你實(shí)際將要提交的地方。

盡量不要用此語(yǔ)句,運(yùn)行這條命令將會(huì)打開你在初始配置時(shí)的代碼編輯器(如未配置,則默認(rèn)打開Vim編輯器,Vim 很受 Unix 或 Linux 系統(tǒng)用戶的歡迎,但是對(duì)新用戶來說,并不太好用。如何退出 Vim )。

配置自己的編譯器:

$ git config --global core.editor <your-editor's-config-went-here>
<!--Using Notepad++ as your editor-->
$ git config --global core.editor "'C:/Program Files (x86)/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"

假如配置了自己的編譯器,當(dāng)你快速切回終端,會(huì)看到終端凍結(jié)了,并等待你在彈出的代碼編輯器完成編輯。不用擔(dān)心。當(dāng)我們向代碼編輯器添加必要的內(nèi)容,并最終關(guān)閉代碼編輯器窗口后,終端將不再凍結(jié),并回到正常狀況。

代碼編輯器 Commit 消息解釋說明
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
#
# Initial commit
#
# Changes to be committed:
# new file:   css/app.css
# new file:   index.html
# new file:   js/app.js
#

第一段精確地告訴了我們需要執(zhí)行的操作 - 我們需要為該 commit 提供一條消息。此外 ,任何以字符 # 開頭的行將被忽略。在后面還提示:這將是初始 commit。最后,給出了將提交 commit 的文件列表。

因?yàn)檫@是存儲(chǔ)庫(kù)的第一個(gè) commit,我們將使用 commit 消息 "Initial commit"。文本 "Initial commit" 并不特殊,只是第一個(gè) commit 的常用消息。如果你想使用其他消息,完全可以!

在代碼編輯器的第一行輸出 commit 消息:(隨便)如:first initial ---這是你提交的文件修改的信息

完成提交

(若是配置的自定義的編輯器)現(xiàn)在保存文件并關(guān)閉編輯器窗口(只關(guān)閉面板/標(biāo)簽頁(yè)還不夠,你還需要關(guān)閉 git commit 命令打開的代碼編輯器窗口)。

(若是Vim編輯器)wq 是退出。
(注:以下是我自己的git bash窗口)

  1. 進(jìn)入Vim編輯器


    進(jìn)入Vim編輯器
  2. insert信息

    insert信息

  3. 退出

    退出

回到終端:



終于提交了第一個(gè) commit(注:只是提交到本地倉(cāng)庫(kù))。

使用 -m 選項(xiàng)繞過編輯器

提示:如果你要編寫的提交說明很簡(jiǎn)短,不想等打開代碼編輯器后再輸入信息,可以直接在命令行中使用 -m 選項(xiàng)傳入信息:

<!--常用這條語(yǔ)句-->
$ git commit -m "Initial commit"

注意,要提交 commit,待提交的文件必須位于暫存區(qū)。要將文件從工作目錄移到暫存區(qū)用 git add。

所以當(dāng)你修改文件后就執(zhí)行 git commit 則輸出:"no changes added to commit"。這是因?yàn)槟銢]有使用 git add 將文件從工作目錄移到暫存區(qū)。

那么何時(shí)進(jìn)行commit呢?

這種情況事自身情況而定,一般情況下是每寫完一個(gè)功能提交一下,這樣當(dāng)一個(gè)更改有bug,你需要撤消該更改時(shí),則不用同時(shí)撤消另一個(gè)更改。

深入研究
編寫良好的提交說明:

參考:

git diff

git diff 顯示文件兩個(gè)版本之間的差異,輸出與git log -p 輸出一樣。"顯示尚未 commit 的更改"。

<!--git diff 命令可以用來查看已被加入但是尚未提交的更改。-->
$ git diff

git log -p 其實(shí)就是在后臺(tái)使用了 git diff

讓git忽略某些文件

創(chuàng)建 .gitignore 文件,并添加到項(xiàng)目跟目錄。你只需列出希望 git ignore(忽略,不跟蹤)的文件名,git 將忽略這些文件。

要添加忽略的文件將用到 通配符 |

通配符允許你使用特殊的字符來表示某些格式/字符。在 .gitignore 文件中,你可以使用:

  • 空白行作為空格
  • # - 將行標(biāo)記為注釋
  • * - 與 0 個(gè)或多個(gè)字符匹配(如:*.jpg將忽略所有后綴為jpg的文件)
  • ? - 與 1 個(gè)字符匹配(如:git 忽略"be?rs”,則將忽略bears、beers,但是beavers不能忽略)
  • [abc] - 與 a、b 或 c 匹配
  • ** - 與嵌套目錄匹配 - a/**/z 與以下項(xiàng)匹配
    • a/z
    • a/b/z
    • a/b/c/z

忽略文件 - 英 | git 圖書

六、標(biāo)簽、分支 和 合并

標(biāo)簽 git tag

git tag 為特定提交添加標(biāo)簽,標(biāo)簽是提交的額外標(biāo)記,可以指示有用信息。

$ git tag -a v1.0

上述命令將打開代碼編輯器,并等待你為標(biāo)簽輸入信息。


打開自配置代碼編譯器(VS Code)

注意:在上述命令 (git tag -a v1.0) 中,使用了 -a 選項(xiàng)。該選項(xiàng)告訴 git 創(chuàng)建一個(gè)帶注釋的標(biāo)簽。如果你沒有提供該選項(xiàng)(即 git tag v1.0),那么它將創(chuàng)建一個(gè)輕量級(jí)標(biāo)簽。

建議使用帶注釋的標(biāo)簽,因?yàn)樗鼈儼舜罅康念~外信息,例如:

  • 標(biāo)簽創(chuàng)建者
  • 標(biāo)簽創(chuàng)建日期
  • 標(biāo)簽消息

因此,你應(yīng)該始終使用帶注釋的標(biāo)簽。

驗(yàn)證標(biāo)簽

保存并退出編輯器后,命令行上什么也不會(huì)顯示。那么如何知道已經(jīng)向項(xiàng)目中添加了標(biāo)簽?zāi)??只需輸?git tag,命令行會(huì)顯示倉(cāng)庫(kù)中的所有標(biāo)簽。

這時(shí)利用git log 將看不到添加的標(biāo)簽。所以需要用到 git log --decorate 。--decorate 選項(xiàng)將顯示默認(rèn)視圖隱藏起來的一些詳情。

在 2.13 版 git 中,log 命令已改為自動(dòng)啟用 --decorate 選項(xiàng)。這意味著,你不需要在命令中包含 --decorate 選項(xiàng),因?yàn)樗呀?jīng)自動(dòng)包含了!因此下面的命令輸出結(jié)果完全一樣:

$ git log --decorate
$ git log

輸出結(jié)果顯示的 tag: v1.0 了嗎?這就是標(biāo)簽!標(biāo)簽與 commit 相綁定。因此,該標(biāo)簽與 commit 的 SHA 位于同一行。

刪除標(biāo)簽

如果將標(biāo)簽消息中的某個(gè)字打錯(cuò)了,或標(biāo)簽名稱打錯(cuò)了(輸入 v0.1,而不是 v1.0),如何修正這個(gè)錯(cuò)誤?最簡(jiǎn)單的方法是刪除這個(gè)標(biāo)簽并重新創(chuàng)建。

可以通過輸入 -d 選項(xiàng) (表示 delete 刪除?。┘由蠘?biāo)簽名稱來刪除 git 標(biāo)簽:

$ git tag -d v1.0
向以前的 commit 添加標(biāo)簽

運(yùn)行 git tag -a v1.0 將為最近的 commit 添加標(biāo)簽。但是如果你想向倉(cāng)庫(kù)中很久之前的 Commit 添加標(biāo)簽?zāi)兀?/p>

只需提供要添加標(biāo)簽的 commit 的 SHA 即可!

$ git tag -a v1.0 a87984
深入研究
分支 git branch

git branch 創(chuàng)建分支,用于并行開發(fā)項(xiàng)目的不同功能。而不會(huì)對(duì)哪些提交屬于哪個(gè)功能感到困惑。

一個(gè)關(guān)于分支的視頻

注:gti branch [dev] [SHA] 在某個(gè)SHA值處創(chuàng)建一個(gè)分支dev

$ git branch
//1.列出倉(cāng)庫(kù)中的所有分支名稱
//2.創(chuàng)建新的分支 git branch [name]
//3.刪除分支 git branch -d [name]
創(chuàng)建分支
//創(chuàng)建一個(gè)叫做"sidebar"的分支
$ git branch sidebar

//補(bǔ)充:創(chuàng)建分支并切換到這個(gè)分支
$ git checkout -b dev
切換分支

git checkout 可以在不同的分支和標(biāo)簽之間進(jìn)行切換。

注意,在進(jìn)行 commit 時(shí),該 commit 將添加到當(dāng)前分支上。

雖然我們創(chuàng)建了新的 sidebar 分支,但是并沒有在這個(gè)分支上。如果我們現(xiàn)在進(jìn)行 commit 的話,該 commit 將添加到 master 分支,而不是 sidebar 分支。所以要在分支之間進(jìn)行切換,我們需要使用以下命令。

$ git checkout sidebar

運(yùn)行該命令將:

  • 從工作目錄中刪除 git 跟蹤的所有文件和目錄
    • (git 跟蹤的文件存儲(chǔ)在倉(cāng)庫(kù)中,因此什么也不會(huì)丟失)
  • 轉(zhuǎn)到倉(cāng)庫(kù),并提取分支指向的 commit 所對(duì)應(yīng)的所有文件和目錄

查看具體分支信息:git log --oneline --decorate

活躍分支(當(dāng)前分支)
  1. 當(dāng)前提示符上就有當(dāng)前分支(如綠色的單詞)
  2. 通過git branch查看,前面有 * 號(hào)的即為當(dāng)前分支
刪除分支

分支用來進(jìn)行開發(fā)或?qū)?xiàng)目進(jìn)行修正,不會(huì)影響到項(xiàng)目(因?yàn)楦氖窃诜种线M(jìn)行的)。在分支上做出更改后,你可以將該分支組合到 master 分支上(這種“分支組合過程”叫做“合并”(merge)。

合并了分支的更改后,你可能不再需要該分支了。如果你想刪除分支,可以使用 -d 選項(xiàng)。

//刪除"sidebar"分支
$ git branch -d sidebar

注意,無法刪除當(dāng)前所在的分支。因此要?jiǎng)h除 sidebar 分支,你需要切換到 master 分支,或者創(chuàng)建并切換到新的分支。

補(bǔ)充:如果某個(gè)分支上有任何其他分支上都沒有包含的 commit(也就是這個(gè) commit 是要被刪除的分支獨(dú)有的),git 不會(huì)刪除該分支。如果你創(chuàng)建了 sidebar 分支,向其添加了 commit,然后嘗試使用 git branch -d sidebar 刪除該分支,git 不會(huì)讓你刪除該分支,因?yàn)槟銦o法刪除當(dāng)前所在的分支。如果你切換到 master 分支并嘗試刪除 sidebar 分支,git 也不會(huì)讓你刪除,因?yàn)?sidebar 分支上的新 commit 會(huì)丟失!要強(qiáng)制刪除,你需要使用大寫的 D 選項(xiàng) - git branch -D sidebar。

同時(shí)查看所有分支

我們?cè)?git log 輸出結(jié)果中看不到其他分支,觸發(fā)切換到某個(gè)分支。如果能在 git log 輸出結(jié)果中看到所有分支,是不是很棒?

我們將使用新的 --graph 和 --all 選項(xiàng):

$ git log --oneline --decorate --graph --all

--graph 選項(xiàng)將條目和行添加到輸出的最左側(cè)。顯示了實(shí)際的分支。--all 選項(xiàng)會(huì)顯示倉(cāng)庫(kù)中的所有分支。

總結(jié):
// 列出所有分支
$ git branch
// 創(chuàng)建新的"dev"分支
$ git branch dev
// 重命名當(dāng)前分支
$ git branch -m [new_name]
// 切換到"dev"分支
$ git checkout dev
// 創(chuàng)建并切換到"side"分支
$ git checkout -b side
// 刪除"side"分支
$ git branch -d side
// 強(qiáng)制刪除"side"分支
$ git branch -D side

// 創(chuàng)建新的分支 并切換,且與master分支同節(jié)點(diǎn)
$ git checkout -b footer master
// 查看log信息 包含標(biāo)簽
$ git log --oneline --decorate
// 查看所有l(wèi)og信息 包含標(biāo)簽 及所有分支
$ git log --oneline --decorate --graph --all

深入研究

合并 git merge

git merge 將不同分支上的更改自動(dòng)合并在一起。

注意普通合并 和 快進(jìn)合并

當(dāng)你要合并分支時(shí),務(wù)必知道當(dāng)前位于哪個(gè)分支上。注意,合并分支會(huì)提交 commit。如在mater分支則合并后將把更改合并到master上,如在dev分支上則合并后將更改合并到了dev分支。

如果你在錯(cuò)誤的分支上進(jìn)行了合并,可以使用以下命令撤消合并:

git reset --hard HEAD^

(確保包含 ^ 字符!它屬于“相對(duì) commit 引用”并表示“父 級(jí) commit”。)

合并指令
$ git merge <name-of-branch-to-merge-in>

發(fā)生合并時(shí),git 將:

  • 查看將合并的分支
  • 查看分支的歷史記錄并尋找兩個(gè)分支的 commit 歷史記錄中都有的單個(gè) commit
  • 將單個(gè)分支上更改的代碼行合并到一起
  • 提交一個(gè) commit 來記錄合并操作

注:分支名是自定義的,以下所說的分支都是自己命名的。

快進(jìn)合并

在我們的項(xiàng)目中,我們檢出了 master 分支,我希望它擁有 footer 分支上的更改。用語(yǔ)言描述的話就是“我想要合并 footer 分支。”。注意表述“合并…”;在進(jìn)行合并時(shí),另一個(gè)分支上的更改將出現(xiàn)在當(dāng)前檢出的分支上。

我再?gòu)?qiáng)調(diào)下,當(dāng)我們合并時(shí),我們將其他分支合并到當(dāng)前(檢出的)分支上。我們不是將兩個(gè)分支合并到一個(gè)新的分支上。也不是將當(dāng)前分支合并到其他分支上。

因?yàn)?footer 直接在 master 前面,因此這種合并最簡(jiǎn)單。將 footer 合并到 master 中將導(dǎo)致快進(jìn)合并(Fast-forward merge)??爝M(jìn)合并將使當(dāng)前檢出的分支向前移動(dòng),直到它指向與另一個(gè)分支(這里是 footer)指向的 commit 一樣為止。

要合并 footer 分支,運(yùn)行:

$ git merge footer
普通合并

要合并 sidebar 分支,確保你位于 master 分支上,并運(yùn)行:

$ git merge sidebar

因?yàn)楹喜⒌氖莾蓚€(gè)完全不一樣的分支,因此將提交 commit。在進(jìn)行 commit 時(shí),需要提供 commit 消息。因?yàn)檫@是合并 commit,因此已經(jīng)提供了默認(rèn)消息。你也可以更改消息,但通常都會(huì)直接使用默認(rèn)的合并 commit 消息。因此當(dāng)你 "的代碼編輯器打開" 并包含該消息時(shí),直接關(guān)閉編輯器以確認(rèn)使用該 commit 消息。


小節(jié):

HEAD 指針?biāo)赶虻姆种⒕哂泻喜?commit。

$ git merge <other-branch>

合并有以下兩種類型:

  • 快進(jìn)合并 – 要合并的分支必須位于檢出分支前面。檢出分支的指針將向前移動(dòng),指向另一分支所指向的同一 commit。
  • 普通類型的合并
    • 兩個(gè)完全不同的分支被合并
    • 創(chuàng)建一個(gè)合并 commit
深入研究
合并沖突

大部分情況下,git 將能夠成功地合并分支。但是,有時(shí)候 git 無法完全自動(dòng)地進(jìn)行合并。合并失敗時(shí),就稱為合并沖突。

如果出現(xiàn)合并沖突,git 將嘗試盡可能合并多的內(nèi)容,然后將留下特殊選項(xiàng)(例如 >>> 和 <<<),告訴你(沒錯(cuò),告訴作為程序員的你?。┬枰獜暮翁幨謩?dòng)修復(fù)。

什么導(dǎo)致了合并沖突

正如你所知道的,git 會(huì)跟蹤文件中的代碼行。如果完全相同的行在不同的文件中更改了,將產(chǎn)生合并沖突。例如,你在兩個(gè)分支上都更改了標(biāo)題,因此 git 根本不知道你要保留哪個(gè)標(biāo)題。它肯定不會(huì)隨機(jī)選擇一個(gè)標(biāo)題!

小知識(shí):在最近修改 master 分支的 commit 前面創(chuàng)建一個(gè) heading-update 分支。

創(chuàng)建一個(gè)不是從 master 分支上分叉的分支。如果我們?cè)趶?master 分支上分叉的分支上做出更改,那么該更改將在此更改前面,git 將直接使用該更改,而不是使用我們剛剛在 master 上做出的更改。因此我們需要將該分支“放在過去”。

我們創(chuàng)建一個(gè)位于最近 commit 之前的 commit 上的分支。使用 git log 獲取上一個(gè) commit 的 SHA,并在該 commit 上創(chuàng)建一個(gè)分支。

  1. 首先獲取SHA值
    git log --oneline --decorate --graph --all
  2. 創(chuàng)建分支
    git branch heading-update 25cdbb6
  3. 切換到分支
    git checkout heading-update

在創(chuàng)建 heading-update 分支后,我的 git log 輸出結(jié)果如下所示:
git log --oneline --decorate --graph --all


修改并提交:



合并后,將發(fā)生沖突:



進(jìn)入代碼進(jìn)行手動(dòng)合并即可。
合并沖突指示符解釋
  • <<<<<<< HEAD 此行下方的所有內(nèi)容(直到下個(gè)指示符)顯示了當(dāng)前分支上的行 或者 原始內(nèi)容
  • ======= 表示原始行內(nèi)容的結(jié)束位置,之后的所有行(直到下個(gè)指示符)是被合并的當(dāng)前分支上的行的內(nèi)容,可以說 被修改的內(nèi)容
  • >>>>>>> heading-update 是要被合并的分支(此例中是 heading-update 分支)上的行結(jié)束指示符

注意在刪除代碼解決沖突時(shí)(提示符也要?jiǎng)h除),自己想保留哪些代碼,然后保存文件,暫存文件(add),提交commit

深入研究

七、 撤銷與更改

更改最后一個(gè) commit

git commit --amend 可以更改最近的提交

  • 編輯文件
  • 保存文件(編輯器一般會(huì)自動(dòng)保存Ctrl+S)
  • 暫存文件(git add .)
  • 運(yùn)行 git commit --amend

當(dāng)然你也可以執(zhí)行新的 commit 即git commit,但是這樣就會(huì)出現(xiàn)兩個(gè) commit 執(zhí)行完全相同的任務(wù)

還原 revert

git revert [SHA] 撤銷在該提交中做出的更改,同時(shí)在該提交中添加的行將被刪除。

運(yùn)行 git revert [SHA](隨即彈出代碼編輯器,以便編輯/確認(rèn)提供的 commit 消息).輸出結(jié)果顯示了被還原的commit 中提交的說明,注意它還創(chuàng)建了一個(gè)新的commit來記錄這一更改。

深入研究
重置 reset

git reset 按順序刪除提交,潛在危險(xiǎn):它將會(huì)從倉(cāng)庫(kù)中刪除項(xiàng)目。

重置(reset) 似乎和 還原(revert) 相似,但它們實(shí)際上差別很大。還原會(huì)創(chuàng)建一個(gè)新的 commit,并還原或撤消之前的 commit。但是重置會(huì)清除 commit!

好消息是:git 會(huì)在完全清除任何內(nèi)容之前,持續(xù)跟蹤大約 30 天。要調(diào)用這些內(nèi)容,你需要使用 git reflog 命令。請(qǐng)參閱以下鏈接以了解詳情:

深度研究
以下內(nèi)容了解即可:
相關(guān) commit 引用

有時(shí)候你可能需要引用相對(duì)于另一個(gè) commit 的 commit。例如,有時(shí)候你需要告訴 git 調(diào)用當(dāng)前 commit 的前一個(gè) commit,或者是前兩個(gè) commit。我們可以使用特殊的“祖先引用”字符來告訴 git 這些相對(duì)引用。這些字符為:

  • ^ – 表示父 commit
  • ~ – 表示第一個(gè)父 commit

我們可以通過以下方式引用之前的 commit:

  • 父 commit – 以下內(nèi)容表示當(dāng)前 commit 的父 commit
    • HEAD^
    • HEAD~
    • HEAD~1
  • 祖父 commit – 以下內(nèi)容表示當(dāng)前 commit 的祖父 commit
    • HEAD^^
    • HEAD~2
  • 曾祖父 commit – 以下內(nèi)容表示當(dāng)前 commit 的曾祖父 commit
    • HEAD^^^
    • HEAD~3

^ 和 ~ 的區(qū)別主要體現(xiàn)在通過合并而創(chuàng)建的 commit 中。合并 commit 具有兩個(gè)父級(jí)。對(duì)于合并 commit,^ 引用用來表示第一個(gè)父 commit,而 ^2 表示第二個(gè)父 commit。第一個(gè)父 commit 是當(dāng)你運(yùn)行 git merge 時(shí)所處的分支,而第二個(gè)父 commit 是被合并的分支。

如:

* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
*   796ddb0 Merge branch 'heading-update'
|\  
| * 4c9749e (heading-update) Set page heading to "Crusade"
* | 0c5975a Set page heading to "Quest"
|/  
*   1a56a81 Merge branch 'sidebar'
|\  
| * f69811c (sidebar) Update sidebar with favorite movie
| * e6c65a6 Add new sidebar content
* | e014d91 (footer) Add links to social media
* | 209752a Improve site heading for SEO
* | 3772ab1 Set background color for page
|/  
* 5bfe5e7 Add starting HTML structure
* 6fa5f34 Add .gitignore file
* a879849 Add header to blog
* 94de470 Initial commit

我們來看看如何引用一些之前的 commit。因?yàn)?HEAD 指向 9ec05ca commit:

  • HEAD^ 是 db7e87a commit
  • HEAD~1 同樣是 db7e87a commit
  • HEAD^^ 是 796ddb0 commit
  • HEAD~2 同樣是 796ddb0 commit
  • HEAD^^^ 是 0c5975a commit
  • HEAD~3 同樣是 0c5975a commit
  • HEAD^^^2 是 4c9749e commit(這是曾祖父的 (HEAD^^) 第二個(gè)父 commit (^2))

那么 HEAD~6 引用的是哪個(gè)commit?
答案是:209752a
(提示利用最左邊的*號(hào))

那么 HEAD~4^2 引用的是哪個(gè)commit?
答:f69811c
(HEAD~4引用的是當(dāng)前分支的第四個(gè)父commit,然后^2告訴我們他是合并commit的第二個(gè)父commit(被合并的那個(gè)commit!))

git reset 命令
$ git reset <reference-to-commit>

git reset 的選項(xiàng)

  • git reset --mixed HEAD~1 (默認(rèn)項(xiàng)=git reset HEAD~1)
    • 將HEAD移到前一個(gè)提交,當(dāng)前提交所作的更改將會(huì)存留于工作區(qū),這時(shí)在暫存文件(add)并提交(commit)將會(huì)獲得相同的提交內(nèi)容,只是SHA將改變,因?yàn)樘峤坏臅r(shí)間戳不同。
  • git reset --soft HEAD~1
    • 將HEAD移到前一個(gè)提交,當(dāng)前提交所作的更改將會(huì)存留于暫存區(qū),這時(shí)只需重新提交(commit)將會(huì)獲得相同的提交,SHA改變。
  • git reset --hard HEAD~1
    • 將HEAD移到前一個(gè)提交,當(dāng)前提交所作的更改將會(huì)刪除。

其實(shí)我們?cè)谶M(jìn)行任何重置操作之前,可以在最近的 commit 上創(chuàng)建一個(gè) backup 分支,因此如果出現(xiàn)錯(cuò)誤,可以返回這些 commit:
git branch backup

再次聲明:參考 Udacity -侵刪



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容