本機測試環境:macOS 11
14 | 怎麼刪除不需要的分支?#
git branch -d <BRANCH_NAME>
-d
:--delete
- 有條件地刪除一個本地分支
- ❗️條件:當該分支被合併到上游分支(本地分支對應的遠端分支,可設置)或者 HEAD(當前分支)時,才能成功執行刪除
git branch -D <BRANCH_NAME>
-D
:--delete --force
- 強制刪除一個本地分支
- 當確定刪除分支沒有風險時,可以使用
PS:
- 不能刪除當前的分支,如需刪除,可通過
git checkout <BRANCH_NAME>
先切換分支 - 刪除遠程分支:
git push <REMOTE> --delete <BRANCH_NAME>
- 通過
git fetch -p / --prune
同步分支列表,此時就不會顯示遠程已被刪除的分支了
- 通過
15 | 怎麼修改最新 commit 的 message?#
輸入 git commit --amend
,彈出文本編輯器,修改新的 commit message,如紅框所示:
輸入 :wq
保存並退出後,終端顯示修改結果:
再次查看 commit 信息:
修改成功!
PS:git commit --amend
- 本質上是替換上一次提交,不局限於修改 message
- 一般用於修改未 push 到遠端之前的 commit 操作
16 | 怎麼修改老舊 commit 的 message?#
git rebase -i
可以用來修改前幾次的 commit 信息, -i
為使用交互模式
—— 具體步驟 ——
先使用 git log
查看當前的版本歷史:
我們想修改順數第 3 個 commit 的 message,那麼需要基於它的父 commit 來操作。
使用git rebase -i d8796c00719323800976e6c7fcfe6b02627ec6b2
命令,末尾添加父 commit 的 hash 值(如果要對最開始的 commit 進行修改,可使用 git rebase -i --root
) ,彈出編輯界面:
commit 的順序倒置了一下;從 Commands 部分可以看到有很多用法。
我們使用 reword / r
命令,保留某次 commit,但修改其 message,如下:
:wq
保存退出後,又彈出一個新的編輯界面:
修改 commit 的 message,再:wq
保存退出。
修改成功,❗️可以看到這裡使用了分離頭指針,修改完成後,還更新了 master 分支的指向,說明 commit 的 hash 值很可能發生了改變。
那麼驗證一下,再次通過 git log
查看版本歷史:
可以發現順數第 3 個 commit 的 message 修改成功,前 3 個 commit 的 hash 值都發生了改變,除了第 3 個 commit 的 message 都沒有改變,其他信息其實也都沒有改變,可自行驗證。
⚠️:
- 一般基於自己的分支,用在還沒有提交到集成分支(團隊)之前,否則一定要慎用,其會對集成分支造成影響。
rebase
常用於變基,可關注其和merge
的相同點和不同點,參考- Git 分支 - 變基—— 官方
- GIT 使用 rebase 和 merge 的正確姿勢—— 知乎
17 | 怎樣把連續的多個 commit 整理成 1 個?#
同樣使用 git rebase -i
—— 具體步驟 ——
先使用 git log
查看當前的版本歷史:
現在想合併上面紅框內的 3 個 commit,減少分支裡的 commit 數。
使用 git rebase -i --root
命令,末尾添加父 commit 的 hash 值(因為要對最開始的 commit 進行修改,所以使用--root
) ,彈出編輯界面:
要合併的 3 個 commit 見上面紅框內,順序與 git log
展示的順序相反。
使用 Commands 中的 squash / s
命令,可以將某個 commit 融合到前一個 commit 中,修改如下:
:wq
保存退出後,又彈出一個新的編輯界面:
添加第 2 行,作為融合 3 個 commit 的 message,保留下面每個 commit 的 message,再:wq
保存退出。
整合成功,❗️可以看到這裡使用了分離頭指針,修改完成後,還更新了 master 分支的指向。
git log
也驗證了整合的結果,可以發現 2 個 commit 的 hash 值都發生了改變,因為 commit 的 message 或者父 commit 的指向發生了改變。
PS:
- 整合後會導致一些 commit 沒有用了,git 會進行清理。
- 通過
gitk --all
觀察此時的分支情況:
- 可以發現出現了兩棵樹,兩個分支之間已經沒有關聯
- 這是因為 master 分支的根 commit 已經改變
18 | 怎樣把間隔的幾個 commit 整理成 1 個?#
命令: git rebase -i
通過 git log
命令查看版本歷史:
想合併如紅框所示的間隔的幾個 commit,同樣使用 git rebase -i 76e5c9ca
命令,後面添加的是父 commit 的 hash 值,彈出如下熟悉的窗口:
調整要合併的 commit 位置,並使用 s
命令,修改如下:
:wq
保存退出後,提示存在合併衝突,需要解決。
上面藍框中提示了接下來可以進行的操作,這裡我們嘗試第一種方式:解決衝突。
PS:還可以借助 git status
查看目前建議的操作:
接下來查看 readme 文件,解決裡面的衝突,刪除紅框位置的代碼即可:
再運行 git add readme
添加剛才的修改記錄。
然後按前面的提示運行 git rebase --continue
命令,彈出編輯框,輸入 message 信息, :wq
保存退出即可(可能還需要解決衝突,重複上述步驟即可)
最後 git log
查看版本歷史:
可以看出:如果被間隔的 commit 和要合併的 commit 有關聯,很可能不符合預期,導致它們被合併成一個 commit,所以自己要對這幾個 commit 的內容非常清楚。
PS:個人認為不太實用,容易產生衝突。
19 | 怎麼比較暫存區和 HEAD 所含文件的差異?#
命令: git diff --cached
別忘了這張圖:
- HEAD 指向的是當前分支,分支指向的則是一個 commit。
演示:
修改 readme.md 文件後,輸入 git add readme.md
將修改添加到暫存區;
再輸入git diff --cached
比較暫存區和 HEAD 的差異:
可以看到添加了兩行文本,確認符合預期後就可以 commit 了。
20 | 怎麼比較工作區和暫存區所含文件的差異?#
命令: git diff
還可以在命令後添加文件名,從而比較指定文件在工作區和暫存區的差異,如 git diff readme.md
,當然也可以添加多個文件名。
21 | 如何讓暫存區恢復成和 HEAD 的一樣?#
場景:暫存區的內容不想要了,暫存區👉HEAD。
命令: git reset HEAD
, HEAD
後面什麼也不加,則將所有文件恢復成和 HEAD
一致
可以通過 git status
觀察 reset
前後的變化,見下圖:
reset
之後,修改的操作又放到了工作區,可以通過 git add
命令又將它們添加到暫存區。
PS:
git diff --cached
可以驗證暫存區和 HEAD 內容的一致性。
git reset HEAD
和 git restore --staged .
的效果是一樣的,後者是 git 2.23 + 後新增的。
22 | 如何讓工作區的文件恢復為和暫存區一樣?#
場景:工作區做了修改,但是不想要了,想恢復為和暫存區一樣。
命令: git checkout <FILENAME>
再提醒下這張圖:
過程演示:
首先通過 git add
,將修改後的 readme.md 文件,從工作區添加到暫存區:
觀察操作前後 git diff --cached
的結果,即暫存區與 HEAD 差異的變化,可知 git add
成功。
然後,再來修改readme.md 文件,通過 git diff
觀察工作區和暫存區的差異:
紅框部分為此次的修改。
通過 git status
查看工作區和暫存區的情況:
圖中上半部分是暫存區中待 commit的內容;下半部分是工作區中待添加到暫存區的內容。
可以看到兩個部分都有對 readme.md 的修改,分別對應剛才的兩次修改。
❗️此時,如果想把 readme.md 文件在工作區的內容恢復回暫存區狀態,使用 git checkout readme.md
即可。
可以看到工作區的 readme.md 文件已經不在待添加狀態了。
PS:
- 慎用
git checkout
,可能導致文件找不回。 - Git 2.23 之後,用
git switch
和git restore
來替代git checkout
功能:git switch
替換git checkout
切換分支的功能;git restore
替換對工作區文件進行恢復的功能。 git checkout <FILENAME>
和git restore <FILENAME>
的效果是一樣的,後者是 git 2.23 + 後新增的。
23 | 怎樣取消暫存區部分文件的更改?#
命令: git reset HEAD [FILENAME]
,類似第 21 節,在命令後面多添加了具體的文件名,也可以指定多個文件,用空格隔開。
下面示例展示了暫存區中的修改被一個一個退回到工作區中:
如果想把工作區中的修改再添加到暫存區,使用 git add
命令即可。
24 | 消除最近的幾次提交#
命令: git reset --hard <HASH_OF_COMMIT>
,可以改變 HEAD 的 commit 指向,並且暫存區和工作區的內容都回到該 commit。
請看下面的例子:
觀察 git reset --hard
前後的兩個點:
1)通過 git log
可以看出 HEAD 的指向後退了兩個 commit;
2)通過 git diff
可以看出工作區和暫存區從存在差異到沒有差異。
25 | 看看不同提交的指定文件的差異#
命令: git diff <HASH_OF_COMMIT> <HASH_OF_COMMIT> [-- FILENAME]
最後添加指定的文件,如果不添加,則會展示所有文件的差異;
<HASH_OF_COMMIT>
可以用直接換成分支名,分支名本質上也是指向一個 commit。
PS:加--
會使 Git 更明確你的意圖。
26 | 正確刪除文件的方法#
命令: git rm <FILENAME>
效果如下:
本質上相當於刪除了文件,並把這個刪除操作同步到了工作區和暫存區裡,等同於:
先通過 git reset --hard HEAD
讓工作區和暫存區都恢復到 HEAD 的狀態;
通過 git status
,分別觀察 rm
文件以及 git rm
文件後,工作區和暫存區的狀態:
1)前者改變了工作區,但沒有暫存;
2)後者同步了暫存區,此時可以 commit 這次修改了。
27 | 開發中臨時加塞了緊急任務怎麼處理?#
臨時加塞了緊急任務,當前工作還只是半成品,該怎麼辦呢?
git stash
:將當前工作區和暫存區的內容遷移到一個額外的棧上,只對被跟蹤的文件有效。
完成了緊急任務,此時要恢復剛剛的半成品,先查看 stash 了哪些內容。
git stash list
:顯示 stash 的記錄列表。
然後,將最近的一次 stash 內容恢復到工作區內。
git stash apply
:恢復棧頂的保存記錄,且記錄列表裡還保留該記錄。
git stash pop
:恢復棧頂的保存記錄,並把記錄列表裡的該記錄刪除。
- PS:也可以指定某一個記錄恢復。
過程展示:
來了緊急任務,先將暫存區裡未完成的內容 stash 起來。
此時 stash 記錄列表裡添加了一個記錄 {0},並且當前工作區和暫存區裡已經乾淨。
忙完了,恢復 stash 裡最近的一個記錄。
可以看到 apply
是不會清除 stash 記錄的,並且被恢復的內容放到了工作區,而之前 stash 的是暫存區的內容。
再來另一種方式的恢復,使用 pop
並且添加 --index
參數。
可以看到 pop
會清除該 stash 記錄,並且被恢復的內容放到了與 stash 時一樣的暫存區, --index
其實就代表還會恢復暫存區。
PS:
WIP 全稱 Work in progress,表示正在工作過程中,引申含義為 “目前工作區中的代碼正在編寫中,這部分代碼不能獨立運行,是半成品”。
28 | 如何指定不需要 Git 管理的文件?#
方法:在 .gitignore
文件中聲明文件類型或文件名即可(必須是 .gitignore
)
聲明 doc / 和 doc 的區別演示:
1)doc/:忽略文件夾,但不會忽略同名的文件
創建 doc 文件夾,在裡面添加 readhim 文件。
在 .gitignore
文件裡添加 doc/
後,Git 將忽略對 doc 文件夾的跟蹤。
但是,將 doc 文件夾換成 doc文件後,Git 將不會忽略跟蹤 doc 文件。
2)doc:同時忽略文件夾和同名文件
而對於忽略規則 doc,不管是文件夾還是同名文件,都会讓 Git 忽略對其跟蹤。
⚠️:
- 常用通配符
*
忽略同類的文件 - 文件已經被跟蹤後,
.gitignore
文件就不起作用了- 如果提交 commit 後,想再忽略一些已經提交的文件呢?
- 可以把想忽略的文件先添加到
.gitignore
文件中,然後通過git rm --cached FILENAME
的方式忽略掉無需跟蹤的文件。
- 在 Github 上新建倉庫時,可以選擇添加一份合適的.gitignore 文件:(很貼心❤️)
PS:對於不同的語言或項目,常見的 .gitignore
文件可參考github/gitignore庫。
29 | 如何將 Git 倉庫備份到本地?#
相關命令: git clone
、 git remote
、 git push
常用傳輸協議:本地協議、http/https 協議、ssh 協議,後兩者在工作中最常用。
傳輸協議又可以分為兩類:啞協議和智能協議,這裡以本地協議為例:
1)/Path/to/.git;2)file:///Path/to/.git
前者為啞協議,後者為智能協議。
那麼兩者在 Git 備份時有什麼區別呢?
智能協議相比啞協議,1)傳輸進度可見,2)並且傳輸速度更快。
智能協議和啞協議的效果對比:
通過 pwd
獲取到了之前演示用的倉庫地址為: /Users/double/Desktop/test
現在通過兩種協議,將倉庫備份到另外的地方:test_remote 目錄下。
啞協議
⚠️: --bare
代表創建裸倉庫,即不帶工作區的倉庫,其方便作為服務端;備份的是.git 文件夾,所以地址要添加 /.git
;最後的 ya.git
表示倉庫名。
智能協議
回到 test.remote 目錄下,使用智能協議,即添加 file://
前綴。
對比可以看到直觀的區別:啞協議不帶進度條,而智能協議有進度條。
上面展示的是從遠端克隆 (clone
)倉庫到本地的過程,那如何推送 (push
) 本地倉庫到遠端呢?
回到原倉庫(這裡原倉庫的角色從遠端轉變成了本地),新建一個名為 new
的分支。
此時遠端 intel.git 倉庫還只有 3 個分支:
在本地添加遠端伺服器:
git remote add intel file:///Users/double/Desktop/test_remote/intel.git
PS: intel
為伺服器別名,後面是伺服器地址,使用智能協議。
通過 git remote -v
可以查看詳細的伺服器信息。
接下來進行推送: git push REMOTE_NAME
命令
⚠️:直接推送會報錯,需要先設置上游。
再次查看下遠端 intel.git 倉庫的分支情況:
可以看到分支 new
已經被推送過來了~
學到這裡,有哪些是你之前不知道的呢?歡迎交流~