本機測試環境: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 已經被推送過來了~
學到這裡,有哪些是你之前不知道的呢?歡迎交流~