Git 小技巧

我的個人常用 Git 指令收藏冊(cheat sheet)

前言

原來 Git 比想像中的易學難精

剛開始學 git,我是隨便買了一本薄薄的書,然後跟著裡面的內容照著跑,就把 git 的基礎學起來了,例如說暫存區、分支、commit、tag、push 等等
曾經我以為這樣就算學會 git 了,我也這樣用 git 用了一兩年,直到有一天在實習的公司被問到說

你會用 Git?那你會用 rebase 嗎?

…那是什麼?

不會 rebase 的話就不算會 Git 喔

所以趕快去把 rebase 學起來。
哦哦,rebase 就是把一個分支搬到另一個分支上面,來讓歷史紀錄變得漂亮嘛,讓大家都像是從 develop 長出來的一樣。

這下好了,我會 rebase 了,我總算能打遍天下無敵手了吧。

後來到第二間公司實習,主管說,自己 commit 很醜沒關係,但你在推出去之前要把自己的 commit 修一下

要會用 rebase 修剪 commit,不然就不算是會 rebase

原來我 rebase 還沒學完嗎?

因為第二間公司有圖書館,我就趁沒事的時候在圖書館逛逛有什麼書,就找到了好像近幾年蠻有名的「為你自己學 Git」,寫的淺顯易懂,即便是已經會一些 git 基本操作的人,也能夠學到一些東西,十分推薦給大家看(無業配)。當然還看了不少相關書籍與網路文章,感受到其實 git 有很多很方便的進階用法,也所以才有這篇文章。

Git 的檔案分區

雖然會使用指令,但其實一開始我並不了解每個階段的名稱,因此在此附上:

  • 工作目錄
  • 暫存區 Stage
  • 儲存庫 Repository

git add 從工作目錄把變更加到暫存區
git commit 提交,把暫存區的變更加入儲存庫,正式納入版本管理

Config

常用的兩個 config 檔案:

  • --global:user 層級的,一般放在 ~/.gitconfig
  • --local:repo 層級,放在 .git/config

local 會優先

使用 git config -l 快速查看設定,也可以搭配 --global 之類的 flag,只查看某層級的設定

預設編輯器

以 VSCode 為例:

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

記得先裝 VSCode Command Line Tool

在編輯器修改 config

git config -e

像我習慣把編輯器設定為 VSCode,就可以直接打開 .git/config,很方便

Stage 相關操作

加錯檔案

如果用 git rm 也會把檔案砍掉,因為其實 git rm = rm + git add
改用 git rm --cached 就可以不砍掉檔案,單純移出暫存區

同理

git mv = mv + git add 前後兩個檔名

不想要檔案變更(回到修改前狀態)

checkout 抵銷:

git checkout -- <file>:只復原工作區
git checkout HEAD -- <file>:連暫存區也復原,相當於 git reset --hard -- <file>

同理也可以拿取特定分支檔案

git checkout <hash/branch> -- <file>

同理,可以用 git checkout <hash/branch> -f 硬切分支,會同時取消所有的變更!

.gitignore 忽略的檔案又想要加入版本控制

兩個方法:

  1. .gitignore 內加入例外(反向):!filename

  2. git add -f 強制加入(我忽略你的忽略,但不推薦)

可以用 git clean -fx 一次清光不該被加入 stage 的檔案

Commit 相關情境

修改上次 commit

git commit --amend 直接合併到上個 commit
git commit --amend --author "David Ye <david@dwye.dev>" 修改 commit 的作者

或是直接使用 rebase 來修改也可以,通常用在需要修改比較前面的 commit,或是一次修改大量 commits

拆掉 commit

git reset HEAD~
HEAD 是目前所在的 commit
~ 代表往前走一個 commit,因此也可以用多個 ~ 拆多個

Branch 操作

新增 branch 同時切過去

git checkout -b <new_branch>,一步完成,就不需要先創建分支才切過去,使用頻率極高

branch 名稱修改

切到該分支,然後 git branch -m <new_name>

Reset 與 Checkout 的差別

checkout 是移動現在的工作區到哪個 commit
-> 移動工作區,分支放在地上
reset 是設定分支到某個地方,已經 commit 的其實不會動到
-> 帶著分支移動

所以 reset 可以拿來救 rebase!

git reset ORIG_HEAD --hard

ORIG_HEAD 就是 rebase 操作前的原先 commit

git reset --hard 到底在幹嘛

--hard 會把 staging 的檔案全部清掉,reset 預設是切到 HEAD,所以分支不動
於是只有 --hard 生效
就像帶著分支原地跳一下,把身上的東西抖掉的感覺 XD

reset 可以當作 add 的相反

git reset -- <file> 取消 add,變更會保留在工作區
git checkout -- <file> 連變更都移除掉!

Remote

git remote 列出節點(origin)
git remote --verbose 列出節點 / 網址 / fetch / push
git branch --all 可以看到遠端分支

Push

git push -u origin master 設定上游分支!
git push origin master 只推送分支,不設定下次也會預設推到這
git push origin master:another_name 推到不同名分支

刪除遠端分支

其實就是推一個空分支上去

git push origin :another_name

Pull

pull = fetch + merge

fetch: 更新 remote 分支到本機
merge: 把本機分支和 remote 分支合併!

pull --rebase = fetch + rebase

當你推分支推不上去

兩個方法:

  1. push -f 聽我的,強制更新遠端分支!
  2. pull --rebase 跟新自己的分支,然後 push

Rebase

git rebase <hash/branch>

從 branch/hash 重新開始長出分支到現在的 commit

操作完畢後,ORIG_HEAD 會指向操作前的舊的 commit,雖然舊的 commit 會被藏起來,但其實舊的 commit 還是會存在一段時間的,因此可以用 git reset --hard 返回。

(當你害怕的時候,就用 git rebase --abort 逃跑吧 XD)

修改過去:-i 的互動式修改

git rebase -i <hash/branch>

開啟互動模式,會顯示出所有 commit,上到下是舊到新

  • pick: 保留
  • reword: 改 message
  • squash: 把分支和上一個壓扁
  • fixup: 和 squash 相同,但 commit message 直接扔掉不保留
  • edit: 停在這,回編輯器,等到 commit 後繼續,用來修改 commit
    • 所以可以搭配 reset 拆 commit 內容:把檔案扔出暫存區,然後分次 commit

也可以換順序

也能直接刪掉 commit,但不建議 (會有更動消失掉)

git rebase -i --autosquash <hash/branch>

會把 commit message 是 squash! <commit>fixup! <commit> 的 commit 分別自動標記成 squashfixup

可以搭配 git commit --fixup 使用,很方便

worktree: 一個 repo 多個目錄

git worktree add <path> <branch> 設定分支為某個 path 底下的檔案

git worktree list 列出 worktree

刪除 worktree

先刪掉該資料夾(上面的 path),然後 git worktree prune
可以先 git worktree prune -n 看看會刪掉 (dry run)

submodule

執行 git clone 時不會自動把 submodule 一起 clone 過來,所以要初始化 submodule 並 clone:

git submodule update --init --recursive

相關設定會存在 .git/config

刪除 submodule

  1. git submodule deinit <path>
  2. git rm <path>

更換 submodule 的 reference url

用指令更改:

git config -f .gitmodules submodule.<name>.url <url>

或手動更改 .gitmodules

然後 sync:

git submodule sync

其他技巧

Log

  • git log --oneline 可以只看一行的 commit message,比起一般的 git log 我更常用這個
  • git refloggit log -g 看 HEAD 移動,就像是你的操作記錄,想要回到某個 commit 卻忘了在哪,可以使用這個方法找到,包含回到 rebase 之前的某個 commit 也行

Tig

可以使用 tig 這套工具,提供更簡明的 git 相關顯示以及互動式操作:
https://github.com/jonas/tig

tig example

Git 學習資源推薦

同時也是本篇的參考資料

免費電子書

實體書籍

其他參考資料