【工具】Git 常用操作 – 備忘錄
- 2020 年 3 月 10 日
- 筆記
後面會把前端進階的課程內容都總結一遍。有些都是很常見的知識,但是為了梳理自己的知識樹,所以盡量模糊的地方都會記錄
筆記列表在公眾號右下角
Git 的內容已經很爛啦,我也把 Git 的原理相關的書看過研究過,但是奈何在項目中無非就用到那麼幾條命令,久而久之,其他命令就忘記了,當真正需要用的時候,又要搜索搜索學習學習,浪費太多時間。
網上資料很多,但是不一定適合自己,只好把用到概率大的命令統統詳細記一遍,免得以後不時之需,也方便我複習,舒服~
本來只打算記錄10條命令,學著學著發現其他命令也挺重要,然後就逐漸增加到 16 條命令,寫了我 三個多星期,哎……不過也好,以後就不用為 git 花這麼多心思了
這篇先記錄這 16 條命令
1、git add
2、git commit
3、git push
4、git branch
5、git checkout
6、git merge
7、git log
8、git diff
9、git show
10、git status
11、git rebase
12、git pull
13、git stash
14、git reset
15、git reglog
16、git revert
0
基本概念
平時我們寫的程式碼都是在工作區,沒有 add 的修改 和 沒有追蹤的文件都會放在這裡,以VScode 為例,工作區主要指這個地方

執行add命令後就是提交到了暫存區,就是 VSCode 的這個地方

再執行commit命令後就把程式碼提交到了本地版本庫
最後再執行push命令把本地程式碼提交到遠程版本庫。
1
git add
有什麼用?
將工作區內的文件提交到暫存區,添加文件進暫存區時,會根據文件的內容計算出HASH值
如果hash值存在,表示這個文件內容已經存在git倉庫中,只需要更新索引
如果hash不存在,則在objects目錄下新建一個blob文件,添加index索引。
? 有什麼用法?
1
— git add -a
提交所有變化,包括被修改,被刪除,新增的文件
2
— git add -u
提交被修改(modified),被刪除(deleted)文件,不包括新文件(new)
3
— git add .
提交新文件(new)和被修改(modified)文件,不包括被刪除(deleted)文件
2
git commit
有什麼用?
將暫存區里的改動(git add 提交到暫存區的內容)給提交到本地的版本庫,並生成一條提交記錄,每條記錄會配有一個40位哈希值的id
? 有什麼用法?
1
— git commit -m "你的註解"
提交一條記錄,並為這條記錄添加註解,就是解釋這條記錄做了什麼改動
2
— git commit -a -m "你的註解"
這條命令相當於省略了 git add -u 的操作,幫你先工作區中的被修改,被刪除的文件,不包括新文件 提交到暫存區
然後再根據你的注釋 生成提交記錄,並提交到版本庫
3
— git commit -amend
這個命令就大有用處了,雖然我根本沒用過,所以這才是我總結這篇文章的目的啊,你看多少盲點啊
大概有兩個用處
1、修改上一條提交記錄的註解
比如當你提交了記錄,但是突然發現這個記錄的註解寫得不是很好,你想重寫怎麼辦,用這個命令就對了
開始實操!
比如現在已經有一條記錄了,如下

現在我們想修改這條記錄的註解,直接輸入命令,然後跳出這個介面,如下,然後在這紅色標註的地方修改注釋ok了

修改完成後,我們來看下記錄,如下,可以看到已經被成功修改了

以為到這裡就完了嗎,沒錯,真的完了,但是要科普一下編輯頁面的操作,沒錯,就是輸入 –amend 命令後彈出的介面
我們來看下介面的簡單操作
1、進入編輯狀態:按 inert 鍵或者 按 i 鍵,進入編輯狀態,不要瞎按了,進入介面的時候還是只讀狀態
2、編輯完成操作:按 ESC 鍵,退出編輯狀態,按 :w 保存輸入的內容,按 :q 退出介面 ,不過通常我們可以直接按 :wq 保存並退出
3、注意是 :w ,:q,:wq ,前面有一個冒號的啊,不要省略了
4、放棄修改:按三個鍵,:e! ,放棄所有修改,從上次保存文件開始編輯
2、給上一條已經提交的記錄,額外新添加更多修改
比如我已經提交了一條記錄,新增了一個文件 1.txt
但是後面發現我少提交了一個文件 2.txt,現在我要把 2.txt 添加到上一條記錄上
開始操作!
看下當前已經提交的記錄修改的內容,只提交了 1.txt

現在來把 2.txt 也合併到上一條記錄中
1、git add 2.txt ,把 2.txt 也提交到暫存區
2、git commit –amend
跳出編輯介面,如果你不用修改注釋的話,就直接按 :q 退出介面就合併成功了,如果你要編輯就參考上面的操作說明

然後再查看記錄,如下,發現這條記錄已經增加了 2.txt 啦,合併成功!!

3
git push
有什麼用?
將 本地版本庫的分支 推送到 遠程主機上 指定的分支上
? 有什麼用法?
1
— git push origin master:master
一般形式為
git push <遠程主機名> <本地分支名>:<遠程分支名>
把 本地分支 master 推送到 遠程主機 origin 的 master 分支上,origin 就是倉庫地址
如果遠程分支名和 本地分支名一樣,通常我們可以省略掉遠程分支名
像這樣
git push origin master
2
— git push -u origin master:master
為本地分支 指定推送的默認主機 和 遠程分支
因為一般情況下,我們推送的時候,我們不可能寫很長的命令
git push origin master:master
所以通常會指定默認主機和遠程分支,這樣我們直接 git push 就可以完成推送了
而且這裡我們同樣可以省略 遠程分支名(如果和本地分支一樣)
如果我們沒有設置默認主機和分支,直接 git push,則會報錯

?Tip
在 Git2.0之前,我們可以不用設置默認主機和遠程分支,就可以直接使用 git push
但是在 Git2.0 之後,Git 修改了他們的默認 push 規則,把 push 的默認規則改成了 simple。表示,如果沒有指定當前分支的 遠程分支,那麼就會報錯
而 Git2.0之前,push 的默認規則是 matching,默認為本地分支指定了遠程同名的分支,也就是說,本地的分支叫 master,遠程的分支也是 master,那麼這兩者就會默認進行關聯,建立追蹤關係,本地 master 會自動推到遠程的 master
而在 Git2.0 之後,雖然名字一樣都是master,你也必須手動設置一遍,但是如果你手動設置的本地分支名字和 綁定過得遠程分支名字不一樣,同樣會報錯
比如你把本地的 test 和 遠程的 master 分支 建立追蹤關係,之後 你直接使用 git push 還是會報錯,因為 Git2.0 設置的 push 規則是 simple,規定 本地和遠程 分支名要一樣
如果你實在想名字不一樣,還想直接使用 git push,那麼你可以把 push 規則設置為 upstream
git config --global push.default upstream
3
— git push -f
忽略差異,強行推送本地分支,覆蓋遠程分支
一般團隊合作的時候,因為同事已經先推送了他的程式碼,此時我再推送的話,就會先拉取他的程式碼,並且處理差異,但是這條命令可以讓我們暴力推送,當然這麼做是肯定不行的。。。
4
— git push origin -d master
刪除遠程分支 ,這個master 就是遠程分支的 master
5
— git push –tag
推送本地打的標籤。
通常我們使用 git push 的時候,並不會把本地的標籤推送上去,所以我們需要額外去推送我們的標籤
標籤通常用來給 項目確定版本,所以會打上 v1.0.0 等標籤,其實也就是給 commit 提交記錄打上標籤,該標籤到上一個標籤之前所有的提交屬於一個版本,以此來區分程式碼版本
怎麼打上標籤呢?
git tag -a v2.0.0 -m "xxxx"
-a 後面接的是你要打的標籤名,通常設置為版本號
-m 後面接的是這次標籤的注釋
4
git branch
有什麼用?
管理本地分支和遠程分支
? 有什麼用法?
1
— git branch <分支名字>
創建分支
你創建分支成功之後,你仍然處在之前的分支,並沒有切換到新建的分支
2
— git branch -a
查看所有分支,包括本地分支和遠程分支

3
— git branch -r
查看遠程分支

4
— git branch -d <分支名字>: 刪除本地分支
如果你要同時刪除多個分支,空格隔開分支名字就行了

3
— git branch -D <分支名字>
強行刪除
如果刪除的時候分支被佔用或者有衝突的時候,可以強行刪除,只要你覺得沒影響
3
— git branch -d -r <遠程分支名字>
刪除遠程分支
3
— git branch -vv
查看本地分支對應的遠程分支,也就是他們的追蹤關係,如下圖的 本地 b2 對應 遠程 origin/rb2,本地 master 對應遠程 origin/rmaster

如果你的本地分支和遠程分支沒有建立追蹤關係,那麼是無法顯示遠程分支的,如上圖的 本地 b3 分支,沒有和遠程任何分支建立關係
3
— git branch -m <old 分支名> <new 分支名>
給分支重命名
3
— git branch –set-upstream-to=<遠程主機>/<遠程分支> <本地分支>
為 本地分支 綁定 默認遠程主機 和 遠程分支
如果你不寫本地分支,那麼就會默認為你當前所在的分支
比如你當前本地在 b2 分支上,你輸入命令
git branch –set-upstream-to=origin/rb2
那麼 本地分支 b2 就會遠程分支 rb2 建立了追蹤關係了
5
git checkout
有什麼用?
用來操作分支和文件
? 有什麼用法?
1
— git checkout <分支名字>
切換分支,這個當然簡單,比如,從 master 切換到 b2

但是我們要保證切換的分支都是存在的,否則就會報錯

2
— git checkout -b <分支名字>
創建分支並切換
之前我們創建的分支的時候,還要手動再切換過去有點麻煩,這裡有個一條龍命令完成 創建+切換

3
— git checkout <文件名字>
放棄工作區下指定文件的修改,就是放棄你還沒有提交的修改。
注意是修改,如果是新增文件,不會被刪除
3
— git checkout .
放棄工作區下所有文件的修改,同上,放棄的是沒有提交的修改
6
git merge
有什麼用?
用來合併分支內容的,比如把 分支 B 合併到 分支 A 上
? 有什麼用法?
1
— git merge <分支名稱>
用於合併分支
比如你想把 分支 A 合併到 主分支 master,你需要先切換到主分支 master,然後再合併分支 A
git checkout master
git merge A
默認合併模式是fast-forward,意思是 快進 ,相當於直接吞併分支,以上面為例
先說明,每個分支都有一個指針指向當前分支最新的提交,像這樣
一個主分支 master 有一個指針,分支 A 有一個指針

當 master 要合併 分支 A ,使用 快進 模式,master 指針會直接移到 分支A 的指針處
相當於 master 分支拐到 分支A的方向,後面的提交就 順著 分支A 原本的方向,而不是原本 master 的方向

雖然說是相當於 master 吞併了分支 A,但只是看著像而已,分支 A 仍然是獨立的,你繼續在 分支A 提交,會走到另一個方向,而不會和 master 互相影響
但是並不會都適合快進模式,比如當 master 已經有提交了,之前的虛擬提交 7 是真實存在的

這個時候肯定是無法直接吞併 分支 A 了,需要用到非快進模式,就是下一個指令會做的
也即是說,快進模式 只有在 分離出分支之後的 沒有任何提交 的分支適用
舉例就是
分支 B 分離 出 分支 C,然後在分支C 上工作,並且分離之後分支 B上沒有任何提交,此時 分支B 再 合併 分支C ,就可以使用快進模式
2
— git merge <分支名> –no-ff
ff 是 fast-forward 的意思,那麼 no-ff 就是禁用快進的意思,就是不用快進模式進行合併
複製 合併分支 的最後一個提交的內容 ,跟 被合併分支 的所有內容 進行合併,形成一個新的提交
這麼看肯定懵逼,必須要舉例來說,就是
比如說 master 分離出了 分支 A,然後在 分支A 上進行了幾次提交,然後切換回 master,要合併 分支A
使用 –no–ff 模式,就會複製master 的最後一個提交內容,然後和 分支A 所有內容進行合併,變成一個新的提交 ,提交放在 master 上

3
— git merge <分支名> –squash
把 被合併分支上所有的內容都 添加到你 合併分支的 暫存區,等待你手動提交
? 繼續舉例
仍然是 master 要合併 分支 A,然後分支A 進行了幾次提交,這幾次提交的內容是 新增了幾個文件
然後在 master 上 使用 squash 合併 分支A
然後,分支A 上的所有修改的內容,都會直接放到 master 的暫存區內(就是已經 add 而沒有 commit)
然後需要你手動提交,然後 分支A 的所有內容 就 變成 master 的一個全新的提交

3
— git merge –abort
回到文件 發生衝突之前的狀態
比如你同事提交了程式碼,然後你拉取更新,發現有很多衝突,然後一一解決了,然後運行發現項目跑不了了,卧槽,怎麼辦,想直接恢復到沒有拉取更新之前的狀態,那麼就用這個命令
比如一個項目中有一個 1.txt,是空白內容,你和同事同時拉下這個項目開始工作。
1、你在 1.txt 第一行 寫下 神仙朱
2、同事在 1.txt 第一行寫下 神仙豬崽,提交並上傳到遠程
3、然後你拉取他的程式碼,發生了衝突,你解決了,讓 1.txt 都保留了 神仙朱 和神仙豬崽(只解決衝突,未提交),然後再運行項目發現,特么,跑不起來了!!
4、你想要恢復到發生衝突之前的內容,就是 1.txt 只有 神仙朱
5、那麼你就用到這個命令 ,就可以滿足你。
需要注意的是!!!
如果你解決完衝突之後,就馬上提交了解決完衝突之後的文件,那麼這個命令是無法幫你恢復的!!!!!!!

所以提醒我們的是在解決完衝突之後,不要猴急的馬上提交,先運行項目觀察一下先
7
git log
有什麼用?
查看提交資訊
git log 的用法有很多,但是都沒有什麼難理解的,就是參數多而已,這裡就簡略列舉一些可能會用到的就好
? 有什麼用法?
1
— 過濾分類查找提交歷史
1、git log –committer=<pattern>
過濾得到 某個人 提交的歷史

2、git log –after=<date>
過濾得到某個時間之後的提交,時間的格式一般寫成 這樣 2020.02.17 02:46:01
並且這個時間是包含的
你過濾 2020.02.17 02:46:01 之後的提交,那麼 2020.02.17 02:46:01 這個時間的提交也包含在內

3、git log –before=<date>
過濾得到某個時間之前的提交,時間格式如上,時間也是包含的

4、git log –after=<date> –before=<date>
相應的,你可以獲取到某段時間內的提交
一定要注意這個 時間之前和之後,有時會有點懵逼
2020.02.17 02:46:01 之後的時間,肯定是比它晚比如是 2020.02.17 02:47:00
2020.02.17 03:00:00 之前的時間,肯定是比它早,比如是 2020.02.17 02:59:00
我之前把 after 和 before 的兩個時間理解倒了,好半天篩選不出來

5、git log –grep=<pattern>
過濾得到 含有某些注釋資訊的 提交
比如你有三條提交,分別如下
git commit -m "1"
git commit -m "2"
git commit -m "3"
然後你想過濾得到注釋資訊含有 3的的提交
git log –grep=3
然後就可以了
當然了,這是支援正則的,你可以放上符合你要求的正則
6、git log –merges
過濾得到 那麼屬於合併 的提交
比如說,如果我們的使用 merge 合併的時候,使用 –no-off 模式,就會自動生成一個合併提交,像這樣的帶有這種注釋的提交

然後我們想過濾得到這種提交,就可以使用這個命令

7、git shortlog
按照提交者,把提交分類

8、git log <filename>
查看單個文件的所有提交記錄

2
— 查看提交歷史的細節多少
1、git log
這是我們最常規的用法,顯示不多不少的提交資訊,包含 commitId,時間,作者,提交資訊等

2、git log –oneline
查看簡略的提交資訊,有時我們只是為了快速翻閱,對其他資訊無所謂,所以想把提交資訊一行顯示就可以了

3、git log –stat
查看更詳細的提交資訊,有時想看每個提交修改了什麼內容

4、git log –graph –oneline –decorate
顯示倉庫網路圖,可以讓我們可以直接地觀察分支情況,想必我們之前肯定看到過這種圖,五顏六色各種交叉的線

其實我們用這個命令就可以在本地查看這種線了

8
git diff
有什麼用?
用於查看比較 文件 各個修改之間的差異
? 有什麼用法?
1
— git diff
工作區不為空,暫存區為空,比較 工作區和最後一個提交 的相同文件 的 差異
工作區不為空,暫存區不為空,比較 工作區和 暫存區的相同文件 的 差異
工作區為空,暫存區不為空,不比較
總的老說,必須有工作區,暫存區為不空就和暫存區比,為空就和最後一個提交比
diff 的使用作用都是一樣的,區別只是在於 哪個誰跟誰比較而已
更重要的,我們需要了解一下 git diff 的輸出內容

我們看到內容大概有五類內容,逐個來說明
第一行:diff –git a/master.txt b/master.txt
表示正在比較的是哪個文件,a 版本的 master.txt(改動前的內容),b 版本的 master.txt(改動後的內容)
第二行:index e19f8de..4af7630 100644
表示比較的兩個版本的 哈希值,index 區域的 e19f8de 和 工作區中的 4af7630 。
index 表示 暫存區或者本地倉庫
100644 表示對象的模式,其中 100 表示普通文件,644 表示許可權『』
第三、四行:— a/master.txt +++ b/master.txt
— a/master.txt 中的 "—" 表示變動前的版本
+++ b/master.txt中的"+++"表示變動後的版本
第五行:@@ -5,3 +5,5 @@
-5,3:減號,表示這是改動前的文件。5表示第五行,3表示連續3行,從第 5 行開始連續 3行(包括第5行)
+5,5:加號,表示這是改動後的文件。5表示第五行,5表示連續5行,從第5行開始連續 5行(包括第5行)
所以總的意思是,改動前的文件的 第 5 行開始連續 3行 和 改動後的文件 的 從第5行開始連續 5行 這部分內容有差異
其他:+測試diff 輸出內容
+ 號,表示改動後的文件 新增的內容
– 號,表示改送後文件 刪除的內容
2
— git diff –cached
比較 暫存區 和 最後一個提交的相同文件 的 差異
同樣的,你也可以使用 cached 這個參數讓 暫存區 和 指定提交進行比較
git diff –cached commit-ID
3
— git diff HEAD
比較 工作區 加上暫存區 這兩個一起 和 最後一個提交的相同文件的差異
4
— 其他
1、git diff commit-ID
你可以指定某個 commit 來 比較工作區 和 指定提交 之間 相同文件的 差異
2、git diff commit-ID-1 commit-ID-2
也可以比較兩個 commit 的差異
3、git diff <branch-1> <branch-2>
也可以比較兩個分支的差異
太多了,就列舉這麼多先,估計也不太用得到
9
git show
有什麼用?
用於查看 各種類型對象的 相關資訊,包括 blob 對象,tree 對象,tag 標籤,commit 對象
但是說實話,我只用過show 來查看 commit 對象而已,其他對於我來說簡直盲區…..
我忍著巨大的耐心去弄懂這個東西,真的很不容易
commit,tag,blob,tree 這四個東西其實是有聯繫的
簡單來說
tag,就是 commit 的 別名,用於方便尋找。比如我們查看某個commit 通常要拿到它的 commitID,但是總歸是很麻煩,所以就可以給 commit 一個 別名,這樣我們可以直接通過查詢別名來 查看 commit 資訊
blob,表示項目中的文件,保存著 commit 時的文件的內容,比如說這個commit 我給文件新增了內容 "222",然後文件所有內容是 111 222,那麼這個 commit 的 blob 存的就是 111 222,後面的 commit 修改的文件內容不會影響前面 commit的 blob
tree,表示項目目錄結構,保存著 commit 時的 目錄結構,同理,後面commit修改的文件目錄不會影響前面commit的 tree,tree 下可能還會包含 blob 和 tree
commit ,就是提交,存儲了很多資訊,比如 tree,blob,作者,注釋等等,每個commit 相當於把 當時的目錄結構存在 tree,文件內容存在 blob,這樣才能在回退的時候,完全恢復內容
? 有什麼用法?
1
— git show commit-ID
查看單個提交,並且更加詳細些,包含了 diff 的輸出內容,該diff內容是和上一個提交進行比較的

同理地,我們也可以使用git show 來查看 tag,tree,blob
對於 tag 可以直接使用別名,對於tree 和 blob 則是使用 哈希值,像 commitID 一樣
查看tree 則是看到目錄結構和文件

查看 blob 則是 看到文件內容

2
— git show <commitID> –stat
顯示簡略的提交資訊。
如果你不用看到 diff 的內容的話可以這麼看就ok 了

10
git status
有什麼用?
用於查看 工作目錄和 暫存區的狀態
? 有什麼用法?
1
— git status
如下,顯示暫存區中修改(已經 add),工作區中修改(之前add 過,修改後沒有add),工作區中沒有追蹤的文件

2
— git status -s
簡略顯示工作區和暫存區的狀態,一行搞定

紅色表示在工作區(沒有add),綠色表示在暫存區(已經add)
然後各種字母的意思是
D,刪除
R,重命名
M,修改
A,新添加
U,更新但是沒有合併
C,複製
??,沒有追蹤
11
git rebase
有什麼用?
這個的作用太多了,可以用於合併分支,可以用於撤銷提交
? 有什麼用法?
這裡主要記錄是4個命令
1、git rebase
2、git rebase –onto
3、git rebase –abort
4、git rebase -i
1
— git rebase <要合併的分支名>
重設分支基礎點,作用跟 merge 一樣,都是合併分支,只不過 rebase 可以把手腳做得很乾凈,不會留下合併的痕迹
是怎麼不留痕迹呢?
相當於直接把 要合併的分支上的提交 搬到 目標分支 上
比如要把 分支 A 通過 rebase 合併到 master 上,相當於把 分支A 上的所有提交 直接搬到 master 上,而不是和 master 合併

搬過去的提交雖然是一毛一樣的,連哈希值都一樣,都是我測試修改 master 上的提交,並不會影響到 分支r 的提交,所以應該是複製了一份

這樣搬過去的樣子,像不像直接把 整個分支A 搬到 master 上?
所以這就是重設分支基礎點啦,從 基礎點3 變成基礎點 7 了
這樣 4 和 5 就好像是直接從 master 上提交的一樣,完全看不出 合併的 痕迹 了
而如果 merge 則會留下痕迹,因為會生成一個 合併的新提交,而 rebase 則不會產生任何多餘的 提交,就顯得乾淨許多了

但是 rebase 到底是怎麼操作的呢?
? 舉個例子
比如要把 分支 A rebase 到 master 上,按步驟來
1、切換到 分支 A,git checkout A
2、開始rebase,git rebase master
3、這時可能會產生很多衝突,解決完畢 並且 add 之後,不使用 git commit 提交,因為此時 rebase 還沒完成,所以使用 git rebase –continue 繼續操作。每次繼續可能又有新衝突,繼續按上面來就可以了
4、rebase 完成,切換到 master,git checkout master
5、讓 master指針 指向此時 master 分支最新的提交,git merge A
6、大功告成
2
— git rebase –onto <合併到哪個分支> <起始提交ID> <結尾提交ID>
這個的作用和 直接 rebase 一樣,但是 onto 的作用讓我們可以指定合併的 起點和 終點
不像上條命令那樣 直接合併整條分支,而是可以選其中幾個提交來進行合併
需要注意的是,合併時不包括 起始提交,但是包括 結尾提交
? 舉個例子
比如 分支A 上有提交 3,4,5,6 四個提交,我只要把 其中 4 和5 合併到 master 上
1、切換到分支 A 上,git checkout A
2、開始rebase,git rebase master commit-3-id commit-5-id(因為不包含起始提交,所以如果要 包含4,需要往前挪一個提交)
3、同樣的,這裡也可能會產生衝突,解決完畢 並且 add 之後,使用 git rebase –continue 繼續 rebase 過程,還有衝突,則照樣解決繼續
4、rebase 完成,此時我們截取的部分提交成了 野生分支,我們需要給它一個名分安放它
git checkout -b new_b

5、同樣的,master 沒有指向最新的提交,所以我們需要讓 master指針 指向此時 master 分支最新的提交,git merge new_b
6、大功告成,刪除 new_b ,git branch -d new_b
3
— git rebase –abort
如果在 rebase 的過程中,你後悔了,可以取消(有內鬼,終止交易), 然後 內容就會恢復到沒有開始rebase之前一樣
3
— git rebase -i
這個命令可以做的事情有很多
1、修改以前的提交
2、刪除很久以前的提交
3、多個 commit 合併為一個
….
可以做的還有很多,先列舉這些
1修改以前的提交
之前我們知道可以使用 git commit –amend 來修改最新的提交,但是如果想修改的提交不是最新的,這個命令就沒辦法了,所以今天使用 git rebase -i
舉例,比如我要修改 master 分支上的 倒數第三個提交
? 操作如下
1、切換到 master 分支,git checkout master
2、開始修改,指定到倒數第三個提交,git rebase -i HEAD~3(為什麼是~3 後面有)
3、跳到一個新頁面

上面的圖中出現了三個提交,這裡的提交是從舊到新的,就是說我要改的倒數第三個提交是 這個介面顯示的第一個(對比下 git log 顯示的提交)

4、因為我要修改,所以選擇 edit 模式,把 pick 修改成 edit(記得按下 insert 進入編輯模式,完成之後按下 Esc,然後英文模式下按下三個鍵 :wq 保存)

5、跳出這個介面,提示 rebase 過程現在停在 倒數第三個 提交的位置

6、查看下 日誌,真的是

7、項目中現在的內容是 倒數第三個提交的內容,現在你可以去項目中直接修改文件
我加了一行文字

8、然後就是 git add 和 git commit –amend,如果不用修改注釋就直接 :q 退出,需要修改就 :wq 退出,關於這個介面的編輯按鍵操作 已經在 前面的 git commit 命令中已經有記錄

9、繼續 rebase 過程,之前開始 rebase 的時候,git 已經提示我們,如果完成了,就使用 git rebase –continue 繼續完成 rebase 過程
如果執行命令發生衝突,便解決衝突,然後 add 之後,再使用 git rebase–continue 繼續完成 rebase 過程,如果仍有衝突,也是一樣解決。
直到最後提示成功

偏移符號
Git 中有兩個偏移符號 ^ 和 ~ ,用於方便指定當前提交 後面的提交,往後數的意思
^ 和 ~ 作用 一樣,但是用法稍有不同,都是跟在 commit 後面,這個commit 可以是哈希值,可以是指針(比如 master,HEAD)
1、^ 表示從指定地方往後數1個,^^ 表示往後數2個,反正就是 n個^ 就表示往後數 n 個
2、~ 則要跟上一個數字,~1 表示 從指定地方往後數1個,~2 表示往後數2個,,反正就是 ~n 就表示往後數 n 個
比如說,當前最新提交的id是 4850ba,往後數一個就是 4850ba^,往後數兩個就是 4850ba~2 也可以是 4850ba^^
可以看個圖了解下

看了偏移符號之後,就要清楚一下為什麼修改倒數第三個提交 是 執行 HEAD~3 了
首先 HEAD~3 是 倒數第四個提交

如果你要修改倒數第三個,那我肯定是要把指針挪到倒數第四個
所以你要修改哪個commit,就要在 git rebase -i 中指定往後挪一個 commit
2刪除很久以前的提交
和上面的操作是差不多的,只不過是選不同的模式而已,但是這個操作就簡單很多了
現在我們要刪除 master 分支倒數第三個提交
? 操作如下
1、切換到 master 分支,git checkout master
2、開始 rebase,git rebase -i HEAD~3,然後把 第一個 的模式改成 drop,或者可以直接把這一行刪掉

同樣,為什麼是 HEAD~3 ,上面 <修改以前的提交> 有說到過,就是你要操作哪個commit,就要在命令中往後指定一個 commit
你要刪除倒數第三個 commit,就是 HEAD~2,往後一個就是 HEAD~3
3、保存退出,如果有衝突,則像前面一樣解決 add 然後 git rebase –continue 繼續 reabse 過程
然後就會提示成功

查看日誌,已經不見那個提交了

3多個 commit 合併為一個
看例子,我要把 master 上最新的三個提交合成一個

? 操作如下
1、切換到master,git checkout master
2、開始 rebase,git rebase -i HEAD~3,進入我們熟悉的編輯介面(這個介面的操作之前說過了,這裡就不說了)
這裡為什麼是 HEAD~3,上面有說,可以回看
然後把後面兩個提交模式修改成 s,也就是 squash 的簡寫

第一個不用改會報錯,錯誤如下,意思大概是合成要保留一個最前面的提交

如果期間發生衝突,解決之後 ,add 之後使用 git rebase –continue,仍有衝突則照樣解決然後跟前面一樣就行
3、然後跳到另一個編輯的介面,含有這三個提交的資訊

我們把這三個注釋都刪掉,然後改成一條注釋,表明這條是我們合併的注釋

4、編輯完成,:wq 保存退出,提示成功了

5、看下日誌,已經合併成功了

rebase 總結
rebase 作用很大,但是也很麻煩,很難操作,要是一步弄錯了就慘了,所以我在項目中也沒有用過,但是肯定有必要學習的
其實還有很多用處,但是就先記住這幾個就好了
12
git pull
有什麼用?
拉取 遠程主機的 指定分支 的內容,再和 本地指定分支進行合併
? 有什麼用法?
1
— git pull origin master:master
一般形式是
git pull <遠程主機名> <本地分支名>:<遠程分支名>
如果本地分支和遠程分支一樣,可以省略遠程分支名和 冒號
git pull origin master
但是通常我們可以更加簡寫,git pull
只要我們為本地分支建立了默認主機和遠程分支追蹤關係的話
就像 git push -u 那樣
記住,你給哪條分支做了綁定,才能在某條分支上簡寫命令。如果你只給 master 分支建立默認主機和默認遠程分支,可以直接使用 git pull,但是切換到 分支 b1 直接使用 git pull ,仍然會報錯,除非 b1 也做綁定

2
— git pull –rebase
一般 git pull 默認是 git fetch + git merge
而 git pull –rebase = git fetch + git rebase
上面我們也說過 git merge 了,合併的時候可能會形成分叉,導致線路不好看
而 rebase 則會把拉取的提交直接放到 分支上,不會形成分叉,具體可以看上面 rebase 的指令
而使用 git pull –rebase 同樣會有衝突,衝突解決完之後 只需要
1、git add 解決衝突的文件
2、git rebase –continue 來繼續 rebase 過程
continue 之後又會有新的衝突,就繼續解決衝突,然後按上面的過程來就好了
直到 rebase 過程完成
13
git stash
有什麼用?
把改動臨時存起來,需要的時候再提取
這個命令還是簡單滴,而且能用到的機會很多,所以需要記住這個
? 有什麼用法?
1
— git stash
把 工作區和 暫存區的 內容都保存起來
看下圖,本來 工作區和暫存區有內容的,輸入 stash 之後,就被清空了,暫時保存到一個地方

並且我可以無限 stash,因為每次 stash 都會單獨存一份,不會互相衝突
2
— git stash save "注釋"
通常我們臨時報錯的時候,最好還是給這次操作添加一個注釋,說明正在做什麼東西

3
— git stash -u
上面臨時保存的文件只包括已經追蹤過的文件,新添加沒有追蹤過的文件是被排除在外的
不過加上 參數 -u 就可以解決
4
— git stash list
查看 stash 的存放列表

列表是時間倒序排列的,最新的 保存放在最前面
5
— git stash pop
我們已經知道怎麼保存了,當然要知道怎麼取出來了默認取出 stash 列表中最新的保存

但是需要注意的是,雖然 stash 可以把 暫存區和工作區的內容都保存起來
但是恢復的時候,會把所有內容都只恢復到工作區,就是原來在暫存區的現在也跑到工作區了,
6
— git stash pop stash@{n}
因為存在列表,所以我們當然可以選擇我們要恢復的 第幾個 stash 了
n 從0 開始,第 0 個表示最新,依次遞減
7
— git stash drop stash@{n}
刪除 stash,同上,我們可以選擇刪除第幾個
14
git reset
有什麼用?
reset 命令我們一般用來撤銷 提交,而它是怎麼撤銷的呢?它是通過移動 HEAD 指針來撤銷的,並且它會連帶 分支指針一起移動
一般 HEAD 都是指向最新提交的,當前項目內容就是 HEAD指向的那個提交時 的內容

如果我們把 HEAD 往後挪一個提交,那麼項目內容就會恢復到前一個提交時的內容

然後你再次提交的時候,就往另一條線走了,所以 提交5 就當成是撤銷了

? 有什麼用法?
1
— git reset –mixed <撤銷的提交往後挪一個>
撤銷提交的同時,把 工作區,暫存區,以及撤銷的那個提交 的內容,統統放進 工作區
並且,reset 默認的參數是 mixed,所以 加不加都一樣
怎麼指定我要撤銷哪個提交?
這裡命令中指定撤銷哪個提交,不是撤銷哪個就指定哪個,而是需要往後挪一個
比如你要撤銷 最新的提交 6a75b5,那麼命令中的參數就是
git reset –mixed 6a75b5^
因為你知道撤銷提交是通過 移動 HEAD 指針實現的,你要撤銷哪個提交,肯定是把 HEAD 指針挪到後面一位的位置
怎麼撤銷多個提交?
比如說你使用命令
git reset –mixed HEAD~3
那麼 HEAD 指針就移動到 倒數第四個位置,那麼 前面的所有 commit 都會被 撤銷

? 命令解釋示例如下
此時倉庫已經存在兩個提交,並且我添加一行數據放在暫存區,一行數據放在工作區

然後使用 reset 撤銷最新的提交

2
— git reset –hard <撤銷的提交往後挪一個>
撤銷提交的同時,清空工作區,清空暫存區,已經撤銷的提交內容也不要,反正就是啥都不要
同樣和上面例子一樣

使用 reset –hard

3
— git reset –soft <撤銷的提交往後挪一個>
撤銷提交的同時,保留工作區、暫存區不動, 把 撤銷的提交的內容 放進暫存區
同樣和上面例子一樣

然後使用 reset –soft 撤銷提交

15
git reflog
有什麼用?
查看 本地倉庫 的 指針的 移動記錄。
主要是可以幫助你找到你刪除過的分支,因為你刪除分支其實只是刪除指針,並沒有把提交也刪除了
所以只要找到 指針的移動記錄,就能找到之前它指向的提交,這樣就可以找回刪除的分支了
但是注意的是,如果 commit 所在的分支沒有任何指針,那麼在一段時間在 Git 自動回收,所以如果你知道自己刪錯了,最好快點操作,不然就沒戲了
你看下面這條分支就沒有任何指針

但是 1、2、3 存在 另一條有指針的分支上,所以沒事,而 4和 5 就變成野生的,過段時間就會被回收
? 有什麼用法?
1
— git reflog <分支指針>
幫你找到 指定的指針的移動記錄。
如果我們不明確指定是什麼指針,那麼就會顯示HEAD 的移動記錄
? 舉例
比如我新建了 分支 test,然後在上面新增兩個提交,然後切換到 master,然後把 test 刪除了
所以我們 可以看到 HEAD 移動記錄是這樣的,如下圖

從 test 切換到 master 之後,test 被刪除了,然後可以看到 HEAD 在 test 中最後一次的指向是下圖中標紅的提交

所以我們找到這個提交之後,就可以找到曾經 test 分支的所有提交啦
? 步驟如下
1、找到 HEAD 在 test 分支最後一次指向 commitID 是 5b38e41
2、切換到這個提交,git checkout 5b38e41
3、然後創建分支, git checkout -b test,此時就生成一個 test 指針指向了 commit 5b38e41
同樣的,我們可以查看其他指針的動向,比如我的 分支 new_b

16
git revert
有什麼用?
用於內容的撤銷。
但是這個撤銷跟之前的 rebase reset 不一樣,這個指令不會動 你要撤銷的提交一根寒毛
那它是怎麼起到撤銷的作用呢?
比如你要撤銷的提交ID 是 6ab4e5
修改的內容是
1、增加了一行文字
2、刪除了一個文件
然後你使用了 revert 指令撤銷
然後就會生成一個新的提交,這個修改的內容跟你要撤銷的 commit 完全相反,它的修改是
1、刪除 那個 commit 增加的一行文字
2、恢復 那個 commit 刪除的文件
也就是說,這個 revert 撤銷,只是 新增一個commit用來 抹去 你要撤銷的commit 的修改
? 有什麼用法?
1
— git revert <你要撤銷的提交>
作用在上面已經講完啦,就是 新增一個commit用來 抹去 你要撤銷的commit 的修改
並且你要在命令中指定你撤銷的提交
? 舉例
來看下日誌,我準備要撤銷標紅的這個提交

看下這個 commit 的修改內容,如下圖,我就在末尾加了一個 2

按道理,如果我要revert 這個commit 的話,那麼 生成的 commit 應該是刪除 這個新增的 2
來試下,輸入 git revert 4cd4b8a 之後,就跳到一個介面,此時 revert 已經生成了一個 提交,然後這是用於注釋提交的介面,如果你不需要修改注釋,就 :wq 退出

之後就成功增加了一條提交

我們看下這個新增的提交,如下圖,沒錯,就是刪除了 2

你是不是會覺得這個命令挺冗餘的,明明我可以自己手動抹去內容然後新增提交啊,為什麼還要 這個命令
但是如果你要撤銷的內容很多的時候,自己手動一個個改肯定是麻煩不少
何不直接使用一個命令一步到位呢?
17
最後
鑒於本人能力有限,難免會有疏漏錯誤的地方,請大家多多包涵, 如果有任何描述不當的地方,歡迎後台聯繫本人,領取紅包