前言

接續前一篇,直接進入紀錄的部分。

分支相關指令

# 列出 local 分支
$ git branch
# 列出 remote 分支
$ git branch -r # 等於 --remote
# 列出 local + remote 分支
$ git branch -a
# 開新分支
$ git branch branch_name
# 在指定 commit 的位置開新分支
$ git branch branch_name 657fce7
# 更名分支
$ git branch -m old_name new_name
# 刪除分支,若該分支內容未備份於其他分支會給予提示並且不可刪除,此時使用大寫 -D 以強制刪除
$ git branch -d branch_name
# 切換分支
$ git checkout branch_name
# 存在分支則切換分支,不存在分支則新建分支並切換分支
$ git checkout -b branch_name
# 在指定 commit 的位置,存在分支則切換分支,不存在分支則新建分支並切換分支
$ git checkout -b branch_name 657fce7
# 切換到遠端分支時會發生 detached HEAD 狀態,使用追蹤參數在 local 建立追蹤分支,即可解決
$ git checkout -t origin/branch_name # 等於 --track
# 回復分支,紀錄或找到被刪除分支最新 commit 的 SHA-1 值則可回復
$ git branch branch_name b174a5a
# 列出所有節點紀錄,等同於 GUI 線圖
$ git show-branch -a
# 強制遷移 master branch 到指定位置,HEAD 要先移到其他位置,忘開分支就 commit 時可能有用
$ git branch -f master e12d8ef

合併分支相關

# 合併分支時,如果原分支比對要被合進來的分支,發現根莖相同,用線圖來看就是仍在一直線上,會啟用 快轉模式(Fast Forward),效果看起來是只平移原分支
# 合併分支時,如果原分支比對要被合進來的分支,發現根莖不相同,線圖類似 Y 形分岔,合併時會產生新的 commit,作為合併紀錄點,若發生 衝突(Conflict) 也會一併解決
# 使用以下指令可以總是產生分岔合併節點,即使刪除已合併過的分支,也可以在線圖上知道這裡曾經合併過
$ git merge branch_name --no-ff
# 重設基礎合併,此方法會將當前分支的每個 commit 照順序嫁接到指定的分支上
$ git rebase branch_name
# 此為危險操作,可用 Reflog 指令找到危險操作前最新 commit 的 SHA-1 值,並且執行 Reset 指令回復
# 或是使用危險操作皆有的額外紀錄點 ORIG_HEAD,執行 Reset 指令回復
$ git reset ORIG_HEAD --hard
# rebase 指令進行時,若發生 衝突(Conflict),處理方式和 merge 不同,此時看線圖會看到 HEAD 節點正卡出狀況的新移植節點上,此時將衝突解決,之後執行以下指令繼續 rebase
$ git rebase --continue
# 若衝突發生點不是文字檔而是圖片或其他二進位檔
$ git checkout --ours sample.jpg # 保留原分支檔
$ git checkout --theirs sample.jpg # 保留被合分支檔
# rebase 互動模式(interactively),開啟編輯器,編輯每筆 commit 前的動作,關閉後再逐筆修改需要再編輯的 commit
# 動作分別有,p(pick,預設,表使用該 commit)、r(reword,修改 commit message)、e(edit,停在該節點進 amend 流程)、s(squash,粉碎 commit,合到前一個 commit)、f(fixup,效果同 s,但不保留 commit message)、d(drop,移除 commit)
# 編輯動作時可直接調整順序,若整行刪除等同於 d 動作
# 常用狀況舉例,使用 s 動作處理多合一,使用 e 動作處理一分多(reset 之後,增加多筆 commit)
$ git rebase -i 657fce7

標籤相關

分兩種類型,輕量標籤(lightweight tag),適用於個人標記;有附註標籤(annotated tag),適用於版本發布

# 新增輕量標籤在當前 commit
$ git tag tag_name
# 新增輕量標籤在指定 commit
$ git tag tag_name 51d54ff
# 新增有附註標籤在指定 commit,-a 表要附註解,-m 效果同 commit 時
$ git tag tag_name 51d54ff -a -m "Big Cats are comming"
# 刪除標籤
$ git tag -d tag_name
# 顯示 commit 紀錄,any_point 可代入 標籤名 / SHA-1 值
$ git show any_point

暫存相關

可先 commit,之後再 reset 拆掉繼續做,或是使用 stash 指令放入暫存

# 新增暫存
$ git stash
# 新增暫存,連同 Untracked 的檔案
$ git stash -u
# 列出所有暫存,暫存訊息有預設格式,在哪個 branch + 當時在哪個 commit
$ git stash list
stash@{0}: WIP on branch_a: 053fb21 branch_a_latest_commit_message
stash@{1}: WIP on branch_b: b174a5a branch_b_latest_commit_message
# 取出暫存,不指定第幾個暫存則預設取編號最小
$ git stash apply stash@{1}
# 刪除暫存
$ git stash drop stash@{1}
# 取出並刪除暫存,等同於 apply + drop
$ git stash pop stash@{1}

撿其他 branch 的 commit

# 合併進當前位置
$ git cherry-pick 6a498ec
# 合併進當前位置,可一次撿多個,依順序
$ git cherry-pick fd23e1c 6a498ec f4f4442
# 撿過來但先不合併
$ git cherry-pick 6a498ec --no-commit

重要資料進版控,移除方法

  1. 軟刪除,還是有辦法復原
# 讓所有 commit 執行指定命令,刪除重要資料
$ git filter-branch --tree-filter "rm -f config/database.yml"
# 復原
$ git reset refs/original/refs/heads/master --hard
  1. 徹底刪除
# 讓所有 commit 執行指定命令,刪除重要資料
$ git filter-branch -f --tree-filter "rm -f config/database.yml"
# 刪除執行 filter-branch 產生的還原點
$ rm .git/refs/original/refs/heads/master
# 清除 Reflog 流程,強制讓紀錄過期,之後手動執行回收機制
$ git reflog expire --all --expire=now
# 前一行已讓紀錄過期,檢視過期的未追蹤檔
# 不加 --unreachable 則檢視所有待過期項目,包含 commit、tree、blob 物件等等
# 加 --no-reflogs 則額外檢視目前只存在於 Reflog 中的待過期項目
# 待過期項目有兩種狀態,Unreachable、Dangling,大部分會是 Unreachable,此狀態表示該項目在當前保留節點的周圍;Dangling 則表示完全與當前節點無關項目
# 舉例來說,A -> B -> C -> D,reset 後兩個節點,剩下 A -> B,此時 C 的狀態是 Unreachable,D 則是 Dangling
$ git fsck --unreachable
# 執行回收機制
$ git gc --prune=now # 等同於 git gc & git prune --expire=now
# 若已推到 remote 則強制覆蓋
$ git push -f

remote 相關

# 增加 remote,origin 是慣例用詞,代表遠端,可任意命名,最後一長串是遠端伺服器的連線位置
$ git remote add origin git@github.com:GitHub_id/repository_name.git
# 從 local 推到 remote,此時是將 local 的 master branch 推到 remote 的 master branch
# 參數 -u(--set-upstream),設定 local 端分支的上游,會 追蹤(track) 並默認為推送目標,設定一次之後,下次推送只要 git push,每個分支只有一個上游
$ git push -u origin master
# 上面的推送指令拿掉參數等同於下面的指令
$ git push origin master:master # 最後的分支名對應 local branch:remote branch,可以讓 remote 用不同名字
# 強制推送,少用
$ git push -f
# 取得遠端更新內容
$ git fetch
# 偽更新 local
$ git pull # 實際等同於 git fetch & git merge
# 偽更新 local,改用 rebase
$ git pull --rebase
# 檢視所有 remote 資訊
$ git remote -v
# 刪除遠端分支
$ git push origin :branch_name # 概念大概是推一個空的分支到遠端
# 複製 remote 專案
$ git clone git@github.com:sample_user/project_name.git
# 複製 remote 專案,並在 local 用不同的資料夾名
$ git clone git@github.com:sample_user/project_name.git new_project_name

fork

fork 其他人的專案時,為同步原專案最新進度,進行以下流程

# 增加 remote,upstream 名為慣例用詞
$ git remote add upstream git@github.com:GitHub_id/repository_name.git
# 取得遠端更新內容
$ git fetch upstream
# 合併遠端分支
$ git merge upstream/master
# 更新 fork 過來的專案
$ git push origin master

更新檔 相關

產生 patch 檔案,從來沒用過

# 範例 git log
$ git log --oneline
6e6ed76 (HEAD -> master) add product page
6aba968 update info.html
fd7cd38 (origin/master, origin/HEAD) Update about.html
2eb8fea add readme
953cbd9 update info page
# 產生更新檔,指定範圍,有點像 slice
$ git format-patch fd7cd38..6e6ed76
0001-update-info.html.patch
0002-add-product-page.patch
# 產生更新檔,指定區間,-2 代表最新兩筆 commit
$ git format-patch -2
0001-update-info.html.patch
0002-add-product-page.patch
# 產生更新檔,-o 參數指定輸出位置
$ git format-patch -2 -o /tmp/patches
/tmp/patches/0001-update-info.html.patch
/tmp/patches/0002-add-product-page.patch
# 使用更新檔
$ git am /tmp/patches/*

Git Flow

  1. master 穩定、可上線的版本,更新只能從別的分支合併過來,通常會加版本標籤
  2. develop 開發主幹,新增功能時從這裡切 feature branch 出去,完成後再合回來
  3. hotfix 線上版本緊急修復,從 master 切出去,完成後合到 master、develop
  4. release 上線前的測試分支,從 develop 切出或合併,測完後合到 master、develop,要合到 develop 是因為有些狀況要到 production 環境才會遇到,修復後合回開發分支避免之後再遇到
  5. feature 新功能分支,從 develop 切出,完成後合回 develop

二分法除錯

適用在大範圍 commit,不確定何時開始出錯的狀況

# 開始執行二分法,執行這行後,HEAD 會跳到兩點正中間的 commit
$ git bisect start 起始點 結束點
$ git bisect start HEAD f6b72af
# 確認該 commit 是否已出問題
$ git bisect good # 沒問題,執行這行,再跳到 HEAD 和 第一個中間點中間
$ git bisect bad # 有問題,執行這行,再跳到 f6b72af 和 第一個中間點中間,若找到出問題的 commit 就結束流程
# 中途放棄流程
$ git bisect reset

建立新目錄的分支,此方法適用於正在跑 CI/CD 流程時,仍想處理其他需求的狀況,Git v2.6.0 以上才有

$ git worktree add -b cat ../copycat
# 此方法會再與專案同一層的位置建立目錄,最後一樣可以合併回來,處理完後,新的目錄即可刪除