前言
原來 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"
在編輯器修改 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
忽略的檔案又想要加入版本控制
兩個方法:
-
在
.gitignore
內加入例外(反向):!filename
-
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
當你推分支推不上去
兩個方法:
push -f
聽我的,強制更新遠端分支!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 分別自動標記成 squash
和 fixup
可以搭配 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
git submodule deinit <path>
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 reflog
或git log -g
看 HEAD 移動,就像是你的操作記錄,想要回到某個 commit 卻忘了在哪,可以使用這個方法找到,包含回到rebase
之前的某個 commit 也行
Tig
可以使用 tig
這套工具,提供更簡明的 git 相關顯示以及互動式操作:
https://github.com/jonas/tig
Git 學習資源推薦
同時也是本篇的參考資料
免費電子書
- 為你自己學 Git:淺顯易懂的教學,大推
- Git-Tutorials 基本使用教學:較精簡
- Pro Git:官方,前半有中譯