目錄

Git 常用指令跟設定

基礎概念

  1. Working directory: 工作目錄

  2. Staging area: 暫存區

  3. remote and origin: remote 指的是遠端的 repo,概念,origin 是真的一個指標指向一個 url

    $ git remote -v
    origin  https://github.com/example/repo.git (fetch)
    origin  https://github.com/example/repo.git (push)
  4. HEAD: 指向現在整個工作環境所在的 commit

  5. Branch: 只是指標,所以切換 branch 就是把 HEAD 指標移動到 MASTER 指標上

Config

[user]
    email = bses30074@gmail.com
    name = Yan-Hao-Wang
[core]
    editor = vim
    autocrlf = input
[color]
    ui = true
[diff]
    algorithm = histogram
    indentHeuristic = true
[branch]
    sort = -committerdate
[pull]
    rebase = true
[alias]
    st = status
    rst = restore
    ck = checkout
    lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
  • autocrlf = input: 在送出(Commit)時自動將 CRLF 轉為 LF,但在拉取(Checkout)時不做變動
  • ui = true: 強制在終端機顯示顏色,讓 status 或 diff 的結果一眼就能看出哪些是新增或刪除
  • algorithm = histogram: 比起預設的 myers,histogram 能更聰明地處理程式碼塊的搬移,產生的 diff 邏輯更貼近人類閱讀程式碼的方式
  • indentHeuristic = true: 當你移動函數或修改巢狀縮排時,Git 會試著讓 diff 的起點和終點對齊縮排,讓修改看起來更整潔
  • sort = -committerdate: 當你執行 git branch 時,它會把最近操作過的分支排在最上面
  • rebase = true 執行 git pull 時預設用 rebase 而不是 merge
  • lg = ...: 它會用漂亮的顏色與線條畫出分支拓撲圖,並顯示相對時間(如 2 hours ago)與作者

常用指令速查表

分類 指令 功能說明 備註
暫存與提交 (Add/Commit) git add -p 互動式暫存已追蹤檔案的部分區塊 適合將一個檔案拆成多個 commit
git add *xxx* 可以用正則表達式來加入檔案,會比較快 避免打太長的路徑
git commit --amend 將目前的修改併入上一次 commit 常用於修正 commit 訊息或補檔
還原 (Restore) git restore <file> 將工作目錄檔案還原至 HEAD 狀態 取代舊版 git checkout <file>
git restore --staged <file> 將檔案從暫存區移回工作目錄 讓檔案變成「已修改但未暫存」
git restore --source HEAD~2 <f> 將特定檔案還原至前兩個版本的狀態 只針對單一檔案進行時光倒流
重置 (Reset) git reset --soft HEAD~1 退回版本,保留暫存區與工作目錄修改 適合重新組合多個 commit
git reset --mixed HEAD~1 退回版本,重置暫存區,保留工作目錄 Git 的預設重置模式
git reset --hard HEAD~1 退回版本,刪除所有未提交的修改 危險:所有更動都會消失
日誌與差異 (Log/Diff) git log --oneline <file> 以單行模式顯示特定檔案的 commit 紀錄 快速追蹤該檔案的歷史
git log --name-only 若要在 git log 中僅顯示被變更的檔案名稱
git log --name-status 若需要同時顯示操作類型(新增、修改、刪除)
git diff --staged 比較「暫存區」與「最後一次提交」差異 確認準備要 commit 的內容
git diff --color-words 顯示單字級差異,不顯示 +/- 符號 適合需要複製乾淨程式碼時使用
git diff -U1000 顯示前後各 1000 行的上下文 適合 Review 較長的函數邏輯
git diff > a.patch 產生成純文字格式的補丁檔,之後使用 git apply 套用 純文字專案專用。適合透過 Email 傳送輕量化的程式碼變更
git diff --binary > a.patch 產生成包含二進位資料的完整補丁檔 含有編譯產物或圖檔時必用
分支 (Branch/Checkout) git checkout -b <name> 建立新分支並立即切換過去 常用於開發新 Feature
git branch -m <new_name> 修改當前分支的名稱
git branch -vv 查看本地分支與遠端分支的追蹤關係 確認 Upstream 設定是否正確
git branch -D <branch> 強制刪除分支(不論是否已合併)
合併與重底 (Merge/Rebase) git merge <branch> --no-ff 合併時強制產生一個 Merge Commit 保留完整的分支開發軌跡
git rebase <new_base> 將當前分支的基準點移動到 new_base 維持線性歷史,線圖更整潔
git rebase -i HEAD~n 互動式 Rebase,編輯最近 n 個 commit 可進行 squash (合併) 或 edit
遠端 (Remote/Push) git push <rem> <b1>:<b2> 本地 b1 推送到遠端並命名為 b2 手動指定遠端分支名稱
git push <rem> :<branch> 刪除指定的遠端分支 冒號前留白代表「推入空內容」
git branch --set-upstream-to=<u> 設定本地分支追蹤特定的遠端分支 例如:origin/main
暫存 (Stash) git stash pop 取出最新的暫存進度並從清單中移除 處理臨時需要切換分支的情況
git stash list 列出目前所有暫存的進度
git stash push -m "Messages..." 存入時加上訊息 (Message)
git stash --keep-index 只把 unstage 的東西 stash
git stash apply stash@{x} 套用某個暫存,但不刪除
git stash show stash@{x} 查看特定 Index 的檔案清單
git stash show -p stash@{x} 查看特定 Index 的程式碼差異
git stash drop stash@{x} 刪除特定 Index 的暫存
特殊 git mv 可被 Git 識別的移動檔案

Patch Add

可以特別談談 git add -p,他應該常常被使用:

指令 意義 說明
y Yes 將這個區塊加入暫存區。
n No 將這個區塊加入暫存區。
q Quit 退出,不處理剩下的區塊。
s Split 將目前的區塊拆分成更小的塊(如果改動分得很開)。
e Edit 手動編輯這個區塊(最強大但也最進階的功能)。
d Don’t add 跳過這個檔案中剩下的所有區塊。
? Help 顯示所有指令的詳細說明。

其中 edit 比較複雜,你假如不想要這行 +,就直接把他刪除。假如不想要這行 -,就把他的 - 變成空白,然後 # 好像是把這一行註解。

警告

Edit 超級難用,他非常在乎每一行,我有遇到因為我的 vimrc 有 autocmd BufWritePre * :%s/\s\+$//e Automatically remove trailing whitespace on save,但因為 Edit 很嚴格,他要每個空白都在,導致我一直不能成功。

最後要使用 :noautocmd wq 才能成功,找超級久。

Rebase

特別談談 rebase 的詳細用法,基礎用法像是,假設你想修改最近 3 次的提交 ,可以用 git rebase -i HEAD~3,然後你會有不同的選項可以選,像是:

  • pick: 保留該提交
  • reword: 修改提交訊息
  • edit: 停下來讓你修改代碼內容
  • squash: 將該提交合併到前一個提交中(保留訊息)
  • fixup: 類似 squash,但會捨棄該提交的訊息(常用於修正小錯誤)
  • drop: 刪除該提交

Edit 修改特定的 Commit

如果你發現某個舊的 commit 少改了一個檔案,或是程式碼有 bug,可以使用 edit。

更改標記:在互動視窗中,將目標 commit 前方的 pick 改成 edit(或簡寫 e),儲存並退出。

進入編輯狀態:Git 會停在那個 commit。此時你可以修改程式碼。

加入修改:

git add <檔案名稱>
git commit --amend --no-edit
git rebase --continue

Fixup and Auto Squash

除了以上方法,還有一個快速 fixup commit 的方式。

# Git 會自動幫你產生一個 commit,標題叫 fixup! <原本 commit 的標題>。這個 fixup! 字眼就是給 Git 看的「自動合體雷達」
git commit --fixup <你想要 merge 進去的 commit>
# 執行 rebase 時加上 --autosquash 參數
git rebase -i --autosquash <你想要 merge 進去的 commit 的上一個 commit>
  1. 自動排隊:Git 會自動把那個 fixup! 開頭的 commit,從最後面搬到目標 commit a1b2c3d 的正下方
  2. 自動選模式:Git 會自動把該行的指令從 pick 改成 fixup
  3. 直接儲存即可:你不需要在那邊手動剪下貼上、改 f。你只要看一眼,確認 Git 沒排錯,直接輸入 :wq 離開

Stash

Stash 本質上就是一個「隱形的 Commit」

通常一個 Stash 包含:

  • Index Commit:保存你當時 git add 過的暫存區內容
  • Working Tree Commit:保存你當時在工作區改動但還沒 add 的內容
  • (有時還有)Untracked Commit:如果你用了 -u,它會再開一個 Commit 存新檔案

Git 沒有直接修改 Stash message 的方式,所以我們只能。

hash=$(git rev-parse stash@{1}) # 取得該 stash 的 Commit Hash
git stash store -m "你的新訊息內容" $hash # 存入一個帶有新訊息的 stash
git stash drop stash@{2} # 刪除原本舊的 stash

其他情況

我們可能會想說,最新的幾個 commit 有沒有特定不小心加進去的特定關鍵字,像是可能是 log 相關的。

# 最快掃描法:檢查「從 master 分叉後」的所有改動
# -E: 正則表示法
# -i: 無視大小寫
git diff master..HEAD | grep -Ei "info|debug|log"

 # 精確追蹤法:找出「是哪一筆 commit」帶進來的
git log -p master..HEAD -G"StoreManager: Unlink callback"

Github

Sync

我們假如有自己的 branch,想要 sync,所以要 merge master,可以直接打

git rebase master

這樣可以讓我們的 commit 還是在最新的地方,不會 sync 之後,修改的 commit 在新 merge 進來的 commit 前面。

Reference