(參考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
在命令行工具中運(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。
深入研究
- Git 內(nèi)部原理 - 底層命令和高層命令 : 英 | 中
- 自定義 Git - Git Hooks - 英 | 中
實(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 -p/--patch
顯示對(duì)文件作出實(shí)際更改的選項(xiàng)。該選項(xiàng)是 --patch,可以簡(jiǎn)寫為 -p?!狙a(bǔ)丁信息】
$ 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 新加的行
以上命令可以組合使用:
- git log -p --stat 將在補(bǔ)丁信息上方顯示統(tǒng)計(jì)信息。實(shí)際上,順序并不重要。
- git log --stat -p 也會(huì)在補(bǔ)丁信息上方顯示統(tǒng)計(jì)信息。
- git log --stat --oneline 簡(jiǎn)約及統(tǒng)計(jì)信息。
- git log -p -w 將顯示補(bǔ)丁信息,但是不會(huì)突出顯示僅更改了空格的行。
另外,如果知道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ū)中。

暫存文件
在終端上運(yùn)行以下命令,使用 git add 將 index.html 添加到暫存區(qū):
$ git add index.html
注意: 這里僅添加了 index.html 文件。稍后我們將添加 CSS 和 JavaScript 文件。

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窗口)
-
進(jìn)入Vim編輯器
進(jìn)入Vim編輯器 -
insert信息
insert信息 -
退出
退出
回到終端:

終于提交了第一個(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 相關(guān)聯(lián) - 英
- 起步 - 初次運(yùn)行 Git 前的配置 (git book):英 | 中
編寫良好的提交說明:
參考:
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
六、標(biāo)簽、分支 和 合并
標(biāo)簽 git tag
git tag 為特定提交添加標(biāo)簽,標(biāo)簽是提交的額外標(biāo)記,可以指示有用信息。
$ git tag -a v1.0
上述命令將打開代碼編輯器,并等待你為標(biāo)簽輸入信息。

注意:在上述命令 (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
深入研究
- 添加標(biāo)簽:英 | 中 git 圖書
- git tag - 英 git文檔
分支 git branch
git branch 創(chuàng)建分支,用于并行開發(fā)項(xiàng)目的不同功能。而不會(huì)對(duì)哪些提交屬于哪個(gè)功能感到困惑。
注: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)前分支)

- 當(dāng)前提示符上就有當(dāng)前分支(如綠色的單詞)
- 通過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)合并在一起。
當(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 圖書
- git-merge git 文檔 (英)
- git 合并 Atlassian 博客 (英)
合并沖突
大部分情況下,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è)分支。
- 首先獲取SHA值
git log --oneline --decorate --graph --all- 創(chuàng)建分支
git branch heading-update 25cdbb6- 切換到分支
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來記錄這一更改。
深入研究
- git-revert git 文檔 (英)
- git revert Atlassian 教程 (英)
重置 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)參閱以下鏈接以了解詳情:
深度研究
- 來自 Git 文檔的 git-reset (英)
- 來自 Git Blog 的 Reset Demystified (英)
- 來自 Git Book 的 祖先引用 英 | 中
以下內(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 -侵刪





